public void ComputeMeshLaplacian(MeshLaplacian meshLaplacian, MeshBuffers meshBuffers)
        {
            Debug.Assert(vertexCount == meshBuffers.vertexCount);

            // Ls x = diffcoords
            unsafe
            {
                var vertexPositionX = new double[vertexCount];
                var vertexPositionY = new double[vertexCount];
                var vertexPositionZ = new double[vertexCount];

                fixed(Vector3 *src = meshBuffers.vertexPositions)
                fixed(double *dstX = vertexPositionX)
                fixed(double *dstY = vertexPositionY)
                fixed(double *dstZ = vertexPositionZ)
                {
                    for (int i = 0; i != vertexCount; i++)
                    {
                        dstX[i] = src[i].x;
                        dstY[i] = src[i].y;
                        dstZ[i] = src[i].z;
                    }
                }

                ArrayUtils.ResizeCheckedIfLessThan(ref meshLaplacian.vertexDifferentialX, vertexCount);
                ArrayUtils.ResizeCheckedIfLessThan(ref meshLaplacian.vertexDifferentialY, vertexCount);
                ArrayUtils.ResizeCheckedIfLessThan(ref meshLaplacian.vertexDifferentialZ, vertexCount);

                Ls.Multiply(vertexPositionX, meshLaplacian.vertexDifferentialX);
                Ls.Multiply(vertexPositionY, meshLaplacian.vertexDifferentialY);
                Ls.Multiply(vertexPositionZ, meshLaplacian.vertexDifferentialZ);

                meshLaplacian.internalCount = vertexCount;
            }
        }
예제 #2
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();
        }
        bool UpdateMeshBuffers()
        {
            meshBakedOrAsset = null;
            {
                var mf = GetComponent <MeshFilter>();
                if (mf != null)
                {
                    meshBakedOrAsset = mf.sharedMesh;
                }

                var smr = GetComponent <SkinnedMeshRenderer>();
                if (smr != null)
                {
                    if (meshBakedSmr == null)
                    {
                        meshBakedSmr           = new Mesh();
                        meshBakedSmr.name      = "SkinAttachmentTarget(BakeMesh)";
                        meshBakedSmr.hideFlags = HideFlags.HideAndDontSave & ~HideFlags.DontUnloadUnusedAsset;
                        meshBakedSmr.MarkDynamic();
                    }

                    meshBakedOrAsset = meshBakedSmr;

                    Profiler.BeginSample("smr.BakeMesh");
                    {
                        smr.BakeMesh(meshBakedSmr);
                        {
                            meshBakedSmr.bounds = smr.bounds;
                        }
                    }
                    Profiler.EndSample();
                }
            }

            if (meshBakedOrAsset == null)
            {
                return(false);
            }

            if (meshBuffers == null || meshBuffersLastAsset != meshBakedOrAsset)
            {
                meshBuffers = new MeshBuffers(meshBakedOrAsset);
            }
            else
            {
                meshBuffers.LoadPositionsFrom(meshBakedOrAsset);
                meshBuffers.LoadNormalsFrom(meshBakedOrAsset);
            }

            meshBuffersLastAsset = meshBakedOrAsset;
            return(true);
        }
        protected override void OnMeshInstanceCreated()
        {
            blendInputsRendered = false;

            //Debug.Log("OnMeshInstanceCreated from meshAsset " + meshAsset.GetInstanceID());
            if (meshAssetBuffers == null)
            {
                meshAssetBuffers = new MeshBuffers(meshAsset);
            }
            else
            {
                meshAssetBuffers.LoadFrom(meshAsset);
            }
        }
        public void SetDeltas(MeshBuffers buffersFrame0, MeshBuffers buffersFrameX)
        {
            int vertexCount = buffersFrame0.vertexCount;

            if (vertexCount < buffersFrameX.vertexCount)
            {
                Debug.LogWarning("target has more vertices (" + buffersFrameX.vertexCount + " vs. " + vertexCount + "), ignoring excess");
            }
            else if (vertexCount > buffersFrameX.vertexCount)
            {
                Debug.LogError("target has too few vertices (" + buffersFrameX.vertexCount + " vs. " + vertexCount + "), aborting");
                return;
            }

            ArrayUtils.ResizeChecked(ref deltaPositions, vertexCount);
            ArrayUtils.ResizeChecked(ref deltaNormals, vertexCount);

            for (int i = 0; i != vertexCount; i++)
            {
                deltaPositions[i] = buffersFrameX.vertexPositions[i] - buffersFrame0.vertexPositions[i];
                deltaNormals[i]   = buffersFrameX.vertexNormals[i] - buffersFrame0.vertexNormals[i];
            }
        }
예제 #6
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();
            }
        }
        public void ResolveMeshBuffers(MeshBuffers meshBuffers, MeshLaplacian meshLaplacian)
        {
            Debug.Assert(externalCount == meshBuffers.vertexCount);
            Debug.Assert(internalCount == meshLaplacian.internalCount);

            int constraintCount = constraintIndices.Length;

            // c = 'm' spatial constraints [c0 c1 ... cm]
            // Lc = [Ls I|0] where dim(I) = m
            // Lc x = [diffcoords c]
            // x* = (Lc^T Lc)^-1 Lc^T [diffcoords c]
            unsafe
            {
                var constrainedDifferentialX = new double[internalCount + constraintCount];
                var constrainedDifferentialY = new double[internalCount + constraintCount];
                var constrainedDifferentialZ = new double[internalCount + constraintCount];

                fixed(double *srcX = meshLaplacian.vertexDifferentialX)
                fixed(double *srcY = meshLaplacian.vertexDifferentialY)
                fixed(double *srcZ = meshLaplacian.vertexDifferentialZ)
                fixed(double *dstX = constrainedDifferentialX)
                fixed(double *dstY = constrainedDifferentialY)
                fixed(double *dstZ = constrainedDifferentialZ)
                {
                    UnsafeUtility.MemCpy(dstX, srcX, sizeof(double) * internalCount);
                    UnsafeUtility.MemCpy(dstY, srcY, sizeof(double) * internalCount);
                    UnsafeUtility.MemCpy(dstZ, srcZ, sizeof(double) * internalCount);

                    for (int i = 0; i != constraintCount; i++)
                    {
                        int k = externalFromInternal[constraintIndices[i]];
                        dstX[internalCount + i] = constraintWeight * meshBuffers.vertexPositions[k].x;
                        dstY[internalCount + i] = constraintWeight * meshBuffers.vertexPositions[k].y;
                        dstZ[internalCount + i] = constraintWeight * meshBuffers.vertexPositions[k].z;
                    }
                }

                var LcT_constrainedDifferentialX = new double[internalCount + constraintCount];
                var LcT_constrainedDifferentialY = new double[internalCount + constraintCount];
                var LcT_constrainedDifferentialZ = new double[internalCount + constraintCount];

                LcT.Multiply(constrainedDifferentialX, LcT_constrainedDifferentialX);
                LcT.Multiply(constrainedDifferentialY, LcT_constrainedDifferentialY);
                LcT.Multiply(constrainedDifferentialZ, LcT_constrainedDifferentialZ);

                var resultPositionX = new double[internalCount + constraintCount];
                var resultPositionY = new double[internalCount + constraintCount];
                var resultPositionZ = new double[internalCount + constraintCount];

                Profiler.BeginSample("chol-solve");
                LcT_Lc_chol.Solve(LcT_constrainedDifferentialX, resultPositionX);
                LcT_Lc_chol.Solve(LcT_constrainedDifferentialY, resultPositionY);
                LcT_Lc_chol.Solve(LcT_constrainedDifferentialZ, resultPositionZ);
                Profiler.EndSample();

                fixed(double *srcX = resultPositionX)
                fixed(double *srcY = resultPositionY)
                fixed(double *srcZ = resultPositionZ)
                fixed(float *dstX  = &meshBuffers.vertexPositions[0].x)
                fixed(float *dstY  = &meshBuffers.vertexPositions[0].y)
                fixed(float *dstZ  = &meshBuffers.vertexPositions[0].z)
                {
                    const int dstStride = 3;                    // sizeof(Vector3) / sizeof(float)

                    for (int i = 0; i != internalCount; i++)
                    {
                        int k = externalFromInternal[i];
                        dstX[k * dstStride] = (float)srcX[i];
                        dstY[k * dstStride] = (float)srcY[i];
                        dstZ[k * dstStride] = (float)srcZ[i];
                    }
                }
            }
        }
예제 #8
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);
                }
            }
        }
예제 #9
0
 public MeshAdjacency(MeshBuffers meshBuffers, bool welded = false)
 {
     LoadFrom(meshBuffers, welded);
 }
예제 #10
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();
            }
        }
예제 #11
0
        public static void FitFramesToBlendShapes(SkinDeformation[] frames, UnityEngine.Mesh mesh, int[] blendShapeIndices, Method fittingMethod, Param fittingParam)
        {
            // Ax = b
            // x* = (A^T A)^-1 A^T b
            int numShapes    = mesh.blendShapeCount;
            int numVertices  = mesh.vertexCount;
            int numEquations = 0;
            int numVariables = blendShapeIndices.Length;

            var meshBuffers = null as MeshBuffers;
            var meshEdges   = null as MeshEdges;

            switch (fittingParam)
            {
            case Param.DeltaPosition:
                numEquations = 3 * numVertices;
                break;

            case Param.OutputEdgeLength:
            case Param.OutputEdgeCurvature:
                meshBuffers  = new MeshBuffers(mesh);
                meshEdges    = new MeshEdges(mesh.triangles);
                numEquations = meshEdges.edges.Length;
                break;
            }

            var tmpPositions = new UnityEngine.Vector3[numVertices];
            var tmpTangents  = new UnityEngine.Vector3[numVertices];
            var tmpNormals   = new UnityEngine.Vector3[numVertices];

            var edgeLengths    = null as float[];
            var edgeCurvatures = null as float[];

            // prepare A
            var A = new double[numEquations, numVariables];
            var _ = new double[numEquations];
            {
                for (int j = 0; j != numVariables; j++)
                {
                    int k = blendShapeIndices[j];

                    EditorUtilityProxy.DisplayProgressBar("Building 'A'", "Processing shape " + k + " (" + mesh.GetBlendShapeName(k) + ")", j / (float)numVariables);
                    mesh.GetBlendShapeFrameVertices(k, 0, tmpPositions, tmpNormals, tmpTangents);

                    switch (fittingParam)
                    {
                    case Param.DeltaPosition:
                        for (int i = 0; i != numVertices; i++)
                        {
                            _[i * 3 + 0] = tmpPositions[i].x;
                            _[i * 3 + 1] = tmpPositions[i].y;
                            _[i * 3 + 2] = tmpPositions[i].z;
                        }
                        break;

                    case Param.OutputEdgeLength:
                        for (int i = 0; i != numVertices; i++)
                        {
                            tmpPositions[i] += meshBuffers.vertexPositions[i];
                        }
                        meshEdges.ComputeLengths(ref edgeLengths, tmpPositions);
                        for (int i = 0; i != numEquations; i++)
                        {
                            _[i] = edgeLengths[i];
                        }
                        break;

                    case Param.OutputEdgeCurvature:
                        for (int i = 0; i != numVertices; i++)
                        {
                            tmpPositions[i] += meshBuffers.vertexPositions[i];
                            tmpNormals[i]   += meshBuffers.vertexNormals[i];
                            tmpNormals[i].Normalize();
                        }
                        meshEdges.ComputeCurvatures(ref edgeCurvatures, tmpPositions, tmpNormals);
                        for (int i = 0; i != numEquations; i++)
                        {
                            _[i] = edgeCurvatures[i];
                        }
                        break;
                    }

                    A.SetColumn(j, _);
                }
            }

            // prepare (A^T A)^-1 A^T for LLS
            var At_A_inv_At = null as double[, ];

            if (fittingMethod == Method.LinearLeastSquares)
            {
                EditorUtilityProxy.DisplayProgressBar("Computing A^T", "Processing ...", 0.0f);
                var At = A.Transpose();
                EditorUtilityProxy.DisplayProgressBar("Computing (A^T A)", "Processing ...", 0.25f);
                var At_A = At.Dot(A);
                EditorUtilityProxy.DisplayProgressBar("Computing (A^T A)^-1", "Processing ...", 0.5f);
                var At_A_inv = At_A.Inverse();
                EditorUtilityProxy.DisplayProgressBar("Computing (A^T A)^-1 A^T", "Processing ...", 0.75f);
                At_A_inv_At = At_A_inv.Dot(At);
            }

            // prepare A[][] for NNLS
            var A_jagged = null as double[][];

            if (fittingMethod == Method.NonNegativeLeastSquares)
            {
                A_jagged = MakeJagged(A);
            }

            // prepare shared job data
            sharedJobData.frames            = frames;
            sharedJobData.blendShapeIndices = blendShapeIndices;

            sharedJobData.meshBuffers = meshBuffers;
            sharedJobData.meshEdges   = meshEdges;

            sharedJobData.numEquations = numEquations;
            sharedJobData.numVariables = numVariables;
            sharedJobData.numVertices  = numVertices;

            sharedJobData.fittingMethod = fittingMethod;
            sharedJobData.fittingParam  = fittingParam;

            sharedJobData.At_A_inv_At = At_A_inv_At;
            sharedJobData.A_jagged    = A_jagged;

            // prepare jobs
            var jobs       = new FrameFittingJob[frames.Length];
            var jobHandles = new JobHandle[frames.Length];

            for (int k = 0; k != jobs.Length; k++)
            {
                jobs[k].frameIndex = k;
            }

            // execute jobs
            for (int k = 0; k != jobs.Length; k++)
            {
                jobHandles[k] = jobs[k].Schedule();
            }

            // wait until done
            var progressTime0        = DateTime.Now;
            var progressNumCompleted = -1;

            while (true)
            {
                int numCompleted = 0;
                for (int i = 0; i != jobs.Length; i++)
                {
                    numCompleted += (jobHandles[i].IsCompleted ? 1 : 0);
                }

                if (numCompleted == jobs.Length)
                {
                    break;
                }

                if (numCompleted > progressNumCompleted)
                {
                    var progressVal = numCompleted / (float)frames.Length;
                    var progressMsg = "Processing frames ... Completed " + numCompleted + " / " + frames.Length;
                    if (progressVal > 0.0f)
                    {
                        var timeElapsed = DateTime.Now - progressTime0;
                        var timeArrival = TimeSpan.FromMilliseconds(timeElapsed.TotalMilliseconds / progressVal);
                        var timeRemains = timeArrival - timeElapsed;

                        progressMsg += " ... Est. time " + string.Format("{0}m{1:D2}s", (int)UnityEngine.Mathf.Floor((float)timeRemains.TotalMinutes), timeRemains.Seconds);
                    }

                    switch (sharedJobData.fittingMethod)
                    {
                    case Method.LinearLeastSquares:
                        EditorUtilityProxy.DisplayProgressBar("Computing x* = (A^T A)^-1 A^T b", progressMsg, progressVal);
                        break;

                    case Method.NonNegativeLeastSquares:
                        EditorUtilityProxy.DisplayProgressBar("Computing x* = NonNegativeLeastSquares(A, b)", progressMsg, progressVal);
                        break;
                    }

                    progressNumCompleted = numCompleted;
                }
                else
                {
                    System.Threading.Thread.Sleep(1);
                }
            }
        }