diff --git a/source/engine/export/exporter3dm.js b/source/engine/export/exporter3dm.js index b1d1eee..d2f9eff 100644 --- a/source/engine/export/exporter3dm.js +++ b/source/engine/export/exporter3dm.js @@ -49,7 +49,7 @@ export class Exporter3dm extends ExporterBase files.push (rhinoFile); let rhinoDoc = new this.rhino.File3dm (); - exporterModel.EnumerateTransformedMeshes ((mesh) => { + exporterModel.EnumerateTransformedMeshInstances ((mesh) => { let meshBuffer = ConvertMeshToMeshBuffer (mesh); for (let primitiveIndex = 0; primitiveIndex < meshBuffer.PrimitiveCount (); primitiveIndex++) { let primitive = meshBuffer.GetPrimitive (primitiveIndex); diff --git a/source/engine/export/exporterbim.js b/source/engine/export/exporterbim.js index 4e76a80..7d7cabb 100644 --- a/source/engine/export/exporterbim.js +++ b/source/engine/export/exporterbim.js @@ -38,7 +38,7 @@ export class ExporterBim extends ExporterBase this.ExportProperties (exporterModel.GetModel (), bimContent.info); let meshId = 0; - exporterModel.EnumerateTransformedMeshes ((mesh) => { + exporterModel.EnumerateTransformedMeshInstances ((mesh) => { let bimMesh = { mesh_id : meshId, coordinates : [], diff --git a/source/engine/export/exportergltf.js b/source/engine/export/exportergltf.js index bbe8f43..1d60a18 100644 --- a/source/engine/export/exportergltf.js +++ b/source/engine/export/exportergltf.js @@ -184,7 +184,7 @@ export class ExporterGltf extends ExporterBase { let meshDataArr = []; - exporterModel.EnumerateTransformedMeshes ((mesh) => { + exporterModel.EnumerateTransformedMeshInstances ((mesh) => { let buffer = ConvertMeshToMeshBuffer (mesh); meshDataArr.push ({ name : mesh.GetName (), diff --git a/source/engine/export/exportermodel.js b/source/engine/export/exportermodel.js index e6e70d9..e3359ce 100644 --- a/source/engine/export/exportermodel.js +++ b/source/engine/export/exportermodel.js @@ -21,6 +21,8 @@ export class ExporterModel { this.model = model; this.settings = settings || new ExporterSettings (); + this.visibleMeshes = null; + this.meshToVisibleMeshIndex = null; } GetModel () @@ -56,6 +58,32 @@ export class ExporterModel return triangleCount; } + MeshCount () + { + let meshCount = 0; + this.EnumerateMeshes ((mesh) => { + meshCount += 1; + }); + return meshCount; + } + + EnumerateMeshes (onMesh) + { + this.FillVisibleMeshCache (); + for (let meshIndex = 0; meshIndex < this.model.MeshCount (); meshIndex++) { + if (this.visibleMeshes.has (meshIndex)) { + let mesh = this.model.GetMesh (meshIndex); + onMesh (mesh); + } + } + } + + MapMeshIndex (meshIndex) + { + this.FillVisibleMeshCache (); + return this.meshToVisibleMeshIndex.get (meshIndex); + } + MeshInstanceCount () { let meshInstanceCount = 0; @@ -74,7 +102,7 @@ export class ExporterModel }); } - EnumerateTransformedMeshes (onMesh) + EnumerateTransformedMeshInstances (onMesh) { this.EnumerateMeshInstances ((meshInstance) => { let transformation = meshInstance.GetTransformation (); @@ -95,7 +123,7 @@ export class ExporterModel EnumerateVerticesAndTriangles (callbacks) { let transformedMeshes = []; - this.EnumerateTransformedMeshes ((mesh) => { + this.EnumerateTransformedMeshInstances ((mesh) => { transformedMeshes.push (mesh); }); @@ -116,11 +144,35 @@ export class ExporterModel EnumerateTrianglesWithNormals (onTriangle) { - this.EnumerateTransformedMeshes ((mesh) => { + this.EnumerateTransformedMeshInstances ((mesh) => { mesh.EnumerateTriangleVertices ((v0, v1, v2) => { let normal = CalculateTriangleNormal (v0, v1, v2); onTriangle (v0, v1, v2, normal); }); }); } + + FillVisibleMeshCache () + { + if (this.visibleMeshes !== null && this.meshToVisibleMeshIndex !== null) { + return; + } + + this.visibleMeshes = new Set (); + this.model.EnumerateMeshInstances ((meshInstance) => { + let meshInstanceId = meshInstance.GetId (); + if (this.settings.isMeshVisible (meshInstanceId)) { + this.visibleMeshes.add (meshInstanceId.meshIndex); + } + }); + + this.meshToVisibleMeshIndex = new Map (); + let visibleMeshIndex = 0; + for (let meshIndex = 0; meshIndex < this.model.MeshCount (); meshIndex++) { + if (this.visibleMeshes.has (meshIndex)) { + this.meshToVisibleMeshIndex.set (meshIndex, visibleMeshIndex); + visibleMeshIndex += 1; + } + } + } } diff --git a/source/engine/export/exporterobj.js b/source/engine/export/exporterobj.js index 04d6688..ea23f3d 100644 --- a/source/engine/export/exporterobj.js +++ b/source/engine/export/exporterobj.js @@ -68,7 +68,7 @@ export class ExporterObj extends ExporterBase let normalOffset = 0; let uvOffset = 0; let usedMaterialName = null; - exporterModel.EnumerateTransformedMeshes ((mesh) => { + exporterModel.EnumerateTransformedMeshInstances ((mesh) => { objWriter.WriteArrayLine (['g', this.GetExportedMeshName (mesh.GetName ())]); for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) { let vertex = mesh.GetVertex (vertexIndex); diff --git a/source/engine/model/model.js b/source/engine/model/model.js index 7a49855..25a4ed0 100644 --- a/source/engine/model/model.js +++ b/source/engine/model/model.js @@ -164,7 +164,7 @@ export class Model extends ModelObject3D }); } - EnumerateTransformedMeshes (onMesh) + EnumerateTransformedMeshInstances (onMesh) { this.EnumerateMeshInstances ((meshInstance) => { const transformed = meshInstance.GetTransformedMesh (); diff --git a/test/tests/exporter_test.js b/test/tests/exporter_test.js index 727a744..f7e952a 100644 --- a/test/tests/exporter_test.js +++ b/test/tests/exporter_test.js @@ -1,5 +1,6 @@ import * as assert from 'assert'; import * as OV from '../../source/engine/main.js'; +import { CreateHierarchicalTestModelForExport } from '../utils/testutils.js'; export default function suite () { @@ -63,10 +64,9 @@ function CreateTestModel () return model; } -function Export (model, format, extension, onReady) +function Export (model, settings, format, extension, onReady) { let exporter = new OV.Exporter (); - let settings = new OV.ExporterSettings (); exporter.Export (model, settings, format, extension, { onSuccess : function (files) { onReady (files); @@ -88,7 +88,8 @@ describe ('Exporter', function () { it ('Obj Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Text, 'obj', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Text, 'obj', function (result) { assert.strictEqual (result.length, 5); let mtlFile = result[0]; @@ -160,7 +161,8 @@ describe ('Exporter', function () { it ('Stl Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Text, 'stl', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Text, 'stl', function (result) { assert.strictEqual (result.length, 1); let stlFile = result[0]; @@ -206,7 +208,8 @@ describe ('Exporter', function () { it ('Stl Binary Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Binary, 'stl', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Binary, 'stl', function (result) { assert.strictEqual (result.length, 1); let stlFile = result[0]; @@ -233,7 +236,8 @@ describe ('Exporter', function () { it ('Off Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Text, 'off', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Text, 'off', function (result) { assert.strictEqual (result.length, 1); let offFile = result[0]; @@ -263,7 +267,8 @@ describe ('Exporter', function () { it ('Ply Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Text, 'ply', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Text, 'ply', function (result) { assert.strictEqual (result.length, 1); let plyFile = result[0]; @@ -300,7 +305,8 @@ describe ('Exporter', function () { it ('Ply Binary Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Binary, 'ply', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Binary, 'ply', function (result) { assert.strictEqual (result.length, 1); let plyFile = result[0]; @@ -325,7 +331,8 @@ describe ('Exporter', function () { it ('Gltf Ascii Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Text, 'gltf', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Text, 'gltf', function (result) { assert.strictEqual (result.length, 3); let gltfFile = result[0]; @@ -366,7 +373,8 @@ describe ('Exporter', function () { it ('Gltf Binary Export', function (done) { let model = CreateTestModel (); - Export (model, OV.FileFormat.Binary, 'glb', function (result) { + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Binary, 'glb', function (result) { assert.strictEqual (result.length, 1); let glbFile = result[0]; @@ -395,6 +403,36 @@ describe ('Exporter', function () { }); }); }); + + it ('Gltf Hierarchical Export No Filter', function (done) { + let model = CreateHierarchicalTestModelForExport (); + let settings = new OV.ExporterSettings (); + Export (model, settings, OV.FileFormat.Binary, 'glb', function (result) { + assert.strictEqual (result.length, 1); + + let glbFile = result[0]; + assert.strictEqual (glbFile.GetName (), 'model.glb'); + + let contentBuffer = glbFile.GetBufferContent (); + let importer = new OV.ImporterGltf (); + importer.Import (glbFile.GetName (), 'glb', contentBuffer, { + getDefaultMaterialColor () { + return new OV.RGBColor (0, 0, 0); + }, + getFileBuffer (filePath) { + return null; + }, + onSuccess () { + let importedModel = importer.GetModel (); + assert.ok (OV.CheckModel (importedModel)); + assert.strictEqual (importedModel.MaterialCount (), 3); + assert.strictEqual (importedModel.MeshCount (), 4); + assert.strictEqual (importedModel.MeshInstanceCount (), 4); + done (); + } + }); + }); + }); }); } diff --git a/test/tests/exportermodel_test.js b/test/tests/exportermodel_test.js index 092755f..00ab55a 100644 --- a/test/tests/exportermodel_test.js +++ b/test/tests/exportermodel_test.js @@ -1,85 +1,14 @@ import * as assert from 'assert'; import * as OV from '../../source/engine/main.js'; +import { CreateTestModelForExport, CreateHierarchicalTestModelForExport } from '../utils/testutils.js'; export default function suite () { -function CreateTestModel () -{ - let model = new OV.Model (); - - for (let i = 0; i < 3; i++) { - let material = new OV.PhongMaterial (); - material.name = 'Material ' + i.toString (); - model.AddMaterial (material); - } - - let root = model.GetRootNode (); - for (let i = 0; i < 3; i++) { - let genParams = new OV.GeneratorParams ().SetMaterial (i); - let cube = OV.GenerateCuboid (genParams, 1.0, 1.0, 1.0); - let meshIndex = model.AddMesh (cube); - let node = new OV.Node (); - node.AddMeshIndex (meshIndex); - node.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (i, 0.0, 0.0))); - root.AddChildNode (node); - } - - OV.FinalizeModel (model); - return model; -} - -function CreateHierarchicalTestModel () -{ - let model = new OV.Model (); - - let material1 = new OV.PhongMaterial (); - material1.name = 'Material 1'; - model.AddMaterial (material1); - - let material2 = new OV.PhongMaterial (); - material2.name = 'Material 1'; - model.AddMaterial (material2); - - let cubeParams1 = new OV.GeneratorParams ().SetMaterial (0); - let cube1 = OV.GenerateCuboid (cubeParams1, 1.0, 1.0, 1.0); - let cube1Mesh = model.AddMesh (cube1); - - let cubeParams2 = new OV.GeneratorParams ().SetMaterial (1); - let cube2 = OV.GenerateCuboid (cubeParams2, 1.0, 1.0, 1.0); - let cube2Mesh = model.AddMesh (cube2); - - let root = model.GetRootNode (); - let node1 = new OV.Node (); - node1.SetName ('Node 1'); - - let node11 = new OV.Node (); - node11.SetName ('Node 1.1'); - node11.AddMeshIndex (cube1Mesh); - node1.AddChildNode (node11); - - let node12 = new OV.Node (); - node12.SetName ('Node 1.2'); - node12.AddMeshIndex (cube2Mesh); - node12.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (2.0, 0.0, 0.0))); - node1.AddChildNode (node12); - - let node13 = new OV.Node (); - node13.SetName ('Node 1.2'); - node13.AddMeshIndex (cube2Mesh); - node13.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (4.0, 0.0, 0.0))); - node1.AddChildNode (node13); - - root.AddChildNode (node1); - - OV.FinalizeModel (model); - return model; -} - function GetExporterModelBoundingBox (exporterModel) { let calculator = new OV.BoundingBoxCalculator3D (); - exporterModel.EnumerateTransformedMeshes ((mesh) => { + exporterModel.EnumerateTransformedMeshInstances ((mesh) => { mesh.EnumerateVertices ((vertex) => { calculator.AddPoint (vertex); }); @@ -90,7 +19,7 @@ function GetExporterModelBoundingBox (exporterModel) describe ('Exporter Model', function () { it ('No filter test', function () { - let model = CreateTestModel (); + let model = CreateTestModelForExport (); let exporterModel = new OV.ExporterModel (model); assert.strictEqual (exporterModel.MeshInstanceCount (), 3); let boundingBox = GetExporterModelBoundingBox (exporterModel); @@ -99,7 +28,7 @@ describe ('Exporter Model', function () { }); it ('Model filter test', function () { - let model = CreateTestModel (); + let model = CreateTestModelForExport (); let settings = new OV.ExporterSettings ({ isMeshVisible : (meshInstanceId) => { return !meshInstanceId.IsEqual (new OV.MeshInstanceId (3, 2)); @@ -115,7 +44,7 @@ describe ('Exporter Model', function () { it ('Model transformation test', function () { let rotation = OV.QuaternionFromAxisAngle (new OV.Coord3D (0.0, 1.0, 0.0), -Math.PI / 2.0); - let model = CreateTestModel (); + let model = CreateTestModelForExport (); let settings = new OV.ExporterSettings ({ transformation : new OV.Transformation (new OV.Matrix ().CreateRotation (rotation.x, rotation.y, rotation.z, rotation.w)) }); @@ -129,7 +58,7 @@ describe ('Exporter Model', function () { it ('Model filter and transformation test', function () { let rotation = OV.QuaternionFromAxisAngle (new OV.Coord3D (0.0, 1.0, 0.0), -Math.PI / 2.0); - let model = CreateTestModel (); + let model = CreateTestModelForExport (); let settings = new OV.ExporterSettings ({ transformation : new OV.Transformation (new OV.Matrix ().CreateRotation (rotation.x, rotation.y, rotation.z, rotation.w)), isMeshVisible : (meshInstanceId) => { @@ -144,45 +73,66 @@ describe ('Exporter Model', function () { }); it ('Hierarchical model test no filter', function () { - let model = CreateHierarchicalTestModel (); + let model = CreateHierarchicalTestModelForExport (); let settings = new OV.ExporterSettings ({ isMeshVisible : (meshInstanceId) => { return true; } }); let exporterModel = new OV.ExporterModel (model, settings); - assert.strictEqual (exporterModel.MeshInstanceCount (), 3); + assert.strictEqual (exporterModel.MeshCount (), 3); + assert.strictEqual (exporterModel.MeshInstanceCount (), 4); let boundingBox = GetExporterModelBoundingBox (exporterModel); assert.ok (OV.CoordIsEqual3D (boundingBox.min, new OV.Coord3D (0.0, 0.0, 0.0))); - assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (5.0, 1.0, 1.0))); + assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (7.0, 1.0, 1.0))); }); - it ('Hierarchical model test filter', function () { - let model = CreateHierarchicalTestModel (); + it ('Hierarchical model test filter 1', function () { + let model = CreateHierarchicalTestModelForExport (); let settings = new OV.ExporterSettings ({ isMeshVisible : (meshInstanceId) => { return !meshInstanceId.IsEqual (new OV.MeshInstanceId (3, 1)); } }); let exporterModel = new OV.ExporterModel (model, settings); - assert.strictEqual (exporterModel.MeshInstanceCount (), 2); + assert.strictEqual (exporterModel.MeshCount (), 3); + assert.strictEqual (exporterModel.MeshInstanceCount (), 3); let boundingBox = GetExporterModelBoundingBox (exporterModel); assert.ok (OV.CoordIsEqual3D (boundingBox.min, new OV.Coord3D (0.0, 0.0, 0.0))); - assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (3.0, 1.0, 1.0))); + assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (7.0, 1.0, 1.0))); }); it ('Hierarchical model test filter 2', function () { - let model = CreateHierarchicalTestModel (); + let model = CreateHierarchicalTestModelForExport (); let settings = new OV.ExporterSettings ({ isMeshVisible : (meshInstanceId) => { - return !meshInstanceId.IsEqual (new OV.MeshInstanceId (3, 1)) && !meshInstanceId.IsEqual (new OV.MeshInstanceId (2, 1)); + return !meshInstanceId.IsEqual (new OV.MeshInstanceId (2, 1)); } }); let exporterModel = new OV.ExporterModel (model, settings); - assert.strictEqual (exporterModel.MeshInstanceCount (), 1); + assert.strictEqual (exporterModel.MeshCount (), 3); + assert.strictEqual (exporterModel.MeshInstanceCount (), 3); let boundingBox = GetExporterModelBoundingBox (exporterModel); assert.ok (OV.CoordIsEqual3D (boundingBox.min, new OV.Coord3D (0.0, 0.0, 0.0))); - assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (1.0, 1.0, 1.0))); + assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (7.0, 1.0, 1.0))); + }); + + it ('Hierarchical model test filter 3', function () { + let model = CreateHierarchicalTestModelForExport (); + let settings = new OV.ExporterSettings ({ + isMeshVisible : (meshInstanceId) => { + return !meshInstanceId.IsEqual (new OV.MeshInstanceId (2, 1)) && !meshInstanceId.IsEqual (new OV.MeshInstanceId (3, 1)); + } + }); + let exporterModel = new OV.ExporterModel (model, settings); + assert.strictEqual (exporterModel.MeshCount (), 2); + assert.strictEqual (exporterModel.MeshInstanceCount (), 2); + assert.strictEqual (exporterModel.MapMeshIndex (0), 0); + assert.strictEqual (exporterModel.MapMeshIndex (1), undefined); + assert.strictEqual (exporterModel.MapMeshIndex (2), 1); + let boundingBox = GetExporterModelBoundingBox (exporterModel); + assert.ok (OV.CoordIsEqual3D (boundingBox.min, new OV.Coord3D (0.0, 0.0, 0.0))); + assert.ok (OV.CoordIsEqual3D (boundingBox.max, new OV.Coord3D (7.0, 1.0, 1.0))); }); }); diff --git a/test/tests/model_test.js b/test/tests/model_test.js index 828897c..21fa088 100644 --- a/test/tests/model_test.js +++ b/test/tests/model_test.js @@ -390,7 +390,7 @@ describe ('Node Hierarchy', function () { }); let meshes = []; - model.EnumerateTransformedMeshes ((mesh) => { + model.EnumerateTransformedMeshInstances ((mesh) => { meshes.push (mesh); }); diff --git a/test/utils/testutils.js b/test/utils/testutils.js index a465aad..eb62415 100644 --- a/test/utils/testutils.js +++ b/test/utils/testutils.js @@ -138,7 +138,7 @@ export function ModelToObjectSimple (model) }); } - model.EnumerateTransformedMeshes ((mesh) => { + model.EnumerateTransformedMeshInstances ((mesh) => { let boundingBox = OV.GetBoundingBox (mesh); let meshObj = { name : mesh.GetName (), @@ -378,3 +378,98 @@ export function GetTranslatedRotatedCubesModel () OV.FinalizeModel (model); return model; } + +export function CreateTestModelForExport () +{ + let model = new OV.Model (); + + for (let i = 0; i < 3; i++) { + let material = new OV.PhongMaterial (); + material.name = 'Material ' + i.toString (); + model.AddMaterial (material); + } + + let root = model.GetRootNode (); + for (let i = 0; i < 3; i++) { + let genParams = new OV.GeneratorParams ().SetMaterial (i); + let cube = OV.GenerateCuboid (genParams, 1.0, 1.0, 1.0); + let meshIndex = model.AddMesh (cube); + let node = new OV.Node (); + node.AddMeshIndex (meshIndex); + node.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (i, 0.0, 0.0))); + root.AddChildNode (node); + } + + OV.FinalizeModel (model); + return model; +} + +export function CreateHierarchicalTestModelForExport () +{ + // Node 1 (0) + // - Node 1.1 (1) -> Mesh 1 (0) + // - Node 1.2 (2) -> Mesh 2 (1) + // - Node 1.3 (3) -> Mesh 2 (1) + // - Node 1.4 (4) -> Mesh 3 (2) + + let model = new OV.Model (); + + let material1 = new OV.PhongMaterial (); + material1.name = 'Material 1'; + material1.color = new OV.RGBColor (255, 0, 0); + model.AddMaterial (material1); + + let material2 = new OV.PhongMaterial (); + material2.name = 'Material 2'; + material1.color = new OV.RGBColor (0, 255, 0); + model.AddMaterial (material2); + + let material3 = new OV.PhongMaterial (); + material3.name = 'Material 3'; + material3.color = new OV.RGBColor (0, 0, 255); + model.AddMaterial (material3); + + let cubeParams1 = new OV.GeneratorParams ().SetMaterial (0); + let cube1 = OV.GenerateCuboid (cubeParams1, 1.0, 1.0, 1.0); + let cube1Mesh = model.AddMesh (cube1); + + let cubeParams2 = new OV.GeneratorParams ().SetMaterial (1); + let cube2 = OV.GenerateCuboid (cubeParams2, 1.0, 1.0, 1.0); + let cube2Mesh = model.AddMesh (cube2); + + let cubeParams3 = new OV.GeneratorParams ().SetMaterial (2); + let cube3 = OV.GenerateCuboid (cubeParams3, 1.0, 1.0, 1.0); + let cube3Mesh = model.AddMesh (cube3); + + let root = model.GetRootNode (); + let node1 = new OV.Node (); + node1.SetName ('Node 1'); + + let node11 = new OV.Node (); + node11.SetName ('Node 1.1'); + node11.AddMeshIndex (cube1Mesh); + node1.AddChildNode (node11); + + let node12 = new OV.Node (); + node12.SetName ('Node 1.2'); + node12.AddMeshIndex (cube2Mesh); + node12.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (2.0, 0.0, 0.0))); + node1.AddChildNode (node12); + + let node13 = new OV.Node (); + node13.SetName ('Node 1.3'); + node13.AddMeshIndex (cube2Mesh); + node13.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (4.0, 0.0, 0.0))); + node1.AddChildNode (node13); + + let node14 = new OV.Node (); + node14.SetName ('Node 1.4'); + node14.AddMeshIndex (cube3Mesh); + node14.SetTransformation (new OV.Transformation (new OV.Matrix ().CreateTranslation (6.0, 0.0, 0.0))); + node1.AddChildNode (node14); + + root.AddChildNode (node1); + + OV.FinalizeModel (model); + return model; +}