Ejemplo n.º 1
0
        protected override void OnMeshInstanceCreated()
        {
            meshAssetRadius = meshAsset.bounds.extents.magnitude;            // conservative

            if (meshBuffers == null)
            {
                meshBuffers = new MeshBuffers(meshInstance);
            }
            else
            {
                meshBuffers.LoadFrom(meshInstance);
            }

            if (meshAdjacency == null)
            {
                meshAdjacency = new MeshAdjacency(meshBuffers);
            }
            else
            {
                meshAdjacency.LoadFrom(meshBuffers);
            }

            if (meshIslands == null)
            {
                meshIslands = new MeshIslands(meshAdjacency);
            }
            else
            {
                meshIslands.LoadFrom(meshAdjacency);
            }

            DiscoverSkinningBone();
        }
        public void LoadFrom(MeshAdjacency meshAdjacency)
        {
            vertexCount = meshAdjacency.vertexCount;

            ArrayUtils.ResizeCheckedIfLessThan(ref vertexIsland, vertexCount);

            islandCount = 0;
            islandVertices.Allocate(vertexCount, vertexCount);

            for (int i = 0; i != vertexCount; i++)
            {
                vertexIsland[i] = i;
                islandVertices.Append(i, i);
            }

            for (int i = 0; i != vertexCount; i++)
            {
                foreach (int j in meshAdjacency.vertexVertices[i])
                {
                    int islandA = vertexIsland[i];
                    int islandB = vertexIsland[j];
                    if (islandB != islandA)
                    {
                        // update vertex->island lookup
                        foreach (int vertex in islandVertices[islandB])
                        {
                            vertexIsland[vertex] = islandA;
                        }

                        // move adjacent island
                        islandVertices.AppendMove(islandA, islandB);
                    }
                }
            }

            // remove empty islands
            for (int i = 0; i != vertexCount; i++)
            {
                if (islandVertices.lists[i].size != 0)
                {
                    islandVertices.lists[islandCount++] = islandVertices.lists[i];
                }
            }

            for (int i = islandCount; i != vertexCount; i++)
            {
                islandVertices.lists[i].head = -1;
                islandVertices.lists[i].size = 0;
            }

            // update vertex->island lookup
            for (int i = 0; i != islandCount; i++)
            {
                foreach (int vertex in islandVertices[i])
                {
                    vertexIsland[vertex] = i;
                }
            }
        }
        private int InternalValence(MeshAdjacency meshAdjacency, int i)
        {
            int n = 0;

            foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]])
            {
                if (internalFromExternal[k] != -1)
                {
                    n++;
                }
            }
            return(n);
        }
        static int[] BuildPairsFromIndices(MeshAdjacency meshAdjacency, int[] indices)
        {
            var pairs = new List <int>();

            foreach (int i in indices)
            {
                foreach (int j in meshAdjacency.vertexVertices[i])
                {
                    pairs.Add(i);
                    pairs.Add(j);
                }
            }
            return(pairs.ToArray());
        }
Ejemplo n.º 5
0
        static void ImportClip(SkinDeformationClip clip)
        {
            try
            {
                var progressTitle = "Importing '" + clip.name + "'";
                var progressIndex = 0;
                var progressCount = 4.0f;

                EditorUtility.DisplayProgressBar(progressTitle, "Loading assets", progressIndex++ / progressCount);

                var sourceObjPaths     = null as string[];
                var sourceMeshAssets   = null as Mesh[];
                var sourceAlbedoAssets = null as Texture2D[];

                int frameCount       = 0;
                int frameVertexCount = 0;

                var useExternalLoader = (clip.importSettings.readFrom == SkinDeformationClip.InputType.ExternalObj);
                if (useExternalLoader)
                {
                    sourceObjPaths = GetFilesAtPath(clip.importSettings.externalObjPath, clip.importSettings.externalObjPattern);
                    Debug.Assert(sourceObjPaths.Length > 0, "source .obj count == 0 (check import settings)");

                    using (var nativeMesh = NativeMeshObjLoader.Parse(sourceObjPaths[0]))
                    {
                        frameCount       = sourceObjPaths.Length;
                        frameVertexCount = nativeMesh.vertexCount;
                    }
                }
                else
                {
                    sourceMeshAssets = GetAssetsAtPath <Mesh>(clip.importSettings.meshAssetPath, clip.importSettings.meshAssetPrefix);
                    Debug.Assert(sourceMeshAssets.Length > 0, "mesh count == 0 (check import settings)");

                    sourceAlbedoAssets = GetAssetsAtPath <Texture2D>(clip.importSettings.albedoAssetPath, clip.importSettings.albedoAssetPrefix);
                    if (sourceAlbedoAssets.Length != sourceMeshAssets.Length)
                    {
                        sourceAlbedoAssets = null;
                        Debug.LogWarning("mesh asset count != albedo asset count: skipping albedos");
                    }

                    frameCount       = sourceMeshAssets.Length;
                    frameVertexCount = sourceMeshAssets[0].vertexCount;
                }

                int frameFittedWeightsCount = 0;                // modified later
                var frames = new SkinDeformation[frameCount];

                int subframeCount = frameCount - 1;
                var subframes     = new SkinDeformationClip.Subframe[subframeCount];

                MeshBuffers buffersFrame0 = new MeshBuffers(frameVertexCount);
                MeshBuffers buffersFrameX = new MeshBuffers(frameVertexCount);
                MeshBuffers buffersTarget = buffersFrame0;

                if (clip.importSettings.transferTarget != null)
                {
                    buffersTarget = new MeshBuffers(clip.importSettings.transferTarget);
                }

                MeshAdjacency weldedAdjacency = new MeshAdjacency(buffersTarget, clip.importSettings.solveWelded);

                EditorUtility.DisplayProgressBar(progressTitle, "Importing frames", progressIndex++ / progressCount);
                {
                    var sourceRotation = Quaternion.Euler(clip.importSettings.applyRotation);
                    var sourceScale    = clip.importSettings.applyScale;

                    if (useExternalLoader)
                    {
                        using (var nativeMesh = NativeMeshObjLoader.Parse(sourceObjPaths[0]))
                        {
                            buffersFrame0.LoadFrom(nativeMesh);
                            buffersFrame0.ApplyRotation(sourceRotation);
                            buffersFrame0.ApplyScale(sourceScale);
                        }
                    }
                    else
                    {
                        buffersFrame0.LoadFrom(sourceMeshAssets[0]);
                        buffersFrame0.ApplyRotation(sourceRotation);
                        buffersFrame0.ApplyScale(sourceScale);
                    }

                    var denoiseIndices = ResolveIndexArrayFromVertexSelectionArray(clip.importSettings.denoiseRegions, weldedAdjacency);
                    var denoiseFactor  = clip.importSettings.denoiseStrength;

                    if (denoiseFactor < float.Epsilon)
                    {
                        denoiseIndices = new int[0];
                    }

                    var transplantIndices = ResolveIndexArrayFromVertexSelectionArray(clip.importSettings.transplantRegions, weldedAdjacency);
                    var transplantFactor  = clip.importSettings.transplantStrength;
                    var transplantSource  = clip.importSettings.transferTarget;

                    if (transplantFactor < float.Epsilon || transplantSource == null)
                    {
                        transplantIndices = new int[0];
                    }

#if SOLVE_FULL_LAPLACIAN
                    var laplacianConstraintCount   = frameVertexCount;
                    var laplacianConstraintIndices = null as int[];

                    unsafe
                    {
                        using (var laplacianFreeVertexMap = new UnsafeArrayBool(frameVertexCount))
                        {
                            laplacianFreeVertexMap.Clear(false);

                            for (int k = 0; k != denoiseIndices.Length; k++)
                            {
                                if (laplacianFreeVertexMap.val[denoiseIndices[k]] == false)
                                {
                                    laplacianFreeVertexMap.val[denoiseIndices[k]] = true;
                                    laplacianConstraintCount--;
                                }
                            }

                            for (int k = 0; k != transplantIndices.Length; k++)
                            {
                                if (laplacianFreeVertexMap.val[transplantIndices[k]] == false)
                                {
                                    laplacianFreeVertexMap.val[transplantIndices[k]] = true;
                                    laplacianConstraintCount--;
                                }
                            }

                            laplacianConstraintIndices = new int[laplacianConstraintCount];

                            for (int i = 0, k = 0; i != frameVertexCount; i++)
                            {
                                if (laplacianFreeVertexMap.val[i] == false)
                                {
                                    laplacianConstraintIndices[k++] = i;
                                }
                            }
                        }
                    }
#else
                    var laplacianROIIndices      = denoiseIndices.Union(transplantIndices).ToArray();
                    var laplacianConstraintCount = frameVertexCount - laplacianROIIndices.Length;
#endif

#if SOLVE_FULL_LAPLACIAN
                    var meshLaplacianTransform = null as MeshLaplacianTransform;
#else
                    var meshLaplacianTransform = null as MeshLaplacianTransformROI;
#endif
                    var meshLaplacian         = new MeshLaplacian();
                    var meshLaplacianDenoised = new MeshLaplacian();

                    var transplantBuffers   = new MeshBuffers(frameVertexCount);
                    var transplantLaplacian = new MeshLaplacian();

                    var laplacianResolve = (laplacianConstraintCount < frameVertexCount);
                    if (laplacianResolve)
                    {
#if SOLVE_FULL_LAPLACIAN
                        meshLaplacianTransform = new MeshLaplacianTransform(weldedAdjacency, laplacianConstraintIndices);
#else
                        meshLaplacianTransform = new MeshLaplacianTransformROI(weldedAdjacency, laplacianROIIndices, 0);
                        {
                            for (int i = 0; i != denoiseIndices.Length; i++)
                            {
                                denoiseIndices[i] = meshLaplacianTransform.internalFromExternal[denoiseIndices[i]];
                            }
                            for (int i = 0; i != transplantIndices.Length; i++)
                            {
                                transplantIndices[i] = meshLaplacianTransform.internalFromExternal[transplantIndices[i]];
                            }
                        }
#endif
                        meshLaplacianTransform.ComputeMeshLaplacian(meshLaplacianDenoised, buffersFrame0);

                        if (transplantIndices.Length > 0 && transplantSource != null)
                        {
                            transplantBuffers.LoadFrom(transplantSource);
                            meshLaplacianTransform.ComputeMeshLaplacian(transplantLaplacian, transplantBuffers);
                        }
                    }

                    for (int i = 0; i != frameCount; i++)
                    {
                        EditorUtility.DisplayProgressBar(progressTitle, "Importing frames", (progressIndex - 1 + ((float)i / frameCount)) / progressCount);

                        if (useExternalLoader)
                        {
                            using (var nativeMesh = NativeMeshObjLoader.Parse(sourceObjPaths[i]))
                            {
                                buffersFrameX.LoadFrom(nativeMesh);
                                buffersFrameX.ApplyRotation(sourceRotation);
                                buffersFrameX.ApplyScale(sourceScale);
                            }
                        }
                        else
                        {
                            buffersFrameX.LoadFrom(sourceMeshAssets[i]);
                            buffersFrameX.ApplyRotation(sourceRotation);
                            buffersFrameX.ApplyScale(sourceScale);
                        }

                        if (laplacianResolve)
                        {
                            meshLaplacianTransform.ComputeMeshLaplacian(meshLaplacian, buffersFrameX);

                            double historyFactor = denoiseFactor;
                            foreach (int j in denoiseIndices)
                            {
                                double dx = denoiseFactor * meshLaplacianDenoised.vertexDifferentialX[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialX[j];
                                double dy = denoiseFactor * meshLaplacianDenoised.vertexDifferentialY[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialY[j];
                                double dz = denoiseFactor * meshLaplacianDenoised.vertexDifferentialZ[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialZ[j];
                                meshLaplacian.vertexDifferentialX[j]         = dx;
                                meshLaplacian.vertexDifferentialY[j]         = dy;
                                meshLaplacian.vertexDifferentialZ[j]         = dz;
                                meshLaplacianDenoised.vertexDifferentialX[j] = dx;
                                meshLaplacianDenoised.vertexDifferentialY[j] = dy;
                                meshLaplacianDenoised.vertexDifferentialZ[j] = dz;
                            }

                            foreach (int j in transplantIndices)
                            {
                                meshLaplacian.vertexDifferentialX[j] = transplantFactor * transplantLaplacian.vertexDifferentialX[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialX[j];
                                meshLaplacian.vertexDifferentialY[j] = transplantFactor * transplantLaplacian.vertexDifferentialY[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialY[j];
                                meshLaplacian.vertexDifferentialZ[j] = transplantFactor * transplantLaplacian.vertexDifferentialZ[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialZ[j];
                            }

                            meshLaplacianTransform.ResolveMeshBuffers(buffersFrameX, meshLaplacian);

                            buffersFrameX.RecalculateNormals(weldedAdjacency);
                            buffersFrameX.ApplyWeldedChanges(weldedAdjacency);
                        }

                        frames[i].SetAlbedo((sourceAlbedoAssets != null) ? sourceAlbedoAssets[i] : null);
                        frames[i].SetDeltas(buffersFrame0, buffersFrameX);

                        var targetVertexCount = buffersFrame0.vertexCount;
                        if (targetVertexCount != buffersFrameX.vertexCount)
                        {
                            Debug.LogWarning("frame " + i + " has different vertexCount (" + buffersFrameX.vertexCount + " vs " + targetVertexCount + " in frame 0)");
                        }
                    }

                    for (int i = 0; i != subframeCount; i++)
                    {
                        subframes[i].frameIndexLo = i;
                        subframes[i].frameIndexHi = i + 1;
                        subframes[i].fractionLo   = 0.0f;
                        subframes[i].fractionHi   = 1.0f;
                    }

                    if (clip.importSettings.keyframes)
                    {
                        ImportFrameIntervalsFromCSV(clip.importSettings.keyframesCSV, frameCount - 1, ref subframeCount, ref subframes);
                    }
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Retargeting frames", progressIndex++ / progressCount);
                {
                    switch (clip.importSettings.transferMode)
                    {
                    case SkinDeformationClip.TransferMode.PassThrough:
                    {
                        // clean copy
                    }
                    break;

                    case SkinDeformationClip.TransferMode.PassThroughWithFirstFrameDelta:
                    {
                        buffersFrameX.LoadFrom(clip.importSettings.transferTarget);

                        Vector3 anchorOrigin = buffersFrame0.CalcMeshCenter();
                        Vector3 anchorTarget = buffersFrameX.CalcMeshCenter();
                        Vector3 offsetTarget = anchorOrigin - anchorTarget;
                        for (int j = 0; j != frameVertexCount; j++)
                        {
                            buffersFrameX.vertexPositions[j] += offsetTarget;
                        }

                        SkinDeformation firstFrameDelta;
                        firstFrameDelta = new SkinDeformation();
                        firstFrameDelta.SetDeltas(buffersFrameX, buffersFrame0);

                        for (int i = 0; i != frameCount; i++)
                        {
                            EditorUtility.DisplayProgressBar(progressTitle, "Retargeting frames", (progressIndex - 1 + ((float)i / frameCount)) / progressCount);

                            for (int j = 0; j != frameVertexCount; j++)
                            {
                                frames[i].deltaPositions[j] += firstFrameDelta.deltaPositions[j];
                                frames[i].deltaNormals[j]   += firstFrameDelta.deltaNormals[j];
                            }
                        }
                    }
                    break;
                    }
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Fitting frames to blend shapes", progressIndex++ / progressCount);
                {
                    if (clip.importSettings.fitToBlendShapes)
                    {
                        frameFittedWeightsCount = clip.importSettings.transferTarget.blendShapeCount;
                    }
                    else
                    {
                        frameFittedWeightsCount = 0;
                    }

                    for (int i = 0; i != frameCount; i++)
                    {
                        frames[i].fittedWeights = new float[frameFittedWeightsCount];
                    }

                    if (frameFittedWeightsCount > 0)
                    {
                        var blendShapeIndicesCommaSep = clip.importSettings.fittedIndices;
                        var blendShapeIndices         = Array.ConvertAll <string, int>(blendShapeIndicesCommaSep.Split(','), int.Parse);

                        SkinDeformationFitting.FitFramesToBlendShapes(frames, clip.importSettings.transferTarget, blendShapeIndices, clip.importSettings.fittingMethod, clip.importSettings.fittingParam);
                    }
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Saving binary", progressIndex++ / progressCount);
                {
                    clip.lastImport              = clip.importSettings.Clone();
                    clip.frameCount              = frameCount;
                    clip.frameVertexCount        = frameVertexCount;
                    clip.frameFittedWeightsCount = frameFittedWeightsCount;
                    clip.frames = frames;
                    clip.framesContainAlbedo        = (frames[0].albedo != null);
                    clip.framesContainDeltas        = (frames[0].deltaPositions.Length > 0);
                    clip.framesContainFittedWeights = (frames[0].fittedWeights.Length > 0);
                    clip.subframeCount = subframeCount;
                    clip.subframes     = subframes;
                    clip.version++;

                    EditorUtility.SetDirty(clip);
                    AssetDatabase.SaveAssets();

                    clip.SaveFrameData();
                }
            }
            catch (Exception ex)
            {
                Debug.LogError(ex);
            }
            finally
            {
                EditorUtility.ClearProgressBar();
            }
        }
Ejemplo n.º 6
0
        public static int[] ResolveIndexArrayFromVertexSelectionArray(TextAsset[] vertexSelections, MeshAdjacency weldedAdjacency = null)
        {
            var indices = new int[0];

            foreach (var vertexSelection in vertexSelections)
            {
                indices = indices.Union(ResolveIndexArrayFromVertexSelection(vertexSelection, weldedAdjacency)).ToArray();
            }
            return(indices);
        }
Ejemplo n.º 7
0
        public static int[] ResolveIndexArrayFromVertexSelection(TextAsset vertexSelection, MeshAdjacency weldedAdjacency = null)
        {
            if (vertexSelection == null)
            {
                return(new int[0]);
            }

            var indicesCommaSep = vertexSelection.text.Trim('[', ']');

            if (indicesCommaSep.Length > 0)
            {
                var parsed = Array.ConvertAll <string, int>(indicesCommaSep.Split(','), int.Parse);
                if (weldedAdjacency != null)
                {
                    return(parsed.Select(i => weldedAdjacency.vertexResolve[i]).Distinct().ToArray());
                }
                else
                {
                    return(parsed);
                }
            }
            else
            {
                return(new int[0]);
            }
        }
        public void BuildFrom(MeshAdjacency meshAdjacency, int[] constraintIndices)
        {
            vertexCount = meshAdjacency.vertexCount;

            this.constraintIndices = constraintIndices.Clone() as int[];
            this.constraintWeight  = 1.0;

            // count unconstrained laplacian non-zero fields
            int nzmax = vertexCount;

            for (int i = 0; i != vertexCount; i++)
            {
                nzmax += meshAdjacency.vertexVertices.lists[i].size;
            }

            // build Ls
            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Ls", 0.0f);
            var Ls_storage = new CoordinateStorage <double>(vertexCount, vertexCount, nzmax);

            for (int i = 0; i != vertexCount; i++)            // D
            {
                //TODO proper fix
                //Ls_storage.At(i, i, meshAdjacency.vertexVertices.lists[i].size);
                Ls_storage.At(i, i, Mathf.Max(1, meshAdjacency.vertexVertices.lists[i].size));
            }
            for (int i = 0; i != vertexCount; i++)            // A
            {
                foreach (var j in meshAdjacency.vertexVertices[i])
                {
                    Ls_storage.At(i, j, -1.0);
                }
            }
            Ls = Converter.ToCompressedColumnStorage(Ls_storage) as SparseMatrix;

            // build Lc
            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Lc", 0.0f);
            var Lc_storage = new CoordinateStorage <double>(vertexCount + constraintIndices.Length, vertexCount, nzmax + constraintIndices.Length);

            for (int i = 0; i != vertexCount; i++)
            {
                //TODO proper fix
                //Lc_storage.At(i, i, meshAdjacency.vertexVertices.lists[i].size);
                Lc_storage.At(i, i, Mathf.Max(1, meshAdjacency.vertexVertices.lists[i].size));
            }
            for (int i = 0; i != vertexCount; i++)
            {
                foreach (var j in meshAdjacency.vertexVertices[i])
                {
                    Lc_storage.At(i, j, -1.0);
                }
            }
            for (int i = 0; i != constraintIndices.Length; i++)
            {
                Lc_storage.At(vertexCount + i, constraintIndices[i], constraintWeight);
            }
            Lc = Converter.ToCompressedColumnStorage(Lc_storage) as SparseMatrix;

            // build LcT
            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT", 0.0f);
            LcT = Lc.Transpose() as SparseMatrix;

            // build LcT_Lc
            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc", 0.0f);
            LcT_Lc = LcT.Multiply(Lc) as SparseMatrix;

            // build LcT_Lc_chol
            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc_chol", 0.0f);
            LcT_Lc_chol = SparseCholesky.Create(LcT_Lc, ColumnOrdering.MinimumDegreeAtPlusA);

            // done
            EditorUtilityProxy.ClearProgressBar();
        }
 public MeshLaplacianTransform(MeshAdjacency meshAdjacency, int[] constraintIndices)
 {
     BuildFrom(meshAdjacency, constraintIndices);
 }
        public void BuildFrom(MeshAdjacency meshAdjacency, int[] roiIndices, int roiBoundaryLevels, int[] roiConstraintIndices = null)
        {
            unsafe
            {
                using (var visited = new UnsafeArrayBool(meshAdjacency.vertexCount))
                    using (var visitedBoundary = new UnsafeArrayBool(meshAdjacency.vertexCount))
                        using (var visitor = new UnsafeBFS(meshAdjacency.vertexCount))
                        {
                            // find boundary
                            visited.Clear(false);
                            visitedBoundary.Clear(false);
                            visitor.Clear();

                            int visitedCount         = 0;
                            int visitedBoundaryCount = 0;

                            foreach (int i in roiIndices)
                            {
                                visited.val[i] = true;
                                visitedCount++;
                                visitor.Ignore(i);
                            }

                            foreach (int i in roiIndices)
                            {
                                foreach (var j in meshAdjacency.vertexVertices[i])
                                {
                                    visitor.Insert(j);
                                }
                            }

                            // step boundary
                            while (visitor.MoveNext())
                            {
                                int i = visitor.position;

                                visited.val[i] = true;
                                visitedCount++;
                                visitedBoundary.val[i] = true;
                                visitedBoundaryCount++;

                                if (visitor.depth < roiBoundaryLevels)
                                {
                                    foreach (var j in meshAdjacency.vertexVertices[i])
                                    {
                                        visitor.Insert(j);
                                    }
                                }
                            }

                            // add constraints
                            if (roiConstraintIndices != null)
                            {
                                foreach (int i in roiConstraintIndices)
                                {
                                    if (visited.val[i])
                                    {
                                        if (visitedBoundary.val[i] == false)
                                        {
                                            visitedBoundary.val[i] = true;
                                            visitedBoundaryCount++;
                                        }
                                    }
                                    else
                                    {
                                        Debug.LogWarning("ignoring user constraint outside ROI: vertex " + i);
                                    }
                                }
                            }

                            // build translations
                            internalCount = 0;
                            externalCount = meshAdjacency.vertexCount;

                            internalFromExternal = new int[externalCount];
                            externalFromInternal = new int[visitedCount];

                            for (int i = 0; i != meshAdjacency.vertexCount; i++)
                            {
                                if (visited.val[i])
                                {
                                    int internalIndex = internalCount++;
                                    externalFromInternal[internalIndex] = i;
                                    internalFromExternal[i]             = internalIndex;
                                }
                                else
                                {
                                    internalFromExternal[i] = -1;
                                }
                            }

                            // find constraint indices
                            constraintIndices = new int[visitedBoundaryCount];
                            constraintWeight  = 1.0;

                            int constraintCount = 0;
                            for (int i = 0; i != internalCount; i++)
                            {
                                if (visitedBoundary.val[externalFromInternal[i]])
                                {
                                    constraintIndices[constraintCount++] = i;
                                }
                            }

                            // count unconstrained laplacian non-zero fields
                            int nzmax = internalCount;
                            for (int i = 0; i != internalCount; i++)
                            {
                                nzmax += InternalValence(meshAdjacency, i);
                            }

                            // build Ls
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Ls", 0.0f);
                            var Ls_storage = new CoordinateStorage <double>(internalCount, internalCount, nzmax);
                            for (int i = 0; i != internalCount; i++)            // D
                            {
                                //TODO proper fix
                                //Ls_storage.At(i, i, InternalValence(meshAdjacency, i));
                                Ls_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i)));
                            }
                            for (int i = 0; i != internalCount; i++)            // A
                            {
                                foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]])
                                {
                                    int j = internalFromExternal[k];
                                    if (j != -1)
                                    {
                                        Ls_storage.At(i, j, -1.0);
                                    }
                                }
                            }
                            Ls = Converter.ToCompressedColumnStorage(Ls_storage) as SparseMatrix;

                            // build Lc
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Lc", 0.0f);
                            var Lc_storage = new CoordinateStorage <double>(internalCount + constraintCount, internalCount, nzmax + constraintCount);
                            for (int i = 0; i != internalCount; i++)
                            {
                                //TODO proper fix
                                //Lc_storage.At(i, i, InternalValence(meshAdjacency, i));
                                Lc_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i)));
                            }
                            for (int i = 0; i != internalCount; i++)
                            {
                                foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]])
                                {
                                    int j = internalFromExternal[k];
                                    if (j != -1)
                                    {
                                        Lc_storage.At(i, j, -1.0);
                                    }
                                }
                            }
                            for (int i = 0; i != constraintIndices.Length; i++)
                            {
                                Lc_storage.At(internalCount + i, constraintIndices[i], constraintWeight);
                            }
                            Lc = Converter.ToCompressedColumnStorage(Lc_storage) as SparseMatrix;

                            // build LcT
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT", 0.0f);
                            LcT = Lc.Transpose() as SparseMatrix;

                            // build LcT_Lc
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc", 0.0f);
                            LcT_Lc = LcT.Multiply(Lc) as SparseMatrix;

                            // build LcT_Lc_chol
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc_chol", 0.0f);
                            LcT_Lc_chol = SparseCholesky.Create(LcT_Lc, ColumnOrdering.MinimumDegreeAtPlusA);

                            // done
                            EditorUtilityProxy.ClearProgressBar();
                        }
            }
        }
 public MeshLaplacianTransformROI(MeshAdjacency meshAdjacency, int[] roiIndices, int roiConstraintBoundary, int[] roiConstraintIndices = null)
 {
     BuildFrom(meshAdjacency, roiIndices, roiConstraintBoundary, roiConstraintIndices);
 }
 public MeshIslands(MeshAdjacency meshAdjacency)
 {
     LoadFrom(meshAdjacency);
 }
        public void OnDrawGizmosSelected()
        {
            var activeGO = UnityEditor.Selection.activeGameObject;

            if (activeGO == null)
            {
                return;
            }
            if (activeGO != this.gameObject && activeGO.GetComponent <SkinAttachment>() == null)
            {
                return;
            }

            Gizmos.matrix = this.transform.localToWorldMatrix;

            if (showWireframe)
            {
                Profiler.BeginSample("show-wire");
                {
                    var meshVertexCount = meshBuffers.vertexCount;
                    var meshPositions   = meshBuffers.vertexPositions;
                    var meshNormals     = meshBuffers.vertexNormals;

                    Gizmos.color = Color.Lerp(Color.clear, Color.green, 0.25f);
                    Gizmos.DrawWireMesh(meshBakedOrAsset, 0);

                    Gizmos.color = Color.red;
                    for (int i = 0; i != meshVertexCount; i++)
                    {
                        Gizmos.DrawRay(meshPositions[i], meshNormals[i] * 0.001f);                        // 1mm
                    }
                }
                Profiler.EndSample();
            }

            if (showUVSeams)
            {
                Profiler.BeginSample("show-seams");
                {
                    Gizmos.color = Color.cyan;
                    var weldedAdjacency = new MeshAdjacency(meshBuffers, true);
                    for (int i = 0; i != weldedAdjacency.vertexCount; i++)
                    {
                        if (weldedAdjacency.vertexWelded.GetCount(i) > 0)
                        {
                            bool seam = false;
                            foreach (var j in weldedAdjacency.vertexVertices[i])
                            {
                                if (weldedAdjacency.vertexWelded.GetCount(j) > 0)
                                {
                                    seam = true;
                                    if (i < j)
                                    {
                                        Gizmos.DrawLine(meshBuffers.vertexPositions[i], meshBuffers.vertexPositions[j]);
                                    }
                                }
                            }
                            if (!seam)
                            {
                                Gizmos.color = Color.magenta;
                                Gizmos.DrawRay(meshBuffers.vertexPositions[i], meshBuffers.vertexNormals[i] * 0.003f);
                                Gizmos.color = Color.cyan;
                            }
                        }
                    }
                }
                Profiler.EndSample();
            }

            if (showResolved)
            {
                Profiler.BeginSample("show-resolve");
                unsafe
                {
                    var attachmentIndex = 0;
                    var attachmentCount = attachData.itemCount;

                    using (var resolvedPositions = new UnsafeArrayVector3(attachmentCount))
                        using (var resolvedNormals = new UnsafeArrayVector3(attachmentCount))
                        {
                            var resolveTransform = Matrix4x4.identity;
                            var resolveJob       = ScheduleResolve(attachmentIndex, attachmentCount, ref resolveTransform, resolvedPositions.val, resolvedNormals.val);

                            JobHandle.ScheduleBatchedJobs();

                            resolveJob.Complete();

                            Gizmos.color = Color.yellow;
                            Vector3 size = 0.0002f * Vector3.one;

                            for (int i = 0; i != attachmentCount; i++)
                            {
                                Gizmos.DrawCube(resolvedPositions.val[i], size);
                            }
                        }
                }
                Profiler.EndSample();
            }
        }
Ejemplo n.º 14
0
        static void ImportClip(SkinDeformationClip clip)
        {
            var sourceObjsPreloaded = null as NativeMeshSOA[];

            try
            {
                var progressTitle = "Importing '" + clip.name + "'";
                var progressIndex = 0;
                var progressCount = 4.0f;

                EditorUtility.DisplayProgressBar(progressTitle, "Loading assets", progressIndex++ / progressCount);

                var sourceObjPaths     = null as string[];
                var sourceMeshAssets   = null as Mesh[];
                var sourceAlbedoAssets = null as Texture2D[];

                var referenceObjPath      = clip.settings.referenceObjPath;
                var referenceMeshAsset    = clip.settings.referenceMeshAsset;
                var referenceIsFirstFrame = clip.settings.referenceIsFirstFrame;

                int frameCount            = 0;
                int frameVertexCount      = 0;
                int frameFaceIndicesCount = 0;

                var useExternalLoader = (clip.settings.sourceFrom == SkinDeformationClip.SourceType.ExternalObj);
                if (useExternalLoader)
                {
                    sourceObjPaths = GetFilesAtPath(clip.settings.externalObjPath, clip.settings.externalObjPattern);
                    Debug.Assert(sourceObjPaths.Length > 0, "source obj count == 0 (check import settings)");

                    if (referenceIsFirstFrame)
                    {
                        referenceObjPath = sourceObjPaths[0];
                        sourceObjPaths   = sourceObjPaths.Skip(1).ToArray();
                    }

                    using (var nativeMesh = NativeMeshObjLoader.Parse(referenceObjPath))
                    {
                        frameCount            = sourceObjPaths.Length;
                        frameVertexCount      = nativeMesh.vertexCount;
                        frameFaceIndicesCount = nativeMesh.faceIndicesCount;
                    }

                    if (clip.settings.externalObjPreloadThreaded)
                    {
                        sharedJobData.paths         = sourceObjPaths;
                        sharedJobData.vertexAttribs = NativeMeshObjLoader.VertexAttribs.Position;                        // ignored for now
                        sharedJobData.vertexOrder   = NativeMeshObjLoader.VertexOrder.ByDefinition;
                        sharedJobData.result        = new NativeMeshSOA[sourceObjPaths.Length];
                        sharedJobData.resultTouched = new bool[sourceObjPaths.Length];

                        for (int i = 0; i != sharedJobData.result.Length; i++)
                        {
                            sharedJobData.result[i].Allocate(frameVertexCount, frameFaceIndicesCount, Allocator.Persistent);
                        }

                        unsafe
                        {
                            fixed(bool *resultTouched = sharedJobData.resultTouched)
                            {
                                var jobDef = new ObjLoaderJob();
                                var job    = jobDef.Schedule(sourceObjPaths.Length, 1);

                                JobHandle.ScheduleBatchedJobs();

                                while (true)
                                {
                                    int numTotal   = sourceObjPaths.Length;
                                    int numTouched = 0;

                                    for (int i = 0; i != numTotal; i++)
                                    {
                                        if (resultTouched[i])
                                        {
                                            numTouched++;
                                        }
                                    }

                                    EditorUtility.DisplayProgressBar(progressTitle, "Loading assets (" + numTouched + " / " + numTotal + ")", (progressIndex - 1 + ((float)numTouched / numTotal)) / progressCount);

                                    if (job.IsCompleted)
                                    {
                                        break;
                                    }
                                }

                                job.Complete();
                            }
                        }

                        sourceObjsPreloaded = sharedJobData.result;
                    }
                }
                else
                {
                    sourceMeshAssets = GetAssetsAtPath <Mesh>(clip.settings.meshAssetFolder, clip.settings.meshAssetPrefix);
                    Debug.Assert(sourceMeshAssets.Length > 0, "source mesh count == 0 (check import settings)");

                    sourceAlbedoAssets = GetAssetsAtPath <Texture2D>(clip.settings.albedoAssetFolder, clip.settings.albedoAssetPrefix);
                    if (sourceAlbedoAssets.Length != sourceMeshAssets.Length)
                    {
                        sourceAlbedoAssets = null;
                        Debug.LogWarning("source albedo count != mesh count (SKIPPING albedo assets)");
                    }

                    if (referenceIsFirstFrame)
                    {
                        referenceMeshAsset = sourceMeshAssets[0];
                        sourceMeshAssets   = sourceMeshAssets.Skip(1).ToArray();

                        if (sourceAlbedoAssets != null)
                        {
                            sourceAlbedoAssets = sourceAlbedoAssets.Skip(1).ToArray();
                        }
                    }

                    Debug.Assert(referenceMeshAsset != null);

                    frameCount       = sourceMeshAssets.Length;
                    frameVertexCount = referenceMeshAsset.vertexCount;
                }

                Debug.Log("frameCount: " + frameCount);
                Debug.Log("frameVertexCount: " + frameVertexCount);

                int frameFittedWeightsCount = 0;                // modified later
                var frames = new SkinDeformation[frameCount];

                int subframeCount = frameCount - 1;
                var subframes     = new SkinDeformationClip.Subframe[subframeCount];

                MeshBuffers meshBuffers          = new MeshBuffers(frameVertexCount);
                MeshBuffers meshBuffersReference = new MeshBuffers(frameVertexCount);

                MeshAdjacency weldedAdjacency = new MeshAdjacency(meshBuffersReference, clip.settings.solveWelded);

                EditorUtility.DisplayProgressBar(progressTitle, "Importing frames", progressIndex++ / progressCount);
                {
                    var sourceRotation = Quaternion.Euler(clip.settings.applyRotation);
                    var sourceScale    = clip.settings.applyScaling;

                    if (useExternalLoader)
                    {
                        using (var referenceObj = NativeMeshObjLoader.Parse(referenceObjPath))
                        {
                            meshBuffersReference.LoadFrom(referenceObj);
                            meshBuffersReference.ApplyRotation(sourceRotation);
                            meshBuffersReference.ApplyScale(sourceScale);
                        }
                    }
                    else
                    {
                        meshBuffersReference.LoadFrom(referenceMeshAsset);
                        meshBuffersReference.ApplyRotation(sourceRotation);
                        meshBuffersReference.ApplyScale(sourceScale);
                    }

                    var denoiseIndices = ResolveIndexArrayFromVertexSelectionArray(clip.settings.denoiseRegions, weldedAdjacency);
                    var denoiseFactor  = clip.settings.denoiseStrength;
                    if (denoiseFactor < float.Epsilon)
                    {
                        denoiseIndices = new int[0];
                    }

                    var transplantIndices = ResolveIndexArrayFromVertexSelectionArray(clip.settings.transplantRegions, weldedAdjacency);
                    var transplantFactor  = clip.settings.transplantStrength;
                    if (transplantFactor < float.Epsilon)
                    {
                        transplantIndices = new int[0];
                    }

#if SOLVE_FULL_LAPLACIAN
                    var laplacianConstraintCount   = frameVertexCount;
                    var laplacianConstraintIndices = null as int[];

                    unsafe
                    {
                        using (var laplacianFreeVertexMap = new UnsafeArrayBool(frameVertexCount))
                        {
                            laplacianFreeVertexMap.Clear(false);

                            for (int k = 0; k != denoiseIndices.Length; k++)
                            {
                                if (laplacianFreeVertexMap.val[denoiseIndices[k]] == false)
                                {
                                    laplacianFreeVertexMap.val[denoiseIndices[k]] = true;
                                    laplacianConstraintCount--;
                                }
                            }

                            for (int k = 0; k != transplantIndices.Length; k++)
                            {
                                if (laplacianFreeVertexMap.val[transplantIndices[k]] == false)
                                {
                                    laplacianFreeVertexMap.val[transplantIndices[k]] = true;
                                    laplacianConstraintCount--;
                                }
                            }

                            laplacianConstraintIndices = new int[laplacianConstraintCount];

                            for (int i = 0, k = 0; i != frameVertexCount; i++)
                            {
                                if (laplacianFreeVertexMap.val[i] == false)
                                {
                                    laplacianConstraintIndices[k++] = i;
                                }
                            }
                        }
                    }
#else
                    var laplacianROIIndices      = denoiseIndices.Union(transplantIndices).ToArray();
                    var laplacianConstraintCount = frameVertexCount - laplacianROIIndices.Length;
#endif

#if SOLVE_FULL_LAPLACIAN
                    var laplacianTransform = null as MeshLaplacianTransform;
#else
                    var laplacianTransform = null as MeshLaplacianTransformROI;
#endif
                    var meshLaplacian          = new MeshLaplacian();
                    var meshLaplacianDenoised  = new MeshLaplacian();
                    var meshLaplacianReference = new MeshLaplacian();

                    var laplacianResolve = (laplacianConstraintCount < frameVertexCount);
                    if (laplacianResolve)
                    {
#if SOLVE_FULL_LAPLACIAN
                        laplacianTransform = new MeshLaplacianTransform(weldedAdjacency, laplacianConstraintIndices);
#else
                        laplacianTransform = new MeshLaplacianTransformROI(weldedAdjacency, laplacianROIIndices, 0);
                        {
                            for (int i = 0; i != denoiseIndices.Length; i++)
                            {
                                denoiseIndices[i] = laplacianTransform.internalFromExternal[denoiseIndices[i]];
                            }
                            for (int i = 0; i != transplantIndices.Length; i++)
                            {
                                transplantIndices[i] = laplacianTransform.internalFromExternal[transplantIndices[i]];
                            }
                        }
#endif
                        laplacianTransform.ComputeMeshLaplacian(meshLaplacianDenoised, meshBuffersReference);
                        laplacianTransform.ComputeMeshLaplacian(meshLaplacianReference, meshBuffersReference);
                    }

                    for (int i = 0; i != frameCount; i++)
                    {
                        EditorUtility.DisplayProgressBar(progressTitle, "Importing frames (" + (i + 1) + " / " + frameCount + ")", (progressIndex - 1 + ((float)i / frameCount)) / progressCount);

                        if (useExternalLoader)
                        {
                            if (clip.settings.externalObjPreloadThreaded)
                            {
                                meshBuffers.LoadFrom(sourceObjsPreloaded[i]);
                            }
                            else
                            {
                                using (var sourceObj_i = NativeMeshObjLoader.Parse(sourceObjPaths[i]))
                                {
                                    meshBuffers.LoadFrom(sourceObj_i);
                                }
                            }

                            meshBuffers.ApplyRotation(sourceRotation);
                            meshBuffers.ApplyScale(sourceScale);
                        }
                        else
                        {
                            meshBuffers.LoadFrom(sourceMeshAssets[i]);
                            meshBuffers.ApplyRotation(sourceRotation);
                            meshBuffers.ApplyScale(sourceScale);
                        }

                        if (meshBuffers.vertexCount != frameVertexCount)
                        {
                            Debug.LogWarning("frame " + i + " has " + meshBuffers.vertexCount + " vertices (expected " + frameVertexCount + ")");
                        }

                        if (laplacianResolve)
                        {
                            laplacianTransform.ComputeMeshLaplacian(meshLaplacian, meshBuffers);

                            double historyFactor = denoiseFactor;
                            foreach (int j in denoiseIndices)
                            {
                                double dx = denoiseFactor * meshLaplacianDenoised.vertexDifferentialX[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialX[j];
                                double dy = denoiseFactor * meshLaplacianDenoised.vertexDifferentialY[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialY[j];
                                double dz = denoiseFactor * meshLaplacianDenoised.vertexDifferentialZ[j] + (1.0 - denoiseFactor) * meshLaplacian.vertexDifferentialZ[j];
                                meshLaplacian.vertexDifferentialX[j]         = dx;
                                meshLaplacian.vertexDifferentialY[j]         = dy;
                                meshLaplacian.vertexDifferentialZ[j]         = dz;
                                meshLaplacianDenoised.vertexDifferentialX[j] = dx;
                                meshLaplacianDenoised.vertexDifferentialY[j] = dy;
                                meshLaplacianDenoised.vertexDifferentialZ[j] = dz;
                            }

                            foreach (int j in transplantIndices)
                            {
                                meshLaplacian.vertexDifferentialX[j] = transplantFactor * meshLaplacianReference.vertexDifferentialX[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialX[j];
                                meshLaplacian.vertexDifferentialY[j] = transplantFactor * meshLaplacianReference.vertexDifferentialY[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialY[j];
                                meshLaplacian.vertexDifferentialZ[j] = transplantFactor * meshLaplacianReference.vertexDifferentialZ[j] + (1.0 - transplantFactor) * meshLaplacian.vertexDifferentialZ[j];
                            }

                            laplacianTransform.ResolveMeshBuffers(meshBuffers, meshLaplacian);

                            meshBuffers.RecalculateNormals(weldedAdjacency);
                            meshBuffers.ApplyWeldedChanges(weldedAdjacency);
                        }

                        frames[i].SetAlbedo(sourceAlbedoAssets != null ? sourceAlbedoAssets[i] : null);
                        frames[i].SetDeltas(meshBuffersReference, meshBuffers);
                    }

                    for (int i = 0; i != subframeCount; i++)
                    {
                        subframes[i].frameIndexLo = i;
                        subframes[i].frameIndexHi = i + 1;
                        subframes[i].fractionLo   = 0.0f;
                        subframes[i].fractionHi   = 1.0f;
                    }

                    if (clip.settings.keyframes)
                    {
                        ImportFrameIntervalsFromCSV(clip.settings.keyframesCSV, frameCount - 1, ref subframeCount, ref subframes);
                    }
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Transferring frames", progressIndex++ / progressCount);
                if (clip.settings.transferTarget != null)
                {
                    var targetBuffers       = new MeshBuffers(clip.settings.transferTarget);
                    var targetVertexCount   = targetBuffers.vertexCount;
                    var targetVertexResolve = new int[targetVertexCount];

                    Debug.LogFormat("transferMode: {0}", clip.settings.transferMode);
                    Debug.LogFormat("targetVertexCount: {0}", targetVertexCount);

                    switch (clip.settings.transferMode)
                    {
                    case SkinDeformationClip.ImportSettings.TransferMode.ByVertexIndex:
                    {
                        Debug.Assert(frameVertexCount == targetVertexCount, "target vertex count does not match reference vertex count");
                        for (int i = 0; i != targetVertexCount; i++)
                        {
                            targetVertexResolve[i] = i;
                        }
                    }
                    break;

                    case SkinDeformationClip.ImportSettings.TransferMode.ByVertexPosition:
                    {
                        var referenceBSP             = new KdTree3(meshBuffersReference.vertexPositions, meshBuffersReference.vertexCount);
                        var referenceDelta2Max       = 0.0f;
                        var referenceDelta2Count     = 0;
                        var referenceDelta2Threshold = 1e-10f;

                        for (int i = 0; i != targetVertexCount; i++)
                        {
                            targetVertexResolve[i] = referenceBSP.FindNearest(ref targetBuffers.vertexPositions[i]);
                        }

                        for (int i = 0; i != targetVertexCount; i++)
                        {
                            Vector3 posTarget    = targetBuffers.vertexPositions[i];
                            Vector3 posReference = meshBuffersReference.vertexPositions[targetVertexResolve[i]];

                            var delta2 = Vector3.SqrMagnitude(posTarget - posReference);
                            if (delta2 > referenceDelta2Threshold)
                            {
                                referenceDelta2Max = Mathf.Max(delta2, referenceDelta2Max);
                                referenceDelta2Count++;
                            }
                        }

                        if (referenceDelta2Count > 0)
                        {
                            Debug.LogWarning("some (" + referenceDelta2Count + ") target vertices were far from reference (max delta: " + referenceDelta2Max + ")");
                        }
                    }
                    break;
                    }

                    var targetDeltaPositions = new Vector3[targetVertexCount];
                    var targetDeltaNormals   = new Vector3[targetVertexCount];

                    for (int frameIndex = 0; frameIndex != frameCount; frameIndex++)
                    {
                        EditorUtility.DisplayProgressBar(progressTitle, "Transferring frames (" + (frameIndex + 1) + " / " + frameCount + ")", (progressIndex - 1 + ((float)frameIndex / frameCount)) / progressCount);

                        for (int i = 0; i != targetVertexCount; i++)
                        {
                            int j = targetVertexResolve[i];                            // reference index

                            targetDeltaPositions[i] = frames[frameIndex].deltaPositions[j];
                            targetDeltaNormals[i]   = frames[frameIndex].deltaNormals[j];
                        }

                        ArrayUtils.ResizeChecked(ref frames[frameIndex].deltaPositions, targetVertexCount);
                        ArrayUtils.ResizeChecked(ref frames[frameIndex].deltaNormals, targetVertexCount);

                        ArrayUtils.CopyChecked(targetDeltaPositions, ref frames[frameIndex].deltaPositions, targetVertexCount);
                        ArrayUtils.CopyChecked(targetDeltaNormals, ref frames[frameIndex].deltaNormals, targetVertexCount);
                    }

                    frameVertexCount = targetVertexCount;

                    /*
                     * switch (clip.importSettings.transferMode)
                     * {
                     *      case SkinDeformationClip.TransferMode.PassThrough:
                     *              {
                     *                      // clean copy
                     *              }
                     *              break;
                     *
                     *      case SkinDeformationClip.TransferMode.PassThroughWithFirstFrameDelta:
                     *              {
                     *                      buffersFrameX.LoadFrom(clip.importSettings.transferTarget);
                     *
                     *                      Vector3 anchorOrigin = buffersFrame0.CalcMeshCenter();
                     *                      Vector3 anchorTarget = buffersFrameX.CalcMeshCenter();
                     *                      Vector3 offsetTarget = anchorOrigin - anchorTarget;
                     *                      for (int j = 0; j != frameVertexCount; j++)
                     *                      {
                     *                              buffersFrameX.vertexPositions[j] += offsetTarget;
                     *                      }
                     *
                     *                      SkinDeformation firstFrameDelta;
                     *                      firstFrameDelta = new SkinDeformation();
                     *                      firstFrameDelta.SetDeltas(buffersFrameX, buffersFrame0);
                     *
                     *                      for (int i = 0; i != frameCount; i++)
                     *                      {
                     *                              EditorUtility.DisplayProgressBar(progressTitle, "Retargeting frames", (progressIndex - 1 + ((float)i / frameCount)) / progressCount);
                     *
                     *                              for (int j = 0; j != frameVertexCount; j++)
                     *                              {
                     *                                      frames[i].deltaPositions[j] += firstFrameDelta.deltaPositions[j];
                     *                                      frames[i].deltaNormals[j] += firstFrameDelta.deltaNormals[j];
                     *                              }
                     *                      }
                     *              }
                     *              break;
                     * }
                     */
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Fitting frames to blend shapes", progressIndex++ / progressCount);
                if (clip.settings.transferTarget != null)
                {
                    if (clip.settings.fitToBlendShapes)
                    {
                        frameFittedWeightsCount = clip.settings.transferTarget.blendShapeCount;
                    }
                    else
                    {
                        frameFittedWeightsCount = 0;
                    }

                    for (int i = 0; i != frameCount; i++)
                    {
                        frames[i].fittedWeights = new float[frameFittedWeightsCount];
                    }

                    if (frameFittedWeightsCount > 0)
                    {
                        var blendShapeIndicesCommaSep = clip.settings.fittedIndices;
                        var blendShapeIndices         = Array.ConvertAll <string, int>(blendShapeIndicesCommaSep.Split(','), int.Parse);

                        SkinDeformationFitting.FitFramesToBlendShapes(frames, clip.settings.transferTarget, blendShapeIndices, clip.settings.fittingMethod, clip.settings.fittingParam);
                    }
                }
                else
                {
                    for (int i = 0; i != frameCount; i++)
                    {
                        frames[i].fittedWeights = new float[0];
                    }
                }

                EditorUtility.DisplayProgressBar(progressTitle, "Saving binary", progressIndex++ / progressCount);
                {
                    clip.settingsLastImported    = clip.settings.Clone();
                    clip.frameCount              = frameCount;
                    clip.frameVertexCount        = frameVertexCount;
                    clip.frameFittedWeightsCount = frameFittedWeightsCount;
                    clip.frames = frames;
                    clip.framesContainAlbedo        = (frames[0].albedo != null);
                    clip.framesContainDeltas        = (frames[0].deltaPositions.Length > 0);
                    clip.framesContainFittedWeights = (frames[0].fittedWeights.Length > 0);
                    clip.subframeCount = subframeCount;
                    clip.subframes     = subframes;
                    clip.version++;

                    EditorUtility.SetDirty(clip);
                    AssetDatabase.SaveAssets();

                    clip.SaveFrameData();
                }
            }
            catch (Exception ex)
            {
                Debug.LogError(ex);
            }
            finally
            {
                if (sourceObjsPreloaded != null)
                {
                    for (int i = 0; i != sourceObjsPreloaded.Length; i++)
                    {
                        sourceObjsPreloaded[i].Dispose();
                    }

                    sourceObjsPreloaded = null;
                }

                EditorUtility.ClearProgressBar();
            }
        }