Example #1
0
        public void LoadFrom(MeshBuffers meshBuffers, bool welded = false)
        {
            vertexCount = meshBuffers.vertexCount;
            vertexTriangles.Allocate(vertexCount, vertexCount * 8);
            vertexVertices.Allocate(vertexCount, vertexCount * 8);
            vertexWelded.Allocate(vertexCount, vertexCount);

            ArrayUtils.ResizeCheckedIfLessThan(ref vertexResolve, vertexCount);

            triangleCount = meshBuffers.triangleCount / 3;
            triangleTriangles.Allocate(triangleCount, triangleCount * 8);
            triangleVertices.Allocate(triangleCount, triangleCount * 3);

            int[] triangles = meshBuffers.triangles;

            // build vertex-triangle
            {
                for (int i = 0; i != triangleCount; i++)
                {
                    int _0 = i * 3;
                    int v0 = triangles[_0 + 0];
                    int v1 = triangles[_0 + 1];
                    int v2 = triangles[_0 + 2];

                    vertexTriangles.Append(v0, i);
                    vertexTriangles.Append(v1, i);
                    vertexTriangles.Append(v2, i);
                }
            }

            // build vertex-welded
            unsafe
            {
                if (welded)
                {
                    // for each vertex
                    //   if vertex != closest vertex
                    //     replace references to vertex with closest vertex
                    triangles = triangles.Clone() as int[];

                    using (var vertexWeldedMap = new UnsafeArrayBool(meshBuffers.vertexCount))
                    {
                        vertexWeldedMap.Clear(false);

                        var vertexBSP = new KdTree3(meshBuffers.vertexPositions, meshBuffers.vertexCount);

                        for (int i = 0; i != vertexCount; i++)
                        {
                            int j = vertexBSP.FindNearest(ref meshBuffers.vertexPositions[i]);
                            if (j != i)
                            {
                                //Debug.Assert(vertexWeldedMap.val[j] == false);

                                // replace references to i with j, keeping j
                                foreach (var triangle in vertexTriangles[i])
                                {
                                    int _0 = triangle * 3;
                                    int v0 = triangles[_0 + 0];
                                    int v1 = triangles[_0 + 1];
                                    //..v2 = triangles[_0 + 2];

                                    if (v0 == i)
                                    {
                                        triangles[_0 + 0] = j;
                                    }
                                    else if (v1 == i)
                                    {
                                        triangles[_0 + 1] = j;
                                    }
                                    else                                     // (v2 == i)
                                    {
                                        triangles[_0 + 2] = j;
                                    }

                                    // store i under j, so we can recover i at a later time
                                    if (vertexWeldedMap.val[i] == false)
                                    {
                                        vertexWeldedMap.val[i] = true;
                                        vertexWelded.Append(j, i);
                                    }
                                }
                            }
                        }
                    }

                    // rebuild vertex-triangle
                    vertexTriangles.Clear();

                    for (int i = 0; i != triangleCount; i++)
                    {
                        int _0 = i * 3;
                        int v0 = triangles[_0 + 0];
                        int v1 = triangles[_0 + 1];
                        int v2 = triangles[_0 + 2];

                        vertexTriangles.Append(v0, i);
                        vertexTriangles.Append(v1, i);
                        vertexTriangles.Append(v2, i);
                    }
                }
            }

            // build vertex-resolve
            {
                for (int i = 0; i != vertexCount; i++)
                {
                    vertexResolve[i] = i;
                }

                for (int i = 0; i != vertexCount; i++)
                {
                    foreach (var j in vertexWelded[i])
                    {
                        vertexResolve[j] = i;
                    }
                }
            }

            // build vertex-vertex
            unsafe
            {
                using (var vertexAdded = new UnsafeArrayBool(vertexCount))
                {
                    vertexAdded.Clear(false);

                    for (int i = 0; i != vertexCount; i++)
                    {
                        foreach (int triangle in vertexTriangles[i])
                        {
                            int _0 = triangle * 3;
                            int v0 = triangles[_0 + 0];
                            int v1 = triangles[_0 + 1];
                            int v2 = triangles[_0 + 2];

                            int vA, vB;
                            if (i == v0)
                            {
                                vA = v1;
                                vB = v2;
                            }
                            else if (i == v1)
                            {
                                vA = v2;
                                vB = v0;
                            }
                            else                             // (i == v2)
                            {
                                vA = v0;
                                vB = v1;
                            }

                            if (vertexAdded.val[vA] == false && (vertexAdded.val[vA] = true))
                            {
                                vertexVertices.Append(i, vA);
                            }

                            if (vertexAdded.val[vB] == false && (vertexAdded.val[vB] = true))
                            {
                                vertexVertices.Append(i, vB);
                            }
                        }

                        foreach (int j in vertexVertices[i])
                        {
                            vertexAdded.val[j] = false;
                        }
                    }
                }
            }

            // build triangle-triangle
            unsafe
            {
                using (var triangleAdded = new UnsafeArrayBool(triangleCount))
                {
                    triangleAdded.Clear(false);

                    for (int i = 0; i != triangleCount; i++)
                    {
                        int _0 = i * 3;
                        int v0 = triangles[_0 + 0];
                        int v1 = triangles[_0 + 1];
                        int v2 = triangles[_0 + 2];

                        triangleAdded.val[i] = true;

                        foreach (int j in vertexTriangles[v0])
                        {
                            if (triangleAdded.val[j] == false && (triangleAdded.val[j] = true))
                            {
                                triangleTriangles.Append(i, j);
                            }
                        }

                        foreach (int j in vertexTriangles[v1])
                        {
                            if (triangleAdded.val[j] == false && (triangleAdded.val[j] = true))
                            {
                                triangleTriangles.Append(i, j);
                            }
                        }

                        foreach (int j in vertexTriangles[v2])
                        {
                            if (triangleAdded.val[j] == false && (triangleAdded.val[j] = true))
                            {
                                triangleTriangles.Append(i, j);
                            }
                        }

                        triangleAdded.val[i] = false;

                        foreach (int j in triangleTriangles[i])
                        {
                            triangleAdded.val[j] = false;
                        }
                    }
                }
            }

            // build triangle-vertex
            {
                for (int i = 0; i != triangleCount; i++)
                {
                    int _0 = i * 3;
                    int v0 = triangles[_0 + 0];
                    int v1 = triangles[_0 + 1];
                    int v2 = triangles[_0 + 2];

                    triangleVertices.Append(i, v0);
                    triangleVertices.Append(i, v1);
                    triangleVertices.Append(i, v2);
                }
            }
        }
Example #2
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();
            }
        }