@jfk-solutions/demo3d-file-format 1.6.0 → 1.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/README.md +78 -1
- package/dist/examples/export-embedded-viewer-standalone.html +193 -0
- package/dist/examples/three-render-smoke-standalone.html +4933 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +36 -3
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +178 -11
- package/dist/model.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +135 -9
- package/dist/parser.js.map +1 -1
- package/dist/raw3d.d.ts +35 -4
- package/dist/raw3d.d.ts.map +1 -1
- package/dist/raw3d.js +63 -6
- package/dist/raw3d.js.map +1 -1
- package/dist/three/index.d.ts +4 -0
- package/dist/three/index.d.ts.map +1 -1
- package/dist/three/index.js +186 -21
- package/dist/three/index.js.map +1 -1
- package/dist/three/presentation.d.ts +31 -0
- package/dist/three/presentation.d.ts.map +1 -0
- package/dist/three/presentation.js +137 -0
- package/dist/three/presentation.js.map +1 -0
- package/dist/three/raw3d.d.ts +4 -0
- package/dist/three/raw3d.d.ts.map +1 -1
- package/dist/three/raw3d.js +202 -14
- package/dist/three/raw3d.js.map +1 -1
- package/dist/xml.d.ts +4 -1
- package/dist/xml.d.ts.map +1 -1
- package/dist/xml.js +14 -3
- package/dist/xml.js.map +1 -1
- package/package.json +6 -2
package/dist/three/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Demo3DConveyorSideProperties, Demo3DDimensionAspect, Demo3DDimensionPoint, Demo3DExtrusionPolygon, Demo3DExtrusionProfile, Demo3DMaterial, Demo3DMesh, Demo3DPackage, Demo3DProject, Demo3DPhotoEye, Demo3DResource, Demo3DSupportStand, Demo3DVisual } from "../model.js";
|
|
1
|
+
import { Demo3DConveyorSideProperties, Demo3DDimensionAspect, Demo3DDimensionPoint, Demo3DExtrusionPolygon, Demo3DExtrusionProfile, Demo3DMaterial, Demo3DMesh, Demo3DPackage, Demo3DPointCloud, Demo3DProject, Demo3DPhotoEye, Demo3DResource, Demo3DSupportStand, Demo3DVisual } from "../model.js";
|
|
2
2
|
import { Demo3DBinaryBlock, Demo3DXmlElement } from "../xml.js";
|
|
3
3
|
export { createRaw3DThreeGroup, createRaw3DThreeScene, decodeRaw3DThreeGeometry } from "./raw3d.js";
|
|
4
4
|
export { createDemo3DThreeRenderer } from "./renderer.js";
|
|
5
|
+
export { applyDemo3DThreeRenderMode } from "./presentation.js";
|
|
5
6
|
export async function createDemo3DThreeScene(parsed, options = {}) {
|
|
6
7
|
const three = await resolveThree(options);
|
|
7
8
|
const group = await createDemo3DThreeGroup(parsed, { ...options, three });
|
|
@@ -45,6 +46,38 @@ export async function createDemo3DThreeGroup(parsed, options = {}) {
|
|
|
45
46
|
};
|
|
46
47
|
return root;
|
|
47
48
|
}
|
|
49
|
+
/** Dispose geometries, materials, and textures owned by a generated Demo3D or RAW3D group. */
|
|
50
|
+
export function disposeDemo3DThreeGroup(root) {
|
|
51
|
+
const geometries = new Set();
|
|
52
|
+
const materials = new Set();
|
|
53
|
+
const textures = new Set();
|
|
54
|
+
root.traverse((object) => {
|
|
55
|
+
const renderable = object;
|
|
56
|
+
if (renderable.geometry) {
|
|
57
|
+
geometries.add(renderable.geometry);
|
|
58
|
+
}
|
|
59
|
+
const objectMaterials = Array.isArray(renderable.material)
|
|
60
|
+
? renderable.material
|
|
61
|
+
: renderable.material ? [renderable.material] : [];
|
|
62
|
+
for (const material of objectMaterials) {
|
|
63
|
+
materials.add(material);
|
|
64
|
+
for (const value of Object.values(material)) {
|
|
65
|
+
if (value?.isTexture) {
|
|
66
|
+
textures.add(value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
for (const geometry of geometries) {
|
|
72
|
+
geometry.dispose();
|
|
73
|
+
}
|
|
74
|
+
for (const material of materials) {
|
|
75
|
+
material.dispose();
|
|
76
|
+
}
|
|
77
|
+
for (const texture of textures) {
|
|
78
|
+
texture.dispose();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
48
81
|
export function decodeDemo3DThreeGeometry(mesh, three) {
|
|
49
82
|
if (mesh.meshFormat !== "TriangleList") {
|
|
50
83
|
throw new Error(`Unsupported Demo3D mesh format: ${mesh.meshFormat ?? "unknown"}`);
|
|
@@ -147,6 +180,7 @@ function createState(three, project, options, buffers) {
|
|
|
147
180
|
options,
|
|
148
181
|
warnings: [],
|
|
149
182
|
meshById: new Map(project.meshes.flatMap((mesh) => (mesh.id ? [[mesh.id, mesh]] : []))),
|
|
183
|
+
pointCloudById: new Map(project.pointClouds.flatMap((pointCloud) => pointCloud.id ? [[pointCloud.id, pointCloud]] : [])),
|
|
150
184
|
serializedObjectById: indexSerializedObjects(project),
|
|
151
185
|
textureImageById: indexTextureImages(project),
|
|
152
186
|
drawingBlockById: indexDrawingBlocks(project),
|
|
@@ -182,6 +216,7 @@ function createState(three, project, options, buffers) {
|
|
|
182
216
|
unreconstructedProceduralVisuals: 0,
|
|
183
217
|
imageVisuals: 0,
|
|
184
218
|
lights: 0,
|
|
219
|
+
pointClouds: 0,
|
|
185
220
|
missingGeometryPlaceholders: 0,
|
|
186
221
|
serializedRenderables: 0,
|
|
187
222
|
unsupported: 0
|
|
@@ -404,6 +439,9 @@ function createDirectVisualObject(visual, state, parentVisual) {
|
|
|
404
439
|
if (visual.typeName === "e3d:ImportedImageVisual") {
|
|
405
440
|
return createImportedImageObject(visual, state);
|
|
406
441
|
}
|
|
442
|
+
if (visual.typeName === "e3d:PointCloudVisual") {
|
|
443
|
+
return createPointCloudObject(visual, state);
|
|
444
|
+
}
|
|
407
445
|
if (visual.typeName === "e3d:StraightBeltConveyor" ||
|
|
408
446
|
visual.typeName === "e3d:CurveBeltConveyor" ||
|
|
409
447
|
visual.typeName === "e3d:InjectorBeltConveyor") {
|
|
@@ -446,6 +484,89 @@ function createDirectVisualObject(visual, state, parentVisual) {
|
|
|
446
484
|
state.stats.directVisuals += 1;
|
|
447
485
|
return object;
|
|
448
486
|
}
|
|
487
|
+
function createPointCloudObject(visual, state) {
|
|
488
|
+
const referenceId = visual.properties?.textOf("PointCloudRef");
|
|
489
|
+
const pointCloud = referenceId ? state.pointCloudById.get(referenceId) : undefined;
|
|
490
|
+
if (!pointCloud) {
|
|
491
|
+
warn(state, {
|
|
492
|
+
code: "DEMO3D_THREE_MISSING_POINT_CLOUD",
|
|
493
|
+
message: `Point cloud reference ${referenceId ?? "<missing>"} could not be resolved.`,
|
|
494
|
+
sourceId: visual.id,
|
|
495
|
+
sourceType: visual.typeName,
|
|
496
|
+
xmlPath: visual.xml.path
|
|
497
|
+
});
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
const group = new state.three.Group();
|
|
501
|
+
group.name = visual.displayName ?? pointCloud.name ?? referenceId ?? visual.typeName;
|
|
502
|
+
const fallbackColor = primaryMaterial(visual)?.diffuse;
|
|
503
|
+
const pointSize = Math.min(128, Math.max(1, numberChild(visual.properties, "PointSize", 1)));
|
|
504
|
+
for (const [primitiveIndex, primitive] of pointCloud.primitives.entries()) {
|
|
505
|
+
const bufferName = primitive.pointsBufferName;
|
|
506
|
+
const buffer = bufferName ? state.bufferByName.get(bufferName.toLowerCase()) : undefined;
|
|
507
|
+
if (!buffer) {
|
|
508
|
+
warn(state, {
|
|
509
|
+
code: "DEMO3D_THREE_MISSING_POINT_CLOUD_BUFFER",
|
|
510
|
+
message: `Point cloud buffer ${bufferName ?? "<missing>"} could not be resolved.`,
|
|
511
|
+
sourceId: visual.id,
|
|
512
|
+
sourceType: visual.typeName,
|
|
513
|
+
xmlPath: primitive.xml.path
|
|
514
|
+
});
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (buffer.byteLength % 12 !== 0) {
|
|
518
|
+
warn(state, {
|
|
519
|
+
code: "DEMO3D_THREE_INVALID_POINT_CLOUD_BUFFER",
|
|
520
|
+
message: `Point cloud buffer ${bufferName} has ${buffer.byteLength} bytes; expected packed Float32 XYZ triples.`,
|
|
521
|
+
sourceId: visual.id,
|
|
522
|
+
sourceType: visual.typeName,
|
|
523
|
+
xmlPath: primitive.xml.path
|
|
524
|
+
});
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const positions = new Float32Array(buffer.byteLength / 4);
|
|
528
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
529
|
+
for (let index = 0; index < positions.length; index += 3) {
|
|
530
|
+
positions[index] = view.getFloat32(index * 4, true);
|
|
531
|
+
positions[index + 1] = view.getFloat32((index + 1) * 4, true);
|
|
532
|
+
positions[index + 2] = -view.getFloat32((index + 2) * 4, true);
|
|
533
|
+
}
|
|
534
|
+
const geometry = new state.three.BufferGeometry();
|
|
535
|
+
geometry.setAttribute("position", new state.three.BufferAttribute(positions, 3));
|
|
536
|
+
geometry.computeBoundingBox();
|
|
537
|
+
geometry.computeBoundingSphere();
|
|
538
|
+
const material = new state.three.PointsMaterial({
|
|
539
|
+
color: demo3dColorToHex(primitive.color ?? fallbackColor ?? -1),
|
|
540
|
+
size: pointSize,
|
|
541
|
+
sizeAttenuation: false
|
|
542
|
+
});
|
|
543
|
+
const points = new state.three.Points(geometry, material);
|
|
544
|
+
points.name = pointCloud.primitives.length > 1 ? `${group.name} ${primitiveIndex + 1}` : group.name;
|
|
545
|
+
points.userData.demo3d = {
|
|
546
|
+
kind: "point-cloud-primitive",
|
|
547
|
+
pointCloudId: pointCloud.id,
|
|
548
|
+
bufferName,
|
|
549
|
+
pointCount: positions.length / 3
|
|
550
|
+
};
|
|
551
|
+
group.add(points);
|
|
552
|
+
state.stats.geometries += 1;
|
|
553
|
+
state.stats.materials += 1;
|
|
554
|
+
}
|
|
555
|
+
if (group.children.length === 0) {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
group.userData.demo3d = {
|
|
559
|
+
kind: "point-cloud",
|
|
560
|
+
id: visual.id,
|
|
561
|
+
pointCloudId: pointCloud.id,
|
|
562
|
+
typeName: visual.typeName,
|
|
563
|
+
xmlPath: visual.xml.path
|
|
564
|
+
};
|
|
565
|
+
state.stats.groups += 1;
|
|
566
|
+
state.stats.directVisuals += 1;
|
|
567
|
+
state.stats.pointClouds += 1;
|
|
568
|
+
return group;
|
|
569
|
+
}
|
|
449
570
|
function createBeltConveyorObject(visual, state) {
|
|
450
571
|
if (visual.typeName === "e3d:CurveBeltConveyor") {
|
|
451
572
|
return createCurveBeltConveyorObject(visual, state);
|
|
@@ -629,17 +750,10 @@ function readProceduralBelt(visual, state) {
|
|
|
629
750
|
if (!properties || isFalse(properties.textOf("CenterVisible"))) {
|
|
630
751
|
return undefined;
|
|
631
752
|
}
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
message: "Straight belt conveyors with a custom CenterProfile are not supported.",
|
|
637
|
-
sourceId: visual.id,
|
|
638
|
-
sourceType: visual.typeName,
|
|
639
|
-
xmlPath: visual.xml.path
|
|
640
|
-
});
|
|
641
|
-
return undefined;
|
|
642
|
-
}
|
|
753
|
+
const centerProfileElement = properties.child("CenterProfile");
|
|
754
|
+
const centerProfile = centerProfileElement && centerProfileElement.children.length > 0
|
|
755
|
+
? new Demo3DExtrusionProfile(centerProfileElement)
|
|
756
|
+
: undefined;
|
|
643
757
|
const startPadding = numberChild(properties, "StartPadding", 0);
|
|
644
758
|
const endPadding = numberChild(properties, "EndPadding", 0);
|
|
645
759
|
const leftPadding = numberChild(properties, "LeftPadding", 0);
|
|
@@ -682,6 +796,7 @@ function readProceduralBelt(visual, state) {
|
|
|
682
796
|
zOffset: (rightPadding - leftPadding) / 2,
|
|
683
797
|
startCap,
|
|
684
798
|
endCap,
|
|
799
|
+
centerProfile,
|
|
685
800
|
surfaceMaterial,
|
|
686
801
|
sideMaterial
|
|
687
802
|
};
|
|
@@ -703,21 +818,55 @@ function beltCap(value, visual, state) {
|
|
|
703
818
|
return "Box";
|
|
704
819
|
}
|
|
705
820
|
function getProceduralBeltGeometry(belt, state) {
|
|
706
|
-
const
|
|
821
|
+
const profileKey = belt.centerProfile?.polygons
|
|
822
|
+
.map((polygon) => polygon.points.map((point) => `${point.x},${point.y}`).join(";"))
|
|
823
|
+
.join("/") ?? "";
|
|
824
|
+
const key = `belt:${belt.length}:${belt.width}:${belt.thickness}:${belt.endRadius}:${belt.xOffset}:${belt.zOffset}:${belt.startCap}:${belt.endCap}:${profileKey}`;
|
|
707
825
|
const cached = state.primitiveGeometryCache.get(key);
|
|
708
826
|
if (cached) {
|
|
709
827
|
return cached;
|
|
710
828
|
}
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
829
|
+
const profilePolygon = belt.centerProfile?.polygons.find((polygon) => polygon.points.length >= 3);
|
|
830
|
+
const geometry = belt.centerProfile && profilePolygon
|
|
831
|
+
? createProfileBeltGeometry(belt, belt.centerProfile, profilePolygon, state)
|
|
832
|
+
: belt.startCap === "Box" && belt.endCap === "Box"
|
|
833
|
+
? createRectangularBeltGeometry(belt, state)
|
|
834
|
+
: createRoundedBeltGeometry(belt, state);
|
|
835
|
+
if (!profilePolygon) {
|
|
836
|
+
convertTriangleGeometryToThreeCoordinates(geometry);
|
|
837
|
+
}
|
|
715
838
|
geometry.computeBoundingBox();
|
|
716
839
|
geometry.computeBoundingSphere();
|
|
717
840
|
state.primitiveGeometryCache.set(key, geometry);
|
|
718
841
|
state.stats.geometries += 1;
|
|
719
842
|
return geometry;
|
|
720
843
|
}
|
|
844
|
+
function createProfileBeltGeometry(belt, profile, polygon, state) {
|
|
845
|
+
const shape = new state.three.Shape();
|
|
846
|
+
polygon.points.forEach((point, index) => {
|
|
847
|
+
const lateral = -(point.x - profile.anchor.x);
|
|
848
|
+
const vertical = point.y - profile.anchor.y;
|
|
849
|
+
if (index === 0) {
|
|
850
|
+
shape.moveTo(lateral, vertical);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
shape.lineTo(lateral, vertical);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
shape.closePath();
|
|
857
|
+
const geometry = new state.three.ExtrudeGeometry(shape, {
|
|
858
|
+
bevelEnabled: false,
|
|
859
|
+
curveSegments: 1,
|
|
860
|
+
depth: belt.length,
|
|
861
|
+
steps: 1
|
|
862
|
+
});
|
|
863
|
+
geometry.rotateY(Math.PI / 2);
|
|
864
|
+
geometry.translate(belt.xOffset, 0, belt.zOffset);
|
|
865
|
+
for (const group of geometry.groups) {
|
|
866
|
+
group.materialIndex = group.materialIndex === 0 ? 1 : 0;
|
|
867
|
+
}
|
|
868
|
+
return geometry;
|
|
869
|
+
}
|
|
721
870
|
function createCurveBeltConveyorObject(visual, state) {
|
|
722
871
|
const belt = readProceduralCurveBelt(visual, state);
|
|
723
872
|
if (!belt) {
|
|
@@ -894,7 +1043,8 @@ function createRackObject(visual, state) {
|
|
|
894
1043
|
}
|
|
895
1044
|
const frameHeight = numberChild(properties, "FrameHeight", 0);
|
|
896
1045
|
const uprightWidth = numberChild(properties, "UprightWidth", 0.05);
|
|
897
|
-
const
|
|
1046
|
+
const serializedUprightDepth = numberChild(properties, "UprightDepth", 0.05);
|
|
1047
|
+
const uprightDepth = serializedUprightDepth > 0 ? serializedUprightDepth : uprightWidth;
|
|
898
1048
|
const frameDepth = numberChildOptional(properties, "FrameDepth") ?? numberChild(properties, "BayDepth", 0);
|
|
899
1049
|
const strutWidth = numberChild(properties, "StrutWidth", 0.05);
|
|
900
1050
|
if (frameHeight <= 0 || frameDepth <= 0 || uprightWidth <= 0 || uprightDepth <= 0 || strutWidth <= 0) {
|
|
@@ -1644,13 +1794,24 @@ function getDirectVisualGeometry(visual, state) {
|
|
|
1644
1794
|
else if (visual.typeName === "e3d:CylinderVisual") {
|
|
1645
1795
|
geometry = new state.three.CylinderGeometry(numberChild(properties, "Radius", 0.5), numberChild(properties, "Radius", 0.5), numberChild(properties, "Length", 1), 32, 1, false, degreesToRadians(numberChild(properties, "StartAngle", 0)), degreesToRadians(numberChild(properties, "Angle", 360)));
|
|
1646
1796
|
}
|
|
1797
|
+
else if (visual.typeName === "e3d:SphereVisual") {
|
|
1798
|
+
geometry = new state.three.SphereGeometry(numberChild(properties, "Radius", 0.5), 32, 16);
|
|
1799
|
+
}
|
|
1647
1800
|
else if (visual.typeName === "e3d:WedgeVisual") {
|
|
1648
1801
|
geometry = createWedgeGeometry(numberChild(properties, "Width", 1), numberChild(properties, "Height", 1), numberChild(properties, "Length", 1), state);
|
|
1649
1802
|
}
|
|
1650
1803
|
if (!geometry) {
|
|
1651
1804
|
return undefined;
|
|
1652
1805
|
}
|
|
1653
|
-
|
|
1806
|
+
// BoxGeometry and the box-tube extrusion are already created in Three's
|
|
1807
|
+
// coordinate system. Reflecting these symmetric, Three-native geometries
|
|
1808
|
+
// swaps their front/back UV layouts even though their bounds do not change.
|
|
1809
|
+
// Other direct primitives still need the Demo3D-to-Three Z reflection.
|
|
1810
|
+
if (visual.typeName !== "e3d:BoxVisual" &&
|
|
1811
|
+
visual.typeName !== "e3d:BoxTubeVisual" &&
|
|
1812
|
+
visual.typeName !== "e3d:SphereVisual") {
|
|
1813
|
+
convertTriangleGeometryToThreeCoordinates(geometry);
|
|
1814
|
+
}
|
|
1654
1815
|
geometry.computeBoundingBox();
|
|
1655
1816
|
geometry.computeBoundingSphere();
|
|
1656
1817
|
state.primitiveGeometryCache.set(key, geometry);
|
|
@@ -1867,7 +2028,9 @@ function getPrimitiveGeometry(aspect, renderable, state) {
|
|
|
1867
2028
|
if (!geometry) {
|
|
1868
2029
|
return undefined;
|
|
1869
2030
|
}
|
|
1870
|
-
|
|
2031
|
+
if (type !== "e3d:BoxRendererAspect" && type !== "e3d:ContainerRendererAspect") {
|
|
2032
|
+
convertTriangleGeometryToThreeCoordinates(geometry);
|
|
2033
|
+
}
|
|
1871
2034
|
geometry.computeBoundingBox();
|
|
1872
2035
|
geometry.computeBoundingSphere();
|
|
1873
2036
|
state.primitiveGeometryCache.set(key, geometry);
|
|
@@ -2048,7 +2211,9 @@ function getTexture(textureReference, state) {
|
|
|
2048
2211
|
texture.colorSpace = state.three.SRGBColorSpace;
|
|
2049
2212
|
}
|
|
2050
2213
|
texture.name = image.name ?? image.id;
|
|
2051
|
-
|
|
2214
|
+
// Demo3D's generated primitive UVs use the conventional bottom-left texture
|
|
2215
|
+
// origin expected by Three's TextureLoader.
|
|
2216
|
+
texture.flipY = true;
|
|
2052
2217
|
state.textureCache.set(textureReference, texture);
|
|
2053
2218
|
state.stats.textures += 1;
|
|
2054
2219
|
return texture;
|