@onerjs/serializers 8.42.9 → 8.43.2
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/3MF/3mfSerializer.d.ts +1 -1
- package/3MF/3mfSerializer.js +11 -10
- package/3MF/3mfSerializer.js.map +1 -1
- package/package.json +3 -3
package/3MF/3mfSerializer.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface IThreeMfSerializerOptions extends IThreeMfSerializerBaseOptions
|
|
|
31
31
|
*/
|
|
32
32
|
export declare class ThreeMfSerializer extends AbstractThreeMfSerializer<Mesh | InstancedMesh, IThreeMfSerializerOptions> {
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Default serialization options: meter units, no instance export.
|
|
35
35
|
*/
|
|
36
36
|
static DefaultOptions: IThreeMfSerializerOptions;
|
|
37
37
|
/**
|
package/3MF/3mfSerializer.js
CHANGED
|
@@ -9,14 +9,14 @@ import { ST_Unit } from "./core/model/3mf.interfaces.js";
|
|
|
9
9
|
import { AbstractThreeMfSerializer } from "./core/model/3mf.serializer.js";
|
|
10
10
|
import { ThreeMfSerializerGlobalConfiguration } from "./3mfSerializer.configuration.js";
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Factory for generating sequential resource IDs for 3MF objects.
|
|
13
13
|
*/
|
|
14
14
|
class IncrementalIdFactory {
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
* @param from
|
|
18
|
-
* @param to
|
|
19
|
-
* @param step
|
|
16
|
+
* Creates an ID factory with the specified range and increment.
|
|
17
|
+
* @param from - Starting ID value
|
|
18
|
+
* @param to - Minimum bound for ID generation (throws if exceeded)
|
|
19
|
+
* @param step - Increment value for each ID
|
|
20
20
|
*/
|
|
21
21
|
constructor(from = 0, to = Number.MIN_SAFE_INTEGER, step = 1) {
|
|
22
22
|
this._from = from;
|
|
@@ -25,8 +25,9 @@ class IncrementalIdFactory {
|
|
|
25
25
|
this._i = from;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* @returns
|
|
28
|
+
* Generates the next ID in the sequence.
|
|
29
|
+
* @returns The next ID value
|
|
30
|
+
* @throws Error if ID generation exceeds the configured bounds
|
|
30
31
|
*/
|
|
31
32
|
next() {
|
|
32
33
|
if (this._i < this._to) {
|
|
@@ -37,8 +38,8 @@ class IncrementalIdFactory {
|
|
|
37
38
|
return v;
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
|
-
*
|
|
41
|
-
* @returns
|
|
41
|
+
* Resets the factory to start generating from the initial value again.
|
|
42
|
+
* @returns This factory instance for chaining
|
|
42
43
|
*/
|
|
43
44
|
reset() {
|
|
44
45
|
this._i = this._from;
|
|
@@ -309,7 +310,7 @@ export class ThreeMfSerializer extends AbstractThreeMfSerializer {
|
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
/**
|
|
312
|
-
*
|
|
313
|
+
* Default serialization options: meter units, no instance export.
|
|
313
314
|
*/
|
|
314
315
|
ThreeMfSerializer.DefaultOptions = { unit: ST_Unit.meter, exportInstances: false };
|
|
315
316
|
/**
|
package/3MF/3mfSerializer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"3mfSerializer.js","sourceRoot":"","sources":["../../../../dev/serializers/src/3MF/3mfSerializer.ts"],"names":[],"mappings":"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","sourcesContent":["// Babylonjs\r\nimport type { SubMesh } from \"core/Meshes\";\r\nimport { Mesh } from \"core/Meshes/mesh\";\r\nimport { InstancedMesh } from \"core/Meshes/instancedMesh\";\r\nimport { Matrix } from \"core/Maths/math\";\r\nimport { Tools } from \"core/Misc/tools\";\r\n\r\n// 3MF\r\nimport { ThreeMfMeshBuilder, type ThreeMfModelBuilder } from \"./core/model/3mf.builder\";\r\nimport { Matrix3d } from \"./core/model/3mf\";\r\n\r\nimport { ST_Unit, type I3mfModel, type I3mfObject } from \"./core/model/3mf.interfaces\";\r\nimport type { I3mfVertexData } from \"./core/model/3mf.types\";\r\nimport { AbstractThreeMfSerializer, type IThreeMfSerializerBaseOptions } from \"./core/model/3mf.serializer\";\r\nimport { ThreeMfSerializerGlobalConfiguration } from \"./3mfSerializer.configuration\";\r\n\r\n/**\r\n *\r\n */\r\nclass IncrementalIdFactory {\r\n /** */\r\n _from: number;\r\n /** */\r\n _to: number;\r\n /** */\r\n _step: number;\r\n /** */\r\n _i: number;\r\n\r\n /**\r\n *\r\n * @param from\r\n * @param to\r\n * @param step\r\n */\r\n public constructor(from: number = 0, to: number = Number.MIN_SAFE_INTEGER, step: number = 1) {\r\n this._from = from;\r\n this._to = to;\r\n this._step = step;\r\n this._i = from;\r\n }\r\n\r\n /**\r\n *\r\n * @returns\r\n */\r\n public next(): number {\r\n if (this._i < this._to) {\r\n throw new Error(\"ST_ResourceID out of bound\");\r\n }\r\n const v = this._i;\r\n this._i += this._step;\r\n return v;\r\n }\r\n\r\n /**\r\n *\r\n * @returns\r\n */\r\n public reset(): IncrementalIdFactory {\r\n this._i = this._from;\r\n return this;\r\n }\r\n}\r\n\r\n/**\r\n * Options controlling how meshes are exported into the 3MF model.\r\n *\r\n * Notes:\r\n * - These flags are kept generic here and are expected to be interpreted by the concrete serializer/model builder.\r\n * - Defaults are set in AbstractThreeMfSerializer.DEFAULT_3MF_EXPORTER_OPTIONS.\r\n */\r\nexport interface IThreeMfSerializerOptions extends IThreeMfSerializerBaseOptions {\r\n /**\r\n * If true, export mesh instances (multiple references to the same geometry) when supported.\r\n * If false, geometry may be duplicated depending on the concrete implementation.\r\n */\r\n exportInstances?: boolean;\r\n}\r\n\r\n/**\r\n * Babylon.js to 3MF serializer.\r\n *\r\n * This serializer converts Babylon meshes into a 3MF model, then relies on the base class\r\n * (AbstractThreeMfSerializer) to package the OPC parts into a zip stream.\r\n *\r\n * Design notes:\r\n * - First pass: export \"source\" meshes (non-instances) and build an index to map Babylon mesh/submesh to 3MF object id.\r\n * - Second pass (optional): export instances as additional build items referencing the original object ids.\r\n * - Submesh export is handled by extracting per-submesh vertex/index buffers so materials/colors can be preserved\r\n * by downstream steps that attach per-object properties.\r\n */\r\nexport class ThreeMfSerializer extends AbstractThreeMfSerializer<Mesh | InstancedMesh, IThreeMfSerializerOptions> {\r\n /**\r\n *\r\n */\r\n static DefaultOptions: IThreeMfSerializerOptions = { unit: ST_Unit.meter, exportInstances: false };\r\n\r\n /**\r\n * Babylon's vertex buffer semantic for positions.\r\n * Babylon uses string-based \"kind\" keys for vertex buffers.\r\n */\r\n private static _PositionKind = \"position\";\r\n\r\n /**\r\n * Cached promise so we only attempt to load fflate once.\r\n * This prevents multiple concurrent LoadScriptAsync calls.\r\n */\r\n private _fflateReadyPromise?: Promise<any>;\r\n\r\n /**\r\n * @param opts serializer options (merged with defaults in base class).\r\n */\r\n public constructor(opts: Partial<IThreeMfSerializerOptions> = {}) {\r\n super({ ...ThreeMfSerializer.DefaultOptions, ...opts });\r\n }\r\n\r\n /**\r\n * Build a 3MF model from Babylon meshes.\r\n *\r\n * Important: this method should not allocate huge intermediate data unless needed.\r\n * Submesh extraction does allocate new position/index arrays for each exported submesh.\r\n * @param builder\r\n * @param meshes\r\n * @returns\r\n */\r\n public override toModel(builder: ThreeMfModelBuilder, ...meshes: Array<Mesh | InstancedMesh>): I3mfModel {\r\n // avoid parasits..\r\n meshes = meshes.filter((m) => m instanceof Mesh || m instanceof InstancedMesh);\r\n\r\n const idFactory = new IncrementalIdFactory();\r\n\r\n const modelBuilder = builder;\r\n\r\n /**\r\n * Index mapping Babylon elements to the created 3MF object.\r\n *\r\n * Why:\r\n * - Instances need to reference the base object id.\r\n * - When exporting submeshes, instances should reference per-submesh objects rather than the whole mesh.\r\n *\r\n * Key type:\r\n * - When exportSubmeshes is true: we store entries for each SubMesh.\r\n * - Otherwise: we store entries for each Mesh.\r\n */\r\n const index = new Map<Mesh | SubMesh, I3mfObject>();\r\n\r\n /**\r\n * If exportInstances is enabled, we collect instanced meshes during the first pass and process them later.\r\n * If exportInstances is disabled, instances are ignored (they will not appear in the output).\r\n */\r\n const instances: Array<InstancedMesh> | null = this.options.exportInstances ? [] : null;\r\n\r\n // First pass: export base meshes (non-instances).\r\n // This creates the \"resource objects\" referenced by build items.\r\n for (let j = 0; j < meshes.length; j++) {\r\n const babylonMesh = meshes[j];\r\n if (babylonMesh instanceof InstancedMesh) {\r\n // Defer instance handling to the second pass.\r\n instances?.push(babylonMesh);\r\n continue;\r\n }\r\n\r\n const objectName = babylonMesh.name || `mesh${j}`;\r\n\r\n // Convert Babylon world matrix to 3MF build transform (3x4).\r\n // This transform will be attached to the build item referencing the created object.\r\n const worldTransform = babylonMesh.getWorldMatrix();\r\n const buildTransform = this._handleBabylonTo3mfMatrixTransformToRef(worldTransform, Matrix3d.Zero());\r\n\r\n // Submeshes can carry material/color boundaries in Babylon.\r\n // When exportSubmeshes is enabled, we export each submesh as its own 3MF object so\r\n // consumers can attach per-object properties (e.g. colors) later.\r\n const subMeshes = babylonMesh.subMeshes;\r\n if (subMeshes === undefined) {\r\n // very unlikely...\r\n continue;\r\n }\r\n\r\n // Babylon.js automatically creates one SubMesh covering the whole mesh if you don’t define any.\r\n const isStandalone = subMeshes.length == 1;\r\n for (let k = 0; k < subMeshes.length; k++) {\r\n const subMesh = subMeshes[k];\r\n\r\n const data = this._extractSubMesh(babylonMesh, subMesh);\r\n\r\n if (data) {\r\n const submeshName = isStandalone ? `${objectName}` : `${objectName}_${k}`;\r\n\r\n const object = new ThreeMfMeshBuilder(idFactory.next()).withData(data).withName(submeshName).build();\r\n\r\n // Add object to resources.\r\n modelBuilder.withMesh(object);\r\n\r\n // Add a build item referencing the object at the mesh world transform.\r\n modelBuilder.withBuild(object.id, buildTransform);\r\n\r\n // Cache mapping for instances (instances will reference this object id).\r\n index.set(subMesh, object);\r\n }\r\n }\r\n }\r\n\r\n // Second pass: export instances as additional build items.\r\n //\r\n // In 3MF terms:\r\n // - We do not duplicate geometry for each instance.\r\n // - We emit multiple build items referencing the same object id, each with its own transform.\r\n if (instances && instances.length) {\r\n // Group instances by their source mesh to keep related builds close in the XML.\r\n const grouped = this._groupBy(instances, (i) => i.sourceMesh);\r\n\r\n for (const [_babylonMesh, _instances] of Array.from(grouped.entries())) {\r\n if (!_instances || !_instances.length) {\r\n continue;\r\n }\r\n\r\n for (let j = 0; j < _instances.length; j++) {\r\n const mesh = _instances[j];\r\n const worldTransform = mesh.getWorldMatrix();\r\n\r\n // If we exported submeshes, the base \"resource objects\" are per-submesh.\r\n // For an instance we emit a build item per submesh object.\r\n const subMeshes = _babylonMesh.subMeshes;\r\n\r\n for (let k = 0; k < subMeshes.length; k++) {\r\n const subMesh = subMeshes[k];\r\n\r\n // Look up the 3MF object created for this submesh in the first pass.\r\n const objectRef = index.get(subMesh);\r\n\r\n if (objectRef) {\r\n modelBuilder.withBuild(objectRef.id, this._handleBabylonTo3mfMatrixTransformToRef(worldTransform, Matrix3d.Zero()));\r\n continue;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return modelBuilder.build();\r\n }\r\n\r\n /**\r\n * Ensure the zip library (fflate) is available in the current runtime.\r\n *\r\n * Host assumptions:\r\n * - This implementation relies on fflate being exposed on globalThis.fflate.\r\n * - If it is not present, it loads a script from ThreeMfSerializerGlobalConfiguration.FFLATEUrl using Babylon Tools.LoadScriptAsync.\r\n * @returns\r\n */\r\n public override async ensureZipLibReadyAsync(): Promise<any> {\r\n if (this._fflateReadyPromise) {\r\n return await this._fflateReadyPromise;\r\n }\r\n\r\n this._fflateReadyPromise = (async () => {\r\n // globalThis is the global object in all modern JS runtimes (browser, workers, Node, etc.).\r\n const g = globalThis as any;\r\n\r\n // If fflate is not already present, load it dynamically.\r\n // This assumes the loaded script sets globalThis.fflate.\r\n if (!g.fflate) {\r\n await Tools.LoadScriptAsync(ThreeMfSerializerGlobalConfiguration.FFLATEUrl);\r\n }\r\n\r\n return g.fflate;\r\n })();\r\n\r\n return await this._fflateReadyPromise;\r\n }\r\n\r\n /**\r\n * Extract a single SubMesh into a standalone vertex/index buffer pair.\r\n *\r\n * Why:\r\n * - 3MF mesh objects typically reference a contiguous vertex array and triangle indices.\r\n * - Babylon SubMesh references a slice of the global index buffer, but shares the vertex buffer.\r\n * - To serialize each submesh independently, we build a compacted vertex buffer containing only the used vertices\r\n * and remap indices accordingly.\r\n *\r\n * Complexity:\r\n * - O(indexCount) time, O(uniqueVerticesInSubmesh) additional memory.\r\n * @param mesh\r\n * @param sm\r\n * @returns\r\n */\r\n private _extractSubMesh(mesh: Mesh, sm: SubMesh): I3mfVertexData | undefined {\r\n const allInd = mesh.getIndices();\r\n if (!allInd) {\r\n return undefined;\r\n }\r\n\r\n const allPos = mesh.getVerticesData(ThreeMfSerializer._PositionKind);\r\n if (!allPos) {\r\n return undefined;\r\n }\r\n\r\n // Fast path: the submesh covers the full index buffer, so reuse the original arrays.\r\n // Note: This returns Babylon-owned arrays; if callers mutate, they will affect source mesh data.\r\n // Kept as-is to avoid extra allocations.\r\n if (sm.indexStart == 0 && sm.indexCount == allInd.length) {\r\n return {\r\n positions: allPos,\r\n indices: allInd,\r\n };\r\n }\r\n\r\n const indStart = sm.indexStart;\r\n\r\n // Map old vertex index -> new compacted vertex index.\r\n const map = new Map<number, number>();\r\n\r\n // Compacted positions (x,y,z repeated). We assemble into number[] then convert to Float32Array at the end.\r\n const newPositions: number[] = [];\r\n\r\n // Indices for the compacted vertex buffer.\r\n // Uint32Array is used to support large meshes; ensure downstream 3MF writer supports 32-bit indices if needed.\r\n const newIndices = new Uint32Array(sm.indexCount);\r\n\r\n for (let i = 0; i < sm.indexCount; i++) {\r\n const oldVi = allInd[indStart + i];\r\n\r\n let newVi = map.get(oldVi);\r\n if (newVi === undefined) {\r\n newVi = map.size;\r\n map.set(oldVi, newVi);\r\n\r\n // Copy the corresponding position (assumes positions are 3-floats per vertex).\r\n // If the source mesh uses a different stride or includes morph targets, this ignores them.\r\n const p = oldVi * 3;\r\n newPositions.push(allPos[p], allPos[p + 1], allPos[p + 2]);\r\n }\r\n\r\n newIndices[i] = newVi;\r\n }\r\n\r\n return {\r\n positions: new Float32Array(newPositions),\r\n indices: newIndices,\r\n };\r\n }\r\n\r\n /**\r\n * Group items by a computed key.\r\n * Used to group instances by sourceMesh so the resulting XML is easier to read and debug.\r\n * @param items\r\n * @param key\r\n * @returns\r\n */\r\n private _groupBy<T, K>(items: readonly T[], key: (v: T) => K): Map<K, T[]> {\r\n const m = new Map<K, T[]>();\r\n for (const it of items) {\r\n const k = key(it);\r\n const arr = m.get(k);\r\n if (arr) {\r\n arr.push(it);\r\n } else {\r\n m.set(k, [it]);\r\n }\r\n }\r\n return m;\r\n }\r\n\r\n /**\r\n * Basis conversion from Babylon coordinate system to the expected 3MF coordinate system.\r\n *\r\n * Here we rotate +90 degrees around X:\r\n * - This is commonly used to convert between Y-up and Z-up conventions.\r\n * - Verify this matches your pipeline (Babylon is typically left-handed Y-up).\r\n */\r\n private static readonly _R_BJS_TO_3MF = Matrix.RotationX(Math.PI / 2).multiply(Matrix.Scaling(1, -1, 1));\r\n\r\n /**\r\n * Converts a Babylon.js 4x4 matrix into a 3MF 3x4 transform matrix and writes the result into ref.\r\n *\r\n * Babylon.js conventions:\r\n * - Babylon exposes matrices with logical row/column indexing (M(row, column)).\r\n * - It stores the 16 coefficients in a contiguous array in row-major order:\r\n * [ M00, M01, M02, M03,\r\n * M10, M11, M12, M13,\r\n * M20, M21, M22, M23,\r\n * M30, M31, M32, M33 ]\r\n *\r\n * 3MF expectation:\r\n * - 3MF uses an affine transform represented as a 3x4 matrix (12 values).\r\n * - The values are taken from the first 3 columns of the 4x4 matrix, across the 4 rows:\r\n * m00 m01 m02 m10 m11 m12 m20 m21 m22 m30 m31 m32\r\n *\r\n * Steps:\r\n * 1) Compose Babylon transform with the basis change:\r\n * tmp = tBjs * _R_BJS_TO_3MF\r\n * 2) Extract the 12 coefficients in 3MF order from tmp.m.\r\n *\r\n * Interop note:\r\n * - Do not transpose here. We only reorder values to match the 3MF 3x4 serialization order.\r\n * - Transposition is only relevant when interfacing with code that assumes column-major storage.\r\n *\r\n * @param tBjs Babylon.js 4x4 matrix.\r\n * @param ref Output 3MF 3x4 matrix container (ref.values assigned).\r\n * @returns ref, for chaining.\r\n */\r\n private _handleBabylonTo3mfMatrixTransformToRef(tBjs: Matrix, ref: Matrix3d): Matrix3d {\r\n const tmp = tBjs.multiplyToRef(ThreeMfSerializer._R_BJS_TO_3MF, Matrix.Zero());\r\n const a = tmp.m;\r\n\r\n // a is Babylon row-major storage. Extract rows 0..3, cols 0..2 in 3MF order.\r\n // 3MF order: m00 m01 m02 m10 m11 m12 m20 m21 m22 m30 m31 m32\r\n ref.values = [a[0], a[1], a[2], a[4], a[5], a[6], a[8], a[9], a[10], a[12], a[13], a[14]];\r\n return ref;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"3mfSerializer.js","sourceRoot":"","sources":["../../../../dev/serializers/src/3MF/3mfSerializer.ts"],"names":[],"mappings":"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","sourcesContent":["// Babylonjs\r\nimport type { SubMesh } from \"core/Meshes\";\r\nimport { Mesh } from \"core/Meshes/mesh\";\r\nimport { InstancedMesh } from \"core/Meshes/instancedMesh\";\r\nimport { Matrix } from \"core/Maths/math\";\r\nimport { Tools } from \"core/Misc/tools\";\r\n\r\n// 3MF\r\nimport { ThreeMfMeshBuilder, type ThreeMfModelBuilder } from \"./core/model/3mf.builder\";\r\nimport { Matrix3d } from \"./core/model/3mf\";\r\n\r\nimport { ST_Unit, type I3mfModel, type I3mfObject } from \"./core/model/3mf.interfaces\";\r\nimport type { I3mfVertexData } from \"./core/model/3mf.types\";\r\nimport { AbstractThreeMfSerializer, type IThreeMfSerializerBaseOptions } from \"./core/model/3mf.serializer\";\r\nimport { ThreeMfSerializerGlobalConfiguration } from \"./3mfSerializer.configuration\";\r\n\r\n/**\r\n * Factory for generating sequential resource IDs for 3MF objects.\r\n */\r\nclass IncrementalIdFactory {\r\n private _from: number;\r\n private _to: number;\r\n private _step: number;\r\n private _i: number;\r\n\r\n /**\r\n * Creates an ID factory with the specified range and increment.\r\n * @param from - Starting ID value\r\n * @param to - Minimum bound for ID generation (throws if exceeded)\r\n * @param step - Increment value for each ID\r\n */\r\n public constructor(from: number = 0, to: number = Number.MIN_SAFE_INTEGER, step: number = 1) {\r\n this._from = from;\r\n this._to = to;\r\n this._step = step;\r\n this._i = from;\r\n }\r\n\r\n /**\r\n * Generates the next ID in the sequence.\r\n * @returns The next ID value\r\n * @throws Error if ID generation exceeds the configured bounds\r\n */\r\n public next(): number {\r\n if (this._i < this._to) {\r\n throw new Error(\"ST_ResourceID out of bound\");\r\n }\r\n const v = this._i;\r\n this._i += this._step;\r\n return v;\r\n }\r\n\r\n /**\r\n * Resets the factory to start generating from the initial value again.\r\n * @returns This factory instance for chaining\r\n */\r\n public reset(): IncrementalIdFactory {\r\n this._i = this._from;\r\n return this;\r\n }\r\n}\r\n\r\n/**\r\n * Options controlling how meshes are exported into the 3MF model.\r\n *\r\n * Notes:\r\n * - These flags are kept generic here and are expected to be interpreted by the concrete serializer/model builder.\r\n * - Defaults are set in AbstractThreeMfSerializer.DEFAULT_3MF_EXPORTER_OPTIONS.\r\n */\r\nexport interface IThreeMfSerializerOptions extends IThreeMfSerializerBaseOptions {\r\n /**\r\n * If true, export mesh instances (multiple references to the same geometry) when supported.\r\n * If false, geometry may be duplicated depending on the concrete implementation.\r\n */\r\n exportInstances?: boolean;\r\n}\r\n\r\n/**\r\n * Babylon.js to 3MF serializer.\r\n *\r\n * This serializer converts Babylon meshes into a 3MF model, then relies on the base class\r\n * (AbstractThreeMfSerializer) to package the OPC parts into a zip stream.\r\n *\r\n * Design notes:\r\n * - First pass: export \"source\" meshes (non-instances) and build an index to map Babylon mesh/submesh to 3MF object id.\r\n * - Second pass (optional): export instances as additional build items referencing the original object ids.\r\n * - Submesh export is handled by extracting per-submesh vertex/index buffers so materials/colors can be preserved\r\n * by downstream steps that attach per-object properties.\r\n */\r\nexport class ThreeMfSerializer extends AbstractThreeMfSerializer<Mesh | InstancedMesh, IThreeMfSerializerOptions> {\r\n /**\r\n * Default serialization options: meter units, no instance export.\r\n */\r\n static DefaultOptions: IThreeMfSerializerOptions = { unit: ST_Unit.meter, exportInstances: false };\r\n\r\n /**\r\n * Babylon's vertex buffer semantic for positions.\r\n * Babylon uses string-based \"kind\" keys for vertex buffers.\r\n */\r\n private static _PositionKind = \"position\";\r\n\r\n /**\r\n * Cached promise so we only attempt to load fflate once.\r\n * This prevents multiple concurrent LoadScriptAsync calls.\r\n */\r\n private _fflateReadyPromise?: Promise<any>;\r\n\r\n /**\r\n * @param opts serializer options (merged with defaults in base class).\r\n */\r\n public constructor(opts: Partial<IThreeMfSerializerOptions> = {}) {\r\n super({ ...ThreeMfSerializer.DefaultOptions, ...opts });\r\n }\r\n\r\n /**\r\n * Build a 3MF model from Babylon meshes.\r\n *\r\n * Important: this method should not allocate huge intermediate data unless needed.\r\n * Submesh extraction does allocate new position/index arrays for each exported submesh.\r\n * @param builder\r\n * @param meshes\r\n * @returns\r\n */\r\n public override toModel(builder: ThreeMfModelBuilder, ...meshes: Array<Mesh | InstancedMesh>): I3mfModel {\r\n // avoid parasits..\r\n meshes = meshes.filter((m) => m instanceof Mesh || m instanceof InstancedMesh);\r\n\r\n const idFactory = new IncrementalIdFactory();\r\n\r\n const modelBuilder = builder;\r\n\r\n /**\r\n * Index mapping Babylon elements to the created 3MF object.\r\n *\r\n * Why:\r\n * - Instances need to reference the base object id.\r\n * - When exporting submeshes, instances should reference per-submesh objects rather than the whole mesh.\r\n *\r\n * Key type:\r\n * - When exportSubmeshes is true: we store entries for each SubMesh.\r\n * - Otherwise: we store entries for each Mesh.\r\n */\r\n const index = new Map<Mesh | SubMesh, I3mfObject>();\r\n\r\n /**\r\n * If exportInstances is enabled, we collect instanced meshes during the first pass and process them later.\r\n * If exportInstances is disabled, instances are ignored (they will not appear in the output).\r\n */\r\n const instances: Array<InstancedMesh> | null = this.options.exportInstances ? [] : null;\r\n\r\n // First pass: export base meshes (non-instances).\r\n // This creates the \"resource objects\" referenced by build items.\r\n for (let j = 0; j < meshes.length; j++) {\r\n const babylonMesh = meshes[j];\r\n if (babylonMesh instanceof InstancedMesh) {\r\n // Defer instance handling to the second pass.\r\n instances?.push(babylonMesh);\r\n continue;\r\n }\r\n\r\n const objectName = babylonMesh.name || `mesh${j}`;\r\n\r\n // Convert Babylon world matrix to 3MF build transform (3x4).\r\n // This transform will be attached to the build item referencing the created object.\r\n const worldTransform = babylonMesh.getWorldMatrix();\r\n const buildTransform = this._handleBabylonTo3mfMatrixTransformToRef(worldTransform, Matrix3d.Zero());\r\n\r\n // Submeshes can carry material/color boundaries in Babylon.\r\n // When exportSubmeshes is enabled, we export each submesh as its own 3MF object so\r\n // consumers can attach per-object properties (e.g. colors) later.\r\n const subMeshes = babylonMesh.subMeshes;\r\n if (subMeshes === undefined) {\r\n // very unlikely...\r\n continue;\r\n }\r\n\r\n // Babylon.js automatically creates one SubMesh covering the whole mesh if you don’t define any.\r\n const isStandalone = subMeshes.length == 1;\r\n for (let k = 0; k < subMeshes.length; k++) {\r\n const subMesh = subMeshes[k];\r\n\r\n const data = this._extractSubMesh(babylonMesh, subMesh);\r\n\r\n if (data) {\r\n const submeshName = isStandalone ? `${objectName}` : `${objectName}_${k}`;\r\n\r\n const object = new ThreeMfMeshBuilder(idFactory.next()).withData(data).withName(submeshName).build();\r\n\r\n // Add object to resources.\r\n modelBuilder.withMesh(object);\r\n\r\n // Add a build item referencing the object at the mesh world transform.\r\n modelBuilder.withBuild(object.id, buildTransform);\r\n\r\n // Cache mapping for instances (instances will reference this object id).\r\n index.set(subMesh, object);\r\n }\r\n }\r\n }\r\n\r\n // Second pass: export instances as additional build items.\r\n //\r\n // In 3MF terms:\r\n // - We do not duplicate geometry for each instance.\r\n // - We emit multiple build items referencing the same object id, each with its own transform.\r\n if (instances && instances.length) {\r\n // Group instances by their source mesh to keep related builds close in the XML.\r\n const grouped = this._groupBy(instances, (i) => i.sourceMesh);\r\n\r\n for (const [_babylonMesh, _instances] of Array.from(grouped.entries())) {\r\n if (!_instances || !_instances.length) {\r\n continue;\r\n }\r\n\r\n for (let j = 0; j < _instances.length; j++) {\r\n const mesh = _instances[j];\r\n const worldTransform = mesh.getWorldMatrix();\r\n\r\n // If we exported submeshes, the base \"resource objects\" are per-submesh.\r\n // For an instance we emit a build item per submesh object.\r\n const subMeshes = _babylonMesh.subMeshes;\r\n\r\n for (let k = 0; k < subMeshes.length; k++) {\r\n const subMesh = subMeshes[k];\r\n\r\n // Look up the 3MF object created for this submesh in the first pass.\r\n const objectRef = index.get(subMesh);\r\n\r\n if (objectRef) {\r\n modelBuilder.withBuild(objectRef.id, this._handleBabylonTo3mfMatrixTransformToRef(worldTransform, Matrix3d.Zero()));\r\n continue;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return modelBuilder.build();\r\n }\r\n\r\n /**\r\n * Ensure the zip library (fflate) is available in the current runtime.\r\n *\r\n * Host assumptions:\r\n * - This implementation relies on fflate being exposed on globalThis.fflate.\r\n * - If it is not present, it loads a script from ThreeMfSerializerGlobalConfiguration.FFLATEUrl using Babylon Tools.LoadScriptAsync.\r\n * @returns\r\n */\r\n public override async ensureZipLibReadyAsync(): Promise<any> {\r\n if (this._fflateReadyPromise) {\r\n return await this._fflateReadyPromise;\r\n }\r\n\r\n this._fflateReadyPromise = (async () => {\r\n // globalThis is the global object in all modern JS runtimes (browser, workers, Node, etc.).\r\n const g = globalThis as any;\r\n\r\n // If fflate is not already present, load it dynamically.\r\n // This assumes the loaded script sets globalThis.fflate.\r\n if (!g.fflate) {\r\n await Tools.LoadScriptAsync(ThreeMfSerializerGlobalConfiguration.FFLATEUrl);\r\n }\r\n\r\n return g.fflate;\r\n })();\r\n\r\n return await this._fflateReadyPromise;\r\n }\r\n\r\n /**\r\n * Extract a single SubMesh into a standalone vertex/index buffer pair.\r\n *\r\n * Why:\r\n * - 3MF mesh objects typically reference a contiguous vertex array and triangle indices.\r\n * - Babylon SubMesh references a slice of the global index buffer, but shares the vertex buffer.\r\n * - To serialize each submesh independently, we build a compacted vertex buffer containing only the used vertices\r\n * and remap indices accordingly.\r\n *\r\n * Complexity:\r\n * - O(indexCount) time, O(uniqueVerticesInSubmesh) additional memory.\r\n * @param mesh\r\n * @param sm\r\n * @returns\r\n */\r\n private _extractSubMesh(mesh: Mesh, sm: SubMesh): I3mfVertexData | undefined {\r\n const allInd = mesh.getIndices();\r\n if (!allInd) {\r\n return undefined;\r\n }\r\n\r\n const allPos = mesh.getVerticesData(ThreeMfSerializer._PositionKind);\r\n if (!allPos) {\r\n return undefined;\r\n }\r\n\r\n // Fast path: the submesh covers the full index buffer, so reuse the original arrays.\r\n // Note: This returns Babylon-owned arrays; if callers mutate, they will affect source mesh data.\r\n // Kept as-is to avoid extra allocations.\r\n if (sm.indexStart == 0 && sm.indexCount == allInd.length) {\r\n return {\r\n positions: allPos,\r\n indices: allInd,\r\n };\r\n }\r\n\r\n const indStart = sm.indexStart;\r\n\r\n // Map old vertex index -> new compacted vertex index.\r\n const map = new Map<number, number>();\r\n\r\n // Compacted positions (x,y,z repeated). We assemble into number[] then convert to Float32Array at the end.\r\n const newPositions: number[] = [];\r\n\r\n // Indices for the compacted vertex buffer.\r\n // Uint32Array is used to support large meshes; ensure downstream 3MF writer supports 32-bit indices if needed.\r\n const newIndices = new Uint32Array(sm.indexCount);\r\n\r\n for (let i = 0; i < sm.indexCount; i++) {\r\n const oldVi = allInd[indStart + i];\r\n\r\n let newVi = map.get(oldVi);\r\n if (newVi === undefined) {\r\n newVi = map.size;\r\n map.set(oldVi, newVi);\r\n\r\n // Copy the corresponding position (assumes positions are 3-floats per vertex).\r\n // If the source mesh uses a different stride or includes morph targets, this ignores them.\r\n const p = oldVi * 3;\r\n newPositions.push(allPos[p], allPos[p + 1], allPos[p + 2]);\r\n }\r\n\r\n newIndices[i] = newVi;\r\n }\r\n\r\n return {\r\n positions: new Float32Array(newPositions),\r\n indices: newIndices,\r\n };\r\n }\r\n\r\n /**\r\n * Group items by a computed key.\r\n * Used to group instances by sourceMesh so the resulting XML is easier to read and debug.\r\n * @param items\r\n * @param key\r\n * @returns\r\n */\r\n private _groupBy<T, K>(items: readonly T[], key: (v: T) => K): Map<K, T[]> {\r\n const m = new Map<K, T[]>();\r\n for (const it of items) {\r\n const k = key(it);\r\n const arr = m.get(k);\r\n if (arr) {\r\n arr.push(it);\r\n } else {\r\n m.set(k, [it]);\r\n }\r\n }\r\n return m;\r\n }\r\n\r\n /**\r\n * Basis conversion from Babylon coordinate system to the expected 3MF coordinate system.\r\n *\r\n * Here we rotate +90 degrees around X:\r\n * - This is commonly used to convert between Y-up and Z-up conventions.\r\n * - Verify this matches your pipeline (Babylon is typically left-handed Y-up).\r\n */\r\n private static readonly _R_BJS_TO_3MF = Matrix.RotationX(Math.PI / 2).multiply(Matrix.Scaling(1, -1, 1));\r\n\r\n /**\r\n * Converts a Babylon.js 4x4 matrix into a 3MF 3x4 transform matrix and writes the result into ref.\r\n *\r\n * Babylon.js conventions:\r\n * - Babylon exposes matrices with logical row/column indexing (M(row, column)).\r\n * - It stores the 16 coefficients in a contiguous array in row-major order:\r\n * [ M00, M01, M02, M03,\r\n * M10, M11, M12, M13,\r\n * M20, M21, M22, M23,\r\n * M30, M31, M32, M33 ]\r\n *\r\n * 3MF expectation:\r\n * - 3MF uses an affine transform represented as a 3x4 matrix (12 values).\r\n * - The values are taken from the first 3 columns of the 4x4 matrix, across the 4 rows:\r\n * m00 m01 m02 m10 m11 m12 m20 m21 m22 m30 m31 m32\r\n *\r\n * Steps:\r\n * 1) Compose Babylon transform with the basis change:\r\n * tmp = tBjs * _R_BJS_TO_3MF\r\n * 2) Extract the 12 coefficients in 3MF order from tmp.m.\r\n *\r\n * Interop note:\r\n * - Do not transpose here. We only reorder values to match the 3MF 3x4 serialization order.\r\n * - Transposition is only relevant when interfacing with code that assumes column-major storage.\r\n *\r\n * @param tBjs Babylon.js 4x4 matrix.\r\n * @param ref Output 3MF 3x4 matrix container (ref.values assigned).\r\n * @returns ref, for chaining.\r\n */\r\n private _handleBabylonTo3mfMatrixTransformToRef(tBjs: Matrix, ref: Matrix3d): Matrix3d {\r\n const tmp = tBjs.multiplyToRef(ThreeMfSerializer._R_BJS_TO_3MF, Matrix.Zero());\r\n const a = tmp.m;\r\n\r\n // a is Babylon row-major storage. Extract rows 0..3, cols 0..2 in 3MF order.\r\n // 3MF order: m00 m01 m02 m10 m11 m12 m20 m21 m22 m30 m31 m32\r\n ref.values = [a[0], a[1], a[2], a[4], a[5], a[6], a[8], a[9], a[10], a[12], a[13], a[14]];\r\n return ref;\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onerjs/serializers",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.43.2",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"module": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"postcompile": "build-tools -c add-js-to-es6"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@onerjs/core": "8.
|
|
21
|
+
"@onerjs/core": "8.43.2",
|
|
22
22
|
"@dev/build-tools": "^1.0.0",
|
|
23
23
|
"@lts/serializers": "^1.0.0",
|
|
24
|
-
"babylonjs-gltf2interface": "^8.
|
|
24
|
+
"babylonjs-gltf2interface": "^8.43.2"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@onerjs/core": "^8.0.0",
|