/// <summary> /// /// </summary> /// <returns></returns> public IOSkeleton ToIOSkeleton() { IOSkeleton ioskel = new IOSkeleton(); Dictionary <SBBone, IOBone> sbtoio = new Dictionary <SBBone, IOBone>(); foreach (var b in GetBones()) { IOBone bone = new IOBone() { Name = b.Name, Scale = new System.Numerics.Vector3(b.SX, b.SY, b.SZ), RotationEuler = new System.Numerics.Vector3(b.RX, b.RY, b.RZ), Translation = new System.Numerics.Vector3(b.X, b.Y, b.Z), }; sbtoio.Add(b, bone); if (b.Parent == null) { ioskel.RootBones.Add(bone); } else { bone.Parent = sbtoio[b.Parent]; } } return(ioskel); }
/// <summary> /// /// </summary> /// <param name="model"></param> private List <Node> ProcessModel(IOModel model) { List <Node> nodes = new List <Node>(); // bones foreach (var root in model.Skeleton.RootBones) { nodes.Add(ProcessSkeleton(root)); } // get root bone IOBone rootBone = null; if (model.Skeleton != null && model.Skeleton.RootBones.Count > 0) { rootBone = model.Skeleton.RootBones[0]; } // mesh foreach (var mesh in model.Meshes) { nodes.Add(ProcessMesh(mesh, model, rootBone)); } return(nodes); }
/// <summary> /// /// </summary> /// <param name="bone"></param> /// <returns></returns> private HSD_JOBJ IOBoneToJOBJ(IOBone bone) { HSD_JOBJ jobj = new HSD_JOBJ() { TX = bone.TranslationX, TY = bone.TranslationY, TZ = bone.TranslationZ, RX = bone.RotationEuler.X, RY = bone.RotationEuler.Y, RZ = bone.RotationEuler.Z, SX = bone.ScaleX, SY = bone.ScaleY, SZ = bone.ScaleZ, }; if (Settings.ImportBoneNames) { jobj.ClassName = bone.Name; } _cache.NameToJOBJ.Add(bone.Name, jobj); _cache.jobjToWorldTransform.Add(jobj, MatrixNumericsToTKMatrix(bone.WorldTransform)); foreach (var child in bone.Children) { jobj.AddChild(IOBoneToJOBJ(child)); } return(jobj); }
/// <summary> /// /// </summary> /// <param name="bone"></param> /// <returns></returns> private HSD_JOBJ IOBoneToJOBJ(IOBone bone) { #if DEBUG // if (Settings.CleanRoot) { if (bone.Parent == null) { _cache.CleanRotMatrix = Matrix4.CreateFromQuaternion(new Quaternion(bone.Rotation.X, bone.Rotation.Y, bone.Rotation.Z, bone.Rotation.W)); bone.Rotation = System.Numerics.Quaternion.Identity; } else { // fix position var pos = Vector3.TransformNormal(new Vector3(bone.TranslationX, bone.TranslationY, bone.TranslationZ), _cache.CleanRotMatrix); bone.Translation = new System.Numerics.Vector3(pos.X, pos.Y, pos.Z); } // clean scale bone.Scale = new System.Numerics.Vector3(1, 1, 1); } // if (bone.Parent == null && Settings.Meleeify) { Meleeify(bone); } #endif HSD_JOBJ jobj = new HSD_JOBJ() { TX = bone.TranslationX, TY = bone.TranslationY, TZ = bone.TranslationZ, RX = bone.RotationEuler.X, RY = bone.RotationEuler.Y, RZ = bone.RotationEuler.Z, SX = bone.ScaleX, SY = bone.ScaleY, SZ = bone.ScaleZ, }; if (Settings.ImportBoneNames) { jobj.ClassName = bone.Name; } Console.WriteLine(bone.Name + " " + jobj.Children.Length); _cache.NameToJOBJ.Add(bone.Name, jobj); _cache.jobjToWorldTransform.Add(jobj, MatrixNumericsToTKMatrix(bone.WorldTransform)); foreach (var child in bone.Children) { jobj.AddChild(IOBoneToJOBJ(child)); } return(jobj); }
public static void QueueBones(IOBone b, Queue <IOBone> q, IOSkeleton Skeleton) { q.Enqueue(b); foreach (IOBone c in b.Children) { QueueBones(c, q, Skeleton); } }
/// <summary> /// /// </summary> /// <param name="bone"></param> /// <returns></returns> private Node ProcessSkeleton(IOBone bone) { Node n = new Node() { Name = bone.Name, sID = bone.Name, ID = bone.Name, Matrix = new Matrix[] { new Matrix() }, Type = Node_Type.JOINT }; n.Matrix[0].FromMatrix(bone.LocalTransform); n.node = new Node[bone.Children.Length]; int childIndex = 0; foreach (var child in bone.Children) { n.node[childIndex++] = ProcessSkeleton(child); } return(n); }
/// <summary> /// /// </summary> /// <param name="n"></param> /// <param name="bones"></param> private IOBone LoadNodes(Node n, IOBone parent, IOModel model, List <string> skeletonIds) { // create bone to represent node IOBone bone = new IOBone() { Name = n.Name, AltID = n.sID }; // load matrix if (n.Matrix != null && n.Matrix.Length >= 0) { bone.LocalTransform = n.Matrix[0].ToMatrix(); } else { // or segmented transform Vector3 scale = Vector3.One; Vector3 position = Vector3.Zero; Vector4 rx = Vector4.UnitX; Vector4 ry = Vector4.UnitY; Vector4 rz = Vector4.UnitZ; if (n.Scale != null && n.Scale.Length > 0) { var val = n.Scale[0].GetValues(); scale = new Vector3(val[0], val[1], val[2]); } if (n.Translate != null && n.Translate.Length > 0) { var val = n.Translate[0].GetValues(); position = new Vector3(val[0], val[1], val[2]); } if (n.Rotate != null && n.Rotate.Length > 0) { foreach (var r in n.Rotate) { var val = r.GetValues(); switch (r.sID) { case "rotationX": rx = new Vector4(val[0], val[1], val[2], val[3]); break; case "rotationY": ry = new Vector4(val[0], val[1], val[2], val[3]); break; case "rotationZ": rz = new Vector4(val[0], val[1], val[2], val[3]); break; } } } // create transform bone.LocalTransform = Matrix4x4.CreateTranslation(position) * Matrix4x4.CreateFromAxisAngle(new Vector3(rz.X, rz.Y, rz.Z), rz.W) * Matrix4x4.CreateFromAxisAngle(new Vector3(ry.X, ry.Y, ry.Z), ry.W) * Matrix4x4.CreateFromAxisAngle(new Vector3(rx.X, rx.Y, rx.Z), rx.W) * Matrix4x4.CreateScale(scale); } // add this node to parent if (parent != null) { parent.AddChild(bone); } // load children if (n.node != null) { foreach (var v in n.node) { LoadNodes(v, bone, model, skeletonIds); } } // load instanced geometry if (n.Instance_Geometry != null) { foreach (var g in n.Instance_Geometry) { var geom = LoadGeometryFromID(n, g.URL); geom.TransformVertices(bone.WorldTransform); geom.ParentBone = bone; model.Meshes.Add(geom); } } // load instanced geometry controllers if (n.Instance_Controller != null) { foreach (var c in n.Instance_Controller) { var geom = LoadGeometryControllerFromID(n, c.URL); geom.TransformVertices(bone.WorldTransform); geom.ParentBone = bone; model.Meshes.Add(geom); } } // detect skeleton if ((!string.IsNullOrEmpty(bone.Name) && skeletonIds.Contains(bone.Name)) || (n.Type == Node_Type.JOINT && parent == null) || (n.Instance_Camera == null && n.Instance_Controller == null && n.Instance_Geometry == null && n.Instance_Light == null && n.Instance_Node == null && parent == null && n.node != null && n.node.Length > 0)) { model.Skeleton.RootBones.Add(bone); } // complete return(bone); }
/// <summary> /// /// </summary> /// <param name="l"></param> /// <returns></returns> private IOBone CreateBoneFromNode(FbxNode l) { IOBone bone = new IOBone() { Name = GetNameWithoutNamespace(l.Properties[NodeDescSize - 2].ToString()) }; // scan and extract info from properties var properties = l.GetNodesByName("P"); if (properties.Length == 0) { properties = l.GetNodesByName("Property"); } Quaternion preRot = new Quaternion(0, 0, 0, 1); Quaternion postRot = new Quaternion(0, 0, 0, 1); foreach (var prop in properties) { if (prop.Properties.Count < PropertyDescSize) { continue; } if (prop.Properties[0].ToString() == "PreRotation") { preRot = MathExt.FromEulerAngles( MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 1]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 2])); } if (prop.Properties[0].ToString() == "PostRotation") { postRot = MathExt.FromEulerAngles( MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 1]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 2])); } if (prop.Properties[0].ToString() == "Lcl Translation") { bone.Translation = new Vector3((float)(double)prop.Properties[PropertyDescSize], (float)(double)prop.Properties[PropertyDescSize + 1], (float)(double)prop.Properties[PropertyDescSize + 2]); } if (prop.Properties[0].ToString() == "Lcl Rotation") { bone.RotationEuler = new Vector3( MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 1]), MathExt.DegToRad((float)(double)prop.Properties[PropertyDescSize + 2])); } if (prop.Properties[0].ToString() == "Lcl Scaling") { bone.Scale = new Vector3((float)(double)prop.Properties[PropertyDescSize], (float)(double)prop.Properties[PropertyDescSize + 1], (float)(double)prop.Properties[PropertyDescSize + 2]); } } bone.Rotation = preRot * bone.Rotation * postRot; return(bone); }
/// <summary> /// /// </summary> /// <param name="root"></param> private static void Meleeify(IOBone root) { Dictionary <IOBone, System.Numerics.Matrix4x4> worldTransform = new Dictionary <IOBone, System.Numerics.Matrix4x4>(); Queue <IOBone> queue = new Queue <IOBone>(); // gather final positions queue.Enqueue(root); while (queue.Count > 0) { var bone = queue.Dequeue(); worldTransform.Add(bone, bone.WorldTransform); foreach (var child in bone.Children) { queue.Enqueue(child); } } // reset skeleton queue.Enqueue(root); while (queue.Count > 0) { var bone = queue.Dequeue(); // reset rotation and scale List <string> specialBones = new List <string>() { "LShoulderJA", "RShoulderJA", "RHaveN", "LHaveN", "LLegJA", "LFootJA", "RLegJA", "RFootJA" }; List <string> commonBones = new List <string>() { "TopN", "TransN", "XRotN", "YRotN", "HipN", "WaistN", "LLegJA", "LLegJ", "LKneeJ", "LFootJA", "LFootJ", "RLegJA", "RLegJ", "RKneeJ", "RFootJA", "RFootJ", "BustN", "LShoulderN", "LShoulderJA", "LShoulderJ", "LArmJ", "LHandN", "L1stNa", "L1stNb", "L2ndNa", "L2ndNb", "L3rdNa", "L3rdNb", "L4thNa", "L4thNb", "LThumbNa", "LThumbNb", "LHandNb", "NeckN", "HeadN", "RShoulderN", "RShoulderJA", "RShoulderJ", "RArmJ", "RHandN", "R1stNa", "R1stNb", "R2ndNa", "R2ndNb", "R3rdNa", "R3rdNb", "R4thNa", "R4thNb", "RThumbNa", "RThumbNb", "RHandNb", "ThrowN", "TransN2" }; Vector3[] specialBoneRotations = new Vector3[] { new Vector3(-90, 0, 0), new Vector3(-90, 0, -180), new Vector3(0, -90, -180), new Vector3(-90, -90, 270), new Vector3(-90, 0, -90), new Vector3(0, 0, -90), new Vector3(-90, 0, -90), new Vector3(0, 0, -90) }; var boneIndex = specialBones.IndexOf(bone.Name); if (boneIndex != -1) { var r = specialBoneRotations[boneIndex]; bone.RotationEuler = new System.Numerics.Vector3((float)(r.X * Math.PI / 180), (float)(r.Y * Math.PI / 180), (float)(r.Z * Math.PI / 180)); } else if (commonBones.Contains(bone.Name)) { bone.Rotation = new System.Numerics.Quaternion(0, 0, 0, 1); } bone.Scale = new System.Numerics.Vector3(1, 1, 1); bone.Translation = new System.Numerics.Vector3(0, 0, 0); // calcuate new translation if (bone.Parent != null) { var currentPoint = System.Numerics.Vector3.Transform(System.Numerics.Vector3.Zero, bone.WorldTransform); var targetPoint = System.Numerics.Vector3.Transform(System.Numerics.Vector3.Zero, worldTransform[bone]); var dis = System.Numerics.Vector3.Subtract(targetPoint, currentPoint); if (System.Numerics.Matrix4x4.Invert(bone.Parent.WorldTransform, out System.Numerics.Matrix4x4 inverse)) { bone.Translation = System.Numerics.Matrix4x4.Multiply(worldTransform[bone], inverse).Translation; Console.WriteLine(bone.Name + " " + bone.Translation.ToString()); } } foreach (var child in bone.Children) { queue.Enqueue(child); } } }
/// <summary> /// /// </summary> /// <param name="filePath"></param> /// <returns></returns> public IOScene GetScene(string filePath) { IOScene scene = new IOScene(); IOModel model = new IOModel(); scene.Models.Add(model); using (FileStream stream = new FileStream(filePath, FileMode.Open)) using (StreamReader r = new StreamReader(stream)) { Dictionary <string, IOMesh> nameToMesh = new Dictionary <string, IOMesh>(); HashSet <IOBone> meshBones = new HashSet <IOBone>(); Dictionary <int, IOBone> idxToBone = new Dictionary <int, IOBone>(); Dictionary <int, int> idxToParent = new Dictionary <int, int>(); string mode = ""; int time = 0; while (!r.EndOfStream) { // read and clean line args var line = r.ReadLine().Trim(); var args = Regex.Replace(line, @"\s+", " ").Split(' '); // check for grouping switch (args[0]) { case "nodes": case "skeleton": case "triangles": case "end": mode = args[0]; break; } switch (mode) { case "nodes": if (args.Length >= 3) { args = line.Split('"'); var index = int.Parse(args[0].Trim()); var name = args[1]; var parentIndex = int.Parse(args[2].Trim()); IOBone bone = new IOBone() { Name = name }; idxToBone.Add(index, bone); idxToParent.Add(index, parentIndex); } break; case "skeleton": if (args.Length == 2 && args[0] == "time") { int.TryParse(args[1], out time); } if (args.Length == 7) { var index = int.Parse(args[0]); if (time == 0) { idxToBone[index].Translation = new System.Numerics.Vector3(float.Parse(args[1]), float.Parse(args[2]), float.Parse(args[3])); idxToBone[index].RotationEuler = new System.Numerics.Vector3(float.Parse(args[4]), float.Parse(args[5]), float.Parse(args[6])); } } break; case "triangles": { if (args.Length > 0 && args.Length < 9 && args[0] != "triangles") { var material = string.Join(" ", args); var v1 = ParseVertex(r.ReadLine(), idxToBone, out IOBone parent); var v2 = ParseVertex(r.ReadLine(), idxToBone, out parent); var v3 = ParseVertex(r.ReadLine(), idxToBone, out parent); var meshName = parent.Name + material; if (!meshBones.Contains(parent)) { meshBones.Add(parent); } meshName = Regex.Replace(meshName.Trim(), @"\s+", "_").Replace("#", ""); if (!nameToMesh.ContainsKey(meshName)) { // create and load material IOMaterial mat = new IOMaterial() { Name = material }; scene.Materials.Add(mat); // create io mesh var iomesh = new IOMesh() { Name = meshName }; // create triangle polygon iomesh.Polygons.Add(new IOPolygon() { MaterialName = material, PrimitiveType = IOPrimitive.TRIANGLE }); nameToMesh.Add(meshName, iomesh); } var mesh = nameToMesh[meshName]; mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count); mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count + 1); mesh.Polygons[0].Indicies.Add(mesh.Vertices.Count + 2); mesh.Vertices.Add(v1); mesh.Vertices.Add(v2); mesh.Vertices.Add(v3); } } break; } } // create skeleton hierarchy foreach (var bone in idxToBone) { var parent = idxToParent[bone.Key]; if (parent == -1) { if (meshBones.Count > 1 && meshBones.Contains(bone.Value)) { continue; } else { model.Skeleton.RootBones.Add(bone.Value); } } else { idxToBone[parent].AddChild(bone.Value); } } // dump mesh model.Meshes.AddRange(nameToMesh.Values); } return(scene); }
/// <summary> /// /// </summary> /// <returns></returns> private static IOVertex ParseVertex(string line, Dictionary <int, IOBone> idxToBones, out IOBone parentBone) { var args = Regex.Replace(line.Trim(), @"\s+", " ").Split(' '); IOVertex vertex = new IOVertex(); // parse parent index int parent = int.Parse(args[0]); // parse attributes vertex.Position = new System.Numerics.Vector3(float.Parse(args[1]), float.Parse(args[2]), float.Parse(args[3])); vertex.Normal = new System.Numerics.Vector3(float.Parse(args[4]), float.Parse(args[5]), float.Parse(args[6])); vertex.UVs.Add(new System.Numerics.Vector2(float.Parse(args[7]), float.Parse(args[8]))); // transform by parent so we can ignore it if (parent != -1) { vertex.Transform(idxToBones[parent].WorldTransform); parentBone = idxToBones[parent]; } else { parentBone = null; } // parse weights if (args.Length >= 10) { int links = int.Parse(args[9]); for (int i = 0; i < links; i++) { vertex.Envelope.Weights.Add(new IOBoneWeight() { BoneName = idxToBones[int.Parse(args[10 + i * 2])].Name, Weight = float.Parse(args[11 + i * 2]) }); } } return(vertex); }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <returns></returns> private Node ProcessMesh(IOMesh mesh, IOModel model, IOBone rootBone) { Node n = new Node() { Name = mesh.Name, sID = mesh.Name, ID = mesh.Name, Type = Node_Type.NODE }; var materials = mesh.Polygons.Select(e => e.MaterialName).Distinct(); if (mesh.HasEnvelopes()) { var geom = new Instance_Controller(); geom.URL = "#" + GenerateGeometryController(mesh, model.Skeleton); if (rootBone != null) { geom.Skeleton = new Skeleton[] { new Skeleton() { Value = "#" + rootBone.Name } } } ; n.Instance_Controller = new Instance_Controller[] { geom }; n.Instance_Controller[0].Bind_Material = new IONET.Collada.FX.Materials.Bind_Material[] { new Bind_Material() { Technique_Common = new FX.Technique_Common.Technique_Common_Bind_Material() { Instance_Material = materials.Select(e => new Instance_Material_Geometry() { Symbol = e, Target = "#" + e }).ToArray() } } }; } else { var geom = new Instance_Geometry(); geom.URL = "#" + GenerateGeometry(mesh); n.Instance_Geometry = new Instance_Geometry[] { geom }; n.Instance_Geometry[0].Bind_Material = new IONET.Collada.FX.Materials.Bind_Material[] { new Bind_Material() { Technique_Common = new FX.Technique_Common.Technique_Common_Bind_Material() { Instance_Material = materials.Select(e => new Instance_Material_Geometry() { Symbol = e, Target = "#" + e }).ToArray() } } }; } return(n); }