import assert from 'node:assert/strict' import { register } from 'node:module' import { readFile } from 'node:fs/promises' import { pipeSurface } from '../../../src/modules/cockpit/domain/pipePhysical.js' register('../../cockpit-real-data-wayfinding/verify/t32-adapter-io-loader.mjs', import.meta.url) const { createCesiumPipeModels } = await import('../../../src/modules/cockpit/services/cesiumPipeModels.js') const { createPipeModelShader } = await import('../../../src/modules/cockpit/services/pipeModelShader.js') const { LightingModel } = await import('cesium') globalThis.t32Models = [] globalThis.t32TerrainFails = false globalThis.window = { location: { href: 'http://test.invalid/cockpit/', origin: 'http://test.invalid' } } const nativeFetch = globalThis.fetch const source = { entities: { values: [] } } const viewer = { terrainProvider: {}, isDestroyed: () => false, scene: { requestRender() {}, primitives: { add() {}, remove(m) { m.destroy() } } } } const pipes = [ { code: 'pe', industry: 'WATER_SUPPLY', diameter: 400, material: 'PE', positions: [[110.39, 28.44], [110.3901, 28.44]] }, { code: 'steel', industry: 'WATER_SUPPLY', diameter: 200, material: '钢管', positions: [[110.39, 28.4401], [110.3901, 28.4401]] }, ] function values(model, key) { const accessor = model.gltf.accessors[model.gltf.nodes[0].extensions.EXT_mesh_gpu_instancing.attributes[key]] const view = model.gltf.bufferViews[accessor.bufferView] const data = model.gltf.buffers[view.buffer].uri.split(',')[1] const bytes = Buffer.from(data, 'base64') return new Float32Array(bytes.buffer, bytes.byteOffset + (view.byteOffset || 0), accessor.count * 3) } let adapter try { globalThis.fetch = async (url) => String(url).startsWith('blob:') ? nativeFetch(url) : new Response(await readFile(new URL('../../../public/cockpit/models/pipes/spline.gltf', import.meta.url), 'utf8')) adapter = createCesiumPipeModels(viewer, { source }) await adapter.sync({ pipes }) assert.equal(globalThis.t32Models.length, 2, '同业不同材质必须拆为不同着色批次') const [pe, steel] = globalThis.t32Models assert.ok(Math.abs(values(pe, 'SCALE')[0] - 3.2) < 1e-6) assert.ok(Math.abs(values(steel, 'SCALE')[0] - 1.6) < 1e-6) assert.ok(values(pe, 'SCALE')[2] > 9 && values(pe, 'SCALE')[2] < 11, '轴向长度保持地理路径') const shader = createPipeModelShader('WATER_SUPPLY', pipeSurface('PE')) assert.equal(shader.lightingModel, LightingModel.PBR) assert.equal(shader.uniforms.u_pipeMetalness.value, 0) shader.destroy() await adapter.sync({ pipes: [{ ...pipes[0], material: '钢管' }, pipes[1]] }) assert.ok(globalThis.t32Models.length > 2, '仅材质变化也触发3D更新') assert.equal(pe.isDestroyed(), true) console.log('PASS 主图GPU实例源直径和地理长度、按材质分组/PBR参数、仅材质更新与旧批次释放') } finally { adapter?.destroy(); globalThis.fetch = nativeFetch }