t54-main-map-material.mjs 2.9 KB

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