浏览代码

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 6 年之前
父节点
当前提交
0f44c3a123

+ 6 - 0
dist/preview release/what's new.md

@@ -67,12 +67,18 @@
 - Added `Tools.CustomRequestHeaders`, `Tools.UseCustomRequestHeaders`, `Tools.InjectCustomRequestHeaders` to send Custom Request Headers alongside XMLHttpRequest's i.e. when loading files (Tools.Loadfile) from resources requiring special headers like 'Authorization' ([susares](https://github.com/susares))
 - Added `.serialize` and `.Parse` functions in `ReflectionProbe` to retrieve reflection probes when parsing a previously serialized material ([julien-moreau](https://github.com/julien-moreau))
 - GizmoManager clearGizmoOnEmptyPointerEvent options and onAttachedToMeshObservable event ([TrevorDev](https://github.com/TrevorDev))
+- Added support for overriding the mesh used for the world matrix for a mesh with a skeleton ([bghgary](https://github.com/bghgary))
+- Added support for linking a bone to a transform node ([bghgary](https://github.com/bghgary))
+
 ### glTF Loader
 
 - Added support for mesh instancing for improved performance when multiple nodes point to the same mesh ([bghgary](https://github.com/bghgary))
 - Create `TransformNode` objects instead of `Mesh` objects for glTF nodes without geometry ([bghgary](https://github.com/bghgary))
 - Added glTF JSON pointers to metadata of nodes, materials, and textures ([bghgary](https://github.com/bghgary))
 - Load KTX textures in the gltf2 loader when textureFormat is set on engine ([TrevorDev](https://github.com/TrevorDev))
+- Skinned meshes now behave as intended by glTF ([bghgary](https://github.com/bghgary))
+  - Skinned meshes now set an override mesh instead of reparenting to the `__root__` transform node
+  - Loaded bones are linked with the transform node created for the corresponding glTF node
 
 ### glTF Serializer
 

+ 25 - 37
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -432,6 +432,18 @@ module BABYLON.GLTF2 {
                 }
             }
 
+            // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
+            // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
+            if (this.gltf.nodes) {
+                for (const node of this.gltf.nodes) {
+                    if (node._babylonTransformNode && node._babylonBones) {
+                        for (const babylonBone of node._babylonBones) {
+                            babylonBone.linkTransformNode(node._babylonTransformNode);
+                        }
+                    }
+                }
+            }
+
             promises.push(this._loadAnimationsAsync());
 
             this.logClose();
@@ -445,9 +457,6 @@ module BABYLON.GLTF2 {
                     callback(babylonMesh);
                 }
             }
-            else if (node._babylonTransformNode instanceof AbstractMesh) {
-                callback(node._babylonTransformNode);
-            }
         }
 
         private _getMeshes(): AbstractMesh[] {
@@ -561,12 +570,6 @@ module BABYLON.GLTF2 {
                     for (const index of node.children) {
                         const childNode = ArrayItem.Get(`${context}/children/${index}`, this.gltf.nodes, index);
                         promises.push(this.loadNodeAsync(`/nodes/${childNode.index}`, childNode, (childBabylonMesh) => {
-                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                            if (childNode.skin != undefined) {
-                                childBabylonMesh.parent = this._rootBabylonMesh;
-                                return;
-                            }
-
                             childBabylonMesh.parent = babylonTransformNode;
                         }));
                     }
@@ -616,16 +619,16 @@ module BABYLON.GLTF2 {
                 const primitive = mesh.primitives[0];
                 promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, name, node, mesh, primitive, (babylonMesh) => {
                     node._babylonTransformNode = babylonMesh;
+                    node._primitiveBabylonMeshes = [babylonMesh];
                 }));
             }
             else {
-                const babylonTransformNode = new TransformNode(name, this.babylonScene);
-                node._babylonTransformNode = babylonTransformNode;
+                node._babylonTransformNode = new TransformNode(name, this.babylonScene);
+                node._primitiveBabylonMeshes = [];
                 for (const primitive of primitives) {
                     promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, `${name}_primitive${primitive.index}`, node, mesh, primitive, (babylonMesh) => {
-                        babylonMesh.parent = babylonTransformNode;
-                        node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-                        node._primitiveBabylonMeshes.push(babylonMesh);
+                        babylonMesh.parent = node._babylonTransformNode!;
+                        node._primitiveBabylonMeshes!.push(babylonMesh);
                     }));
                 }
             }
@@ -893,14 +896,16 @@ module BABYLON.GLTF2 {
             };
 
             if (skin._data) {
-                const data = skin._data;
-                return data.promise.then(() => {
-                    assignSkeleton(data.babylonSkeleton);
-                });
+                assignSkeleton(skin._data.babylonSkeleton);
+                return skin._data.promise;
             }
 
             const skeletonId = `skeleton${skin.index}`;
             const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+
+            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+            babylonSkeleton.overrideMesh = this._rootBabylonMesh;
+
             this._loadBones(context, skin, babylonSkeleton);
             assignSkeleton(babylonSkeleton);
 
@@ -1102,12 +1107,6 @@ module BABYLON.GLTF2 {
                 return Promise.resolve();
             }
 
-            // Ignore animations targeting TRS of skinned nodes.
-            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-            if (targetNode.skin != undefined && channel.target.path !== AnimationChannelTargetPath.WEIGHTS) {
-                return Promise.resolve();
-            }
-
             const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler);
             return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then((data) => {
                 let targetPath: string;
@@ -1234,19 +1233,8 @@ module BABYLON.GLTF2 {
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
 
-                    const babylonTransformNode = targetNode._babylonTransformNode!;
-                    const babylonBones = targetNode._babylonBones;
-                    if (babylonBones) {
-                        const babylonAnimationTargets = [babylonTransformNode, ...babylonBones];
-                        for (const babylonAnimationTarget of babylonAnimationTargets) {
-                            babylonAnimationTarget.animations.push(babylonAnimation);
-                        }
-                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
-                    }
-                    else {
-                        babylonTransformNode.animations.push(babylonAnimation);
-                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
-                    }
+                    targetNode._babylonTransformNode!.animations.push(babylonAnimation);
+                    babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode!);
                 }
             });
         }

+ 20 - 0
src/Bones/babylon.bone.ts

@@ -46,6 +46,9 @@ module BABYLON {
         private _needToCompose = false;
 
         /** @hidden */
+        public _linkedTransformNode: Nullable<TransformNode> = null;
+
+        /** @hidden */
         get _matrix(): Matrix {
             this._compose();
             return this._localMatrix;
@@ -193,6 +196,23 @@ module BABYLON {
             return this._absoluteTransform;
         }
 
+        /**
+         * Links with the given transform node.
+         * The local matrix of this bone is copied from the transform node every frame.
+         * @param transformNode defines the transform node to link to
+         */
+        public linkTransformNode(transformNode: Nullable<TransformNode>): void {
+            if (this._linkedTransformNode) {
+                this._skeleton._numBonesWithLinkedTransformNode--;
+            }
+
+            this._linkedTransformNode = transformNode;
+
+            if (this._linkedTransformNode) {
+                this._skeleton._numBonesWithLinkedTransformNode++;
+            }
+        }
+
         // Properties (matches AbstractMesh properties)
 
         /** Gets or sets current position (in local space) */

+ 22 - 3
src/Bones/babylon.skeleton.ts

@@ -5,17 +5,21 @@ module BABYLON {
      */
     export class Skeleton implements IAnimatable {
         /**
-         * Gets the list of child bones
+         * Defines the list of child bones
          */
         public bones = new Array<Bone>();
         /**
-         * Gets an estimate of the dimension of the skeleton at rest
+         * Defines an estimate of the dimension of the skeleton at rest
          */
         public dimensionsAtRest: Vector3;
         /**
-         * Gets a boolean indicating if the root matrix is provided by meshes or by the current skeleton (this is the default value)
+         * Defines a boolean indicating if the root matrix is provided by meshes or by the current skeleton (this is the default value)
          */
         public needInitialSkinMatrix = false;
+        /**
+         * Defines a mesh that override the matrix used to get the world matrix (null by default).
+         */
+        public overrideMesh: Nullable<AbstractMesh> = null;
 
         /**
          * Gets the list of animations attached to this skeleton
@@ -37,6 +41,9 @@ module BABYLON {
 
         private _canUseTextureForBones = false;
 
+        /** @hidden */
+        public _numBonesWithLinkedTransformNode = 0;
+
         /**
          * Specifies if the skeleton should be serialized
          */
@@ -370,6 +377,18 @@ module BABYLON {
          * Build all resources required to render a skeleton
          */
         public prepare(): void {
+            // Update the local matrix of bones with linked transform nodes.
+            if (this._numBonesWithLinkedTransformNode > 0) {
+                for (const bone of this.bones) {
+                    if (bone._linkedTransformNode) {
+                        // Computing the world matrix also computes the local matrix.
+                        bone._linkedTransformNode.computeWorldMatrix();
+                        bone._matrix = bone._linkedTransformNode._localMatrix;
+                        bone.markAsDirty();
+                    }
+                }
+            }
+
             if (!this._isDirty) {
                 return;
             }

+ 5 - 4
src/Mesh/babylon.abstractMesh.ts

@@ -1136,18 +1136,19 @@ module BABYLON {
 
         /** @hidden */
         public _updateBoundingInfo(): AbstractMesh {
+            const effectiveMesh = (this.skeleton && this.skeleton.overrideMesh) || this;
             if (this._boundingInfo) {
-                this._boundingInfo.update(this.worldMatrixFromCache);
+                this._boundingInfo.update(effectiveMesh.worldMatrixFromCache);
             }
             else {
-                this._boundingInfo = new BoundingInfo(this.absolutePosition, this.absolutePosition, this.worldMatrixFromCache);
+                this._boundingInfo = new BoundingInfo(this.absolutePosition, this.absolutePosition, effectiveMesh.worldMatrixFromCache);
             }
-            this._updateSubMeshesBoundingInfo(this.worldMatrixFromCache);
+            this._updateSubMeshesBoundingInfo(effectiveMesh.worldMatrixFromCache);
             return this;
         }
 
         /** @hidden */
-        public _updateSubMeshesBoundingInfo(matrix: Matrix): AbstractMesh {
+        public _updateSubMeshesBoundingInfo(matrix: DeepImmutable<Matrix>): AbstractMesh {
             if (!this.subMeshes) {
                 return this;
             }

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

@@ -1505,10 +1505,12 @@ module BABYLON {
                 return this;
             }
 
+            const effectiveMesh = (this.skeleton && this.skeleton.overrideMesh) || this;
+
             var sideOrientation = this.overrideMaterialSideOrientation;
             if (sideOrientation == null) {
                 sideOrientation = this._effectiveMaterial.sideOrientation;
-                if (this._getWorldMatrixDeterminant() < 0) {
+                if (effectiveMesh._getWorldMatrixDeterminant() < 0) {
                     sideOrientation = (sideOrientation === Material.ClockWiseSideOrientation ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation);
                 }
             }
@@ -1526,7 +1528,7 @@ module BABYLON {
                 this._bind(subMesh, effect, fillMode);
             }
 
-            var world = this.getWorldMatrix();
+            var world = effectiveMesh.getWorldMatrix();
 
             if (this._effectiveMaterial._storeEffectOnSubMeshes) {
                 this._effectiveMaterial.bindForSubMesh(world, this, subMesh);

+ 1 - 1
src/Mesh/babylon.subMesh.ts

@@ -236,7 +236,7 @@ module BABYLON {
          * @param world defines the world matrix to use to update the bounding info
          * @returns the submesh
          */
-        public updateBoundingInfo(world: Matrix): SubMesh {
+        public updateBoundingInfo(world: DeepImmutable<Matrix>): SubMesh {
             let boundingInfo = this.getBoundingInfo();
 
             if (!boundingInfo) {

+ 11 - 10
src/Mesh/babylon.transformNode.ts

@@ -84,7 +84,8 @@ module BABYLON {
         // Cache
         /** @hidden */
         public _poseMatrix: Matrix;
-        private _localWorld = Matrix.Zero();
+        /** @hidden */
+        public _localMatrix = Matrix.Zero();
 
         private _absolutePosition = Vector3.Zero();
         private _pivotMatrix = Matrix.Identity();
@@ -413,7 +414,7 @@ module BABYLON {
          */
         public setPositionWithLocalVector(vector3: Vector3): TransformNode {
             this.computeWorldMatrix();
-            this.position = Vector3.TransformNormal(vector3, this._localWorld);
+            this.position = Vector3.TransformNormal(vector3, this._localMatrix);
             return this;
         }
 
@@ -424,7 +425,7 @@ module BABYLON {
         public getPositionExpressedInLocalSpace(): Vector3 {
             this.computeWorldMatrix();
             const invLocalWorldMatrix = Tmp.Matrix[0];
-            this._localWorld.invertToRef(invLocalWorldMatrix);
+            this._localMatrix.invertToRef(invLocalWorldMatrix);
             return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
         }
 
@@ -435,7 +436,7 @@ module BABYLON {
          */
         public locallyTranslate(vector3: Vector3): TransformNode {
             this.computeWorldMatrix(true);
-            this.position = Vector3.TransformCoordinates(vector3, this._localWorld);
+            this.position = Vector3.TransformCoordinates(vector3, this._localMatrix);
             return this;
         }
 
@@ -940,7 +941,7 @@ module BABYLON {
             }
 
             // Local world
-            Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localWorld);
+            Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localMatrix);
 
             // Parent
             if (this.parent && this.parent.getWorldMatrix) {
@@ -952,22 +953,22 @@ module BABYLON {
                         Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
                     }
 
-                    this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
+                    this._localMatrix.getTranslationToRef(Tmp.Vector3[5]);
                     Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
-                    this._worldMatrix.copyFrom(this._localWorld);
+                    this._worldMatrix.copyFrom(this._localMatrix);
                     this._worldMatrix.setTranslation(Tmp.Vector3[5]);
 
                 } else {
                     if (this._transformToBoneReferal) {
-                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
+                        this._localMatrix.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
                         Tmp.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
                     } else {
-                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
+                        this._localMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
                     }
                 }
                 this._markSyncedWithParent();
             } else {
-                this._worldMatrix.copyFrom(this._localWorld);
+                this._worldMatrix.copyFrom(this._localMatrix);
             }
 
             // Normal matrix