@inweb/viewer-three 26.7.6 → 26.8.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 +367 -230
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +4 -2
- package/dist/viewer-three.module.js +335 -159
- package/dist/viewer-three.module.js.map +1 -1
- package/lib/Viewer/components/HighlighterComponent.d.ts +2 -2
- package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +0 -2
- package/lib/Viewer/loaders/index.d.ts +1 -1
- package/package.json +5 -5
- package/src/Viewer/commands/SetSelected.ts +1 -0
- package/src/Viewer/commands/index.ts +1 -1
- package/src/Viewer/components/HighlighterComponent.ts +38 -16
- package/src/Viewer/components/LightComponent.ts +11 -15
- package/src/Viewer/components/SelectionComponent.ts +1 -2
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +187 -65
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +7 -12
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +120 -114
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +7 -13
- package/src/Viewer/loaders/index.ts +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { LineBasicMaterial,
|
|
1
|
+
import { LineBasicMaterial, MeshPhongMaterial, Object3D, WebGLRenderTarget } from "three";
|
|
2
2
|
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
|
|
3
3
|
import { IComponent, ResizeEvent } from "@inweb/viewer-core";
|
|
4
4
|
import { Viewer } from "../Viewer";
|
|
5
5
|
export declare class HighlighterComponent implements IComponent {
|
|
6
6
|
protected viewer: Viewer;
|
|
7
7
|
renderTarget: WebGLRenderTarget;
|
|
8
|
-
highlightMaterial:
|
|
8
|
+
highlightMaterial: MeshPhongMaterial;
|
|
9
9
|
outlineMaterial: LineMaterial;
|
|
10
10
|
highlightLineMaterial: LineBasicMaterial;
|
|
11
11
|
highlightLineGlowMaterial: LineMaterial;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { Group } from "three";
|
|
2
1
|
import { ILoader, LoadParams } from "@inweb/viewer-core";
|
|
3
2
|
import { Viewer } from "../Viewer";
|
|
4
3
|
import { DynamicGltfLoader } from "./DynamicGltfLoader/DynamicGltfLoader.js";
|
|
5
4
|
export declare class GLTFCloudDynamicLoader implements ILoader {
|
|
6
5
|
viewer: Viewer;
|
|
7
|
-
scene: Group;
|
|
8
6
|
gltfLoader: DynamicGltfLoader;
|
|
9
7
|
requestId: number;
|
|
10
8
|
constructor(viewer: Viewer);
|
|
@@ -11,7 +11,7 @@ import { ILoadersRegistry } from "@inweb/viewer-core";
|
|
|
11
11
|
*
|
|
12
12
|
* The loader should do:
|
|
13
13
|
*
|
|
14
|
-
* - Load
|
|
14
|
+
* - Load raw data from file and convert it to the `Three.js` scene.
|
|
15
15
|
* - Add scene to the viewer `scene`.
|
|
16
16
|
* - Create `ModelImpl` for the scene and to the viewer `models` list.
|
|
17
17
|
* - Synchronize viewer options and overlay.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inweb/viewer-three",
|
|
3
|
-
"version": "26.
|
|
3
|
+
"version": "26.8.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": "~26.
|
|
39
|
-
"@inweb/eventemitter2": "~26.
|
|
40
|
-
"@inweb/markup": "~26.
|
|
41
|
-
"@inweb/viewer-core": "~26.
|
|
38
|
+
"@inweb/client": "~26.8.0",
|
|
39
|
+
"@inweb/eventemitter2": "~26.8.0",
|
|
40
|
+
"@inweb/markup": "~26.8.0",
|
|
41
|
+
"@inweb/viewer-core": "~26.8.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/three": "^0.173.0",
|
|
@@ -80,9 +80,9 @@ commands.registerCommand("applyModelTransform", applyModelTransform);
|
|
|
80
80
|
commands.registerCommand("clearMarkup", clearMarkup);
|
|
81
81
|
commands.registerCommand("clearSelected", clearSelected);
|
|
82
82
|
commands.registerCommand("clearSlices", clearSlices);
|
|
83
|
+
commands.registerCommand("collect", collect);
|
|
83
84
|
commands.registerCommand("createPreview", createPreview);
|
|
84
85
|
commands.registerCommand("explode", explode);
|
|
85
|
-
commands.registerCommand("collect", collect);
|
|
86
86
|
commands.registerCommand("getDefaultViewPositions", getDefaultViewPositions);
|
|
87
87
|
commands.registerCommand("getModels", getModels);
|
|
88
88
|
commands.registerCommand("getSelected", getSelected);
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
Color,
|
|
26
26
|
EdgesGeometry,
|
|
27
27
|
LineBasicMaterial,
|
|
28
|
-
|
|
28
|
+
MeshPhongMaterial,
|
|
29
29
|
Object3D,
|
|
30
30
|
RGBAFormat,
|
|
31
31
|
UnsignedByteType,
|
|
@@ -43,7 +43,7 @@ import { HighlighterUtils } from "./HighlighterUtils";
|
|
|
43
43
|
export class HighlighterComponent implements IComponent {
|
|
44
44
|
protected viewer: Viewer;
|
|
45
45
|
public renderTarget: WebGLRenderTarget;
|
|
46
|
-
public highlightMaterial:
|
|
46
|
+
public highlightMaterial: MeshPhongMaterial;
|
|
47
47
|
public outlineMaterial: LineMaterial;
|
|
48
48
|
public highlightLineMaterial: LineBasicMaterial;
|
|
49
49
|
public highlightLineGlowMaterial: LineMaterial;
|
|
@@ -76,6 +76,8 @@ export class HighlighterComponent implements IComponent {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
highlight(objects: Object3D | Object3D[]) {
|
|
79
|
+
const { edgesVisibility } = this.viewer.options;
|
|
80
|
+
|
|
79
81
|
if (!Array.isArray(objects)) objects = [objects];
|
|
80
82
|
if (!objects.length) return;
|
|
81
83
|
|
|
@@ -93,25 +95,27 @@ export class HighlighterComponent implements IComponent {
|
|
|
93
95
|
wireframe.position.copy(object.position);
|
|
94
96
|
wireframe.rotation.copy(object.rotation);
|
|
95
97
|
wireframe.scale.copy(object.scale);
|
|
98
|
+
wireframe.visible = edgesVisibility;
|
|
96
99
|
|
|
97
100
|
object.parent.add(wireframe);
|
|
101
|
+
object.userData.highlightWireframe = wireframe;
|
|
98
102
|
|
|
99
|
-
object.userData.highlightwireframe = wireframe;
|
|
100
103
|
object.userData.originalMaterial = object.material;
|
|
101
104
|
object.material = this.highlightLineMaterial;
|
|
102
105
|
object.isHighlighted = true;
|
|
103
106
|
} else if (object.isMesh) {
|
|
104
|
-
const edgesGeometry = new EdgesGeometry(object.geometry,
|
|
107
|
+
const edgesGeometry = new EdgesGeometry(object.geometry, 60);
|
|
105
108
|
const lineGeometry = new LineSegmentsGeometry().fromEdgesGeometry(edgesGeometry);
|
|
106
109
|
|
|
107
110
|
const wireframe = new Wireframe(lineGeometry, this.outlineMaterial);
|
|
108
111
|
wireframe.position.copy(object.position);
|
|
109
112
|
wireframe.rotation.copy(object.rotation);
|
|
110
113
|
wireframe.scale.copy(object.scale);
|
|
114
|
+
wireframe.visible = edgesVisibility;
|
|
111
115
|
|
|
112
116
|
object.parent.add(wireframe);
|
|
117
|
+
object.userData.highlightWireframe = wireframe;
|
|
113
118
|
|
|
114
|
-
object.userData.highlightwireframe = wireframe;
|
|
115
119
|
object.userData.originalMaterial = object.material;
|
|
116
120
|
object.material = this.highlightMaterial;
|
|
117
121
|
object.isHighlighted = true;
|
|
@@ -128,29 +132,35 @@ export class HighlighterComponent implements IComponent {
|
|
|
128
132
|
|
|
129
133
|
object.isHighlighted = false;
|
|
130
134
|
object.material = object.userData.originalMaterial;
|
|
131
|
-
object.userData.
|
|
135
|
+
object.userData.highlightWireframe.removeFromParent();
|
|
132
136
|
|
|
133
137
|
delete object.userData.originalMaterial;
|
|
134
|
-
delete object.userData.
|
|
138
|
+
delete object.userData.highlightWireframe;
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
geometryEnd = () => {
|
|
139
|
-
const { facesColor, facesTransparancy, edgesColor } = this.viewer.options;
|
|
143
|
+
const { facesColor, facesTransparancy, edgesColor, edgesOverlap, facesOverlap } = this.viewer.options;
|
|
140
144
|
|
|
141
|
-
this.highlightMaterial = new
|
|
145
|
+
this.highlightMaterial = new MeshPhongMaterial({
|
|
142
146
|
color: new Color(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255),
|
|
143
147
|
transparent: true,
|
|
144
148
|
opacity: (255 - facesTransparancy) / 255,
|
|
145
|
-
depthTest:
|
|
146
|
-
depthWrite:
|
|
149
|
+
depthTest: !facesOverlap,
|
|
150
|
+
depthWrite: !facesOverlap,
|
|
151
|
+
specular: 0x222222,
|
|
152
|
+
shininess: 10,
|
|
153
|
+
reflectivity: 0.05,
|
|
154
|
+
polygonOffset: true,
|
|
155
|
+
polygonOffsetFactor: 1,
|
|
156
|
+
polygonOffsetUnits: 1,
|
|
147
157
|
});
|
|
148
158
|
|
|
149
159
|
this.outlineMaterial = new LineMaterial({
|
|
150
160
|
color: new Color(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255),
|
|
151
161
|
linewidth: 1.5,
|
|
152
|
-
depthTest:
|
|
153
|
-
depthWrite:
|
|
162
|
+
depthTest: !edgesOverlap,
|
|
163
|
+
depthWrite: !edgesOverlap,
|
|
154
164
|
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
155
165
|
});
|
|
156
166
|
|
|
@@ -165,21 +175,33 @@ export class HighlighterComponent implements IComponent {
|
|
|
165
175
|
linewidth: 5,
|
|
166
176
|
transparent: true,
|
|
167
177
|
opacity: 0.8,
|
|
168
|
-
depthTest:
|
|
169
|
-
depthWrite:
|
|
178
|
+
depthTest: !edgesOverlap,
|
|
179
|
+
depthWrite: !edgesOverlap,
|
|
170
180
|
resolution: new Vector2(window.innerWidth, window.innerHeight),
|
|
171
181
|
});
|
|
172
182
|
};
|
|
173
183
|
|
|
174
184
|
optionsChange = () => {
|
|
175
|
-
const { facesColor, facesTransparancy, edgesColor } =
|
|
185
|
+
const { facesColor, facesTransparancy, edgesColor, edgesVisibility, edgesOverlap, facesOverlap } =
|
|
186
|
+
this.viewer.options;
|
|
176
187
|
|
|
177
188
|
this.highlightMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
|
|
178
189
|
this.highlightMaterial.opacity = (255 - facesTransparancy) / 255;
|
|
190
|
+
this.highlightMaterial.depthTest = !facesOverlap;
|
|
191
|
+
this.highlightMaterial.depthWrite = !facesOverlap;
|
|
192
|
+
|
|
179
193
|
this.outlineMaterial.color.setRGB(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255);
|
|
194
|
+
this.outlineMaterial.depthTest = !edgesOverlap;
|
|
195
|
+
this.outlineMaterial.depthWrite = !edgesOverlap;
|
|
196
|
+
|
|
180
197
|
this.highlightLineMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
|
|
181
198
|
this.highlightLineGlowMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
|
|
182
199
|
|
|
200
|
+
this.viewer.selected.forEach((selected) => {
|
|
201
|
+
const wireframe = selected.userData.highlightWireframe;
|
|
202
|
+
if (wireframe) wireframe.visible = edgesVisibility;
|
|
203
|
+
});
|
|
204
|
+
|
|
183
205
|
this.viewer.update();
|
|
184
206
|
};
|
|
185
207
|
|
|
@@ -36,20 +36,10 @@ export class LightComponent implements IComponent {
|
|
|
36
36
|
constructor(viewer: Viewer) {
|
|
37
37
|
this.viewer = viewer;
|
|
38
38
|
|
|
39
|
-
this.ambientLight = new AmbientLight(0xffffff, 1);
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
this.directionalLight = new DirectionalLight(0xffffff, 1);
|
|
43
|
-
this.directionalLight.position.set(0.5, 0, 0.866); // ~60º
|
|
44
|
-
this.viewer.scene.add(this.directionalLight);
|
|
45
|
-
|
|
39
|
+
this.ambientLight = new AmbientLight(0xffffff, 1.0);
|
|
40
|
+
this.directionalLight = new DirectionalLight(0xffffff, 1.0);
|
|
46
41
|
this.frontLight = new DirectionalLight(0xffffff, 1.25);
|
|
47
|
-
this.frontLight.position.set(0, 1, 0);
|
|
48
|
-
this.viewer.scene.add(this.frontLight);
|
|
49
|
-
|
|
50
42
|
this.hemisphereLight = new HemisphereLight(0xffffff, 0x444444, 1.25);
|
|
51
|
-
this.hemisphereLight.position.set(0, 0, 1);
|
|
52
|
-
this.viewer.scene.add(this.hemisphereLight);
|
|
53
43
|
|
|
54
44
|
this.viewer.addEventListener("databasechunk", this.geometryEnd);
|
|
55
45
|
this.viewer.addEventListener("clear", this.geometryEnd);
|
|
@@ -84,15 +74,21 @@ export class LightComponent implements IComponent {
|
|
|
84
74
|
const extentsSize = this.viewer.extents.getBoundingSphere(new Sphere()).radius;
|
|
85
75
|
|
|
86
76
|
this.directionalLight.position
|
|
87
|
-
.set(0.5, 0, 0
|
|
77
|
+
.set(0.5, 0.866, 0) // ~60º
|
|
88
78
|
.multiplyScalar(extentsSize * 2)
|
|
89
79
|
.add(extentsCenter);
|
|
90
80
|
this.directionalLight.target.position.copy(extentsCenter);
|
|
91
81
|
|
|
92
|
-
this.frontLight.position
|
|
82
|
+
this.frontLight.position
|
|
83
|
+
.set(0, 0, 1)
|
|
84
|
+
.multiplyScalar(extentsSize * 2)
|
|
85
|
+
.add(extentsCenter);
|
|
93
86
|
this.frontLight.target.position.copy(extentsCenter);
|
|
94
87
|
|
|
95
|
-
this.hemisphereLight.position
|
|
88
|
+
this.hemisphereLight.position
|
|
89
|
+
.set(0, 0, 1)
|
|
90
|
+
.multiplyScalar(extentsSize * 3)
|
|
91
|
+
.add(extentsCenter);
|
|
96
92
|
|
|
97
93
|
this.viewer.scene.add(this.ambientLight);
|
|
98
94
|
this.viewer.scene.add(this.directionalLight);
|
|
@@ -68,7 +68,7 @@ export class SelectionComponent implements IComponent {
|
|
|
68
68
|
this.viewer.models.forEach((model) => {
|
|
69
69
|
const objects = model.getVisibleObjects();
|
|
70
70
|
const intersects = this.getPointerIntersects(upPosition, objects);
|
|
71
|
-
|
|
71
|
+
if (intersects.length > 0) intersections.push({ ...intersects[0], model });
|
|
72
72
|
});
|
|
73
73
|
intersections = intersections.sort((a, b) => a.distance - b.distance);
|
|
74
74
|
|
|
@@ -126,7 +126,6 @@ export class SelectionComponent implements IComponent {
|
|
|
126
126
|
if (!Array.isArray(objects)) objects = [objects];
|
|
127
127
|
if (!objects.length) return;
|
|
128
128
|
|
|
129
|
-
model.showObjects(objects);
|
|
130
129
|
model.showOriginalObjects(objects);
|
|
131
130
|
this.highlighter.highlight(objects);
|
|
132
131
|
|
|
@@ -86,7 +86,9 @@ export class DynamicGltfLoader {
|
|
|
86
86
|
this.mergedPoints = new Set();
|
|
87
87
|
|
|
88
88
|
this.isolatedObjects = [];
|
|
89
|
-
|
|
89
|
+
//!!window.WebGL2RenderingContext && this.renderer.getContext() instanceof WebGL2RenderingContext
|
|
90
|
+
this.useVAO = false;
|
|
91
|
+
this.visibleEdges = true;
|
|
90
92
|
|
|
91
93
|
this.handleToOptimizedObjects = new Map();
|
|
92
94
|
|
|
@@ -95,6 +97,10 @@ export class DynamicGltfLoader {
|
|
|
95
97
|
this.oldOptimizeObjects = new Set();
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
setVisibleEdges(visible) {
|
|
101
|
+
this.visibleEdges = visible;
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
getAvailableMemory() {
|
|
99
105
|
let memoryLimit = 6 * 1024 * 1024 * 1024;
|
|
100
106
|
try {
|
|
@@ -115,7 +121,7 @@ export class DynamicGltfLoader {
|
|
|
115
121
|
console.warn("Error detecting available memory:", error);
|
|
116
122
|
}
|
|
117
123
|
|
|
118
|
-
return memoryLimit;
|
|
124
|
+
return memoryLimit / 3;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
getAbortController() {
|
|
@@ -220,7 +226,7 @@ export class DynamicGltfLoader {
|
|
|
220
226
|
console.log(`Final memory usage: ${Math.round(currentMemoryUsage / (1024 * 1024))}MB`);
|
|
221
227
|
}
|
|
222
228
|
|
|
223
|
-
async loadNode(nodeId) {
|
|
229
|
+
async loadNode(nodeId, onLoadFinishCb) {
|
|
224
230
|
const node = this.nodes.get(nodeId);
|
|
225
231
|
if (!node || node.loaded || node.loading) return;
|
|
226
232
|
|
|
@@ -228,38 +234,138 @@ export class DynamicGltfLoader {
|
|
|
228
234
|
const meshDef = node.structure.getJson().meshes[node.meshIndex];
|
|
229
235
|
|
|
230
236
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
const bufferRequests = [];
|
|
238
|
+
const primitiveReqMap = new Map();
|
|
239
|
+
for (let primIdx = 0; primIdx < meshDef.primitives.length; primIdx++) {
|
|
240
|
+
const primitive = meshDef.primitives[primIdx];
|
|
241
|
+
const reqs = [];
|
|
242
|
+
|
|
243
|
+
if (primitive.attributes.POSITION !== undefined) {
|
|
244
|
+
const accessorIndex = primitive.attributes.POSITION;
|
|
245
|
+
const accessor = node.structure.json.accessors[accessorIndex];
|
|
246
|
+
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
247
|
+
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
248
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
249
|
+
const count = accessor.count;
|
|
250
|
+
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
251
|
+
reqs.push({
|
|
252
|
+
offset: byteOffset,
|
|
253
|
+
length: byteLength,
|
|
254
|
+
componentType: accessor.componentType,
|
|
255
|
+
accessorIndex,
|
|
256
|
+
type: "position",
|
|
257
|
+
primIdx,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
237
260
|
|
|
238
261
|
if (primitive.attributes.NORMAL !== undefined) {
|
|
239
|
-
|
|
262
|
+
const accessorIndex = primitive.attributes.NORMAL;
|
|
263
|
+
const accessor = node.structure.json.accessors[accessorIndex];
|
|
264
|
+
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
265
|
+
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
266
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
267
|
+
const count = accessor.count;
|
|
268
|
+
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
269
|
+
reqs.push({
|
|
270
|
+
offset: byteOffset,
|
|
271
|
+
length: byteLength,
|
|
272
|
+
componentType: accessor.componentType,
|
|
273
|
+
accessorIndex,
|
|
274
|
+
type: "normal",
|
|
275
|
+
primIdx,
|
|
276
|
+
});
|
|
240
277
|
}
|
|
241
278
|
|
|
242
279
|
if (primitive.attributes.TEXCOORD_0 !== undefined) {
|
|
243
|
-
|
|
280
|
+
const accessorIndex = primitive.attributes.TEXCOORD_0;
|
|
281
|
+
const accessor = node.structure.json.accessors[accessorIndex];
|
|
282
|
+
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
283
|
+
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
284
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
285
|
+
const count = accessor.count;
|
|
286
|
+
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
287
|
+
reqs.push({
|
|
288
|
+
offset: byteOffset,
|
|
289
|
+
length: byteLength,
|
|
290
|
+
componentType: accessor.componentType,
|
|
291
|
+
accessorIndex,
|
|
292
|
+
type: "uv",
|
|
293
|
+
primIdx,
|
|
294
|
+
});
|
|
244
295
|
}
|
|
245
296
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
297
|
+
if (primitive.indices !== undefined) {
|
|
298
|
+
const accessorIndex = primitive.indices;
|
|
299
|
+
const accessor = node.structure.json.accessors[accessorIndex];
|
|
300
|
+
const bufferView = node.structure.json.bufferViews[accessor.bufferView];
|
|
301
|
+
const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
|
|
302
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
303
|
+
const count = accessor.count;
|
|
304
|
+
const byteLength = count * components * node.structure.getComponentSize(accessor.componentType);
|
|
305
|
+
reqs.push({
|
|
306
|
+
offset: byteOffset,
|
|
307
|
+
length: byteLength,
|
|
308
|
+
componentType: accessor.componentType,
|
|
309
|
+
accessorIndex,
|
|
310
|
+
type: "index",
|
|
311
|
+
primIdx,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
primitiveReqMap.set(primIdx, reqs);
|
|
315
|
+
bufferRequests.push(...reqs);
|
|
316
|
+
}
|
|
252
317
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
318
|
+
if (bufferRequests.length === 0) {
|
|
319
|
+
node.loaded = true;
|
|
320
|
+
node.loading = false;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
bufferRequests.sort((a, b) => a.offset - b.offset);
|
|
324
|
+
const minOffset = bufferRequests[0].offset;
|
|
325
|
+
const maxOffset = Math.max(...bufferRequests.map((r) => r.offset + r.length));
|
|
326
|
+
const totalLength = maxOffset - minOffset;
|
|
327
|
+
|
|
328
|
+
const { buffer, relOffset: baseRelOffset } = await node.structure.scheduleRequest({
|
|
329
|
+
offset: minOffset,
|
|
330
|
+
length: totalLength,
|
|
331
|
+
componentType: null,
|
|
332
|
+
});
|
|
256
333
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
334
|
+
for (const req of bufferRequests) {
|
|
335
|
+
const relOffset = req.offset - minOffset;
|
|
336
|
+
req.data = node.structure.createTypedArray(buffer, baseRelOffset + relOffset, req.length, req.componentType);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
for (let primIdx = 0; primIdx < meshDef.primitives.length; primIdx++) {
|
|
340
|
+
const primitive = meshDef.primitives[primIdx];
|
|
341
|
+
const geometry = new BufferGeometry();
|
|
342
|
+
const reqs = primitiveReqMap.get(primIdx);
|
|
343
|
+
|
|
344
|
+
if (primitive.attributes.POSITION !== undefined) {
|
|
345
|
+
const req = reqs.find((r) => r.type === "position" && r.accessorIndex === primitive.attributes.POSITION);
|
|
346
|
+
const accessor = node.structure.json.accessors[primitive.attributes.POSITION];
|
|
347
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
348
|
+
geometry.setAttribute("position", new BufferAttribute(req.data, components));
|
|
260
349
|
}
|
|
261
350
|
|
|
262
|
-
|
|
351
|
+
if (primitive.attributes.NORMAL !== undefined) {
|
|
352
|
+
const req = reqs.find((r) => r.type === "normal" && r.accessorIndex === primitive.attributes.NORMAL);
|
|
353
|
+
const accessor = node.structure.json.accessors[primitive.attributes.NORMAL];
|
|
354
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
355
|
+
geometry.setAttribute("normal", new BufferAttribute(req.data, components));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (primitive.attributes.TEXCOORD_0 !== undefined) {
|
|
359
|
+
const req = reqs.find((r) => r.type === "uv" && r.accessorIndex === primitive.attributes.TEXCOORD_0);
|
|
360
|
+
const accessor = node.structure.json.accessors[primitive.attributes.TEXCOORD_0];
|
|
361
|
+
const components = node.structure.getNumComponents(accessor.type);
|
|
362
|
+
geometry.setAttribute("uv", new BufferAttribute(req.data, components));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (primitive.indices !== undefined) {
|
|
366
|
+
const req = reqs.find((r) => r.type === "index" && r.accessorIndex === primitive.indices);
|
|
367
|
+
geometry.setIndex(new BufferAttribute(req.data, 1));
|
|
368
|
+
}
|
|
263
369
|
|
|
264
370
|
let material;
|
|
265
371
|
if (primitive.material !== undefined) {
|
|
@@ -274,8 +380,7 @@ export class DynamicGltfLoader {
|
|
|
274
380
|
Material.prototype.copy.call(pointsMaterial, material);
|
|
275
381
|
pointsMaterial.color.copy(material.color);
|
|
276
382
|
pointsMaterial.map = material.map;
|
|
277
|
-
pointsMaterial.sizeAttenuation = false;
|
|
278
|
-
|
|
383
|
+
pointsMaterial.sizeAttenuation = false;
|
|
279
384
|
mesh = new Points(geometry, pointsMaterial);
|
|
280
385
|
} else if (
|
|
281
386
|
primitive.mode === GL_CONSTANTS.TRIANGLES ||
|
|
@@ -284,7 +389,6 @@ export class DynamicGltfLoader {
|
|
|
284
389
|
primitive.mode === undefined
|
|
285
390
|
) {
|
|
286
391
|
mesh = new Mesh(geometry, material);
|
|
287
|
-
|
|
288
392
|
if (primitive.mode === GL_CONSTANTS.TRIANGLE_STRIP) {
|
|
289
393
|
mesh.drawMode = TriangleStripDrawMode;
|
|
290
394
|
} else if (primitive.mode === GL_CONSTANTS.TRIANGLE_FAN) {
|
|
@@ -297,39 +401,30 @@ export class DynamicGltfLoader {
|
|
|
297
401
|
} else if (primitive.mode === GL_CONSTANTS.LINE_LOOP) {
|
|
298
402
|
mesh = new LineLoop(geometry, material);
|
|
299
403
|
}
|
|
300
|
-
|
|
301
404
|
if (node.extras) {
|
|
302
405
|
mesh.userData = { ...mesh.userData, ...node.extras };
|
|
303
406
|
}
|
|
304
|
-
|
|
305
407
|
if (meshDef.extras) {
|
|
306
408
|
mesh.userData = { ...mesh.userData, ...meshDef.extras };
|
|
307
409
|
}
|
|
308
|
-
|
|
309
410
|
if (primitive.extras) {
|
|
310
411
|
mesh.userData = { ...mesh.userData, ...primitive.extras };
|
|
311
412
|
}
|
|
312
|
-
|
|
313
413
|
if (node.handle) {
|
|
314
414
|
mesh.userData.handle = node.handle;
|
|
315
415
|
} else {
|
|
316
416
|
mesh.userData.handle = `${node.structure.id}_${mesh.userData.handle}`;
|
|
317
417
|
}
|
|
318
|
-
|
|
319
418
|
if (mesh.material.name === "edges") {
|
|
320
419
|
mesh.userData.isEdge = true;
|
|
321
420
|
} else {
|
|
322
421
|
mesh.userData.isEdge = false;
|
|
323
422
|
}
|
|
324
|
-
|
|
325
423
|
this.registerObjectWithHandle(mesh, mesh.userData.handle);
|
|
326
|
-
|
|
327
424
|
mesh.position.copy(node.position);
|
|
328
|
-
|
|
329
425
|
if (!geometry.attributes.normal) {
|
|
330
426
|
geometry.computeVertexNormals();
|
|
331
427
|
}
|
|
332
|
-
|
|
333
428
|
if (material.aoMap && geometry.attributes.uv) {
|
|
334
429
|
geometry.setAttribute("uv2", geometry.attributes.uv);
|
|
335
430
|
}
|
|
@@ -339,17 +434,17 @@ export class DynamicGltfLoader {
|
|
|
339
434
|
this.scene.add(mesh);
|
|
340
435
|
}
|
|
341
436
|
node.object = mesh;
|
|
342
|
-
|
|
343
437
|
this.totalLoadedObjects++;
|
|
344
438
|
mesh.visible = this.totalLoadedObjects < this.graphicsObjectLimit;
|
|
345
439
|
}
|
|
346
|
-
|
|
347
440
|
node.loaded = true;
|
|
348
441
|
node.loading = false;
|
|
349
|
-
|
|
350
442
|
const geometrySize = this.estimateGeometrySize(node.object);
|
|
351
443
|
this.geometryCache.set(node.object.uuid, geometrySize);
|
|
352
444
|
this.currentMemoryUsage += geometrySize;
|
|
445
|
+
if (onLoadFinishCb) {
|
|
446
|
+
onLoadFinishCb();
|
|
447
|
+
}
|
|
353
448
|
} catch (error) {
|
|
354
449
|
if (error.name !== "AbortError") {
|
|
355
450
|
console.error(`Error loading node ${nodeId}:`, error);
|
|
@@ -474,7 +569,7 @@ export class DynamicGltfLoader {
|
|
|
474
569
|
return volumeB - volumeA;
|
|
475
570
|
});
|
|
476
571
|
|
|
477
|
-
if (!ignoreEdges) {
|
|
572
|
+
if (!ignoreEdges && this.visibleEdges) {
|
|
478
573
|
this.nodesToLoad.push(...this.edgeNodes);
|
|
479
574
|
}
|
|
480
575
|
|
|
@@ -601,33 +696,13 @@ export class DynamicGltfLoader {
|
|
|
601
696
|
async processNodes() {
|
|
602
697
|
const nodesToLoad = this.nodesToLoad;
|
|
603
698
|
let loadedCount = 0;
|
|
699
|
+
let lastLoadedCount = 0;
|
|
604
700
|
const totalNodes = nodesToLoad.length;
|
|
605
701
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
for (const nodeId of batch) {
|
|
612
|
-
if (this.abortController.signal.aborted) {
|
|
613
|
-
throw new DOMException("Loading aborted", "AbortError");
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
617
|
-
|
|
618
|
-
if (this.currentMemoryUsage + estimatedSize > this.memoryLimit) {
|
|
619
|
-
console.log(`Memory limit reached after loading ${loadedCount} nodes`);
|
|
620
|
-
this.dispatchEvent("geometryerror", { message: "Memory limit reached" });
|
|
621
|
-
this.dispatchEvent("update");
|
|
622
|
-
return loadedCount;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
batchPromises.push(this.loadNode(nodeId));
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
await Promise.all(batchPromises);
|
|
629
|
-
loadedCount += batch.length;
|
|
630
|
-
|
|
702
|
+
const loadProgress = async () => {
|
|
703
|
+
loadedCount++;
|
|
704
|
+
if (loadedCount - lastLoadedCount > 1000) {
|
|
705
|
+
lastLoadedCount = loadedCount;
|
|
631
706
|
this.updateMemoryIndicator();
|
|
632
707
|
this.dispatchEvent("geometryprogress", {
|
|
633
708
|
percentage: Math.round((loadedCount / totalNodes) * 100),
|
|
@@ -645,6 +720,34 @@ export class DynamicGltfLoader {
|
|
|
645
720
|
setTimeout(resolve, 0);
|
|
646
721
|
});
|
|
647
722
|
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
try {
|
|
726
|
+
const loadOperations = [];
|
|
727
|
+
for (const nodeId of nodesToLoad) {
|
|
728
|
+
if (this.abortController.signal.aborted) {
|
|
729
|
+
throw new DOMException("Loading aborted", "AbortError");
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const estimatedSize = await this.estimateNodeSize(nodeId);
|
|
733
|
+
|
|
734
|
+
if (this.currentMemoryUsage + estimatedSize > this.memoryLimit) {
|
|
735
|
+
console.log(`Memory limit reached after loading ${loadedCount} nodes`);
|
|
736
|
+
this.dispatchEvent("geometryerror", {
|
|
737
|
+
message: "Memory limit reached",
|
|
738
|
+
});
|
|
739
|
+
this.dispatchEvent("update");
|
|
740
|
+
return loadedCount;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
loadOperations.push(this.loadNode(nodeId, loadProgress));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
for (const structure of this.structures) {
|
|
747
|
+
loadOperations.push(structure.flushBufferRequests());
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
await Promise.all(loadOperations);
|
|
648
751
|
|
|
649
752
|
this.dispatchEvent("geometryend", {
|
|
650
753
|
totalLoaded: loadedCount,
|
|
@@ -1625,4 +1728,23 @@ export class DynamicGltfLoader {
|
|
|
1625
1728
|
}
|
|
1626
1729
|
});
|
|
1627
1730
|
}
|
|
1731
|
+
|
|
1732
|
+
// Возвращает bounding box для конкретной структуры
|
|
1733
|
+
getStructureGeometryExtent(structureId) {
|
|
1734
|
+
const extent = new Box3();
|
|
1735
|
+
for (const [nodeId, node] of this.nodes.entries()) {
|
|
1736
|
+
if (!node.geometryExtents) continue;
|
|
1737
|
+
if (!nodeId.startsWith(structureId + "_")) continue;
|
|
1738
|
+
if (node.object && this.hiddenHandles && this.hiddenHandles.has(node.object.userData.handle)) continue;
|
|
1739
|
+
const transformedBox = node.geometryExtents.clone();
|
|
1740
|
+
if (node.group && node.group.matrix) {
|
|
1741
|
+
transformedBox.applyMatrix4(node.group.matrix);
|
|
1742
|
+
if (node.group.parent && node.group.parent.matrix) {
|
|
1743
|
+
transformedBox.applyMatrix4(node.group.parent.matrix);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
extent.union(transformedBox);
|
|
1747
|
+
}
|
|
1748
|
+
return extent;
|
|
1749
|
+
}
|
|
1628
1750
|
}
|