support optimized transform hierarchy

This commit is contained in:
Perfare 2018-09-09 17:32:12 +08:00
parent 18af0a8856
commit 2d0278db87
4 changed files with 137 additions and 12 deletions

View File

@ -362,5 +362,10 @@ namespace AssetStudio
} }
return null; return null;
} }
public string FindBonePath(uint hash)
{
return m_TOS.Find(pair => pair.Key == hash).Value;
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using SharpDX;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -7,12 +8,19 @@ namespace AssetStudio
{ {
public class Transform public class Transform
{ {
public PPtr m_GameObject = new PPtr(); public PPtr m_GameObject;
public float[] m_LocalRotation; public float[] m_LocalRotation;
public float[] m_LocalPosition; public float[] m_LocalPosition;
public float[] m_LocalScale; public float[] m_LocalScale;
public List<PPtr> m_Children = new List<PPtr>(); public List<PPtr> m_Children;
public PPtr m_Father = new PPtr();//can be transform or type 224 (as seen in Minions) public PPtr m_Father;
public Transform(Vector3 t, Quaternion q, Vector3 s)
{
m_LocalPosition = new[] { t.X, t.Y, t.Z };
m_LocalRotation = new[] { q.X, q.Y, q.Z, q.W };
m_LocalScale = new[] { s.X, s.Y, s.Z };
}
public Transform(AssetPreloadData preloadData) public Transform(AssetPreloadData preloadData)
{ {
@ -24,6 +32,7 @@ namespace AssetStudio
m_LocalPosition = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; m_LocalPosition = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() };
m_LocalScale = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() }; m_LocalScale = new[] { reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle() };
int m_ChildrenCount = reader.ReadInt32(); int m_ChildrenCount = reader.ReadInt32();
m_Children = new List<PPtr>(m_ChildrenCount);
for (int j = 0; j < m_ChildrenCount; j++) for (int j = 0; j < m_ChildrenCount; j++)
{ {
m_Children.Add(sourceFile.ReadPPtr()); m_Children.Add(sourceFile.ReadPPtr());

View File

@ -22,6 +22,7 @@ namespace AssetStudio
private Dictionary<uint, string> morphChannelInfo = new Dictionary<uint, string>(); private Dictionary<uint, string> morphChannelInfo = new Dictionary<uint, string>();
private HashSet<AssetPreloadData> animationClipHashSet = new HashSet<AssetPreloadData>(); private HashSet<AssetPreloadData> animationClipHashSet = new HashSet<AssetPreloadData>();
private Dictionary<uint, string> bonePathHash = new Dictionary<uint, string>(); private Dictionary<uint, string> bonePathHash = new Dictionary<uint, string>();
private bool deoptimize;
public ModelConverter(GameObject m_GameObject) public ModelConverter(GameObject m_GameObject)
{ {
@ -198,6 +199,18 @@ namespace AssetStudio
return frame; return frame;
} }
private ImportedFrame ConvertFrame(Transform trans, string name)
{
var frame = new ImportedFrame();
frame.Name = name;
frame.InitChildren(0);
var m_EulerRotation = QuatToEuler(new[] { trans.m_LocalRotation[0], -trans.m_LocalRotation[1], -trans.m_LocalRotation[2], trans.m_LocalRotation[3] });
frame.LocalRotation = new[] { m_EulerRotation[0], m_EulerRotation[1], m_EulerRotation[2] };
frame.LocalScale = new[] { trans.m_LocalScale[0], trans.m_LocalScale[1], trans.m_LocalScale[2] };
frame.LocalPosition = new[] { -trans.m_LocalPosition[0], trans.m_LocalPosition[1], trans.m_LocalPosition[2] };
return frame;
}
private void ConvertFrames(Transform trans, ImportedFrame parent) private void ConvertFrames(Transform trans, ImportedFrame parent)
{ {
var frame = ConvertFrames(trans); var frame = ConvertFrames(trans);
@ -354,10 +367,6 @@ namespace AssetStudio
{ {
//Bone //Bone
iMesh.BoneList = new List<ImportedBone>(sMesh.m_Bones.Length); iMesh.BoneList = new List<ImportedBone>(sMesh.m_Bones.Length);
/*if (sMesh.m_Bones.Length >= 256)
{
throw new Exception("Too many bones (" + mesh.m_BindPose.Length + ")");
}*/
for (int i = 0; i < sMesh.m_Bones.Length; i++) for (int i = 0; i < sMesh.m_Bones.Length; i++)
{ {
var bone = new ImportedBone(); var bone = new ImportedBone();
@ -405,6 +414,52 @@ namespace AssetStudio
iMesh.BoneList.Add(bone); iMesh.BoneList.Add(bone);
} }
if (sMesh.m_Bones.Length == 0 && mesh.m_BindPose?.Length > 0 && mesh.m_BoneNameHashes?.Length > 0)
{
//TODO move to Init method use Animator.m_HasTransformHierarchy to judge
if (!deoptimize)
{
DeoptimizeTransformHierarchy();
deoptimize = true;
}
//TODO Repeat code with above
for (int i = 0; i < mesh.m_BindPose.Length; i++)
{
var bone = new ImportedBone();
var boneHash = mesh.m_BoneNameHashes[i];
bone.Name = GetNameFromBonePathHashes(boneHash);
if (string.IsNullOrEmpty(bone.Name))
{
bone.Name = avatar?.FindBoneName(boneHash);
}
if (string.IsNullOrEmpty(bone.Name))
{
//throw new Exception("A Bone could neither be found by hash in Avatar nor by index in SkinnedMeshRenderer.");
continue;
}
var om = new float[4, 4];
var m = mesh.m_BindPose[i];
om[0, 0] = m[0, 0];
om[0, 1] = -m[1, 0];
om[0, 2] = -m[2, 0];
om[0, 3] = m[3, 0];
om[1, 0] = -m[0, 1];
om[1, 1] = m[1, 1];
om[1, 2] = m[2, 1];
om[1, 3] = m[3, 1];
om[2, 0] = -m[0, 2];
om[2, 1] = m[1, 2];
om[2, 2] = m[2, 2];
om[2, 3] = m[3, 2];
om[3, 0] = -m[0, 3];
om[3, 1] = m[1, 3];
om[3, 2] = m[2, 3];
om[3, 3] = m[3, 3];
bone.Matrix = om;
iMesh.BoneList.Add(bone);
}
}
//Morphs //Morphs
if (mesh.m_Shapes != null) if (mesh.m_Shapes != null)
{ {
@ -537,7 +592,7 @@ namespace AssetStudio
return GetTransformPath(Father) + "/" + m_GameObject.m_Name; return GetTransformPath(Father) + "/" + m_GameObject.m_Name;
} }
return String.Empty + m_GameObject.m_Name; return m_GameObject.m_Name;
} }
private ImportedMaterial ConvertMaterial(Material mat) private ImportedMaterial ConvertMaterial(Material mat)
@ -968,5 +1023,56 @@ namespace AssetStudio
double newValue = count * 360.0 + cur; double newValue = count * 360.0 + cur;
return (float)newValue; return (float)newValue;
} }
private void DeoptimizeTransformHierarchy()
{
if (avatar == null)
return;
// 1. Figure out the skeletonPaths from the unstripped avatar
var skeletonPaths = new List<string>();
foreach (var id in avatar.m_Avatar.m_AvatarSkeleton.m_ID)
{
var path = avatar.FindBonePath(id);
skeletonPaths.Add(path);
}
// 2. Restore the original transform hierarchy
// Prerequisite: skeletonPaths follow pre-order traversal
var rootFrame = FrameList[0];
rootFrame.ClearChild();
for (var i = 1; i < skeletonPaths.Count; i++) // start from 1, skip the root transform because it will always be there.
{
var path = skeletonPaths[i];
var strs = path.Split('/');
string transformName;
ImportedFrame parentFrame;
if (strs.Length == 1)
{
transformName = path;
parentFrame = rootFrame;
}
else
{
transformName = strs.Last();
var parentFrameName = strs[strs.Length - 2];
parentFrame = ImportedHelpers.FindFrame(parentFrameName, rootFrame);
}
var skeletonPose = avatar.m_Avatar.m_DefaultPose;
var xform = skeletonPose.m_X[i];
if (!(xform.t is Vector3 t))
{
var v4 = (Vector4)xform.t;
t = (Vector3)v4;
}
if (!(xform.s is Vector3 s))
{
var v4 = (Vector4)xform.s;
s = (Vector3)v4;
}
var curTransform = new Transform(t, xform.q, s);
var frame = ConvertFrame(curTransform, transformName);
parentFrame.AddChild(frame);
}
}
} }
} }

View File

@ -64,6 +64,11 @@ namespace AssetStudio
return children.IndexOf(obj); return children.IndexOf(obj);
} }
public void ClearChild()
{
children.Clear();
}
public IEnumerator<ImportedFrame> GetEnumerator() public IEnumerator<ImportedFrame> GetEnumerator()
{ {
return children.GetEnumerator(); return children.GetEnumerator();
@ -199,7 +204,7 @@ namespace AssetStudio
public static class ImportedHelpers public static class ImportedHelpers
{ {
public static ImportedFrame FindFrame(String name, ImportedFrame root) public static ImportedFrame FindFrame(string name, ImportedFrame root)
{ {
ImportedFrame frame = root; ImportedFrame frame = root;
if ((frame != null) && (frame.Name == name)) if ((frame != null) && (frame.Name == name))
@ -218,7 +223,7 @@ namespace AssetStudio
return null; return null;
} }
public static ImportedMesh FindMesh(String frameName, List<ImportedMesh> importedMeshList) public static ImportedMesh FindMesh(string frameName, List<ImportedMesh> importedMeshList)
{ {
foreach (ImportedMesh mesh in importedMeshList) foreach (ImportedMesh mesh in importedMeshList)
{ {
@ -252,7 +257,7 @@ namespace AssetStudio
return null; return null;
} }
public static ImportedMaterial FindMaterial(String name, List<ImportedMaterial> importedMats) public static ImportedMaterial FindMaterial(string name, List<ImportedMaterial> importedMats)
{ {
foreach (ImportedMaterial mat in importedMats) foreach (ImportedMaterial mat in importedMats)
{ {