From 0b462754a569bc0c996c1af45eceffad859951fd Mon Sep 17 00:00:00 2001 From: Perfare Date: Sun, 28 Jul 2019 16:41:23 +0800 Subject: [PATCH] Implemented BlendShape export --- AssetStudio/Classes/AnimationClip.cs | 4 +- AssetStudio/IImported.cs | 7 +++ AssetStudioFBX/AssetStudioFBXExporter.cpp | 58 +++++++++++++++-- AssetStudioUtility/ModelConverter.cs | 77 ++++++++++++++++++++++- 4 files changed, 138 insertions(+), 8 deletions(-) diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index 8263429..942d9e2 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -292,7 +292,7 @@ namespace AssetStudio public AnimationCurve curve; public string attribute; public string path; - public int classID; + public ClassIDType classID; public PPtr script; @@ -301,7 +301,7 @@ namespace AssetStudio curve = new AnimationCurve(reader, reader.ReadSingle); attribute = reader.ReadAlignedString(); path = reader.ReadAlignedString(); - classID = reader.ReadInt32(); + classID = (ClassIDType)reader.ReadInt32(); script = new PPtr(reader); } } diff --git a/AssetStudio/IImported.cs b/AssetStudio/IImported.cs index 6b1ad46..aeb186c 100644 --- a/AssetStudio/IImported.cs +++ b/AssetStudio/IImported.cs @@ -227,6 +227,7 @@ namespace AssetStudio public List> Scalings = new List>(); public List> Rotations = new List>(); public List> Translations = new List>(); + public ImportedBlendShape BlendShape; } public class ImportedKeyframe @@ -243,6 +244,12 @@ namespace AssetStudio } } + public class ImportedBlendShape + { + public string ChannelName; + public List> Keyframes = new List>(); + } + public class ImportedMorph { public string Path { get; set; } diff --git a/AssetStudioFBX/AssetStudioFBXExporter.cpp b/AssetStudioFBX/AssetStudioFBXExporter.cpp index ef52df1..b364f00 100644 --- a/AssetStudioFBX/AssetStudioFBXExporter.cpp +++ b/AssetStudioFBX/AssetStudioFBXExporter.cpp @@ -96,7 +96,7 @@ namespace AssetStudio } pBindPose = FbxPose::Create(pScene, "BindPose"); - pBindPose->SetIsBindPose(true); + pScene->AddPose(pBindPose); frameToNode = gcnew Dictionary(); meshFrames = imported->MeshList != nullptr ? gcnew List() : nullptr; @@ -173,13 +173,25 @@ namespace AssetStudio if (frameToNode->TryGetValue(frame, pointer)) { auto pNode = (FbxNode*)pointer; - if (allBones || bonePaths->Contains(frame->Path)) + if (allBones) { FbxSkeleton* pJoint = FbxSkeleton::Create(pScene, ""); pJoint->Size.Set(FbxDouble(boneSize)); pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); pNode->SetNodeAttribute(pJoint); } + else if (bonePaths->Contains(frame->Path)) + { + FbxSkeleton* pJoint = FbxSkeleton::Create(pScene, ""); + pJoint->Size.Set(FbxDouble(boneSize)); + pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); + pNode->SetNodeAttribute(pJoint); + + pJoint = FbxSkeleton::Create(pScene, ""); + pJoint->Size.Set(FbxDouble(boneSize)); + pJoint->SetSkeletonType(FbxSkeleton::eLimbNode); + pNode->GetParent()->SetNodeAttribute(pJoint); + } else { FbxNull* pNull = FbxNull::Create(pScene, ""); @@ -344,7 +356,7 @@ namespace AssetStudio } } - FbxMesh* pMesh = FbxMesh::Create(pScene, ""); + FbxMesh* pMesh = FbxMesh::Create(pScene, pFrameNode->GetName()); pFrameNode->SetNodeAttribute(pMesh); int vertexCount = 0; @@ -755,6 +767,42 @@ namespace AssetStudio eulerFilter->SetQualityTolerance(filterPrecision); eulerFilter->Apply(lCurve, 3); } + + //BlendShape + if (keyframeList->BlendShape != nullptr) + { + FbxString channelName; + WITH_MARSHALLED_STRING + ( + pClipName, + keyframeList->BlendShape->ChannelName, + channelName = FbxString(pClipName); + ); + + auto lGeometry = (FbxGeometry*)pNode->GetNodeAttribute(); + FbxBlendShape* lBlendShape = (FbxBlendShape*)lGeometry->GetDeformer(0, FbxDeformer::eBlendShape); + int lBlendShapeChannelCount = lBlendShape->GetBlendShapeChannelCount(); + for (int lChannelIndex = 0; lChannelIndex < lBlendShapeChannelCount; ++lChannelIndex) + { + FbxBlendShapeChannel* lChannel = lBlendShape->GetBlendShapeChannel(lChannelIndex); + FbxString lChannelName = lChannel->GetNameOnly(); + if (lChannelName == channelName) + { + FbxAnimCurve* lAnimCurve = lGeometry->GetShapeChannel(0, lChannelIndex, lAnimLayer, true); + lAnimCurve->KeyModifyBegin(); + + for each (auto keyframe in keyframeList->BlendShape->Keyframes) + { + lTime.SetSecondDouble(keyframe->time); + int lKeyIndex = lAnimCurve->KeyAdd(lTime); + lAnimCurve->KeySetValue(lKeyIndex, keyframe->value); + lAnimCurve->KeySetInterpolation(lKeyIndex, FbxAnimCurveDef::eInterpolationCubic); + } + + lAnimCurve->KeyModifyEnd(); + } + } + } } } } @@ -773,7 +821,7 @@ namespace AssetStudio FbxNode* pNode = (FbxNode*)frameToNode[frame]; FbxMesh* pMesh = pNode->GetMesh(); - FbxBlendShape* lBlendShape = FbxBlendShape::Create(pScene, pNode->GetName()); + FbxBlendShape* lBlendShape = FbxBlendShape::Create(pScene, pMesh->GetNameOnly() + FbxString("BlendShape")); pMesh->AddDeformer(lBlendShape); for (int i = 0; i < morph->Channels->Count; i++) @@ -791,7 +839,7 @@ namespace AssetStudio for each(ImportedMorphKeyframe^ keyframe in channel->KeyframeList) { - FbxShape* lShape = FbxShape::Create(pScene, ""); + FbxShape* lShape = FbxShape::Create(pScene, FbxString(keyframe->Weight)); lBlendShapeChannel->AddTargetShape(lShape, keyframe->Weight); auto vectorCount = pMesh->GetControlPointsCount(); diff --git a/AssetStudioUtility/ModelConverter.cs b/AssetStudioUtility/ModelConverter.cs index ddb03b5..ba92663 100644 --- a/AssetStudioUtility/ModelConverter.cs +++ b/AssetStudioUtility/ModelConverter.cs @@ -21,6 +21,7 @@ namespace AssetStudio private Dictionary bonePathHash = new Dictionary(); private Dictionary textureNameDictionary = new Dictionary(); private Dictionary transformDictionary = new Dictionary(); + Dictionary morphChannelNames = new Dictionary(); public ModelConverter(GameObject m_GameObject, AnimationClip[] animationList = null) { @@ -444,6 +445,9 @@ namespace AssetStudio var channel = new ImportedMorphChannel(); morph.Channels.Add(channel); var shapeChannel = mesh.m_Shapes.channels[i]; + + morphChannelNames.Add(shapeChannel.nameHash, shapeChannel.name); + channel.Name = shapeChannel.name; channel.KeyframeList = new List(shapeChannel.frameCount); var frameEnd = shapeChannel.frameIndex + shapeChannel.frameCount; @@ -773,6 +777,31 @@ namespace AssetStudio } } } + foreach (var m_FloatCurve in animationClip.m_FloatCurves) + { + if (m_FloatCurve.classID == ClassIDType.SkinnedMeshRenderer) //BlendShape + { + var channelName = m_FloatCurve.attribute; + int dotPos = channelName.IndexOf('.'); + if (dotPos >= 0) + { + channelName = channelName.Substring(dotPos + 1); + } + + var path = FixBonePath(m_FloatCurve.path); + if (string.IsNullOrEmpty(path)) + { + path = GetPathByChannelName(channelName); + } + var track = iAnim.FindTrack(path); + track.BlendShape = new ImportedBlendShape(); + track.BlendShape.ChannelName = channelName; + foreach (var m_Curve in m_FloatCurve.curve.m_Curve) + { + track.BlendShape.Keyframes.Add(new ImportedKeyframe(m_Curve.time, m_Curve.value)); + } + } + } } else { @@ -822,6 +851,26 @@ namespace AssetStudio private void ReadCurveData(ImportedKeyframedAnimation iAnim, AnimationClipBindingConstant m_ClipBindingConstant, int index, float time, float[] data, int offset, ref int curveIndex) { var binding = m_ClipBindingConstant.FindBinding(index); + if (binding.typeID == ClassIDType.SkinnedMeshRenderer) //BlendShape + { + var channelName = GetChannelNameFromHash(binding.attribute); + int dotPos = channelName.IndexOf('.'); + if (dotPos >= 0) + { + channelName = channelName.Substring(dotPos + 1); + } + + var bPath = FixBonePath(GetPathFromHash(binding.path)); + if (string.IsNullOrEmpty(bPath)) + { + bPath = GetPathByChannelName(channelName); + } + var bTrack = iAnim.FindTrack(bPath); + bTrack.BlendShape = new ImportedBlendShape(); + bTrack.BlendShape.ChannelName = channelName; + bTrack.BlendShape.Keyframes.Add(new ImportedKeyframe(time, data[curveIndex++ + offset])); + return; + } if (binding.path == 0) { curveIndex++; @@ -868,7 +917,6 @@ namespace AssetStudio ))); break; default: - //track.Curve.Add(new ImportedKeyframe(time, data[curveIndex++])); curveIndex++; break; } @@ -974,5 +1022,32 @@ namespace AssetStudio } } } + + private string GetPathByChannelName(string channelName) + { + foreach (var morph in MorphList) + { + foreach (var channel in morph.Channels) + { + if (channel.Name == channelName) + { + return morph.Path; + } + } + } + return null; + } + + private string GetChannelNameFromHash(uint attribute) + { + if (morphChannelNames.TryGetValue(attribute, out var name)) + { + return name; + } + else + { + return null; + } + } } }