您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

404 行
11 KiB

using System;
using System.IO;
using UnityEngine;
using Unity.DemoTeam.Attributes;
#if UNITY_EDITOR
using UnityEditor;
using System.Text.RegularExpressions;
#endif
namespace Unity.DemoTeam.DigitalHuman
{
[CreateAssetMenu(menuName = "Digital Human/Skin Deformation Clip")]
public class SkinDeformationClip : ScriptableObject
{
public unsafe struct Frame
{
public const int FLT_STRIDE = 9;
public const int FLT_OFFSET_POSITION = 0;
public const int FLT_OFFSET_TANGENT = 3;
public const int FLT_OFFSET_NORMAL = 6;
public float* deltaPosTanNrm;
//TODO don't write the data interleaved if we're not going to use it as such!
//public float* deltaPositions;
//public float* deltaTangents;
//public float* deltaNormals;
public float* fittedWeights;
public Texture2D albedo;
}
[Serializable]
public struct Subframe
{
public int frameIndexLo;
public int frameIndexHi;
public float fractionLo;
public float fractionHi;
}
[HideInInspector]
public int frameCount = 0;
[HideInInspector]
public float frameRate = 30.0f;
[HideInInspector]
public int frameVertexCount = 0;
[HideInInspector]
public int frameFittedWeightsCount = 0;
[HideInInspector]
public SkinDeformation[] frames = new SkinDeformation[0];
[HideInInspector]
public bool framesContainAlbedo;
[HideInInspector]
public bool framesContainDeltas;
[HideInInspector]
public bool framesContainFittedWeights;
[HideInInspector]
public NativeFrameStream frameData;
[HideInInspector]
public string frameDataStreamingAssetsPath = null;
[HideInInspector]
private bool frameDataPending = true;
[HideInInspector]
public int subframeCount = 0;
[HideInInspector]
public Subframe[] subframes = new Subframe[0];
[HideInInspector]
public int version = -1;
//--- accessors ---
public double Duration
{
get
{
return subframeCount / frameRate;
}
}
public unsafe Frame GetFrame(int frameIndex)
{
if (frameDataPending)
{
Debug.Log("hotloading frame data");
LoadFrameData();
}
var floatPtr = (float*)frameData.ReadFrame(frameIndex);
{
Frame frame;
//frame.deltaPositions = floatPtr + 0 * frameVertexCount;
//frame.deltaTangents = floatPtr + 3 * frameVertexCount;
//frame.deltaNormals = floatPtr + 6 * frameVertexCount;
frame.deltaPosTanNrm = floatPtr + 0 * frameVertexCount;
frame.fittedWeights = floatPtr + 9 * frameVertexCount;
frame.albedo = frames[frameIndex].albedo;
return frame;
}
}
public int GetFrameSizeBytes()
{
return (9 * frameVertexCount + frameFittedWeightsCount) * sizeof(float);
}
public void PrepareFrame(int frameIndex)
{
frameData.SeekFrame(frameIndex);
}
//--- import settings begin ---
public enum TransferMode
{
PassThrough,
PassThroughWithFirstFrameDelta,
}
[Serializable]
public class ImportSettings
{
[Header("Asset paths")]
public string keyframesCSV;
[EditableIf("externalLoader", false)] public string meshFolder;
[EditableIf("externalLoader", false)] public string meshPrefix;
[EditableIf("externalLoader", false)] public string albedoFolder;
[EditableIf("externalLoader", false)] public string albedoPrefix;
public bool externalLoader = false;
[EditableIf("externalLoader", true)] public string externalObjPath;
[EditableIf("externalLoader", true)] public string externalObjPattern = "*.obj";
[Header("Mesh transform")]
public Vector3 applyRotation = Vector3.zero;
public float applyScale = 1.0f;
[Header("Mesh processing")]
[Range(0.0f, 1.0f)]
public float denoiseFactor = 0.0f;
public TextAsset[] denoiseRegion;
[Range(0.0f, 1.0f)]
public float transplantFactor = 0.0f;
public TextAsset[] transplantRegion;
public bool solveRegionPreview = false;
public bool solveWelded = false;
[Header("Frame transfer")]
//PACKAGETODO cleanup
public Mesh transferTarget;
public TextAsset transferRegion;
public TransferMode transferMode;
[Header("Frame fitting")]
public bool fitToBlendShapes = false;
[TextArea(1, 20)]
public string fittedIndices = "";
public SkinDeformationFitting.Method fittingMethod = SkinDeformationFitting.Method.LinearLeastSquares;
public SkinDeformationFitting.Param fittingParam = SkinDeformationFitting.Param.DeltaPosition;
public ImportSettings Clone()
{
var c = this.MemberwiseClone() as ImportSettings;
c.keyframesCSV = c.keyframesCSV.Clone() as string;
c.meshFolder = c.meshFolder.Clone() as string;
c.meshPrefix = c.meshPrefix.Clone() as string;
c.albedoFolder = c.albedoFolder.Clone() as string;
c.albedoPrefix = c.albedoPrefix.Clone() as string;
c.denoiseRegion = c.denoiseRegion.Clone() as TextAsset[];
c.transplantRegion = c.transplantRegion.Clone() as TextAsset[];
c.fittedIndices = c.fittedIndices.Clone() as string;
return c;
}
}
[ReadOnly]
public ImportSettings lastBuild = new ImportSettings();
public ImportSettings importSettings = new ImportSettings();
//--- import settings end ---
//--- frame data serialization begin ---
void OnEnable()
{
if (frameDataPending)
{
LoadFrameData();
}
}
void OnDisable()
{
UnloadFrameData();
}
void OnDestroy()
{
UnloadFrameData();
}
void LoadFrameData()
{
#if UNITY_EDITOR
string filename = AssetDatabase.GetAssetPath(this) + "_frames.bin";
#else
string filename = Application.streamingAssetsPath + frameDataStreamingAssetsPath;
Debug.Log("LoadFrameData " + filename + ")");
#endif
int frameOffset = 3 * sizeof(Int32);
int frameSize = GetFrameSizeBytes();
frameData.Dispose();
frameData = new NativeFrameStream(filename, frameOffset, frameCount, frameSize, 2, 16);
frameDataPending = false;
if (!File.Exists(filename))
{
Debug.LogError("failed to load frame data (filename = " + filename + ")");
return;
}
/* OLD
for (int i = 0; i != frameCount; i++)
{
frames[i].Allocate(frameVertexCount, frameFittedWeightsCount);
}
using (FileStream stream = File.OpenRead(filename))
{
using (BinaryReader reader = new BinaryReader(stream))
{
int __frameCount = reader.ReadInt32();
int __frameVertexCount = reader.ReadInt32();
int __frameFittedWeightsCount = reader.ReadInt32();
Debug.Assert(__frameCount == frameCount);
Debug.Assert(__frameVertexCount == frameVertexCount);
Debug.Assert(__frameFittedWeightsCount == frameFittedWeightsCount);
var srcCursor = 0;
var srcBuffer = new float[3 * 3 * frameVertexCount + 1 * frameFittedWeightsCount];
var dstBuffer = new byte[4 * srcBuffer.Length];
for (int i = 0; i != frameCount; i++)
{
reader.Read(dstBuffer, 0, dstBuffer.Length);
Buffer.BlockCopy(dstBuffer, 0, srcBuffer, 0, dstBuffer.Length);
for (int j = 0; j != frameVertexCount; j++)
{
frames[i].deltaPositions[j].x = srcBuffer[srcCursor++];
frames[i].deltaPositions[j].y = srcBuffer[srcCursor++];
frames[i].deltaPositions[j].z = srcBuffer[srcCursor++];
frames[i].deltaTangents[j].x = srcBuffer[srcCursor++];
frames[i].deltaTangents[j].y = srcBuffer[srcCursor++];
frames[i].deltaTangents[j].z = srcBuffer[srcCursor++];
frames[i].deltaNormals[j].x = srcBuffer[srcCursor++];
frames[i].deltaNormals[j].y = srcBuffer[srcCursor++];
frames[i].deltaNormals[j].z = srcBuffer[srcCursor++];
}
for (int j = 0; j != frameFittedWeightsCount; j++)
{
frames[i].fittedWeights[j] = srcBuffer[srcCursor++];
}
srcCursor = 0;
}
frameDataPending = false;
}
}
*/
}
public void UnloadFrameData()
{
frameData.Dispose();
frameDataPending = true;
}
#if UNITY_EDITOR
public void SaveFrameData()
{
string filenameAsset = AssetDatabase.GetAssetPath(this);
string filenameFrameData = filenameAsset + "_frames.bin";
UnloadFrameData();
if (File.Exists(filenameFrameData))
File.Delete(filenameFrameData);
using (FileStream stream = File.Create(filenameFrameData))
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(frameCount);
writer.Write(frameVertexCount);
writer.Write(frameFittedWeightsCount);
var srcCursor = 0;
var srcBuffer = new float[3 * 3 * frameVertexCount + 1 * frameFittedWeightsCount];
var dstBuffer = new byte[4 * srcBuffer.Length];
for (int i = 0; i != frameCount; i++)
{
srcCursor = 0;
Debug.Assert(frames[i].deltaPositions.Length == frameVertexCount, "invalid vertex count");
for (int j = 0; j != frameVertexCount; j++)
{
srcBuffer[srcCursor++] = frames[i].deltaPositions[j].x;
srcBuffer[srcCursor++] = frames[i].deltaPositions[j].y;
srcBuffer[srcCursor++] = frames[i].deltaPositions[j].z;
srcBuffer[srcCursor++] = frames[i].deltaTangents[j].x;
srcBuffer[srcCursor++] = frames[i].deltaTangents[j].y;
srcBuffer[srcCursor++] = frames[i].deltaTangents[j].z;
srcBuffer[srcCursor++] = frames[i].deltaNormals[j].x;
srcBuffer[srcCursor++] = frames[i].deltaNormals[j].y;
srcBuffer[srcCursor++] = frames[i].deltaNormals[j].z;
}
Debug.Assert(frames[i].fittedWeights.Length == frameFittedWeightsCount, "invalid fitted weights count");
for (int j = 0; j != frameFittedWeightsCount; j++)
{
srcBuffer[srcCursor++] = frames[i].fittedWeights[j];
}
Buffer.BlockCopy(srcBuffer, 0, dstBuffer, 0, dstBuffer.Length);
writer.Write(dstBuffer, 0, dstBuffer.Length);
writer.Flush();
}
frameDataPending = true;
}
}
}
#endif
//--- frame data serialization end ---
#if UNITY_EDITOR
[ContextMenu("Save To StreamingAssets")]
public void SaveToStreamingAssets()
{
string filenameAsset = AssetDatabase.GetAssetPath(this);
string filenameFrameData = filenameAsset + "_frames.bin";
frameDataStreamingAssetsPath = "/SkinDeformationClip/" + AssetDatabase.AssetPathToGUID(filenameAsset) + "__" + this.name;
var copySrc = filenameFrameData;
var copyDst = Application.streamingAssetsPath + frameDataStreamingAssetsPath;
//Debug.Log("filenameAsset: " + filenameAsset);
//Debug.Log("copySrc: " + copySrc);
//Debug.Log("copyDst: " + copyDst);
var copyDstDir = copyDst.Substring(0, copyDst.LastIndexOf('/'));
try
{
if (File.Exists(copyDst))
File.Delete(copyDst);
Directory.CreateDirectory(copyDstDir);
File.Copy(copySrc, copyDst);
}
catch (Exception ex)
{
Debug.LogError(ex.ToString());
}
}
#endif
}
#if UNITY_EDITOR
public class SkinDeformationClipBuildProcessor : UnityEditor.Build.IPreprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report)
{
var clips = Resources.FindObjectsOfTypeAll<SkinDeformationClip>();
foreach (var clip in clips)
{
clip.SaveToStreamingAssets();
EditorUtility.SetDirty(clip);
}
AssetDatabase.SaveAssets();
}
}
#endif
}