Пример #1
0
    private int BoneAnimationFrameIndexOf(GPUSkinning_BoneAnimationFrame frame, GPUSkinning_Bone bone)
    {
        GPUSkinning_Bone[] bones = frame.bones;
        int numBones             = bones.Length;

        for (int i = 0; i < numBones; ++i)
        {
            if (bones[i] == bone)
            {
                return(i);
            }
        }
        return(-1);
    }
Пример #2
0
    private void UpdateBoneTransformMatrix(GPUSkinning_Bone bone, Matrix4x4 parentMatrix, GPUSkinning_BoneAnimationFrame frame)
    {
        int       index = BoneAnimationFrameIndexOf(frame, bone);
        Matrix4x4 mat   = parentMatrix * frame.matrices[index];

        bone.animationMatrix = mat * bone.bindpose;

        GPUSkinning_Bone[] children = bone.children;
        int numChildren             = children.Length;

        for (int i = 0; i < numChildren; ++i)
        {
            UpdateBoneTransformMatrix(children[i], mat, frame);
        }
    }
Пример #3
0
    private string GetBoneHierarchyName(GPUSkinning_Bone bone)
    {
        string str = string.Empty;

        GPUSkinning_Bone currentBone = bone;

        while (currentBone != null)
        {
            if (str == string.Empty)
            {
                str = currentBone.name;
            }
            else
            {
                str = currentBone.name + "/" + str;
            }

            currentBone = currentBone.parent;
        }

        return(str);
    }
Пример #4
0
    public GPUSkinning_Bone GetBoneByHierarchyName(string hierarchyName)
    {
        System.Func <GPUSkinning_Bone, string, GPUSkinning_Bone> Search = null;
        Search = (bone, name) =>
        {
            if (name == hierarchyName)
            {
                return(bone);
            }
            foreach (GPUSkinning_Bone child in bone.children)
            {
                GPUSkinning_Bone result = Search(child, name + "/" + child.name);
                if (result != null)
                {
                    return(result);
                }
            }
            return(null);
        };

        return(Search(bones[rootBoneIndex], bones[rootBoneIndex].name));
    }
Пример #5
0
    public override void Init(GPUSkinning gpuSkinning)
    {
        base.Init(gpuSkinning);

        shaderPropID_Time = Shader.PropertyToID("_GameTime");

        smr  = gpuSkinning.GetComponentInChildren <SkinnedMeshRenderer>();
        mesh = smr.sharedMesh;

        // Init Bones
        int numBones = smr.bones.Length;

        bones = new GPUSkinning_Bone[numBones];
        for (int i = 0; i < numBones; ++i)
        {
            GPUSkinning_Bone bone = new GPUSkinning_Bone();
            bones[i]       = bone;
            bone.transform = smr.bones[i];
            bone.name      = bone.transform.gameObject.name;
            bone.bindpose  = mesh.bindposes[i] /*smr to bone*/;
        }

        // Construct Hierarchy
        for (int i = 0; i < numBones; ++i)
        {
            if (bones[i].transform == smr.rootBone)
            {
                rootBoneIndex = i;
                break;
            }
        }
        System.Action <GPUSkinning_Bone> CollectChildren = null;
        CollectChildren = (currentBone) =>
        {
            List <GPUSkinning_Bone> children = new List <GPUSkinning_Bone>();
            for (int j = 0; j < currentBone.transform.childCount; ++j)
            {
                Transform        childTransform = currentBone.transform.GetChild(j);
                GPUSkinning_Bone childBone      = GetBoneByTransform(childTransform);
                if (childBone != null)
                {
                    childBone.parent = currentBone;
                    children.Add(childBone);
                    CollectChildren(childBone);
                }
            }
            currentBone.children = children.ToArray();
        };
        CollectChildren(bones[rootBoneIndex]);

        // New MeshFilter MeshRenderer
        mf = gpuSkinning.gameObject.AddComponent <MeshFilter>();
        mr = gpuSkinning.gameObject.AddComponent <MeshRenderer>();

        newMtrl = new Material(Shader.Find("Unlit/GPUSkinning"));
        newMtrl.CopyPropertiesFromMaterial(smr.sharedMaterial);
        mr.sharedMaterial = newMtrl;

        // New Mesh
        newMesh           = new Mesh();
        newMesh.vertices  = mesh.vertices;
        newMesh.tangents  = GPUSkinningUtil.ExtractBoneWeights(mesh);
        newMesh.uv        = mesh.uv;
        newMesh.triangles = mesh.triangles;
        mf.sharedMesh     = newMesh;

#if UNITY_EDITOR
        // Extract bone animation data
        int boneAnimationsCount = 0;
        boneAnimations = new GPUSkinning_BoneAnimation[gpuSkinning.GetComponent <Animator>().runtimeAnimatorController.animationClips.Length];
        foreach (AnimationClip animClip in gpuSkinning.GetComponent <Animator>().runtimeAnimatorController.animationClips)
        {
            GPUSkinning_BoneAnimation boneAnimation = ScriptableObject.CreateInstance <GPUSkinning_BoneAnimation>();
            boneAnimation.fps      = 60;
            boneAnimation.animName = animClip.name;
            boneAnimation.frames   = new GPUSkinning_BoneAnimationFrame[(int)(animClip.length * boneAnimation.fps)];
            boneAnimation.length   = animClip.length;
            boneAnimations[boneAnimationsCount++] = boneAnimation;

            for (int frameIndex = 0; frameIndex < boneAnimation.frames.Length; ++frameIndex)
            {
                GPUSkinning_BoneAnimationFrame frame = new GPUSkinning_BoneAnimationFrame();
                boneAnimation.frames[frameIndex] = frame;
                float second = (float)(frameIndex) / (float)boneAnimation.fps;

                List <GPUSkinning_Bone> bones2              = new List <GPUSkinning_Bone>();
                List <Matrix4x4>        matrices            = new List <Matrix4x4>();
                List <string>           bonesHierarchyNames = null; if (boneAnimation.bonesHierarchyNames == null)
                {
                    bonesHierarchyNames = new List <string>();
                }
                EditorCurveBinding[] curvesBinding = AnimationUtility.GetCurveBindings(animClip);
                foreach (var curveBinding in curvesBinding)
                {
                    GPUSkinning_Bone bone = GetBoneByHierarchyName(curveBinding.path);

                    if (bones2.Contains(bone))
                    {
                        continue;
                    }
                    bones2.Add(bone);

                    if (bonesHierarchyNames != null)
                    {
                        bonesHierarchyNames.Add(GetBoneHierarchyName(bone));
                    }

                    AnimationCurve curveRX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.x");
                    AnimationCurve curveRY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.y");
                    AnimationCurve curveRZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.z");
                    AnimationCurve curveRW = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.w");

                    AnimationCurve curvePX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.x");
                    AnimationCurve curvePY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.y");
                    AnimationCurve curvePZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.z");

                    float curveRX_v = curveRX.Evaluate(second);
                    float curveRY_v = curveRY.Evaluate(second);
                    float curveRZ_v = curveRZ.Evaluate(second);
                    float curveRW_v = curveRW.Evaluate(second);

                    float curvePX_v = curvePX.Evaluate(second);
                    float curvePY_v = curvePY.Evaluate(second);
                    float curvePZ_v = curvePZ.Evaluate(second);

                    Vector3    translation = new Vector3(curvePX_v, curvePY_v, curvePZ_v);
                    Quaternion rotation    = new Quaternion(curveRX_v, curveRY_v, curveRZ_v, curveRW_v);
                    NormalizeQuaternion(ref rotation);
                    matrices.Add(
                        Matrix4x4.TRS(translation, rotation, Vector3.one)
                        );
                }

                frame.bones    = bones2.ToArray();
                frame.matrices = matrices.ToArray();
                if (boneAnimation.bonesHierarchyNames == null)
                {
                    boneAnimation.bonesHierarchyNames = bonesHierarchyNames.ToArray();
                }
            }
        }
        // Save as ScriptableObject
        AssetDatabase.CreateAsset(boneAnimations[0], "Assets/GPUSkinning/Resources/anim0.asset");
        AssetDatabase.Refresh();
#else
        // Read from ScriptableObject directly
        boneAnimations = new GPUSkinning_BoneAnimation[] { Resources.Load("anim0") as GPUSkinning_BoneAnimation };
        foreach (var boneAnimation in boneAnimations)
        {
            foreach (var frame in boneAnimation.frames)
            {
                int numBones2 = boneAnimation.bonesHierarchyNames.Length;
                frame.bones = new GPUSkinning_Bone[numBones2];
                for (int i = 0; i < numBones2; ++i)
                {
                    frame.bones[i] = GetBoneByHierarchyName(boneAnimation.bonesHierarchyNames[i]);
                }
            }
        }
#endif

        // Spawn many models
        if (spawnPoints != null)
        {
            List <GPUSkinning_SpawnObject> list = new List <GPUSkinning_SpawnObject>();
            for (int i = 0; i < spawnPoints.Length; ++i)
            {
                for (int j = 0; j < spawnPoints[i].childCount; ++j)
                {
                    GPUSkinning_SpawnObject spawnObject = new GPUSkinning_SpawnObject();
                    list.Add(spawnObject);
                    spawnObject.transform         = spawnPoints[i].GetChild(j);
                    spawnObject.mf                = spawnObject.transform.gameObject.AddComponent <MeshFilter>();
                    spawnObject.mr                = spawnObject.transform.gameObject.AddComponent <MeshRenderer>();
                    spawnObject.mr.sharedMaterial = newMtrl;
                    spawnObject.mf.sharedMesh     = newMesh;
                }
            }
            spawnObjects = list.ToArray();
        }
    }
Пример #6
0
    public void Update()
    {
        if (!isDrawing)
        {
            return;
        }

        if (bone == null)
        {
            bone = gpuSkinning.model.GetBoneByHierarchyName(jointPath);
            if (bone != null)
            {
                boneIndex = System.Array.IndexOf(gpuSkinning.model.bones, bone);
            }
        }
        if (bone == null)
        {
            return;
        }

        if (instancingMatrices == null)
        {
            instancingMatrices = new Matrix4x4[gpuSkinning.model.spawnObjects.Length];
            playMode0_mpb      = new MaterialPropertyBlock();

            hierarchyToObjectMatrices = new Matrix4x4[gpuSkinning.model.spawnObjects.Length];
            playMode1_mpb             = new MaterialPropertyBlock();
        }

        if (gpuSkinning.playingMode.IsPlayMode0())
        {
            Matrix4x4 hierarchyToObject = bone.hierarchyMatrix * localMatrix;
            var       spawnObjects      = gpuSkinning.model.spawnObjects;
            int       numSpawnObjects   = spawnObjects == null ? 0 : spawnObjects.Length;


            for (int i = 0; i < numSpawnObjects; ++i)
            {
                var spawnObject = spawnObjects[i];

                // (1)
                // I think "transform.hasChanged" is not a reliable api in a complex case, it would be better to write your own game logic instead.
                if (spawnObject.transform.hasChanged)
                {
                    spawnObject.transform.hasChanged = false;
                    // (2)
                    // Huge amount of accessing "transform.localToWorldMatrix" will spend much cpu time, so cache these in a buffer, updating once data is dirty.
                    instancingMatrices[i] = spawnObject.transform.localToWorldMatrix;
                }

                if (!isBuiltInDrawMeshInstancedSupported || !gpuSkinning.instancing.IsGPUInstancingOn())
                {
                    // (3)
                    // Matrix multiplication is a slow operation. Optimize it.
                    Matrix4x4 hierarchyToWorld = instancingMatrices[i] * hierarchyToObject;
                    // (4)
                    // Invoke many times of "Graphics.DrawMesh" api will cause performance impact.
                    // So "Graphics.DrawMesh" is not better than MeshRenderer in some cases.
                    // Here is just a example for joint. Overhead should be considered in production.
                    Graphics.DrawMesh(mesh, hierarchyToWorld, material, 0);
                }
            }

            if (isBuiltInDrawMeshInstancedSupported && gpuSkinning.instancing.IsGPUInstancingOn())
            {
                playMode0_mpb.SetMatrix(shaderPropId_HierarchyToObjectMat, hierarchyToObject);
#if UNITY_5_5 || UNITY_5_6 || UNITY_5_7 || UNITY_5_8 || UNITY_5_9 || UNITY_6 || UNITY_2017 || UNITY_2018 // etc.
                // (5)
                // https://forum.unity3d.com/threads/graphics-drawmesh-drawmeshinstanced-fundamentally-bottlenecked-by-the-cpu.429120/
                Graphics.DrawMeshInstanced(mesh, 0, material, instancingMatrices, numSpawnObjects, playMode0_mpb);
#endif
            }
        }
        else
        {
            var   spawnObjects    = gpuSkinning.model.spawnObjects;
            int   numSpawnObjects = spawnObjects == null ? 0 : spawnObjects.Length;
            bool  isInstancingOn  = gpuSkinning.instancing.IsGPUInstancingOn();
            int   fps             = gpuSkinning.model.boneAnimations[0].fps;
            float animLength      = gpuSkinning.model.boneAnimations[0].length;

            for (int i = 0; i < numSpawnObjects; ++i)
            {
                var spawnObject = spawnObjects[i];

                // (1)
                if (spawnObject.transform.hasChanged)
                {
                    spawnObject.transform.hasChanged = false;
                    // (2)
                    instancingMatrices[i] = spawnObject.transform.localToWorldMatrix;
                }

                float     timeOffset        = isInstancingOn ? spawnObject.timeOffset_instancingOn : spawnObject.timeOffset_instancingOff;
                int       frameIndex        = (int)(((gpuSkinning.second + timeOffset) * fps) % (animLength * fps));
                int       frameStartIndex   = frameIndex * gpuSkinning.matrixTexture.numHierarchyMatricesPerFrame;
                Matrix4x4 hierarchyToObject = gpuSkinning.matrixTexture.hierarchyMatrices[frameStartIndex + boneIndex];

                if (!isBuiltInDrawMeshInstancedSupported || !gpuSkinning.instancing.IsGPUInstancingOn())
                {
                    // (3)
                    Matrix4x4 hierarchyToWorld = instancingMatrices[i] * hierarchyToObject * localMatrix;
                    // (4)
                    Graphics.DrawMesh(mesh, hierarchyToWorld, material, 0);
                }
                else
                {
                    hierarchyToObjectMatrices[i] = hierarchyToObject;
                }
            }

            if (isBuiltInDrawMeshInstancedSupported && gpuSkinning.instancing.IsGPUInstancingOn())
            {
                playMode1_mpb.SetMatrixArray(shaderPorpId_HierarchyToObjectMats, hierarchyToObjectMatrices);
                playMode1_mpb.SetMatrix(shaderPropId_JointLocalMatrix, localMatrix);
#if UNITY_5_5 || UNITY_5_6 || UNITY_5_7 || UNITY_5_8 || UNITY_5_9 || UNITY_6 || UNITY_2017 || UNITY_2018 // etc.
                // (5)
                Graphics.DrawMeshInstanced(mesh, 0, material, instancingMatrices, numSpawnObjects, playMode1_mpb);
#endif
            }
        }
    }
Пример #7
0
    private void Start()
    {
        shaderPropID_Matrices = Shader.PropertyToID("_Matrices");

        smr  = GetComponentInChildren <SkinnedMeshRenderer>();
        mesh = smr.sharedMesh;

        // Init Bones
        int numBones = smr.bones.Length;

        bones = new GPUSkinning_Bone[numBones];
        for (int i = 0; i < numBones; ++i)
        {
            GPUSkinning_Bone bone = new GPUSkinning_Bone();
            bones[i]       = bone;
            bone.transform = smr.bones[i];
            bone.bindpose  = mesh.bindposes[i] /*smr to bone*/;
        }

        matricesUniformBlock = new Matrix4x4[numBones];

        // Construct Bones' Hierarchy
        for (int i = 0; i < numBones; ++i)
        {
            if (bones[i].transform == smr.rootBone)
            {
                rootBoneIndex = i;
                break;
            }
        }
        System.Action <GPUSkinning_Bone> CollectChildren = null;
        CollectChildren = (currentBone) =>
        {
            List <GPUSkinning_Bone> children = new List <GPUSkinning_Bone>();
            for (int j = 0; j < currentBone.transform.childCount; ++j)
            {
                Transform        childTransform = currentBone.transform.GetChild(j);
                GPUSkinning_Bone childBone      = GetBoneByTransform(childTransform);
                if (childBone != null)
                {
                    childBone.parent = currentBone;
                    children.Add(childBone);
                    CollectChildren(childBone);
                }
            }
            currentBone.children = children.ToArray();
        };
        CollectChildren(bones[rootBoneIndex]);

        // New MeshFilter MeshRenderer
        mf = gameObject.AddComponent <MeshFilter>();
        mr = gameObject.AddComponent <MeshRenderer>();

        newMtrl = new Material(Shader.Find("Unlit/GPUSkinning"));
        newMtrl.CopyPropertiesFromMaterial(smr.sharedMaterial);
        mr.sharedMaterial = newMtrl;

        // Fetch bone-weight storing as tangents
        int numVertices = mesh.vertexCount;

        Vector4[] tangents = new Vector4[mesh.vertexCount];
        Vector4[] uv2      = new Vector4[numVertices];
        Vector4[] uv3      = new Vector4[numVertices];

        for (int i = 0; i < numVertices; ++i)
        {
            BoneWeight boneWeight = mesh.boneWeights[i];
            tangents[i].x = boneWeight.boneIndex0;
            tangents[i].y = boneWeight.weight0;
            tangents[i].z = boneWeight.boneIndex1;
            tangents[i].w = boneWeight.weight1;


            Vector4 skinData_01 = new Vector4();
            skinData_01.x = boneWeight.boneIndex0;
            skinData_01.y = boneWeight.weight0;
            skinData_01.z = boneWeight.boneIndex1;
            skinData_01.w = boneWeight.weight1;
            uv2[i]        = skinData_01;

            Vector4 skinData_23 = new Vector4();
            skinData_23.x = boneWeight.boneIndex2;
            skinData_23.y = boneWeight.weight2;
            skinData_23.z = boneWeight.boneIndex3;
            skinData_23.w = boneWeight.weight3;
            uv3[i]        = skinData_23;
        }

        // New Mesh
        newMesh           = new Mesh();
        newMesh.vertices  = mesh.vertices;
        newMesh.normals   = mesh.normals;
        newMesh.tangents  = tangents;
        newMesh.uv        = mesh.uv;
        newMesh.triangles = mesh.triangles;
        mf.sharedMesh     = newMesh;

        newMesh.SetUVs(1, new List <Vector4>(uv2));
        newMesh.SetUVs(2, new List <Vector4>(uv3));

        /*
         * // Fetch animations' data
         #if UNITY_EDITOR
         * int boneAnimationsCount = 0;
         *      boneAnimations = new GPUSkinning_BoneAnimation[GetComponent<Animator>().runtimeAnimatorController.animationClips.Length];
         *      foreach(AnimationClip animClip in GetComponent<Animator>().runtimeAnimatorController.animationClips)
         *      {
         *              GPUSkinning_BoneAnimation boneAnimation = ScriptableObject.CreateInstance<GPUSkinning_BoneAnimation>();
         *              boneAnimation.fps = 30;
         *              boneAnimation.animName = animClip.name;
         *              boneAnimation.frames = new GPUSkinning_BoneAnimationFrame[(int)(animClip.length * boneAnimation.fps)];
         *              boneAnimation.length = animClip.length;
         *              boneAnimations[boneAnimationsCount++] = boneAnimation;
         *
         *              for(int frameIndex = 0; frameIndex < boneAnimation.frames.Length; ++frameIndex)
         *              {
         *                      GPUSkinning_BoneAnimationFrame frame = new GPUSkinning_BoneAnimationFrame();
         *                      boneAnimation.frames[frameIndex] = frame;
         *                      float second = (float)(frameIndex) / (float)boneAnimation.fps;
         *
         *                      List<GPUSkinning_Bone> bones2 = new List<GPUSkinning_Bone>();
         *                      List<Matrix4x4> matrices = new List<Matrix4x4>();
         *      List<string> bonesHierarchyNames = new List<string>();
         *      EditorCurveBinding[] curvesBinding = AnimationUtility.GetCurveBindings(animClip);
         *                      foreach(var curveBinding in curvesBinding)
         *                      {
         *                              GPUSkinning_Bone bone = GetBoneByHierarchyName(curveBinding.path);
         *
         *                              if(bones2.Contains(bone))
         *                              {
         *                                      continue;
         *                              }
         *                              bones2.Add(bone);
         *
         *          bonesHierarchyNames.Add(GetBoneHierarchyName(bone));
         *
         *          AnimationCurve curveRX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.x");
         *                              AnimationCurve curveRY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.y");
         *                              AnimationCurve curveRZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.z");
         *                              AnimationCurve curveRW = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.w");
         *
         *                              AnimationCurve curvePX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.x");
         *                              AnimationCurve curvePY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.y");
         *                              AnimationCurve curvePZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.z");
         *
         *                              float curveRX_v = curveRX.Evaluate(second);
         *                              float curveRY_v = curveRY.Evaluate(second);
         *                              float curveRZ_v = curveRZ.Evaluate(second);
         *                              float curveRW_v = curveRW.Evaluate(second);
         *
         *                              float curvePX_v = curvePX.Evaluate(second);
         *                              float curvePY_v = curvePY.Evaluate(second);
         *                              float curvePZ_v = curvePZ.Evaluate(second);
         *
         *                              Vector3 translation = new Vector3(curvePX_v, curvePY_v, curvePZ_v);
         *                              Quaternion rotation = new Quaternion(curveRX_v, curveRY_v, curveRZ_v, curveRW_v);
         *                              NormalizeQuaternion(ref rotation);
         *                              matrices.Add(
         *                                      Matrix4x4.TRS(translation, rotation, Vector3.one)
         *                              );
         *                      }
         *
         *                      frame.bones = bones2.ToArray();
         *                      frame.matrices = matrices.ToArray();
         *      frame.bonesHierarchyNames = bonesHierarchyNames.ToArray();
         *              }
         *      }
         * AssetDatabase.CreateAsset(boneAnimations[0], "Assets/GPUSkinning/Resources/anim0.asset");
         * AssetDatabase.Refresh();
         #else
         * boneAnimations = new GPUSkinning_BoneAnimation[] { Resources.Load("anim0") as GPUSkinning_BoneAnimation };
         * foreach(var boneAnimation in boneAnimations)
         * {
         *  foreach(var frame in boneAnimation.frames)
         *  {
         *      int numBones2 = frame.bonesHierarchyNames.Length;
         *      frame.bones = new GPUSkinning_Bone[numBones2];
         *      for(int i = 0; i < numBones2; ++i)
         *      {
         *          frame.bones[i] = GetBoneByHierarchyName(frame.bonesHierarchyNames[i]);
         *      }
         *  }
         * }
         #endif
         *
         *
         * GameObject.Destroy(transform.Find("pelvis").gameObject);
         * GameObject.Destroy(transform.Find("mutant_mesh").gameObject);
         * Object.Destroy(gameObject.GetComponent<Animator>());
         */
        //smr.enabled = false;

        //PrintBones();
    }
Пример #8
0
    private void Start()
    {
        shaderPropID_Matrices        = Shader.PropertyToID("_Matrices");
        shaderPropID_MatricesTex     = Shader.PropertyToID("_MatricesTex");
        shaderPropID_MatricesTexSize = Shader.PropertyToID("_MatricesTexSize");
        shaderPropID_AnimLength      = Shader.PropertyToID("_AnimLength");
        shaderPropID_AnimFPS         = Shader.PropertyToID("_AnimFPS");
        shaderPropID_TerrainTex      = Shader.PropertyToID("_TerrainTex");
        shaderPropID_TerrainSize     = Shader.PropertyToID("_TerrainSize");
        shaderPropID_TerrainPos      = Shader.PropertyToID("_TerrainPos");
        shaderPropID_mpb_time        = Shader.PropertyToID("_mpb_time");

        smr  = GetComponentInChildren <SkinnedMeshRenderer>();
        mesh = smr.sharedMesh;

        // 初始化骨骼对象
        int numBones = smr.bones.Length;

        bones = new GPUSkinning_Bone[numBones];
        for (int i = 0; i < numBones; ++i)
        {
            GPUSkinning_Bone bone = new GPUSkinning_Bone();
            bones[i]       = bone;
            bone.transform = smr.bones[i];
            bone.bindpose  = mesh.bindposes[i] /*smr to bone*/;
        }

        matricesUniformBlock = new Matrix4x4[numBones];

        // 构建骨骼的层级结构
        for (int i = 0; i < numBones; ++i)
        {
            if (bones[i].transform == smr.rootBone)
            {
                rootBoneIndex = i;
                break;
            }
        }
        System.Action <GPUSkinning_Bone> CollectChildren = null;
        CollectChildren = (currentBone) =>
        {
            List <GPUSkinning_Bone> children = new List <GPUSkinning_Bone>();
            for (int j = 0; j < currentBone.transform.childCount; ++j)
            {
                Transform        childTransform = currentBone.transform.GetChild(j);
                GPUSkinning_Bone childBone      = GetBoneByTransform(childTransform);
                if (childBone != null)
                {
                    childBone.parent = currentBone;
                    children.Add(childBone);
                    CollectChildren(childBone);
                }
            }
            currentBone.children = children.ToArray();
        };
        CollectChildren(bones[rootBoneIndex]);

        // New MeshFilter MeshRenderer
        mf = gameObject.AddComponent <MeshFilter>();
        mr = gameObject.AddComponent <MeshRenderer>();

        newMtrl = new Material(Shader.Find("Unlit/GPUSkinning"));
        newMtrl.CopyPropertiesFromMaterial(smr.sharedMaterial);
        mr.sharedMaterial = newMtrl;

        // 保存骨骼动画权重
        Vector4[] tangents = new Vector4[mesh.vertexCount];
        for (int i = 0; i < mesh.vertexCount; ++i)
        {
            BoneWeight boneWeight = mesh.boneWeights[i];
            tangents[i].x = boneWeight.boneIndex0;
            tangents[i].y = boneWeight.weight0;
            tangents[i].z = boneWeight.boneIndex1;
            tangents[i].w = boneWeight.weight1;
        }

        // New Mesh
        newMesh           = new Mesh();
        newMesh.vertices  = mesh.vertices;
        newMesh.tangents  = tangents;
        newMesh.uv        = mesh.uv;
        newMesh.triangles = mesh.triangles;
        mf.sharedMesh     = newMesh;

        // 为每个角色生成差异化数据
        if (IsMatricesTextureSupported())
        {
            additionalVertexStreames = new Mesh[50];
            for (int i = 0; i < additionalVertexStreames.Length; ++i)
            {
                Mesh      m   = new Mesh();
                float     rnd = Random.Range(0.0f, 10.0f);
                Vector2[] uv2 = new Vector2[mesh.vertexCount];
                for (int j = 0; j < mesh.vertexCount; ++j)
                {
                    Vector2 uv = Vector2.zero;
                    uv.x   = rnd;
                    uv2[j] = uv;
                }
                m.vertices = newMesh.vertices;
                m.uv2      = uv2;
                m.UploadMeshData(true);
                additionalVertexStreames[i] = m;
            }
            mr.additionalVertexStreams = additionalVertexStreames[0];
        }

#if UNITY_EDITOR
        // 从 Unity 的 Animation 中提取骨骼动画所需要的数据
        int boneAnimationsCount = 0;
        boneAnimations = new GPUSkinning_BoneAnimation[GetComponent <Animator>().runtimeAnimatorController.animationClips.Length];
        foreach (AnimationClip animClip in GetComponent <Animator>().runtimeAnimatorController.animationClips)
        {
            GPUSkinning_BoneAnimation boneAnimation = ScriptableObject.CreateInstance <GPUSkinning_BoneAnimation>();
            boneAnimation.fps      = 60;
            boneAnimation.animName = animClip.name;
            boneAnimation.frames   = new GPUSkinning_BoneAnimationFrame[(int)(animClip.length * boneAnimation.fps)];
            boneAnimation.length   = animClip.length;
            boneAnimations[boneAnimationsCount++] = boneAnimation;

            for (int frameIndex = 0; frameIndex < boneAnimation.frames.Length; ++frameIndex)
            {
                GPUSkinning_BoneAnimationFrame frame = new GPUSkinning_BoneAnimationFrame();
                boneAnimation.frames[frameIndex] = frame;
                float second = (float)(frameIndex) / (float)boneAnimation.fps;

                List <GPUSkinning_Bone> bones2              = new List <GPUSkinning_Bone>();
                List <Matrix4x4>        matrices            = new List <Matrix4x4>();
                List <string>           bonesHierarchyNames = null; if (boneAnimation.bonesHierarchyNames == null)
                {
                    bonesHierarchyNames = new List <string>();
                }
                EditorCurveBinding[] curvesBinding = AnimationUtility.GetCurveBindings(animClip);
                foreach (var curveBinding in curvesBinding)
                {
                    GPUSkinning_Bone bone = GetBoneByHierarchyName(curveBinding.path);

                    if (bones2.Contains(bone))
                    {
                        continue;
                    }
                    bones2.Add(bone);

                    if (bonesHierarchyNames != null)
                    {
                        bonesHierarchyNames.Add(GetBoneHierarchyName(bone));
                    }

                    AnimationCurve curveRX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.x");
                    AnimationCurve curveRY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.y");
                    AnimationCurve curveRZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.z");
                    AnimationCurve curveRW = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalRotation.w");

                    AnimationCurve curvePX = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.x");
                    AnimationCurve curvePY = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.y");
                    AnimationCurve curvePZ = AnimationUtility.GetEditorCurve(animClip, curveBinding.path, curveBinding.type, "m_LocalPosition.z");

                    float curveRX_v = curveRX.Evaluate(second);
                    float curveRY_v = curveRY.Evaluate(second);
                    float curveRZ_v = curveRZ.Evaluate(second);
                    float curveRW_v = curveRW.Evaluate(second);

                    float curvePX_v = curvePX.Evaluate(second);
                    float curvePY_v = curvePY.Evaluate(second);
                    float curvePZ_v = curvePZ.Evaluate(second);

                    Vector3    translation = new Vector3(curvePX_v, curvePY_v, curvePZ_v);
                    Quaternion rotation    = new Quaternion(curveRX_v, curveRY_v, curveRZ_v, curveRW_v);
                    NormalizeQuaternion(ref rotation);
                    matrices.Add(
                        Matrix4x4.TRS(translation, rotation, Vector3.one)
                        );
                }

                frame.bones    = bones2.ToArray();
                frame.matrices = matrices.ToArray();
                if (boneAnimation.bonesHierarchyNames == null)
                {
                    boneAnimation.bonesHierarchyNames = bonesHierarchyNames.ToArray();
                }
            }
        }
        AssetDatabase.CreateAsset(boneAnimations[0], "Assets/GPUSkinning/Resources/anim0.asset");
        AssetDatabase.Refresh();
#else
        // 直接读取序列化的骨骼动画数据
        boneAnimations = new GPUSkinning_BoneAnimation[] { Resources.Load("anim0") as GPUSkinning_BoneAnimation };
        foreach (var boneAnimation in boneAnimations)
        {
            foreach (var frame in boneAnimation.frames)
            {
                int numBones2 = boneAnimation.bonesHierarchyNames.Length;
                frame.bones = new GPUSkinning_Bone[numBones2];
                for (int i = 0; i < numBones2; ++i)
                {
                    frame.bones[i] = GetBoneByHierarchyName(boneAnimation.bonesHierarchyNames[i]);
                }
            }
        }
#endif

        // 创建出更多的角色模型
        if (spawnPoints != null)
        {
            List <GPUSkinning_SpawnObject> list = new List <GPUSkinning_SpawnObject>();
            for (int i = 0; i < spawnPoints.Length; ++i)
            {
                for (int j = 0; j < spawnPoints[i].childCount; ++j)
                {
                    GPUSkinning_SpawnObject spawnObject = new GPUSkinning_SpawnObject();
                    list.Add(spawnObject);
                    spawnObject.transform         = spawnPoints[i].GetChild(j);
                    spawnObject.mf                = spawnObject.transform.gameObject.AddComponent <MeshFilter>();
                    spawnObject.mr                = spawnObject.transform.gameObject.AddComponent <MeshRenderer>();
                    spawnObject.mr.sharedMaterial = newMtrl;
                    spawnObject.mf.sharedMesh     = newMesh;
                    if (additionalVertexStreames != null)
                    {
                        // 当开启 GPUInstancing 时,这个是不需要的,偷懒就直接设置了
                        spawnObject.mr.additionalVertexStreams = additionalVertexStreames[Random.Range(0, additionalVertexStreames.Length)];
                    }
                }
            }
            spawnObjects = list.ToArray();
        }

        // 销毁并暂停 Unity 的 Animator
        GameObject.Destroy(transform.FindChild("pelvis").gameObject);
        GameObject.Destroy(transform.FindChild("mutant_mesh").gameObject);
        Object.Destroy(gameObject.GetComponent <Animator>());

        smr.enabled = false;

        //PrintBones();

        CreateMatricesTexture();

        BakeAnimationsToTexture();

        SetPlayMode0();

        InitTerrain();

        SetTerrainHeightSwitch();

        InitGPUInstancing();

        InitComputeBuffer(newMesh);

        newMesh.UploadMeshData(true);
    }