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; } /// /// 定义submesh的顶点索引 /// 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 relativePathList = new List(); 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 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; } }