@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.
- package/README.md +8 -2
- package/dist/ArchitecturalLighting.d.ts +35 -0
- package/dist/ArchitecturalLighting.d.ts.map +1 -0
- package/dist/ArchitecturalLighting.js +162 -0
- package/dist/ArchitecturalLighting.js.map +1 -0
- package/dist/ArchitecturalMaterialCatalog.d.ts +1 -1
- package/dist/ArchitecturalMaterialCatalog.d.ts.map +1 -1
- package/dist/ArchitecturalMaterialCatalog.js +1 -1
- package/dist/ArchitecturalMaterialCatalog.js.map +1 -1
- package/dist/ArchitecturalMeasurement.d.ts +27 -0
- package/dist/ArchitecturalMeasurement.d.ts.map +1 -0
- package/dist/ArchitecturalMeasurement.js +112 -0
- package/dist/ArchitecturalMeasurement.js.map +1 -0
- package/dist/CanonicalProductScene.d.ts +13 -0
- package/dist/CanonicalProductScene.d.ts.map +1 -0
- package/dist/CanonicalProductScene.js +78 -0
- package/dist/CanonicalProductScene.js.map +1 -0
- package/dist/CascadedShadowMaps.d.ts +1 -1
- package/dist/CascadedShadowMaps.d.ts.map +1 -1
- package/dist/CascadedShadowMaps.js +1 -1
- package/dist/CascadedShadowMaps.js.map +1 -1
- package/dist/DepthPass.d.ts +1 -1
- package/dist/DepthPass.d.ts.map +1 -1
- package/dist/DepthPass.js +2 -2
- package/dist/DepthPass.js.map +1 -1
- package/dist/EnvironmentBackgroundPass.d.ts +1 -1
- package/dist/EnvironmentBackgroundPass.d.ts.map +1 -1
- package/dist/EnvironmentBackgroundPass.js +2 -2
- package/dist/EnvironmentBackgroundPass.js.map +1 -1
- package/dist/EnvironmentPlatform.js +2 -2
- package/dist/EnvironmentPlatform.js.map +1 -1
- package/dist/ForwardPass.d.ts +1 -1
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +24 -10
- package/dist/ForwardPass.js.map +1 -1
- package/dist/Geometry.d.ts +2 -0
- package/dist/Geometry.d.ts.map +1 -1
- package/dist/Geometry.js +15 -0
- package/dist/Geometry.js.map +1 -1
- package/dist/InstancedPBRMaterial.js +1 -1
- package/dist/InstancedPBRMaterial.js.map +1 -1
- package/dist/InstancedUnlitMaterial.js +1 -1
- package/dist/InstancedUnlitMaterial.js.map +1 -1
- package/dist/Material.js +2 -2
- package/dist/Material.js.map +1 -1
- package/dist/MaterialBinding.js +4 -4
- package/dist/MaterialBinding.js.map +1 -1
- package/dist/MaterialInstance.js +2 -2
- package/dist/MaterialInstance.js.map +1 -1
- package/dist/MorphUnlitMaterial.js +1 -1
- package/dist/MorphUnlitMaterial.js.map +1 -1
- package/dist/NormalMappedPBRMaterial.js +1 -1
- package/dist/NormalMappedPBRMaterial.js.map +1 -1
- package/dist/OceanSurface.d.ts +62 -0
- package/dist/OceanSurface.d.ts.map +1 -0
- package/dist/OceanSurface.js +240 -0
- package/dist/OceanSurface.js.map +1 -0
- package/dist/PBRMaterial.d.ts.map +1 -1
- package/dist/PBRMaterial.js +3 -3
- package/dist/PBRMaterial.js.map +1 -1
- package/dist/PortableShaderMaterial.d.ts +60 -0
- package/dist/PortableShaderMaterial.d.ts.map +1 -0
- package/dist/PortableShaderMaterial.js +223 -0
- package/dist/PortableShaderMaterial.js.map +1 -0
- package/dist/PostProcessPass.d.ts.map +1 -1
- package/dist/PostProcessPass.js +30 -6
- package/dist/PostProcessPass.js.map +1 -1
- package/dist/ProceduralTexture.d.ts +27 -0
- package/dist/ProceduralTexture.d.ts.map +1 -0
- package/dist/ProceduralTexture.js +336 -0
- package/dist/ProceduralTexture.js.map +1 -0
- package/dist/ProductTurntable.d.ts +96 -0
- package/dist/ProductTurntable.d.ts.map +1 -0
- package/dist/ProductTurntable.js +639 -0
- package/dist/ProductTurntable.js.map +1 -0
- package/dist/RenderDevice.d.ts +30 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.d.ts +3 -2
- package/dist/Renderer.d.ts.map +1 -1
- package/dist/Renderer.js +23 -9
- package/dist/Renderer.js.map +1 -1
- package/dist/RendererPostprocessPlan.js +4 -4
- package/dist/RendererPostprocessPlan.js.map +1 -1
- package/dist/ScreenSpaceLineMaterial.js +1 -1
- package/dist/ScreenSpaceLineMaterial.js.map +1 -1
- package/dist/ShaderChunks.d.ts.map +1 -1
- package/dist/ShaderChunks.js +81 -19
- package/dist/ShaderChunks.js.map +1 -1
- package/dist/ShaderLibrary.d.ts +2 -78
- package/dist/ShaderLibrary.d.ts.map +1 -1
- package/dist/ShaderLibrary.js +127 -793
- package/dist/ShaderLibrary.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts +100 -0
- package/dist/ShaderLibraryCore.d.ts.map +1 -0
- package/dist/ShaderLibraryCore.js +800 -0
- package/dist/ShaderLibraryCore.js.map +1 -0
- package/dist/ShaderModule.d.ts +1 -1
- package/dist/ShaderModule.d.ts.map +1 -1
- package/dist/ShaderModule.js +4 -2
- package/dist/ShaderModule.js.map +1 -1
- package/dist/ShadowPass.d.ts +1 -1
- package/dist/ShadowPass.d.ts.map +1 -1
- package/dist/ShadowPass.js +6 -4
- package/dist/ShadowPass.js.map +1 -1
- package/dist/SkinnedLitMaterial.js +1 -1
- package/dist/SkinnedLitMaterial.js.map +1 -1
- package/dist/SkinnedUnlitMaterial.js +1 -1
- package/dist/SkinnedUnlitMaterial.js.map +1 -1
- package/dist/SpaceEnvironment.d.ts +54 -0
- package/dist/SpaceEnvironment.d.ts.map +1 -0
- package/dist/SpaceEnvironment.js +116 -0
- package/dist/SpaceEnvironment.js.map +1 -0
- package/dist/TerrainHeightfield.d.ts +60 -0
- package/dist/TerrainHeightfield.d.ts.map +1 -0
- package/dist/TerrainHeightfield.js +239 -0
- package/dist/TerrainHeightfield.js.map +1 -0
- package/dist/TextureBinding.d.ts +14 -0
- package/dist/TextureBinding.d.ts.map +1 -1
- package/dist/TextureBinding.js +17 -0
- package/dist/TextureBinding.js.map +1 -1
- package/dist/TexturedPBRMaterial.js +1 -1
- package/dist/TexturedPBRMaterial.js.map +1 -1
- package/dist/TexturedUnlitMaterial.js +1 -1
- package/dist/TexturedUnlitMaterial.js.map +1 -1
- package/dist/UnlitMaterial.js +1 -1
- package/dist/UnlitMaterial.js.map +1 -1
- package/dist/VegetationFixtures.d.ts +1 -1
- package/dist/VegetationFixtures.d.ts.map +1 -1
- package/dist/VegetationFixtures.js +1 -1
- package/dist/VegetationFixtures.js.map +1 -1
- package/dist/VegetationScatter.d.ts +68 -0
- package/dist/VegetationScatter.d.ts.map +1 -0
- package/dist/VegetationScatter.js +241 -0
- package/dist/VegetationScatter.js.map +1 -0
- package/dist/VoxelWorld.d.ts +51 -0
- package/dist/VoxelWorld.d.ts.map +1 -0
- package/dist/VoxelWorld.js +183 -0
- package/dist/VoxelWorld.js.map +1 -0
- package/dist/Weather.d.ts +44 -0
- package/dist/Weather.d.ts.map +1 -0
- package/dist/Weather.js +140 -0
- package/dist/Weather.js.map +1 -0
- package/dist/WebGL2Device.d.ts +22 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +134 -23
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +19 -2
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +188 -9
- package/dist/WebGPUDevice.js.map +1 -1
- package/dist/index.d.ts +25 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -19
- package/dist/index.js.map +1 -1
- package/dist/lean/LeanProductRenderer.d.ts +29 -0
- package/dist/lean/LeanProductRenderer.d.ts.map +1 -0
- package/dist/lean/LeanProductRenderer.js +38 -0
- package/dist/lean/LeanProductRenderer.js.map +1 -0
- package/dist/lean/LeanProductionRenderer.d.ts +38 -0
- package/dist/lean/LeanProductionRenderer.d.ts.map +1 -0
- package/dist/lean/LeanProductionRenderer.js +88 -0
- package/dist/lean/LeanProductionRenderer.js.map +1 -0
- package/dist/lean-runtime.d.ts +18 -0
- package/dist/lean-runtime.d.ts.map +1 -0
- package/dist/lean-runtime.js +13 -0
- package/dist/lean-runtime.js.map +1 -0
- package/dist/postprocess/EffectComposer.d.ts.map +1 -1
- package/dist/postprocess/EffectComposer.js +1 -3
- package/dist/postprocess/EffectComposer.js.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.js +5 -1
- package/dist/production-runtime/PBRHDRPipeline.js.map +1 -1
- package/dist/production-runtime/ProductionRendererTypes.d.ts +9 -0
- package/dist/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
- package/dist/production-runtime/ProductionRendererTypes.js.map +1 -1
- package/dist/production-runtime/ProductionRuntimeRenderer.d.ts +5 -0
- package/dist/production-runtime/ProductionRuntimeRenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionRuntimeRenderer.js +53 -5
- package/dist/production-runtime/ProductionRuntimeRenderer.js.map +1 -1
- package/dist/production-runtime/ProductionWebGL2Renderer.d.ts +10 -0
- package/dist/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGL2Renderer.js +21 -2
- package/dist/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.js +4 -1
- package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/webgpu-device-entry.d.ts +25 -0
- package/dist/webgpu-device-entry.d.ts.map +1 -0
- package/dist/webgpu-device-entry.js +24 -0
- package/dist/webgpu-device-entry.js.map +1 -0
- package/dist/webgpu.d.ts +25 -0
- package/dist/webgpu.d.ts.map +1 -0
- package/dist/webgpu.js +24 -0
- package/dist/webgpu.js.map +1 -0
- 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
|