@aura3d/rendering 1.5.2 → 2.0.0

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 (196) hide show
  1. package/README.md +8 -2
  2. package/dist/ArchitecturalLighting.d.ts +35 -0
  3. package/dist/ArchitecturalLighting.d.ts.map +1 -0
  4. package/dist/ArchitecturalLighting.js +162 -0
  5. package/dist/ArchitecturalLighting.js.map +1 -0
  6. package/dist/ArchitecturalMaterialCatalog.d.ts +1 -1
  7. package/dist/ArchitecturalMaterialCatalog.d.ts.map +1 -1
  8. package/dist/ArchitecturalMaterialCatalog.js +1 -1
  9. package/dist/ArchitecturalMaterialCatalog.js.map +1 -1
  10. package/dist/ArchitecturalMeasurement.d.ts +27 -0
  11. package/dist/ArchitecturalMeasurement.d.ts.map +1 -0
  12. package/dist/ArchitecturalMeasurement.js +112 -0
  13. package/dist/ArchitecturalMeasurement.js.map +1 -0
  14. package/dist/CanonicalProductScene.d.ts +13 -0
  15. package/dist/CanonicalProductScene.d.ts.map +1 -0
  16. package/dist/CanonicalProductScene.js +78 -0
  17. package/dist/CanonicalProductScene.js.map +1 -0
  18. package/dist/CascadedShadowMaps.d.ts +1 -1
  19. package/dist/CascadedShadowMaps.d.ts.map +1 -1
  20. package/dist/CascadedShadowMaps.js +1 -1
  21. package/dist/CascadedShadowMaps.js.map +1 -1
  22. package/dist/DepthPass.d.ts +1 -1
  23. package/dist/DepthPass.d.ts.map +1 -1
  24. package/dist/DepthPass.js +2 -2
  25. package/dist/DepthPass.js.map +1 -1
  26. package/dist/EnvironmentBackgroundPass.d.ts +1 -1
  27. package/dist/EnvironmentBackgroundPass.d.ts.map +1 -1
  28. package/dist/EnvironmentBackgroundPass.js +2 -2
  29. package/dist/EnvironmentBackgroundPass.js.map +1 -1
  30. package/dist/EnvironmentPlatform.js +2 -2
  31. package/dist/EnvironmentPlatform.js.map +1 -1
  32. package/dist/ForwardPass.d.ts +1 -1
  33. package/dist/ForwardPass.d.ts.map +1 -1
  34. package/dist/ForwardPass.js +24 -10
  35. package/dist/ForwardPass.js.map +1 -1
  36. package/dist/Geometry.d.ts +2 -0
  37. package/dist/Geometry.d.ts.map +1 -1
  38. package/dist/Geometry.js +15 -0
  39. package/dist/Geometry.js.map +1 -1
  40. package/dist/InstancedPBRMaterial.js +1 -1
  41. package/dist/InstancedPBRMaterial.js.map +1 -1
  42. package/dist/InstancedUnlitMaterial.js +1 -1
  43. package/dist/InstancedUnlitMaterial.js.map +1 -1
  44. package/dist/Material.js +2 -2
  45. package/dist/Material.js.map +1 -1
  46. package/dist/MaterialBinding.js +4 -4
  47. package/dist/MaterialBinding.js.map +1 -1
  48. package/dist/MaterialInstance.js +2 -2
  49. package/dist/MaterialInstance.js.map +1 -1
  50. package/dist/MorphUnlitMaterial.js +1 -1
  51. package/dist/MorphUnlitMaterial.js.map +1 -1
  52. package/dist/NormalMappedPBRMaterial.js +1 -1
  53. package/dist/NormalMappedPBRMaterial.js.map +1 -1
  54. package/dist/OceanSurface.d.ts +62 -0
  55. package/dist/OceanSurface.d.ts.map +1 -0
  56. package/dist/OceanSurface.js +240 -0
  57. package/dist/OceanSurface.js.map +1 -0
  58. package/dist/PBRMaterial.d.ts.map +1 -1
  59. package/dist/PBRMaterial.js +3 -3
  60. package/dist/PBRMaterial.js.map +1 -1
  61. package/dist/PortableShaderMaterial.d.ts +60 -0
  62. package/dist/PortableShaderMaterial.d.ts.map +1 -0
  63. package/dist/PortableShaderMaterial.js +223 -0
  64. package/dist/PortableShaderMaterial.js.map +1 -0
  65. package/dist/PostProcessPass.d.ts.map +1 -1
  66. package/dist/PostProcessPass.js +30 -6
  67. package/dist/PostProcessPass.js.map +1 -1
  68. package/dist/ProceduralTexture.d.ts +27 -0
  69. package/dist/ProceduralTexture.d.ts.map +1 -0
  70. package/dist/ProceduralTexture.js +336 -0
  71. package/dist/ProceduralTexture.js.map +1 -0
  72. package/dist/ProductTurntable.d.ts +96 -0
  73. package/dist/ProductTurntable.d.ts.map +1 -0
  74. package/dist/ProductTurntable.js +639 -0
  75. package/dist/ProductTurntable.js.map +1 -0
  76. package/dist/RenderDevice.d.ts +30 -0
  77. package/dist/RenderDevice.d.ts.map +1 -1
  78. package/dist/RenderDevice.js.map +1 -1
  79. package/dist/Renderer.d.ts +3 -2
  80. package/dist/Renderer.d.ts.map +1 -1
  81. package/dist/Renderer.js +23 -9
  82. package/dist/Renderer.js.map +1 -1
  83. package/dist/RendererPostprocessPlan.js +4 -4
  84. package/dist/RendererPostprocessPlan.js.map +1 -1
  85. package/dist/ScreenSpaceLineMaterial.js +1 -1
  86. package/dist/ScreenSpaceLineMaterial.js.map +1 -1
  87. package/dist/ShaderChunks.d.ts.map +1 -1
  88. package/dist/ShaderChunks.js +81 -19
  89. package/dist/ShaderChunks.js.map +1 -1
  90. package/dist/ShaderLibrary.d.ts +2 -78
  91. package/dist/ShaderLibrary.d.ts.map +1 -1
  92. package/dist/ShaderLibrary.js +127 -793
  93. package/dist/ShaderLibrary.js.map +1 -1
  94. package/dist/ShaderLibraryCore.d.ts +100 -0
  95. package/dist/ShaderLibraryCore.d.ts.map +1 -0
  96. package/dist/ShaderLibraryCore.js +800 -0
  97. package/dist/ShaderLibraryCore.js.map +1 -0
  98. package/dist/ShaderModule.d.ts +1 -1
  99. package/dist/ShaderModule.d.ts.map +1 -1
  100. package/dist/ShaderModule.js +4 -2
  101. package/dist/ShaderModule.js.map +1 -1
  102. package/dist/ShadowPass.d.ts +1 -1
  103. package/dist/ShadowPass.d.ts.map +1 -1
  104. package/dist/ShadowPass.js +6 -4
  105. package/dist/ShadowPass.js.map +1 -1
  106. package/dist/SkinnedLitMaterial.js +1 -1
  107. package/dist/SkinnedLitMaterial.js.map +1 -1
  108. package/dist/SkinnedUnlitMaterial.js +1 -1
  109. package/dist/SkinnedUnlitMaterial.js.map +1 -1
  110. package/dist/SpaceEnvironment.d.ts +54 -0
  111. package/dist/SpaceEnvironment.d.ts.map +1 -0
  112. package/dist/SpaceEnvironment.js +116 -0
  113. package/dist/SpaceEnvironment.js.map +1 -0
  114. package/dist/TerrainHeightfield.d.ts +60 -0
  115. package/dist/TerrainHeightfield.d.ts.map +1 -0
  116. package/dist/TerrainHeightfield.js +239 -0
  117. package/dist/TerrainHeightfield.js.map +1 -0
  118. package/dist/TextureBinding.d.ts +14 -0
  119. package/dist/TextureBinding.d.ts.map +1 -1
  120. package/dist/TextureBinding.js +17 -0
  121. package/dist/TextureBinding.js.map +1 -1
  122. package/dist/TexturedPBRMaterial.js +1 -1
  123. package/dist/TexturedPBRMaterial.js.map +1 -1
  124. package/dist/TexturedUnlitMaterial.js +1 -1
  125. package/dist/TexturedUnlitMaterial.js.map +1 -1
  126. package/dist/UnlitMaterial.js +1 -1
  127. package/dist/UnlitMaterial.js.map +1 -1
  128. package/dist/VegetationFixtures.d.ts +1 -1
  129. package/dist/VegetationFixtures.d.ts.map +1 -1
  130. package/dist/VegetationFixtures.js +1 -1
  131. package/dist/VegetationFixtures.js.map +1 -1
  132. package/dist/VegetationScatter.d.ts +68 -0
  133. package/dist/VegetationScatter.d.ts.map +1 -0
  134. package/dist/VegetationScatter.js +241 -0
  135. package/dist/VegetationScatter.js.map +1 -0
  136. package/dist/VoxelWorld.d.ts +51 -0
  137. package/dist/VoxelWorld.d.ts.map +1 -0
  138. package/dist/VoxelWorld.js +183 -0
  139. package/dist/VoxelWorld.js.map +1 -0
  140. package/dist/Weather.d.ts +44 -0
  141. package/dist/Weather.d.ts.map +1 -0
  142. package/dist/Weather.js +140 -0
  143. package/dist/Weather.js.map +1 -0
  144. package/dist/WebGL2Device.d.ts +22 -0
  145. package/dist/WebGL2Device.d.ts.map +1 -1
  146. package/dist/WebGL2Device.js +134 -23
  147. package/dist/WebGL2Device.js.map +1 -1
  148. package/dist/WebGPUDevice.d.ts +19 -2
  149. package/dist/WebGPUDevice.d.ts.map +1 -1
  150. package/dist/WebGPUDevice.js +188 -9
  151. package/dist/WebGPUDevice.js.map +1 -1
  152. package/dist/index.d.ts +25 -32
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +35 -19
  155. package/dist/index.js.map +1 -1
  156. package/dist/lean/LeanProductRenderer.d.ts +29 -0
  157. package/dist/lean/LeanProductRenderer.d.ts.map +1 -0
  158. package/dist/lean/LeanProductRenderer.js +38 -0
  159. package/dist/lean/LeanProductRenderer.js.map +1 -0
  160. package/dist/lean/LeanProductionRenderer.d.ts +38 -0
  161. package/dist/lean/LeanProductionRenderer.d.ts.map +1 -0
  162. package/dist/lean/LeanProductionRenderer.js +88 -0
  163. package/dist/lean/LeanProductionRenderer.js.map +1 -0
  164. package/dist/lean-runtime.d.ts +18 -0
  165. package/dist/lean-runtime.d.ts.map +1 -0
  166. package/dist/lean-runtime.js +13 -0
  167. package/dist/lean-runtime.js.map +1 -0
  168. package/dist/postprocess/EffectComposer.d.ts.map +1 -1
  169. package/dist/postprocess/EffectComposer.js +1 -3
  170. package/dist/postprocess/EffectComposer.js.map +1 -1
  171. package/dist/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
  172. package/dist/production-runtime/PBRHDRPipeline.js +5 -1
  173. package/dist/production-runtime/PBRHDRPipeline.js.map +1 -1
  174. package/dist/production-runtime/ProductionRendererTypes.d.ts +9 -0
  175. package/dist/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
  176. package/dist/production-runtime/ProductionRendererTypes.js.map +1 -1
  177. package/dist/production-runtime/ProductionRuntimeRenderer.d.ts +5 -0
  178. package/dist/production-runtime/ProductionRuntimeRenderer.d.ts.map +1 -1
  179. package/dist/production-runtime/ProductionRuntimeRenderer.js +53 -5
  180. package/dist/production-runtime/ProductionRuntimeRenderer.js.map +1 -1
  181. package/dist/production-runtime/ProductionWebGL2Renderer.d.ts +10 -0
  182. package/dist/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
  183. package/dist/production-runtime/ProductionWebGL2Renderer.js +21 -2
  184. package/dist/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
  185. package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
  186. package/dist/production-runtime/ProductionWebGPURenderer.js +4 -1
  187. package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
  188. package/dist/webgpu-device-entry.d.ts +25 -0
  189. package/dist/webgpu-device-entry.d.ts.map +1 -0
  190. package/dist/webgpu-device-entry.js +24 -0
  191. package/dist/webgpu-device-entry.js.map +1 -0
  192. package/dist/webgpu.d.ts +25 -0
  193. package/dist/webgpu.d.ts.map +1 -0
  194. package/dist/webgpu.js +24 -0
  195. package/dist/webgpu.js.map +1 -0
  196. package/package.json +11 -3
@@ -0,0 +1,639 @@
1
+ import { Geometry } from "./Geometry.js";
2
+ import { PBRMaterial } from "./PBRMaterial.js";
3
+ import { createProceduralTexture } from "./ProceduralTexture.js";
4
+ import { Sampler } from "./Sampler.js";
5
+ import { TexturedPBRMaterial } from "./TexturedPBRMaterial.js";
6
+ import { TexturedUnlitMaterial } from "./TexturedUnlitMaterial.js";
7
+ import { UnlitMaterial } from "./UnlitMaterial.js";
8
+ import { createExternalParityEnvironmentLighting } from "./ExternalParityRenderPreset.js";
9
+ import { DirectionalLight } from "@aura3d/scene";
10
+ const sourceFiles = [
11
+ "master:src/ecommerce/turntable/TurntableController.ts",
12
+ "master:src/ecommerce/turntable/HotspotManager.ts",
13
+ "master:src/ecommerce/turntable/LightingPresetManager.ts",
14
+ "master:src/ecommerce/turntable/CaptureManager.ts",
15
+ "master:src/ecommerce/turntable/BatchProcessor.ts",
16
+ "master:src/ecommerce/turntable/ARExporter.ts"
17
+ ];
18
+ const lightingPresets = {
19
+ studio: {
20
+ activeLightCount: 4,
21
+ ambientIntensity: 0.2,
22
+ keyIntensity: 1.5,
23
+ fillIntensity: 0.5,
24
+ rimIntensity: 0.8,
25
+ environmentIntensity: 1,
26
+ shadowEnabled: true,
27
+ shadowSoftness: 0.5
28
+ },
29
+ soft: {
30
+ activeLightCount: 4,
31
+ ambientIntensity: 0.5,
32
+ keyIntensity: 0.8,
33
+ fillIntensity: 0.8,
34
+ rimIntensity: 0.4,
35
+ environmentIntensity: 0.85,
36
+ shadowEnabled: false,
37
+ shadowSoftness: 1
38
+ },
39
+ inspection: {
40
+ activeLightCount: 3,
41
+ ambientIntensity: 0.32,
42
+ keyIntensity: 1.15,
43
+ fillIntensity: 0.65,
44
+ rimIntensity: 0.35,
45
+ environmentIntensity: 1.05,
46
+ shadowEnabled: true,
47
+ shadowSoftness: 0.35
48
+ },
49
+ dramatic: {
50
+ activeLightCount: 3,
51
+ ambientIntensity: 0.05,
52
+ keyIntensity: 2.5,
53
+ fillIntensity: 0.12,
54
+ rimIntensity: 1.2,
55
+ environmentIntensity: 0.7,
56
+ shadowEnabled: true,
57
+ shadowSoftness: 0.1
58
+ },
59
+ neutral: {
60
+ activeLightCount: 4,
61
+ ambientIntensity: 0.3,
62
+ keyIntensity: 1,
63
+ fillIntensity: 0.6,
64
+ rimIntensity: 0.3,
65
+ environmentIntensity: 1,
66
+ shadowEnabled: true,
67
+ shadowSoftness: 0.5
68
+ }
69
+ };
70
+ export function createProductTurntableFixture(options = {}) {
71
+ const elapsedSeconds = finiteOrDefault(options.elapsedSeconds, 0, "elapsedSeconds");
72
+ const interactionCount = integerOrDefault(options.interactionCount, 0, "interactionCount");
73
+ const interactionAgeMs = finiteOrDefault(options.interactionAgeMs, Number.POSITIVE_INFINITY, "interactionAgeMs");
74
+ const canvasWidth = Math.max(320, integerOrDefault(options.canvasWidth, 960, "canvasWidth"));
75
+ const canvasHeight = Math.max(240, integerOrDefault(options.canvasHeight, 540, "canvasHeight"));
76
+ const lightingPreset = options.lightingPreset ?? "studio";
77
+ const speedRadiansPerSecond = Math.PI / 8;
78
+ const resumeDelayMs = 2_000;
79
+ const transitionDurationSeconds = 0.5;
80
+ const pausedByInteraction = interactionCount > 0 && interactionAgeMs < resumeDelayMs;
81
+ const resumeBlend = pausedByInteraction ? 0 : Math.min(1, Math.max(0, (interactionAgeMs - resumeDelayMs) / (transitionDurationSeconds * 1000)));
82
+ const currentSpeedRadiansPerSecond = Number((speedRadiansPerSecond * resumeBlend).toFixed(4));
83
+ const rotationRadians = Number(((elapsedSeconds * speedRadiansPerSecond) % (Math.PI * 2)).toFixed(4));
84
+ const hotspots = createHotspots(rotationRadians, canvasWidth, canvasHeight);
85
+ const visibleHotspotCount = hotspots.filter((hotspot) => hotspot.screen.visible).length;
86
+ const capture = createCapturePlan(Boolean(options.captureRequested), Math.max(0, integerOrDefault(options.exportedBytes, 0, "exportedBytes")));
87
+ const lighting = createLighting(lightingPreset);
88
+ const hash = hashProductTurntable([
89
+ rotationRadians,
90
+ currentSpeedRadiansPerSecond,
91
+ visibleHotspotCount,
92
+ lighting.activeLightCount,
93
+ lighting.environmentIntensity,
94
+ capture.completedBatchTasks,
95
+ capture.exportedBytes
96
+ ]);
97
+ return {
98
+ id: "external-product-turntable-fixture",
99
+ source: "origin-master-ecommerce-turntable-adapted",
100
+ sourceFiles,
101
+ autoRotate: true,
102
+ speedRadiansPerSecond,
103
+ direction: "cw",
104
+ pauseOnInteraction: true,
105
+ resumeDelayMs,
106
+ smoothTransition: true,
107
+ transitionDurationSeconds,
108
+ currentSpeedRadiansPerSecond,
109
+ rotationRadians,
110
+ pausedByInteraction,
111
+ interactionCount,
112
+ hotspots,
113
+ visibleHotspotCount,
114
+ lighting,
115
+ capture,
116
+ manifestHash: hash,
117
+ claimBoundary: "This fixture adapts old ecommerce turntable concepts into deterministic external product evidence. It does not claim a complete ecommerce pipeline, native USDZ export, video capture, PIM integration, or AR platform parity."
118
+ };
119
+ }
120
+ export function createProductTurntableRenderKit(options = {}) {
121
+ const fixture = createProductTurntableFixture(options);
122
+ const environment = createExternalParityEnvironmentLighting(productTurntableEnvironmentPreset(fixture.lighting.activePreset));
123
+ const sampler = new Sampler({ minFilter: "linear-mipmap-linear", magFilter: "linear", addressU: "repeat", addressV: "repeat" });
124
+ const detailSampler = new Sampler({ minFilter: "linear", magFilter: "linear", addressU: "repeat", addressV: "repeat" });
125
+ const textures = [
126
+ createProceduralTexture("metallic-paint", { width: 128, height: 128 }),
127
+ createProceduralTexture("carbon-fiber", { width: 128, height: 128 }),
128
+ createProceduralTexture("metallic-roughness-map", { width: 128, height: 128 }),
129
+ createProceduralTexture("normal-from-height", { width: 128, height: 128 }),
130
+ createProceduralTexture("sci-fi-panel", { width: 128, height: 128 }),
131
+ createProceduralTexture("concrete-asphalt", { width: 128, height: 128 }),
132
+ createProceduralTexture("racing-stripes", { width: 128, height: 128 })
133
+ ];
134
+ const [paint, carbon, metallicRoughness, normal, panel, concrete, accentStripes] = textures;
135
+ const geometryLibrary = new Map([
136
+ ["ear-shell", Geometry.capsule({ radius: 0.34, height: 0.9, segments: 64, rings: 16, textured: true })],
137
+ ["ear-pad", Geometry.uvSphere(0.42, 64, 32, { textured: true })],
138
+ ["headband", Geometry.texturedCube(1)],
139
+ ["control", Geometry.texturedCube(1)],
140
+ ["accent", Geometry.uvSphere(0.18, 48, 24, { textured: true })],
141
+ ["stage", Geometry.texturedCube(1)]
142
+ ]);
143
+ const materialLibrary = new Map([
144
+ ["painted-shell", new TexturedPBRMaterial({
145
+ name: "turntable-painted-metal-shell",
146
+ baseColor: [0.48, 0.5, 0.52, 1],
147
+ metallic: 0.62,
148
+ roughness: 0.32,
149
+ metallicRoughnessTexture: metallicRoughness,
150
+ metallicRoughnessSampler: sampler,
151
+ normalTexture: normal,
152
+ normalSampler: detailSampler,
153
+ normalScale: 0.12,
154
+ clearcoatFactor: 0.34,
155
+ clearcoatRoughnessFactor: 0.22
156
+ })],
157
+ ["carbon-pad", new TexturedPBRMaterial({
158
+ name: "turntable-carbon-cushion",
159
+ baseColor: [0.08, 0.09, 0.11, 1],
160
+ metallic: 0.08,
161
+ roughness: 0.64,
162
+ baseColorTexture: carbon,
163
+ baseColorSampler: sampler,
164
+ normalTexture: normal,
165
+ normalSampler: detailSampler,
166
+ normalScale: 0.22
167
+ })],
168
+ ["brushed-trim", new PBRMaterial({
169
+ name: "turntable-brushed-trim",
170
+ baseColor: [0.86, 0.82, 0.72, 1],
171
+ metallic: 1,
172
+ roughness: 0.19,
173
+ specularFactor: 1
174
+ })],
175
+ ["control-panel", new TexturedPBRMaterial({
176
+ name: "turntable-control-panel",
177
+ baseColor: [0.16, 0.42, 0.58, 1],
178
+ metallic: 0.18,
179
+ roughness: 0.38,
180
+ baseColorTexture: panel,
181
+ baseColorSampler: sampler,
182
+ normalTexture: normal,
183
+ normalSampler: detailSampler,
184
+ normalScale: 0.16,
185
+ emissiveColor: [0.01, 0.12, 0.16],
186
+ emissiveStrength: 0.18
187
+ })],
188
+ ["status-light", new PBRMaterial({
189
+ name: "turntable-status-light",
190
+ baseColor: [0.18, 0.92, 1, 1],
191
+ metallic: 0,
192
+ roughness: 0.18,
193
+ emissiveColor: [0.06, 0.72, 1],
194
+ emissiveStrength: 1.4
195
+ })],
196
+ ["amber-status-light", new PBRMaterial({
197
+ name: "turntable-amber-status-light",
198
+ baseColor: [1, 0.62, 0.12, 1],
199
+ metallic: 0,
200
+ roughness: 0.2,
201
+ emissiveColor: [1, 0.36, 0.06],
202
+ emissiveStrength: 0.9
203
+ })],
204
+ ["magenta-status-light", new PBRMaterial({
205
+ name: "turntable-magenta-status-light",
206
+ baseColor: [0.94, 0.16, 0.86, 1],
207
+ metallic: 0,
208
+ roughness: 0.2,
209
+ emissiveColor: [0.56, 0.04, 0.5],
210
+ emissiveStrength: 0.82
211
+ })],
212
+ ["studio-stage", new TexturedUnlitMaterial({
213
+ name: "turntable-studio-stage",
214
+ texture: concrete,
215
+ sampler,
216
+ textureTransform: { scale: [7.5, 3.75] },
217
+ color: [0.18, 0.2, 0.22, 1]
218
+ })],
219
+ ["studio-backdrop", new UnlitMaterial({
220
+ name: "turntable-studio-backdrop",
221
+ color: [0.018, 0.023, 0.03, 1]
222
+ })],
223
+ ["studio-accent-stripe", new TexturedUnlitMaterial({
224
+ name: "turntable-studio-accent-stripe",
225
+ texture: accentStripes,
226
+ sampler,
227
+ textureTransform: { scale: [2.8, 0.8] },
228
+ color: [0.72, 0.62, 0.42, 1]
229
+ })],
230
+ ["paint-finish-reference", new TexturedUnlitMaterial({
231
+ name: "turntable-metallic-paint-finish-reference",
232
+ texture: paint,
233
+ sampler,
234
+ textureTransform: { scale: [2.4, 0.8] },
235
+ color: [0.2, 0.12, 0.1, 1]
236
+ })],
237
+ ["studio-softbox", new UnlitMaterial({
238
+ name: "turntable-studio-softbox-panel",
239
+ color: [0.1, 0.12, 0.14, 1]
240
+ })],
241
+ ["dark-detail-line", new UnlitMaterial({
242
+ name: "turntable-dark-detail-line",
243
+ color: [0.018, 0.022, 0.026, 1]
244
+ })],
245
+ ["cyan-detail-line", new UnlitMaterial({
246
+ name: "turntable-cyan-detail-line",
247
+ color: [0.08, 0.34, 0.38, 1]
248
+ })],
249
+ ["warm-detail-line", new UnlitMaterial({
250
+ name: "turntable-warm-detail-line",
251
+ color: [0.52, 0.31, 0.18, 1]
252
+ })]
253
+ ]);
254
+ const angle = fixture.rotationRadians;
255
+ const includeStage = options.includeStage !== false;
256
+ const collectedLights = createProductTurntableCollectedLights(fixture.lighting);
257
+ const renderItems = [
258
+ item(geometryLibrary, materialLibrary, "left-ear-shell", "ear-shell", "painted-shell", -0.56, 0.1, 0, 0.82, 1.08, 0.64, angle),
259
+ item(geometryLibrary, materialLibrary, "right-ear-shell", "ear-shell", "painted-shell", 0.56, 0.1, 0, 0.82, 1.08, 0.64, angle),
260
+ item(geometryLibrary, materialLibrary, "left-ear-pad", "ear-pad", "carbon-pad", -0.56, -0.02, 0.08, 0.56, 0.72, 0.26, angle),
261
+ item(geometryLibrary, materialLibrary, "right-ear-pad", "ear-pad", "carbon-pad", 0.56, -0.02, 0.08, 0.56, 0.72, 0.26, angle),
262
+ item(geometryLibrary, materialLibrary, "outer-headband", "headband", "brushed-trim", 0, 0.75, -0.05, 1.52, 0.14, 0.22, angle),
263
+ item(geometryLibrary, materialLibrary, "inner-headband", "headband", "carbon-pad", 0, 0.56, 0.02, 1.16, 0.1, 0.18, angle),
264
+ item(geometryLibrary, materialLibrary, "left-yoke", "headband", "brushed-trim", -0.54, 0.45, 0, 0.16, 0.64, 0.16, angle),
265
+ item(geometryLibrary, materialLibrary, "right-yoke", "headband", "brushed-trim", 0.54, 0.45, 0, 0.16, 0.64, 0.16, angle),
266
+ item(geometryLibrary, materialLibrary, "control-module", "control", "control-panel", 0.82, 0.04, 0.16, 0.18, 0.34, 0.12, angle),
267
+ item(geometryLibrary, materialLibrary, "status-light", "accent", "status-light", 0.91, 0.08, 0.28, 0.16, 0.16, 0.16, angle),
268
+ item(geometryLibrary, materialLibrary, "left-amber-accent", "accent", "amber-status-light", -0.96, 0.16, 0.24, 0.1, 0.1, 0.1, angle),
269
+ item(geometryLibrary, materialLibrary, "right-magenta-accent", "accent", "magenta-status-light", 0.36, -0.34, 0.3, 0.1, 0.1, 0.1, angle)
270
+ ];
271
+ for (const side of [-1, 1]) {
272
+ for (let index = 0; index < 5; index += 1) {
273
+ renderItems.push(item(geometryLibrary, materialLibrary, `ear-pad-stitch-${side}-${index}`, "headband", index % 2 === 0 ? "dark-detail-line" : "cyan-detail-line", side * 0.56, -0.24 + index * 0.085, -0.22, 0.46, 0.014, 0.012, angle));
274
+ }
275
+ for (let index = 0; index < 7; index += 1) {
276
+ renderItems.push(item(geometryLibrary, materialLibrary, `ear-pad-vertical-stitch-${side}-${index}`, "headband", index % 2 === 0 ? "dark-detail-line" : "cyan-detail-line", side * (0.34 + index * 0.075), -0.04, -0.235, 0.012, 0.43, 0.012, angle));
277
+ }
278
+ for (let index = 0; index < 4; index += 1) {
279
+ renderItems.push(item(geometryLibrary, materialLibrary, `shell-highlight-strake-${side}-${index}`, "headband", index % 2 === 0 ? "warm-detail-line" : "dark-detail-line", side * (0.56 + index * 0.035), 0.22 + index * 0.078, -0.24, 0.34 - index * 0.035, 0.012, 0.012, angle));
280
+ }
281
+ for (let index = 0; index < 7; index += 1) {
282
+ renderItems.push(item(geometryLibrary, materialLibrary, `shell-machined-vertical-${side}-${index}`, "headband", index % 2 === 0 ? "warm-detail-line" : "dark-detail-line", side * (0.31 + index * 0.07), 0.25, -0.245, 0.012, 0.58, 0.012, angle));
283
+ }
284
+ }
285
+ for (let index = 0; index < 7; index += 1) {
286
+ renderItems.push(item(geometryLibrary, materialLibrary, `headband-machined-groove-${index}`, "headband", index % 2 === 0 ? "dark-detail-line" : "cyan-detail-line", -0.58 + index * 0.19, 0.78, -0.24, 0.08, 0.016, 0.012, angle));
287
+ }
288
+ for (let index = 0; index < 6; index += 1) {
289
+ renderItems.push(item(geometryLibrary, materialLibrary, `control-module-ridge-${index}`, "headband", index % 2 === 0 ? "cyan-detail-line" : "dark-detail-line", 0.78, -0.08 + index * 0.052, -0.26, 0.16, 0.01, 0.01, angle));
290
+ }
291
+ for (let index = 0; index < 6; index += 1) {
292
+ renderItems.push(item(geometryLibrary, materialLibrary, `control-module-vertical-grille-${index}`, "headband", index % 2 === 0 ? "cyan-detail-line" : "dark-detail-line", 0.72 + index * 0.035, 0.06, -0.275, 0.01, 0.34, 0.01, angle));
293
+ }
294
+ if (includeStage) {
295
+ renderItems.push(item(geometryLibrary, materialLibrary, "full-studio-backdrop", "stage", "studio-backdrop", 0, 0.18, -1.48, 7.8, 4.35, 0.05, 0), item(geometryLibrary, materialLibrary, "studio-stage", "stage", "studio-stage", 0, -0.62, -0.42, 4.4, 0.08, 0.16, 0), item(geometryLibrary, materialLibrary, "backdrop", "stage", "studio-backdrop", 0, 0.18, -1.12, 4.4, 2.25, 0.05, 0), item(geometryLibrary, materialLibrary, "stage-accent-stripe-left", "stage", "studio-accent-stripe", -1.04, -0.51, -0.34, 1.18, 0.035, 0.035, 0), item(geometryLibrary, materialLibrary, "stage-accent-stripe-right", "stage", "studio-accent-stripe", 1.04, -0.51, -0.34, 1.18, 0.035, 0.035, 0), item(geometryLibrary, materialLibrary, "left-softbox-panel", "stage", "studio-softbox", -1.78, 0.22, -0.94, 0.12, 1.16, 0.04, 0), item(geometryLibrary, materialLibrary, "right-softbox-panel", "stage", "studio-softbox", 1.78, 0.22, -0.94, 0.12, 1.16, 0.04, 0), item(geometryLibrary, materialLibrary, "top-softbox-panel", "stage", "studio-softbox", 0, 1.08, -0.94, 1.52, 0.07, 0.04, 0));
296
+ appendTurntableStageDetails(renderItems, geometryLibrary, materialLibrary);
297
+ }
298
+ const postprocess = {
299
+ targetFormat: "rgba16f",
300
+ toneMapping: {
301
+ operator: "filmic",
302
+ exposure: fixture.lighting.activePreset === "dramatic" ? 1.32 : 1.12,
303
+ gamma: 2.2,
304
+ inputColorSpace: "linear",
305
+ outputColorSpace: "srgb"
306
+ },
307
+ colorGrade: {
308
+ contrast: 1.1,
309
+ saturation: 1.08,
310
+ vibrance: 0.16,
311
+ vignette: 0.12,
312
+ sharpening: 0.28
313
+ },
314
+ bloom: { threshold: 0.78, intensity: 0.12, radius: 1 },
315
+ fxaa: true
316
+ };
317
+ const source = {
318
+ renderItems,
319
+ cameraPolicy: "auto-frame",
320
+ cameraFrameBounds: { min: [-1.35, -0.52, -0.38], max: [1.35, 1, 0.42] },
321
+ collectedLights,
322
+ // `ambientIntensity` and `environmentIntensity` are preset properties, so they must reach
323
+ // the frame the renderer actually samples. In this renderer the ambient term *is*
324
+ // `environmentLighting.intensity`, so the preset's ambient drives that directly rather
325
+ // than being approximated by an extra directional light. The bundle returned by
326
+ // `createExternalParityEnvironmentLighting` is cached and shared across callers, so both
327
+ // values are applied to a copy rather than mutated in place.
328
+ environmentLighting: applyPresetEnvironmentLighting(environment.lighting, fixture.lighting.ambientIntensity, fixture.lighting.environmentIntensity),
329
+ shadow: {
330
+ // `shadowEnabled`/`shadowSoftness` are preset properties, so a preset that declares
331
+ // shadows off (soft) must actually render without them, and a hard-shadow preset
332
+ // (dramatic, softness 0.1) must not use the same PCF radius as a diffuse one.
333
+ enabled: fixture.lighting.shadowEnabled,
334
+ light: collectedLights[0]?.source,
335
+ size: 768,
336
+ strength: 0.42,
337
+ bias: 0.0025,
338
+ slopeBias: 1.2,
339
+ filter: "pcf",
340
+ pcfRadius: Number((0.6 + fixture.lighting.shadowSoftness * 1.3).toFixed(4)),
341
+ pcfSamples: 9
342
+ },
343
+ postprocess,
344
+ frustumCulling: false
345
+ };
346
+ return {
347
+ fixture,
348
+ source,
349
+ renderItems,
350
+ geometryLibrary,
351
+ materialLibrary,
352
+ bounds: { min: [-2.2, -0.7, -1.18], max: [2.2, 1.13, 0.44] },
353
+ postprocess,
354
+ dispose: () => {
355
+ for (const geometry of geometryLibrary.values())
356
+ geometry.dispose();
357
+ for (const texture of textures)
358
+ texture.dispose();
359
+ }
360
+ };
361
+ }
362
+ function appendTurntableStageDetails(renderItems, geometryLibrary, materialLibrary) {
363
+ for (let index = 0; index < 5; index += 1) {
364
+ renderItems.push(item(geometryLibrary, materialLibrary, `studio-floor-perspective-line-${index}`, "stage", "dark-detail-line", -1.2 + index * 0.6, -0.62, -0.305, 0.006, 0.58, 0.009, 0));
365
+ }
366
+ for (let index = 0; index < 3; index += 1) {
367
+ renderItems.push(item(geometryLibrary, materialLibrary, `studio-floor-cross-reference-${index}`, "stage", "dark-detail-line", 0, -0.74 + index * 0.14, -0.295, 1.42, 0.007, 0.009, 0));
368
+ }
369
+ }
370
+ /**
371
+ * Builds the turntable's three-point rig from the *active lighting preset*.
372
+ *
373
+ * These intensities used to be hardcoded, so `lightingPreset` changed only the reported
374
+ * `fixture.lighting` metadata while every preset rendered an identical frame. That made the
375
+ * preset a self-reported claim rather than a rendering input, and it left the studio preset
376
+ * measurably underlit: the kit reported `keyIntensity: 1.5` and `environmentIntensity: 1`
377
+ * while the rig actually submitted a fixed 2.35 key with no ambient term at all, and the
378
+ * root quality gate's `salientRatio` sat at 0.096 against its 0.105 floor.
379
+ *
380
+ * The preset's `keyIntensity`/`fillIntensity`/`rimIntensity` are relative weights, scaled by
381
+ * a shared gain so the default studio preset keeps its previously-tuned absolute key value.
382
+ * `ambientIntensity` is handled separately by `applyPresetEnvironmentLighting`, because in this
383
+ * renderer the ambient term is `environmentLighting.intensity` rather than a fourth
384
+ * directional light. Submitting it in both places would double-count it.
385
+ */
386
+ /**
387
+ * Applies the active preset's ambient and environment intensities to an environment bundle.
388
+ *
389
+ * `ambientIntensity` sets the ambient colour term directly; `environmentIntensity` scales the
390
+ * sampled environment map and its specular contribution. Textures and samplers are shared by
391
+ * reference because they are immutable GPU resources, so only scalars differ per preset.
392
+ */
393
+ function applyPresetEnvironmentLighting(lighting, ambientIntensity, environmentIntensity) {
394
+ for (const [name, value] of [["ambientIntensity", ambientIntensity], ["environmentIntensity", environmentIntensity]]) {
395
+ if (!Number.isFinite(value) || value < 0) {
396
+ throw new RangeError(`Product turntable ${name} must be finite and non-negative`);
397
+ }
398
+ }
399
+ const scaled = (value) => typeof value === "number" ? Number((value * environmentIntensity).toFixed(4)) : value;
400
+ return {
401
+ ...lighting,
402
+ intensity: Number(ambientIntensity.toFixed(4)),
403
+ ...(typeof lighting.environmentMapIntensity === "number"
404
+ ? { environmentMapIntensity: scaled(lighting.environmentMapIntensity) }
405
+ : {}),
406
+ ...(typeof lighting.environmentMapSpecularIntensity === "number"
407
+ ? { environmentMapSpecularIntensity: scaled(lighting.environmentMapSpecularIntensity) }
408
+ : {}),
409
+ ...(lighting.proceduralMap
410
+ ? {
411
+ proceduralMap: {
412
+ ...lighting.proceduralMap,
413
+ ...(typeof lighting.proceduralMap.intensity === "number"
414
+ ? { intensity: scaled(lighting.proceduralMap.intensity) }
415
+ : {}),
416
+ ...(typeof lighting.proceduralMap.specularIntensity === "number"
417
+ ? { specularIntensity: scaled(lighting.proceduralMap.specularIntensity) }
418
+ : {})
419
+ }
420
+ }
421
+ : {})
422
+ };
423
+ }
424
+ function createProductTurntableCollectedLights(lighting) {
425
+ // Chosen so studio (keyIntensity 1.5) reproduces the previously hardcoded 2.35 key.
426
+ const gain = 2.35 / 1.5;
427
+ const keyIntensity = Number((lighting.keyIntensity * gain).toFixed(4));
428
+ const fillIntensity = Number((lighting.fillIntensity * gain).toFixed(4));
429
+ const rimIntensity = Number((lighting.rimIntensity * gain).toFixed(4));
430
+ const key = new DirectionalLight("turntable-key-shadow");
431
+ const fill = new DirectionalLight("turntable-fill");
432
+ const rim = new DirectionalLight("turntable-rim");
433
+ key.castsShadow = lighting.shadowEnabled;
434
+ key.color = [1, 0.92, 0.78];
435
+ key.intensity = keyIntensity;
436
+ fill.color = [0.5, 0.64, 0.95];
437
+ fill.intensity = fillIntensity;
438
+ rim.color = [0.85, 0.92, 1];
439
+ rim.intensity = rimIntensity;
440
+ return [
441
+ {
442
+ kind: "directional",
443
+ color: [1, 0.92, 0.78],
444
+ intensity: keyIntensity,
445
+ position: [0, 0, 0],
446
+ direction: [0.42, -0.64, -0.64],
447
+ right: [1, 0, 0],
448
+ up: [0, 1, 0],
449
+ range: 0,
450
+ width: 0,
451
+ height: 0,
452
+ spotAngle: 0,
453
+ penumbra: 0,
454
+ castsShadow: lighting.shadowEnabled,
455
+ layerMask: 0xffffffff,
456
+ source: key
457
+ },
458
+ {
459
+ kind: "directional",
460
+ color: [0.5, 0.64, 0.95],
461
+ intensity: fillIntensity,
462
+ position: [0, 0, 0],
463
+ direction: [-0.5, -0.3, -0.82],
464
+ right: [1, 0, 0],
465
+ up: [0, 1, 0],
466
+ range: 0,
467
+ width: 0,
468
+ height: 0,
469
+ spotAngle: 0,
470
+ penumbra: 0,
471
+ castsShadow: false,
472
+ layerMask: 0xffffffff,
473
+ source: fill
474
+ },
475
+ {
476
+ kind: "directional",
477
+ color: [0.85, 0.92, 1],
478
+ intensity: rimIntensity,
479
+ position: [0, 0, 0],
480
+ direction: [-0.15, -0.12, 0.98],
481
+ right: [1, 0, 0],
482
+ up: [0, 1, 0],
483
+ range: 0,
484
+ width: 0,
485
+ height: 0,
486
+ spotAngle: 0,
487
+ penumbra: 0,
488
+ castsShadow: false,
489
+ layerMask: 0xffffffff,
490
+ source: rim
491
+ }
492
+ ];
493
+ }
494
+ function createHotspots(rotationRadians, canvasWidth, canvasHeight) {
495
+ const anchors = [
496
+ {
497
+ id: "material-finish",
498
+ group: "material",
499
+ label: "Metallic shell",
500
+ content: "Product finish, carbon accent, and procedural roughness maps are bound through the current renderer.",
501
+ position: [-0.3, 0.24, 0.18],
502
+ threshold: -0.15
503
+ },
504
+ {
505
+ id: "comfort-band",
506
+ group: "comfort",
507
+ label: "Comfort band",
508
+ content: "Generated multi-part glTF geometry keeps headband and cushion slots independently selectable.",
509
+ position: [0, 0.52, 0.1],
510
+ threshold: -0.05
511
+ },
512
+ {
513
+ id: "control-module",
514
+ group: "controls",
515
+ label: "Control module",
516
+ content: "The selected controls part is exposed in runtime state for ecommerce hotspot evidence.",
517
+ position: [0.38, 0.06, 0.2],
518
+ threshold: -0.1
519
+ }
520
+ ];
521
+ return anchors.map((anchor, index) => {
522
+ const projected = projectHotspot(anchor.position, rotationRadians, canvasWidth, canvasHeight, index);
523
+ return {
524
+ id: anchor.id,
525
+ group: anchor.group,
526
+ label: anchor.label,
527
+ content: anchor.content,
528
+ position: anchor.position,
529
+ screen: projected,
530
+ visibilityThreshold: anchor.threshold
531
+ };
532
+ });
533
+ }
534
+ function productTurntableEnvironmentPreset(preset) {
535
+ if (preset === "soft" || preset === "neutral")
536
+ return "softbox";
537
+ if (preset === "inspection")
538
+ return "inspection";
539
+ if (preset === "dramatic")
540
+ return "exhibit";
541
+ return "studio";
542
+ }
543
+ function item(geometryLibrary, materialLibrary, label, geometryId, materialId, tx, ty, tz, sx, sy, sz, yawRadians) {
544
+ const geometry = geometryLibrary.get(geometryId);
545
+ const material = materialLibrary.get(materialId);
546
+ if (!geometry)
547
+ throw new Error(`Missing product turntable geometry: ${geometryId}`);
548
+ if (!material)
549
+ throw new Error(`Missing product turntable material: ${materialId}`);
550
+ return {
551
+ geometry,
552
+ material,
553
+ label,
554
+ modelMatrix: modelMatrix(tx, ty, tz, sx, sy, sz, yawRadians)
555
+ };
556
+ }
557
+ function modelMatrix(tx, ty, tz, sx, sy, sz, yawRadians) {
558
+ const cos = Math.cos(yawRadians);
559
+ const sin = Math.sin(yawRadians);
560
+ return new Float32Array([
561
+ cos * sx, 0, -sin * sx, 0,
562
+ 0, sy, 0, 0,
563
+ sin * sz, 0, cos * sz, 0,
564
+ tx, ty, tz, 1
565
+ ]);
566
+ }
567
+ function projectHotspot(position, rotationRadians, canvasWidth, canvasHeight, index) {
568
+ const cos = Math.cos(rotationRadians);
569
+ const sin = Math.sin(rotationRadians);
570
+ const x = position[0] * cos - position[2] * sin;
571
+ const z = position[0] * sin + position[2] * cos;
572
+ const screenX = Math.round(canvasWidth * (0.5 + x * 0.36));
573
+ const screenY = Math.round(canvasHeight * (0.5 - position[1] * 0.42 + index * 0.018));
574
+ return {
575
+ x: screenX,
576
+ y: screenY,
577
+ visible: z > -0.38 && screenX >= 0 && screenX <= canvasWidth && screenY >= 0 && screenY <= canvasHeight,
578
+ occluded: false
579
+ };
580
+ }
581
+ function createLighting(activePreset) {
582
+ const descriptor = lightingPresets[activePreset];
583
+ return {
584
+ source: "origin-master-ecommerce-lighting-preset-manager-adapted",
585
+ presets: ["studio", "soft", "inspection", "dramatic", "neutral"],
586
+ activePreset,
587
+ transitionSupported: true,
588
+ ...descriptor
589
+ };
590
+ }
591
+ function createCapturePlan(captureRequested, exportedBytes) {
592
+ const completedBatchTasks = captureRequested && exportedBytes > 0 ? 3 : 2;
593
+ return {
594
+ source: "origin-master-ecommerce-capture-batch-export-adapted",
595
+ screenshotFormats: ["png", "jpeg", "webp"],
596
+ screenshotViews: ["hero", "front", "detail", "exploded"],
597
+ spinFrameCount: 72,
598
+ spinDurationSeconds: 4,
599
+ batchTasks: ["thumbnail", "screenshot", "360-spin", "ar-export"],
600
+ completedBatchTasks,
601
+ captureRequested,
602
+ exportedBytes,
603
+ arExportFormats: ["glb"],
604
+ blockedExportClaims: [
605
+ "native-USDZ-export",
606
+ "browser-video-recording-pipeline",
607
+ "AR-Quick-Look-and-Scene-Viewer-platform-parity",
608
+ "ecommerce-PIM-or-commerce-platform-integration"
609
+ ]
610
+ };
611
+ }
612
+ function finiteOrDefault(value, fallback, name) {
613
+ if (value === undefined)
614
+ return fallback;
615
+ if (!Number.isFinite(value) && value !== Number.POSITIVE_INFINITY)
616
+ throw new RangeError(`${name} must be finite.`);
617
+ return value;
618
+ }
619
+ function integerOrDefault(value, fallback, name) {
620
+ if (value === undefined)
621
+ return fallback;
622
+ if (!Number.isInteger(value) || value < 0)
623
+ throw new RangeError(`${name} must be a non-negative integer.`);
624
+ return value;
625
+ }
626
+ function hashProductTurntable(values) {
627
+ let hash = 0x811c9dc5;
628
+ for (const value of values) {
629
+ const scaled = Math.round(value * 1000);
630
+ hash ^= scaled & 0xff;
631
+ hash = Math.imul(hash, 0x01000193) >>> 0;
632
+ hash ^= (scaled >>> 8) & 0xff;
633
+ hash = Math.imul(hash, 0x01000193) >>> 0;
634
+ hash ^= (scaled >>> 16) & 0xff;
635
+ hash = Math.imul(hash, 0x01000193) >>> 0;
636
+ }
637
+ return hash.toString(16).padStart(8, "0");
638
+ }
639
+ //# sourceMappingURL=ProductTurntable.js.map