/// <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); }
static void Main(string[] args) { ADAM6KReqService ADAM6KReqService = new ADAM6KReqService(); DeviceModel Device = new DeviceModel() { IPAddress = "172.18.3.241" }; if (ADAM6KReqService.OpenCOM(Device.IPAddress)) { Device = ADAM6KReqService.GetDevice(); string typ = ADAM6KReqService.GetDevRng(Device.ModuleType); Console.WriteLine("Get range code is [%s].", typ); IOModel IOitem = new IOModel() { Id = 40, Ch = 0, cRng = 251, }; ADAM6KReqService.UpdateIOConfig(IOitem); Console.WriteLine("Change range code is [%s].", IOitem.cRng); } Console.ReadKey(); }
static void Main(string[] args) { ADAM6KReqService ADAM6KReqService = new ADAM6KReqService(); DeviceModel Device = new DeviceModel() { IPAddress = "172.18.3.241" }; if (ADAM6KReqService.OpenCOM(Device.IPAddress)) { Device = ADAM6KReqService.GetDevice(); IOModel IOitem = new IOModel() { Id = 0, Ch = 0, cEn = 0, }; ADAM6KReqService.UpdateIOConfig(IOitem); Console.WriteLine("Change channel mask is disable."); } Console.ReadKey(); }
/// <summary> /// /// </summary> /// <param name="filePath"></param> /// <returns></returns> public IOScene GetScene(string filePath) { FbxHelper helper = new FbxHelper(FbxIO.ReadBinary(filePath)); //TODO: FBX Version 2006-2010 unupported. (Anything < 7100 works. But textures aren't grabbed) //FBX Version 2011-2020 <-- Tested & Supported! //FBX Version 2011-2020 currently supported if (helper.Version < 7100) { throw new NotSupportedException($"FBX Version {helper.Version} not supported"); } IOScene scene = new IOScene(); IOModel model = new IOModel(); model.Skeleton = helper.GetSkeleton(); scene.Models.Add(model); model.Meshes.AddRange(helper.ExtractMesh()); scene.Materials.AddRange(helper.GetMaterials()); return(scene); }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <returns></returns> private Node ProcessMesh(IOMesh mesh, IOModel model) { 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); 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); }
/// <summary> /// Gets the model information in this scene as an IO Model /// </summary> /// <returns></returns> public override IOModel GetIOModel() { IOModel iomodel = new IOModel(); iomodel.Skeleton = (SBSkeleton)Skeleton; foreach (var mesh in Model.Meshes) { var iomesh = new IOMesh(); iomesh.Name = mesh.Name; iomodel.Meshes.Add(iomesh); iomesh.HasPositions = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.Position0); iomesh.HasNormals = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.Normal0); iomesh.HasUV0 = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.map1); iomesh.HasUV1 = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet); iomesh.HasUV2 = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet1); iomesh.HasUV3 = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.uvSet2); iomesh.HasColor = mesh.ExportAttributes.Contains(SSBHLib.Tools.MESHAttribute.colorSet1); iomesh.HasBoneWeights = true; iomesh.Indices.AddRange(mesh.Indices); foreach (var vertex in mesh.Vertices) { var iovertex = new IOVertex(); iovertex.Position = vertex.Position0; iovertex.Normal = vertex.Normal0; iovertex.Tangent = vertex.Tangent0; iovertex.UV0 = vertex.Map1; iovertex.UV1 = vertex.UvSet; iovertex.UV2 = vertex.UvSet1; iovertex.Color = vertex.ColorSet1; iovertex.BoneIndices = new Vector4(vertex.BoneIndices.X, vertex.BoneIndices.Y, vertex.BoneIndices.Z, vertex.BoneIndices.W); iovertex.BoneWeights = vertex.BoneWeights; // single bind fix if (mesh.ParentBone != "" && Skeleton != null) { var parentBone = Skeleton[mesh.ParentBone]; if (parentBone != null) { iovertex.Position = Vector3.TransformPosition(vertex.Position0, parentBone.WorldTransform); iovertex.Normal = Vector3.TransformNormal(vertex.Normal0, parentBone.WorldTransform); iovertex.BoneIndices = new Vector4(Skeleton.IndexOfBone(parentBone), 0, 0, 0); iovertex.BoneWeights = new Vector4(1, 0, 0, 0); } } iomesh.Vertices.Add(iovertex); } } return(iomodel); }
static void Main(string[] args) { ADAM6KReqService ADAM6KReqService = new ADAM6KReqService(); DeviceModel Device = new DeviceModel() { IPAddress = "172.18.3.188" }; if (ADAM6KReqService.OpenCOM(Device.IPAddress)) { Device = ADAM6KReqService.GetDevice(); List <IOModel> IO_Data = (List <IOModel>)ADAM6KReqService.GetListOfIOItems(""); // IOModel IOitem = new IOModel();//need to get twice. foreach (var item in (List <IOModel>)ADAM6KReqService.GetListOfIOItems("")) { if (item.Id == 0 && item.Ch == 0) { IOitem = new IOModel() { Id = item.Id, Ch = item.Ch, Tag = item.Tag, Val = item.Val, En = item.En, //DI Md = item.Md, Inv = item.Inv, Fltr = item.Fltr, FtLo = item.FtLo, FtHi = item.FtHi, FqT = item.FqT, FqP = item.FqP, CntIV = item.CntIV, CntKp = item.CntKp, OvLch = item.OvLch, }; } } IOitem.Md = 2; IOitem.Inv = 0; IOitem.Fltr = 1; // IOModel IOitem = new IOModel() //{ // Id = 0, // Ch = 0, // cEn = 0, //}; ADAM6KReqService.UpdateIOConfig(IOitem); Console.WriteLine("Change channel mask is disable."); } Console.ReadKey(); }
public static void ExportIOModelAsSMD(string FileName, IOModel Model) { SMD file = new SMD(); if (Model.HasSkeleton) { var bonelist = new List <SBBone>(Model.Skeleton.Bones); var frame = new SMDSkeletonFrame(); file.skeleton.Add(frame); frame.time = 0; foreach (var bone in bonelist) { file.nodes.Add(new SMDNode() { Name = bone.Name, ID = bonelist.IndexOf(bone), ParentID = bonelist.IndexOf(bone.Parent) }); frame.skeletons.Add(new SMDSkeleton() { BoneID = bonelist.IndexOf(bone), Position = bone.Translation, Rotation = bone.RotationEuler }); } } if (Model.HasMeshes) { Dictionary <string, int> UniqueMeshNames = new Dictionary <string, int>(); foreach (var mesh in Model.Meshes) { if (!UniqueMeshNames.ContainsKey(mesh.Name)) { UniqueMeshNames.Add(mesh.Name, 0); } string Name = mesh.Name + (UniqueMeshNames[mesh.Name] == 0 ? "" : "_" + UniqueMeshNames[mesh.Name]); UniqueMeshNames[mesh.Name]++; for (int i = 0; i < mesh.Indices.Count; i += 3) { var triangle = new SMDTriangle(); triangle.Material = Name; triangle.vertex1 = IOVertexToSMDVertex(mesh.Vertices[(int)mesh.Indices[i + 0]]); triangle.vertex2 = IOVertexToSMDVertex(mesh.Vertices[(int)mesh.Indices[i + 1]]); triangle.vertex3 = IOVertexToSMDVertex(mesh.Vertices[(int)mesh.Indices[i + 2]]); file.triangles.Add(triangle); } } } file.Save(FileName); }
/// <summary> /// /// </summary> /// <param name="model"></param> private static void InvertSingleBinds(IOModel model) { foreach (var m in model.Meshes) { foreach (var v in m.Vertices) { if (v.Envelope.Weights.Count > 0 && v.Envelope.Weights[0].Weight == 1) { System.Numerics.Matrix4x4.Invert(model.Skeleton.GetBoneByName(v.Envelope.Weights[0].BoneName).WorldTransform, out System.Numerics.Matrix4x4 inv); v.Transform(inv); } } } }
public override bool ExportToFile(IOModel model, string filename) { var tempFile = Path.Combine(Path.GetDirectoryName(filename), "_proxy.obj"); var tempMtlFile = Path.Combine(Path.GetDirectoryName(filename), "_proxy.mtl"); var objExporter = new ObjExporter(_settings, _callback); var objData = objExporter.ExportToFile(model, tempFile); AssimpContext context = new AssimpContext(); context.SetConfig(new NormalSmoothingAngleConfig(90.0f)); Scene scene = context.ImportFile(tempFile); switch (_exporter) { case Exporter.Collada: { context.ExportFile(scene, filename, "collada"); // Open up exported collada file and fix all texture filenames to relative XDocument exported = XDocument.Load(filename); foreach (var node in exported.Root.DescendantNodes()) { if (node is XElement) { var element = (XElement)node; if (element.Name.LocalName == "init_from" && element.Value.EndsWith(".png")) { element.Value = Path.GetFileName(element.Value); } } } exported.Save(filename); } break; case Exporter.GLTF2: context.ExportFile(scene, filename, "gltf2"); break; case Exporter.X3d: context.ExportFile(scene, filename, "x3d"); break; } File.Delete(tempFile); File.Delete(tempMtlFile); return(true); }
public override void FromIOModel(IOModel iomodel) { //System.Windows.Forms.MessageBox.Show("Importing Model to DAT not supported"); // use the existing skeleton always iomodel.ConvertToSkeleton((SBSkeleton)Skeleton); // single bound vertices are stored in inverse transform positions iomodel.InvertSingleBinds(); // dobjs to import to var dobjs = GetMeshObjects(); foreach (SBHsdMesh m in GetMeshObjects()) { m.DOBJ.Pobj = null; } var attributeGroup = MakeRiggedAttributes(); var singleAttributeGroup = MakeSingleAttributes(); // get a compressor ready // the compressor will handle making the compressed attribute buffers POBJ_Generator compressor = new POBJ_Generator(); // import the iomeshes into their respective dobjs foreach (var iomesh in iomodel.Meshes) { int dobjId = -1; int.TryParse(iomesh.Name.Replace("DOBJ_", ""), out dobjId); SBConsole.WriteLine(iomesh.Name + " imported:" + (dobjId != -1)); if (dobjId != -1) { var dobj = (SBHsdMesh)dobjs[dobjId]; dobj.ImportPOBJs(iomesh, (SBSkeleton)Skeleton, compressor, dobj.ParentBone == "JOBJ_0" ? attributeGroup : singleAttributeGroup); } } // finalizes and remakes the buffer compressor.SaveChanges(); // refresh everything RefreshRendering(); }
/// <summary> /// Imports information into this scene from an IO Model /// </summary> public override void FromIOModel(IOModel iomodel) { // copy skeleton Skeleton = iomodel.Skeleton; // make temp material UltimateMaterial material = new UltimateMaterial(); material.Name = "SFX_PBS_0100080008008269_opaque"; material.Label = "skin_sonic_001"; //TODO more elegant material management Materials.Add(material); // convert meshes SBUltimateModel model = new SBUltimateModel(); foreach (var iomesh in iomodel.Meshes) { SBUltimateMesh <UltimateVertex> mesh = new SBUltimateMesh <UltimateVertex>(); mesh.Name = iomesh.Name; mesh.ParentBone = ""; mesh.Material = material; model.Meshes.Add(mesh); mesh.Indices = iomesh.Indices; iomesh.GenerateTangentsAndBitangents(); foreach (var vertex in iomesh.Vertices) { mesh.Vertices.Add(IOToUltimateVertex(vertex)); } //TODO: make more customizable through import settings mesh.ExportAttributes.Add(SSBHLib.Tools.MESHAttribute.Position0); mesh.ExportAttributes.Add(SSBHLib.Tools.MESHAttribute.Normal0); mesh.ExportAttributes.Add(SSBHLib.Tools.MESHAttribute.Tangent0); mesh.ExportAttributes.Add(SSBHLib.Tools.MESHAttribute.map1); mesh.ExportAttributes.Add(SSBHLib.Tools.MESHAttribute.colorSet1); } Model = model; }
/// <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)); } // mesh foreach (var mesh in model.Meshes) { nodes.Add(ProcessMesh(mesh, model)); } return(nodes); }
public void ExportIOModel(string FileName, IOModel model) { StringBuilder o = new StringBuilder(); // keep track of used names so we don't have name overlap Dictionary <string, int> UsedNames = new Dictionary <string, int>(); // uuuggh int VertexCount = 1; foreach (IOMesh mesh in model.Meshes) { // append the index number for this mesh name string Meshname = mesh.Name; if (UsedNames.ContainsKey(mesh.Name)) { UsedNames[mesh.Name] += 1; o.AppendLine($"g _{UsedNames[mesh.Name]}"); } else { UsedNames.Add(mesh.Name, 0); o.AppendLine($"o {Meshname}"); o.AppendLine($"g _{UsedNames[mesh.Name]}"); } foreach (IOVertex v in mesh.Vertices) { o.AppendLine($"v {v.Position.X} {v.Position.Y} {v.Position.Z}"); o.AppendLine($"vn {v.Normal.X} {v.Normal.Y} {v.Normal.Z}"); o.AppendLine($"vt {v.UV0.X} {v.UV0.Y}"); } for (int i = 0; i < mesh.Indices.Count; i += 3) { o.AppendLine($"f {VertexCount + mesh.Indices[i]}/{VertexCount + mesh.Indices[i]}/{VertexCount + mesh.Indices[i]}" + $" {VertexCount + mesh.Indices[i + 1]}/{VertexCount + mesh.Indices[i + 1]}/{VertexCount + mesh.Indices[i + 1]}" + $" {VertexCount + mesh.Indices[i + 2]}/{VertexCount + mesh.Indices[i + 2]}/{VertexCount + mesh.Indices[i + 2]}"); } VertexCount += mesh.Vertices.Count; } System.IO.File.WriteAllText(FileName, o.ToString()); }
internal static IOModel IOModelFactory(IOType ioType, string args, ProcessWrapper.ProcessControlHandler handler) { IOModel outModel = null; switch (ioType) { case IOType.StdIO: outModel = new StdIOModel(); break; case IOType.PIPES: outModel = new PipeIOModel(); break; case IOType.QUEUES: outModel = new QueueIOModel(); break; } if (outModel != null) { outModel.BaseInit(args); outModel.SetReadHandler(handler); } return(outModel); }
double AIRead()//unit: V { IOModel DevIO = new IOModel(); foreach (var item in ADAM6KReqService.GetListOfIOItems("")) { var readInfo = (IOModel)item; if (readInfo.Id.Equals(ai_id_offset + Ref_IO_Mod.Ch)) { DevIO = readInfo; break; } } double rData = 0.0; //判斷輸出的數值單位 if (//Uni-polar DevIO.Rng == (int)ValueRange.mV_0To150 || DevIO.Rng == (int)ValueRange.mV_0To500 //Bi-polar || DevIO.Rng == (int)ValueRange.mV_Neg15To15 || DevIO.Rng == (int)ValueRange.mV_Neg50To50 || DevIO.Rng == (int)ValueRange.mV_Neg100To100 || DevIO.Rng == (int)ValueRange.mV_Neg150To150 || DevIO.Rng == (int)ValueRange.mV_Neg500To500) { rData = (double)DevIO.Val / 1000000; } else if (DevIO.Rng == (int)ValueRange.Jtype_0To760C || DevIO.Rng == (int)ValueRange.Ktype_0To1370C || DevIO.Rng == (int)ValueRange.Ttype_Neg100To400C || DevIO.Rng == (int)ValueRange.Etype_0To1000C || DevIO.Rng == (int)ValueRange.Rtype_500To1750C || DevIO.Rng == (int)ValueRange.Stype_500To1750C || DevIO.Rng == (int)ValueRange.Btype_500To1800C) { rData = (double)DevIO.Val; } else { rData = (double)DevIO.Val / 1000; } return(rData); }
public void ExportIOModel(string FileName, IOModel model) { DialogResult UV1dialog = MessageBox.Show("Export UV1?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (UV1dialog == DialogResult.Yes) { ExportUV1 = true; } DialogResult UV2dialog = MessageBox.Show("Export UV2?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (UV2dialog == DialogResult.Yes) { ExportUV2 = true; } DialogResult UV3dialog = MessageBox.Show("Export UV3?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (UV3dialog == DialogResult.Yes) { ExportUV3 = true; } DialogResult Bonedialog = MessageBox.Show("Export Bone weights?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (Bonedialog == DialogResult.Yes) { ExportBoneWeights = true; } StringBuilder csv = new StringBuilder(); if (model.HasMeshes) { foreach (IOMesh mesh in model.Meshes) { UVNum = 1; if (ExportUV1 == true) { UVNum++; } if (ExportUV2 == true) { UVNum++; } if (ExportUV3 == true) { UVNum++; } csv.Append($"Obj Name:{mesh.Name}\n"); if (ExportBoneWeights == true) { if (mesh.HasBoneWeights) { csv.Append("Bone_Suport\n"); } } csv.Append($"UV_Num:{UVNum}\n"); csv.Append("vert_Array\n"); foreach (IOVertex v in mesh.Vertices) { csv.Append($"{v.Position.X},{v.Position.Y},{v.Position.Z}\n"); if (mesh.HasNormals) { csv.Append($"{v.Normal.X},{v.Normal.Y},{v.Normal.Z}\n"); } else { csv.Append("0.000000,0.000000,1.000000\n"); } if (mesh.HasColor) { //Let's convert these floats to byte range with this dirty trick csv.Append($"{v.Color.X * 255 % 256},{v.Color.Y * 255 % 256},{v.Color.Z * 255 % 256},{v.Color.W * 255 % 256}\n"); } else { csv.Append("127,127,127,255\n"); } if (mesh.HasUV0) { csv.Append($"{v.UV0.X},{v.UV0.Y}\n"); } else { csv.Append("0.000000,0.000000\n"); } if (ExportUV1 == true) { if (mesh.HasUV1) { csv.Append($"{v.UV1.X},{v.UV1.Y}\n"); } else { csv.Append("0.000000,0.000000\n"); } } if (ExportUV2 == true) { if (mesh.HasUV2) { csv.Append($"{v.UV2.X},{v.UV2.Y}\n"); } else { csv.Append("0.000000,0.000000\n"); } } if (ExportUV3 == true) { if (mesh.HasUV3) { csv.Append($"{v.UV3.X},{v.UV3.Y}\n"); } else { csv.Append("0.000000,0.000000\n"); } } } csv.Append("face_Array\n"); for (int i = 0; i < mesh.Indices.Count; i += 3) { csv.Append($"{mesh.Indices[i] + 1},{mesh.Indices[i + 1] + 1},{mesh.Indices[i + 2] + 1}\n"); } if (ExportBoneWeights == true) { csv.Append("bone_Array\n"); foreach (var v in mesh.Vertices) { if (v.BoneWeights[0] != 0) { csv.Append($"{model.Skeleton.Bones[(int)v.BoneIndices.X].Name},{v.BoneWeights[0]},"); } if (v.BoneWeights[1] != 0) { csv.Append($"{model.Skeleton.Bones[(int)v.BoneIndices.Y].Name},{v.BoneWeights[1]},"); } if (v.BoneWeights[2] != 0) { csv.Append($"{model.Skeleton.Bones[(int)v.BoneIndices.Z].Name},{v.BoneWeights[2]},"); } if (v.BoneWeights[3] != 0) { csv.Append($"{model.Skeleton.Bones[(int)v.BoneIndices.W].Name},{v.BoneWeights[3]},"); } csv.Append("\n"); } } } } File.WriteAllText(FileName, csv.ToString()); }
//============================================// void GetDeviceItems(int id) { //var obj = ADAM6KReqService.GetListOfIOItems(""); bool flg = false; List <IOModel> IO_Data = (List <IOModel>)ADAM6KReqService.GetListOfIOItems(""); if (id >= di_id_offset) { foreach (var item in IO_Data) { if (item.Id == id && item.Ch == id - di_id_offset) { IOitem = new IOModel() { Id = item.Id, Ch = item.Ch, Tag = item.Tag, Val = item.Val, //AI En = item.En, Rng = item.Rng, Evt = item.Evt, LoA = item.LoA, HiA = item.HiA, Eg = item.Eg, EgF = item.EgF, Val_Eg = item.Val_Eg, cEn = item.cEn, cRng = item.cRng, EnLA = item.EnLA, EnHA = item.EnHA, LAMd = item.LAMd, HAMd = item.HAMd, cLoA = item.cLoA, cHiA = item.cHiA, LoS = item.LoS, HiS = item.HiS, // add basic Res = item.Res, EnB = item.EnB, BMd = item.BMd, AiT = item.AiT, Smp = item.Smp, AvgM = item.AvgM, //DI Md = item.Md, Inv = item.Inv, Fltr = item.Fltr, FtLo = item.FtLo, FtHi = item.FtHi, FqT = item.FqT, FqP = item.FqP, CntIV = item.CntIV, CntKp = item.CntKp, OvLch = item.OvLch, //DO FSV = item.FSV, PsLo = item.PsLo, PsHi = item.PsHi, HDT = item.HDT, LDT = item.LDT, ACh = item.ACh, AMd = item.AMd, }; } } } //if (id >= ai_id_offset) { //ViewData = IOitem; chiTxtbox.Text = (id - di_id_offset).ToString(); //return true; } //else if (id >= do_id_offset) { mod = DOitem; } flg = true; }
/// <summary> /// Imports an IO model into the scene /// </summary> /// <param name="iomodel"></param> public virtual void FromIOModel(IOModel iomodel) { }
public IOModel GetIOModel() { IOModel outModel = new IOModel(); Mesh meshFile = null; Matl materialFile = null; foreach (FileNode n in Parent.Nodes) { if (n.Text.Equals(model.MeshString)) { meshFile = ((NumsbhNode)n).mesh; } if (n.Text.Equals(model.SkeletonFileName)) { outModel.Skeleton = (RSkeleton)((SkelNode)n).GetRenderableNode(); } if (n.Text.Equals(model.MaterialFileNames[0].MaterialFileName)) { materialFile = ((MatlNode)n).Material; } } Dictionary <string, int> indexByBoneName = new Dictionary <string, int>(); if (outModel.Skeleton != null) { for (int i = 0; i < outModel.Skeleton.Bones.Count; i++) { indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i); } } Dictionary <string, int> materialNameToIndex = new Dictionary <string, int>(); if (materialFile != null) { int materialIndex = 0; foreach (var entry in materialFile.Entries) { materialNameToIndex.Add(entry.ShaderLabel, materialIndex++); IOMaterial material = new IOMaterial { Name = entry.ShaderLabel }; outModel.Materials.Add(material); foreach (var attr in entry.Attributes) { if (attr.ParamId == MatlEnums.ParamId.Texture0) { IOTexture dif = new IOTexture { Name = attr.DataObject.ToString() }; material.DiffuseTexture = dif; } } } } if (meshFile != null) { SsbhVertexAccessor vertexAccessor = new SsbhVertexAccessor(meshFile); { SsbhRiggingAccessor riggingAccessor = new SsbhRiggingAccessor(meshFile); foreach (MeshObject obj in meshFile.Objects) { IOMesh outMesh = new IOMesh() { Name = obj.Name, }; outModel.Meshes.Add(outMesh); // get material if (materialFile != null) { foreach (var entry in model.ModelEntries) { if (entry.MeshName.Equals(obj.Name) && entry.SubIndex == obj.SubMeshIndex) { outMesh.MaterialIndex = materialNameToIndex[entry.MaterialLabel]; break; } } } IOVertex[] vertices = new IOVertex[obj.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new IOVertex(); } foreach (MeshAttribute attr in obj.Attributes) { SsbhVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj); if (attr.AttributeStrings[0].Name.Equals("Position0")) { outMesh.HasPositions = true; for (int i = 0; i < values.Length; i++) { vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("Normal0")) { outMesh.HasNormals = true; for (int i = 0; i < values.Length; i++) { vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } // Flip UVs vertically for export. if (attr.AttributeStrings[0].Name.Equals("map1")) { outMesh.HasUV0 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV0 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet")) { outMesh.HasUV1 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV1 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet1")) { outMesh.HasUV2 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV2 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet2")) { outMesh.HasUV3 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV3 = new OpenTK.Vector2(values[i].X, 1 - values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("colorSet1")) { outMesh.HasColor = true; for (int i = 0; i < values.Length; i++) { vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W) / 127f; } } } // Fix SingleBinds if (outModel.Skeleton != null && !obj.ParentBoneName.Equals("")) { int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName); if (parentIndex != -1) { for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].Normal = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName]; vertices[i].BoneWeights.X = 1; outMesh.HasBoneWeights = true; } } } // Apply Rigging SsbhVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex); foreach (SsbhVertexInfluence influence in influences) { outMesh.HasBoneWeights = true; // Some influences refer to bones that don't exist in the skeleton. // _eff bones? if (!indexByBoneName.ContainsKey(influence.BoneName)) { continue; } if (vertices[influence.VertexIndex].BoneWeights.X == 0) { vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.X = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Y == 0) { vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Z == 0) { vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.W == 0) { vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.W = influence.Weight; } } outMesh.Vertices.AddRange(vertices); outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj)); } } } return(outModel); }
public void ExportIOModel(string FileName, IOModel model) { using (StudioSB.GUI.SBCustomDialog d = new GUI.SBCustomDialog(ExportSettings)) { if (d.ShowDialog() != System.Windows.Forms.DialogResult.OK) { return; } } using (DAEWriter writer = new DAEWriter(FileName, false)) { writer.WriteAsset(); // Material IO needs big rework if (model.HasMaterials && ExportSettings.ExportTextures) { List <string> TextureNames = new List <string>(); var png = false; foreach (var tex in model.Textures) { var filePath = System.IO.Path.GetDirectoryName(FileName) + "\\" + tex.Name; TextureNames.Add(tex.Name); if (tex.InternalFormat == OpenTK.Graphics.OpenGL.InternalFormat.Rgba) { png = true; Tools.FileTools.WriteBitmapFile(filePath + ".png", tex.Width, tex.Height, tex.Arrays[0].Mipmaps[0]); } else { IO_DDS.Export(filePath + ".dds", tex); } } writer.WriteLibraryImages(TextureNames.ToArray(), png ? ".png" : ".dds"); writer.StartMaterialSection(); foreach (var mat in model.Materials) { writer.WriteMaterial(mat.Name); } writer.EndMaterialSection(); writer.StartEffectSection(); foreach (var mat in model.Materials) { writer.WriteEffect(mat.Name, mat.DiffuseTexture); } writer.EndEffectSection(); } else { writer.WriteLibraryImages(); } if (model.HasSkeleton) { foreach (var bone in model.Skeleton.Bones) { float[] Transform = new float[] { bone.Transform.M11, bone.Transform.M21, bone.Transform.M31, bone.Transform.M41, bone.Transform.M12, bone.Transform.M22, bone.Transform.M32, bone.Transform.M42, bone.Transform.M13, bone.Transform.M23, bone.Transform.M33, bone.Transform.M43, bone.Transform.M14, bone.Transform.M24, bone.Transform.M34, bone.Transform.M44 }; float[] InvTransform = new float[] { bone.InvWorldTransform.M11, bone.InvWorldTransform.M21, bone.InvWorldTransform.M31, bone.InvWorldTransform.M41, bone.InvWorldTransform.M12, bone.InvWorldTransform.M22, bone.InvWorldTransform.M32, bone.InvWorldTransform.M42, bone.InvWorldTransform.M13, bone.InvWorldTransform.M23, bone.InvWorldTransform.M33, bone.InvWorldTransform.M43, bone.InvWorldTransform.M14, bone.InvWorldTransform.M24, bone.InvWorldTransform.M34, bone.InvWorldTransform.M44 }; writer.AddJoint(bone.Name, bone.Parent == null ? "" : bone.Parent.Name, Transform, InvTransform); } } writer.StartGeometrySection(); foreach (var mesh in model.Meshes) { writer.StartGeometryMesh(mesh.Name); if (mesh.MaterialIndex != -1) { writer.CurrentMaterial = model.Materials[mesh.MaterialIndex].Name; } // collect sources List <float> Position = new List <float>(); List <float> Normal = new List <float>(); List <float> UV0 = new List <float>(); List <float> UV1 = new List <float>(); List <float> UV2 = new List <float>(); List <float> UV3 = new List <float>(); List <float> Color = new List <float>(); List <int[]> BoneIndices = new List <int[]>(); List <float[]> BoneWeights = new List <float[]>(); foreach (var vertex in mesh.Vertices) { Position.Add(vertex.Position.X); Position.Add(vertex.Position.Y); Position.Add(vertex.Position.Z); Normal.Add(vertex.Normal.X); Normal.Add(vertex.Normal.Y); Normal.Add(vertex.Normal.Z); UV0.Add(vertex.UV0.X); UV0.Add(vertex.UV0.Y); UV1.Add(vertex.UV1.X); UV1.Add(vertex.UV1.Y); UV2.Add(vertex.UV2.X); UV2.Add(vertex.UV2.Y); UV3.Add(vertex.UV3.X); UV3.Add(vertex.UV3.Y); Color.AddRange(new float[] { vertex.Color.X, vertex.Color.Y, vertex.Color.Z, vertex.Color.W }); List <int> bIndices = new List <int>(); List <float> bWeights = new List <float>(); if (vertex.BoneWeights.X > 0) { bIndices.Add((int)vertex.BoneIndices.X); bWeights.Add(vertex.BoneWeights.X); } if (vertex.BoneWeights.Y > 0) { bIndices.Add((int)vertex.BoneIndices.Y); bWeights.Add(vertex.BoneWeights.Y); } if (vertex.BoneWeights.Z > 0) { bIndices.Add((int)vertex.BoneIndices.Z); bWeights.Add(vertex.BoneWeights.Z); } if (vertex.BoneWeights.W > 0) { bIndices.Add((int)vertex.BoneIndices.W); bWeights.Add(vertex.BoneWeights.W); } BoneIndices.Add(bIndices.ToArray()); BoneWeights.Add(bWeights.ToArray()); } // write sources if (mesh.HasPositions) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.POSITION, Position.ToArray(), mesh.Indices.ToArray()); } if (mesh.HasNormals) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.NORMAL, Normal.ToArray(), mesh.Indices.ToArray()); } if (mesh.HasColor) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.COLOR, Color.ToArray(), mesh.Indices.ToArray()); } if (mesh.HasUV0) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.TEXCOORD, UV0.ToArray(), mesh.Indices.ToArray(), 0); } if (mesh.HasUV1) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.TEXCOORD, UV1.ToArray(), mesh.Indices.ToArray(), 1); } if (mesh.HasUV2) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.TEXCOORD, UV2.ToArray(), mesh.Indices.ToArray(), 2); } if (mesh.HasUV3) { writer.WriteGeometrySource(mesh.Name, DAEWriter.VERTEX_SEMANTIC.TEXCOORD, UV3.ToArray(), mesh.Indices.ToArray(), 3); } if (mesh.HasBoneWeights) { writer.AttachGeometryController(BoneIndices, BoneWeights); } writer.EndGeometryMesh(); } writer.EndGeometrySection(); } }
public void ExportIOModel(string FileName, IOModel model) { int num = 0; string filepath = Path.GetDirectoryName(FileName); string filename = Path.GetFileNameWithoutExtension(FileName); if (model.HasMeshes) { //.ply only support a sigle mesh, So export each mesh as a seperate file foreach (IOMesh mesh in model.Meshes) { StringBuilder o = new StringBuilder(); //Using \n instead of AppendLine because it adds a white space, no text editor can see. //No idea why. Opening it in a hex editor shows 0D //.ply doesn't support white spaces unless it's a comment o.Append("ply\n"); o.Append("format ascii 1.0\n"); o.Append("comment Created by CrossMod: https://github.com/Ploaj/CrossMod/ \n"); o.Append($"element vertex {mesh.Vertices.Count}\n"); o.Append("property float x\n" + "property float y\n" + "property float z\n"); if (mesh.HasNormals) { o.Append("property float nx\n" + "property float ny\n" + "property float nz\n"); } if (mesh.HasUV0) { o.Append("property float s\n" + "property float t\n"); } if (mesh.HasColor) { o.Append("property uchar red\n" + "property uchar green\n" + "property uchar blue\n" + "property uchar alpha\n"); } //Divide vertex count by 3 to get triangle count o.Append($"element face {mesh.Indices.Count / 3}\n"); o.Append("property list uchar uint vertex_indices\n"); o.Append("end_header\n"); foreach (IOVertex v in mesh.Vertices) { o.Append($"{v.Position.X} {v.Position.Y} {v.Position.Z}"); if (mesh.HasNormals) { o.Append($" {v.Normal.X} {v.Normal.Y} {v.Normal.Z}"); } if (mesh.HasUV0) { o.Append($" {v.UV0.X} {v.UV0.Y}"); } if (mesh.HasColor) { o.Append($" {v.Color.X} {v.Color.Y} {v.Color.Z} {v.Color.W}"); } o.Append("\n"); } for (int i = 0; i < mesh.Indices.Count; i += 3) { o.Append($"3 {mesh.Indices[i]} {mesh.Indices[i + 1]} {mesh.Indices[i + 2]}\n"); } File.WriteAllText($"{filepath}\\{filename}_{num++}.ply", o.ToString()); } } }
public override IOModel ImportFromFile(string filename) { var model = new IOModel(); var materials = new List <string>(); var positions = new List <Vector3>(); var textures = new Dictionary <int, Texture>(); using (var reader = new StreamReader(File.OpenRead(filename))) { var line = reader.ReadLine(); if (line.Trim() != "Metasequoia Document") { logger.Error("Not a valid Metasequoia file"); return(null); } while (!reader.EndOfStream) { line = reader.ReadLine().Trim(); if (line == "" || line == "}") { continue; } // Parse chunks var chunk = line.Split(' ')[0]; if (chunk == "Format") { } else if (chunk == "Thumbnail") { IgnoreChunk(reader); } else if (chunk == "Scene") { IgnoreChunk(reader); } else if (chunk == "Material") { var numMaterials = int.Parse(line.Split(' ')[1]); if (numMaterials == 0) { return(null); } for (var i = 0; i < numMaterials; i++) { var materialString = reader.ReadLine().Trim(); var tokensMaterial = materialString.Split(' '); var material = new IOMaterial(tokensMaterial[0]); for (var j = 0; j < tokensMaterial.Length; j++) { var texturePath = ""; if (tokensMaterial[j].StartsWith("tex")) { texturePath = tokensMaterial[j].Substring(5, tokensMaterial[j].Length - 7); if (texturePath != "") { string basePath = Path.GetDirectoryName(filename); if (!File.Exists(Path.Combine(basePath, texturePath))) { basePath = Path.Combine(Path.GetDirectoryName(filename), "Texture"); } if (!File.Exists(Path.Combine(basePath, texturePath))) { throw new FileNotFoundException("Texture " + texturePath + " could not be found"); } textures.Add(i, GetTexture(basePath, texturePath)); } material.Texture = textures[i]; break; } } model.Materials.Add(material); } } else if (chunk == "Object") { var name = line.Split(' ')[1]; var mesh = new IOMesh(name.Replace("\"", "")); var tokensName = mesh.Name.Split('_'); positions = new List <Vector3>(); if (name.Contains("TeRoom_")) { model.HasMultipleRooms = true; } var lastVertex = 0; var translation = Vector3.Zero; while (!reader.EndOfStream) { line = reader.ReadLine().Trim(); var tokens = line.Split(' '); if (tokens[0] == "translation" && model.HasMultipleRooms) { translation = ApplyAxesTransforms(new Vector3(ParseFloatCultureInvariant(tokens[1]), ParseFloatCultureInvariant(tokens[2]), ParseFloatCultureInvariant(tokens[3]))); } else if (tokens[0] == "vertex") { var numVertices = int.Parse(tokens[1]); for (var i = 0; i < numVertices; i++) { var tokensPosition = reader.ReadLine().Trim().Split(' '); var newPos = ApplyAxesTransforms(new Vector3(ParseFloatCultureInvariant(tokensPosition[0]), ParseFloatCultureInvariant(tokensPosition[1]), ParseFloatCultureInvariant(tokensPosition[2])) ); positions.Add(newPos); } line = reader.ReadLine().Trim(); } else if (tokens[0] == "face") { var numFaces = int.Parse(tokens[1]); for (var i = 0; i < numFaces; i++) { line = reader.ReadLine().Trim(); var numVerticesInFace = int.Parse(line.Substring(0, line.IndexOf(' '))); var poly = new IOPolygon(numVerticesInFace == 3 ? IOPolygonShape.Triangle : IOPolygonShape.Quad); // We MUST have vertices var stringVertices = GetSubBlock(line, "V"); if (stringVertices == "") { return(null); } var tokensVertices = stringVertices.Split(' '); for (var k = 0; k < numVerticesInFace; k++) { var index = int.Parse(tokensVertices[k]); mesh.Positions.Add(positions[index]); poly.Indices.Add(lastVertex); lastVertex++; } // Change vertex winding if (_settings.InvertFaces) { poly.Indices.Reverse(); } // UV var stringUV = GetSubBlock(line, "UV"); if (stringUV != "") { var tokensUV = stringUV.Split(' '); for (var k = 0; k < numVerticesInFace; k++) { var uv = ApplyUVTransform(new Vector2(ParseFloatCultureInvariant(tokensUV[2 * k]), ParseFloatCultureInvariant(tokensUV[2 * k + 1])), textures[0].Image.Width, textures[0].Image.Height); mesh.UV.Add(uv); } } // Colors var stringColor = GetSubBlock(line, "COL"); if (stringColor != "") { var tokensColor = stringColor.Split(' '); for (var k = 0; k < numVerticesInFace; k++) { var color = ApplyColorTransform(GetColor(long.Parse(tokensColor[k]))); mesh.Colors.Add(color); } } else { for (var k = 0; k < numVerticesInFace; k++) { var color = ApplyColorTransform(Vector4.One); mesh.Colors.Add(color); } } // Material index var stringMaterialIndex = GetSubBlock(line, "M"); var materialIndex = 0; if (stringMaterialIndex != "") { materialIndex = int.Parse(stringMaterialIndex); } // Add polygon to the submesh (and add submesh if not existing yet) var material = model.Materials[materialIndex]; if (!mesh.Submeshes.ContainsKey(material)) { mesh.Submeshes.Add(material, new IOSubmesh(material)); } mesh.Submeshes[material].Polygons.Add(poly); } line = reader.ReadLine().Trim(); } else if (tokens[0] == "vertexattr") { // section to ignore IgnoreChunk(reader); } else if (tokens[0] == "}") { break; } } model.Meshes.Add(mesh); } } } CalculateNormals(model); return(model); }
public static WadAnimation ImportAnimationFromModel(WadToolClass tool, IWin32Window owner, int nodeCount, string fileName) { IOModel tmpModel = null; // Import the model try { var settings = new IOGeometrySettings() { ProcessAnimations = true, ProcessGeometry = false }; using (var form = new GeometryIOSettingsDialog(settings)) { form.AddPreset(IOSettingsPresets.AnimationSettingsPresets); string resultingExtension = Path.GetExtension(fileName).ToLowerInvariant(); if (resultingExtension.Equals(".fbx")) { form.SelectPreset("3dsmax Filmbox (FBX)"); } else if (resultingExtension.Equals(".dae")) { form.SelectPreset("3dsmax COLLADA"); } if (form.ShowDialog(owner) != DialogResult.OK) { return(null); } var importer = BaseGeometryImporter.CreateForFile(fileName, settings, null); tmpModel = importer.ImportFromFile(fileName); // We don't support animation importing from custom-written mqo importer yet... if (importer is MetasequoiaImporter) { tool.SendMessage("Metasequoia importer isn't currently supported.", PopupType.Error); return(null); } // If no animations, return null if (tmpModel.Animations.Count == 0) { tool.SendMessage("Selected file has no supported animations!", PopupType.Error); return(null); } } } catch (Exception ex) { tool.SendMessage("Unknown error while importing animation. \n" + ex?.Message, PopupType.Error); logger.Warn(ex, "'ImportAnimationFromModel' failed."); return(null); } IOAnimation animToImport; if (tmpModel.Animations.Count > 1) { using (var dialog = new AnimationImportDialog(tmpModel.Animations.Select(o => o.Name).ToList())) { dialog.ShowDialog(owner); if (dialog.DialogResult == DialogResult.Cancel) { return(null); } else { animToImport = tmpModel.Animations[dialog.AnimationToImport]; } } } else { animToImport = tmpModel.Animations[0]; } // Integrity check, for cases when something totally went wrong with assimp if (animToImport == null) { tool.SendMessage("Animation importer encountered serious error. No animation imported.", PopupType.Error); return(null); } // Integrity check, is there any valid frames? if (animToImport.Frames.Count <= 0) { tool.SendMessage("Selected animation has no frames!", PopupType.Error); return(null); } // Integrity check, number of bones = number of nodes? if (animToImport.NumNodes != nodeCount) { tool.SendMessage("Selected animation has different number of bones!", PopupType.Error); return(null); } WadAnimation animation = new WadAnimation(); animation.Name = animToImport.Name; foreach (var frame in animToImport.Frames) { var keyFrame = new WadKeyFrame(); keyFrame.Offset = frame.Offset; frame.Angles.ForEach(angle => keyFrame.Angles.Add(new WadKeyFrameRotation() { Rotations = angle })); animation.KeyFrames.Add(keyFrame); } animation.EndFrame = (ushort)(animToImport.Frames.Count - 1); return(animation); }
/// <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); }
public override IOModel GetIOModel() { var iomodel = new IOModel(); iomodel.Skeleton = (SBSkeleton)Skeleton; List <SBHsdBone> bones = new List <SBHsdBone>(); foreach (SBHsdBone bone in Skeleton.Bones) { bones.Add(bone); } Dictionary <HSDStruct, string> tobjToName = new Dictionary <HSDStruct, string>(); foreach (var tex in tobjToSurface) { tex.Value.Name = $"TOBJ_{iomodel.Textures.Count}"; tobjToName.Add(tex.Key, tex.Value.Name); iomodel.Textures.Add(tex.Value); } foreach (SBHsdMesh me in GetMeshObjects()) { var dobj = me.DOBJ; var parent = Skeleton.Bones[0]; foreach (var b in Skeleton.Bones) { if (b is SBHsdBone bone) { if (bone.GetJOBJ().Dobj != null) { if (bone.GetJOBJ().Dobj.List.Contains(dobj)) { parent = b; break; } } } } var iomesh = new IOMesh(); iomesh.Name = me.Name; iomodel.Meshes.Add(iomesh); iomesh.HasPositions = true; iomesh.HasColor = true; iomesh.HasNormals = true; iomesh.HasBoneWeights = true; iomesh.HasUV0 = true; if (dobj.Pobj != null) { foreach (var pobj in dobj.Pobj.List) { var dl = pobj.ToDisplayList(); var vertices = GX_VertexAccessor.GetDecodedVertices(dl, pobj); HSD_Envelope[] bindGroups = null;; if (pobj.EnvelopeWeights != null) { bindGroups = pobj.EnvelopeWeights; } var offset = 0; foreach (var v in dl.Primitives) { List <GX_Vertex> strip = new List <GX_Vertex>(); for (int i = 0; i < v.Count; i++) { strip.Add(vertices[offset + i]); } offset += v.Count; iomesh.Vertices.AddRange(ConvertGXDLtoTriangleList(v.PrimitiveType, SBHsdMesh.GXVertexToHsdVertex(strip, bones, bindGroups), (SBHsdBone)parent)); } } } iomesh.Optimize(); // flip faces var temp = iomesh.Indices.ToArray(); iomesh.Indices.Clear(); for (int i = 0; i < temp.Length; i += 3) { if (i + 2 < temp.Length) { iomesh.Indices.Add(temp[i + 2]); iomesh.Indices.Add(temp[i + 1]); iomesh.Indices.Add(temp[i]); } else { break; } } iomesh.MaterialIndex = iomodel.Materials.Count; IOMaterialPhong mat = new IOMaterialPhong(); mat.Name = iomesh.Name + "_material"; if (dobj.Mobj.Material != null) { mat.DiffuseColor = dobj.Mobj.Material.DiffuseColor; mat.SpecularColor = dobj.Mobj.Material.SpecularColor; mat.AmbientColor = dobj.Mobj.Material.AmbientColor; } if (dobj.Mobj.Textures != null) { mat.DiffuseTexture = tobjToName[dobj.Mobj.Textures._s]; } iomodel.Materials.Add(mat); } return(iomodel); }
public IOModel GetIOModel() { IOModel outModel = new IOModel(); MESH meshFile = null; foreach (FileNode n in Parent.Nodes) { if (n.Text.Equals(_model.MeshString)) { meshFile = ((NUMSHB_Node)n).mesh; } if (n.Text.Equals(_model.SkeletonFileName)) { outModel.Skeleton = (RSkeleton)((SKEL_Node)n).GetRenderableNode(); } } Dictionary <string, int> indexByBoneName = new Dictionary <string, int>(); if (outModel.Skeleton != null) { for (int i = 0; i < outModel.Skeleton.Bones.Count; i++) { indexByBoneName.Add(outModel.Skeleton.Bones[i].Name, i); } } if (meshFile != null) { using (SSBHVertexAccessor vertexAccessor = new SSBHVertexAccessor(meshFile)) { SSBHRiggingAccessor riggingAccessor = new SSBHRiggingAccessor(meshFile); foreach (MeshObject obj in meshFile.Objects) { IOMesh outMesh = new IOMesh() { Name = obj.Name, }; outModel.Meshes.Add(outMesh); IOVertex[] vertices = new IOVertex[obj.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new IOVertex(); } foreach (MeshAttribute attr in obj.Attributes) { SSBHVertexAttribute[] values = vertexAccessor.ReadAttribute(attr.AttributeStrings[0].Name, 0, obj.VertexCount, obj); if (attr.AttributeStrings[0].Name.Equals("Position0")) { outMesh.HasPositions = true; for (int i = 0; i < values.Length; i++) { vertices[i].Position = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("Normal0")) { outMesh.HasNormals = true; for (int i = 0; i < values.Length; i++) { vertices[i].Normal = new OpenTK.Vector3(values[i].X, values[i].Y, values[i].Z); } } if (attr.AttributeStrings[0].Name.Equals("map1")) { outMesh.HasUV0 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV0 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet")) { outMesh.HasUV1 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV1 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet1")) { outMesh.HasUV2 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV2 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("uvSet2")) { outMesh.HasUV3 = true; for (int i = 0; i < values.Length; i++) { vertices[i].UV3 = new OpenTK.Vector2(values[i].X, values[i].Y); } } if (attr.AttributeStrings[0].Name.Equals("colorSet1")) { outMesh.HasColor = true; for (int i = 0; i < values.Length; i++) { vertices[i].Color = new OpenTK.Vector4(values[i].X, values[i].Y, values[i].Z, values[i].W); } } } // Fix SingleBinds if (outModel.Skeleton != null && !obj.ParentBoneName.Equals("")) { int parentIndex = outModel.Skeleton.GetBoneIndex(obj.ParentBoneName); if (parentIndex != -1) { for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = OpenTK.Vector3.TransformPosition(vertices[i].Position, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].Normal = OpenTK.Vector3.TransformNormal(vertices[i].Normal, outModel.Skeleton.Bones[parentIndex].WorldTransform); vertices[i].BoneIndices.X = indexByBoneName[obj.ParentBoneName]; vertices[i].BoneWeights.X = 1; } } } // Apply Rigging SSBHVertexInfluence[] influences = riggingAccessor.ReadRiggingBuffer(obj.Name, (int)obj.SubMeshIndex); foreach (SSBHVertexInfluence influence in influences) { // Some influences refer to bones that don't exist in the skeleton. // _eff bones? if (!indexByBoneName.ContainsKey(influence.BoneName)) { continue; } if (vertices[influence.VertexIndex].BoneWeights.X == 0) { vertices[influence.VertexIndex].BoneIndices.X = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.X = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Y == 0) { vertices[influence.VertexIndex].BoneIndices.Y = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Y = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.Z == 0) { vertices[influence.VertexIndex].BoneIndices.Z = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.Z = influence.Weight; } else if (vertices[influence.VertexIndex].BoneWeights.W == 0) { vertices[influence.VertexIndex].BoneIndices.W = indexByBoneName[influence.BoneName]; vertices[influence.VertexIndex].BoneWeights.W = influence.Weight; } } outMesh.Vertices.AddRange(vertices); outMesh.Indices.AddRange(vertexAccessor.ReadIndices(0, obj.IndexCount, obj)); } } } return(outModel); }
/// <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> /// <param name="filePath"></param> /// <returns></returns> public IOScene GetScene(string filePath) { // generate a new scene IOScene scene = new IOScene(); // load collada file _collada = Collada.LoadFromFile(filePath); // failed to load collada file if (_collada == null) { return(scene); } // load material library's to scene if (_collada.Library_Materials != null) { foreach (var mat in _collada.Library_Materials.Material) { scene.Materials.Add(LoadMaterial(mat)); } } // look through all visual scene foreach (var colscene in _collada.Library_Visual_Scene.Visual_Scene) { // treat each scene as a "model" IOModel model = new IOModel() { Name = colscene.Name }; // scan skeletons List <string> skelIDs = new List <string>(); foreach (var v in colscene.Node) { if (GetSkeletonReferences(v, out List <string> joints)) { foreach (var j in joints) { if (!skelIDs.Contains(j)) { skelIDs.Add(j); } } } } // load nodes foreach (var v in colscene.Node) { LoadNodes(v, null, model, skelIDs); } // add model scene.Models.Add(model); } // cleanup _collada = null; // done return(scene); }
public override bool ExportToFile(IOModel model, string filename) { var path = Path.GetDirectoryName(filename); var materialPath = path + "\\" + Path.GetFileNameWithoutExtension(filename) + ".mtl"; using (var writer = new StreamWriter(filename, false)) { writer.WriteLine("# Exported by Tomb Editor " + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\n"); writer.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(filename) + ".mtl" + "\n"); // Optimize all data var poscol = new List <Vector3[]>(); var poscolIndices = new Dictionary <int, int>(); var normals = new List <Vector3>(); var normalIndices = new Dictionary <int, int>(); var uvs = new List <Vector2>(); var uvIndices = new Dictionary <int, int>(); // Pack positions and colours int meshCount = 0; foreach (var mesh in model.Meshes) { if (mesh.Positions.Count == 0) { continue; } for (int i = 0; i < mesh.Positions.Count; i++) { var position = ApplyAxesTransforms(mesh.Positions[i]); var colour = ApplyColorTransform(mesh.Colors.Count > i ? mesh.Colors[i] : new Vector4(2)).To3(); int found = -1; for (int j = 0; j < poscol.Count; j++) { if (poscol[j][0] == position && poscol[j][1] == colour) { found = j; break; } } if (found == -1) { found = poscol.Count; poscol.Add(new Vector3[2] { position, colour }); } poscolIndices.Add(i + meshCount, found); } meshCount += mesh.Positions.Count; } // Pack normals meshCount = 0; foreach (var mesh in model.Meshes) { if (mesh.Normals.Count == 0) { mesh.CalculateNormals(); } for (int i = 0; i < mesh.Normals.Count; i++) { var normal = mesh.Normals[i]; int found = -1; for (int j = 0; j < normals.Count; j++) { if (normals[j] == normal) { found = j; break; } } if (found == -1) { found = normals.Count; normals.Add(normal); } normalIndices.Add(i + meshCount, found); } meshCount += mesh.Normals.Count; } // Pack UV coords meshCount = 0; foreach (var mesh in model.Meshes) { if (mesh.UV.Count == 0) { continue; } for (int i = 0; i < mesh.UV.Count; i++) { var uv = mesh.UV[i]; int found = -1; for (int j = 0; j < uvs.Count; j++) { if (uvs[j] == uv) { found = j; break; } } if (found == -1) { found = uvs.Count; uvs.Add(uv); } uvIndices.Add(i + meshCount, found); } meshCount += mesh.UV.Count; } // Save vertices if (!_settings.UseVertexColor) { foreach (var pc in poscol) { writer.WriteLine("v " + pc[0].X.ToString(CultureInfo.InvariantCulture) + " " + pc[0].Y.ToString(CultureInfo.InvariantCulture) + " " + pc[0].Z.ToString(CultureInfo.InvariantCulture)); } } else { // Include vertex colour, it may be parsed correctly by some software, e.g. MeshMixer and MeshLab. foreach (var pc in poscol) { writer.WriteLine("v " + pc[0].X.ToString(CultureInfo.InvariantCulture) + " " + pc[0].Y.ToString(CultureInfo.InvariantCulture) + " " + pc[0].Z.ToString(CultureInfo.InvariantCulture) + " " + pc[1].X.ToString(CultureInfo.InvariantCulture) + " " + pc[1].Y.ToString(CultureInfo.InvariantCulture) + " " + pc[1].Z.ToString(CultureInfo.InvariantCulture)); } } writer.WriteLine("# " + poscol.Count + " vertices total.\n"); // Save normals foreach (var normal in normals) { writer.WriteLine("vn " + normal.X.ToString(CultureInfo.InvariantCulture) + " " + normal.Y.ToString(CultureInfo.InvariantCulture) + " " + normal.Z.ToString(CultureInfo.InvariantCulture)); } writer.WriteLine("# " + normals.Count + " normals total.\n"); // Save UVs foreach (var uv in uvs) { writer.WriteLine("vt " + uv.X.ToString(CultureInfo.InvariantCulture) + " " + (1.0f - uv.Y).ToString(CultureInfo.InvariantCulture) + " 0.000"); } writer.WriteLine("# " + uvs.Count + " UVs total.\n"); int pCount = 0; int uCount = 0; int nCount = 0; foreach (var mesh in model.Meshes) { writer.WriteLine("o " + mesh.Name + "\n"); // Save faces IOMaterial lastMaterial = null; int faceCount = 0; foreach (var submesh in mesh.Submeshes) { if (submesh.Value.Polygons.Count == 0) { continue; } if (lastMaterial == null || lastMaterial != submesh.Key) { lastMaterial = submesh.Key; writer.WriteLine("\n" + "usemtl " + lastMaterial.Name.ToString()); } foreach (var poly in submesh.Value.Polygons) { var indices = new List <int>(); indices.Add(poly.Indices[0]); indices.Add(poly.Indices[1]); indices.Add(poly.Indices[2]); if (poly.Shape == IOPolygonShape.Quad) { indices.Add(poly.Indices[3]); } // Change vertex winding if (_settings.InvertFaces) { indices.Reverse(); } var v1 = indices[0]; var v2 = indices[1]; var v3 = indices[2]; var v4 = (poly.Shape == IOPolygonShape.Quad ? indices[3] : 0); if (poly.Shape == IOPolygonShape.Triangle) { writer.WriteLine("f " + (poscolIndices[v1 + pCount] + 1) + "/" + (uvIndices [v1 + uCount] + 1) + "/" + (normalIndices[v1 + nCount] + 1) + " " + (poscolIndices[v2 + pCount] + 1) + "/" + (uvIndices [v2 + uCount] + 1) + "/" + (normalIndices[v2 + nCount] + 1) + " " + (poscolIndices[v3 + pCount] + 1) + "/" + (uvIndices [v3 + uCount] + 1) + "/" + (normalIndices[v3 + nCount] + 1)); } else { writer.WriteLine("f " + (poscolIndices[v1 + pCount] + 1) + "/" + (uvIndices [v1 + uCount] + 1) + "/" + (normalIndices[v1 + nCount] + 1) + " " + (poscolIndices[v2 + pCount] + 1) + "/" + (uvIndices [v2 + uCount] + 1) + "/" + (normalIndices[v2 + nCount] + 1) + " " + (poscolIndices[v3 + pCount] + 1) + "/" + (uvIndices [v3 + uCount] + 1) + "/" + (normalIndices[v3 + nCount] + 1) + " " + (poscolIndices[v4 + pCount] + 1) + "/" + (uvIndices [v4 + uCount] + 1) + "/" + (normalIndices[v4 + nCount] + 1)); } faceCount++; } } pCount += mesh.Positions.Count; uCount += mesh.UV.Count; nCount += mesh.Normals.Count; writer.WriteLine("# " + faceCount + " faces total.\n"); } } using (var writer = new StreamWriter(materialPath, false)) { writer.WriteLine("# Exported by Tomb Editor " + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\n"); foreach (var material in model.UsedMaterials) { writer.WriteLine("newmtl " + material.Name); writer.WriteLine(" Ka 1.000000 1.000000 1.000000"); writer.WriteLine(" Kd 1.000000 1.000000 1.000000"); writer.WriteLine(" Ni 1.000000"); writer.WriteLine(" Ns 10.000000"); writer.WriteLine(" d " + (material.AdditiveBlending ? "0.500000" : "1.000000")); writer.WriteLine(" illum 2"); writer.WriteLine(" map_Ka " + material.TexturePath); writer.WriteLine(" map_Kd " + material.TexturePath); writer.WriteLine("\n"); } } return(true); }