374 lines
16 KiB
C#
374 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Xml;
|
|
using UnityEngine;
|
|
|
|
namespace DefaultNamespace
|
|
{
|
|
public struct OgreBoneWeight
|
|
{
|
|
public int VertexIndex;
|
|
public int BoneIndex;
|
|
public float Weight;
|
|
}
|
|
|
|
public struct OgreSubMeshName
|
|
{
|
|
public string Name;
|
|
public int Index;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 定义submesh的顶点索引
|
|
/// </summary>
|
|
public struct OgreSubMesh
|
|
{
|
|
public string Material; // 材质名字
|
|
public int FaceCount; // 面数量
|
|
public int[] Triangles;
|
|
}
|
|
|
|
public class OgreMeshData
|
|
{
|
|
public Vector3[] Vertices;
|
|
public Vector3[] Normals;
|
|
public Vector2[][] UVs;
|
|
public OgreBoneWeight[] BoneWeights;
|
|
public string SkeletonLink;
|
|
|
|
public OgreSubMesh[] SubMesh;
|
|
public OgreSubMeshName[] SubMeshName;
|
|
|
|
public void ReadXml(string filename)
|
|
{
|
|
XmlDocument meshDoc = new XmlDocument();
|
|
meshDoc.Load(filename);
|
|
|
|
XmlElement rootNode = meshDoc.DocumentElement;
|
|
XmlNode sharedGeometryNode = rootNode.GetElementsByTagName("sharedgeometry")[0];
|
|
XmlNode submeshesNode = rootNode.GetElementsByTagName("submeshes")[0];
|
|
XmlNode submeshnamesNode = rootNode.GetElementsByTagName("submeshnames")[0];
|
|
|
|
int vertexCount = int.Parse(sharedGeometryNode.Attributes["vertexcount"].Value);
|
|
|
|
Vertices = new Vector3[vertexCount];
|
|
Normals = new Vector3[vertexCount];
|
|
|
|
// vertex
|
|
XmlNode vertexBufferNode1 = sharedGeometryNode.ChildNodes[0];
|
|
bool isPositions = bool.Parse(vertexBufferNode1.Attributes["positions"].Value);
|
|
bool isNormals = bool.Parse(vertexBufferNode1.Attributes["normals"].Value);
|
|
for (int i = 0; i < vertexBufferNode1.ChildNodes.Count; i++)
|
|
{
|
|
XmlNode vertexNode = vertexBufferNode1.ChildNodes[i];
|
|
if (isPositions)
|
|
{
|
|
XmlNode posNode = vertexNode["position"];
|
|
Vertices[i] = posNode.ReadVector3();
|
|
}
|
|
|
|
if (isNormals)
|
|
{
|
|
XmlNode normalNode = vertexNode["normal"];
|
|
Normals[i] = normalNode.ReadVector3();
|
|
}
|
|
}
|
|
|
|
// uv
|
|
XmlNode vertexBufferNode2 = sharedGeometryNode.ChildNodes[1];
|
|
int coordsNum = int.Parse(vertexBufferNode2.Attributes["texture_coords"].Value);
|
|
UVs = new Vector2[coordsNum][];
|
|
for (int i = 0; i < coordsNum; i++)
|
|
UVs[i] = new Vector2[vertexCount];
|
|
|
|
for (int i = 0; i < vertexBufferNode2.ChildNodes.Count; i++)
|
|
{
|
|
XmlNode vertexNode = vertexBufferNode2.ChildNodes[i];
|
|
for (int j = 0; j < vertexNode.ChildNodes.Count; j++)
|
|
{
|
|
XmlNode texcoordNode = vertexNode.ChildNodes[j];
|
|
UVs[j][i] = texcoordNode.ReadUV2();
|
|
}
|
|
}
|
|
|
|
XmlNode skeletonlinkNode = rootNode.SelectSingleNode("skeletonlink");
|
|
if (skeletonlinkNode != null)
|
|
{
|
|
this.SkeletonLink = ((XmlElement)skeletonlinkNode).GetAttribute("name");
|
|
}
|
|
|
|
XmlNode boneassignments = rootNode.SelectSingleNode("boneassignments");
|
|
if (boneassignments is { HasChildNodes: true })
|
|
{
|
|
BoneWeights = new OgreBoneWeight[boneassignments.ChildNodes.Count];
|
|
for (int i = 0; i < boneassignments.ChildNodes.Count; i++)
|
|
{
|
|
XmlElement item = boneassignments.ChildNodes[i] as XmlElement;
|
|
int vIdx = int.Parse(item.GetAttribute("vertexindex"));
|
|
int boneIdx = int.Parse(item.GetAttribute("boneindex"));
|
|
float weight = float.Parse(item.GetAttribute("weight"));
|
|
OgreBoneWeight ogreBoneWeight = new OgreBoneWeight
|
|
{
|
|
VertexIndex = vIdx,
|
|
BoneIndex = boneIdx,
|
|
Weight = weight
|
|
};
|
|
this.BoneWeights[i] = ogreBoneWeight;
|
|
}
|
|
}
|
|
|
|
// subMesh
|
|
int subMeshCount = submeshesNode.ChildNodes.Count;
|
|
SubMesh = new OgreSubMesh[subMeshCount];
|
|
for (int i = 0; i < subMeshCount; i++)
|
|
{
|
|
XmlNode node = submeshesNode.ChildNodes[i];
|
|
XmlNode facesNode = node.FirstChild;
|
|
string material = node.Attributes["material"].Value;
|
|
int count = int.Parse(facesNode.Attributes["count"].Value);
|
|
SubMesh[i] = new OgreSubMesh
|
|
{
|
|
Material = material,
|
|
FaceCount = count,
|
|
Triangles = new int[count * 3]
|
|
};
|
|
|
|
int triangleIdx = 0;
|
|
for (int j = 0; j < facesNode.ChildNodes.Count; j++, triangleIdx += 3)
|
|
{
|
|
XmlNode faceNode = facesNode.ChildNodes[j];
|
|
int v1 = int.Parse(faceNode.Attributes["v1"].Value);
|
|
int v2 = int.Parse(faceNode.Attributes["v2"].Value);
|
|
int v3 = int.Parse(faceNode.Attributes["v3"].Value);
|
|
SubMesh[i].Triangles[triangleIdx] = v1;
|
|
SubMesh[i].Triangles[triangleIdx + 1] = v2;
|
|
SubMesh[i].Triangles[triangleIdx + 2] = v3;
|
|
}
|
|
}
|
|
|
|
// subMeshName
|
|
ReadSubMeshNames(submeshnamesNode);
|
|
}
|
|
|
|
private void ReadSubMeshNames(XmlNode subMeshNameNode)
|
|
{
|
|
int count = subMeshNameNode.ChildNodes.Count;
|
|
this.SubMeshName = new OgreSubMeshName[count];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
XmlElement item = (XmlElement)subMeshNameNode.ChildNodes[i];
|
|
string name = item.GetAttribute("name");
|
|
int index = int.Parse(item.GetAttribute("index"));
|
|
this.SubMeshName[i] = new OgreSubMeshName
|
|
{
|
|
Name = name,
|
|
Index = index
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
public class OgreSkeletonData
|
|
{
|
|
public string BlendMode { get; private set; }
|
|
public OgreSkeletonBone[] Bones { get; private set; }
|
|
public OgreBoneHierarchy[] BoneHierarchies { get; private set; }
|
|
public AnimationClip[] Clips { get; private set; }
|
|
|
|
public void ReadXml(string filename)
|
|
{
|
|
XmlDocument xml = new XmlDocument();
|
|
xml.Load(filename);
|
|
|
|
var root = xml.DocumentElement;
|
|
this.BlendMode = root.GetAttribute("blendmode");
|
|
|
|
XmlNode bonesNode = root.ChildNodes[0];
|
|
this.Bones = new OgreSkeletonBone[bonesNode.ChildNodes.Count];
|
|
for (int i = 0; i < bonesNode.ChildNodes.Count; i++)
|
|
{
|
|
OgreSkeletonBone bone = new OgreSkeletonBone();
|
|
|
|
XmlElement boneNode = bonesNode.ChildNodes[i] as XmlElement;
|
|
bone.Id = int.Parse(boneNode.GetAttribute("id"));
|
|
bone.Name = boneNode.GetAttribute("name");
|
|
bone.Pos = Vector3.zero;
|
|
bone.Rot = Quaternion.identity;
|
|
bone.Scale = Vector3.one;
|
|
|
|
if (boneNode.SelectSingleNode("position") is XmlElement posNode)
|
|
{
|
|
bone.Pos = posNode.ReadVector3();
|
|
}
|
|
|
|
if (boneNode.SelectSingleNode("rotation") is XmlElement rotNode)
|
|
{
|
|
// 弧度
|
|
float angle = float.Parse(rotNode.GetAttribute("angle"));
|
|
float deg = Mathf.Rad2Deg * angle;
|
|
XmlElement axisNode = (XmlElement)rotNode.FirstChild;
|
|
Vector3 axis = axisNode.ReadVector3();
|
|
bone.Rot = Quaternion.AngleAxis(deg, axis).normalized;
|
|
}
|
|
|
|
if (boneNode.SelectSingleNode("scale") is XmlElement scaleNode)
|
|
{
|
|
bone.Scale = scaleNode.ReadVector3();
|
|
}
|
|
|
|
this.Bones[i] = bone;
|
|
}
|
|
|
|
XmlNode bonehierarchyNode = root.ChildNodes[1];
|
|
this.BoneHierarchies = new OgreBoneHierarchy[bonehierarchyNode.ChildNodes.Count];
|
|
for (int i = 0; i < bonehierarchyNode.ChildNodes.Count; i++)
|
|
{
|
|
XmlElement item = bonehierarchyNode.ChildNodes[i] as XmlElement;
|
|
OgreBoneHierarchy h = new OgreBoneHierarchy();
|
|
h.Bone = item.GetAttribute("bone");
|
|
h.Parent = item.GetAttribute("parent");
|
|
BoneHierarchies[i] = h;
|
|
}
|
|
|
|
XmlNode animationsNode = root.SelectSingleNode("animations");
|
|
if (animationsNode != null)
|
|
{
|
|
int animsNum = animationsNode.ChildNodes.Count;
|
|
Clips = new AnimationClip[animsNum];
|
|
for (int i = 0; i < animsNum; i++)
|
|
{
|
|
XmlElement anim = animationsNode.ChildNodes[i] as XmlElement;
|
|
string name = anim.GetAttribute("name");
|
|
float length = float.Parse(anim.GetAttribute("length"));
|
|
|
|
AnimationClip animationClip = new AnimationClip();
|
|
animationClip.name = name;
|
|
Clips[i] = animationClip;
|
|
|
|
XmlNode tracksNode = anim.FirstChild;
|
|
for (int j = 0; j < tracksNode.ChildNodes.Count; j++)
|
|
{
|
|
XmlElement trackNode = tracksNode.ChildNodes[j] as XmlElement;
|
|
string boneName = trackNode.GetAttribute("bone");
|
|
|
|
OgreSkeletonBone boneData = FindBone(boneName);
|
|
|
|
AnimationCurve posX = new AnimationCurve();
|
|
AnimationCurve posY = new AnimationCurve();
|
|
AnimationCurve posZ = new AnimationCurve();
|
|
|
|
// 欧拉角
|
|
AnimationCurve rotX = new AnimationCurve();
|
|
AnimationCurve rotY = new AnimationCurve();
|
|
AnimationCurve rotZ = new AnimationCurve();
|
|
AnimationCurve rotW = new AnimationCurve();
|
|
|
|
XmlNode keyframesNode = trackNode.FirstChild;
|
|
for (int k = 0; k < keyframesNode.ChildNodes.Count; k++)
|
|
{
|
|
XmlElement keyframeNode = keyframesNode.ChildNodes[k] as XmlElement;
|
|
float time = float.Parse(keyframeNode.GetAttribute("time"));
|
|
|
|
Vector3 vector3 = Vector3.zero;
|
|
Quaternion q = Quaternion.identity;
|
|
|
|
foreach (XmlNode itemNodeTmp in keyframeNode.ChildNodes)
|
|
{
|
|
XmlElement itemNode = itemNodeTmp as XmlElement;
|
|
switch (itemNode.Name)
|
|
{
|
|
case "translate":
|
|
vector3 = itemNode.ReadVector3();
|
|
|
|
// 位置
|
|
vector3 += boneData.Pos;
|
|
posX.AddKey(time, vector3.x);
|
|
posY.AddKey(time, vector3.y);
|
|
posZ.AddKey(time, vector3.z);
|
|
|
|
break;
|
|
case "rotate":
|
|
// 保存的是弧度
|
|
float angle = float.Parse(itemNode.GetAttribute("angle"));
|
|
XmlElement axisNode = (XmlElement)itemNode.FirstChild;
|
|
Vector3 axis = axisNode.ReadVector3();
|
|
|
|
float deg = Mathf.Rad2Deg * angle;
|
|
q = Quaternion.AngleAxis(deg, axis).normalized;
|
|
|
|
// 旋转
|
|
q *= boneData.Rot;
|
|
rotX.AddKey(time, q.x);
|
|
rotY.AddKey(time, q.y);
|
|
rotZ.AddKey(time, q.z);
|
|
rotW.AddKey(time, q.w);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<string> relativePathList = new List<string>();
|
|
relativePathList.Add(boneName);
|
|
FindParents(boneName, relativePathList);
|
|
relativePathList.Add("skeleton");
|
|
relativePathList.Reverse();
|
|
string relativePath = Path.Combine(relativePathList.ToArray()).Replace("\\", "/");
|
|
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalPosition.x", posX);
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalPosition.y", posY);
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalPosition.z", posZ);
|
|
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalRotation.x", rotX);
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalRotation.y", rotY);
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalRotation.z", rotZ);
|
|
animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalRotation.w", rotW);
|
|
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalEulerAngles.x", rotX);
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalEulerAngles.y", rotY);
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "m_LocalEulerAngles.z", rotZ);
|
|
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "localEulerAnglesBaked.x", rotX);
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "localEulerAnglesBaked.y", rotY);
|
|
// animationClip.SetCurve(relativePath, typeof(Transform), "localEulerAnglesBaked.z", rotZ);
|
|
}
|
|
|
|
animationClip.frameRate = 24f;
|
|
Debug.Log($"读取动画: {animationClip.name} - {animationClip.length}");
|
|
}
|
|
}
|
|
|
|
// 找到骨骼的层级
|
|
void FindParents(string boneName, List<string> result)
|
|
{
|
|
var cur = this.BoneHierarchies.FirstOrDefault(f => f.Bone == boneName);
|
|
if (cur is null)
|
|
return;
|
|
|
|
// 有父
|
|
result.Add(cur.Parent);
|
|
FindParents(cur.Parent, result);
|
|
}
|
|
|
|
OgreSkeletonBone FindBone(string name)
|
|
{
|
|
return this.Bones.First(f => f.Name == name);
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct OgreSkeletonBone
|
|
{
|
|
public int Id;
|
|
public string Name;
|
|
public Vector3 Pos;
|
|
public Quaternion Rot;
|
|
public Vector3 Scale;
|
|
}
|
|
|
|
public class OgreBoneHierarchy
|
|
{
|
|
public string Bone;
|
|
public string Parent;
|
|
}
|
|
} |