private void BakeAnimationsToTexture()
    {
        if (matricesTex != null)
        {
            Color[] colorBuffer      = matricesTex.GetPixels();
            int     colorBufferIndex = 0;
            hierarchyMatrices = new Matrix4x4[colorBuffer.Length / 3];
            int hierarchyMatrixIndex = 0;

            GPUSkinningUtil.ExtractBoneAnimMatrix(
                gpuSkinning,
                gpuSkinning.model.boneAnimations[0],
                (animMat, hierarchyMat) =>
            {
                hierarchyMatrices[hierarchyMatrixIndex++] = hierarchyMat;

                Color c = colorBuffer[colorBufferIndex];
                c.r     = animMat.m00; c.g = animMat.m01; c.b = animMat.m02; c.a = animMat.m03;
                colorBuffer[colorBufferIndex++] = c;

                c   = colorBuffer[colorBufferIndex];
                c.r = animMat.m10; c.g = animMat.m11; c.b = animMat.m12; c.a = animMat.m13;
                colorBuffer[colorBufferIndex++] = c;

                c   = colorBuffer[colorBufferIndex];
                c.r = animMat.m20; c.g = animMat.m21; c.b = animMat.m22; c.a = animMat.m23;
                colorBuffer[colorBufferIndex++] = c;
            },
                (frameIndex) =>
            {
                if (frameIndex == 0)
                {
                    numHierarchyMatricesPerFrame = hierarchyMatrixIndex;
                    numPixelsPerFrame            = colorBufferIndex;
                }
            }
                );

            matricesTex.SetPixels(colorBuffer);
            matricesTex.Apply(false, true);
        }
    }
    public override void Init(GPUSkinning gpuSkinning)
    {
        base.Init(gpuSkinning);

        if (!SystemInfo.supportsComputeShaders)
        {
            return;
        }

        shaderPropID_Time = Shader.PropertyToID("_GameTime");

        // Material
        Shader shader = Shader.Find("Unlit/ProceduralModel");

        if (!shader.isSupported)
        {
            return;
        }
        proceduralModelMaterial             = new Material(shader);
        proceduralModelMaterial.mainTexture = gpuSkinning.model.newMtrl.mainTexture;

        // Vertices
        int[]     indices  = gpuSkinning.model.newMesh.triangles;
        Vector3[] vertices = gpuSkinning.model.newMesh.vertices;
        Vector4[] tangents = gpuSkinning.model.newMesh.tangents;
        Vector2[] uv       = gpuSkinning.model.newMesh.uv;
        verticesComputeBuffer = new ComputeBuffer(indices.Length, GPUSkinning_ComputeShader_Vertex.size);
        var data = new GPUSkinning_ComputeShader_Vertex[indices.Length];

        for (int i = 0; i < indices.Length; ++i)
        {
            var item = new GPUSkinning_ComputeShader_Vertex();
            item.vertex   = vertices[indices[i]];
            item.tangents = tangents[indices[i]];
            item.uv       = uv[indices[i]];
            data[i]       = item;
        }
        verticesComputeBuffer.SetData(data);
        numVertices_computeBuffer = data.Length;

        // Global Data
        var globalData = new GPUSkinning_CompueteShader_GlobalData();

        globalData.fps        = gpuSkinning.model.boneAnimations[0].fps;
        globalData.animLength = gpuSkinning.model.boneAnimations[0].length;

        // Bone Animation Matrices
        List <GPUSkinning_ComputeShader_Matrix> cbMatricesList = new List <GPUSkinning_ComputeShader_Matrix>();
        int matIndex = 0;

        GPUSkinningUtil.ExtractBoneAnimMatrix(
            gpuSkinning,
            gpuSkinning.model.boneAnimations[0],
            (animMat, hierarchyMat) =>
        {
            var matData = new GPUSkinning_ComputeShader_Matrix();
            matData.mat = animMat;
            cbMatricesList.Add(matData);
            ++matIndex;
        },
            (frameIndex) =>
        {
            if (frameIndex == 0)
            {
                globalData.oneFrameMatricesStride = matIndex;
            }
        }
            );

        matricesComputeBuffer = new ComputeBuffer(cbMatricesList.Count, GPUSkinning_ComputeShader_Matrix.size);
        matricesComputeBuffer.SetData(cbMatricesList.ToArray());

        // Global Data
        globalDataComputeBuffer = new ComputeBuffer(1, GPUSkinning_CompueteShader_GlobalData.size);
        globalDataComputeBuffer.SetData(new GPUSkinning_CompueteShader_GlobalData[] { globalData });

        // Procedural Model Data
        var modelData = new GPUSkinning_ComputeShader_Model[numProceduralInstances];
        int numProceduralModelsPerRow = (int)Mathf.Sqrt(numProceduralInstances);

        for (int i = 0; i < numProceduralInstances; ++i)
        {
            var aModelData = new GPUSkinning_ComputeShader_Model();
            int row        = i / numProceduralModelsPerRow;
            int col        = i - row * numProceduralModelsPerRow;
            aModelData.pos  = new Vector3(-row * proceduralModelGap, -1, -col * proceduralModelGap);
            aModelData.time = Random.Range(0.0f, 10.0f);
            modelData[i]    = aModelData;
        }
        modelDataComputeBuffer = new ComputeBuffer(numProceduralInstances, GPUSkinning_ComputeShader_Model.size);
        modelDataComputeBuffer.SetData(modelData);

        // Draw Procedural Model
        GPUSkinning_Camera.instance.onPostRender += Draw;
    }