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 BuildFrom(MeshAdjacency meshAdjacency, int[] roiIndices, int roiBoundaryLevels, int[] roiConstraintIndices = null) { unsafe { using (var visited = new UnsafeArrayBool(meshAdjacency.vertexCount)) using (var visitedBoundary = new UnsafeArrayBool(meshAdjacency.vertexCount)) using (var visitor = new UnsafeBFS(meshAdjacency.vertexCount)) { // find boundary visited.Clear(false); visitedBoundary.Clear(false); visitor.Clear(); int visitedCount = 0; int visitedBoundaryCount = 0; foreach (int i in roiIndices) { visited.val[i] = true; visitedCount++; visitor.Ignore(i); } foreach (int i in roiIndices) { foreach (var j in meshAdjacency.vertexVertices[i]) { visitor.Insert(j); } } // step boundary while (visitor.MoveNext()) { int i = visitor.position; visited.val[i] = true; visitedCount++; visitedBoundary.val[i] = true; visitedBoundaryCount++; if (visitor.depth < roiBoundaryLevels) { foreach (var j in meshAdjacency.vertexVertices[i]) { visitor.Insert(j); } } } // add constraints if (roiConstraintIndices != null) { foreach (int i in roiConstraintIndices) { if (visited.val[i]) { if (visitedBoundary.val[i] == false) { visitedBoundary.val[i] = true; visitedBoundaryCount++; } } else { Debug.LogWarning("ignoring user constraint outside ROI: vertex " + i); } } } // build translations internalCount = 0; externalCount = meshAdjacency.vertexCount; internalFromExternal = new int[externalCount]; externalFromInternal = new int[visitedCount]; for (int i = 0; i != meshAdjacency.vertexCount; i++) { if (visited.val[i]) { int internalIndex = internalCount++; externalFromInternal[internalIndex] = i; internalFromExternal[i] = internalIndex; } else { internalFromExternal[i] = -1; } } // find constraint indices constraintIndices = new int[visitedBoundaryCount]; constraintWeight = 1.0; int constraintCount = 0; for (int i = 0; i != internalCount; i++) { if (visitedBoundary.val[externalFromInternal[i]]) { constraintIndices[constraintCount++] = i; } } // count unconstrained laplacian non-zero fields int nzmax = internalCount; for (int i = 0; i != internalCount; i++) { nzmax += InternalValence(meshAdjacency, i); } // build Ls EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Ls", 0.0f); var Ls_storage = new CoordinateStorage <double>(internalCount, internalCount, nzmax); for (int i = 0; i != internalCount; i++) // D { //TODO proper fix //Ls_storage.At(i, i, InternalValence(meshAdjacency, i)); Ls_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i))); } for (int i = 0; i != internalCount; i++) // A { foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]]) { int j = internalFromExternal[k]; if (j != -1) { Ls_storage.At(i, j, -1.0); } } } Ls = Converter.ToCompressedColumnStorage(Ls_storage) as SparseMatrix; // build Lc EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Lc", 0.0f); var Lc_storage = new CoordinateStorage <double>(internalCount + constraintCount, internalCount, nzmax + constraintCount); for (int i = 0; i != internalCount; i++) { //TODO proper fix //Lc_storage.At(i, i, InternalValence(meshAdjacency, i)); Lc_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i))); } for (int i = 0; i != internalCount; i++) { foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]]) { int j = internalFromExternal[k]; if (j != -1) { Lc_storage.At(i, j, -1.0); } } } for (int i = 0; i != constraintIndices.Length; i++) { Lc_storage.At(internalCount + i, constraintIndices[i], constraintWeight); } Lc = Converter.ToCompressedColumnStorage(Lc_storage) as SparseMatrix; // build LcT EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT", 0.0f); LcT = Lc.Transpose() as SparseMatrix; // build LcT_Lc EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc", 0.0f); LcT_Lc = LcT.Multiply(Lc) as SparseMatrix; // build LcT_Lc_chol EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc_chol", 0.0f); LcT_Lc_chol = SparseCholesky.Create(LcT_Lc, ColumnOrdering.MinimumDegreeAtPlusA); // done EditorUtilityProxy.ClearProgressBar(); } } }
public 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); } } }
public void Clear() { visited.Clear(false); }
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(); } }