Explorar el Código

Fix skeleton viewer (#8698)

* Add a TPose matrix to the bone

* Fix switching between true and false for computeBonesUsingShaders

* Make sure we display only bones used by the mesh

* Revert to the previous scene animation state (and not always to true)

* Don't handle bone with index == -1 in _getLinesForBonesWithLength

* No need for a promise in _buildSpheresAndSpurs

* Use the TPose matrix instead of the rest pose

* Fix the creation of the matrix weights/indices for the spurs and spheres

* Remove logs

* Don't display bones not used by the mesh when in line mode

* Rename TPose to bindPose

* Fix doc
Popov72 hace 5 años
padre
commit
cfeb440aa8
Se han modificado 4 ficheros con 186 adiciones y 155 borrados
  1. 2 0
      loaders/src/glTF/2.0/glTFLoader.ts
  2. 18 0
      src/Bones/bone.ts
  3. 164 153
      src/Debug/skeletonViewer.ts
  4. 2 2
      src/Meshes/mesh.ts

+ 2 - 0
loaders/src/glTF/2.0/glTFLoader.ts

@@ -1158,6 +1158,8 @@ export class GLTFLoader implements IGLTFLoader {
                 baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
             }
 
+            babylonBone.setBindPose(baseMatrix);
+
             babylonBone.updateMatrix(baseMatrix, false, false);
             babylonBone._updateDifferenceMatrix(undefined, false);
         }

+ 18 - 0
src/Bones/bone.ts

@@ -44,6 +44,7 @@ export class Bone extends Node {
     private _skeleton: Skeleton;
     private _localMatrix: Matrix;
     private _restPose: Matrix;
+    private _bindPose: Matrix;
     private _baseMatrix: Matrix;
     private _absoluteTransform = new Matrix();
     private _invertedAbsoluteTransform = new Matrix();
@@ -95,6 +96,7 @@ export class Bone extends Node {
         this._skeleton = skeleton;
         this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
         this._restPose = restPose ? restPose : this._localMatrix.clone();
+        this._bindPose = this._localMatrix.clone();
         this._baseMatrix = baseMatrix ? baseMatrix : this._localMatrix.clone();
         this._index = index;
 
@@ -213,6 +215,22 @@ export class Bone extends Node {
     }
 
     /**
+     * Gets the bind pose matrix
+     * @returns the bind pose matrix
+     */
+    public getBindPose(): Matrix {
+        return this._bindPose;
+    }
+
+    /**
+     * Sets the bind pose matrix
+     * @param matrix the local-space bind pose to set for this bone
+     */
+    public setBindPose(matrix: Matrix): void {
+        this._bindPose.copyFrom(matrix);
+    }
+
+    /**
      * Gets a matrix used to store world matrix (ie. the matrix sent to shaders)
      */
     public getWorldMatrix(): Matrix {

+ 164 - 153
src/Debug/skeletonViewer.ts

@@ -54,6 +54,8 @@ export class SkeletonViewer {
      /** The Utility Layer to render the gizmos in. */
     private _utilityLayer: Nullable<UtilityLayerRenderer>;
 
+    private _boneIndices: Set<number>;
+
     /** Gets the Scene. */
     get scene(): Scene {
         return this._scene;
@@ -137,6 +139,21 @@ export class SkeletonViewer {
         options.displayOptions.sphereFactor = options.displayOptions.sphereFactor ?? 0.865;
         options.computeBonesUsingShaders = options.computeBonesUsingShaders ?? true;
 
+        const boneIndices = mesh.getVerticesData(VertexBuffer.MatricesIndicesKind);
+        const boneWeights = mesh.getVerticesData(VertexBuffer.MatricesWeightsKind);
+
+        this._boneIndices = new Set();
+
+        if (boneIndices && boneWeights) {
+            for (let i = 0; i < boneIndices.length; ++i) {
+                const index = boneIndices[i], weight = boneWeights[i];
+
+                if (weight !== 0) {
+                    this._boneIndices.add(index);
+                }
+            }
+        }
+
         /* Create Utility Layer */
         this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
         this._utilityLayer.pickUtilitySceneFirst = false;
@@ -234,17 +251,22 @@ export class SkeletonViewer {
 
         let mesh = this.mesh._effectiveMesh;
         var meshPos = mesh.position;
+        let idx = 0;
         for (var i = 0; i < len; i++) {
             var bone = bones[i];
-            var points = this._debugLines[i];
+            var points = this._debugLines[idx];
+            if (bone._index === -1 || !this._boneIndices.has(bone.getIndex())) {
+                continue;
+            }
             if (!points) {
                 points = [Vector3.Zero(), Vector3.Zero()];
-                this._debugLines[i] = points;
+                this._debugLines[idx] = points;
             }
             this._getBonePosition(points[0], bone, meshMat);
             this._getBonePosition(points[1], bone, meshMat, 0, bone.length, 0);
             points[0].subtractInPlace(meshPos);
             points[1].subtractInPlace(meshPos);
+            idx++;
         }
     }
 
@@ -257,7 +279,7 @@ export class SkeletonViewer {
         for (var i = len - 1; i >= 0; i--) {
             var childBone = bones[i];
             var parentBone = childBone.getParent();
-            if (!parentBone) {
+            if (!parentBone || !this._boneIndices.has(childBone.getIndex())) {
                 continue;
             }
             var points = this._debugLines[boneNum];
@@ -274,14 +296,14 @@ export class SkeletonViewer {
     }
 
     /** function to revert the mesh and scene back to the initial state. */
-    private _revert(): void {
+    private _revert(animationState: boolean): void {
         if (this.options.pauseAnimations) {
-            this.scene.animationsEnabled = true;
+            this.scene.animationsEnabled = animationState;
         }
     }
 
     /** function to build and bind sphere joint points and spur bone representations. */
-    private _buildSpheresAndSpurs(spheresOnly = true): Promise<void> {
+    private _buildSpheresAndSpurs(spheresOnly = true): void {
 
         if (this._debugMesh) {
             this._debugMesh.dispose();
@@ -292,183 +314,175 @@ export class SkeletonViewer {
         this._ready = false;
         let scene = this.scene;
         let bones: Bone[] = this.skeleton.bones;
-        let spheres: Mesh[] = [];
+        let spheres: Array<[Mesh, Bone]> = [];
         let spurs: Mesh[] = [];
 
-        return new Promise((resolve, reject) => {
-            try {
-                if (this.options.pauseAnimations) {
-                    scene.animationsEnabled = false;
-                }
+        const animationState = scene.animationsEnabled;
 
-                if (this.options.returnToRest) {
-                    this.skeleton.returnToRest();
+        try {
+            if (this.options.pauseAnimations) {
+                scene.animationsEnabled = false;
+            }
+
+            if (this.options.returnToRest) {
+                this.skeleton.returnToRest();
+            }
+
+            if (this.autoUpdateBonesMatrices) {
+                this.skeleton.computeAbsoluteTransforms();
+            }
+
+            let longestBoneLength = Number.NEGATIVE_INFINITY;
+            let getAbsoluteRestPose = function(bone: Nullable<Bone>, matrix: Matrix) {
+                if (bone === null || bone._index === -1) {
+                    matrix.copyFrom(Matrix.Identity());
+                    return;
                 }
+                getAbsoluteRestPose(bone.getParent(), matrix);
+                bone.getBindPose().multiplyToRef(matrix, matrix);
+                return;
+            };
+
+            let displayOptions = this.options.displayOptions || {};
+
+            for (let i = 0; i < bones.length; i++) {
+                let bone = bones[i];
 
-                if (this.autoUpdateBonesMatrices) {
-                    this.skeleton.computeAbsoluteTransforms();
+                if (bone._index === -1 || !this._boneIndices.has(bone.getIndex())) {
+                    continue;
                 }
 
-                let longestBoneLength = Number.NEGATIVE_INFINITY;
-                let getAbsoluteRestPose = function(bone: Nullable<Bone>, matrix: Matrix) {
-                    if (bone == null) {
-                        matrix.copyFrom(Matrix.Identity());
-                        return;
-                    }
-                    getAbsoluteRestPose(bone.getParent(), matrix);
-                    bone.getRestPose().multiplyToRef(matrix, matrix);
-                    return;
-                };
+                let boneAbsoluteRestTransform = new Matrix();
+                getAbsoluteRestPose(bone, boneAbsoluteRestTransform);
+
+                let anchorPoint = new Vector3();
+                boneAbsoluteRestTransform.decompose(undefined, undefined, anchorPoint);
 
-                let displayOptions = this.options.displayOptions || {};
+                bone.children.forEach((bc, i) => {
+                    let childAbsoluteRestTransform : Matrix = new Matrix();
+                    bc.getRestPose().multiplyToRef(boneAbsoluteRestTransform, childAbsoluteRestTransform);
+                    let childPoint = new Vector3();
+                    childAbsoluteRestTransform.decompose(undefined, undefined, childPoint);
 
-                for (let i = 0; i < bones.length; i++) {
-                    let bone: Bone = bones[i];
+                    let distanceFromParent = Vector3.Distance(anchorPoint, childPoint);
 
-                    if (bone._index === null) {
-                        bone._index = i;
+                    if (distanceFromParent > longestBoneLength) {
+                        longestBoneLength = distanceFromParent;
                     }
-                    if (bone._index === -1) {
-                        continue;
+                    if (spheresOnly) {
+                        return;
                     }
 
-                    let boneAbsoluteRestTransform = new Matrix();
-                    getAbsoluteRestPose(bone, boneAbsoluteRestTransform);
-
-                    let anchorPoint = new Vector3();
-                    boneAbsoluteRestTransform.decompose(undefined, undefined, anchorPoint);
-
-                    bone.children.forEach((bc, i) => {
-                        let childAbsoluteRestTransform : Matrix = new Matrix();
-                        bc.getRestPose().multiplyToRef(boneAbsoluteRestTransform, childAbsoluteRestTransform);
-                        let childPoint = new Vector3();
-                        childAbsoluteRestTransform.decompose(undefined, undefined, childPoint);
-
-                        let distanceFromParent = Vector3.Distance(anchorPoint, childPoint);
-
-                        if (distanceFromParent > longestBoneLength) {
-                            longestBoneLength = distanceFromParent;
-                        }
-                        if (spheresOnly) {
-                            return;
-                        }
-
-                        let dir = childPoint.clone().subtract(anchorPoint.clone());
-                        let h = dir.length();
-                        let up = dir.normalize().scale(h);
-
-                        let midStep = displayOptions.midStep || 0.165;
-                        let midStepFactor = displayOptions.midStepFactor || 0.215;
-
-                        let up0 = up.scale(midStep);
-
-                        let spur = ShapeBuilder.ExtrudeShapeCustom(bc.name + ':spur',
-                        {
-                            shape:  [
-                                        new Vector3(1, -1,  0),
-                                        new Vector3(1,  1,  0),
-                                        new Vector3(-1,  1,  0),
-                                        new Vector3(-1, -1,  0),
-                                        new Vector3(1, -1,  0)
-                                    ],
-                            path:   [ Vector3.Zero(), up0, up ],
-                            scaleFunction:
-                                    (i: number) => {
-                                        switch (i){
-                                            case 0:
-                                            case 2:
-                                            return 0;
-                                            case 1:
-                                            return h * midStepFactor;
-                                        }
+                    let dir = childPoint.clone().subtract(anchorPoint.clone());
+                    let h = dir.length();
+                    let up = dir.normalize().scale(h);
+
+                    let midStep = displayOptions.midStep || 0.165;
+                    let midStepFactor = displayOptions.midStepFactor || 0.215;
+
+                    let up0 = up.scale(midStep);
+
+                    let spur = ShapeBuilder.ExtrudeShapeCustom(bc.name + ':spur',
+                    {
+                        shape:  [
+                                    new Vector3(1, -1,  0),
+                                    new Vector3(1,  1,  0),
+                                    new Vector3(-1,  1,  0),
+                                    new Vector3(-1, -1,  0),
+                                    new Vector3(1, -1,  0)
+                                ],
+                        path:   [ Vector3.Zero(), up0, up ],
+                        scaleFunction:
+                                (i: number) => {
+                                    switch (i){
+                                        case 0:
+                                        case 2:
                                         return 0;
-                                    },
-                            sideOrientation: Mesh.DEFAULTSIDE,
-                            updatable: true
-                        },  scene);
+                                        case 1:
+                                        return h * midStepFactor;
+                                    }
+                                    return 0;
+                                },
+                        sideOrientation: Mesh.DEFAULTSIDE,
+                        updatable: false
+                    },  scene);
+
+                    spur.convertToFlatShadedMesh();
+
+                    let numVertices = spur.getTotalVertices();
+                    let mwk: number[] = [], mik: number[] = [];
 
-                        let ind = spur.getIndices() || [];
-                        let mwk: number[] = [], mik: number[] = [];
+                    for (let i = 0; i < numVertices; i++) {
+                        mwk.push(1, 0, 0, 0);
+                        mik.push(bone.getIndex(), 0, 0, 0);
+                    }
+                    spur.position = anchorPoint.clone();
 
-                        for (let i = 0; i < ind.length; i++) {
-                            mwk.push(1, 0, 0, 0);
-                            mik.push(bone.getIndex(), 0, 0, 0);
-                        }
-                        spur.convertToFlatShadedMesh();
-                        spur.position = anchorPoint.clone();
+                    spur.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
+                    spur.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
 
-                        spur.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
-                        spur.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
-                        spurs.push(spur);
+                    spurs.push(spur);
+                });
 
-                    });
+                let sphereBaseSize = displayOptions.sphereBaseSize || 0.2;
 
-                    let sphereBaseSize = displayOptions.sphereBaseSize || 0.2;
+                let sphere = SphereBuilder.CreateSphere(bone.name + ':sphere', {
+                    segments: 6,
+                    diameter: sphereBaseSize,
+                    updatable: false
+                }, scene);
 
-                    let sphere = SphereBuilder.CreateSphere(bone.name + ':sphere', {
-                        segments: 6,
-                        diameter: sphereBaseSize,
-                        updatable: true
-                    }, scene);
+                const numVertices = sphere.getTotalVertices();
 
-                    let ind = sphere.getIndices() || [];
-                    let mwk: number[] = [], mik: number[] = [];
+                let mwk: number[] = [], mik: number[] = [];
 
-                    for (let i = 0; i < ind.length; i++) {
-                        mwk.push(1, 0, 0, 0);
-                        mik.push(bone.getIndex(), 0, 0, 0);
-                    }
-
-                    sphere.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
-                    sphere.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
-                    sphere.position = anchorPoint.clone();
-                    spheres.push(sphere);
+                for (let i = 0; i < numVertices; i++) {
+                    mwk.push(1, 0, 0, 0);
+                    mik.push(bone.getIndex(), 0, 0, 0);
                 }
 
-                let skip = 0;
-                let sphereScaleUnit = displayOptions.sphereScaleUnit || 2;
-                let sphereFactor = displayOptions.sphereFactor || 0.85;
+                sphere.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
+                sphere.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
 
-                for (let i = 0; i < bones.length; i++) {
-                    let bone: Nullable<Bone> = bones[i];
-                    if (bone.getIndex() === -1) {
-                        skip++;
-                        continue;
-                    }
-                    let sphere = spheres[i - skip];
-                    let scale = 1 / (sphereScaleUnit / longestBoneLength);
+                sphere.position = anchorPoint.clone();
+                spheres.push([sphere, bone]);
+            }
 
-                    let _stepsOut = 0;
-                    let _b: Bone = (bone as Bone) || {};
+            let sphereScaleUnit = displayOptions.sphereScaleUnit || 2;
+            let sphereFactor = displayOptions.sphereFactor || 0.85;
 
-                    while ((_b.getParent()) && (_b.getParent() as Bone).getIndex() !== -1) {
-                        _stepsOut++;
-                        _b = (_b.getParent() as Bone);
-                    }
-                    sphere.scaling.scaleInPlace(scale * Math.pow(sphereFactor, _stepsOut));
-                }
+            const meshes = [];
+            for (let i = 0; i < spheres.length; i++) {
+                let [sphere, bone] = spheres[i];
+                let scale = 1 / (sphereScaleUnit / longestBoneLength);
+
+                let _stepsOut = 0;
+                let _b = bone;
 
-                this.debugMesh = Mesh.MergeMeshes(spheres.concat(spurs), true, true);
-                if (this.debugMesh) {
-                    this.debugMesh.renderingGroupId = this.renderingGroupId;
-                    this.debugMesh.skeleton = this.skeleton;
-                    this.debugMesh.parent = this.mesh;
-                    this.debugMesh.computeBonesUsingShaders = this.options.computeBonesUsingShaders ?? true;
+                while ((_b.getParent()) && (_b.getParent() as Bone).getIndex() !== -1) {
+                    _stepsOut++;
+                    _b = (_b.getParent() as Bone);
                 }
+                sphere.scaling.scaleInPlace(scale * Math.pow(sphereFactor, _stepsOut));
+                meshes.push(sphere);
+            }
 
-                resolve();
-            } catch (err) {
-                console.log(err);
-                this._revert();
-                this.dispose();
+            this.debugMesh = Mesh.MergeMeshes(meshes.concat(spurs), true, true);
+            if (this.debugMesh) {
+                this.debugMesh.renderingGroupId = this.renderingGroupId;
+                this.debugMesh.skeleton = this.skeleton;
+                this.debugMesh.parent = this.mesh;
+                this.debugMesh.computeBonesUsingShaders = this.options.computeBonesUsingShaders ?? true;
+                this.debugMesh.alwaysSelectAsActiveMesh = true;
             }
-        }).then(() => {
-            this._revert();
+
+            this._revert(animationState);
             this.ready = true;
-        }).catch((err) => {
-            console.log(err);
+        } catch (err) {
+            console.error(err);
+            this._revert(animationState);
             this.dispose();
-        });
+        }
     }
 
     /** Update the viewer to sync with current skeleton state, only used for the line display. */
@@ -476,7 +490,6 @@ export class SkeletonViewer {
         if (!this._utilityLayer) {
             return;
         }
-        console.log("dlup");
 
         if (this.autoUpdateBonesMatrices) {
             this.skeleton.computeAbsoluteTransforms();
@@ -509,7 +522,6 @@ export class SkeletonViewer {
     public changeDisplayMode(mode: number): void {
         let wasEnabled = (this.isEnabled) ? true : false;
         if (this.displayMode !== mode) {
-            console.log("Change Display Mode!", mode, wasEnabled);
             this.isEnabled = false;
             if (this._debugMesh) {
                 this._debugMesh.dispose();
@@ -521,7 +533,6 @@ export class SkeletonViewer {
             this.update();
             this._bindObs();
             this.isEnabled = wasEnabled;
-            console.log(this._utilityLayer, this._debugMesh);
         }
     }
 

+ 2 - 2
src/Meshes/mesh.ts

@@ -237,8 +237,8 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
 
         if (value && this._internalMeshDataInfo._sourcePositions && this._internalMeshDataInfo._sourceNormals) {
             // switch from software to GPU computation: we need to reset the vertex and normal buffers that have been updated by the software process
-            this.setVerticesData(VertexBuffer.PositionKind, this._internalMeshDataInfo._sourcePositions);
-            this.setVerticesData(VertexBuffer.NormalKind, this._internalMeshDataInfo._sourceNormals);
+            this.setVerticesData(VertexBuffer.PositionKind, this._internalMeshDataInfo._sourcePositions.slice(), true);
+            this.setVerticesData(VertexBuffer.NormalKind, this._internalMeshDataInfo._sourceNormals.slice(), true);
         }
 
         this._internalAbstractMeshDataInfo._computeBonesUsingShaders = value;