@jfk-solutions/demo3d-file-format 1.3.0 → 1.5.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/README.md +25 -4
- package/dist/model.js +5 -0
- package/dist/model.js.map +1 -1
- package/dist/three/index.d.ts +3 -0
- package/dist/three/index.d.ts.map +1 -1
- package/dist/three/index.js +540 -21
- package/dist/three/index.js.map +1 -1
- package/package.json +1 -1
package/dist/three/index.js
CHANGED
|
@@ -136,6 +136,7 @@ function createState(three, project, options, buffers) {
|
|
|
136
136
|
materialCache: new Map(),
|
|
137
137
|
textureCache: new Map(),
|
|
138
138
|
visualObjectById: new Map(),
|
|
139
|
+
layerVisibilityByName: new Map(project.layers.map((layer) => [layer.name, layer.visible])),
|
|
139
140
|
renderableObjectTrees: new WeakSet(),
|
|
140
141
|
warnedGeneratedTypes: new Set(),
|
|
141
142
|
defaultMaterial: createDemo3DThreeMaterial(undefined, three),
|
|
@@ -150,6 +151,7 @@ function createState(three, project, options, buffers) {
|
|
|
150
151
|
lines: 0,
|
|
151
152
|
directVisuals: 0,
|
|
152
153
|
proceduralBelts: 0,
|
|
154
|
+
proceduralRacks: 0,
|
|
153
155
|
proceduralSupportStands: 0,
|
|
154
156
|
proceduralConveyorSides: 0,
|
|
155
157
|
proceduralPhotoEyes: 0,
|
|
@@ -166,23 +168,25 @@ function createState(three, project, options, buffers) {
|
|
|
166
168
|
};
|
|
167
169
|
}
|
|
168
170
|
function createVisualObject(visual, state, inheritedLayer, parentVisual) {
|
|
169
|
-
const
|
|
171
|
+
const layer = visual.layer ?? inheritedLayer;
|
|
172
|
+
const renderCurrent = isRenderableVisualVisible(visual) && isLayerVisible(layer, state);
|
|
173
|
+
const refs = renderCurrent ? findVisualMeshReferenceIds(visual.xml) : [];
|
|
170
174
|
const meshes = [];
|
|
171
175
|
const textObjects = [];
|
|
172
176
|
const drawingBlockObjects = [];
|
|
173
177
|
const directVisualObjects = [];
|
|
174
178
|
const placeholderObjects = [];
|
|
175
|
-
const textObject = createTextVisualObject(visual, state);
|
|
179
|
+
const textObject = renderCurrent ? createTextVisualObject(visual, state) : undefined;
|
|
176
180
|
if (textObject) {
|
|
177
181
|
textObjects.push(textObject);
|
|
178
182
|
meshes.push(textObject);
|
|
179
183
|
}
|
|
180
|
-
const drawingBlockObject = createDrawingBlockVisualObject(visual, state);
|
|
184
|
+
const drawingBlockObject = renderCurrent ? createDrawingBlockVisualObject(visual, state) : undefined;
|
|
181
185
|
if (drawingBlockObject) {
|
|
182
186
|
drawingBlockObjects.push(drawingBlockObject);
|
|
183
187
|
meshes.push(drawingBlockObject);
|
|
184
188
|
}
|
|
185
|
-
const directVisualObject = createDirectVisualObject(visual, state, parentVisual);
|
|
189
|
+
const directVisualObject = renderCurrent ? createDirectVisualObject(visual, state, parentVisual) : undefined;
|
|
186
190
|
if (directVisualObject) {
|
|
187
191
|
directVisualObjects.push(directVisualObject);
|
|
188
192
|
meshes.push(directVisualObject);
|
|
@@ -196,7 +200,7 @@ function createVisualObject(visual, state, inheritedLayer, parentVisual) {
|
|
|
196
200
|
meshes.push(mesh);
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
|
-
const aspectRenderableObjects = createAspectRenderableObjects(visual, state);
|
|
203
|
+
const aspectRenderableObjects = renderCurrent ? createAspectRenderableObjects(visual, state) : [];
|
|
200
204
|
meshes.push(...aspectRenderableObjects);
|
|
201
205
|
let object;
|
|
202
206
|
if (meshes.length === 1 &&
|
|
@@ -219,7 +223,6 @@ function createVisualObject(visual, state, inheritedLayer, parentVisual) {
|
|
|
219
223
|
object.name = visual.displayName ?? visual.id ?? visual.typeName;
|
|
220
224
|
applyDemo3DTransform(object, visual.xml.textOf("LR"));
|
|
221
225
|
applyDemo3DScale(object, visual.properties?.textOf("Scale"));
|
|
222
|
-
const layer = visual.layer ?? inheritedLayer;
|
|
223
226
|
object.userData.demo3d = {
|
|
224
227
|
kind: "visual",
|
|
225
228
|
id: visual.id,
|
|
@@ -228,7 +231,8 @@ function createVisualObject(visual, state, inheritedLayer, parentVisual) {
|
|
|
228
231
|
xmlPath: visual.xml.path,
|
|
229
232
|
localTransform: visual.localTransform,
|
|
230
233
|
localTransformText: visual.xml.textOf("LR"),
|
|
231
|
-
layer
|
|
234
|
+
layer,
|
|
235
|
+
layerVisible: isLayerVisible(layer, state)
|
|
232
236
|
};
|
|
233
237
|
if (visual.id) {
|
|
234
238
|
state.visualObjectById.set(visual.id, object);
|
|
@@ -242,7 +246,9 @@ function createVisualObject(visual, state, inheritedLayer, parentVisual) {
|
|
|
242
246
|
if (containsRenderable) {
|
|
243
247
|
state.renderableObjectTrees.add(object);
|
|
244
248
|
}
|
|
245
|
-
|
|
249
|
+
if (renderCurrent) {
|
|
250
|
+
reportUnreconstructedProceduralVisual(visual, containsRenderable, state);
|
|
251
|
+
}
|
|
246
252
|
return object;
|
|
247
253
|
}
|
|
248
254
|
function reportUnreconstructedProceduralVisual(visual, containsRenderable, state) {
|
|
@@ -375,9 +381,16 @@ function createDirectVisualObject(visual, state, parentVisual) {
|
|
|
375
381
|
if (visual.typeName === "e3d:ImportedImageVisual") {
|
|
376
382
|
return createImportedImageObject(visual, state);
|
|
377
383
|
}
|
|
378
|
-
if (visual.typeName === "e3d:StraightBeltConveyor"
|
|
384
|
+
if (visual.typeName === "e3d:StraightBeltConveyor" ||
|
|
385
|
+
visual.typeName === "e3d:CurveBeltConveyor" ||
|
|
386
|
+
visual.typeName === "e3d:InjectorBeltConveyor") {
|
|
379
387
|
return state.options.renderProceduralBelts === true
|
|
380
|
-
?
|
|
388
|
+
? createBeltConveyorObject(visual, state)
|
|
389
|
+
: undefined;
|
|
390
|
+
}
|
|
391
|
+
if (visual.typeName === "e3d:RackVisual") {
|
|
392
|
+
return state.options.renderProceduralRacks === true
|
|
393
|
+
? createRackObject(visual, state)
|
|
381
394
|
: undefined;
|
|
382
395
|
}
|
|
383
396
|
if (visual.typeName === "e3d:SupportStand") {
|
|
@@ -410,7 +423,16 @@ function createDirectVisualObject(visual, state, parentVisual) {
|
|
|
410
423
|
state.stats.directVisuals += 1;
|
|
411
424
|
return object;
|
|
412
425
|
}
|
|
413
|
-
function
|
|
426
|
+
function createBeltConveyorObject(visual, state) {
|
|
427
|
+
if (visual.typeName === "e3d:CurveBeltConveyor") {
|
|
428
|
+
return createCurveBeltConveyorObject(visual, state);
|
|
429
|
+
}
|
|
430
|
+
if (visual.typeName === "e3d:InjectorBeltConveyor") {
|
|
431
|
+
const injector = createInjectorBeltConveyorObject(visual, state);
|
|
432
|
+
if (injector) {
|
|
433
|
+
return injector;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
414
436
|
const belt = readProceduralBelt(visual, state);
|
|
415
437
|
if (!belt) {
|
|
416
438
|
return undefined;
|
|
@@ -439,6 +461,146 @@ function createStraightBeltConveyorObject(visual, state) {
|
|
|
439
461
|
state.stats.proceduralBelts += 1;
|
|
440
462
|
return object;
|
|
441
463
|
}
|
|
464
|
+
const DEFAULT_BELT_WIDTH = 0.5;
|
|
465
|
+
const DEFAULT_BELT_DIAMETER = 0.06;
|
|
466
|
+
function appendOrientedTriangle(target, a, b, c, normal) {
|
|
467
|
+
const abx = b[0] - a[0];
|
|
468
|
+
const aby = b[1] - a[1];
|
|
469
|
+
const abz = b[2] - a[2];
|
|
470
|
+
const acx = c[0] - a[0];
|
|
471
|
+
const acy = c[1] - a[1];
|
|
472
|
+
const acz = c[2] - a[2];
|
|
473
|
+
const crossX = aby * acz - abz * acy;
|
|
474
|
+
const crossY = abz * acx - abx * acz;
|
|
475
|
+
const crossZ = abx * acy - aby * acx;
|
|
476
|
+
const forward = crossX * normal[0] + crossY * normal[1] + crossZ * normal[2] >= 0;
|
|
477
|
+
target.push(...a, ...(forward ? b : c), ...(forward ? c : b));
|
|
478
|
+
}
|
|
479
|
+
function appendOrientedQuad(target, a, b, c, d, normal) {
|
|
480
|
+
appendOrientedTriangle(target, a, b, c, normal);
|
|
481
|
+
appendOrientedTriangle(target, a, c, d, normal);
|
|
482
|
+
}
|
|
483
|
+
function createInjectorBeltConveyorObject(visual, state) {
|
|
484
|
+
const belt = readProceduralInjectorBelt(visual);
|
|
485
|
+
if (!belt) {
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
488
|
+
const geometry = getProceduralInjectorBeltGeometry(belt, state);
|
|
489
|
+
const object = new state.three.Mesh(geometry, [
|
|
490
|
+
getMaterial(belt.surfaceMaterial, state),
|
|
491
|
+
getMaterial(belt.sideMaterial, state)
|
|
492
|
+
]);
|
|
493
|
+
object.name = visual.displayName ?? visual.id ?? visual.typeName;
|
|
494
|
+
object.userData.demo3d = {
|
|
495
|
+
kind: "procedural-belt",
|
|
496
|
+
id: visual.id,
|
|
497
|
+
name: visual.displayName,
|
|
498
|
+
typeName: visual.typeName,
|
|
499
|
+
xmlPath: visual.xml.path,
|
|
500
|
+
loopHeight: belt.thickness,
|
|
501
|
+
startEdge: belt.startEdge,
|
|
502
|
+
endEdge: belt.endEdge,
|
|
503
|
+
connectorShaped: true
|
|
504
|
+
};
|
|
505
|
+
state.stats.meshes += 1;
|
|
506
|
+
state.stats.directVisuals += 1;
|
|
507
|
+
state.stats.proceduralBelts += 1;
|
|
508
|
+
return object;
|
|
509
|
+
}
|
|
510
|
+
function readProceduralInjectorBelt(visual) {
|
|
511
|
+
const properties = visual.properties;
|
|
512
|
+
if (!properties || isFalse(properties.textOf("CenterVisible"))) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
const startEdge = conveyorConnectorEdge(visual, "Start");
|
|
516
|
+
const serializedEndEdge = conveyorConnectorEdge(visual, "End");
|
|
517
|
+
if (!startEdge || !serializedEndEdge) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
const sameDirection = pointDistanceSquared(startEdge[0], serializedEndEdge[0]) +
|
|
521
|
+
pointDistanceSquared(startEdge[1], serializedEndEdge[1]);
|
|
522
|
+
const reversedDirection = pointDistanceSquared(startEdge[0], serializedEndEdge[1]) +
|
|
523
|
+
pointDistanceSquared(startEdge[1], serializedEndEdge[0]);
|
|
524
|
+
const endEdge = sameDirection <= reversedDirection
|
|
525
|
+
? serializedEndEdge
|
|
526
|
+
: [serializedEndEdge[1], serializedEndEdge[0]];
|
|
527
|
+
const beltDiameter = numberChildOptional(properties, "CenterDiameter") ??
|
|
528
|
+
numberChildOptional(properties, "BeltDiameter") ??
|
|
529
|
+
DEFAULT_BELT_DIAMETER;
|
|
530
|
+
const thickness = isTrue(properties.textOf("UseBeltCenterHeight"))
|
|
531
|
+
? numberChild(properties, "BeltCenterHeight", beltDiameter)
|
|
532
|
+
: beltDiameter;
|
|
533
|
+
if (thickness <= 0) {
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
const surfaceMaterial = firstMaterial(properties.child("SurfaceMaterial") ?? properties);
|
|
537
|
+
const sideMaterial = firstMaterial(properties.child("SurfaceSideMaterial") ?? properties) ?? surfaceMaterial;
|
|
538
|
+
return { startEdge, endEdge, thickness, surfaceMaterial, sideMaterial };
|
|
539
|
+
}
|
|
540
|
+
function conveyorConnectorEdge(visual, name) {
|
|
541
|
+
const connector = visual.xml.child("CN")?.children.find((candidate) => candidate.xsiType === "e3d:ConveyorConnector" && candidate.textOf("Name") === name);
|
|
542
|
+
if (!connector) {
|
|
543
|
+
return undefined;
|
|
544
|
+
}
|
|
545
|
+
const start = pipePoint(connector.textOf("Start"));
|
|
546
|
+
const end = pipePoint(connector.textOf("End"));
|
|
547
|
+
return start && end ? [start, end] : undefined;
|
|
548
|
+
}
|
|
549
|
+
function pipePoint(value) {
|
|
550
|
+
const values = parsePipeNumbers(value);
|
|
551
|
+
if (values.length === 0) {
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
return [values[0] ?? 0, values[1] ?? 0, -(values[2] ?? 0)];
|
|
555
|
+
}
|
|
556
|
+
function pointDistanceSquared(left, right) {
|
|
557
|
+
return ((left[0] - right[0]) ** 2 +
|
|
558
|
+
(left[1] - right[1]) ** 2 +
|
|
559
|
+
(left[2] - right[2]) ** 2);
|
|
560
|
+
}
|
|
561
|
+
function getProceduralInjectorBeltGeometry(belt, state) {
|
|
562
|
+
const key = `injector-belt:${[...belt.startEdge, ...belt.endEdge].map((point) => point.join(",")).join(";")}:${belt.thickness}`;
|
|
563
|
+
const cached = state.primitiveGeometryCache.get(key);
|
|
564
|
+
if (cached) {
|
|
565
|
+
return cached;
|
|
566
|
+
}
|
|
567
|
+
const surfacePositions = [];
|
|
568
|
+
const sidePositions = [];
|
|
569
|
+
const top = [belt.startEdge[0], belt.startEdge[1], belt.endEdge[1], belt.endEdge[0]];
|
|
570
|
+
const bottom = top.map((point) => [point[0], point[1] - belt.thickness, point[2]]);
|
|
571
|
+
const center = [
|
|
572
|
+
top.reduce((sum, point) => sum + point[0], 0) / top.length,
|
|
573
|
+
top.reduce((sum, point) => sum + point[1], 0) / top.length,
|
|
574
|
+
top.reduce((sum, point) => sum + point[2], 0) / top.length
|
|
575
|
+
];
|
|
576
|
+
appendOrientedQuad(surfacePositions, top[0], top[1], top[2], top[3], [0, 1, 0]);
|
|
577
|
+
appendOrientedQuad(surfacePositions, bottom[0], bottom[3], bottom[2], bottom[1], [0, -1, 0]);
|
|
578
|
+
appendInjectorBeltWall(surfacePositions, top[0], top[1], bottom[1], bottom[0], center);
|
|
579
|
+
appendInjectorBeltWall(sidePositions, top[1], top[2], bottom[2], bottom[1], center);
|
|
580
|
+
appendInjectorBeltWall(surfacePositions, top[2], top[3], bottom[3], bottom[2], center);
|
|
581
|
+
appendInjectorBeltWall(sidePositions, top[3], top[0], bottom[0], bottom[3], center);
|
|
582
|
+
const geometry = new state.three.BufferGeometry();
|
|
583
|
+
geometry.setAttribute("position", new state.three.Float32BufferAttribute([...surfacePositions, ...sidePositions], 3));
|
|
584
|
+
geometry.addGroup(0, surfacePositions.length / 3, 0);
|
|
585
|
+
geometry.addGroup(surfacePositions.length / 3, sidePositions.length / 3, 1);
|
|
586
|
+
geometry.computeVertexNormals();
|
|
587
|
+
geometry.computeBoundingBox();
|
|
588
|
+
geometry.computeBoundingSphere();
|
|
589
|
+
state.primitiveGeometryCache.set(key, geometry);
|
|
590
|
+
state.stats.geometries += 1;
|
|
591
|
+
return geometry;
|
|
592
|
+
}
|
|
593
|
+
function appendInjectorBeltWall(target, topA, topB, bottomB, bottomA, center) {
|
|
594
|
+
const middleX = (topA[0] + topB[0]) / 2;
|
|
595
|
+
const middleZ = (topA[2] + topB[2]) / 2;
|
|
596
|
+
let normalX = topB[2] - topA[2];
|
|
597
|
+
let normalZ = -(topB[0] - topA[0]);
|
|
598
|
+
if ((middleX - center[0]) * normalX + (middleZ - center[2]) * normalZ < 0) {
|
|
599
|
+
normalX = -normalX;
|
|
600
|
+
normalZ = -normalZ;
|
|
601
|
+
}
|
|
602
|
+
appendOrientedQuad(target, topA, topB, bottomB, bottomA, [normalX, 0, normalZ]);
|
|
603
|
+
}
|
|
442
604
|
function readProceduralBelt(visual, state) {
|
|
443
605
|
const properties = visual.properties;
|
|
444
606
|
if (!properties || isFalse(properties.textOf("CenterVisible"))) {
|
|
@@ -463,12 +625,12 @@ function readProceduralBelt(visual, state) {
|
|
|
463
625
|
const beltLength = numberChildOptional(properties, "BeltLength") ??
|
|
464
626
|
(serializedLength === undefined ? undefined : serializedLength - startPadding - endPadding);
|
|
465
627
|
const serializedWidth = numberChildOptional(properties, "Width");
|
|
466
|
-
const beltWidth = numberChildOptional(properties, "BeltWidth") ?? serializedWidth;
|
|
628
|
+
const beltWidth = numberChildOptional(properties, "BeltWidth") ?? serializedWidth ?? DEFAULT_BELT_WIDTH;
|
|
467
629
|
const effectiveLength = beltLength ?? 0;
|
|
468
|
-
const effectiveWidth =
|
|
630
|
+
const effectiveWidth = beltWidth - leftPadding - rightPadding;
|
|
469
631
|
const centerDiameter = numberChildOptional(properties, "CenterDiameter") ??
|
|
470
632
|
numberChildOptional(properties, "BeltDiameter") ??
|
|
471
|
-
|
|
633
|
+
DEFAULT_BELT_DIAMETER;
|
|
472
634
|
const useBeltCenterHeight = isTrue(properties.textOf("UseBeltCenterHeight"));
|
|
473
635
|
const loopHeight = useBeltCenterHeight
|
|
474
636
|
? numberChild(properties, "BeltCenterHeight", centerDiameter)
|
|
@@ -533,6 +695,131 @@ function getProceduralBeltGeometry(belt, state) {
|
|
|
533
695
|
state.stats.geometries += 1;
|
|
534
696
|
return geometry;
|
|
535
697
|
}
|
|
698
|
+
function createCurveBeltConveyorObject(visual, state) {
|
|
699
|
+
const belt = readProceduralCurveBelt(visual, state);
|
|
700
|
+
if (!belt) {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
703
|
+
const geometry = getProceduralCurveBeltGeometry(belt, state);
|
|
704
|
+
const object = new state.three.Mesh(geometry, [
|
|
705
|
+
getMaterial(belt.surfaceMaterial, state),
|
|
706
|
+
getMaterial(belt.sideMaterial, state)
|
|
707
|
+
]);
|
|
708
|
+
object.name = visual.displayName ?? visual.id ?? visual.typeName;
|
|
709
|
+
object.userData.demo3d = {
|
|
710
|
+
kind: "procedural-belt",
|
|
711
|
+
id: visual.id,
|
|
712
|
+
name: visual.displayName,
|
|
713
|
+
typeName: visual.typeName,
|
|
714
|
+
xmlPath: visual.xml.path,
|
|
715
|
+
innerRadius: belt.innerRadius,
|
|
716
|
+
width: belt.width,
|
|
717
|
+
loopHeight: belt.thickness,
|
|
718
|
+
angle: belt.angle
|
|
719
|
+
};
|
|
720
|
+
state.stats.meshes += 1;
|
|
721
|
+
state.stats.directVisuals += 1;
|
|
722
|
+
state.stats.proceduralBelts += 1;
|
|
723
|
+
return object;
|
|
724
|
+
}
|
|
725
|
+
function readProceduralCurveBelt(visual, state) {
|
|
726
|
+
const properties = visual.properties;
|
|
727
|
+
if (!properties || isFalse(properties.textOf("CenterVisible"))) {
|
|
728
|
+
return undefined;
|
|
729
|
+
}
|
|
730
|
+
const centerProfile = properties.child("CenterProfile");
|
|
731
|
+
if (centerProfile && (centerProfile.children.length > 0 || centerProfile.text.length > 0)) {
|
|
732
|
+
warn(state, {
|
|
733
|
+
code: "DEMO3D_THREE_UNSUPPORTED_BELT_PROFILE",
|
|
734
|
+
message: "Curve belt conveyors with a custom CenterProfile are not supported.",
|
|
735
|
+
sourceId: visual.id,
|
|
736
|
+
sourceType: visual.typeName,
|
|
737
|
+
xmlPath: visual.xml.path
|
|
738
|
+
});
|
|
739
|
+
return undefined;
|
|
740
|
+
}
|
|
741
|
+
const innerRadius = numberChild(properties, "InnerRadius", 0.5);
|
|
742
|
+
const width = numberChildOptional(properties, "BeltWidth")
|
|
743
|
+
?? numberChildOptional(properties, "Width")
|
|
744
|
+
?? DEFAULT_BELT_WIDTH;
|
|
745
|
+
const beltDiameter = numberChildOptional(properties, "CenterDiameter") ??
|
|
746
|
+
numberChildOptional(properties, "BeltDiameter") ??
|
|
747
|
+
DEFAULT_BELT_DIAMETER;
|
|
748
|
+
const thickness = isTrue(properties.textOf("UseBeltCenterHeight"))
|
|
749
|
+
? numberChild(properties, "BeltCenterHeight", beltDiameter)
|
|
750
|
+
: beltDiameter;
|
|
751
|
+
const angle = degreesToRadians(numberChild(properties, "Angle", 90));
|
|
752
|
+
const stepAngle = Math.abs(degreesToRadians(numberChild(properties, "StepAngle", 15)));
|
|
753
|
+
if (innerRadius <= 0 || width <= 0 || thickness <= 0 || Math.abs(angle) <= 1e-6) {
|
|
754
|
+
warn(state, {
|
|
755
|
+
code: "DEMO3D_THREE_INVALID_BELT_DIMENSIONS",
|
|
756
|
+
message: `Curve belt conveyor dimensions must be positive and its angle non-zero (inner radius ${innerRadius}, width ${width}, height ${thickness}, angle ${numberChild(properties, "Angle", 90)}).`,
|
|
757
|
+
sourceId: visual.id,
|
|
758
|
+
sourceType: visual.typeName,
|
|
759
|
+
xmlPath: visual.xml.path
|
|
760
|
+
});
|
|
761
|
+
return undefined;
|
|
762
|
+
}
|
|
763
|
+
const surfaceMaterial = firstMaterial(properties.child("SurfaceMaterial") ?? properties);
|
|
764
|
+
const sideMaterial = firstMaterial(properties.child("SurfaceSideMaterial") ?? properties) ?? surfaceMaterial;
|
|
765
|
+
return {
|
|
766
|
+
innerRadius,
|
|
767
|
+
width,
|
|
768
|
+
thickness,
|
|
769
|
+
angle,
|
|
770
|
+
stepAngle: stepAngle > 1e-6 ? stepAngle : Math.PI / 12,
|
|
771
|
+
surfaceMaterial,
|
|
772
|
+
sideMaterial
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function getProceduralCurveBeltGeometry(belt, state) {
|
|
776
|
+
const key = `curve-belt:${belt.innerRadius}:${belt.width}:${belt.thickness}:${belt.angle}:${belt.stepAngle}`;
|
|
777
|
+
const cached = state.primitiveGeometryCache.get(key);
|
|
778
|
+
if (cached) {
|
|
779
|
+
return cached;
|
|
780
|
+
}
|
|
781
|
+
const surfacePositions = [];
|
|
782
|
+
const sidePositions = [];
|
|
783
|
+
const outerRadius = belt.innerRadius + belt.width;
|
|
784
|
+
const segments = Math.max(4, Math.ceil(Math.abs(belt.angle) / belt.stepAngle));
|
|
785
|
+
const point = (radius, y, theta) => [
|
|
786
|
+
radius * Math.cos(theta),
|
|
787
|
+
y,
|
|
788
|
+
radius * Math.sin(theta)
|
|
789
|
+
];
|
|
790
|
+
for (let segment = 0; segment < segments; segment += 1) {
|
|
791
|
+
const theta0 = belt.angle * (segment / segments);
|
|
792
|
+
const theta1 = belt.angle * ((segment + 1) / segments);
|
|
793
|
+
const middle = (theta0 + theta1) / 2;
|
|
794
|
+
const innerTop0 = point(belt.innerRadius, 0, theta0);
|
|
795
|
+
const innerTop1 = point(belt.innerRadius, 0, theta1);
|
|
796
|
+
const outerTop0 = point(outerRadius, 0, theta0);
|
|
797
|
+
const outerTop1 = point(outerRadius, 0, theta1);
|
|
798
|
+
const innerBottom0 = point(belt.innerRadius, -belt.thickness, theta0);
|
|
799
|
+
const innerBottom1 = point(belt.innerRadius, -belt.thickness, theta1);
|
|
800
|
+
const outerBottom0 = point(outerRadius, -belt.thickness, theta0);
|
|
801
|
+
const outerBottom1 = point(outerRadius, -belt.thickness, theta1);
|
|
802
|
+
appendOrientedQuad(surfacePositions, innerTop0, outerTop0, outerTop1, innerTop1, [0, 1, 0]);
|
|
803
|
+
appendOrientedQuad(surfacePositions, innerBottom0, innerBottom1, outerBottom1, outerBottom0, [0, -1, 0]);
|
|
804
|
+
appendOrientedQuad(sidePositions, outerBottom0, outerBottom1, outerTop1, outerTop0, [Math.cos(middle), 0, Math.sin(middle)]);
|
|
805
|
+
appendOrientedQuad(sidePositions, innerBottom0, innerTop0, innerTop1, innerBottom1, [-Math.cos(middle), 0, -Math.sin(middle)]);
|
|
806
|
+
}
|
|
807
|
+
const direction = Math.sign(belt.angle) || 1;
|
|
808
|
+
const start = 0;
|
|
809
|
+
const end = belt.angle;
|
|
810
|
+
appendOrientedQuad(surfacePositions, point(belt.innerRadius, -belt.thickness, start), point(outerRadius, -belt.thickness, start), point(outerRadius, 0, start), point(belt.innerRadius, 0, start), [direction * Math.sin(start), 0, -direction * Math.cos(start)]);
|
|
811
|
+
appendOrientedQuad(surfacePositions, point(belt.innerRadius, -belt.thickness, end), point(belt.innerRadius, 0, end), point(outerRadius, 0, end), point(outerRadius, -belt.thickness, end), [-direction * Math.sin(end), 0, direction * Math.cos(end)]);
|
|
812
|
+
const geometry = new state.three.BufferGeometry();
|
|
813
|
+
geometry.setAttribute("position", new state.three.Float32BufferAttribute([...surfacePositions, ...sidePositions], 3));
|
|
814
|
+
geometry.addGroup(0, surfacePositions.length / 3, 0);
|
|
815
|
+
geometry.addGroup(surfacePositions.length / 3, sidePositions.length / 3, 1);
|
|
816
|
+
geometry.computeVertexNormals();
|
|
817
|
+
geometry.computeBoundingBox();
|
|
818
|
+
geometry.computeBoundingSphere();
|
|
819
|
+
state.primitiveGeometryCache.set(key, geometry);
|
|
820
|
+
state.stats.geometries += 1;
|
|
821
|
+
return geometry;
|
|
822
|
+
}
|
|
536
823
|
function createRectangularBeltGeometry(belt, state) {
|
|
537
824
|
const geometry = new state.three.BoxGeometry(belt.length, belt.thickness, belt.width);
|
|
538
825
|
geometry.translate(belt.xOffset + belt.length / 2, -belt.thickness / 2, belt.zOffset);
|
|
@@ -577,6 +864,117 @@ function createRoundedBeltGeometry(belt, state) {
|
|
|
577
864
|
}
|
|
578
865
|
return geometry;
|
|
579
866
|
}
|
|
867
|
+
function createRackObject(visual, state) {
|
|
868
|
+
const properties = visual.properties;
|
|
869
|
+
if (!properties) {
|
|
870
|
+
return undefined;
|
|
871
|
+
}
|
|
872
|
+
const frameHeight = numberChild(properties, "FrameHeight", 0);
|
|
873
|
+
const uprightWidth = numberChild(properties, "UprightWidth", 0.05);
|
|
874
|
+
const uprightDepth = numberChild(properties, "UprightDepth", 0.05);
|
|
875
|
+
const frameDepth = numberChildOptional(properties, "FrameDepth") ?? numberChild(properties, "BayDepth", 0);
|
|
876
|
+
const strutWidth = numberChild(properties, "StrutWidth", 0.05);
|
|
877
|
+
if (frameHeight <= 0 || frameDepth <= 0 || uprightWidth <= 0 || uprightDepth <= 0 || strutWidth <= 0) {
|
|
878
|
+
warn(state, {
|
|
879
|
+
code: "DEMO3D_THREE_INVALID_RACK_DIMENSIONS",
|
|
880
|
+
message: `Rack dimensions must be positive (height ${frameHeight}, depth ${frameDepth}, upright ${uprightWidth} x ${uprightDepth}, strut ${strutWidth}).`,
|
|
881
|
+
sourceId: visual.id,
|
|
882
|
+
sourceType: visual.typeName,
|
|
883
|
+
xmlPath: visual.xml.path
|
|
884
|
+
});
|
|
885
|
+
return undefined;
|
|
886
|
+
}
|
|
887
|
+
const numBays = Math.max(1, Math.round(numberChild(properties, "NumBays", 1)));
|
|
888
|
+
const bayWidth = Math.max(0, numberChild(properties, "BayWidth", 0));
|
|
889
|
+
const framePitch = bayWidth + uprightWidth + Math.max(0, numberChild(properties, "MinFrameGap", 0));
|
|
890
|
+
const framePositions = [0];
|
|
891
|
+
if (!isFalse(properties.child("MiddleFrames")?.textOf("Visible"))) {
|
|
892
|
+
for (let bay = 1; bay < numBays; bay += 1) {
|
|
893
|
+
framePositions.push(bay * framePitch);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (!isFalse(properties.child("LastFrame")?.textOf("Visible")) && framePitch > 0) {
|
|
897
|
+
framePositions.push(numBays * framePitch);
|
|
898
|
+
}
|
|
899
|
+
const initialStrutHeight = clamp(numberChild(properties, "InitialStrutHeight", strutWidth / 2), 0, frameHeight);
|
|
900
|
+
const strutSpanHeight = numberChild(properties, "StrutSpanHeight", frameHeight);
|
|
901
|
+
const strutHeights = [initialStrutHeight];
|
|
902
|
+
if (strutSpanHeight > 0) {
|
|
903
|
+
for (let height = initialStrutHeight + strutSpanHeight; height < frameHeight - 1e-6; height += strutSpanHeight) {
|
|
904
|
+
strutHeights.push(height);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const extensionHeight = clamp(frameHeight - Math.max(0, numberChild(properties, "ExtensionStrutOffset", strutWidth / 2)), 0, frameHeight);
|
|
908
|
+
if (!strutHeights.some((height) => Math.abs(height - extensionHeight) < 1e-6)) {
|
|
909
|
+
strutHeights.push(extensionHeight);
|
|
910
|
+
}
|
|
911
|
+
strutHeights.sort((left, right) => left - right);
|
|
912
|
+
const uprightMaterial = getColorMaterial(demo3dColorValue(properties.child("UprightColor"), 0xffd3d3d3 | 0), state);
|
|
913
|
+
const strutMaterial = getColorMaterial(demo3dColorValue(properties.child("StrutColor"), 0xffd3d3d3 | 0), state);
|
|
914
|
+
const uprightGeometry = cachedBoxGeometry(uprightWidth, frameHeight, uprightDepth, state);
|
|
915
|
+
const strutGeometry = cachedBoxGeometry(strutWidth, strutWidth, frameDepth, state);
|
|
916
|
+
const group = new state.three.Group();
|
|
917
|
+
group.name = `${visual.displayName ?? visual.id ?? "Rack"} generated geometry`;
|
|
918
|
+
for (const frameX of framePositions) {
|
|
919
|
+
for (const z of [-frameDepth / 2, frameDepth / 2]) {
|
|
920
|
+
const upright = new state.three.Mesh(uprightGeometry, uprightMaterial);
|
|
921
|
+
upright.name = "Rack upright";
|
|
922
|
+
upright.position.set(frameX, frameHeight / 2, z);
|
|
923
|
+
upright.userData.demo3d = { kind: "procedural-rack-upright", frameX };
|
|
924
|
+
group.add(upright);
|
|
925
|
+
state.stats.meshes += 1;
|
|
926
|
+
}
|
|
927
|
+
for (const height of strutHeights) {
|
|
928
|
+
const strut = new state.three.Mesh(strutGeometry, strutMaterial);
|
|
929
|
+
strut.name = "Rack strut";
|
|
930
|
+
strut.position.set(frameX, height, 0);
|
|
931
|
+
strut.userData.demo3d = { kind: "procedural-rack-strut", frameX, height };
|
|
932
|
+
group.add(strut);
|
|
933
|
+
state.stats.meshes += 1;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
group.userData.demo3d = {
|
|
937
|
+
kind: "procedural-rack",
|
|
938
|
+
id: visual.id,
|
|
939
|
+
name: visual.displayName,
|
|
940
|
+
typeName: visual.typeName,
|
|
941
|
+
xmlPath: visual.xml.path,
|
|
942
|
+
frameHeight,
|
|
943
|
+
frameDepth,
|
|
944
|
+
framePositions,
|
|
945
|
+
strutHeights
|
|
946
|
+
};
|
|
947
|
+
state.stats.groups += 1;
|
|
948
|
+
state.stats.directVisuals += 1;
|
|
949
|
+
state.stats.proceduralRacks += 1;
|
|
950
|
+
return group;
|
|
951
|
+
}
|
|
952
|
+
function demo3dColorValue(container, fallback) {
|
|
953
|
+
const numeric = container?.text.trim();
|
|
954
|
+
if (numeric) {
|
|
955
|
+
const parsed = Number(numeric);
|
|
956
|
+
if (Number.isFinite(parsed)) {
|
|
957
|
+
return parsed;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
switch (container?.textOf("name")?.trim().toLowerCase()) {
|
|
961
|
+
case "black":
|
|
962
|
+
return 0xff000000 | 0;
|
|
963
|
+
case "darkgray":
|
|
964
|
+
case "darkgrey":
|
|
965
|
+
return 0xffa9a9a9 | 0;
|
|
966
|
+
case "gray":
|
|
967
|
+
case "grey":
|
|
968
|
+
return 0xff808080 | 0;
|
|
969
|
+
case "lightgray":
|
|
970
|
+
case "lightgrey":
|
|
971
|
+
return 0xffd3d3d3 | 0;
|
|
972
|
+
case "white":
|
|
973
|
+
return 0xffffffff | 0;
|
|
974
|
+
default:
|
|
975
|
+
return fallback;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
580
978
|
function createSupportStandObject(visual, parentVisual, state) {
|
|
581
979
|
const stand = visual instanceof Demo3DSupportStand
|
|
582
980
|
? visual
|
|
@@ -586,7 +984,13 @@ function createSupportStandObject(visual, parentVisual, state) {
|
|
|
586
984
|
const footProfile = properties?.footProfile;
|
|
587
985
|
const floorPlateProfile = properties?.floorPlateProfile;
|
|
588
986
|
const crossBraceProfile = properties?.crossBraceProfile;
|
|
589
|
-
if (!properties
|
|
987
|
+
if (!properties) {
|
|
988
|
+
return undefined;
|
|
989
|
+
}
|
|
990
|
+
if (!legProfile && !crossBraceProfile) {
|
|
991
|
+
return createDefaultSupportStandObject(visual, parentVisual, properties, state);
|
|
992
|
+
}
|
|
993
|
+
if (!legProfile || !crossBraceProfile) {
|
|
590
994
|
warn(state, {
|
|
591
995
|
code: "DEMO3D_THREE_UNSUPPORTED_SUPPORT_STAND_PROFILE",
|
|
592
996
|
message: "Support stand is missing its leg or cross-brace extrusion profile.",
|
|
@@ -654,6 +1058,84 @@ function createSupportStandObject(visual, parentVisual, state) {
|
|
|
654
1058
|
state.stats.proceduralSupportStands += 1;
|
|
655
1059
|
return group;
|
|
656
1060
|
}
|
|
1061
|
+
const DEFAULT_CONVEYOR_HEIGHT = 0.83;
|
|
1062
|
+
const DEFAULT_SUPPORT_LEG_SIZE = 0.04;
|
|
1063
|
+
const DEFAULT_SUPPORT_FOOT_SIZE = 0.06;
|
|
1064
|
+
const DEFAULT_SUPPORT_FLOOR_PLATE_SIZE = 0.1;
|
|
1065
|
+
function createDefaultSupportStandObject(visual, parentVisual, properties, state) {
|
|
1066
|
+
const parentWidth = numberChildOptional(parentVisual?.properties, "RollerWidth") ??
|
|
1067
|
+
numberChildOptional(parentVisual?.properties, "BeltWidth") ??
|
|
1068
|
+
numberChildOptional(parentVisual?.properties, "Width") ??
|
|
1069
|
+
DEFAULT_BELT_WIDTH;
|
|
1070
|
+
const conveyorOffset = properties.conveyorOffset;
|
|
1071
|
+
const sideExtension = Math.abs(conveyorOffset[2] ?? 0);
|
|
1072
|
+
const span = Math.max(parentWidth + sideExtension * 2, 0.1);
|
|
1073
|
+
const top = conveyorOffset[1] ?? 0;
|
|
1074
|
+
const x = conveyorOffset[0] ?? 0;
|
|
1075
|
+
const floorY = numberChild(properties.xml, "FloorY", 0);
|
|
1076
|
+
const explicitStandHeight = numberChildOptional(properties.xml, "StandHeight") ??
|
|
1077
|
+
numberChildOptional(properties.xml, "SupportHeight") ??
|
|
1078
|
+
numberChildOptional(properties.xml, "Height");
|
|
1079
|
+
const explicitLegHeight = numberChildOptional(properties.xml, "LegHeight");
|
|
1080
|
+
const conveyorHeight = numberChildOptional(parentVisual?.properties, "ConveyorHeight") ?? DEFAULT_CONVEYOR_HEIGHT;
|
|
1081
|
+
const componentHeight = explicitLegHeight === undefined
|
|
1082
|
+
? undefined
|
|
1083
|
+
: explicitLegHeight + properties.footHeight + properties.floorPlateHeight;
|
|
1084
|
+
const supportHeight = Math.max(explicitStandHeight ?? componentHeight ?? conveyorHeight + top - floorY, properties.footHeight + properties.floorPlateHeight + 0.001);
|
|
1085
|
+
const bottom = top - supportHeight;
|
|
1086
|
+
const floorPlateHeight = clamp(properties.floorPlateHeight, 0, supportHeight);
|
|
1087
|
+
const footHeight = clamp(properties.footHeight, 0, supportHeight - floorPlateHeight);
|
|
1088
|
+
const legHeight = Math.max(supportHeight - floorPlateHeight - footHeight, 0.001);
|
|
1089
|
+
const braces = [...properties.crossBraceHeights]
|
|
1090
|
+
.filter((height) => height > 0 && height < supportHeight)
|
|
1091
|
+
.sort((left, right) => left - right);
|
|
1092
|
+
const group = new state.three.Group();
|
|
1093
|
+
group.name = `${visual.displayName ?? visual.id ?? "Support stand"} default geometry`;
|
|
1094
|
+
const legMaterial = supportComponentMaterial(visual.properties?.child("LegMaterial"), properties.legMaterial, state);
|
|
1095
|
+
const footMaterial = supportComponentMaterial(visual.properties?.child("FootMaterial"), properties.footMaterial, state);
|
|
1096
|
+
const floorPlateMaterial = supportComponentMaterial(visual.properties?.child("FloorPlateMaterial"), properties.floorPlateMaterial, state);
|
|
1097
|
+
const braceMaterial = supportComponentMaterial(visual.properties?.child("CrossBraceMaterial"), properties.crossBraceMaterial, state);
|
|
1098
|
+
for (const z of [-span / 2, span / 2]) {
|
|
1099
|
+
addDefaultSupportBox(group, "leg", DEFAULT_SUPPORT_LEG_SIZE, legHeight, DEFAULT_SUPPORT_LEG_SIZE, [x, top - legHeight / 2, z], legMaterial, state);
|
|
1100
|
+
if (footHeight > 0) {
|
|
1101
|
+
addDefaultSupportBox(group, "foot", DEFAULT_SUPPORT_FOOT_SIZE, footHeight, DEFAULT_SUPPORT_FOOT_SIZE, [x, bottom + floorPlateHeight + footHeight / 2, z], footMaterial, state);
|
|
1102
|
+
}
|
|
1103
|
+
if (floorPlateHeight > 0) {
|
|
1104
|
+
addDefaultSupportBox(group, "floor plate", DEFAULT_SUPPORT_FLOOR_PLATE_SIZE, floorPlateHeight, DEFAULT_SUPPORT_FLOOR_PLATE_SIZE, [x, bottom + floorPlateHeight / 2, z], floorPlateMaterial, state);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
for (const height of braces) {
|
|
1108
|
+
addDefaultSupportBox(group, "cross brace", DEFAULT_SUPPORT_LEG_SIZE, DEFAULT_SUPPORT_LEG_SIZE, span, [x, bottom + height, 0], braceMaterial, state);
|
|
1109
|
+
}
|
|
1110
|
+
addDefaultSupportBox(group, "top brace", DEFAULT_SUPPORT_LEG_SIZE, DEFAULT_SUPPORT_LEG_SIZE, span, [x, top - DEFAULT_SUPPORT_LEG_SIZE / 2, 0], braceMaterial, state);
|
|
1111
|
+
group.userData.demo3d = {
|
|
1112
|
+
kind: "procedural-support-stand",
|
|
1113
|
+
id: visual.id,
|
|
1114
|
+
name: visual.displayName,
|
|
1115
|
+
typeName: visual.typeName,
|
|
1116
|
+
xmlPath: visual.xml.path,
|
|
1117
|
+
supportHeight,
|
|
1118
|
+
span,
|
|
1119
|
+
braceHeights: braces,
|
|
1120
|
+
approximate: true,
|
|
1121
|
+
defaultProfiles: true
|
|
1122
|
+
};
|
|
1123
|
+
state.stats.groups += 1;
|
|
1124
|
+
state.stats.directVisuals += 1;
|
|
1125
|
+
state.stats.proceduralSupportStands += 1;
|
|
1126
|
+
return group;
|
|
1127
|
+
}
|
|
1128
|
+
function supportComponentMaterial(container, fallbackColor, state) {
|
|
1129
|
+
return materialFromContainer(container, state) ?? getColorMaterial(fallbackColor, state);
|
|
1130
|
+
}
|
|
1131
|
+
function addDefaultSupportBox(group, component, width, height, depth, position, material, state) {
|
|
1132
|
+
const mesh = new state.three.Mesh(cachedBoxGeometry(width, height, depth, state), material);
|
|
1133
|
+
mesh.name = `Default support ${component}`;
|
|
1134
|
+
mesh.position.set(position[0], position[1], position[2]);
|
|
1135
|
+
mesh.userData.demo3d = { kind: "support-stand-component", component, approximate: true };
|
|
1136
|
+
group.add(mesh);
|
|
1137
|
+
state.stats.meshes += 1;
|
|
1138
|
+
}
|
|
657
1139
|
function addSupportProfileMeshes(group, profile, axis, length, position, fallbackColor, component, state) {
|
|
658
1140
|
profile.polygons.forEach((polygon, index) => {
|
|
659
1141
|
if (polygon.points.length < 3) {
|
|
@@ -932,6 +1414,25 @@ function addProceduralRollers(group, visual, state) {
|
|
|
932
1414
|
const material = materialFromContainer(properties.child("SurfaceMaterial") ?? properties, state)
|
|
933
1415
|
?? getColorMaterial(numberChildOptional(properties, "RollerColor"), state);
|
|
934
1416
|
const geometry = cachedRollerGeometry(radius, width, state);
|
|
1417
|
+
if (visual.typeName === "e3d:DiverterRollerConveyor") {
|
|
1418
|
+
const length = numberChild(properties, "Length", 1);
|
|
1419
|
+
const pitch = Math.max(numberChild(properties, "RollerPitch", diameter * 1.2), diameter);
|
|
1420
|
+
const count = Math.max(1, Math.round(numberChildOptional(properties, "RollerCount") ?? length / pitch));
|
|
1421
|
+
const across = Math.max(1, Math.round(numberChild(properties, "NumRollersAcrossWidth", 1)));
|
|
1422
|
+
const totalWidth = numberChild(properties, "Width", width);
|
|
1423
|
+
for (let index = 0; index < count; index += 1) {
|
|
1424
|
+
for (let acrossIndex = 0; acrossIndex < across; acrossIndex += 1) {
|
|
1425
|
+
const roller = new state.three.Mesh(geometry, material);
|
|
1426
|
+
roller.name = `Generated divert roller ${index + 1}.${acrossIndex + 1}`;
|
|
1427
|
+
roller.position.set(((index + 0.5) / count) * length, 0, ((acrossIndex + 0.5) / across - 0.5) * totalWidth);
|
|
1428
|
+
roller.userData.demo3d = { kind: "procedural-roller", index, acrossIndex };
|
|
1429
|
+
group.add(roller);
|
|
1430
|
+
state.stats.meshes += 1;
|
|
1431
|
+
state.stats.proceduralRollers += 1;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
935
1436
|
if (visual.typeName === "e3d:CurveRollerConveyor") {
|
|
936
1437
|
const count = Math.max(1, Math.round(numberChild(properties, "RollerCount", 1)));
|
|
937
1438
|
const innerRadius = numberChild(properties, "InnerRadius", 0.5);
|
|
@@ -1045,6 +1546,7 @@ function cachedBoxGeometry(width, height, depth, state) {
|
|
|
1045
1546
|
function isRollerConveyorType(typeName) {
|
|
1046
1547
|
return (typeName === "e3d:StraightRollerConveyor" ||
|
|
1047
1548
|
typeName === "e3d:CurveRollerConveyor" ||
|
|
1549
|
+
typeName === "e3d:DiverterRollerConveyor" ||
|
|
1048
1550
|
typeName === "e3d:InjectorRollerConveyor");
|
|
1049
1551
|
}
|
|
1050
1552
|
function createImportedImageObject(visual, state) {
|
|
@@ -1207,15 +1709,15 @@ function createMeshObject(meshId, source, material, state) {
|
|
|
1207
1709
|
sourceType,
|
|
1208
1710
|
xmlPath
|
|
1209
1711
|
});
|
|
1210
|
-
return state.options.showPlaceholders ===
|
|
1211
|
-
?
|
|
1212
|
-
:
|
|
1712
|
+
return state.options.showPlaceholders === true
|
|
1713
|
+
? createPlaceholderMesh(sourceId ?? meshId, meshId, material, state)
|
|
1714
|
+
: undefined;
|
|
1213
1715
|
}
|
|
1214
1716
|
const geometry = getGeometry(mesh, state);
|
|
1215
1717
|
if (!geometry) {
|
|
1216
|
-
return state.options.showPlaceholders ===
|
|
1217
|
-
?
|
|
1218
|
-
:
|
|
1718
|
+
return state.options.showPlaceholders === true
|
|
1719
|
+
? createPlaceholderMesh(sourceId ?? meshId, meshId, material, state)
|
|
1720
|
+
: undefined;
|
|
1219
1721
|
}
|
|
1220
1722
|
const object = new state.three.Mesh(geometry, getMaterial(material, state));
|
|
1221
1723
|
object.name = source instanceof Demo3DVisual ? source.displayName ?? meshId : meshId;
|
|
@@ -1904,6 +2406,18 @@ function findMeshReferenceIds(root) {
|
|
|
1904
2406
|
});
|
|
1905
2407
|
return [...ids];
|
|
1906
2408
|
}
|
|
2409
|
+
function findVisualMeshReferenceIds(root) {
|
|
2410
|
+
const ids = new Set();
|
|
2411
|
+
for (const child of root.children) {
|
|
2412
|
+
if (child.localName === "C") {
|
|
2413
|
+
continue;
|
|
2414
|
+
}
|
|
2415
|
+
for (const id of findMeshReferenceIds(child)) {
|
|
2416
|
+
ids.add(id);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
return [...ids];
|
|
2420
|
+
}
|
|
1907
2421
|
function firstMaterial(root) {
|
|
1908
2422
|
let found;
|
|
1909
2423
|
visit(root, (node) => {
|
|
@@ -2056,6 +2570,11 @@ function isRenderableVisualVisible(visual) {
|
|
|
2056
2570
|
const visible = visual.properties?.textOf("Visible");
|
|
2057
2571
|
return visible === undefined || !(visible === "0" || visible.toLowerCase() === "false");
|
|
2058
2572
|
}
|
|
2573
|
+
function isLayerVisible(layerName, state) {
|
|
2574
|
+
return (state.options.includeHiddenLayers === true ||
|
|
2575
|
+
layerName === undefined ||
|
|
2576
|
+
state.layerVisibilityByName.get(layerName) !== false);
|
|
2577
|
+
}
|
|
2059
2578
|
function isTrue(value) {
|
|
2060
2579
|
return value === "1" || value?.toLowerCase() === "true";
|
|
2061
2580
|
}
|