소스 검색

Add support for serialization to TransformNode

David Catuhe 7 년 전
부모
커밋
93ffe1a12f

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 624 - 616
dist/preview release/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 38 - 38
dist/preview release/babylon.js


+ 121 - 21
dist/preview release/babylon.max.js

@@ -8512,7 +8512,7 @@ var BABYLON;
         });
         });
         Object.defineProperty(Engine, "Version", {
         Object.defineProperty(Engine, "Version", {
             get: function () {
             get: function () {
-                return "3.1-beta-2";
+                return "3.1-beta-3";
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -13679,6 +13679,105 @@ var BABYLON;
         };
         };
         TransformNode.prototype._afterComputeWorldMatrix = function () {
         TransformNode.prototype._afterComputeWorldMatrix = function () {
         };
         };
+        /**
+        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.
+        * @param func: callback function to add
+        *
+        * Returns the TransformNode.
+        */
+        TransformNode.prototype.registerAfterWorldMatrixUpdate = function (func) {
+            this.onAfterWorldMatrixUpdateObservable.add(func);
+            return this;
+        };
+        /**
+         * Removes a registered callback function.
+         * Returns the TransformNode.
+         */
+        TransformNode.prototype.unregisterAfterWorldMatrixUpdate = function (func) {
+            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
+            return this;
+        };
+        TransformNode.prototype.clone = function (name, newParent) {
+            var _this = this;
+            var result = BABYLON.SerializationHelper.Clone(function () { return new TransformNode(name, _this.getScene()); }, this);
+            result.name = name;
+            result.id = name;
+            if (newParent) {
+                result.parent = newParent;
+            }
+            return result;
+        };
+        TransformNode.prototype.serialize = function (serializationObject) {
+            if (serializationObject === void 0) { serializationObject = null; }
+            if (!serializationObject) {
+                serializationObject = {};
+            }
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.type = this.getClassName();
+            if (BABYLON.Tags && BABYLON.Tags.HasTags(this)) {
+                serializationObject.tags = BABYLON.Tags.GetTags(this);
+            }
+            serializationObject.position = this.position.asArray();
+            if (this.rotationQuaternion) {
+                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
+            }
+            else if (this.rotation) {
+                serializationObject.rotation = this.rotation.asArray();
+            }
+            serializationObject.scaling = this.scaling.asArray();
+            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.infiniteDistance = this.infiniteDistance;
+            serializationObject.billboardMode = this.billboardMode;
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+            // Metadata
+            if (this.metadata) {
+                serializationObject.metadata = this.metadata;
+            }
+            return serializationObject;
+        };
+        // Statics
+        /**
+         * Returns a new TransformNode object parsed from the source provided.
+         * The parameter `parsedMesh` is the source.
+         * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
+         */
+        TransformNode.Parse = function (parsedTransformNode, scene, rootUrl) {
+            var transformNode = new TransformNode(parsedTransformNode.name, scene);
+            transformNode.id = parsedTransformNode.id;
+            if (BABYLON.Tags) {
+                BABYLON.Tags.AddTagsTo(transformNode, parsedTransformNode.tags);
+            }
+            transformNode.position = BABYLON.Vector3.FromArray(parsedTransformNode.position);
+            if (parsedTransformNode.metadata !== undefined) {
+                transformNode.metadata = parsedTransformNode.metadata;
+            }
+            if (parsedTransformNode.rotationQuaternion) {
+                transformNode.rotationQuaternion = BABYLON.Quaternion.FromArray(parsedTransformNode.rotationQuaternion);
+            }
+            else if (parsedTransformNode.rotation) {
+                transformNode.rotation = BABYLON.Vector3.FromArray(parsedTransformNode.rotation);
+            }
+            transformNode.scaling = BABYLON.Vector3.FromArray(parsedTransformNode.scaling);
+            if (parsedTransformNode.localMatrix) {
+                transformNode.setPivotMatrix(BABYLON.Matrix.FromArray(parsedTransformNode.localMatrix));
+            }
+            else if (parsedTransformNode.pivotMatrix) {
+                transformNode.setPivotMatrix(BABYLON.Matrix.FromArray(parsedTransformNode.pivotMatrix));
+            }
+            transformNode.setEnabled(parsedTransformNode.isEnabled);
+            transformNode.infiniteDistance = parsedTransformNode.infiniteDistance;
+            transformNode.billboardMode = parsedTransformNode.billboardMode;
+            // Parent
+            if (parsedTransformNode.parentId) {
+                transformNode._waitingParentId = parsedTransformNode.parentId;
+            }
+            return transformNode;
+        };
         // Statics
         // Statics
         TransformNode.BILLBOARDMODE_NONE = 0;
         TransformNode.BILLBOARDMODE_NONE = 0;
         TransformNode.BILLBOARDMODE_X = 1;
         TransformNode.BILLBOARDMODE_X = 1;
@@ -14616,24 +14715,6 @@ var BABYLON;
             this._updateBoundingInfo();
             this._updateBoundingInfo();
         };
         };
         /**
         /**
-        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.
-        * @param func: callback function to add
-        *
-        * Returns the AbstractMesh.
-        */
-        AbstractMesh.prototype.registerAfterWorldMatrixUpdate = function (func) {
-            this.onAfterWorldMatrixUpdateObservable.add(func);
-            return this;
-        };
-        /**
-         * Removes a registered callback function.
-         * Returns the AbstractMesh.
-         */
-        AbstractMesh.prototype.unregisterAfterWorldMatrixUpdate = function (func) {
-            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
-            return this;
-        };
-        /**
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.
          * A mesh is in the frustum if its bounding box intersects the frustum.
          * A mesh is in the frustum if its bounding box intersects the frustum.
          * Boolean returned.
          * Boolean returned.
@@ -24722,8 +24803,8 @@ var BABYLON;
         };
         };
         // Statics
         // Statics
         /**
         /**
-         * Returns a new Mesh object what is a deep copy of the passed mesh.
-         * The parameter `parsedMesh` is the mesh to be copied.
+         * Returns a new Mesh object parsed from the source provided.
+         * The parameter `parsedMesh` is the source.
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
          */
         Mesh.Parse = function (parsedMesh, scene, rootUrl) {
         Mesh.Parse = function (parsedMesh, scene, rootUrl) {
@@ -53546,6 +53627,13 @@ var BABYLON;
                             }
                             }
                         }
                         }
                     }
                     }
+                    // Transform nodes
+                    if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
+                        for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
+                            var parsedTransformNode = parsedData.transformNodes[index];
+                            BABYLON.TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                        }
+                    }
                     // Meshes
                     // Meshes
                     if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                     if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                         for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
                         for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
@@ -53606,6 +53694,13 @@ var BABYLON;
                     }
                     }
                     loadedSounds = [];
                     loadedSounds = [];
                     // Connect parents & children and parse actions
                     // Connect parents & children and parse actions
+                    for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
+                        var transformNode = scene.transformNodes[index];
+                        if (transformNode._waitingParentId) {
+                            transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
+                            transformNode._waitingParentId = null;
+                        }
+                    }
                     for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                         var mesh = scene.meshes[index];
                         var mesh = scene.meshes[index];
                         if (mesh._waitingParentId) {
                         if (mesh._waitingParentId) {
@@ -74377,6 +74472,11 @@ var BABYLON;
             for (index = 0; index < scene.skeletons.length; index++) {
             for (index = 0; index < scene.skeletons.length; index++) {
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
             }
             }
+            // Transform nodes
+            serializationObject.transformNodes = [];
+            for (index = 0; index < scene.transformNodes.length; index++) {
+                serializationObject.transformNodes.push(scene.transformNodes[index].serialize());
+            }
             // Geometries
             // Geometries
             serializationObject.geometries = {};
             serializationObject.geometries = {};
             serializationObject.geometries.boxes = [];
             serializationObject.geometries.boxes = [];

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 624 - 616
dist/preview release/babylon.module.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 38 - 38
dist/preview release/babylon.worker.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 5760 - 5752
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 38 - 38
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 121 - 21
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -8512,7 +8512,7 @@ var BABYLON;
         });
         });
         Object.defineProperty(Engine, "Version", {
         Object.defineProperty(Engine, "Version", {
             get: function () {
             get: function () {
-                return "3.1-beta-2";
+                return "3.1-beta-3";
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -13679,6 +13679,105 @@ var BABYLON;
         };
         };
         TransformNode.prototype._afterComputeWorldMatrix = function () {
         TransformNode.prototype._afterComputeWorldMatrix = function () {
         };
         };
+        /**
+        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.
+        * @param func: callback function to add
+        *
+        * Returns the TransformNode.
+        */
+        TransformNode.prototype.registerAfterWorldMatrixUpdate = function (func) {
+            this.onAfterWorldMatrixUpdateObservable.add(func);
+            return this;
+        };
+        /**
+         * Removes a registered callback function.
+         * Returns the TransformNode.
+         */
+        TransformNode.prototype.unregisterAfterWorldMatrixUpdate = function (func) {
+            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
+            return this;
+        };
+        TransformNode.prototype.clone = function (name, newParent) {
+            var _this = this;
+            var result = BABYLON.SerializationHelper.Clone(function () { return new TransformNode(name, _this.getScene()); }, this);
+            result.name = name;
+            result.id = name;
+            if (newParent) {
+                result.parent = newParent;
+            }
+            return result;
+        };
+        TransformNode.prototype.serialize = function (serializationObject) {
+            if (serializationObject === void 0) { serializationObject = null; }
+            if (!serializationObject) {
+                serializationObject = {};
+            }
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.type = this.getClassName();
+            if (BABYLON.Tags && BABYLON.Tags.HasTags(this)) {
+                serializationObject.tags = BABYLON.Tags.GetTags(this);
+            }
+            serializationObject.position = this.position.asArray();
+            if (this.rotationQuaternion) {
+                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
+            }
+            else if (this.rotation) {
+                serializationObject.rotation = this.rotation.asArray();
+            }
+            serializationObject.scaling = this.scaling.asArray();
+            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.infiniteDistance = this.infiniteDistance;
+            serializationObject.billboardMode = this.billboardMode;
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+            // Metadata
+            if (this.metadata) {
+                serializationObject.metadata = this.metadata;
+            }
+            return serializationObject;
+        };
+        // Statics
+        /**
+         * Returns a new TransformNode object parsed from the source provided.
+         * The parameter `parsedMesh` is the source.
+         * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
+         */
+        TransformNode.Parse = function (parsedTransformNode, scene, rootUrl) {
+            var transformNode = new TransformNode(parsedTransformNode.name, scene);
+            transformNode.id = parsedTransformNode.id;
+            if (BABYLON.Tags) {
+                BABYLON.Tags.AddTagsTo(transformNode, parsedTransformNode.tags);
+            }
+            transformNode.position = BABYLON.Vector3.FromArray(parsedTransformNode.position);
+            if (parsedTransformNode.metadata !== undefined) {
+                transformNode.metadata = parsedTransformNode.metadata;
+            }
+            if (parsedTransformNode.rotationQuaternion) {
+                transformNode.rotationQuaternion = BABYLON.Quaternion.FromArray(parsedTransformNode.rotationQuaternion);
+            }
+            else if (parsedTransformNode.rotation) {
+                transformNode.rotation = BABYLON.Vector3.FromArray(parsedTransformNode.rotation);
+            }
+            transformNode.scaling = BABYLON.Vector3.FromArray(parsedTransformNode.scaling);
+            if (parsedTransformNode.localMatrix) {
+                transformNode.setPivotMatrix(BABYLON.Matrix.FromArray(parsedTransformNode.localMatrix));
+            }
+            else if (parsedTransformNode.pivotMatrix) {
+                transformNode.setPivotMatrix(BABYLON.Matrix.FromArray(parsedTransformNode.pivotMatrix));
+            }
+            transformNode.setEnabled(parsedTransformNode.isEnabled);
+            transformNode.infiniteDistance = parsedTransformNode.infiniteDistance;
+            transformNode.billboardMode = parsedTransformNode.billboardMode;
+            // Parent
+            if (parsedTransformNode.parentId) {
+                transformNode._waitingParentId = parsedTransformNode.parentId;
+            }
+            return transformNode;
+        };
         // Statics
         // Statics
         TransformNode.BILLBOARDMODE_NONE = 0;
         TransformNode.BILLBOARDMODE_NONE = 0;
         TransformNode.BILLBOARDMODE_X = 1;
         TransformNode.BILLBOARDMODE_X = 1;
@@ -14616,24 +14715,6 @@ var BABYLON;
             this._updateBoundingInfo();
             this._updateBoundingInfo();
         };
         };
         /**
         /**
-        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.
-        * @param func: callback function to add
-        *
-        * Returns the AbstractMesh.
-        */
-        AbstractMesh.prototype.registerAfterWorldMatrixUpdate = function (func) {
-            this.onAfterWorldMatrixUpdateObservable.add(func);
-            return this;
-        };
-        /**
-         * Removes a registered callback function.
-         * Returns the AbstractMesh.
-         */
-        AbstractMesh.prototype.unregisterAfterWorldMatrixUpdate = function (func) {
-            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
-            return this;
-        };
-        /**
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.
          * A mesh is in the frustum if its bounding box intersects the frustum.
          * A mesh is in the frustum if its bounding box intersects the frustum.
          * Boolean returned.
          * Boolean returned.
@@ -24722,8 +24803,8 @@ var BABYLON;
         };
         };
         // Statics
         // Statics
         /**
         /**
-         * Returns a new Mesh object what is a deep copy of the passed mesh.
-         * The parameter `parsedMesh` is the mesh to be copied.
+         * Returns a new Mesh object parsed from the source provided.
+         * The parameter `parsedMesh` is the source.
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
          */
         Mesh.Parse = function (parsedMesh, scene, rootUrl) {
         Mesh.Parse = function (parsedMesh, scene, rootUrl) {
@@ -53392,6 +53473,13 @@ var BABYLON;
                             }
                             }
                         }
                         }
                     }
                     }
+                    // Transform nodes
+                    if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
+                        for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
+                            var parsedTransformNode = parsedData.transformNodes[index];
+                            BABYLON.TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                        }
+                    }
                     // Meshes
                     // Meshes
                     if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                     if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                         for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
                         for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
@@ -53452,6 +53540,13 @@ var BABYLON;
                     }
                     }
                     loadedSounds = [];
                     loadedSounds = [];
                     // Connect parents & children and parse actions
                     // Connect parents & children and parse actions
+                    for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
+                        var transformNode = scene.transformNodes[index];
+                        if (transformNode._waitingParentId) {
+                            transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
+                            transformNode._waitingParentId = null;
+                        }
+                    }
                     for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                         var mesh = scene.meshes[index];
                         var mesh = scene.meshes[index];
                         if (mesh._waitingParentId) {
                         if (mesh._waitingParentId) {
@@ -74223,6 +74318,11 @@ var BABYLON;
             for (index = 0; index < scene.skeletons.length; index++) {
             for (index = 0; index < scene.skeletons.length; index++) {
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
             }
             }
+            // Transform nodes
+            serializationObject.transformNodes = [];
+            for (index = 0; index < scene.transformNodes.length; index++) {
+                serializationObject.transformNodes.push(scene.transformNodes[index].serialize());
+            }
             // Geometries
             // Geometries
             serializationObject.geometries = {};
             serializationObject.geometries = {};
             serializationObject.geometries.boxes = [];
             serializationObject.geometries.boxes = [];

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 5760 - 5752
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 15 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -434,6 +434,14 @@
                     }
                     }
                 }
                 }
 
 
+                // Transform nodes
+                if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
+                    for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
+                        var parsedTransformNode = parsedData.transformNodes[index];
+                        TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                    }
+                }                
+
                 // Meshes
                 // Meshes
                 if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                 if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                     for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
                     for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
@@ -498,6 +506,13 @@
                 loadedSounds = [];
                 loadedSounds = [];
 
 
                 // Connect parents & children and parse actions
                 // Connect parents & children and parse actions
+                for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
+                    var transformNode = scene.transformNodes[index];
+                    if (transformNode._waitingParentId) {
+                        transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
+                        transformNode._waitingParentId = null;
+                    }
+                }                
                 for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                 for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     var mesh = scene.meshes[index];
                     var mesh = scene.meshes[index];
                     if (mesh._waitingParentId) {
                     if (mesh._waitingParentId) {

+ 0 - 20
src/Mesh/babylon.abstractMesh.ts

@@ -944,26 +944,6 @@
         }
         }
 
 
         /**
         /**
-        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.  
-        * @param func: callback function to add
-        *
-        * Returns the AbstractMesh. 
-        */
-        public registerAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): AbstractMesh {
-            this.onAfterWorldMatrixUpdateObservable.add(func);
-            return this;
-        }
-
-        /**
-         * Removes a registered callback function.  
-         * Returns the AbstractMesh.
-         */
-        public unregisterAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): AbstractMesh {
-            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
-            return this;
-        }
-
-        /**
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.  
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.  
          * A mesh is in the frustum if its bounding box intersects the frustum.  
          * A mesh is in the frustum if its bounding box intersects the frustum.  
          * Boolean returned.  
          * Boolean returned.  

+ 2 - 2
src/Mesh/babylon.mesh.ts

@@ -2222,8 +2222,8 @@
 
 
         // Statics
         // Statics
         /**
         /**
-         * Returns a new Mesh object what is a deep copy of the passed mesh.   
-         * The parameter `parsedMesh` is the mesh to be copied.   
+         * Returns a new Mesh object parsed from the source provided.   
+         * The parameter `parsedMesh` is the source.   
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
          */
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {

+ 122 - 0
src/Mesh/babylon.transformNode.ts

@@ -782,7 +782,129 @@ module BABYLON {
         }   
         }   
 
 
         protected _afterComputeWorldMatrix(): void {
         protected _afterComputeWorldMatrix(): void {
+        }
 
 
+        /**
+        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.  
+        * @param func: callback function to add
+        *
+        * Returns the TransformNode. 
+        */
+        public registerAfterWorldMatrixUpdate(func: (mesh: TransformNode) => void): TransformNode {
+            this.onAfterWorldMatrixUpdateObservable.add(func);
+            return this;
         }
         }
+
+        /**
+         * Removes a registered callback function.  
+         * Returns the TransformNode.
+         */
+        public unregisterAfterWorldMatrixUpdate(func: (mesh: TransformNode) => void): TransformNode {
+            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
+            return this;
+        }        
+
+        public clone(name: string, newParent: Node): Nullable<TransformNode> {
+            var result = SerializationHelper.Clone(() => new TransformNode(name, this.getScene()), this);
+
+            result.name = name;
+            result.id = name;
+
+            if (newParent) {
+                result.parent = newParent;
+            }
+
+            return result;
+        }        
+
+        public serialize(serializationObject: any = null): any {
+            if (!serializationObject) {
+                serializationObject = {};
+            }
+
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.type = this.getClassName();
+
+            if (Tags && Tags.HasTags(this)) {
+                serializationObject.tags = Tags.GetTags(this);
+            }
+
+            serializationObject.position = this.position.asArray();
+
+            if (this.rotationQuaternion) {
+                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
+            } else if (this.rotation) {
+                serializationObject.rotation = this.rotation.asArray();
+            }
+
+            serializationObject.scaling = this.scaling.asArray();
+            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+
+            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.infiniteDistance = this.infiniteDistance;
+
+            serializationObject.billboardMode = this.billboardMode;
+
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+
+            // Metadata
+            if (this.metadata) {
+                serializationObject.metadata = this.metadata;
+            }
+
+            return serializationObject;
+        }
+
+        // Statics
+        /**
+         * Returns a new TransformNode object parsed from the source provided.   
+         * The parameter `parsedMesh` is the source.   
+         * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
+         */
+        public static Parse(parsedTransformNode: any, scene: Scene, rootUrl: string): TransformNode {
+            var transformNode = new TransformNode(parsedTransformNode.name, scene);
+
+            transformNode.id = parsedTransformNode.id;
+
+            if (Tags) {
+                Tags.AddTagsTo(transformNode, parsedTransformNode.tags);
+            }
+
+            transformNode.position = Vector3.FromArray(parsedTransformNode.position);
+
+            if (parsedTransformNode.metadata !== undefined) {
+                transformNode.metadata = parsedTransformNode.metadata;
+            }
+
+            if (parsedTransformNode.rotationQuaternion) {
+                transformNode.rotationQuaternion = Quaternion.FromArray(parsedTransformNode.rotationQuaternion);
+            } else if (parsedTransformNode.rotation) {
+                transformNode.rotation = Vector3.FromArray(parsedTransformNode.rotation);
+            }
+
+            transformNode.scaling = Vector3.FromArray(parsedTransformNode.scaling);
+
+            if (parsedTransformNode.localMatrix) {
+                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
+            } else if (parsedTransformNode.pivotMatrix) {
+                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
+            }
+
+            transformNode.setEnabled(parsedTransformNode.isEnabled);
+            transformNode.infiniteDistance = parsedTransformNode.infiniteDistance;
+
+            transformNode.billboardMode = parsedTransformNode.billboardMode;
+
+            // Parent
+            if (parsedTransformNode.parentId) {
+                transformNode._waitingParentId = parsedTransformNode.parentId;
+            }
+         
+            return transformNode;
+        }        
     }
     }
 }
 }

+ 6 - 0
src/Tools/babylon.sceneSerializer.ts

@@ -215,6 +215,12 @@
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
                 serializationObject.skeletons.push(scene.skeletons[index].serialize());
             }
             }
 
 
+            // Transform nodes
+            serializationObject.transformNodes = [];
+            for (index = 0; index < scene.transformNodes.length; index++) {
+                serializationObject.transformNodes.push(scene.transformNodes[index].serialize());
+            }            
+
             // Geometries
             // Geometries
             serializationObject.geometries = {};
             serializationObject.geometries = {};