@roomle/web-sdk 4.0.0 → 4.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/lib/{BufferGeometry-CnbfgBmz.mjs → BufferGeometry-DcRNeLZD.mjs} +2 -2
  2. package/lib/{BufferGeometry-CnbfgBmz.mjs.map → BufferGeometry-DcRNeLZD.mjs.map} +1 -1
  3. package/lib/{GLTFExporter-BxcjPMva.mjs → GLTFExporter-DdzTpCjW.mjs} +46 -46
  4. package/lib/{GLTFExporter-BxcjPMva.mjs.map → GLTFExporter-DdzTpCjW.mjs.map} +1 -1
  5. package/lib/{GaussianBlurNode-8gO-I1qG.mjs → GaussianBlurNode-CLuzhfDu.mjs} +19 -19
  6. package/lib/{GaussianBlurNode-8gO-I1qG.mjs.map → GaussianBlurNode-CLuzhfDu.mjs.map} +1 -1
  7. package/lib/{Object3D-DMaDdv-D.mjs → Object3D-CEXK2mMb.mjs} +1 -1
  8. package/lib/{Object3D-DMaDdv-D.mjs.map → Object3D-CEXK2mMb.mjs.map} +1 -1
  9. package/lib/Pass-C7MZilDW.mjs +53 -0
  10. package/lib/Pass-C7MZilDW.mjs.map +1 -0
  11. package/lib/{PointLightHelper-CahXSxiA.mjs → PointLightHelper-dDeo1kUI.mjs} +4 -4
  12. package/lib/{PointLightHelper-CahXSxiA.mjs.map → PointLightHelper-dDeo1kUI.mjs.map} +1 -1
  13. package/lib/{SpotLightHelper-BEqeYq1p.mjs → SpotLightHelper-BsMEPgVo.mjs} +3 -3
  14. package/lib/{SpotLightHelper-BEqeYq1p.mjs.map → SpotLightHelper-BsMEPgVo.mjs.map} +1 -1
  15. package/lib/{USDZExporter-ZZoG1mne.mjs → USDZExporter-BIDQSKG4.mjs} +3 -3
  16. package/lib/{USDZExporter-ZZoG1mne.mjs.map → USDZExporter-BIDQSKG4.mjs.map} +1 -1
  17. package/lib/{Vector4-BkA_VGid.mjs → Vector4-D-4p8rfn.mjs} +2 -2
  18. package/lib/{Vector4-BkA_VGid.mjs.map → Vector4-D-4p8rfn.mjs.map} +1 -1
  19. package/lib/{api-CUyBdNba.mjs → api-BEszDVqh.mjs} +10 -10
  20. package/lib/{api-CUyBdNba.mjs.map → api-BEszDVqh.mjs.map} +1 -1
  21. package/lib/{asset-loader.worker-C_5RQYHp.mjs → asset-loader.worker-eqHzNAwa.mjs} +1 -1
  22. package/lib/asset-loader.worker-eqHzNAwa.mjs.map +1 -0
  23. package/lib/{banana-for-scale-B77S3z9R.mjs → banana-for-scale-CiCouumi.mjs} +4 -4
  24. package/lib/{banana-for-scale-B77S3z9R.mjs.map → banana-for-scale-CiCouumi.mjs.map} +1 -1
  25. package/lib/{budgeteer.sw-DxeAvd0v.mjs → budgeteer.sw-BUMElkO1.mjs} +1 -1
  26. package/lib/budgeteer.sw-BUMElkO1.mjs.map +1 -0
  27. package/lib/{common-utils-BiOW17Ao.mjs → common-utils-CCzT4Gnn.mjs} +2 -2
  28. package/lib/{common-utils-BiOW17Ao.mjs.map → common-utils-CCzT4Gnn.mjs.map} +1 -1
  29. package/lib/{component-dimensioning-DPyPzSsa.mjs → component-dimensioning-DWEZ4jZg.mjs} +7 -7
  30. package/lib/{component-dimensioning-DPyPzSsa.mjs.map → component-dimensioning-DWEZ4jZg.mjs.map} +1 -1
  31. package/lib/{component-raycast-helper-RiQIybsX.mjs → component-raycast-helper-D2hyLpdX.mjs} +17 -17
  32. package/lib/{component-raycast-helper-RiQIybsX.mjs.map → component-raycast-helper-D2hyLpdX.mjs.map} +1 -1
  33. package/lib/configurator-BENT3shs.mjs +2 -0
  34. package/lib/{configurator-C_rsqn9l.mjs → configurator-BjLLdSf8.mjs} +16 -16
  35. package/lib/{configurator-C_rsqn9l.mjs.map → configurator-BjLLdSf8.mjs.map} +1 -1
  36. package/lib/{continuous-drawing-helper-C_Yc0uuv.mjs → continuous-drawing-helper-CCuU99Vh.mjs} +3 -3
  37. package/lib/{continuous-drawing-helper-C_Yc0uuv.mjs.map → continuous-drawing-helper-CCuU99Vh.mjs.map} +1 -1
  38. package/lib/{dat.gui.module-BLjoST8r.mjs → dat.gui.module-B2PXSbre.mjs} +1 -1
  39. package/lib/{dat.gui.module-BLjoST8r.mjs.map → dat.gui.module-B2PXSbre.mjs.map} +1 -1
  40. package/lib/{decorate-BAtqSPNS.mjs → decorate-CX1ZGuFY.mjs} +1 -1
  41. package/lib/{decorate-BAtqSPNS.mjs.map → decorate-CX1ZGuFY.mjs.map} +1 -1
  42. package/lib/{dimensioning-helper-D9oxGkry.mjs → dimensioning-helper-CjuQqjBn.mjs} +14 -14
  43. package/lib/{dimensioning-helper-D9oxGkry.mjs.map → dimensioning-helper-CjuQqjBn.mjs.map} +1 -1
  44. package/lib/dimensioning-helper-ImsWfczi.mjs +2 -0
  45. package/lib/{dist-DGov-RNo.mjs → dist-Blgk0vU0.mjs} +1 -1
  46. package/lib/{dist-DGov-RNo.mjs.map → dist-Blgk0vU0.mjs.map} +1 -1
  47. package/lib/{fflate.module-CTnSdWOb.mjs → fflate.module-DTkA0kF8.mjs} +1 -1
  48. package/lib/{fflate.module-CTnSdWOb.mjs.map → fflate.module-DTkA0kF8.mjs.map} +1 -1
  49. package/lib/{glb-viewer-A5vSLFZ5.mjs → glb-viewer-9Ata6ZNJ.mjs} +59 -45
  50. package/lib/glb-viewer-9Ata6ZNJ.mjs.map +1 -0
  51. package/lib/glb-viewer-CGqSGT38.mjs +2 -0
  52. package/lib/{highlight-coordinator-CZEJGeXA.mjs → highlight-coordinator-PyWNABN9.mjs} +3 -3
  53. package/lib/{highlight-coordinator-CZEJGeXA.mjs.map → highlight-coordinator-PyWNABN9.mjs.map} +1 -1
  54. package/lib/{homag-intelligence-BdCDX7LF.mjs → homag-intelligence-BNqPqVgE.mjs} +3 -3
  55. package/lib/{homag-intelligence-BdCDX7LF.mjs.map → homag-intelligence-BNqPqVgE.mjs.map} +1 -1
  56. package/lib/{imos-ix-poc-export-helper-CaXXDNvP.mjs → imos-ix-poc-export-helper-C1LfrtjM.mjs} +3 -3
  57. package/lib/{imos-ix-poc-export-helper-CaXXDNvP.mjs.map → imos-ix-poc-export-helper-C1LfrtjM.mjs.map} +1 -1
  58. package/lib/{kernel-utils-BYEsHVYJ.mjs → kernel-utils-Cn4evFqF.mjs} +2 -2
  59. package/lib/{kernel-utils-BYEsHVYJ.mjs.map → kernel-utils-Cn4evFqF.mjs.map} +1 -1
  60. package/lib/{layer-CIWL5ZW3.mjs → layer-DvZF9ur3.mjs} +1 -1
  61. package/lib/{layer-CIWL5ZW3.mjs.map → layer-DvZF9ur3.mjs.map} +1 -1
  62. package/lib/{main-BUl8iCi1.mjs → main-n3n3jdja.mjs} +1097 -1017
  63. package/lib/{main-BUl8iCi1.mjs.map → main-n3n3jdja.mjs.map} +1 -1
  64. package/lib/{material-viewer-C4SqVaGG.mjs → material-viewer-Cv5Awgwm.mjs} +24 -24
  65. package/lib/{material-viewer-C4SqVaGG.mjs.map → material-viewer-Cv5Awgwm.mjs.map} +1 -1
  66. package/lib/{packages-C9gO5mKk.mjs → packages-B3bi3adH.mjs} +5 -5
  67. package/lib/{packages-C9gO5mKk.mjs.map → packages-B3bi3adH.mjs.map} +1 -1
  68. package/lib/planner-DIT2VvMG.mjs +44 -0
  69. package/lib/{planner-t4QH26WW.mjs.map → planner-DIT2VvMG.mjs.map} +1 -1
  70. package/lib/planner-DaI8ZwEe.mjs +2 -0
  71. package/lib/{plugin-system-COZJdJH_.mjs → plugin-system-FuZpDWqv.mjs} +3 -3
  72. package/lib/{plugin-system-COZJdJH_.mjs.map → plugin-system-FuZpDWqv.mjs.map} +1 -1
  73. package/lib/roomle-headless-setup-CMHahWyC.mjs +612 -0
  74. package/lib/roomle-headless-setup-CMHahWyC.mjs.map +1 -0
  75. package/lib/roomle-headless.d.ts +113 -34
  76. package/lib/roomle-headless.js +433 -84
  77. package/lib/roomle-headless.js.map +1 -1
  78. package/lib/{roomle-renderer-DhLUaHJZ.mjs → roomle-renderer-BR0UKz2y.mjs} +2 -2
  79. package/lib/{roomle-renderer-DhLUaHJZ.mjs.map → roomle-renderer-BR0UKz2y.mjs.map} +1 -1
  80. package/lib/roomle-sdk.d.ts +37 -9
  81. package/lib/roomle-sdk.js +8 -8
  82. package/lib/{roomle-webgpu-renderer-CRGcdahn.mjs → roomle-webgpu-renderer-fOYJoKJU.mjs} +175 -173
  83. package/lib/roomle-webgpu-renderer-fOYJoKJU.mjs.map +1 -0
  84. package/lib/{scene-renderer-D5CfxCag.mjs → scene-renderer-CAzAJ4pS.mjs} +212 -211
  85. package/lib/{scene-renderer-D5CfxCag.mjs.map → scene-renderer-CAzAJ4pS.mjs.map} +1 -1
  86. package/lib/{script-loader-D6IuUFjw.mjs → script-loader-BAWHLt9q.mjs} +3 -3
  87. package/lib/{script-loader-D6IuUFjw.mjs.map → script-loader-BAWHLt9q.mjs.map} +1 -1
  88. package/lib/{shading-settings-BEMqEGzB.mjs → shading-settings-BPpgt0Fq.mjs} +101 -146
  89. package/lib/shading-settings-BPpgt0Fq.mjs.map +1 -0
  90. package/lib/{stats-helper-DAfbSToL.mjs → stats-helper-CqiTcJuc.mjs} +3 -3
  91. package/lib/{stats-helper-DAfbSToL.mjs.map → stats-helper-CqiTcJuc.mjs.map} +1 -1
  92. package/lib/{svgo.browser-DYieWzyw.mjs → svgo.browser-EB1zkPfG.mjs} +1 -1
  93. package/lib/{svgo.browser-DYieWzyw.mjs.map → svgo.browser-EB1zkPfG.mjs.map} +1 -1
  94. package/lib/{three.core-DjCexDCp.mjs → three.core-CmmDf_BZ.mjs} +73 -38
  95. package/lib/{three.core-DjCexDCp.mjs.map → three.core-CmmDf_BZ.mjs.map} +1 -1
  96. package/lib/{three.module-CuRNgijg.mjs → three.module-BkkLbA6n.mjs} +857 -857
  97. package/lib/{three.module-CuRNgijg.mjs.map → three.module-BkkLbA6n.mjs.map} +1 -1
  98. package/lib/three.tsl-8HwZJGJ8.mjs +2 -0
  99. package/lib/{three.tsl-BIBER8nk.mjs → three.tsl-Cw9IeeKW.mjs} +64 -52
  100. package/lib/{three.tsl-BIBER8nk.mjs.map → three.tsl-Cw9IeeKW.mjs.map} +1 -1
  101. package/lib/three.webgpu-Bw4_U6Q1.mjs +3 -0
  102. package/lib/{three.webgpu-Bz4Xtk0X.mjs → three.webgpu-DL5l_0xI.mjs} +1742 -1728
  103. package/lib/{three.webgpu-Bz4Xtk0X.mjs.map → three.webgpu-DL5l_0xI.mjs.map} +1 -1
  104. package/lib/{threejs-utils-Dpvg3Czz.mjs → threejs-utils-PRhv_eX3.mjs} +63 -63
  105. package/lib/{threejs-utils-Dpvg3Czz.mjs.map → threejs-utils-PRhv_eX3.mjs.map} +1 -1
  106. package/lib/{tools-core-BDOFf9le.mjs → tools-core-CmrXkw3J.mjs} +3 -3
  107. package/lib/{tools-core-BDOFf9le.mjs.map → tools-core-CmrXkw3J.mjs.map} +1 -1
  108. package/lib/webgpu-wkXAkJ6p.mjs +5084 -0
  109. package/lib/webgpu-wkXAkJ6p.mjs.map +1 -0
  110. package/lib/workers-YGDNEfpU.mjs +6 -0
  111. package/lib/{workers-DcgrO-z7.mjs.map → workers-YGDNEfpU.mjs.map} +1 -1
  112. package/package.json +5 -4
  113. package/lib/asset-loader.worker-C_5RQYHp.mjs.map +0 -1
  114. package/lib/budgeteer.sw-DxeAvd0v.mjs.map +0 -1
  115. package/lib/configurator-BBlZVIId.mjs +0 -2
  116. package/lib/dimensioning-helper-Bzf1sbpy.mjs +0 -2
  117. package/lib/glb-viewer-A5vSLFZ5.mjs.map +0 -1
  118. package/lib/glb-viewer-B4ILlvw0.mjs +0 -2
  119. package/lib/planner-49YwTdiA.mjs +0 -2
  120. package/lib/planner-t4QH26WW.mjs +0 -44
  121. package/lib/roomle-headless-setup-Cc8EAnVe.mjs +0 -1204
  122. package/lib/roomle-headless-setup-Cc8EAnVe.mjs.map +0 -1
  123. package/lib/roomle-webgpu-renderer-CRGcdahn.mjs.map +0 -1
  124. package/lib/shading-settings-BEMqEGzB.mjs.map +0 -1
  125. package/lib/three.tsl-Q-21y0pO.mjs +0 -2
  126. package/lib/three.webgpu-dtN0kDKB.mjs +0 -3
  127. package/lib/workers-DcgrO-z7.mjs +0 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roomle-headless-setup-CMHahWyC.mjs","names":["#registered"],"sources":["../../packages/headless-core/src/headless-webgpu-queue-polyfill.ts","../../packages/headless-core/src/setup-environment.ts"],"sourcesContent":["/// <reference types=\"@webgpu/types\" />\n// Headless polyfill for GPUQueue.copyExternalImageToTexture (bun-webgpu stubs it).\n// See .agents/headless-sdk.md § copyExternalImageToTexture polyfill.\n\nimport type { RawImagePixels } from '../../common-core/src/common-interfaces';\n\ninterface NativeContext2d {\n clearRect(x: number, y: number, width: number, height: number): void;\n drawImage(\n image: unknown,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void;\n getImageData(\n x: number,\n y: number,\n width: number,\n height: number,\n ): { data: ArrayBufferView };\n}\n\ninterface NativeCanvas {\n width: number;\n height: number;\n getContext(contextId: '2d'): NativeContext2d | null;\n}\n\ninterface ImageDataSource {\n data: ArrayBufferView;\n width: number;\n height: number;\n}\n\ninterface CanvasSource {\n width?: number;\n height?: number;\n getContext(contextId: '2d'): NativeContext2d | null;\n}\n\ninterface DrawableSource {\n width?: number;\n height?: number;\n videoWidth?: number;\n videoHeight?: number;\n}\n\nexport interface QueuePolyfillHelpers {\n createNativeCanvas: (width: number, height: number) => NativeCanvas;\n // The headless <img> proxy stores the real napi Image under a symbol; unwrap\n // it so drawImage runs native-to-native.\n unwrapImage?: (source: unknown) => unknown;\n}\n\nconst asUint8Array = (view: ArrayBufferView): Uint8Array =>\n view instanceof Uint8Array\n ? view\n : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);\n\nconst toExtentSize = (\n size: GPUExtent3DStrict,\n): { width: number; height: number } => {\n if (Array.isArray(size)) {\n return { width: size[0] ?? 0, height: size[1] ?? 0 };\n }\n const { width, height } = size as GPUExtent3DDictStrict;\n return { width: width ?? 0, height: height ?? 0 };\n};\n\nconst isImageDataSource = (source: object): source is ImageDataSource =>\n ArrayBuffer.isView((source as ImageDataSource).data) &&\n typeof (source as ImageDataSource).width === 'number' &&\n typeof (source as ImageDataSource).height === 'number';\n\nconst isCanvasSource = (source: object): source is CanvasSource =>\n typeof (source as CanvasSource).getContext === 'function';\n\nconst flipRowsRGBA = (\n data: Uint8Array,\n width: number,\n height: number,\n): Uint8Array => {\n const rowBytes = width * 4;\n const flipped = new Uint8Array(rowBytes * height);\n for (let row = 0; row < height; row++) {\n flipped.set(\n data.subarray((height - 1 - row) * rowBytes, (height - row) * rowBytes),\n row * rowBytes,\n );\n }\n return flipped;\n};\n\nconst premultiplyAlphaRGBA = (data: Uint8Array): void => {\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3]!;\n if (alpha === 255) {\n continue;\n }\n const factor = alpha / 255;\n data[i] = Math.round(data[i]! * factor);\n data[i + 1] = Math.round(data[i + 1]! * factor);\n data[i + 2] = Math.round(data[i + 2]! * factor);\n }\n};\n\nconst swizzleRedBlueRGBA = (data: Uint8Array): void => {\n for (let i = 0; i < data.length; i += 4) {\n const red = data[i]!;\n data[i] = data[i + 2]!;\n data[i + 2] = red;\n }\n};\n\n// Overrides unconditionally: upstream stubs the method with a fatal error, so\n// there is nothing to feature-detect.\nexport function installCopyExternalImageToTexturePolyfill(\n device: GPUDevice | null | undefined,\n helpers: QueuePolyfillHelpers,\n): void {\n const queue = device?.queue;\n if (!queue || typeof queue.writeTexture !== 'function') {\n return;\n }\n\n // Pooled scratch canvas, reused across uploads instead of one per call.\n let scratchCanvas: NativeCanvas | null = null;\n const getScratchContext = (\n width: number,\n height: number,\n ): NativeContext2d | null => {\n if (!scratchCanvas) {\n scratchCanvas = helpers.createNativeCanvas(width, height);\n } else {\n // Assigning width/height resets the backing store, so only on a change.\n if (scratchCanvas.width !== width) {\n scratchCanvas.width = width;\n }\n if (scratchCanvas.height !== height) {\n scratchCanvas.height = height;\n }\n }\n const context = scratchCanvas.getContext('2d');\n // Resizing resets the canvas; clear defensively for same-size reuse so a\n // smaller-than-canvas draw never leaks the previous upload's pixels.\n context?.clearRect(0, 0, width, height);\n return context;\n };\n\n const extractImagePixels = (\n source: unknown,\n fallbackWidth: number,\n fallbackHeight: number,\n ): RawImagePixels | null => {\n if (!source || typeof source !== 'object') {\n return null;\n }\n\n if (isImageDataSource(source)) {\n return {\n data: asUint8Array(source.data),\n width: source.width,\n height: source.height,\n };\n }\n\n const drawable = source as DrawableSource;\n const width =\n (drawable.width ?? drawable.videoWidth ?? fallbackWidth) | 0 || 0;\n const height =\n (drawable.height ?? drawable.videoHeight ?? fallbackHeight) | 0 || 0;\n if (width <= 0 || height <= 0) {\n return null;\n }\n\n if (isCanvasSource(source)) {\n const canvasContext = source.getContext('2d');\n if (canvasContext) {\n const imageData = canvasContext.getImageData(0, 0, width, height);\n return { data: asUint8Array(imageData.data), width, height };\n }\n }\n\n const scratchContext = getScratchContext(width, height);\n if (!scratchContext) {\n return null;\n }\n scratchContext.drawImage(\n helpers.unwrapImage ? helpers.unwrapImage(source) : source,\n 0,\n 0,\n width,\n height,\n );\n const imageData = scratchContext.getImageData(0, 0, width, height);\n return { data: asUint8Array(imageData.data), width, height };\n };\n\n queue.copyExternalImageToTexture = (\n source: GPUCopyExternalImageSourceInfo,\n destination: GPUCopyExternalImageDestInfo,\n copySize: GPUExtent3DStrict,\n ): undefined => {\n const size = toExtentSize(copySize);\n const extracted = extractImagePixels(\n source?.source,\n size.width,\n size.height,\n );\n if (!extracted) {\n throw new Error(\n '[headless] copyExternalImageToTexture: could not read pixels from ' +\n \"source. Expected ImageData, a canvas exposing getContext('2d'), \" +\n 'or a drawable image.',\n );\n }\n\n const { width, height } = extracted;\n // Fall back to the source dimensions when a copySize dimension is absent.\n const copyWidth = Math.min(width, size.width || width);\n const copyHeight = Math.min(height, size.height || height);\n\n let data = extracted.data;\n let isPrivateCopy = false;\n\n if (source?.flipY) {\n data = flipRowsRGBA(data, width, height);\n isPrivateCopy = true;\n }\n\n // getImageData is straight-alpha RGBA; premultiply / bgra-swizzle to match\n // the destination as requested (bgra is rare, headless prefers rgba8unorm).\n const needsPremultiply = destination?.premultipliedAlpha === true;\n const format = destination?.texture?.format;\n const needsSwizzle =\n typeof format === 'string' && format.startsWith('bgra');\n\n if (needsPremultiply || needsSwizzle) {\n if (!isPrivateCopy) {\n data = new Uint8Array(data);\n isPrivateCopy = true;\n }\n if (needsPremultiply) {\n premultiplyAlphaRGBA(data);\n }\n if (needsSwizzle) {\n swizzleRedBlueRGBA(data);\n }\n }\n\n queue.writeTexture(\n {\n texture: destination.texture,\n mipLevel: destination.mipLevel ?? 0,\n origin: destination.origin ?? { x: 0, y: 0, z: 0 },\n aspect: destination.aspect ?? 'all',\n },\n data,\n { offset: 0, bytesPerRow: width * 4, rowsPerImage: height },\n { width: copyWidth, height: copyHeight, depthOrArrayLayers: 1 },\n );\n return undefined;\n };\n}\n","/// <reference types=\"@webgpu/types\" />\nimport { createRequire } from 'module';\nimport type { IOptionalBrowserSettings } from 'happy-dom';\nimport type { createGPUInstance } from 'bun-webgpu';\nimport { installCopyExternalImageToTexturePolyfill } from './headless-webgpu-queue-polyfill';\n\ntype BunWebGpuInstance = ReturnType<typeof createGPUInstance>;\n\nconst napiCanvas = await import('@napi-rs/canvas').catch((): null => null);\nconst happyDom = await import('happy-dom').catch((): null => null);\n\nif (!napiCanvas) {\n throw new Error(\n '[headless] @napi-rs/canvas is required for headless setup. ' +\n 'Install it (it is an optionalDependency of @roomle/web-sdk)',\n );\n}\n\nif (!happyDom) {\n throw new Error(\n '[headless] happy-dom is required for headless setup. ' +\n 'Install it (it is an optionalDependency of @roomle/web-sdk)',\n );\n}\n\nconst {\n createCanvas: createNapiCanvas,\n Image: NapiImage,\n ImageData: NapiImageData,\n} = napiCanvas;\nconst { GlobalWindow, PropertySymbol } = happyDom;\n\n// Network APIs and built-ins that happy-dom must not override\n// We rely on Node/Bun native implementations for these.\nconst IGNORE_LIST = [\n 'fetch',\n 'Response',\n 'Request',\n 'Headers',\n 'AbortController',\n 'AbortSignal',\n 'ReadableStream',\n 'WritableStream',\n 'TransformStream',\n 'FormData',\n 'URL',\n 'URLSearchParams',\n 'Blob',\n 'File',\n 'constructor',\n 'undefined',\n 'NaN',\n 'global',\n 'globalThis',\n];\n\nclass GlobalRegistrator {\n static #registered: {\n [key: string | symbol]: PropertyDescriptor | null;\n } | null = null;\n\n public static get isRegistered(): boolean {\n return this.#registered !== null;\n }\n\n public static register(options?: {\n width?: number;\n height?: number;\n url?: string;\n settings?: IOptionalBrowserSettings;\n }): void {\n if (this.#registered !== null) {\n throw new Error(\n 'Failed to register. Happy DOM has already been globally registered.',\n );\n }\n\n const window = new GlobalWindow({\n ...options,\n console: globalThis.console,\n });\n\n this.#registered = {};\n\n const propertyDescriptors = Object.getOwnPropertyDescriptors(window);\n\n for (const key of Object.keys(propertyDescriptors)) {\n if (!IGNORE_LIST.includes(key)) {\n const windowPropertyDescriptor = propertyDescriptors[key];\n const globalPropertyDescriptor = Object.getOwnPropertyDescriptor(\n globalThis,\n key,\n );\n\n if (\n globalPropertyDescriptor?.value === undefined ||\n globalPropertyDescriptor?.value !== windowPropertyDescriptor?.value\n ) {\n this.#registered[key] = globalPropertyDescriptor || null;\n\n if (windowPropertyDescriptor?.value === window) {\n (window as any)[key] = globalThis;\n windowPropertyDescriptor.value = globalThis;\n }\n\n Object.defineProperty(globalThis, key, {\n ...windowPropertyDescriptor,\n configurable: true,\n });\n }\n }\n }\n\n const propertySymbols = Object.getOwnPropertySymbols(window);\n\n for (const key of propertySymbols) {\n const propertyDescriptor = Object.getOwnPropertyDescriptor(window, key);\n this.#registered[key] = null;\n\n if (propertyDescriptor!.value === window) {\n (window as any)[key] = globalThis;\n propertyDescriptor!.value = globalThis;\n }\n\n Object.defineProperty(globalThis, key, {\n ...propertyDescriptor,\n configurable: true,\n });\n }\n\n (globalThis as any).document[PropertySymbol.defaultView] = globalThis;\n }\n\n public static async unregister(): Promise<void> {\n if (this.#registered === null) {\n throw new Error(\n 'Failed to unregister. Happy DOM has not previously been globally registered.',\n );\n }\n\n const happyDOM = (globalThis as any).happyDOM;\n\n for (const key of Object.keys(this.#registered)) {\n if (this.#registered[key] !== null) {\n Object.defineProperty(globalThis, key, this.#registered[key]!);\n } else {\n delete (globalThis as any)[key];\n }\n }\n\n this.#registered = null;\n\n if (happyDOM) {\n await happyDOM.close();\n }\n }\n}\n\n// Resolves the bundle directory so root-relative Vite asset URLs like\n// \"/default_256.exr\" can be mapped to real files next to the bundle.\n// We use a bare `import.meta.url` (not wrapped in `new URL(…, import.meta.url)`)\n// because Rollup replaces the latter pattern with a data: URI at build time.\nconst _setupRequire = createRequire(import.meta.url);\nconst _fsSync = _setupRequire('fs');\nconst _pathSync = _setupRequire('path');\nconst _urlSync = _setupRequire('url');\nlet _bundleDir = '';\ntry {\n _bundleDir = _pathSync.dirname(_urlSync.fileURLToPath(import.meta.url));\n} catch {\n // Not a file URL\n}\n\n// Maps a root-relative path (e.g. \"/default_256.exr\") to a local file path\n// next to the bundle. Returns null when the file doesn't exist. */\nconst _resolveRootRelative = (urlPath: string): string | null => {\n if (!_bundleDir || !urlPath.startsWith('/') || urlPath.startsWith('//')) {\n return null;\n }\n const candidate = _pathSync.join(_bundleDir, urlPath);\n return _fsSync.existsSync(candidate) ? candidate : null;\n};\n\n// Patch globalThis.fetch to handle:\n// 1. file:// URLs, Node/undici rejects these natively; Bun accepts them.\n// 2. Root-relative URLs (e.g. \"/default_256.exr\") emitted by Vite's ?url\n// transform; no web server in Node means native fetch throws \"Invalid URL\".\nconst _nativeFetch = globalThis.fetch;\n// @ts-ignore -- this conflicts with DOM types.\nglobalThis.fetch = async (\n input: string | URL | Request,\n init?: RequestInit,\n): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : (input as any).url ?? String(input);\n\n if (url.startsWith('file://')) {\n const buffer = _fsSync.readFileSync(_urlSync.fileURLToPath(url));\n return new Response(buffer, {\n status: 200,\n headers: { 'Content-Type': 'application/octet-stream' },\n });\n }\n\n if (url.startsWith('/') && !url.startsWith('//')) {\n const localPath = _resolveRootRelative(url);\n if (localPath) {\n return new Response(_fsSync.readFileSync(localPath), {\n status: 200,\n headers: { 'Content-Type': 'application/octet-stream' },\n });\n }\n console.warn(\n `[headless] fetch: root-relative asset not found: ${url} (bundle dir: ${_bundleDir || 'unknown'})`,\n );\n return new Response(null, { status: 404, statusText: 'Not Found' });\n }\n\n return _nativeFetch.call(this, input, init);\n};\n\n// Proxy-wrap globalThis.Request so root-relative paths are converted to\n// file:// URLs before the constructor validates them. Three.js's FileLoader\n// calls `new Request(url)` before fetch(), so our fetch patch above is never\n// reached if the constructor throws first.\nconst _OrigRequest = (globalThis as any).Request;\nif (_OrigRequest) {\n (globalThis as any).Request = new Proxy(_OrigRequest, {\n construct(Target: any, [input, init]: [any, any]) {\n if (\n typeof input === 'string' &&\n input.startsWith('/') &&\n !input.startsWith('//')\n ) {\n const localPath =\n _resolveRootRelative(input) ??\n _pathSync.join(_bundleDir || '/tmp', input);\n input = _urlSync.pathToFileURL(localPath).href;\n }\n return Reflect.construct(\n Target,\n init !== undefined ? [input, init] : [input],\n Target,\n );\n },\n get(target: any, prop: string | symbol) {\n return target[prop];\n },\n });\n}\n\nGlobalRegistrator.register();\n\n// Throttle requestAnimationFrame to ~60fps (happy-dom's is unthrottled).\n// See .agents/headless-sdk.md § requestAnimationFrame is throttled to ~60 fps.\n{\n const RAF_INTERVAL_MS = 1000 / 60;\n (globalThis as any).requestAnimationFrame = (\n callback: FrameRequestCallback,\n ): number =>\n setTimeout(\n () => callback(performance.now()),\n RAF_INTERVAL_MS,\n ) as unknown as number;\n (globalThis as any).cancelAnimationFrame = (handle: number): void =>\n clearTimeout(handle as unknown as ReturnType<typeof setTimeout>);\n}\n\n// Provide napi-rs/canvas ImageData globally as GLTFExporter uses `new ImageData(…)`\nif (typeof (globalThis as any).ImageData === 'undefined') {\n (globalThis as any).ImageData = NapiImageData;\n}\n\n// FileReader shim because happy-dom's FileReader only accepts happy-dom Blobs\nclass NodeFileReader {\n public result: ArrayBuffer | string | null = null;\n public error: unknown = null;\n public readyState: number = 0;\n public onload: ((ev: any) => void) | null = null;\n public onloadend: ((ev: any) => void) | null = null;\n public onerror: ((ev: any) => void) | null = null;\n\n readAsArrayBuffer(blob: Blob): void {\n this.readyState = 1;\n blob.arrayBuffer().then(\n (buf) => {\n this.result = buf;\n this.readyState = 2;\n const ev = { target: this };\n this.onload?.(ev);\n this.onloadend?.(ev);\n },\n (err) => {\n this.error = err;\n this.readyState = 2;\n const ev = { target: this };\n this.onerror?.(ev);\n this.onloadend?.(ev);\n },\n );\n }\n\n readAsDataURL(blob: Blob): void {\n this.readyState = 1;\n blob.arrayBuffer().then(\n (buf) => {\n const b64 = Buffer.from(buf).toString('base64');\n const type = (blob as any).type || 'application/octet-stream';\n this.result = `data:${type};base64,${b64}`;\n this.readyState = 2;\n const ev = { target: this };\n this.onload?.(ev);\n this.onloadend?.(ev);\n },\n (err) => {\n this.error = err;\n this.readyState = 2;\n const ev = { target: this };\n this.onerror?.(ev);\n this.onloadend?.(ev);\n },\n );\n }\n\n readAsText(blob: Blob): void {\n this.readyState = 1;\n blob.text().then(\n (text) => {\n this.result = text;\n this.readyState = 2;\n const ev = { target: this };\n this.onload?.(ev);\n this.onloadend?.(ev);\n },\n (err) => {\n this.error = err;\n this.readyState = 2;\n const ev = { target: this };\n this.onerror?.(ev);\n this.onloadend?.(ev);\n },\n );\n }\n\n abort(): void {}\n}\n\n(globalThis as any).FileReader = NodeFileReader;\n\n// Symbol for storing the underlying NapiImage on wrapper elements so that\n// the patched 2d context can unwrap it before calling the native drawImage.\nconst NAPI_IMAGE = Symbol('napiImage');\n\ninterface NapiImageWrapper {\n [NAPI_IMAGE]?: CanvasImageSource;\n}\n\nconst origCreateElement = document.createElement.bind(document);\nconst origCreateElementNS = document.createElementNS.bind(document);\n\n// Patches a napi-rs/canvas 2d context so drawImage() unwraps NAPI_IMAGE wrappers.\n// Skia's native drawImage only accepts its own Image/Canvas types.\nconst patchContext2d = (\n ctx: CanvasRenderingContext2D,\n): CanvasRenderingContext2D => {\n const origDrawImage = ctx.drawImage.bind(ctx);\n ctx.drawImage = (image: CanvasImageSource, ...args: any[]) => {\n const real = (image as NapiImageWrapper | undefined)?.[NAPI_IMAGE] ?? image;\n return origDrawImage(real, ...args);\n };\n return ctx;\n};\n\nlet headlessWebGpuMaxTextureDimension: number | null = null;\n\n// The GPU's granted `maxTextureDimension2D`, or null before device creation\nexport const getHeadlessWebGpuMaxTextureDimension = (): number | null => {\n return headlessWebGpuMaxTextureDimension;\n};\n\nconst SPEC_DEFAULT_MAX_TEXTURE_DIMENSION = 8192;\nconst LIMIT_VALIDATION_HINT = 0x7fffffff;\n\n// Shadows the adapter's limits with mis-read-0 fields bumped high, for\n// bun-webgpu's requiredLimits validation ONLY (see .agents/headless-sdk.md\n// § Adaptive maxTextureDimension2D). Values sent to Dawn are unchanged; without\n// this the device request throws and three falls back to its null WebGL2 backend.\nconst shadowZeroLimits = (adapter: GPUAdapter): (() => void) => {\n const realLimits = adapter.limits;\n const validationHints: Record<string, number> = {};\n for (const limitName in realLimits) {\n const limit = (realLimits as unknown as Record<string, number>)[limitName]!;\n validationHints[limitName] = limit === 0 ? LIMIT_VALIDATION_HINT : limit;\n }\n Object.defineProperty(adapter, 'limits', {\n value: validationHints,\n configurable: true,\n });\n return () =>\n Object.defineProperty(adapter, 'limits', {\n value: realLimits,\n configurable: true,\n });\n};\n\n// Strips an empty requiredLimits ({} from three) so bun-webgpu doesn't\n// pack+validate the full limits struct.\nconst withoutEmptyRequiredLimits = (\n descriptor: GPUDeviceDescriptor | undefined,\n): GPUDeviceDescriptor => {\n const { requiredLimits, ...rest } = descriptor ?? {};\n return requiredLimits && Object.keys(requiredLimits).length > 0\n ? { ...descriptor }\n : (rest as GPUDeviceDescriptor);\n};\n\nconst rememberGrantedTextureDimension = (\n device: GPUDevice,\n adapterMaxTextureDimension: number | undefined,\n) => {\n const granted = device.limits?.maxTextureDimension2D;\n if (typeof granted === 'number' && granted > 0) {\n headlessWebGpuMaxTextureDimension = granted;\n } else if (typeof adapterMaxTextureDimension === 'number') {\n headlessWebGpuMaxTextureDimension = adapterMaxTextureDimension;\n }\n};\n\nconst patchRequestDevice = (adapter: GPUAdapter) => {\n const requestDevice = adapter.requestDevice.bind(adapter);\n const adapterMaxTextureDimension = adapter.limits?.maxTextureDimension2D;\n // Raise the texture limit only when the adapter beats the spec default;\n // otherwise keep the no-requiredLimits path.\n const canRaiseTextureLimit =\n typeof adapterMaxTextureDimension === 'number' &&\n adapterMaxTextureDimension > SPEC_DEFAULT_MAX_TEXTURE_DIMENSION;\n\n const requestDeviceWithShadowedLimits = (\n request: GPUDeviceDescriptor,\n ): Promise<GPUDevice | null> => {\n if (!canRaiseTextureLimit) {\n return requestDevice(request);\n }\n const restoreLimits = shadowZeroLimits(adapter);\n try {\n return requestDevice(request);\n } finally {\n restoreLimits();\n }\n };\n\n adapter.requestDevice = async (descriptor?: GPUDeviceDescriptor) => {\n // Only maxTextureDimension2D is set; every other limit stays unset so it\n // packs the spec default and reaches Dawn as before.\n const request = canRaiseTextureLimit\n ? {\n ...descriptor,\n requiredLimits: {\n ...(descriptor?.requiredLimits ?? {}),\n maxTextureDimension2D: adapterMaxTextureDimension,\n },\n }\n : withoutEmptyRequiredLimits(descriptor);\n\n const device = await requestDeviceWithShadowedLimits(\n request as GPUDeviceDescriptor,\n );\n if (!device) {\n throw new Error(\n '[headless] WebGPU adapter returned no device (adapter already invalid)',\n );\n }\n\n rememberGrantedTextureDimension(device, adapterMaxTextureDimension);\n installCopyExternalImageToTexturePolyfill(device, {\n createNativeCanvas: createNapiCanvas,\n unwrapImage: (source: unknown) =>\n (source as NapiImageWrapper | undefined)?.[NAPI_IMAGE] ?? source,\n });\n return device;\n };\n};\n\n// Default to the discrete GPU (Dawn may otherwise pick an iGPU/CPU adapter);\n// an explicit powerPreference still wins.\nconst patchRequestAdapter = (gpu: BunWebGpuInstance) => {\n const requestAdapter = gpu.requestAdapter.bind(gpu);\n gpu.requestAdapter = async (options?: GPURequestAdapterOptions) => {\n const adapterOptions: GPURequestAdapterOptions = { ...options };\n if (!adapterOptions.powerPreference) {\n adapterOptions.powerPreference = 'high-performance';\n }\n const adapter = await requestAdapter(adapterOptions);\n if (adapter?.info) {\n console.log(\n `[headless] WebGPU adapter: ${adapter.info.vendor ?? '?'} ${adapter.info.device ?? ''} (${adapter.info.description ?? ''})`,\n );\n }\n if (adapter && typeof adapter.requestDevice === 'function') {\n patchRequestDevice(adapter);\n }\n return adapter;\n };\n};\n\nconst installNavigatorGpu = (gpu: BunWebGpuInstance) => {\n const currentNavigator = (globalThis as any).navigator;\n if (!currentNavigator || typeof currentNavigator !== 'object') {\n Object.defineProperty(globalThis, 'navigator', {\n value: { gpu },\n configurable: true,\n writable: true,\n });\n return;\n }\n try {\n currentNavigator.gpu = gpu;\n } catch {\n Object.defineProperty(currentNavigator, 'gpu', {\n value: gpu,\n configurable: true,\n writable: true,\n });\n }\n};\n\n// Device creation is lazy, so probe the adapter at setup time to have the\n// limit available before the first render.\nconst probeMaxTextureDimension = async (gpu: BunWebGpuInstance) => {\n try {\n const adapter = await gpu.requestAdapter({\n powerPreference: 'high-performance',\n });\n const maxTextureDimension = adapter?.limits?.maxTextureDimension2D;\n if (\n typeof maxTextureDimension === 'number' &&\n maxTextureDimension > 0 &&\n headlessWebGpuMaxTextureDimension == null\n ) {\n headlessWebGpuMaxTextureDimension = maxTextureDimension;\n }\n } catch (error) {\n console.log('[headless] adapter texture-limit probe failed', error);\n }\n};\n\nlet webgpuInstance: BunWebGpuInstance | null = null;\ntry {\n const bunWebgpu = await import('bun-webgpu');\n // Synchronous; loads the native Dawn wrapper from the platform companion\n // package (bun-webgpu-<platform>-<arch>) and returns the navigator.gpu object.\n webgpuInstance = bunWebgpu.createGPUInstance();\n // Install the WebGPU global constructors/enums three.js references.\n bunWebgpu.globals();\n // Prefer rgba8unorm for the offscreen canvas (avoids a full-frame B/R\n // swizzle per readback). See .agents/headless-sdk.md § Preferred canvas format.\n webgpuInstance.getPreferredCanvasFormat = () => 'rgba8unorm';\n // See .agents/headless-sdk.md § Device request workarounds.\n if (typeof webgpuInstance.requestAdapter === 'function') {\n patchRequestAdapter(webgpuInstance);\n }\n installNavigatorGpu(webgpuInstance);\n console.log(\n '[headless] Using bun-webgpu (Dawn FFI) for real WebGPU rendering',\n );\n await probeMaxTextureDimension(webgpuInstance);\n} catch (error) {\n console.log('[headless] bun-webgpu not available, WebGPU disabled', error);\n}\n\n// Spec-defined WebGPU flag constants. Used literally so we don't depend on\n// node-webgpu exposing them as module exports.\nconst GPU_TEXTURE_USAGE_COPY_SRC = 0x01;\nconst GPU_TEXTURE_USAGE_TEXTURE_BINDING = 0x04;\nconst GPU_TEXTURE_USAGE_RENDER_ATTACHMENT = 0x10;\nconst GPU_BUFFER_USAGE_COPY_DST = 0x08;\nconst GPU_BUFFER_USAGE_MAP_READ = 0x01;\nconst GPU_MAP_MODE_READ = 0x01;\n// WebGPU mandates copyTextureToBuffer bytesPerRow be a 256-byte multiple.\nconst READBACK_BYTES_PER_ROW_ALIGN = 256;\n// WebGPU spec-default maxBufferSize (256 MiB), enforced by Dawn\nconst MAX_READBACK_STAGING_BYTES = 256 * 1024 * 1024;\n\n// Fullscreen-triangle blit for readbackScaled() GPU downscale.\n// See .agents/headless-sdk.md § canvas WebGPU context shim.\nconst BLIT_WGSL = /* wgsl */ `\nstruct VSOut {\n @builtin(position) pos: vec4f,\n @location(0) uv: vec2f,\n};\n\n@vertex\nfn vs(@builtin(vertex_index) vertexIndex: u32) -> VSOut {\n var corners = array<vec2f, 3>(\n vec2f(0.0, 0.0),\n vec2f(2.0, 0.0),\n vec2f(0.0, 2.0)\n );\n let xy = corners[vertexIndex];\n var out: VSOut;\n out.pos = vec4f(xy * 2.0 - 1.0, 0.0, 1.0);\n // Clip-space y is up, texture v is down, flip v so the output keeps the\n // source orientation (row 0 = top), matching the direct readback path.\n out.uv = vec2f(xy.x, 1.0 - xy.y);\n return out;\n}\n\n@group(0) @binding(0) var srcTex: texture_2d<f32>;\n@group(0) @binding(1) var srcSampler: sampler;\n\n@fragment\nfn fs(in: VSOut) -> @location(0) vec4f {\n return textureSample(srcTex, srcSampler, in.uv);\n}\n`;\n\ninterface RawFramePixels {\n data: Uint8Array;\n width: number;\n height: number;\n}\n\n// Hand-rolled GPUCanvasContext shim (no swapchain surface, render to an\n// offscreen texture, read back). See .agents/headless-sdk.md § canvas WebGPU context shim.\nconst createHeadlessWebGPUContext = (canvas: HTMLCanvasElement) => {\n let device: GPUDevice | null = null;\n let format: GPUTextureFormat = 'bgra8unorm';\n let configuredUsage: GPUTextureUsageFlags = 0;\n let texture: GPUTexture | null = null;\n let textureWidth = 0;\n let textureHeight = 0;\n let lastFramePixels: Uint8Array | null = null;\n let blitPipeline: GPURenderPipeline | null = null;\n let blitSampler: GPUSampler | null = null;\n let blitTexture: GPUTexture | null = null;\n let blitWidth = 0;\n let blitHeight = 0;\n let stagingBuffer: GPUBuffer | null = null;\n let stagingSize = 0;\n let stagingBusy = false;\n\n // Every caller below runs behind a device check on the public methods.\n const requireDevice = (): GPUDevice => {\n if (!device) {\n throw new Error('[headless] WebGPU canvas context has no device');\n }\n return device;\n };\n\n const destroyTexture = (textureToDestroy: GPUTexture) => {\n try {\n textureToDestroy.destroy();\n } catch {\n console.error('[webgpu]: Failed to destroy texture image');\n }\n };\n\n const ensureTexture = (): GPUTexture | null => {\n if (!device) {\n return null;\n }\n const gpuDevice = device;\n const width = Math.max(1, canvas.width | 0);\n const height = Math.max(1, canvas.height | 0);\n if (texture && textureWidth === width && textureHeight === height) {\n return texture;\n }\n if (texture) {\n destroyTexture(texture);\n }\n textureWidth = width;\n textureHeight = height;\n // three's configured usage plus COPY_SRC (readback), RENDER_ATTACHMENT\n // (render target), and TEXTURE_BINDING (readbackScaled blit sampling).\n const usage =\n (configuredUsage || 0) |\n GPU_TEXTURE_USAGE_RENDER_ATTACHMENT |\n GPU_TEXTURE_USAGE_COPY_SRC |\n GPU_TEXTURE_USAGE_TEXTURE_BINDING;\n texture = gpuDevice.createTexture({\n size: { width, height, depthOrArrayLayers: 1 },\n format,\n usage,\n });\n return texture;\n };\n\n const destroyBuffer = (bufferToDestroy: GPUBuffer) => {\n try {\n bufferToDestroy.destroy();\n } catch {\n console.error('[webgpu]: Failed to destroy staging buffer');\n }\n };\n\n const createReadbackBuffer = (sizeInBytes: number): GPUBuffer =>\n requireDevice().createBuffer({\n size: sizeInBytes,\n usage: GPU_BUFFER_USAGE_COPY_DST | GPU_BUFFER_USAGE_MAP_READ,\n });\n\n // Reuse the pooled buffer when free and sized to the band; else a fresh\n // throwaway (pool lifetime owned by configure()/unconfigure()).\n const acquireReadbackBuffer = (\n bandBytes: number,\n ): { buffer: GPUBuffer; release: () => void } => {\n if (stagingBusy) {\n const throwawayBuffer = createReadbackBuffer(bandBytes);\n return {\n buffer: throwawayBuffer,\n release: () => destroyBuffer(throwawayBuffer),\n };\n }\n if (!stagingBuffer || stagingSize !== bandBytes) {\n if (stagingBuffer) {\n destroyBuffer(stagingBuffer);\n }\n stagingBuffer = createReadbackBuffer(bandBytes);\n stagingSize = bandBytes;\n }\n stagingBusy = true;\n return {\n buffer: stagingBuffer,\n release: () => {\n stagingBusy = false;\n },\n };\n };\n\n // Dawn returns the texture's native channel order; the wrapped 2d canvas\n // expects RGBA, so bgra8unorm needs a B/R swizzle.\n const swizzleBgraToRgbaInPlace = (pixels: Uint8Array) => {\n for (let i = 0; i < pixels.length; i += 4) {\n const blue = pixels[i]!;\n pixels[i] = pixels[i + 2]!;\n pixels[i + 2] = blue;\n }\n };\n\n const readPixelsFrom = async (\n sourceTexture: GPUTexture,\n width: number,\n height: number,\n ): Promise<Uint8Array | null> => {\n const gpuDevice = requireDevice();\n const tightRowBytes = width * 4;\n const bytesPerRow =\n Math.ceil(tightRowBytes / READBACK_BYTES_PER_ROW_ALIGN) *\n READBACK_BYTES_PER_ROW_ALIGN;\n // Row-band the copy so no single staging buffer exceeds maxBufferSize\n const rowsPerBand = Math.max(\n 1,\n Math.floor((MAX_READBACK_STAGING_BYTES * 0.95) / bytesPerRow),\n );\n const { buffer: readBuffer, release } = acquireReadbackBuffer(\n bytesPerRow * Math.min(rowsPerBand, height),\n );\n\n const pixels = new Uint8Array(tightRowBytes * height);\n try {\n for (\n let bandStartRow = 0;\n bandStartRow < height;\n bandStartRow += rowsPerBand\n ) {\n const bandHeight = Math.min(rowsPerBand, height - bandStartRow);\n const encoder = gpuDevice.createCommandEncoder();\n encoder.copyTextureToBuffer(\n { texture: sourceTexture, origin: { x: 0, y: bandStartRow, z: 0 } },\n { buffer: readBuffer, bytesPerRow, rowsPerImage: bandHeight },\n { width, height: bandHeight, depthOrArrayLayers: 1 },\n );\n gpuDevice.queue.submit([encoder.finish()]);\n await readBuffer.mapAsync(GPU_MAP_MODE_READ);\n const mappedBytes = new Uint8Array(readBuffer.getMappedRange());\n // Copy tight rows out before unmap invalidates the mapped range.\n for (let rowInBand = 0; rowInBand < bandHeight; rowInBand++) {\n pixels.set(\n mappedBytes.subarray(\n rowInBand * bytesPerRow,\n rowInBand * bytesPerRow + tightRowBytes,\n ),\n (bandStartRow + rowInBand) * tightRowBytes,\n );\n }\n readBuffer.unmap();\n }\n } catch (error: unknown) {\n console.warn('[headless] WebGPU readback failed', error);\n return null;\n } finally {\n release();\n }\n\n if (format === 'bgra8unorm') {\n swizzleBgraToRgbaInPlace(pixels);\n }\n return pixels;\n };\n\n // Reads the current canvas texture into lastFramePixels (sync toDataURL\n // consumes that cache).\n const queueReadback = async (): Promise<void> => {\n if (!device || !texture) {\n return;\n }\n const pixels = await readPixelsFrom(texture, textureWidth, textureHeight);\n if (pixels) {\n lastFramePixels = pixels;\n }\n };\n\n const ensureBlitPipeline = (): {\n pipeline: GPURenderPipeline;\n sampler: GPUSampler;\n } => {\n if (blitPipeline && blitSampler) {\n return { pipeline: blitPipeline, sampler: blitSampler };\n }\n const gpuDevice = requireDevice();\n const shaderModule = gpuDevice.createShaderModule({ code: BLIT_WGSL });\n const pipeline = gpuDevice.createRenderPipeline({\n layout: 'auto',\n vertex: { module: shaderModule, entryPoint: 'vs' },\n fragment: {\n module: shaderModule,\n entryPoint: 'fs',\n targets: [{ format }],\n },\n primitive: { topology: 'triangle-list' },\n });\n const sampler = gpuDevice.createSampler({\n magFilter: 'linear',\n minFilter: 'linear',\n });\n blitPipeline = pipeline;\n blitSampler = sampler;\n return { pipeline, sampler };\n };\n\n const ensureBlitTexture = (width: number, height: number): GPUTexture => {\n if (blitTexture && blitWidth === width && blitHeight === height) {\n return blitTexture;\n }\n if (blitTexture) {\n destroyTexture(blitTexture);\n }\n blitWidth = width;\n blitHeight = height;\n blitTexture = requireDevice().createTexture({\n size: { width, height, depthOrArrayLayers: 1 },\n format,\n usage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT | GPU_TEXTURE_USAGE_COPY_SRC,\n });\n return blitTexture;\n };\n\n const releaseCanvasTexture = () => {\n if (texture) {\n destroyTexture(texture);\n texture = null;\n }\n };\n\n const releaseBlitResources = () => {\n blitPipeline = null;\n blitSampler = null;\n if (blitTexture) {\n destroyTexture(blitTexture);\n blitTexture = null;\n }\n };\n\n const releaseStagingBuffer = () => {\n if (stagingBuffer && !stagingBusy) {\n destroyBuffer(stagingBuffer);\n }\n stagingBuffer = null;\n stagingSize = 0;\n stagingBusy = false;\n };\n\n const readCanvasPixels = async (\n sourceTexture: GPUTexture,\n ): Promise<RawFramePixels | null> => {\n const pixels = await readPixelsFrom(\n sourceTexture,\n textureWidth,\n textureHeight,\n );\n return pixels\n ? { data: pixels, width: textureWidth, height: textureHeight }\n : null;\n };\n\n const blitAndReadScaled = async (\n sourceTexture: GPUTexture,\n targetWidth: number,\n targetHeight: number,\n ): Promise<RawFramePixels | null> => {\n const gpuDevice = requireDevice();\n const { pipeline, sampler } = ensureBlitPipeline();\n const blitTarget = ensureBlitTexture(targetWidth, targetHeight);\n const bindGroup = gpuDevice.createBindGroup({\n layout: pipeline.getBindGroupLayout(0),\n entries: [\n { binding: 0, resource: sourceTexture.createView() },\n { binding: 1, resource: sampler },\n ],\n });\n const encoder = gpuDevice.createCommandEncoder();\n const renderPass = encoder.beginRenderPass({\n colorAttachments: [\n {\n view: blitTarget.createView(),\n loadOp: 'clear',\n storeOp: 'store',\n clearValue: { r: 0, g: 0, b: 0, a: 0 },\n },\n ],\n });\n renderPass.setPipeline(pipeline);\n renderPass.setBindGroup(0, bindGroup);\n renderPass.draw(3);\n renderPass.end();\n gpuDevice.queue.submit([encoder.finish()]);\n const pixels = await readPixelsFrom(blitTarget, targetWidth, targetHeight);\n return pixels\n ? { data: pixels, width: targetWidth, height: targetHeight }\n : null;\n };\n\n return {\n canvas,\n __isHeadlessWebGPUContext: true,\n configure(configuration: GPUCanvasConfiguration) {\n device = configuration.device;\n format = configuration.format || format;\n configuredUsage = configuration.usage || 0;\n // Pipeline, textures and staging buffer are format and device bound\n releaseCanvasTexture();\n releaseBlitResources();\n releaseStagingBuffer();\n },\n unconfigure() {\n releaseCanvasTexture();\n releaseBlitResources();\n releaseStagingBuffer();\n device = null;\n },\n getCurrentTexture() {\n // No implicit per-frame readback, export paths read back explicitly\n // (see .agents/headless-sdk.md § canvas WebGPU context shim).\n return ensureTexture();\n },\n // Read the current frame back (single copy + mapAsync) and cache it for\n // the sync toDataURL path.\n async flushReadback(): Promise<Uint8Array | null> {\n if (texture) {\n await queueReadback();\n }\n return lastFramePixels;\n },\n // GPU-downscale the current frame to target size and read back only that\n // (full-size fallback when no scaling). Does not touch the toDataURL cache.\n async readbackScaled(\n targetWidth: number,\n targetHeight: number,\n ): Promise<RawFramePixels | null> {\n if (!device || !texture) {\n return null;\n }\n const canvasTexture = texture;\n if (targetWidth >= textureWidth && targetHeight >= textureHeight) {\n return readCanvasPixels(canvasTexture);\n }\n try {\n return await blitAndReadScaled(\n canvasTexture,\n targetWidth,\n targetHeight,\n );\n } catch (error) {\n console.warn(\n '[headless] WebGPU scaled readback failed, falling back to full-size readback',\n error,\n );\n return readCanvasPixels(canvasTexture);\n }\n },\n getCachedPixels(): Uint8Array | null {\n return lastFramePixels;\n },\n getPreferredFormat() {\n return webgpuInstance?.getPreferredCanvasFormat?.() ?? 'bgra8unorm';\n },\n };\n};\n\nexport type WebGPUMockContext = ReturnType<typeof createHeadlessWebGPUContext>;\n\n// Wraps a napi-rs/canvas Canvas so it behaves like an HTMLCanvasElement.\n// Fakes the prototype chain via Proxy getPrototypeOf (GLTFExporter checks instanceof).\n// The 2d context's drawImage is patched to unwrap NAPI_IMAGE symbols.\nconst createCanvasProxy = (\n width: number = 300,\n height: number = 150,\n): HTMLCanvasElement => {\n const napiCanvas = createNapiCanvas(width, height);\n let cachedCtx: CanvasRenderingContext2D | null = null;\n let cachedWebGpuContext: WebGPUMockContext | null = null;\n\n const proxy = new Proxy(napiCanvas as unknown as HTMLCanvasElement, {\n get(target: any, prop: string | symbol) {\n if (prop === 'style') {\n return target._style ?? (target._style = {});\n }\n if (prop === 'classList') {\n return (\n target._classList ??\n (target._classList = {\n add() {},\n remove() {},\n contains() {\n return false;\n },\n toggle() {},\n })\n );\n }\n if (prop === Symbol.toStringTag) {\n return 'HTMLCanvasElement';\n }\n\n if (prop === 'getContext') {\n return (contextType: string, attrs?: any) => {\n if (contextType === '2d') {\n if (!cachedCtx) {\n cachedCtx = patchContext2d(target.getContext('2d', attrs));\n }\n return cachedCtx;\n }\n if (contextType === 'webgl2' || contextType === 'webgl') {\n // headless-sdk only runs on webGPU\n return null;\n }\n if (contextType === 'webgpu') {\n if (!webgpuInstance) {\n return null;\n }\n if (!cachedWebGpuContext) {\n cachedWebGpuContext = createHeadlessWebGPUContext(proxy);\n }\n return cachedWebGpuContext;\n }\n return target.getContext(contextType, attrs);\n };\n }\n\n if (prop === 'addEventListener' || prop === 'removeEventListener') {\n return () => {};\n }\n\n if (prop === 'getBoundingClientRect') {\n return () => ({\n x: 0,\n y: 0,\n width: target.width,\n height: target.height,\n top: 0,\n left: 0,\n bottom: target.height,\n right: target.width,\n });\n }\n\n // WebGPU: blit the last cached frame into the 2d canvas (lags one frame\n // unless the caller flushed the readback first, as the image renderer does).\n if (prop === 'toDataURL' && cachedWebGpuContext) {\n const webGpuContext = cachedWebGpuContext;\n return (mimeType?: string, quality?: number) => {\n const pixels = webGpuContext.getCachedPixels();\n if (pixels) {\n target\n .getContext('2d')\n .putImageData(\n new NapiImageData(\n new Uint8ClampedArray(pixels.buffer),\n target.width,\n target.height,\n ),\n 0,\n 0,\n );\n }\n return target.toDataURL(mimeType, quality);\n };\n }\n const value = target[prop];\n return typeof value === 'function' ? value.bind(target) : value;\n },\n\n set(target: any, prop: string | symbol, value: any) {\n target[prop] = value;\n return true;\n },\n\n getPrototypeOf() {\n return HTMLCanvasElement.prototype;\n },\n });\n\n return proxy;\n};\n\n// Creates a happy-dom HTMLImageElement backed by a napi-rs/canvas Image so that\n// images are actually decoded and canvas drawImage() works correctly.\nconst createNapiImageElement = (): HTMLImageElement => {\n const el = origCreateElementNS(\n 'http://www.w3.org/1999/xhtml',\n 'img',\n ) as HTMLImageElement;\n const napiImg = new NapiImage();\n\n (el as any)[NAPI_IMAGE] = napiImg;\n\n let currentSrc = '';\n Object.defineProperty(el, 'src', {\n get() {\n return currentSrc;\n },\n set(url: string) {\n currentSrc = url;\n // napi-rs/canvas Image cannot fetch blob: URLs (created by\n // URL.createObjectURL used heavily by GLTFLoader for embedded\n // textures). Resolve them ourselves via fetch (Node ≥18 understands\n // its own blob URL registry) and feed the bytes in as a Buffer.\n if (typeof url === 'string' && url.startsWith('blob:')) {\n fetch(url)\n .then((r) => r.arrayBuffer())\n .then((ab) => {\n // Guard against a later src reassignment racing this resolution.\n if (currentSrc !== url) {\n return;\n }\n napiImg.src = Buffer.from(ab);\n })\n .catch((err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n el.dispatchEvent(\n new ErrorEvent('error', { error, message: error.message }),\n );\n });\n return;\n }\n napiImg.src = url;\n },\n configurable: true,\n });\n\n napiImg.onload = () => {\n Object.defineProperty(el, 'width', {\n value: napiImg.width,\n writable: true,\n configurable: true,\n });\n Object.defineProperty(el, 'height', {\n value: napiImg.height,\n writable: true,\n configurable: true,\n });\n Object.defineProperty(el, 'naturalWidth', {\n value: napiImg.naturalWidth,\n configurable: true,\n });\n Object.defineProperty(el, 'naturalHeight', {\n value: napiImg.naturalHeight,\n configurable: true,\n });\n Object.defineProperty(el, 'complete', { value: true, configurable: true });\n el.dispatchEvent(new Event('load'));\n };\n\n napiImg.onerror = (err: Error) => {\n el.dispatchEvent(\n new ErrorEvent('error', { error: err, message: err?.message }),\n );\n };\n\n return el;\n};\n\ndocument.createElement = (\n tagName: string,\n options?: ElementCreationOptions,\n) => {\n if (tagName.toLowerCase() === 'canvas') {\n return createCanvasProxy();\n }\n return origCreateElement(tagName, options);\n};\n\ndocument.createElementNS = (\n namespaceURI: string | null,\n qualifiedName: string,\n options?: ElementCreationOptions,\n) => {\n const tag = qualifiedName.toLowerCase();\n if (tag === 'canvas') {\n return createCanvasProxy();\n }\n if (tag === 'img') {\n return createNapiImageElement();\n }\n return origCreateElementNS(namespaceURI, qualifiedName, options);\n};\n"],"mappings":"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"}
@@ -237,6 +237,7 @@ export declare interface Base64Image {
237
237
  width: number;
238
238
  height: number;
239
239
  blob: Blob | null;
240
+ pixels?: RawImagePixels;
240
241
  }
241
242
 
242
243
  export declare type Base64String = string;
@@ -2723,6 +2724,11 @@ export declare interface EnvironmentDetails {
2723
2724
  fog: boolean;
2724
2725
  }
2725
2726
 
2727
+ export declare interface EnvironmentMapOptions {
2728
+ intensity?: number;
2729
+ rotation?: number;
2730
+ }
2731
+
2726
2732
  export declare interface EnvironmentMapResult {
2727
2733
  url: string;
2728
2734
  environmentMap: Texture;
@@ -3421,6 +3427,8 @@ export declare interface GetGroupDataForOrderOptions {
3421
3427
  mode?: PlanRoomGeometryMode;
3422
3428
  }
3423
3429
 
3430
+ export declare const getHeadlessWebGpuMaxTextureDimension: () => number | null;
3431
+
3424
3432
  export declare interface GLBRenderWorkerListener {
3425
3433
  onStart(count: number): void;
3426
3434
  onElementFinished(finishedElements: number): void;
@@ -3456,6 +3464,7 @@ export declare class GLBViewerSceneManager extends SceneManager implements GLBRe
3456
3464
  loadStaticItem(id: string, callback?: (percent: number) => void): Promise<void>;
3457
3465
  loadGLB(url: string, automaticallyAdjustCamera?: boolean, scaling?: Vector3, callback?: (percent: number) => void): Promise<void>;
3458
3466
  preparePerspectiveImage(options?: PrepareImageOptions, providedRenderer?: WebGLRenderer): Promise<Base64Image>;
3467
+ private _prepareWebGpuPerspectiveImage;
3459
3468
  adjustCamera(position: Vector3, yaw: number, pitch: number, fov: number): void;
3460
3469
  onStart(count: number): void;
3461
3470
  onElementFinished(itemsFinished: number): void;
@@ -3778,10 +3787,9 @@ export declare class HdrEnvironmentLoader implements Context {
3778
3787
 
3779
3788
  export declare interface HeadlessInitData extends Partial<InitDataDefinition>, Partial<GlobalInitDataDefinition> {
3780
3789
  configuratorId: string;
3781
- /** Canvas width in pixels. Defaults to 1024. */
3782
3790
  width?: number;
3783
- /** Canvas height in pixels. Defaults to 768. */
3784
3791
  height?: number;
3792
+ featureFlags?: Partial<FeatureFlags>;
3785
3793
  }
3786
3794
 
3787
3795
  export declare class HeadlessRoomleSdk {
@@ -3800,7 +3808,10 @@ export declare class HeadlessRoomleSdk {
3800
3808
  private _loadedStaticItemId;
3801
3809
  private _width;
3802
3810
  private _height;
3811
+ private _featureFlags?;
3812
+ private _webGpuConfig?;
3803
3813
  private _loaderQueue;
3814
+ private _skyboxOriginal;
3804
3815
  /**
3805
3816
  * Create and initialize a headless SDK instance.
3806
3817
  * @param initData Configurator id plus optional canvas size and global init fields.
@@ -3808,6 +3819,7 @@ export declare class HeadlessRoomleSdk {
3808
3819
  */
3809
3820
  static create(initData: HeadlessInitData): Promise<HeadlessRoomleSdk>;
3810
3821
  private _init;
3822
+ private _deepMergeHeadlessInitDefaults;
3811
3823
  private _registerHeadlessServices;
3812
3824
  private _makeStubElement;
3813
3825
  /**
@@ -3852,7 +3864,6 @@ export declare class HeadlessRoomleSdk {
3852
3864
  */
3853
3865
  loadStaticItem(staticItemId: string, initData?: Partial<InitDataDefinition>): Promise<void>;
3854
3866
  /**
3855
- * Temporary workaround:
3856
3867
  * GLB-loaded scenes commonly contain hundreds of thousands of triangles
3857
3868
  * (high-poly furniture/cars). With the default 1024² shadow map those
3858
3869
  * triangles cast a self-shadow pattern back onto themselves that the
@@ -3868,21 +3879,6 @@ export declare class HeadlessRoomleSdk {
3868
3879
  * full PBR direct-light shading is preserved.
3869
3880
  */
3870
3881
  private _suppressGlbSelfShadowing;
3871
- /**
3872
- * Auto-dispatch by id shape using the shared id-inferrer utilities, closely
3873
- * following the UI's `SdkConnector.loadObject`:
3874
- * - `component@…` → not supported in headless, throws
3875
- * - `{…}` (config string) → {@link loadConfigurationString}
3876
- * - 0 colons (plan id) → {@link loadPlan}
3877
- * - 1 colon (item id) → {@link loadStaticItem} if RAPI says the
3878
- * item has no `configuration`, otherwise
3879
- * {@link loadConfigurableItemById}
3880
- * - 2+ colons (config id) → {@link loadConfiguration}
3881
- *
3882
- * RAPI lookup failures during the item branch are propagated rather than
3883
- * swallowed — a transient 5xx routing the call to the wrong loader would
3884
- * surface as an unrelated downstream error.
3885
- */
3886
3882
  loadObject(id: string, initData?: Partial<InitDataDefinition>): Promise<void>;
3887
3883
  /**
3888
3884
  * Load a plan into the planner.
@@ -3951,11 +3947,18 @@ export declare class HeadlessRoomleSdk {
3951
3947
  * route through here so plan-mode and config-mode share a single code path.
3952
3948
  */
3953
3949
  private _getActiveSceneManager;
3950
+ setEnvironmentMap(url: string, options?: EnvironmentMapOptions): Promise<void>;
3951
+ resetEnvironmentMap(options?: EnvironmentMapOptions): Promise<void>;
3952
+ setEnvironmentMapOptions(options: EnvironmentMapOptions): void;
3953
+ private _getEnvironmentSceneManager;
3954
+ setSkybox(url: string): Promise<void>;
3955
+ resetSkybox(): void;
3954
3956
  private _export;
3955
3957
  /**
3956
3958
  * Render a perspective screenshot preserving the scene background.
3957
3959
  * @param outputPath Optional file path to write the PNG to.
3958
- * @param options ssaa, size, quality, rotationY.
3960
+ * @param options ssaa, size, quality, rotationY, plus explicit camera
3961
+ * placement (cameraPosition, cameraPitch, cameraYaw, fov).
3959
3962
  * @returns Data URL of the rendered image.
3960
3963
  */
3961
3964
  exportScreenshot(outputPath?: string, options?: ScreenshotExportOptions): Promise<{
@@ -3964,7 +3967,8 @@ export declare class HeadlessRoomleSdk {
3964
3967
  /**
3965
3968
  * Render a framed perspective image of the scene.
3966
3969
  * @param outputPath Optional file path to write the PNG to.
3967
- * @param options ssaa, size, showDimensions, rotationY.
3970
+ * @param options ssaa, size, showDimensions, rotationY, plus explicit
3971
+ * camera placement (cameraPosition, cameraPitch, cameraYaw, fov).
3968
3972
  * @returns Data URL plus final image dimensions.
3969
3973
  */
3970
3974
  exportPerspectiveImage(outputPath?: string, options?: PerspectiveImageExportOptions): Promise<{
@@ -3983,6 +3987,11 @@ export declare class HeadlessRoomleSdk {
3983
3987
  width: number;
3984
3988
  height: number;
3985
3989
  }>;
3990
+ private _resolveTopImage;
3991
+ private _getRenderDimensions;
3992
+ private _resolveImageDataUrl;
3993
+ private _writeDataUrlToFile;
3994
+ private _rawPixelsToPngDataUrl;
3986
3995
  /** Downscale a data URL image to target dimensions using high-quality filtering. */
3987
3996
  private _ssaaDownscale;
3988
3997
  getConfiguratorApi(): RoomleConfigurator<readonly []>;
@@ -4192,10 +4201,18 @@ export declare class IdbManager implements Context {
4192
4201
  }
4193
4202
 
4194
4203
  export declare interface ImageExportOptions {
4195
- /** Supersampling anti-aliasing factor. Defaults to 2. */
4196
4204
  ssaa?: number;
4197
- /** Output image size in pixels. Defaults to 1024. */
4198
4205
  size?: number;
4206
+ width?: number;
4207
+ height?: number;
4208
+ pathTracer?: boolean;
4209
+ pathTracerSamples?: number;
4210
+ pathTracerSettings?: PathTracerSettings;
4211
+ }
4212
+
4213
+ export declare interface ImageFrame {
4214
+ imageDataUrl: string;
4215
+ pixels?: RawImagePixels;
4199
4216
  }
4200
4217
 
4201
4218
  export declare type ImageKeysOnObject = 'perspectiveImage' | 'topImage';
@@ -4206,26 +4223,32 @@ export declare class ImageRenderer implements Context {
4206
4223
  private _imageRenderer;
4207
4224
  private _thumbnailCamera;
4208
4225
  private _globalInitData;
4209
- private _initData;
4226
+ protected _initData: InitData;
4210
4227
  private _css2DRendererManager;
4211
4228
  constructor(creator: string);
4212
4229
  private _getRenderer;
4230
+ protected _createThreejsImageRenderer(): Promise<WebGLRenderer | WebGPURenderer>;
4231
+ protected _createRoomleRenderer(imageRenderer: WebGLRenderer | WebGPURenderer): Promise<GenericRoomleRenderer>;
4213
4232
  switchToPlannerSettings(): Promise<void>;
4214
4233
  switchToConfiguratorSettings(): Promise<void>;
4215
4234
  updateBounds(bounds: Box3): Promise<void>;
4216
4235
  static getCameraTargetForBBox(bbox: Box3, spacing?: number): CameraTarget;
4217
4236
  preparePerspectiveImage(scene: Scene, camera: PerspectiveCamera, cameraTarget?: CameraTarget, options?: PrepareImageOptions, onBeforeRender?: (camera: Camera) => void): Promise<Base64Image>;
4237
+ protected _getRenderSize(options: PrepareImageOptions): {
4238
+ width: number;
4239
+ height: number;
4240
+ };
4241
+ protected _placeCamera(camera: PerspectiveCamera, cameraTarget: CameraTarget | undefined, angleY: number, _options: PrepareImageOptions): void;
4218
4242
  static placeCameraForPerspectiveImage(camera: PerspectiveCamera, cameraTarget: CameraTarget, angleX: number, angleY: number): void;
4219
4243
  prepareTopImage(scene: Scene, cameraTarget: CameraTarget, options?: PrepareImageOptions): Promise<Base64Image>;
4220
- /**
4221
- * Renders a frame with scene.background/fog temporarily nulled.
4222
- * All scene mutations and restoration happen synchronously — no await
4223
- * boundaries — so the main render loop never sees the modified state.
4224
- */
4225
- private _renderImageFrameWithCleanScene;
4244
+ protected _renderImageFrameWithCleanScene(renderInstance: GenericRoomleRenderer | WebGLRenderer | WebGPURenderer, scene: Scene, camera: Camera, options?: PrepareImageOptions): Promise<ImageFrame>;
4226
4245
  private _compositeWithDimensions;
4227
4246
  prepareThumbnailImage(scene: Scene, viewModel: PlanViewModel, cameraBehaviour: CameraBehaviour, perspectiveOffsetCamera: PerspectiveCamera, options: PrepareImageOptionsPlanner | undefined, onAfterRender: () => void): Promise<Base64Image>;
4228
- private _renderImageFrame;
4247
+ protected _renderFrame(renderInstance: GenericRoomleRenderer | WebGLRenderer | WebGPURenderer, scene: Scene, camera: Camera, options: PrepareImageOptions): Promise<ImageFrame>;
4248
+ protected _renderImageFrame(renderInstance: GenericRoomleRenderer | WebGLRenderer | WebGPURenderer, scene: Scene, camera: Camera, options?: PrepareImageOptions): Promise<ImageFrame>;
4249
+ protected _getThreejsRenderer(renderInstance: GenericRoomleRenderer | WebGLRenderer | WebGPURenderer): WebGLRenderer | WebGPURenderer;
4250
+ protected _renderSingleFrame(renderInstance: GenericRoomleRenderer | WebGLRenderer | WebGPURenderer, scene: Scene, camera: Camera): void;
4251
+ protected _readFrameAndClear(renderer: WebGLRenderer | WebGPURenderer, _options?: PrepareImageOptions): Promise<ImageFrame>;
4229
4252
  }
4230
4253
 
4231
4254
  export declare class InitData implements Context, Omit<InitDataDefinition, 'colors'> {
@@ -4477,6 +4500,8 @@ export { isIdAnItem }
4477
4500
 
4478
4501
  export { isIdAPlan }
4479
4502
 
4503
+ export declare const isModelExportOngoing: () => boolean;
4504
+
4480
4505
  export { isPlanSnapshotId }
4481
4506
 
4482
4507
  export declare const isSafari: () => boolean;
@@ -5683,6 +5708,31 @@ export declare class PartListParameterArray extends EmsArray<KernelPartListParam
5683
5708
  export declare class PartListParameterArray_2 extends EmsArray<KernelPartListParameter> {
5684
5709
  }
5685
5710
 
5711
+ export declare type PathTracerExternalDenoiser = (rgba: Float32Array, width: number, height: number) => Promise<Float32Array>;
5712
+
5713
+ export declare interface PathTracerPreviewFrame {
5714
+ png: Uint8Array;
5715
+ width: number;
5716
+ height: number;
5717
+ samples: number;
5718
+ totalSamples: number;
5719
+ }
5720
+
5721
+ export declare type PathTracerPreviewHook = (frame: PathTracerPreviewFrame) => void;
5722
+
5723
+ export declare interface PathTracerPreviewOptions {
5724
+ minIntervalMs?: number;
5725
+ maxSize?: number;
5726
+ }
5727
+
5728
+ export declare type PathTracerProgressHook = (samples: number, totalSamples: number) => void;
5729
+
5730
+ export declare interface PathTracerSettings {
5731
+ bounces?: number;
5732
+ renderScale?: number;
5733
+ denoise?: boolean;
5734
+ }
5735
+
5686
5736
  export declare interface PerspectiveImageExportOptions extends ImageExportOptions, RotatableImageExportOptions {
5687
5737
  showDimensions?: boolean;
5688
5738
  }
@@ -6353,6 +6403,7 @@ export declare class PlannerSceneManager extends ConfiguratorSceneManager implem
6353
6403
  private _sky;
6354
6404
  private _drawingEnabled;
6355
6405
  private _configureInRoom;
6406
+ private _keepWallsAndCeilingVisible;
6356
6407
  private _planId;
6357
6408
  private _preloadScene;
6358
6409
  private _staticItemGuard;
@@ -6401,6 +6452,7 @@ export declare class PlannerSceneManager extends ConfiguratorSceneManager implem
6401
6452
  resetConfiguratorContext(reason: CancelSelectionReasons): void;
6402
6453
  protected _setupScene(offset?: CanvasOffset, transparent?: boolean): Promise<void>;
6403
6454
  protected onBeforeRender(): void;
6455
+ private _forceWallsAndCeiling;
6404
6456
  protected _getInputManager(): InputManager;
6405
6457
  protected createCameraControl(mode: Enumify_2<typeof CAMERA_TYPE>, _offset: CanvasOffset): void;
6406
6458
  startViewing(): void;
@@ -6534,6 +6586,8 @@ export declare class PlannerSceneManager extends ConfiguratorSceneManager implem
6534
6586
  */
6535
6587
  private _show3dDimensions;
6536
6588
  private _show3dDimensionsMulti;
6589
+ private _resolve3dDimensionPlanElementViewModel;
6590
+ private _resolve3dDimensionPlanElementViewModels;
6537
6591
  /**
6538
6592
  * Re-show 3D dimensions for the currently selected objects.
6539
6593
  * Called when snapping state changes to update editability.
@@ -6817,6 +6871,8 @@ export declare class PlanViewModel extends ConfiguratorViewModel implements Plan
6817
6871
  private commonHandlerForEditableAndTopView;
6818
6872
  isChildObjectRootComponent(roomleComponent: RoomleComponent): boolean;
6819
6873
  protected dockComponent(componentDocking: KernelComponentDocking): void;
6874
+ private _isDockingIntoSelection;
6875
+ private _getPlanObjectViewModelForComponent;
6820
6876
  private _isConstructionElement;
6821
6877
  protected _addComponent(component: RoomleComponent): void;
6822
6878
  private _getPlanObjectViewModelForRuntimeId;
@@ -7226,6 +7282,7 @@ export declare interface PrepareImageOptions {
7226
7282
  export declare interface PrepareImageOptionsPlanner extends PrepareImageOptions {
7227
7283
  object?: Object3D;
7228
7284
  externalObjectsOnly?: boolean;
7285
+ showWallsAndCeiling?: boolean;
7229
7286
  }
7230
7287
 
7231
7288
  export declare interface PreviewComponent extends RoomleComponent {
@@ -7976,6 +8033,12 @@ export declare interface RapiUserSetting extends RapiJson {
7976
8033
  user: RapiUser;
7977
8034
  }
7978
8035
 
8036
+ export declare interface RawImagePixels {
8037
+ data: Uint8Array;
8038
+ width: number;
8039
+ height: number;
8040
+ }
8041
+
7979
8042
  export declare const enum RAYCAST_EVENT {
7980
8043
  CLICK_OUTSIDE = 0
7981
8044
  }
@@ -9983,7 +10046,7 @@ export declare class RoomleWebGPURenderer extends RoomleRenderer<WebGPURenderer>
9983
10046
  private _domHelper;
9984
10047
  private _webGpuGroundShadowReflection;
9985
10048
  private _initData;
9986
- constructor(creator: string, renderer: WebGPURenderer, enableSSGI: boolean, outlineColors?: OutlineColors);
10049
+ constructor(creator: string, renderer: WebGPURenderer, enableSSGI: boolean, outlineColors?: OutlineColors, enableTraa?: boolean);
9987
10050
  private enableDefaultRenderPasses;
9988
10051
  addGroundShadowLightAndFloor(scene: Scene): Promise<void>;
9989
10052
  private addRenderPass;
@@ -10027,10 +10090,17 @@ export declare interface RoomLightSourceDistribution {
10027
10090
  export declare class RoomLightSourceDistributionArray extends EmsArray<RoomLightSourceDistribution> {
10028
10091
  }
10029
10092
 
10030
- /** Mixin for image exports that frame the scene from a rotatable camera. */
10031
10093
  export declare interface RotatableImageExportOptions {
10032
- /** Camera rotation around the Y axis, in radians. */
10033
10094
  rotationY?: number;
10095
+ cameraPosition?: {
10096
+ x: number;
10097
+ y: number;
10098
+ z: number;
10099
+ };
10100
+ cameraPitch?: number;
10101
+ cameraYaw?: number;
10102
+ fov?: number;
10103
+ showWallsAndCeiling?: boolean;
10034
10104
  }
10035
10105
 
10036
10106
  /**
@@ -10124,6 +10194,7 @@ export declare interface SceneEvents {
10124
10194
  export declare abstract class SceneManager implements Context, LifeCycleCallbacks, EventListenerObject {
10125
10195
  _creator_: string;
10126
10196
  protected _domHelper: DomHelper;
10197
+ private _modelExportState;
10127
10198
  protected _css2DRendererManager: CSS2DRendererManager;
10128
10199
  protected _scriptLoader: ScriptLoader;
10129
10200
  private _lifeCycleManager;
@@ -10176,6 +10247,7 @@ export declare abstract class SceneManager implements Context, LifeCycleCallback
10176
10247
  addEventListener(_listener: LifeCycleCallbacks): void;
10177
10248
  removeEventListener(_listener: LifeCycleCallbacks): void;
10178
10249
  get lightCache(): LightCache | null;
10250
+ get initData(): InitData;
10179
10251
  getRoomleRenderer(): GenericRoomleRenderer | null;
10180
10252
  getRoomleRendererReady(): Promise<GenericRoomleRenderer | null>;
10181
10253
  getRenderer(): WebGLRenderer | WebGPURenderer | null;
@@ -10282,7 +10354,6 @@ export declare interface SceneSettings {
10282
10354
  export declare type SceneShadingType = Enumify<typeof SCENE_SHADING_TYPES>;
10283
10355
 
10284
10356
  export declare interface ScreenshotExportOptions extends ImageExportOptions, RotatableImageExportOptions {
10285
- /** JPEG quality (0–1). Defaults to 1. */
10286
10357
  quality?: number;
10287
10358
  }
10288
10359
 
@@ -10330,6 +10401,14 @@ export declare const SET_WALL_MATERIAL_TYPE: {
10330
10401
  readonly EXTERIOR: "exterior";
10331
10402
  };
10332
10403
 
10404
+ export declare const setModelExportOngoing: (modelExportOngoing: boolean) => void;
10405
+
10406
+ export declare function setPathTracerExternalDenoiser(fn: PathTracerExternalDenoiser | null): void;
10407
+
10408
+ export declare const setPathTracerPreviewHook: (hook: PathTracerPreviewHook | null, options?: PathTracerPreviewOptions) => void;
10409
+
10410
+ export declare const setPathTracerProgressHook: (hook: PathTracerProgressHook | null) => void;
10411
+
10333
10412
  export declare const enum SHORT_TYPES {
10334
10413
  UNKNOWN = 0,
10335
10414
  PLAN = 1,