@inweb/viewer-three 27.2.3 → 27.3.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/dist/viewer-three.js +103 -43
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +2 -2
- package/dist/viewer-three.module.js +103 -43
- package/dist/viewer-three.module.js.map +1 -1
- package/package.json +5 -5
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +89 -0
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +44 -33
- package/src/Viewer/models/ModelImpl.ts +13 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inweb/viewer-three",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.3.0",
|
|
4
4
|
"description": "JavaScript library for rendering CAD and BIM files in a browser using Three.js",
|
|
5
5
|
"homepage": "https://cloud.opendesign.com/docs/index.html",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"docs": "typedoc"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@inweb/client": "~27.
|
|
39
|
-
"@inweb/eventemitter2": "~27.
|
|
40
|
-
"@inweb/markup": "~27.
|
|
41
|
-
"@inweb/viewer-core": "~27.
|
|
38
|
+
"@inweb/client": "~27.3.0",
|
|
39
|
+
"@inweb/eventemitter2": "~27.3.0",
|
|
40
|
+
"@inweb/markup": "~27.3.0",
|
|
41
|
+
"@inweb/viewer-core": "~27.3.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@streamparser/json": "^0.0.22",
|
|
@@ -110,6 +110,12 @@ export class DynamicGltfLoader {
|
|
|
110
110
|
this.objectTransforms = new Map(); // originalObject -> Matrix4
|
|
111
111
|
this.transformedGeometries = new Map(); // mergedObject.uuid -> original position data
|
|
112
112
|
|
|
113
|
+
// TODO: Remove when highlight is implemented via shader.
|
|
114
|
+
// Sync transforms to original (hidden) objects so GeometryHighlighter
|
|
115
|
+
// wireframes follow the exploded positions.
|
|
116
|
+
this.syncTransformsToOriginalObjects = true;
|
|
117
|
+
this._originalObjectMatrices = new Map(); // object -> original matrix (for restore)
|
|
118
|
+
|
|
113
119
|
this.activeChunkLoads = 0;
|
|
114
120
|
this.chunkQueue = [];
|
|
115
121
|
|
|
@@ -1495,6 +1501,7 @@ export class DynamicGltfLoader {
|
|
|
1495
1501
|
|
|
1496
1502
|
this.objectTransforms.clear();
|
|
1497
1503
|
this.transformedGeometries.clear();
|
|
1504
|
+
this._originalObjectMatrices.clear();
|
|
1498
1505
|
|
|
1499
1506
|
this.totalLoadedObjects = 0;
|
|
1500
1507
|
this.currentMemoryUsage = 0;
|
|
@@ -2562,6 +2569,76 @@ export class DynamicGltfLoader {
|
|
|
2562
2569
|
this.updateMaterialUniforms();
|
|
2563
2570
|
}
|
|
2564
2571
|
}
|
|
2572
|
+
|
|
2573
|
+
// TODO: Remove this block when highlight is implemented via shader.
|
|
2574
|
+
// Sync transforms to original (hidden) objects so that
|
|
2575
|
+
// GeometryHighlighter wireframes follow exploded positions.
|
|
2576
|
+
if (this.syncTransformsToOriginalObjects) {
|
|
2577
|
+
this._syncOriginalObjectTransforms(objectTransformMap);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
// TODO: Remove when highlight is implemented via shader.
|
|
2582
|
+
_syncOriginalObjectTransforms(objectTransformMap) {
|
|
2583
|
+
// First, restore any previously transformed objects back to their saved position
|
|
2584
|
+
for (const [obj, savedPos] of this._originalObjectMatrices) {
|
|
2585
|
+
obj.position.copy(savedPos);
|
|
2586
|
+
if (obj.userData.highlight) {
|
|
2587
|
+
obj.userData.highlight.position.copy(savedPos);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
this._originalObjectMatrices.clear();
|
|
2591
|
+
|
|
2592
|
+
// The transform matrix is in the structure root's local space.
|
|
2593
|
+
// Original objects may be nested inside groups with their own transforms,
|
|
2594
|
+
// so we must convert the offset from structure-root-local → parent-local space.
|
|
2595
|
+
const _offset = new Vector3();
|
|
2596
|
+
const _parentInverse = new Matrix4();
|
|
2597
|
+
|
|
2598
|
+
if (objectTransformMap instanceof Map) {
|
|
2599
|
+
for (const [object, matrix] of objectTransformMap.entries()) {
|
|
2600
|
+
if (!object.userData?.handle) continue;
|
|
2601
|
+
|
|
2602
|
+
// Save original position before modifying
|
|
2603
|
+
if (!this._originalObjectMatrices.has(object)) {
|
|
2604
|
+
this._originalObjectMatrices.set(object, object.position.clone());
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// Extract translation from the transform matrix (structure-root-local space)
|
|
2608
|
+
_offset.setFromMatrixPosition(matrix);
|
|
2609
|
+
|
|
2610
|
+
// Convert offset from structure-root-local to world space,
|
|
2611
|
+
// then from world space to parent-local space.
|
|
2612
|
+
// The structure root's matrixWorld converts local→world.
|
|
2613
|
+
// The parent's inverse matrixWorld converts world→parent-local.
|
|
2614
|
+
// But we only need to transform a direction/offset (not a point),
|
|
2615
|
+
// so we transform two points and subtract.
|
|
2616
|
+
if (object.userData.structureId) {
|
|
2617
|
+
const rootGroup = this.structureRoots.get(object.userData.structureId);
|
|
2618
|
+
if (rootGroup && object.parent && object.parent !== rootGroup) {
|
|
2619
|
+
// Transform offset: structureRoot-local → world → parent-local
|
|
2620
|
+
// For a vector (not point): apply rotation/scale only
|
|
2621
|
+
const origin = new Vector3(0, 0, 0);
|
|
2622
|
+
origin.applyMatrix4(rootGroup.matrixWorld);
|
|
2623
|
+
_offset.applyMatrix4(rootGroup.matrixWorld);
|
|
2624
|
+
_offset.sub(origin);
|
|
2625
|
+
|
|
2626
|
+
const parentOrigin = new Vector3(0, 0, 0);
|
|
2627
|
+
_parentInverse.copy(object.parent.matrixWorld).invert();
|
|
2628
|
+
parentOrigin.applyMatrix4(_parentInverse);
|
|
2629
|
+
_offset.applyMatrix4(_parentInverse);
|
|
2630
|
+
_offset.sub(parentOrigin);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
object.position.add(_offset);
|
|
2635
|
+
|
|
2636
|
+
// Also update highlight wireframe if it exists right now
|
|
2637
|
+
if (object.userData.highlight) {
|
|
2638
|
+
object.userData.highlight.position.copy(object.position);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2565
2642
|
}
|
|
2566
2643
|
|
|
2567
2644
|
createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
|
|
@@ -2675,6 +2752,18 @@ export class DynamicGltfLoader {
|
|
|
2675
2752
|
clearTransforms() {
|
|
2676
2753
|
this.objectTransforms.clear();
|
|
2677
2754
|
this._resetTransformData(true);
|
|
2755
|
+
|
|
2756
|
+
// TODO: Remove when highlight is implemented via shader.
|
|
2757
|
+
// Restore original object positions
|
|
2758
|
+
if (this.syncTransformsToOriginalObjects) {
|
|
2759
|
+
for (const [obj, savedPos] of this._originalObjectMatrices) {
|
|
2760
|
+
obj.position.copy(savedPos);
|
|
2761
|
+
if (obj.userData.highlight) {
|
|
2762
|
+
obj.userData.highlight.position.copy(savedPos);
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
this._originalObjectMatrices.clear();
|
|
2766
|
+
}
|
|
2678
2767
|
}
|
|
2679
2768
|
|
|
2680
2769
|
clearHandleTransforms() {
|
|
@@ -133,7 +133,7 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
override explode(scale = 0, coeff = 4): this {
|
|
136
|
-
const
|
|
136
|
+
const centersCache = new Map();
|
|
137
137
|
|
|
138
138
|
const calcObjectCenter = (object: Object3D, target: Vector3): Vector3 => {
|
|
139
139
|
const extents = new Box3().setFromObject(object);
|
|
@@ -141,74 +141,85 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
141
141
|
const handle = object.userData.handle;
|
|
142
142
|
if (!handle) return extents.getCenter(target);
|
|
143
143
|
|
|
144
|
-
const center =
|
|
144
|
+
const center = centersCache.get(handle);
|
|
145
145
|
if (center) return target.copy(center);
|
|
146
146
|
|
|
147
147
|
const objects = this.getObjectsByHandles(handle);
|
|
148
148
|
objects.forEach((x: Object3D) => extents.expandByObject(x));
|
|
149
149
|
extents.getCenter(target);
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
centersCache.set(handle, target.clone());
|
|
152
152
|
|
|
153
153
|
return target;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
object.children
|
|
159
|
-
.filter((x: Object3D) => !x.userData.isOptimized)
|
|
160
|
-
.forEach((x: Object3D) => {
|
|
161
|
-
const objectDepth = calcExplodeDepth(x, depth + 1);
|
|
162
|
-
if (result < objectDepth) result = objectDepth;
|
|
163
|
-
});
|
|
156
|
+
const calcObjectDepth = (object: Object3D): number => {
|
|
157
|
+
if (object.userData.depth !== undefined) return object.userData.depth;
|
|
164
158
|
|
|
159
|
+
const parent = object.parent;
|
|
160
|
+
const depth = parent && object !== explodeRoot ? calcObjectDepth(parent) + 1 : 0;
|
|
161
|
+
|
|
162
|
+
object.userData.depth = depth;
|
|
165
163
|
object.userData.originalPosition = object.position.clone();
|
|
166
164
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
167
165
|
|
|
168
|
-
return
|
|
169
|
-
}
|
|
166
|
+
return depth;
|
|
167
|
+
};
|
|
170
168
|
|
|
171
169
|
const explodeScale = scale / 100;
|
|
172
170
|
const explodeRoot = this.scene.children[0];
|
|
173
171
|
|
|
174
|
-
if (!explodeRoot.userData.explodeDepth)
|
|
175
|
-
|
|
172
|
+
if (!explodeRoot.userData.explodeDepth) {
|
|
173
|
+
let maxDepth = 0;
|
|
174
|
+
|
|
175
|
+
this.gltfLoader.originalObjects.forEach((object: Object3D) => {
|
|
176
|
+
const depth = calcObjectDepth(object);
|
|
177
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
178
|
+
});
|
|
176
179
|
|
|
180
|
+
explodeRoot.userData.explodeDepth = maxDepth;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
177
184
|
const scaledExplodeDepth = explodeScale * maxDepth + 1;
|
|
178
185
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
179
186
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
180
187
|
|
|
181
|
-
const
|
|
188
|
+
const offsetCache = new Map();
|
|
189
|
+
|
|
190
|
+
const calcObjectOffset = (object: Object3D, target: Vector3): Vector3 => {
|
|
191
|
+
if (offsetCache.has(object)) return target.copy(offsetCache.get(object));
|
|
182
192
|
|
|
183
|
-
|
|
184
|
-
if (object
|
|
185
|
-
if (object.userData.isHighlightWireframe) return;
|
|
193
|
+
const parent = object.parent;
|
|
194
|
+
if (parent && object !== explodeRoot) calcObjectOffset(parent, target);
|
|
186
195
|
|
|
187
|
-
object.
|
|
196
|
+
const depth = object.userData.depth;
|
|
188
197
|
|
|
189
|
-
if (depth > 0 && depth <= explodeDepth
|
|
198
|
+
if (depth > 0 && depth <= explodeDepth) {
|
|
190
199
|
let objectScale = explodeScale * coeff;
|
|
191
200
|
if (depth === explodeDepth) objectScale *= currentSegmentFraction;
|
|
192
201
|
|
|
193
|
-
const parentCenter =
|
|
202
|
+
const parentCenter = parent.userData.originalCenter;
|
|
194
203
|
const objectCenter = object.userData.originalCenter;
|
|
195
|
-
const
|
|
204
|
+
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
196
205
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
|
|
200
|
-
transformMap.set(object, matrix);
|
|
206
|
+
target.add(localOffset);
|
|
201
207
|
}
|
|
202
208
|
|
|
203
|
-
object.
|
|
204
|
-
.filter((x: Object3D) => !x.userData.isOptimized)
|
|
205
|
-
.forEach((x: Object3D) => explodeObject(x, depth + 1));
|
|
206
|
-
}
|
|
209
|
+
offsetCache.set(object, target.clone());
|
|
207
210
|
|
|
208
|
-
|
|
209
|
-
|
|
211
|
+
return target;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const transformMap = new Map();
|
|
215
|
+
|
|
216
|
+
this.gltfLoader.originalObjects.forEach((object: Object3D) => {
|
|
217
|
+
const globalOffset = calcObjectOffset(object, new Vector3());
|
|
218
|
+
transformMap.set(object, new Matrix4().makeTranslation(globalOffset));
|
|
219
|
+
});
|
|
210
220
|
|
|
211
221
|
this.gltfLoader.applyObjectTransforms(transformMap);
|
|
222
|
+
this.scene.updateMatrixWorld();
|
|
212
223
|
|
|
213
224
|
return this;
|
|
214
225
|
}
|
|
@@ -383,35 +383,36 @@ export class ModelImpl implements IModelImpl {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
explode(scale = 0, coeff = 4): this {
|
|
386
|
-
const
|
|
386
|
+
const centersCache = new Map();
|
|
387
387
|
|
|
388
|
-
const
|
|
388
|
+
const calcObjectCenter = (object: Object3D, target: Vector3): Vector3 => {
|
|
389
389
|
const extents = new Box3().setFromObject(object);
|
|
390
390
|
|
|
391
391
|
const handle = object.userData.handle;
|
|
392
392
|
if (!handle) return extents.getCenter(target);
|
|
393
393
|
|
|
394
|
-
const center =
|
|
394
|
+
const center = centersCache.get(handle);
|
|
395
395
|
if (center) return target.copy(center);
|
|
396
396
|
|
|
397
397
|
const objects = this.getObjectsByHandles(handle);
|
|
398
398
|
objects.forEach((x: Object3D) => extents.expandByObject(x));
|
|
399
399
|
extents.getCenter(target);
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
centersCache.set(handle, target.clone());
|
|
402
402
|
|
|
403
403
|
return target;
|
|
404
404
|
};
|
|
405
405
|
|
|
406
406
|
function calcExplodeDepth(object: Object3D, depth: number): number {
|
|
407
407
|
let result = depth;
|
|
408
|
+
|
|
408
409
|
object.children.forEach((x: Object3D) => {
|
|
409
410
|
const objectDepth = calcExplodeDepth(x, depth + 1);
|
|
410
411
|
if (result < objectDepth) result = objectDepth;
|
|
411
412
|
});
|
|
412
413
|
|
|
413
414
|
object.userData.originalPosition = object.position.clone();
|
|
414
|
-
object.userData.originalCenter =
|
|
415
|
+
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
415
416
|
|
|
416
417
|
return result;
|
|
417
418
|
}
|
|
@@ -419,14 +420,16 @@ export class ModelImpl implements IModelImpl {
|
|
|
419
420
|
const explodeScale = scale / 100;
|
|
420
421
|
const explodeRoot = this.scene;
|
|
421
422
|
|
|
422
|
-
if (!explodeRoot.userData.explodeDepth)
|
|
423
|
-
|
|
423
|
+
if (!explodeRoot.userData.explodeDepth) {
|
|
424
|
+
explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
|
|
425
|
+
}
|
|
424
426
|
|
|
427
|
+
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
425
428
|
const scaledExplodeDepth = explodeScale * maxDepth + 1;
|
|
426
429
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
427
430
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
428
431
|
|
|
429
|
-
function explodeObject(object, depth: number) {
|
|
432
|
+
function explodeObject(object: any, depth: number) {
|
|
430
433
|
if (object.isCamera) return;
|
|
431
434
|
if (object.userData.isHighlightWireframe) return;
|
|
432
435
|
|
|
@@ -438,9 +441,9 @@ export class ModelImpl implements IModelImpl {
|
|
|
438
441
|
|
|
439
442
|
const parentCenter = object.parent.userData.originalCenter;
|
|
440
443
|
const objectCenter = object.userData.originalCenter;
|
|
441
|
-
const
|
|
444
|
+
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
442
445
|
|
|
443
|
-
object.position.add(
|
|
446
|
+
object.position.add(localOffset);
|
|
444
447
|
}
|
|
445
448
|
|
|
446
449
|
object.children.forEach((x: Object3D) => explodeObject(x, depth + 1));
|