@inweb/viewer-three 27.2.3 → 27.2.4
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/viewer-three.js +319 -652
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +321 -654
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/controls/WalkControls.d.ts +0 -8
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +5 -23
- package/lib/Viewer/helpers/PlaneHelper2.d.ts +4 -8
- package/lib/Viewer/measurement/Snapper.d.ts +1 -1
- package/package.json +5 -5
- package/src/Viewer/components/SelectionComponent.ts +1 -1
- package/src/Viewer/controls/WalkControls.ts +4 -50
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +31 -191
- package/src/Viewer/draggers/CuttingPlaneXAxis.ts +3 -2
- package/src/Viewer/draggers/CuttingPlaneYAxis.ts +3 -2
- package/src/Viewer/draggers/CuttingPlaneZAxis.ts +3 -2
- package/src/Viewer/draggers/index.ts +0 -2
- package/src/Viewer/helpers/PlaneHelper2.ts +17 -30
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +189 -413
- package/src/Viewer/measurement/Snapper.ts +5 -13
|
@@ -23,10 +23,6 @@ import {
|
|
|
23
23
|
NormalBlending,
|
|
24
24
|
BufferAttribute,
|
|
25
25
|
LineBasicMaterial,
|
|
26
|
-
DataTexture,
|
|
27
|
-
RGBAFormat,
|
|
28
|
-
FloatType,
|
|
29
|
-
NearestFilter,
|
|
30
26
|
} from "three";
|
|
31
27
|
import { GL_CONSTANTS } from "./GltfStructure.js";
|
|
32
28
|
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
@@ -65,6 +61,10 @@ export class DynamicGltfLoader {
|
|
|
65
61
|
|
|
66
62
|
this.memoryLimit = this.getAvailableMemory();
|
|
67
63
|
this.optimizationMemoryMultiplier = 5;
|
|
64
|
+
/**
|
|
65
|
+
* Real memory ~1.7x raw geometry (Three.js objects, Map/Set, buffers overhead). Used for limit
|
|
66
|
+
* checks and display.
|
|
67
|
+
*/
|
|
68
68
|
this.memoryEstimationFactor = 1.7;
|
|
69
69
|
this.loadedGeometrySize = 0;
|
|
70
70
|
this.geometryCache = new Map();
|
|
@@ -126,123 +126,6 @@ export class DynamicGltfLoader {
|
|
|
126
126
|
this.mergedGeometryVisibility = new Map(); // mergedObject -> visibility array
|
|
127
127
|
|
|
128
128
|
this._webglInfoCache = null;
|
|
129
|
-
|
|
130
|
-
// Transform texture support
|
|
131
|
-
this.transformTextureSize = 1024;
|
|
132
|
-
this.transformTexture = this.createDummyTexture();
|
|
133
|
-
this.transformData = null;
|
|
134
|
-
this.identityTransformData = null;
|
|
135
|
-
this.visibilityMaterials = new Set(); // Keep track of materials to update uniforms
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// layout (1 matrix = 4 pixels)
|
|
139
|
-
// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
|
|
140
|
-
// with 8x8 pixel texture max 16 matrices * 4 pixels = (8 * 8)
|
|
141
|
-
// 16x16 pixel texture max 64 matrices * 4 pixels = (16 * 16)
|
|
142
|
-
// 32x32 pixel texture max 256 matrices * 4 pixels = (32 * 32)
|
|
143
|
-
// 64x64 pixel texture max 1024 matrices * 4 pixels = (64 * 64)
|
|
144
|
-
|
|
145
|
-
createDummyTexture() {
|
|
146
|
-
// Create 1x1 dummy texture with identity matrix to prevent shader crash/black screen
|
|
147
|
-
const data = new Float32Array(16); // 4x4 matrix
|
|
148
|
-
const identity = new Matrix4();
|
|
149
|
-
identity.toArray(data);
|
|
150
|
-
|
|
151
|
-
// Correct dummy size: 4x1 to hold at least one matrix
|
|
152
|
-
const dummyData = new Float32Array(16);
|
|
153
|
-
identity.toArray(dummyData);
|
|
154
|
-
const dummyTexture = new DataTexture(dummyData, 4, 1, RGBAFormat, FloatType);
|
|
155
|
-
|
|
156
|
-
dummyTexture.minFilter = NearestFilter;
|
|
157
|
-
dummyTexture.magFilter = NearestFilter;
|
|
158
|
-
dummyTexture.needsUpdate = true;
|
|
159
|
-
return dummyTexture;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
initTransformTexture() {
|
|
163
|
-
if (this.transformTexture) {
|
|
164
|
-
this.transformTexture.dispose();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Logic from BatchedMesh.js _initMatricesTexture
|
|
168
|
-
// layout (1 matrix = 4 pixels)
|
|
169
|
-
// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
|
|
170
|
-
const maxInstanceCount = this.maxObjectId + 1;
|
|
171
|
-
let size = Math.sqrt(maxInstanceCount * 4); // 4 pixels needed for 1 matrix
|
|
172
|
-
size = Math.ceil(size / 4) * 4;
|
|
173
|
-
size = Math.max(size, 4);
|
|
174
|
-
|
|
175
|
-
this.transformTextureSize = size;
|
|
176
|
-
const arraySize = size * size * 4; // 4 floats per RGBA pixel
|
|
177
|
-
this.transformData = new Float32Array(arraySize);
|
|
178
|
-
|
|
179
|
-
// Create and cache identity matrices for fast reset
|
|
180
|
-
this.identityTransformData = new Float32Array(arraySize);
|
|
181
|
-
for (let i = 0; i <= this.maxObjectId; i++) {
|
|
182
|
-
const base = i * 16;
|
|
183
|
-
if (base + 15 < arraySize) {
|
|
184
|
-
this.identityTransformData[base + 0] = 1; // m00
|
|
185
|
-
this.identityTransformData[base + 5] = 1; // m11
|
|
186
|
-
this.identityTransformData[base + 10] = 1; // m22
|
|
187
|
-
this.identityTransformData[base + 15] = 1; // m33
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Initialize with identity matrices
|
|
192
|
-
this._resetTransformData(false);
|
|
193
|
-
|
|
194
|
-
this.transformTexture = new DataTexture(this.transformData, size, size, RGBAFormat, FloatType);
|
|
195
|
-
|
|
196
|
-
this.transformTexture.needsUpdate = true;
|
|
197
|
-
this.transformTexture.generateMipmaps = false;
|
|
198
|
-
|
|
199
|
-
console.log(`Initialized transform texture: ${size}x${size} for ${maxInstanceCount} objects`);
|
|
200
|
-
|
|
201
|
-
this.updateMaterialUniforms();
|
|
202
|
-
|
|
203
|
-
// Force all visibility materials to update
|
|
204
|
-
this.visibilityMaterials.forEach((material) => {
|
|
205
|
-
material.needsUpdate = true;
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Fast reset to Identity matrices without creating Matrix4 objects
|
|
210
|
-
_resetTransformData(updateTexture = true) {
|
|
211
|
-
if (!this.transformData || !this.identityTransformData) return;
|
|
212
|
-
|
|
213
|
-
// Fast copy from cached identity array
|
|
214
|
-
this.transformData.set(this.identityTransformData);
|
|
215
|
-
|
|
216
|
-
if (updateTexture) {
|
|
217
|
-
this.updateTransformTexture();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
updateMaterialUniforms() {
|
|
222
|
-
// Only update if the texture actually changed
|
|
223
|
-
// In three.js, setting `.value = this.transformTexture` triggers uniformity checks
|
|
224
|
-
// We can avoid looping over all materials if the texture reference hasn't changed
|
|
225
|
-
if (
|
|
226
|
-
this._lastTransformTexture === this.transformTexture &&
|
|
227
|
-
this._lastTransformTextureSize === this.transformTextureSize
|
|
228
|
-
) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
this._lastTransformTexture = this.transformTexture;
|
|
233
|
-
this._lastTransformTextureSize = this.transformTextureSize;
|
|
234
|
-
|
|
235
|
-
this.visibilityMaterials.forEach((material) => {
|
|
236
|
-
if (material.userData && material.userData.visibilityUniforms) {
|
|
237
|
-
material.userData.visibilityUniforms.transformTexture.value = this.transformTexture;
|
|
238
|
-
material.userData.visibilityUniforms.transformTextureSize.value = this.transformTextureSize;
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
updateTransformTexture() {
|
|
244
|
-
if (!this.transformTexture) return;
|
|
245
|
-
this.transformTexture.needsUpdate = true;
|
|
246
129
|
}
|
|
247
130
|
|
|
248
131
|
setVisibleEdges(visible) {
|
|
@@ -1286,100 +1169,42 @@ export class DynamicGltfLoader {
|
|
|
1286
1169
|
}
|
|
1287
1170
|
|
|
1288
1171
|
createVisibilityMaterial(material) {
|
|
1289
|
-
this.visibilityMaterials.add(material);
|
|
1290
|
-
|
|
1291
|
-
const uniforms = {
|
|
1292
|
-
transformTexture: { value: this.transformTexture },
|
|
1293
|
-
transformTextureSize: { value: this.transformTextureSize },
|
|
1294
|
-
};
|
|
1295
|
-
material.userData.visibilityUniforms = uniforms;
|
|
1296
|
-
|
|
1297
1172
|
material.onBeforeCompile = (shader) => {
|
|
1298
|
-
shader.uniforms.transformTexture = uniforms.transformTexture;
|
|
1299
|
-
shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
|
|
1300
|
-
|
|
1301
|
-
// 1. Common Definitions
|
|
1302
1173
|
shader.vertexShader = shader.vertexShader.replace(
|
|
1303
1174
|
"#include <common>",
|
|
1304
1175
|
`
|
|
1305
1176
|
#include <common>
|
|
1306
|
-
|
|
1307
1177
|
attribute float visibility;
|
|
1308
|
-
attribute float objectId;
|
|
1309
1178
|
varying float vVisibility;
|
|
1310
|
-
uniform highp sampler2D transformTexture;
|
|
1311
|
-
uniform float transformTextureSize;
|
|
1312
|
-
|
|
1313
|
-
mat4 getTransformMatrix(float instanceId) {
|
|
1314
|
-
int size = int(transformTextureSize);
|
|
1315
|
-
int index = int(instanceId) * 4;
|
|
1316
|
-
|
|
1317
|
-
int x0 = index % size;
|
|
1318
|
-
int y0 = index / size;
|
|
1319
|
-
|
|
1320
|
-
vec4 row0 = texelFetch(transformTexture, ivec2(x0, y0), 0);
|
|
1321
|
-
vec4 row1 = texelFetch(transformTexture, ivec2(x0 + 1, y0), 0);
|
|
1322
|
-
vec4 row2 = texelFetch(transformTexture, ivec2(x0 + 2, y0), 0);
|
|
1323
|
-
vec4 row3 = texelFetch(transformTexture, ivec2(x0 + 3, y0), 0);
|
|
1324
|
-
|
|
1325
|
-
return mat4(row0, row1, row2, row3);
|
|
1326
|
-
}
|
|
1327
1179
|
`
|
|
1328
1180
|
);
|
|
1329
1181
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
"void main() {",
|
|
1182
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
1183
|
+
"#include <common>",
|
|
1333
1184
|
`
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
vVisibility = visibility;
|
|
1185
|
+
#include <common>
|
|
1186
|
+
varying float vVisibility;
|
|
1337
1187
|
`
|
|
1338
1188
|
);
|
|
1339
1189
|
|
|
1340
|
-
// 3. Transform Normal
|
|
1341
|
-
if (shader.vertexShader.includes("#include <beginnormal_vertex>")) {
|
|
1342
|
-
shader.vertexShader = shader.vertexShader.replace(
|
|
1343
|
-
"#include <beginnormal_vertex>",
|
|
1344
|
-
`
|
|
1345
|
-
vec3 objectNormal = vec3( normal );
|
|
1346
|
-
mat3 bm = mat3( batchingMatrix );
|
|
1347
|
-
objectNormal = bm * objectNormal;
|
|
1348
|
-
`
|
|
1349
|
-
);
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
// 4. Transform Position
|
|
1353
1190
|
shader.vertexShader = shader.vertexShader.replace(
|
|
1354
|
-
"
|
|
1191
|
+
"void main() {",
|
|
1355
1192
|
`
|
|
1356
|
-
|
|
1357
|
-
|
|
1193
|
+
void main() {
|
|
1194
|
+
vVisibility = visibility;
|
|
1358
1195
|
`
|
|
1359
1196
|
);
|
|
1360
1197
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
.replace(
|
|
1364
|
-
"#include <common>",
|
|
1365
|
-
`
|
|
1366
|
-
#include <common>
|
|
1367
|
-
varying float vVisibility;
|
|
1198
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
1199
|
+
"void main() {",
|
|
1368
1200
|
`
|
|
1369
|
-
)
|
|
1370
|
-
.replace(
|
|
1371
|
-
"void main() {",
|
|
1372
|
-
`
|
|
1373
1201
|
void main() {
|
|
1374
1202
|
if (vVisibility < 0.5) discard;
|
|
1375
1203
|
`
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
//console.log("!vertex", shader.vertexShader);
|
|
1204
|
+
);
|
|
1379
1205
|
};
|
|
1380
|
-
|
|
1381
|
-
// Ensure the material recompiles to pick up changes
|
|
1382
1206
|
material.needsUpdate = true;
|
|
1207
|
+
|
|
1383
1208
|
return material;
|
|
1384
1209
|
}
|
|
1385
1210
|
|
|
@@ -1507,8 +1332,6 @@ export class DynamicGltfLoader {
|
|
|
1507
1332
|
this.objectIdToIndex.clear();
|
|
1508
1333
|
this.maxObjectId = 0;
|
|
1509
1334
|
this.objectVisibility = new Float32Array();
|
|
1510
|
-
this.meshToNodeMap = null;
|
|
1511
|
-
this.visibilityMaterials.clear();
|
|
1512
1335
|
}
|
|
1513
1336
|
|
|
1514
1337
|
setStructureTransform(structureId, matrix) {
|
|
@@ -1643,10 +1466,6 @@ export class DynamicGltfLoader {
|
|
|
1643
1466
|
|
|
1644
1467
|
this.originalObjects.clear();
|
|
1645
1468
|
this.originalObjectsToSelection.clear();
|
|
1646
|
-
// Clear previous optimization data
|
|
1647
|
-
this.objectIdToIndex.clear();
|
|
1648
|
-
this.maxObjectId = 0;
|
|
1649
|
-
|
|
1650
1469
|
const structureGroups = new Map();
|
|
1651
1470
|
|
|
1652
1471
|
this.dispatchEvent("optimizationprogress", {
|
|
@@ -1655,8 +1474,6 @@ export class DynamicGltfLoader {
|
|
|
1655
1474
|
message: "Collecting scene objects...",
|
|
1656
1475
|
});
|
|
1657
1476
|
|
|
1658
|
-
let totalObjectsToMerge = 0;
|
|
1659
|
-
|
|
1660
1477
|
this.scene.traverse((object) => {
|
|
1661
1478
|
if (object.userData.structureId) {
|
|
1662
1479
|
const structureId = object.userData.structureId;
|
|
@@ -1676,41 +1493,19 @@ export class DynamicGltfLoader {
|
|
|
1676
1493
|
}
|
|
1677
1494
|
|
|
1678
1495
|
const group = structureGroups.get(structureId);
|
|
1679
|
-
let added = false;
|
|
1680
1496
|
|
|
1681
1497
|
if (object instanceof Mesh) {
|
|
1682
1498
|
this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
|
|
1683
|
-
added = true;
|
|
1684
1499
|
} else if (object instanceof LineSegments) {
|
|
1685
1500
|
this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
|
|
1686
|
-
added = true;
|
|
1687
1501
|
} else if (object instanceof Line) {
|
|
1688
1502
|
this.addToMaterialGroup(object, group.mapLines, group.lines);
|
|
1689
|
-
added = true;
|
|
1690
1503
|
} else if (object instanceof Points) {
|
|
1691
1504
|
this.addToMaterialGroup(object, group.mapPoints, group.points);
|
|
1692
|
-
added = true;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
if (added) {
|
|
1696
|
-
totalObjectsToMerge++;
|
|
1697
1505
|
}
|
|
1698
1506
|
}
|
|
1699
1507
|
});
|
|
1700
1508
|
|
|
1701
|
-
// Initialize transform texture and visibility arrays BEFORE merging
|
|
1702
|
-
// This ensures that as we create merged objects, the texture is large enough
|
|
1703
|
-
// and populated with identity matrices, so objects don't disappear (scale 0).
|
|
1704
|
-
if (totalObjectsToMerge > 0) {
|
|
1705
|
-
console.log(`Pre-allocating transform texture for ${totalObjectsToMerge} objects`);
|
|
1706
|
-
this.maxObjectId = totalObjectsToMerge;
|
|
1707
|
-
this.initTransformTexture();
|
|
1708
|
-
this.initializeObjectVisibility();
|
|
1709
|
-
|
|
1710
|
-
// Reset counter so IDs are assigned from 0 during merge
|
|
1711
|
-
this.maxObjectId = 0;
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
1509
|
let processedGroups = 0;
|
|
1715
1510
|
const totalGroups = structureGroups.size;
|
|
1716
1511
|
|
|
@@ -1768,7 +1563,7 @@ export class DynamicGltfLoader {
|
|
|
1768
1563
|
}
|
|
1769
1564
|
});
|
|
1770
1565
|
|
|
1771
|
-
|
|
1566
|
+
this.initializeObjectVisibility();
|
|
1772
1567
|
|
|
1773
1568
|
console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
|
|
1774
1569
|
|
|
@@ -1863,7 +1658,6 @@ export class DynamicGltfLoader {
|
|
|
1863
1658
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
1864
1659
|
|
|
1865
1660
|
const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
|
|
1866
|
-
mergedMesh.frustumCulled = false; // Disable culling because vertex shader moves objects
|
|
1867
1661
|
mergedMesh.userData.isOptimized = true;
|
|
1868
1662
|
rootGroup.add(mergedMesh);
|
|
1869
1663
|
|
|
@@ -2010,7 +1804,6 @@ export class DynamicGltfLoader {
|
|
|
2010
1804
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
2011
1805
|
|
|
2012
1806
|
const mergedLine = new LineSegments(geometry, visibilityMaterial);
|
|
2013
|
-
mergedLine.frustumCulled = false;
|
|
2014
1807
|
mergedLine.userData.isEdge = isEdge;
|
|
2015
1808
|
mergedLine.userData.isOptimized = true;
|
|
2016
1809
|
|
|
@@ -2122,7 +1915,6 @@ export class DynamicGltfLoader {
|
|
|
2122
1915
|
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
2123
1916
|
|
|
2124
1917
|
const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
|
|
2125
|
-
mergedLine.frustumCulled = false;
|
|
2126
1918
|
mergedLine.userData.isEdge = isEdge;
|
|
2127
1919
|
mergedLine.userData.isOptimized = true;
|
|
2128
1920
|
|
|
@@ -2209,33 +2001,7 @@ export class DynamicGltfLoader {
|
|
|
2209
2001
|
|
|
2210
2002
|
if (geometries.length > 0) {
|
|
2211
2003
|
const mergedGeometry = mergeGeometries(geometries, false);
|
|
2212
|
-
|
|
2213
|
-
// Add objectId attribute
|
|
2214
|
-
const totalVertices = mergedGeometry.attributes.position.count;
|
|
2215
|
-
const objectIds = new Float32Array(totalVertices);
|
|
2216
|
-
let vertexOffset = 0;
|
|
2217
|
-
|
|
2218
|
-
group.objects.forEach((points) => {
|
|
2219
|
-
const handle = points.userData.handle;
|
|
2220
|
-
if (!this.objectIdToIndex.has(handle)) {
|
|
2221
|
-
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
2222
|
-
}
|
|
2223
|
-
const objectId = this.objectIdToIndex.get(handle);
|
|
2224
|
-
const count = points.geometry.attributes.position.count;
|
|
2225
|
-
for (let i = 0; i < count; i++) {
|
|
2226
|
-
objectIds[vertexOffset++] = objectId;
|
|
2227
|
-
}
|
|
2228
|
-
});
|
|
2229
|
-
mergedGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
2230
|
-
|
|
2231
|
-
// Add visibility attribute
|
|
2232
|
-
const visibilityArray = new Float32Array(totalVertices);
|
|
2233
|
-
visibilityArray.fill(1.0);
|
|
2234
|
-
mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
2235
|
-
|
|
2236
|
-
const visibilityMaterial = this.createVisibilityMaterial(group.material);
|
|
2237
|
-
const mergedPoints = new Points(mergedGeometry, visibilityMaterial);
|
|
2238
|
-
mergedPoints.frustumCulled = false;
|
|
2004
|
+
const mergedPoints = new Points(mergedGeometry, group.material);
|
|
2239
2005
|
mergedPoints.userData.isOptimized = true;
|
|
2240
2006
|
|
|
2241
2007
|
if (this.useVAO) {
|
|
@@ -2319,41 +2085,15 @@ export class DynamicGltfLoader {
|
|
|
2319
2085
|
});
|
|
2320
2086
|
|
|
2321
2087
|
const finalGeometry = mergeGeometries(geometriesWithIndex, false);
|
|
2322
|
-
|
|
2323
|
-
// Add objectId attribute
|
|
2324
|
-
const totalVertices = finalGeometry.attributes.position.count;
|
|
2325
|
-
const objectIds = new Float32Array(totalVertices);
|
|
2326
|
-
let vertexOffset = 0;
|
|
2327
|
-
|
|
2328
|
-
lineSegmentsArray.forEach((segment) => {
|
|
2329
|
-
const handle = segment.userData.handle;
|
|
2330
|
-
if (!this.objectIdToIndex.has(handle)) {
|
|
2331
|
-
this.objectIdToIndex.set(handle, this.maxObjectId++);
|
|
2332
|
-
}
|
|
2333
|
-
const objectId = this.objectIdToIndex.get(handle);
|
|
2334
|
-
const count = segment.geometry.attributes.position.count;
|
|
2335
|
-
for (let i = 0; i < count; i++) {
|
|
2336
|
-
objectIds[vertexOffset++] = objectId;
|
|
2337
|
-
}
|
|
2338
|
-
});
|
|
2339
|
-
finalGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
|
|
2340
|
-
|
|
2341
|
-
// Add visibility attribute
|
|
2342
|
-
const visibilityArray = new Float32Array(totalVertices);
|
|
2343
|
-
visibilityArray.fill(1.0);
|
|
2344
|
-
finalGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
|
|
2345
|
-
|
|
2346
2088
|
const material = new LineBasicMaterial({
|
|
2347
2089
|
vertexColors: true,
|
|
2348
2090
|
});
|
|
2349
|
-
const visibilityMaterial = this.createVisibilityMaterial(material);
|
|
2350
2091
|
|
|
2351
2092
|
if (this.useVAO) {
|
|
2352
2093
|
this.createVAO(finalGeometry);
|
|
2353
2094
|
}
|
|
2354
2095
|
|
|
2355
|
-
const mergedLine = new LineSegments(finalGeometry,
|
|
2356
|
-
mergedLine.frustumCulled = false;
|
|
2096
|
+
const mergedLine = new LineSegments(finalGeometry, material);
|
|
2357
2097
|
mergedLine.userData.structureId = structureId;
|
|
2358
2098
|
mergedLine.userData.isOptimized = true;
|
|
2359
2099
|
rootGroup.add(mergedLine);
|
|
@@ -2498,69 +2238,21 @@ export class DynamicGltfLoader {
|
|
|
2498
2238
|
return;
|
|
2499
2239
|
}
|
|
2500
2240
|
|
|
2501
|
-
if (!this.transformData) {
|
|
2502
|
-
console.warn("Transform texture not initialized");
|
|
2503
|
-
return;
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
2241
|
// Store transform map directly
|
|
2507
|
-
this.objectTransforms = objectTransformMap;
|
|
2508
|
-
|
|
2509
|
-
// Reset to identity first to ensure clean state
|
|
2510
|
-
this._resetTransformData(false);
|
|
2511
|
-
|
|
2512
|
-
// Cache references for tight loop
|
|
2513
|
-
const transformData = this.transformData;
|
|
2514
|
-
const objectIdToIndex = this.objectIdToIndex;
|
|
2515
|
-
|
|
2516
|
-
// Fast track map iteration using an array of values if we can
|
|
2517
|
-
// While .entries() is fast, sometimes direct properties check is faster
|
|
2518
|
-
let textureNeedsUpdate = false;
|
|
2519
|
-
|
|
2520
|
-
// Process matrices directly into texture array
|
|
2521
|
-
if (objectTransformMap instanceof Map) {
|
|
2522
|
-
// Modern V8 engines optimize for...of on Maps better than Array.from or destructuring iterators
|
|
2523
|
-
for (const [object, matrix] of objectTransformMap.entries()) {
|
|
2524
|
-
const userData = object.userData;
|
|
2525
|
-
if (!userData) continue;
|
|
2526
|
-
|
|
2527
|
-
const handle = userData.handle;
|
|
2528
|
-
if (handle === undefined) continue;
|
|
2242
|
+
this.objectTransforms = new Map(objectTransformMap);
|
|
2529
2243
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
transformData.set(matrix.elements, objectId * 16);
|
|
2534
|
-
textureNeedsUpdate = true;
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
} else {
|
|
2538
|
-
// Fallback for arrays of [object, matrix] pairs
|
|
2539
|
-
const len = objectTransformMap.length;
|
|
2540
|
-
for (let i = 0; i < len; i++) {
|
|
2541
|
-
const pair = objectTransformMap[i];
|
|
2542
|
-
const userData = pair[0].userData;
|
|
2543
|
-
if (!userData) continue;
|
|
2544
|
-
|
|
2545
|
-
const handle = userData.handle;
|
|
2546
|
-
if (handle === undefined) continue;
|
|
2547
|
-
|
|
2548
|
-
const objectId = objectIdToIndex.get(handle);
|
|
2549
|
-
if (objectId !== undefined) {
|
|
2550
|
-
transformData.set(pair[1].elements, objectId * 16);
|
|
2551
|
-
textureNeedsUpdate = true;
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2244
|
+
// Apply transforms to all merged meshes
|
|
2245
|
+
for (const mesh of this.mergedMesh) {
|
|
2246
|
+
this._applyTransformToMergedObject(mesh);
|
|
2554
2247
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
}
|
|
2248
|
+
for (const line of this.mergedLines) {
|
|
2249
|
+
this._applyTransformToMergedObject(line);
|
|
2250
|
+
}
|
|
2251
|
+
for (const lineSegment of this.mergedLineSegments) {
|
|
2252
|
+
this._applyTransformToMergedObject(lineSegment);
|
|
2253
|
+
}
|
|
2254
|
+
for (const point of this.mergedPoints) {
|
|
2255
|
+
this._applyTransformToMergedObject(point);
|
|
2564
2256
|
}
|
|
2565
2257
|
}
|
|
2566
2258
|
|
|
@@ -2581,90 +2273,28 @@ export class DynamicGltfLoader {
|
|
|
2581
2273
|
: Array.from(objects)
|
|
2582
2274
|
: Array.from(this.originalObjects);
|
|
2583
2275
|
|
|
2584
|
-
// Cache inverse matrices for structures
|
|
2585
|
-
const structureInverseMatrices = new Map();
|
|
2586
|
-
|
|
2587
|
-
// Map mesh -> node to access cached geometryExtents
|
|
2588
|
-
if (!this.meshToNodeMap) {
|
|
2589
|
-
this.meshToNodeMap = new Map();
|
|
2590
|
-
for (const node of this.nodes.values()) {
|
|
2591
|
-
if (node.object) {
|
|
2592
|
-
this.meshToNodeMap.set(node.object, node);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2597
2276
|
for (const obj of objectsArray) {
|
|
2598
2277
|
if (!obj.geometry || !obj.geometry.attributes.position) continue;
|
|
2599
2278
|
|
|
2600
|
-
|
|
2601
|
-
if (!obj.userData.explodeVector) {
|
|
2602
|
-
let center = null;
|
|
2279
|
+
const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
|
|
2603
2280
|
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
const box = node.geometryExtents.clone();
|
|
2608
|
-
box.applyMatrix4(obj.matrixWorld);
|
|
2609
|
-
center = new Vector3();
|
|
2610
|
-
box.getCenter(center);
|
|
2611
|
-
}
|
|
2281
|
+
if (obj.matrixWorld) {
|
|
2282
|
+
boundingBox.applyMatrix4(obj.matrixWorld);
|
|
2283
|
+
}
|
|
2612
2284
|
|
|
2613
|
-
|
|
2614
|
-
if (!center) {
|
|
2615
|
-
if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
|
|
2616
|
-
const box = obj.geometry.boundingBox.clone();
|
|
2617
|
-
box.applyMatrix4(obj.matrixWorld);
|
|
2618
|
-
center = new Vector3();
|
|
2619
|
-
box.getCenter(center);
|
|
2620
|
-
}
|
|
2285
|
+
if (boundingBox.isEmpty()) continue;
|
|
2621
2286
|
|
|
2622
|
-
|
|
2623
|
-
|
|
2287
|
+
const objectCenter = new Vector3();
|
|
2288
|
+
boundingBox.getCenter(objectCenter);
|
|
2624
2289
|
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
}
|
|
2628
|
-
const explodeVector = obj.userData.explodeVector;
|
|
2629
|
-
const distance = explodeVector.length();
|
|
2290
|
+
const direction = objectCenter.clone().sub(explodeCenter);
|
|
2291
|
+
const distance = direction.length();
|
|
2630
2292
|
|
|
2631
2293
|
if (distance > 0) {
|
|
2632
|
-
|
|
2633
|
-
const offset =
|
|
2634
|
-
|
|
2635
|
-
// Convert offset from World Space to Local Space of the merged mesh
|
|
2636
|
-
const localOffset = offset.clone();
|
|
2637
|
-
|
|
2638
|
-
if (obj.userData.structureId) {
|
|
2639
|
-
const structureId = obj.userData.structureId;
|
|
2640
|
-
let inverseMatrix = structureInverseMatrices.get(structureId);
|
|
2641
|
-
|
|
2642
|
-
if (!inverseMatrix) {
|
|
2643
|
-
const rootGroup = this.structureRoots.get(structureId);
|
|
2644
|
-
if (rootGroup) {
|
|
2645
|
-
// Reuse cached inverse matrix if possible
|
|
2646
|
-
if (!rootGroup.userData.inverseWorldMatrix) {
|
|
2647
|
-
// rootGroup.updateMatrixWorld(true); // Trust current state
|
|
2648
|
-
rootGroup.userData.inverseWorldMatrix = new Matrix4().copy(rootGroup.matrixWorld).invert();
|
|
2649
|
-
}
|
|
2650
|
-
inverseMatrix = rootGroup.userData.inverseWorldMatrix;
|
|
2651
|
-
structureInverseMatrices.set(structureId, inverseMatrix);
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
if (inverseMatrix) {
|
|
2656
|
-
const zero = new Vector3(0, 0, 0).applyMatrix4(inverseMatrix);
|
|
2657
|
-
const vec = offset.clone().applyMatrix4(inverseMatrix).sub(zero);
|
|
2658
|
-
localOffset.copy(vec);
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2294
|
+
direction.normalize();
|
|
2295
|
+
const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
|
|
2661
2296
|
|
|
2662
|
-
|
|
2663
|
-
if (!matrix) {
|
|
2664
|
-
matrix = new Matrix4();
|
|
2665
|
-
obj.userData.explodeMatrix = matrix;
|
|
2666
|
-
}
|
|
2667
|
-
matrix.makeTranslation(localOffset.x, localOffset.y, localOffset.z);
|
|
2297
|
+
const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
|
|
2668
2298
|
transformMap.set(obj, matrix);
|
|
2669
2299
|
}
|
|
2670
2300
|
}
|
|
@@ -2674,13 +2304,159 @@ export class DynamicGltfLoader {
|
|
|
2674
2304
|
|
|
2675
2305
|
clearTransforms() {
|
|
2676
2306
|
this.objectTransforms.clear();
|
|
2677
|
-
|
|
2307
|
+
|
|
2308
|
+
for (const mesh of this.mergedMesh) {
|
|
2309
|
+
this._restoreOriginalGeometry(mesh);
|
|
2310
|
+
}
|
|
2311
|
+
for (const line of this.mergedLines) {
|
|
2312
|
+
this._restoreOriginalGeometry(line);
|
|
2313
|
+
}
|
|
2314
|
+
for (const lineSegment of this.mergedLineSegments) {
|
|
2315
|
+
this._restoreOriginalGeometry(lineSegment);
|
|
2316
|
+
}
|
|
2317
|
+
for (const point of this.mergedPoints) {
|
|
2318
|
+
this._restoreOriginalGeometry(point);
|
|
2319
|
+
}
|
|
2678
2320
|
}
|
|
2679
2321
|
|
|
2680
2322
|
clearHandleTransforms() {
|
|
2681
2323
|
this.clearTransforms();
|
|
2682
2324
|
}
|
|
2683
2325
|
|
|
2326
|
+
_applyTransformToMergedObject(mergedObject) {
|
|
2327
|
+
const objectData = this.mergedObjectMap.get(mergedObject.uuid);
|
|
2328
|
+
if (!objectData || !objectData.objectMapping) return;
|
|
2329
|
+
|
|
2330
|
+
const geometry = mergedObject.geometry;
|
|
2331
|
+
if (!geometry || !geometry.attributes.position) return;
|
|
2332
|
+
|
|
2333
|
+
const positionAttr = geometry.attributes.position;
|
|
2334
|
+
const positions = positionAttr.array;
|
|
2335
|
+
|
|
2336
|
+
if (!this.transformedGeometries.has(mergedObject.uuid)) {
|
|
2337
|
+
this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
2341
|
+
const tempVector = new Vector3();
|
|
2342
|
+
|
|
2343
|
+
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
2344
|
+
const transform = this.objectTransforms.get(originalMesh);
|
|
2345
|
+
|
|
2346
|
+
if (!transform) {
|
|
2347
|
+
const startIdx = mappingData.startVertexIndex * 3;
|
|
2348
|
+
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
2349
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
2350
|
+
positions[i] = originalPositions[i];
|
|
2351
|
+
}
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
const startVertex = mappingData.startVertexIndex;
|
|
2356
|
+
const vertexCount = mappingData.vertexCount;
|
|
2357
|
+
|
|
2358
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
2359
|
+
const idx = (startVertex + i) * 3;
|
|
2360
|
+
|
|
2361
|
+
tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
|
|
2362
|
+
|
|
2363
|
+
tempVector.applyMatrix4(transform);
|
|
2364
|
+
|
|
2365
|
+
positions[idx] = tempVector.x;
|
|
2366
|
+
positions[idx + 1] = tempVector.y;
|
|
2367
|
+
positions[idx + 2] = tempVector.z;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
if (geometry.attributes.normal) {
|
|
2372
|
+
this._updateNormalsForTransform(geometry, objectData, originalPositions);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
positionAttr.needsUpdate = true;
|
|
2376
|
+
geometry.computeBoundingSphere();
|
|
2377
|
+
geometry.computeBoundingBox();
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
_updateNormalsForTransform(geometry, objectData, originalPositions) {
|
|
2381
|
+
const normalAttr = geometry.attributes.normal;
|
|
2382
|
+
if (!normalAttr) return;
|
|
2383
|
+
|
|
2384
|
+
const normals = normalAttr.array;
|
|
2385
|
+
const tempVector = new Vector3();
|
|
2386
|
+
const normalMatrix = new Matrix4();
|
|
2387
|
+
|
|
2388
|
+
// Store original normals if not already stored
|
|
2389
|
+
const normalsKey = `${geometry.uuid}_normals`;
|
|
2390
|
+
if (!this.transformedGeometries.has(normalsKey)) {
|
|
2391
|
+
this.transformedGeometries.set(normalsKey, new Float32Array(normals));
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
2395
|
+
|
|
2396
|
+
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
2397
|
+
// Direct lookup by object reference - NO HANDLE LOOKUP!
|
|
2398
|
+
const transform = this.objectTransforms.get(originalMesh);
|
|
2399
|
+
|
|
2400
|
+
if (!transform) {
|
|
2401
|
+
// Restore original normals
|
|
2402
|
+
const startIdx = mappingData.startVertexIndex * 3;
|
|
2403
|
+
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
2404
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
2405
|
+
normals[i] = originalNormals[i];
|
|
2406
|
+
}
|
|
2407
|
+
continue;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
// Create normal matrix (inverse transpose of transform)
|
|
2411
|
+
normalMatrix.copy(transform).invert().transpose();
|
|
2412
|
+
|
|
2413
|
+
const startVertex = mappingData.startVertexIndex;
|
|
2414
|
+
const vertexCount = mappingData.vertexCount;
|
|
2415
|
+
|
|
2416
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
2417
|
+
const idx = (startVertex + i) * 3;
|
|
2418
|
+
|
|
2419
|
+
// Get original normal
|
|
2420
|
+
tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
|
|
2421
|
+
|
|
2422
|
+
// Apply normal transformation
|
|
2423
|
+
tempVector.applyMatrix4(normalMatrix).normalize();
|
|
2424
|
+
|
|
2425
|
+
// Write back transformed normal
|
|
2426
|
+
normals[idx] = tempVector.x;
|
|
2427
|
+
normals[idx + 1] = tempVector.y;
|
|
2428
|
+
normals[idx + 2] = tempVector.z;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
normalAttr.needsUpdate = true;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
_restoreOriginalGeometry(mergedObject) {
|
|
2436
|
+
const geometry = mergedObject.geometry;
|
|
2437
|
+
if (!geometry || !geometry.attributes.position) return;
|
|
2438
|
+
|
|
2439
|
+
// Restore original positions
|
|
2440
|
+
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
2441
|
+
if (originalPositions) {
|
|
2442
|
+
const positions = geometry.attributes.position.array;
|
|
2443
|
+
positions.set(originalPositions);
|
|
2444
|
+
geometry.attributes.position.needsUpdate = true;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
// Restore original normals
|
|
2448
|
+
const normalsKey = `${geometry.uuid}_normals`;
|
|
2449
|
+
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
2450
|
+
if (originalNormals && geometry.attributes.normal) {
|
|
2451
|
+
const normals = geometry.attributes.normal.array;
|
|
2452
|
+
normals.set(originalNormals);
|
|
2453
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
geometry.computeBoundingSphere();
|
|
2457
|
+
geometry.computeBoundingBox();
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2684
2460
|
syncHiddenObjects() {
|
|
2685
2461
|
if (this.mergedObjectMap.size === 0) {
|
|
2686
2462
|
console.log("No merged objects to sync");
|