@meimu/mutracer 0.1.3 → 0.1.5
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/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +4155 -4052
- package/docs/api.md +13 -10
- package/docs/materials.md +17 -26
- package/docs/threejs.md +35 -9
- package/docs/viewer.md +36 -3
- package/lib/Engine.js +18 -1
- package/lib/Engine.js.map +1 -1
- package/lib/main.d.ts +1 -1
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/MaterialTable.d.ts +1 -0
- package/lib/renderer/MaterialTable.js +10 -4
- package/lib/renderer/MaterialTable.js.map +1 -1
- package/lib/renderer/WGPUBloomPass.js +18 -18
- package/lib/renderer/WGPUFullQuadPass.js +6 -6
- package/lib/renderer/WGPUJNLMDenoiser.js +69 -69
- package/lib/renderer/WGPURayTraceOutput.js +14 -14
- package/lib/renderer/WGPURayTracePass.d.ts +2 -0
- package/lib/renderer/WGPURayTracePass.js +17 -9
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +15 -15
- package/lib/renderer/WebGLJNLMDenoiser.js +69 -69
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +1 -0
- package/lib/renderer/WebGLRayTracePass.js +8 -1
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/envMapCreation.d.ts +5 -1
- package/lib/renderer/envMapCreation.js +42 -5
- package/lib/renderer/envMapCreation.js.map +1 -1
- package/lib/renderer/geometryUpdates.js +1 -1
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +12 -12
- package/lib/renderer/glsl/chunks/envMap.glsl.js +66 -54
- package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/intersect.glsl.js +323 -315
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +154 -121
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +434 -434
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +42 -41
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +69 -69
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +84 -84
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +12 -12
- package/lib/renderer/glsl/chunks/sampling.glsl.js +75 -75
- package/lib/renderer/glsl/chunks/sobol.glsl.js +61 -61
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +5 -5
- package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +8 -8
- package/lib/renderer/glsl/rayTrace.frag.js +133 -133
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +9 -9
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +52 -52
- package/lib/renderer/lightsUtil.js +6 -4
- package/lib/renderer/lightsUtil.js.map +1 -1
- package/lib/renderer/materialChanges.js +1 -1
- package/lib/renderer/materialChanges.js.map +1 -1
- package/lib/renderer/materialComparison.generated.js +36 -18
- package/lib/renderer/materialComparison.generated.js.map +1 -1
- package/lib/renderer/prepareMeshBVH.js +2 -2
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/sceneChangeDetector.js +4 -1
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- package/lib/renderer/sceneLightChanges.js +3 -2
- package/lib/renderer/sceneLightChanges.js.map +1 -1
- package/lib/renderer/scratchSampling.js +18 -18
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +39 -31
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +476 -470
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +8 -8
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +95 -60
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +469 -469
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +117 -120
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +25 -22
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +127 -127
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +136 -136
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +6 -6
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +73 -73
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +62 -62
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/compose.frag.wgsl.js +105 -105
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/depthQuery.wgsl.js +5 -5
- package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +28 -28
- package/lib/renderer/wgsl/rayTrace.compute.js +251 -248
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/EnvironmentLight.d.ts +9 -3
- package/lib/scene/EnvironmentLight.js +9 -2
- package/lib/scene/EnvironmentLight.js.map +1 -1
- package/lib/scene/OpenPBRMaterial.d.ts +1 -1
- package/lib/scene/OpenPBRMaterial.js +1 -1
- package/lib/scene/OpenPBRMaterial.js.map +1 -1
- package/lib/scene/Scene.d.ts +4 -0
- package/lib/scene/Scene.js.map +1 -1
- package/lib/scene/SpotLight.d.ts +2 -2
- package/lib/scene/SpotLight.js +1 -1
- package/lib/scene/SpotLight.js.map +1 -1
- package/lib/scene/Texture.d.ts +17 -6
- package/lib/scene/Texture.js +13 -4
- package/lib/scene/Texture.js.map +1 -1
- package/lib/util/fromTHREE.js +151 -29
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +1 -1
package/lib/util/fromTHREE.js
CHANGED
|
@@ -26,11 +26,11 @@ function convertTexture(threeTexture, textureStore) {
|
|
|
26
26
|
return;
|
|
27
27
|
const state = [threeTexture.version, threeTexture.image, threeTexture.flipY,
|
|
28
28
|
threeTexture.repeat.x, threeTexture.repeat.y, threeTexture.offset.x,
|
|
29
|
-
threeTexture.offset.y, threeTexture.channel];
|
|
29
|
+
threeTexture.offset.y, threeTexture.channel, threeTexture.wrapS, threeTexture.wrapT, threeTexture.colorSpace];
|
|
30
30
|
const cached = textureStore.get(threeTexture);
|
|
31
31
|
if (cached && snapshotsEqual(cached.state, state))
|
|
32
32
|
return cached.texture;
|
|
33
|
-
const image = threeTexture
|
|
33
|
+
const image = convertTextureImage(threeTexture);
|
|
34
34
|
const texture = cached?.texture ?? new Texture(image);
|
|
35
35
|
texture.image = image;
|
|
36
36
|
texture.source = threeTexture;
|
|
@@ -38,6 +38,13 @@ function convertTexture(threeTexture, textureStore) {
|
|
|
38
38
|
texture.uvScale = [threeTexture.repeat.x, threeTexture.repeat.y];
|
|
39
39
|
texture.uvOffset = [threeTexture.offset.x, threeTexture.offset.y];
|
|
40
40
|
texture.uvChannel = threeTexture.channel === 1 ? 1 : 0;
|
|
41
|
+
const wrap = (value) => value === 1001 ? 'clamp-to-edge'
|
|
42
|
+
: value === 1002 ? 'mirrored-repeat' : 'repeat';
|
|
43
|
+
texture.wrapS = wrap(threeTexture.wrapS);
|
|
44
|
+
texture.wrapT = wrap(threeTexture.wrapT);
|
|
45
|
+
// Three.js NoColorSpace also reads color channels without a transfer function.
|
|
46
|
+
texture.colorSpace = threeTexture.colorSpace === 'srgb' ? 'srgb'
|
|
47
|
+
: threeTexture.colorSpace === 'srgb-linear' || threeTexture.colorSpace === '' ? 'linear' : undefined;
|
|
41
48
|
if (cached)
|
|
42
49
|
texture.markUpdated();
|
|
43
50
|
else
|
|
@@ -57,6 +64,49 @@ function halfFloatToFloat(value) {
|
|
|
57
64
|
}
|
|
58
65
|
return sign * Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
|
|
59
66
|
}
|
|
67
|
+
/** Decode Three.js storage into the RGBA byte/float images used by MuTracer. */
|
|
68
|
+
function convertTextureImage(texture) {
|
|
69
|
+
const image = texture.image;
|
|
70
|
+
if (!image?.data)
|
|
71
|
+
return image;
|
|
72
|
+
// Three.js enums are kept numeric to retain the optional, type-only peer import.
|
|
73
|
+
const channels = texture.format === 1028 ? 1 : texture.format === 1030 ? 2
|
|
74
|
+
: texture.format === 1022 ? 3 : texture.format === 1023 ? 4 : 0;
|
|
75
|
+
if (!channels)
|
|
76
|
+
throw new Error(`Unsupported Three.js data texture format: ${texture.format}`);
|
|
77
|
+
const data = image.data;
|
|
78
|
+
if (data.length !== image.width * image.height * channels) {
|
|
79
|
+
throw new Error('Three.js data texture length does not match its format and dimensions.');
|
|
80
|
+
}
|
|
81
|
+
const half = texture.type === 1016;
|
|
82
|
+
if (channels === 4 && !half && (data instanceof Float32Array || data instanceof Uint8Array ||
|
|
83
|
+
data instanceof Uint8ClampedArray || data instanceof Int32Array))
|
|
84
|
+
return image;
|
|
85
|
+
const bytes = data instanceof Uint8Array || data instanceof Uint8ClampedArray;
|
|
86
|
+
const result = bytes ? new Uint8Array(image.width * image.height * 4)
|
|
87
|
+
: new Float32Array(image.width * image.height * 4);
|
|
88
|
+
const read = (index) => {
|
|
89
|
+
const value = data[index];
|
|
90
|
+
if (half)
|
|
91
|
+
return halfFloatToFloat(value);
|
|
92
|
+
if (data instanceof Uint16Array)
|
|
93
|
+
return value / 65535;
|
|
94
|
+
if (data instanceof Uint32Array)
|
|
95
|
+
return value / 4294967295;
|
|
96
|
+
if (data instanceof Int8Array)
|
|
97
|
+
return Math.max(-1, value / 127);
|
|
98
|
+
if (data instanceof Int16Array)
|
|
99
|
+
return Math.max(-1, value / 32767);
|
|
100
|
+
return value;
|
|
101
|
+
};
|
|
102
|
+
for (let i = 0; i < image.width * image.height; i++) {
|
|
103
|
+
result[i * 4] = read(i * channels);
|
|
104
|
+
result[i * 4 + 1] = channels > 1 ? read(i * channels + 1) : 0;
|
|
105
|
+
result[i * 4 + 2] = channels > 2 ? read(i * channels + 2) : 0;
|
|
106
|
+
result[i * 4 + 3] = channels === 4 ? read(i * channels + 3) : bytes ? 255 : 1;
|
|
107
|
+
}
|
|
108
|
+
return { width: image.width, height: image.height, data: result };
|
|
109
|
+
}
|
|
60
110
|
function convertDataTexture(threeTexture, textureStore) {
|
|
61
111
|
if (!threeTexture?.image?.data || !threeTexture.image.width || !threeTexture.image.height)
|
|
62
112
|
return;
|
|
@@ -64,14 +114,25 @@ function convertDataTexture(threeTexture, textureStore) {
|
|
|
64
114
|
const texture = convertTexture(threeTexture, textureStore);
|
|
65
115
|
if (previous?.state === textureStore.get(threeTexture)?.state)
|
|
66
116
|
return texture;
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
texture.image = { data
|
|
117
|
+
const data = texture.image.data;
|
|
118
|
+
const { width, height } = threeTexture.image;
|
|
119
|
+
// Three.js equirect maps put the sky at v = 1, so with flipY = false the
|
|
120
|
+
// first row is the nadir. MuTracer environments start at the zenith and
|
|
121
|
+
// ignore flipY, so reorder the rows here instead.
|
|
122
|
+
texture.image = { data: threeTexture.flipY ? data : flipRows(data, width, height), width, height };
|
|
123
|
+
texture.flipY = false;
|
|
73
124
|
return texture;
|
|
74
125
|
}
|
|
126
|
+
function flipRows(data, width, height) {
|
|
127
|
+
if (height <= 1)
|
|
128
|
+
return data;
|
|
129
|
+
const rowLength = data.length / height;
|
|
130
|
+
const flipped = new data.constructor(data.length);
|
|
131
|
+
for (let y = 0; y < height; y++) {
|
|
132
|
+
flipped.set(data.subarray((height - 1 - y) * rowLength, (height - y) * rowLength), y * rowLength);
|
|
133
|
+
}
|
|
134
|
+
return flipped;
|
|
135
|
+
}
|
|
75
136
|
function convertMaterial(threeMaterial, textureCache) {
|
|
76
137
|
const material = new OpenPBRMaterial();
|
|
77
138
|
if (threeMaterial.userData.mutracerShadowCatcher === true) {
|
|
@@ -178,8 +239,12 @@ function convertMaterial(threeMaterial, textureCache) {
|
|
|
178
239
|
material.transmissionWeight = physicalMaterial.transmission;
|
|
179
240
|
material.transmissionMap = convertTexture(physicalMaterial.transmissionMap, textureCache);
|
|
180
241
|
material.specularIor = physicalMaterial.ior;
|
|
181
|
-
|
|
182
|
-
|
|
242
|
+
// glTF specular (KHR_materials_specular) only scales the dielectric
|
|
243
|
+
// lobe, while OpenPBR applies it to metals too. A fully metallic
|
|
244
|
+
// surface has no dielectric part, so keep the OpenPBR defaults there.
|
|
245
|
+
const fullyMetallic = physicalMaterial.metalness === 1 && !physicalMaterial.metalnessMap;
|
|
246
|
+
material.specularWeight = fullyMetallic ? 1 : physicalMaterial.specularIntensity;
|
|
247
|
+
if (physicalMaterial.specularColor && !fullyMetallic) {
|
|
183
248
|
material.specularColor =
|
|
184
249
|
physicalMaterial.specularColor.toArray();
|
|
185
250
|
}
|
|
@@ -189,8 +254,10 @@ function convertMaterial(threeMaterial, textureCache) {
|
|
|
189
254
|
material.coatNormalScale = physicalMaterial.clearcoatNormalScale.toArray();
|
|
190
255
|
material.fuzzColorMap = convertTexture(physicalMaterial.sheenColorMap, textureCache);
|
|
191
256
|
material.fuzzRoughnessMap = convertTexture(physicalMaterial.sheenRoughnessMap, textureCache);
|
|
192
|
-
|
|
193
|
-
|
|
257
|
+
if (!fullyMetallic) {
|
|
258
|
+
material.specularWeightMap = convertTexture(physicalMaterial.specularIntensityMap, textureCache);
|
|
259
|
+
material.specularColorMap = convertTexture(physicalMaterial.specularColorMap, textureCache);
|
|
260
|
+
}
|
|
194
261
|
material.coatWeight = physicalMaterial.clearcoat;
|
|
195
262
|
material.coatRoughness = physicalMaterial.clearcoatRoughness;
|
|
196
263
|
// MeshPhysicalMaterial's clearcoat BRDF uses the fixed dielectric F0
|
|
@@ -360,6 +427,12 @@ function convertMaterial(threeMaterial, textureCache) {
|
|
|
360
427
|
!!standardMaterial.alphaMap
|
|
361
428
|
? 'blend'
|
|
362
429
|
: 'opaque';
|
|
430
|
+
if (standardMaterial.side === 1) {
|
|
431
|
+
// BackSide normals are reversed in converted geometry. Keep the source
|
|
432
|
+
// displacement direction and biased base surface unchanged.
|
|
433
|
+
material.displacementScale = -material.displacementScale;
|
|
434
|
+
material.displacementBias = -material.displacementBias;
|
|
435
|
+
}
|
|
363
436
|
return material;
|
|
364
437
|
}
|
|
365
438
|
function copyFloatAttribute(attribute, itemSize = attribute.itemSize) {
|
|
@@ -397,12 +470,13 @@ function copyFloatAttribute(attribute, itemSize = attribute.itemSize) {
|
|
|
397
470
|
}
|
|
398
471
|
return result;
|
|
399
472
|
}
|
|
400
|
-
function convertGeometry(threeGeometry, drawRange) {
|
|
473
|
+
function convertGeometry(threeGeometry, drawRange, vertexColors, backSide) {
|
|
401
474
|
const positionAttrb = threeGeometry.getAttribute('position');
|
|
402
475
|
const normalAttrb = threeGeometry.getAttribute('normal');
|
|
403
476
|
const tangentAttrb = threeGeometry.getAttribute('tangent');
|
|
404
477
|
const uvAttrb = threeGeometry.getAttribute('uv');
|
|
405
478
|
const uv1Attrb = threeGeometry.getAttribute('uv1');
|
|
479
|
+
const colorAttrb = vertexColors && threeGeometry.getAttribute('color');
|
|
406
480
|
const indexAttrb = threeGeometry.getIndex();
|
|
407
481
|
const sourceCount = indexAttrb ? indexAttrb.array.length : positionAttrb.count;
|
|
408
482
|
// Match Array.slice range handling without expanding the complete index stream.
|
|
@@ -427,6 +501,13 @@ function convertGeometry(threeGeometry, drawRange) {
|
|
|
427
501
|
for (let index = 0; index < indexCount; index++)
|
|
428
502
|
indices[index] = start + index;
|
|
429
503
|
}
|
|
504
|
+
if (backSide) {
|
|
505
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
506
|
+
const tmp = indices[i + 1];
|
|
507
|
+
indices[i + 1] = indices[i + 2];
|
|
508
|
+
indices[i + 2] = tmp;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
430
511
|
const geometry = new Geometry({
|
|
431
512
|
position: new Attribute(copyFloatAttribute(positionAttrb), positionAttrb.itemSize),
|
|
432
513
|
normal: normalAttrb &&
|
|
@@ -435,21 +516,40 @@ function convertGeometry(threeGeometry, drawRange) {
|
|
|
435
516
|
uv: uvAttrb && new Attribute(copyFloatAttribute(uvAttrb), uvAttrb.itemSize),
|
|
436
517
|
uv1: uv1Attrb &&
|
|
437
518
|
new Attribute(copyFloatAttribute(uv1Attrb), uv1Attrb.itemSize),
|
|
519
|
+
// MuTracer stores linear RGB only; omit Three.js's optional vertex alpha.
|
|
520
|
+
color: colorAttrb ? new Attribute(copyFloatAttribute(colorAttrb, 3), 3) : undefined,
|
|
438
521
|
indices: new Attribute(indices, 1)
|
|
439
522
|
});
|
|
440
|
-
|
|
441
|
-
|
|
523
|
+
// Generated normals already follow the reversed indices; authored normals do not.
|
|
524
|
+
if (backSide) {
|
|
525
|
+
if (normalAttrb)
|
|
526
|
+
negateNormals(geometry);
|
|
527
|
+
reverseTangentHandedness(geometry);
|
|
442
528
|
}
|
|
443
529
|
updateBoundingBox(geometry);
|
|
444
530
|
return geometry;
|
|
445
531
|
}
|
|
532
|
+
function negateNormals(geometry) {
|
|
533
|
+
const normals = geometry.normal.array;
|
|
534
|
+
for (let i = 0; i < normals.length; i++)
|
|
535
|
+
normals[i] = -normals[i];
|
|
536
|
+
}
|
|
537
|
+
function reverseTangentHandedness(geometry) {
|
|
538
|
+
// Three.js FLIP_SIDED reverses N but preserves T and B. Reversing W
|
|
539
|
+
// preserves that bitangent when the renderer reconstructs cross(N,T)*W.
|
|
540
|
+
const tangent = geometry.tangent;
|
|
541
|
+
if (tangent?.itemSize === 4)
|
|
542
|
+
for (let i = 3; i < tangent.array.length; i += 4)
|
|
543
|
+
tangent.array[i] *= -1;
|
|
544
|
+
}
|
|
446
545
|
function createConversionStore() {
|
|
447
|
-
return { updateId: 0, textures: new WeakMap(),
|
|
546
|
+
return { updateId: 0, textures: new WeakMap(), equirectTextures: new WeakMap(),
|
|
547
|
+
materials: new WeakMap(), geometries: new WeakMap(),
|
|
448
548
|
materialHooks: new WeakMap(), lightHooks: new WeakMap(),
|
|
449
549
|
poseChanged: false, meshes: new WeakMap(), lights: new WeakMap(), deformedGeometries: new WeakMap(), deformations: new WeakMap(),
|
|
450
550
|
instanceMeshes: new WeakMap(), projectionVariants: new Map(), warned: new WeakSet() };
|
|
451
551
|
}
|
|
452
|
-
const geometryAttributeNames = ['position', 'normal', 'tangent', 'uv', 'uv1', 'index'];
|
|
552
|
+
const geometryAttributeNames = ['position', 'normal', 'tangent', 'uv', 'uv1', 'color', 'index'];
|
|
453
553
|
function geometryState(geometry, deformation) {
|
|
454
554
|
const state = [];
|
|
455
555
|
for (let i = 0; i < geometryAttributeNames.length; i++) {
|
|
@@ -703,30 +803,48 @@ function convertScene(scene, options, store) {
|
|
|
703
803
|
? group.count
|
|
704
804
|
: (child.geometry.getIndex()?.count ??
|
|
705
805
|
child.geometry.getAttribute('position').count);
|
|
706
|
-
const
|
|
806
|
+
const sourceMaterial = threeMaterials[group.materialIndex ?? 0];
|
|
807
|
+
// Vertex colors and BackSide orientation are baked into geometry, so
|
|
808
|
+
// groups with different switches cannot share the same cached variant.
|
|
809
|
+
const vertexColors = sourceMaterial.vertexColors === true;
|
|
810
|
+
const backSide = sourceMaterial.side === 1;
|
|
811
|
+
const geometryKey = `${group.start}:${count}:${vertexColors}:${backSide}`;
|
|
707
812
|
let geometry = geometryEntry.groups.get(geometryKey);
|
|
708
813
|
const newGeometry = !geometry;
|
|
709
814
|
if (!geometry) {
|
|
710
|
-
const converted = convertGeometry(child.geometry, { start: group.start, count });
|
|
815
|
+
const converted = convertGeometry(child.geometry, { start: group.start, count }, vertexColors, backSide);
|
|
711
816
|
if (!converted)
|
|
712
817
|
return;
|
|
713
818
|
geometry = converted;
|
|
714
819
|
geometryEntry.groups.set(geometryKey, geometry);
|
|
715
820
|
}
|
|
716
|
-
|
|
821
|
+
// A retained color/side variant may predate the current pose.
|
|
822
|
+
if (deformation && (deformed || newGeometry || store.meshes.get(child)?.get(groupIndex)?.geometry !== geometry)) {
|
|
717
823
|
geometry.dynamic = true;
|
|
718
824
|
geometry.position.array.set(deformation.position);
|
|
719
|
-
if (deformation.normal)
|
|
825
|
+
if (deformation.normal) {
|
|
720
826
|
geometry.normal.array.set(deformation.normal);
|
|
721
|
-
|
|
827
|
+
if (backSide)
|
|
828
|
+
negateNormals(geometry);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
// Retain resident attribute storage so a pose change can refit its BVH.
|
|
832
|
+
const normal = geometry.normal;
|
|
833
|
+
generateVertexNormals(geometry);
|
|
834
|
+
normal.array.set(geometry.normal.array);
|
|
835
|
+
geometry.normal = normal;
|
|
836
|
+
}
|
|
837
|
+
if (deformation.tangent && geometry.tangent) {
|
|
722
838
|
geometry.tangent.array.set(deformation.tangent);
|
|
839
|
+
if (backSide)
|
|
840
|
+
reverseTangentHandedness(geometry);
|
|
841
|
+
}
|
|
723
842
|
if (!child.geometry.getAttribute('tangent'))
|
|
724
843
|
updateDynamicTangents(geometry);
|
|
725
844
|
updateBoundingBox(geometry);
|
|
726
845
|
if (!newGeometry)
|
|
727
846
|
markDeformedGeometry(geometry);
|
|
728
847
|
}
|
|
729
|
-
const sourceMaterial = threeMaterials[group.materialIndex ?? 0];
|
|
730
848
|
let material = resolveMaterial(sourceMaterial);
|
|
731
849
|
const baseMaterial = material;
|
|
732
850
|
const autoUvProjection = !geometry.uv && baseMaterial.uvProjection === 'none' &&
|
|
@@ -890,14 +1008,16 @@ function convertScene(scene, options, store) {
|
|
|
890
1008
|
}
|
|
891
1009
|
});
|
|
892
1010
|
const threeEnvironment = scene.environment;
|
|
893
|
-
const environmentMap = convertDataTexture(threeEnvironment,
|
|
1011
|
+
const environmentMap = convertDataTexture(threeEnvironment, store.equirectTextures);
|
|
894
1012
|
const environmentLight = threeEnvironment && environmentMap
|
|
895
|
-
? retainLight(store, threeEnvironment, new EnvironmentLight({ map: environmentMap }))
|
|
1013
|
+
? retainLight(store, threeEnvironment, new EnvironmentLight({ map: environmentMap, intensity: scene.environmentIntensity }))
|
|
896
1014
|
: undefined;
|
|
897
|
-
if (environmentLight)
|
|
1015
|
+
if (environmentLight) {
|
|
1016
|
+
environmentLight.transform = scene.matrix.clone().makeRotationFromEuler(scene.environmentRotation).elements.slice();
|
|
898
1017
|
lights.push(environmentLight);
|
|
1018
|
+
}
|
|
899
1019
|
const grounded = scene.userData.mutracerGroundedSkybox;
|
|
900
|
-
const groundedTexture = grounded && convertDataTexture(grounded.texture,
|
|
1020
|
+
const groundedTexture = grounded && convertDataTexture(grounded.texture, store.equirectTextures);
|
|
901
1021
|
const hideEnvironmentBackground = scene.userData.mutracerHiddenBackground === true;
|
|
902
1022
|
let background = hideEnvironmentBackground
|
|
903
1023
|
? [0, 0, 0, 0]
|
|
@@ -908,14 +1028,16 @@ function convertScene(scene, options, store) {
|
|
|
908
1028
|
height: grounded.height,
|
|
909
1029
|
radius: grounded.radius,
|
|
910
1030
|
groundHeight: grounded.groundHeight,
|
|
911
|
-
center: grounded.center
|
|
1031
|
+
center: grounded.center,
|
|
1032
|
+
rotation: grounded.rotation,
|
|
1033
|
+
intensity: grounded.intensity
|
|
912
1034
|
}
|
|
913
1035
|
: scene.background
|
|
914
1036
|
? scene.background.isColor
|
|
915
1037
|
? [...scene.background.toArray(), 1]
|
|
916
1038
|
: scene.background === scene.environment && environmentLight
|
|
917
1039
|
? environmentLight.map
|
|
918
|
-
: convertDataTexture(scene.background,
|
|
1040
|
+
: convertDataTexture(scene.background, store.equirectTextures)
|
|
919
1041
|
: undefined;
|
|
920
1042
|
const oldBackground = store.background;
|
|
921
1043
|
if (Array.isArray(background) && Array.isArray(oldBackground) && snapshotsEqual(background, oldBackground)) {
|