rayzee 7.26.0 → 7.26.2
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 +7 -4
- package/dist/rayzee.es.js +1820 -1847
- package/dist/rayzee.umd.js +58 -58
- package/package.json +1 -1
- package/src/PathTracerApp.js +43 -14
- package/src/Processor/AssetLoader.js +103 -37
- package/src/Processor/GeometryExtractor.js +1 -38
- package/src/Processor/SceneProcessor.js +0 -4
- package/src/Processor/utils.js +11 -0
- package/src/Stages/PathTracerStage.js +0 -3
- package/src/managers/MaterialDataManager.js +2 -145
package/package.json
CHANGED
package/src/PathTracerApp.js
CHANGED
|
@@ -57,6 +57,8 @@ const _appsByCanvas = new WeakMap();
|
|
|
57
57
|
* - `app.settings` — {@link RenderSettings} (all render parameters)
|
|
58
58
|
* - `app.stages` — Named pipeline stages for advanced control
|
|
59
59
|
* - `app.sceneMeshes` — meshes backing the BVH, in buffer order (see {@link refitBVH})
|
|
60
|
+
* - `app.sceneModel` — root of the rendered model (a copy, for {@link loadObject3D})
|
|
61
|
+
* - `app.getSceneObject(id)` — the rendered root for an appended object's id
|
|
60
62
|
*
|
|
61
63
|
* Extends EventDispatcher for event-driven communication with stores/UI.
|
|
62
64
|
*/
|
|
@@ -626,8 +628,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
626
628
|
this.interactionManager?.deselect();
|
|
627
629
|
this.transformManager?.detach?.();
|
|
628
630
|
|
|
629
|
-
// Release the loaded model. If loaded via loadObject3D(), the caller owns it —
|
|
630
|
-
// we only detach it from the scene. Otherwise dispose geometries/materials/textures.
|
|
631
631
|
this.assetLoader?.releaseTargetModel();
|
|
632
632
|
|
|
633
633
|
// Clear lights in the WebGPU light scene
|
|
@@ -665,7 +665,13 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
665
665
|
/**
|
|
666
666
|
* Loads a Three.js Object3D directly into the path tracer scene.
|
|
667
667
|
* Builds BVH from the object's meshes and uploads scene data.
|
|
668
|
-
*
|
|
668
|
+
*
|
|
669
|
+
* Renders a copy: `object3d` is never reparented, rewritten or disposed, so passing a
|
|
670
|
+
* subtree of a scene the host still renders is safe. Geometry/material/texture are shared
|
|
671
|
+
* by reference, and any ancestor transform is baked in. Later edits to `object3d` do not
|
|
672
|
+
* reach the render — mutate {@link sceneModel}, then {@link refitBVH}/{@link refitBLASes}.
|
|
673
|
+
*
|
|
674
|
+
* @param {import('three').Object3D} object3d - The Object3D to render; left untouched.
|
|
669
675
|
* @param {string} [name='object3d'] - Display name for the object
|
|
670
676
|
*/
|
|
671
677
|
async loadObject3D( object3d, name = 'object3d' ) {
|
|
@@ -1134,10 +1140,9 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1134
1140
|
/** Tag the primary (replace-loaded) model as a removable scene object (read by the Outliner + removeSceneObject). Idempotent. */
|
|
1135
1141
|
_tagPrimarySceneObject() {
|
|
1136
1142
|
|
|
1137
|
-
const m = this.
|
|
1143
|
+
const m = this.sceneModel;
|
|
1138
1144
|
if ( ! m ) return;
|
|
1139
1145
|
m.userData.__rayzeeSceneObject = true;
|
|
1140
|
-
m.userData.__rayzeeExternal = ( m === this.assetLoader._externalModel );
|
|
1141
1146
|
|
|
1142
1147
|
}
|
|
1143
1148
|
|
|
@@ -1147,12 +1152,12 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1147
1152
|
const scene = this.meshScene;
|
|
1148
1153
|
if ( ! scene ) return;
|
|
1149
1154
|
const floor = this.assetLoader?.floorPlane;
|
|
1150
|
-
const primary = this.
|
|
1155
|
+
const primary = this.sceneModel;
|
|
1151
1156
|
for ( const child of [ ...scene.children ] ) {
|
|
1152
1157
|
|
|
1153
1158
|
if ( child === floor || child === primary ) continue;
|
|
1154
1159
|
if ( ! child.userData?.__rayzeeSceneObject ) continue;
|
|
1155
|
-
this.assetLoader.removeModelRoot( child
|
|
1160
|
+
this.assetLoader.removeModelRoot( child );
|
|
1156
1161
|
|
|
1157
1162
|
}
|
|
1158
1163
|
|
|
@@ -1191,7 +1196,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1191
1196
|
|
|
1192
1197
|
const { root } = await this.assetLoader.appendModel( url );
|
|
1193
1198
|
root.userData.__rayzeeSceneObject = true;
|
|
1194
|
-
root.userData.__rayzeeExternal = false;
|
|
1195
1199
|
if ( name ) root.userData.__rayzeeName = name;
|
|
1196
1200
|
await this._finishRebuildNoReframe( { type: 'ModelAdded', url, id: root.uuid } );
|
|
1197
1201
|
return root.uuid;
|
|
@@ -1207,7 +1211,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1207
1211
|
|
|
1208
1212
|
/**
|
|
1209
1213
|
* Append a caller-owned Object3D to the current scene, then rebuild (no reframe).
|
|
1210
|
-
*
|
|
1214
|
+
* Appends a copy; the object passed in is never mutated. Same rules as {@link loadObject3D}.
|
|
1211
1215
|
* @param {import('three').Object3D} object3d
|
|
1212
1216
|
* @param {Object} [opts]
|
|
1213
1217
|
* @param {string} [opts.name]
|
|
@@ -1227,7 +1231,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1227
1231
|
|
|
1228
1232
|
const { root } = this.assetLoader.appendObject3D( object3d, name || 'object3d' );
|
|
1229
1233
|
root.userData.__rayzeeSceneObject = true;
|
|
1230
|
-
root.userData.__rayzeeExternal = true;
|
|
1231
1234
|
if ( name ) root.userData.__rayzeeName = name;
|
|
1232
1235
|
await this._finishRebuildNoReframe( { type: 'ModelAdded', id: root.uuid } );
|
|
1233
1236
|
return root.uuid;
|
|
@@ -1254,7 +1257,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1254
1257
|
const floor = this.assetLoader?.floorPlane;
|
|
1255
1258
|
if ( floor && floor.uuid === id ) return false; // Ground is not deletable
|
|
1256
1259
|
|
|
1257
|
-
const root =
|
|
1260
|
+
const root = this.getSceneObject( id );
|
|
1258
1261
|
if ( ! root ) return false;
|
|
1259
1262
|
|
|
1260
1263
|
if ( this._loadingInProgress ) {
|
|
@@ -1269,13 +1272,13 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1269
1272
|
this.interactionManager?.deselect();
|
|
1270
1273
|
this.transformManager?.detach?.();
|
|
1271
1274
|
|
|
1272
|
-
if ( root === this.
|
|
1275
|
+
if ( root === this.sceneModel ) {
|
|
1273
1276
|
|
|
1274
1277
|
this.assetLoader.releaseTargetModel();
|
|
1275
1278
|
|
|
1276
1279
|
} else {
|
|
1277
1280
|
|
|
1278
|
-
this.assetLoader.removeModelRoot( root
|
|
1281
|
+
this.assetLoader.removeModelRoot( root );
|
|
1279
1282
|
|
|
1280
1283
|
}
|
|
1281
1284
|
|
|
@@ -1378,6 +1381,32 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1378
1381
|
|
|
1379
1382
|
}
|
|
1380
1383
|
|
|
1384
|
+
/**
|
|
1385
|
+
* Root of the model actually being rendered. For {@link loadObject3D} this is the engine's
|
|
1386
|
+
* copy, not the object you passed — mutate this one, then {@link refitBVH}/{@link refitBLASes}.
|
|
1387
|
+
*
|
|
1388
|
+
* @returns {import('three').Object3D|null} Live reference, or null when nothing is loaded.
|
|
1389
|
+
*/
|
|
1390
|
+
get sceneModel() {
|
|
1391
|
+
|
|
1392
|
+
return this.assetLoader?.targetModel ?? null;
|
|
1393
|
+
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
/**
|
|
1397
|
+
* Resolve an id from {@link addModel}/{@link addModelFromObject3D} to the root being
|
|
1398
|
+
* rendered for it — for an appended Object3D, the engine's copy.
|
|
1399
|
+
*
|
|
1400
|
+
* @param {string} id
|
|
1401
|
+
* @returns {import('three').Object3D|null} Live reference, or null if no such object.
|
|
1402
|
+
*/
|
|
1403
|
+
getSceneObject( id ) {
|
|
1404
|
+
|
|
1405
|
+
const root = this.meshScene?.children.find( c => c.uuid === id );
|
|
1406
|
+
return root?.userData?.__rayzeeSceneObject ? root : null;
|
|
1407
|
+
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1381
1410
|
/**
|
|
1382
1411
|
* Update vertex positions for animation without full BVH rebuild.
|
|
1383
1412
|
* O(N) bottom-up AABB refit instead of O(N log N) SAH rebuild.
|
|
@@ -2839,7 +2868,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2839
2868
|
const animations = this.assetLoader?.animations || [];
|
|
2840
2869
|
if ( animations.length > 0 ) {
|
|
2841
2870
|
|
|
2842
|
-
const mixerRoot = this.
|
|
2871
|
+
const mixerRoot = this.sceneModel || this.meshScene;
|
|
2843
2872
|
this.animationManager.init( this.meshScene, mixerRoot, this._sdf.meshes, animations );
|
|
2844
2873
|
this.animationManager.onFinished = () => {
|
|
2845
2874
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box3, Vector3, RectAreaLight, Color, FloatType, LinearFilter, EquirectangularReflectionMapping,
|
|
1
|
+
import { Box3, BufferGeometry, Vector3, RectAreaLight, Color, FloatType, LinearFilter, EquirectangularReflectionMapping,
|
|
2
2
|
TextureLoader, Texture, SRGBColorSpace, RepeatWrapping, Mesh, MeshStandardMaterial, MeshPhysicalMaterial,
|
|
3
3
|
CircleGeometry, Points, PointsMaterial, LoadingManager, EventDispatcher
|
|
4
4
|
} from 'three';
|
|
@@ -8,9 +8,10 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
|
8
8
|
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
|
|
9
9
|
import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
|
|
10
10
|
import { createMeshesFromMultiMaterialMesh } from 'three/addons/utils/SceneUtils.js';
|
|
11
|
+
import { clone as cloneWithSkeletons } from 'three/addons/utils/SkeletonUtils.js';
|
|
11
12
|
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
|
|
12
13
|
import { unzipSync, zipSync, strFromU8 } from 'three/addons/libs/fflate.module.js';
|
|
13
|
-
import { disposeObjectFromMemory, updateLoading } from './utils';
|
|
14
|
+
import { disposeEngineOwnedResources, disposeObjectFromMemory, updateLoading } from './utils';
|
|
14
15
|
import { BuildTimer } from './BuildTimer.js';
|
|
15
16
|
import { getAssetConfig } from '../AssetConfig.js';
|
|
16
17
|
import { loadPBRTScene, pickEntryPath } from './PBRT/index.js';
|
|
@@ -36,6 +37,18 @@ const SUPPORTED_FORMATS = {
|
|
|
36
37
|
// layers and the image assets they reference.
|
|
37
38
|
const USD_LAYER_RE = /\.(usd|usda|usdc)$/i;
|
|
38
39
|
const USD_IMAGE_RE = /\.(png|jpg|jpeg|avif)$/i;
|
|
40
|
+
// A throwaway stand-in for a geometry the engine must not mutate: the split's mergeGroups()
|
|
41
|
+
// reorders and disposes what it is given, but never writes the attributes.
|
|
42
|
+
function standInForSplit( source ) {
|
|
43
|
+
|
|
44
|
+
const geometry = new BufferGeometry();
|
|
45
|
+
for ( const name in source.attributes ) geometry.setAttribute( name, source.attributes[ name ] );
|
|
46
|
+
if ( source.index ) geometry.setIndex( source.index );
|
|
47
|
+
for ( const group of source.groups ) geometry.addGroup( group.start, group.count, group.materialIndex );
|
|
48
|
+
return geometry;
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
/**
|
|
40
53
|
* AssetLoader class - handles loading of 3D models, environment maps, and archives
|
|
41
54
|
*/
|
|
@@ -48,7 +61,6 @@ export class AssetLoader extends EventDispatcher {
|
|
|
48
61
|
this.camera = camera;
|
|
49
62
|
this.controls = controls;
|
|
50
63
|
this.targetModel = null;
|
|
51
|
-
this._externalModel = null;
|
|
52
64
|
this.floorPlane = null;
|
|
53
65
|
this.sceneScale = 1.0;
|
|
54
66
|
this.loaderCache = {};
|
|
@@ -132,29 +144,58 @@ export class AssetLoader extends EventDispatcher {
|
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
147
|
+
* Deep-clones a caller-owned Object3D so the engine never mutates the host's tree.
|
|
148
|
+
* Geometry/material/texture ride along by reference, so release frees only what the engine
|
|
149
|
+
* allocated — see removeModelRoot().
|
|
150
|
+
*
|
|
151
|
+
* @param {import('three').Object3D} object3d - the caller's object; left untouched.
|
|
152
|
+
* @returns {import('three').Object3D} the engine-owned copy.
|
|
139
153
|
*/
|
|
140
|
-
|
|
154
|
+
_adoptExternalObject( object3d ) {
|
|
141
155
|
|
|
142
|
-
|
|
156
|
+
// Not Object3D.clone(): that leaves SkinnedMeshes bound to the source's bones.
|
|
157
|
+
let model;
|
|
158
|
+
try {
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
model = cloneWithSkeletons( object3d );
|
|
145
161
|
|
|
146
|
-
|
|
162
|
+
} catch ( error ) {
|
|
147
163
|
|
|
148
|
-
|
|
164
|
+
// Object3D.copy() round-trips userData through JSON, so a back-reference throws.
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Cannot render "${object3d.name || object3d.type}": its userData must be JSON-serializable.`,
|
|
167
|
+
{ cause: error }
|
|
168
|
+
);
|
|
149
169
|
|
|
150
|
-
}
|
|
170
|
+
}
|
|
151
171
|
|
|
152
|
-
|
|
172
|
+
model.userData.__rayzeeExternal = true;
|
|
173
|
+
|
|
174
|
+
// Carried through as the scene-object id, unless a second copy already took it.
|
|
175
|
+
if ( ! this.scene?.children.some( c => c.uuid === object3d.uuid ) ) model.uuid = object3d.uuid;
|
|
176
|
+
|
|
177
|
+
// The copy sits under the engine's identity root, which keeps only a local transform.
|
|
178
|
+
if ( object3d.parent ) {
|
|
179
|
+
|
|
180
|
+
object3d.parent.updateWorldMatrix( true, false );
|
|
181
|
+
model.applyMatrix4( object3d.parent.matrixWorld );
|
|
153
182
|
|
|
154
183
|
}
|
|
155
184
|
|
|
185
|
+
return model;
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Releases the current targetModel. See removeModelRoot() for what gets freed. */
|
|
190
|
+
releaseTargetModel() {
|
|
191
|
+
|
|
192
|
+
this.sceneMetadata = null;
|
|
193
|
+
|
|
194
|
+
if ( ! this.targetModel ) return;
|
|
195
|
+
|
|
196
|
+
this.removeModelRoot( this.targetModel );
|
|
197
|
+
|
|
156
198
|
this.targetModel = null;
|
|
157
|
-
this._externalModel = null;
|
|
158
199
|
// Drop the released model's animation clips so a later rebuild doesn't rebind
|
|
159
200
|
// a mixer to disposed nodes. Every load path re-populates this.animations after.
|
|
160
201
|
this.animations = [];
|
|
@@ -1180,21 +1221,32 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1180
1221
|
|
|
1181
1222
|
}
|
|
1182
1223
|
|
|
1183
|
-
// Append a caller-owned Object3D without releasing prior models or reframing.
|
|
1224
|
+
// Append a copy of a caller-owned Object3D without releasing prior models or reframing.
|
|
1184
1225
|
appendObject3D( object3d, name = 'object3d' ) {
|
|
1185
1226
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1227
|
+
const root = this._adoptExternalObject( object3d );
|
|
1228
|
+
root.name = object3d.name || name;
|
|
1229
|
+
this._processAndParent( root );
|
|
1230
|
+
return { root };
|
|
1189
1231
|
|
|
1190
1232
|
}
|
|
1191
1233
|
|
|
1192
|
-
// Detach + dispose
|
|
1193
|
-
|
|
1234
|
+
// Detach + dispose a model root. An adopted root's geometry/materials belong to the
|
|
1235
|
+
// caller, so only the engine's own allocations go.
|
|
1236
|
+
removeModelRoot( root ) {
|
|
1194
1237
|
|
|
1195
1238
|
if ( ! root ) return;
|
|
1196
|
-
|
|
1197
|
-
|
|
1239
|
+
|
|
1240
|
+
if ( root.userData.__rayzeeExternal ) {
|
|
1241
|
+
|
|
1242
|
+
disposeEngineOwnedResources( root );
|
|
1243
|
+
root.parent?.remove( root );
|
|
1244
|
+
|
|
1245
|
+
} else {
|
|
1246
|
+
|
|
1247
|
+
disposeObjectFromMemory( root );
|
|
1248
|
+
|
|
1249
|
+
}
|
|
1198
1250
|
|
|
1199
1251
|
}
|
|
1200
1252
|
|
|
@@ -1510,17 +1562,18 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1510
1562
|
|
|
1511
1563
|
async loadObject3D( object3d, name = 'object3d' ) {
|
|
1512
1564
|
|
|
1513
|
-
object3d.name = object3d.name || name;
|
|
1514
|
-
|
|
1515
1565
|
this.releaseTargetModel();
|
|
1516
|
-
|
|
1517
|
-
this.
|
|
1566
|
+
|
|
1567
|
+
const model = this._adoptExternalObject( object3d );
|
|
1568
|
+
model.name = object3d.name || name;
|
|
1569
|
+
|
|
1570
|
+
this.targetModel = model;
|
|
1518
1571
|
|
|
1519
1572
|
updateLoading( { isLoading: true, status: "Processing Data...", progress: 10 } );
|
|
1520
1573
|
await this.onModelLoad( this.targetModel );
|
|
1521
1574
|
|
|
1522
|
-
this.dispatchEvent( { type: 'load', model
|
|
1523
|
-
return
|
|
1575
|
+
this.dispatchEvent( { type: 'load', model, filename: name } );
|
|
1576
|
+
return model;
|
|
1524
1577
|
|
|
1525
1578
|
}
|
|
1526
1579
|
|
|
@@ -1656,6 +1709,9 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1656
1709
|
processModelObjects( model ) {
|
|
1657
1710
|
|
|
1658
1711
|
let visitedAreaLights = [];
|
|
1712
|
+
// Split after the walk: traverse() caches children.length, so splitting in place
|
|
1713
|
+
// shifts later siblings down a slot and skips one.
|
|
1714
|
+
const multiMaterialMeshes = [];
|
|
1659
1715
|
model.traverse( ( object ) => {
|
|
1660
1716
|
|
|
1661
1717
|
const userData = object.userData;
|
|
@@ -1709,19 +1765,29 @@ export class AssetLoader extends EventDispatcher {
|
|
|
1709
1765
|
// Handle multi-material meshes
|
|
1710
1766
|
if ( object.isMesh && Array.isArray( object.material ) ) {
|
|
1711
1767
|
|
|
1712
|
-
|
|
1713
|
-
const group = createMeshesFromMultiMaterialMesh( object );
|
|
1768
|
+
multiMaterialMeshes.push( object );
|
|
1714
1769
|
|
|
1715
|
-
|
|
1770
|
+
}
|
|
1716
1771
|
|
|
1717
|
-
|
|
1718
|
-
object.parent.remove( object );
|
|
1772
|
+
} );
|
|
1719
1773
|
|
|
1720
|
-
|
|
1774
|
+
const shared = model.userData.__rayzeeExternal === true;
|
|
1721
1775
|
|
|
1722
|
-
|
|
1776
|
+
for ( const object of multiMaterialMeshes ) {
|
|
1723
1777
|
|
|
1724
|
-
|
|
1778
|
+
if ( ! object.parent ) continue;
|
|
1779
|
+
|
|
1780
|
+
console.log( 'Found multi-material mesh:', object.name );
|
|
1781
|
+
if ( shared ) object.geometry = standInForSplit( object.geometry );
|
|
1782
|
+
|
|
1783
|
+
const group = createMeshesFromMultiMaterialMesh( object );
|
|
1784
|
+
// Fresh geometry per group; tag it so an adopted model's release can free it.
|
|
1785
|
+
for ( const child of group.children ) child.geometry.userData.__rayzeeOwned = true;
|
|
1786
|
+
|
|
1787
|
+
object.parent.add( group );
|
|
1788
|
+
object.parent.remove( object );
|
|
1789
|
+
|
|
1790
|
+
}
|
|
1725
1791
|
|
|
1726
1792
|
}
|
|
1727
1793
|
|
|
@@ -183,29 +183,6 @@ export class GeometryExtractor {
|
|
|
183
183
|
materialIndex = this.materials.length - 1;
|
|
184
184
|
this._materialUuidMap.set( material.uuid, materialIndex );
|
|
185
185
|
|
|
186
|
-
// Detect material features for shader optimization (strict > 0 check)
|
|
187
|
-
if ( newMaterial.clearcoat > 0 ) this.sceneFeatures.hasClearcoat = true;
|
|
188
|
-
if ( newMaterial.transmission > 0 ) this.sceneFeatures.hasTransmission = true;
|
|
189
|
-
if ( newMaterial.dispersion > 0 ) this.sceneFeatures.hasDispersion = true;
|
|
190
|
-
if ( newMaterial.iridescence > 0 ) this.sceneFeatures.hasIridescence = true;
|
|
191
|
-
if ( newMaterial.sheen > 0 ) this.sceneFeatures.hasSheen = true;
|
|
192
|
-
if ( newMaterial.transparent || newMaterial.opacity < 1.0 || newMaterial.alphaTest > 0 ) this.sceneFeatures.hasTransparency = true;
|
|
193
|
-
if ( newMaterial.subsurface > 0 ) this.sceneFeatures.hasSubsurface = true;
|
|
194
|
-
|
|
195
|
-
// Detect multi-lobe materials (require multi-lobe MIS for optimal sampling)
|
|
196
|
-
const featureCount = [
|
|
197
|
-
newMaterial.clearcoat > 0,
|
|
198
|
-
newMaterial.transmission > 0,
|
|
199
|
-
newMaterial.iridescence > 0,
|
|
200
|
-
newMaterial.sheen > 0
|
|
201
|
-
].filter( Boolean ).length;
|
|
202
|
-
|
|
203
|
-
if ( featureCount >= 2 ) {
|
|
204
|
-
|
|
205
|
-
this.sceneFeatures.hasMultiLobeMaterials = true;
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
186
|
}
|
|
210
187
|
|
|
211
188
|
return materialIndex;
|
|
@@ -846,19 +823,6 @@ export class GeometryExtractor {
|
|
|
846
823
|
this._materialUuidMap = new Map();
|
|
847
824
|
this._textureIndexCache = new WeakMap();
|
|
848
825
|
|
|
849
|
-
// Reset scene-wide feature detection flags
|
|
850
|
-
this.sceneFeatures = {
|
|
851
|
-
hasClearcoat: false,
|
|
852
|
-
hasTransmission: false,
|
|
853
|
-
hasDispersion: false,
|
|
854
|
-
hasIridescence: false,
|
|
855
|
-
hasSheen: false,
|
|
856
|
-
hasTransparency: false,
|
|
857
|
-
hasSubsurface: false,
|
|
858
|
-
hasMultiLobeMaterials: false, // Materials with 2+ BRDF lobes
|
|
859
|
-
hasMRTOutputs: true // Always enabled for ASVGF/adaptive sampling support
|
|
860
|
-
};
|
|
861
|
-
|
|
862
826
|
}
|
|
863
827
|
|
|
864
828
|
getExtractedData() {
|
|
@@ -888,8 +852,7 @@ export class GeometryExtractor {
|
|
|
888
852
|
specularIntensityMaps: this.specularIntensityMaps,
|
|
889
853
|
specularColorMaps: this.specularColorMaps,
|
|
890
854
|
directionalLights: this.directionalLights,
|
|
891
|
-
cameras: this.cameras
|
|
892
|
-
sceneFeatures: this.sceneFeatures // Scene-wide material feature flags
|
|
855
|
+
cameras: this.cameras
|
|
893
856
|
};
|
|
894
857
|
|
|
895
858
|
}
|
|
@@ -324,7 +324,6 @@ export class SceneProcessor {
|
|
|
324
324
|
|
|
325
325
|
// Store other extracted data
|
|
326
326
|
this.materials = extractedData.materials;
|
|
327
|
-
this.materialCount = this.materials.length; // Store material count for feature scanning
|
|
328
327
|
this.materialTriangleCounts = extractedData.materialTriangleCounts; // Per-material tri count for sort-bin remap
|
|
329
328
|
this.meshes = extractedData.meshes;
|
|
330
329
|
this.meshTriangleRanges = extractedData.meshTriangleRanges; // Per-mesh { start, count } for TLAS/BLAS
|
|
@@ -347,7 +346,6 @@ export class SceneProcessor {
|
|
|
347
346
|
this.specularColorMaps = extractedData.specularColorMaps;
|
|
348
347
|
this.directionalLights = extractedData.directionalLights;
|
|
349
348
|
this.cameras = extractedData.cameras;
|
|
350
|
-
this.sceneFeatures = extractedData.sceneFeatures; // Store material feature flags for shader optimization
|
|
351
349
|
|
|
352
350
|
const duration = performance.now() - startTime;
|
|
353
351
|
this._log( `Geometry extraction complete (${duration.toFixed( 2 )}ms)`, {
|
|
@@ -1157,7 +1155,6 @@ export class SceneProcessor {
|
|
|
1157
1155
|
|
|
1158
1156
|
// Update material arrays (but keep existing triangle data)
|
|
1159
1157
|
this.materials = extractedData.materials;
|
|
1160
|
-
this.materialCount = this.materials.length; // Update material count
|
|
1161
1158
|
this.meshes = extractedData.meshes; // Update mesh data
|
|
1162
1159
|
this.maps = extractedData.maps;
|
|
1163
1160
|
this.normalMaps = extractedData.normalMaps;
|
|
@@ -1166,7 +1163,6 @@ export class SceneProcessor {
|
|
|
1166
1163
|
this.metalnessMaps = extractedData.metalnessMaps;
|
|
1167
1164
|
this.emissiveMaps = extractedData.emissiveMaps;
|
|
1168
1165
|
this.displacementMaps = extractedData.displacementMaps;
|
|
1169
|
-
this.sceneFeatures = extractedData.sceneFeatures; // Update material feature flags
|
|
1170
1166
|
|
|
1171
1167
|
// Bucket textures, remap material indices, regenerate raw material data, and
|
|
1172
1168
|
// build the consolidated bucket arrays — same path as the initial build.
|
package/src/Processor/utils.js
CHANGED
|
@@ -177,6 +177,17 @@ export function disposeObjectFromMemory( object, exeptions = [] ) {
|
|
|
177
177
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/** Frees only geometry the engine allocated inside an adopted model; the rest is the host's. */
|
|
181
|
+
export function disposeEngineOwnedResources( object ) {
|
|
182
|
+
|
|
183
|
+
object?.traverse( ( child ) => {
|
|
184
|
+
|
|
185
|
+
if ( child.geometry?.userData?.__rayzeeOwned ) child.geometry.dispose();
|
|
186
|
+
|
|
187
|
+
} );
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
180
191
|
|
|
181
192
|
|
|
182
193
|
/**
|
|
@@ -453,9 +453,6 @@ export class PathTracerStage extends RenderStage {
|
|
|
453
453
|
await this.sdfs.buildBVH( scene );
|
|
454
454
|
this.cameras = this.sdfs.cameras;
|
|
455
455
|
|
|
456
|
-
// Inject shader defines based on detected material features
|
|
457
|
-
this.materialData.injectMaterialFeatureDefines();
|
|
458
|
-
|
|
459
456
|
// Update uniforms with scene data
|
|
460
457
|
this.updateSceneUniforms();
|
|
461
458
|
this.updateLights();
|