public static FullModelData Open(string filepath) { FullModelData data = new FullModelData(); StaticStorage.hashindex.Load(); Log.Default.Info("Opening Model: {0}", filepath); Read(data, filepath); return(data); }
public static IEnumerable <(string fullpath, string relativepath, FullModelData data)> EveryModel(string root) { foreach (var i in WalkDirectoryTreeDepth(new DirectoryInfo(root), "*.model")) { FullModelData fmd = null; try { fmd = ModelReader.Open(i.FullName); } catch (Exception e) { Log.Default.Warn($"Unable to read {i.FullName}: {e}"); } if (fmd != null) { yield return(i.FullName, i.FullName.Substring(root.Length), fmd); } } }
public static string ExportFile(FullModelData data, string path) { path = path.Replace(".model", ".dae"); List <SectionHeader> sections = data.sections; Dictionary <UInt32, ISection> parsed_sections = data.parsed_sections; byte[] leftover_data = data.leftover_data; // Set up the XML structure library_geometries libgeoms = new library_geometries(); library_visual_scenes libscenes = new library_visual_scenes(); COLLADAScene scene = new COLLADAScene { instance_visual_scene = new InstanceWithExtra { url = "#scene" } }; COLLADA collada = new COLLADA { Items = new object[] { libgeoms, libscenes }, scene = scene, asset = new asset { created = DateTime.UtcNow, modified = DateTime.UtcNow, // Otherwise it defaults to Y-up (see asset's constructor), while we're // using Z-up. Without the asset tag Blender defaults to Z-up, so if you // remove this then the presence of the asset tag flips the model. up_axis = UpAxisType.Z_UP, }, }; // Build the mesh List <geometry> geometries = new List <geometry>(); List <node> nodes = new List <node>(); int model_i = 0; foreach (SectionHeader sectionheader in sections) { if (sectionheader.type == model_data_tag) { Model model_data = (Model)parsed_sections[sectionheader.id]; if (model_data.version == 6) { continue; } model_i++; string model_id = model_i + "-" + model_data.HashName.String; PassthroughGP passthrough_section = model_data.PassthroughGP; Geometry geometry_section = passthrough_section.Geometry; Topology topology_section = passthrough_section.Topology; geometry geom = SerializeModel(geometry_section, topology_section, model_data, model_id); geometries.Add(geom); node root_node = new node { id = "model-" + model_id, name = "Model " + model_id, type = NodeType.NODE, instance_geometry = new instance_geometry[] { new instance_geometry { url = "#model-geom-" + model_id, name = "Model Geom" + model_id } } }; nodes.Add(root_node); if (model_data.SkinBones == null) { continue; } SkinBones sb = model_data.SkinBones; //Console.WriteLine(sb.bones); //Console.WriteLine(sb); node bone_root_node = new node { id = "model-" + model_id + "-boneroot", name = "Model " + model_id + " Bones", type = NodeType.NODE, /*Items = new object[] * { * new matrix * { * sid = "transform", // Apparently Blender really wants this * Values = MathUtil.Serialize(sb.unknown_matrix) * } * }, * ItemsElementName = new ItemsChoiceType2[] * { * ItemsChoiceType2.matrix * }*/ }; root_node.node1 = new node[] { bone_root_node }; Dictionary <Object3D, node> bones = new Dictionary <Object3D, node>(); // In order to find locators, which aren't present in the SkinBones // object, we check the child of each object we process. // // To then process those, we use a queue (to_parse) which lists the // objects in our TODO list to search. We draw from this as we process them. // // We also keep the list of what we have processed in (parsed), so we don't // process anything twice. // // TODO rewrite this code to be based around children, removing the need for a seperate // loop after this that arranges the heirachy of nodes. List <Object3D> parsed = new List <Object3D>(sb.Objects); Queue <Object3D> to_parse = new Queue <Object3D>(sb.Objects); int i = 0; while (to_parse.Count != 0) { Object3D obj = to_parse.Dequeue(); string bonename = obj.HashName.String; // Find the locators and such, and add them to the TODO list foreach (Object3D child in obj.children) { if (!parsed.Contains(child)) // Don't process something twice { parsed.Add(child); to_parse.Enqueue(child); } } Vector3 translate; Quaternion rotate; Vector3 scale; Matrix4x4.Decompose(obj.Transform, out scale, out rotate, out translate); Matrix4x4 final_rot = Matrix4x4.CreateFromQuaternion(rotate); final_rot.Translation = translate; if (obj.Parent == null || !sb.Objects.Contains(obj.Parent)) { Matrix4x4 fixed_obj_transform = obj.WorldTransform; Matrix4x4.Decompose(fixed_obj_transform, out scale, out rotate, out translate); // Fixes the head, but breaks the arms // For now, leave it like this //final_rot = final_rot.MultDiesel(fixed_obj_transform); } // If the object is not contained within the SkinBones // object, it must be a locator. bool locator = !sb.Objects.Contains(obj); // Add the node bones[obj] = new node { id = "model-" + model_id + "-bone-" + bonename, name = (locator ? "locator-" : "") + bonename, type = NodeType.JOINT, Items = new object[] { new matrix { sid = "transform", // Apparently Blender really wants this Values = MathUtil.Serialize(final_rot) }, }, ItemsElementName = new ItemsChoiceType2[] { ItemsChoiceType2.matrix } }; i++; } foreach (var(obj, nod) in bones) { node parent = bone_root_node; if (bones.ContainsKey(obj)) { parent = bones[obj.Parent]; } if (parent.node1 == null) { parent.node1 = new node[1]; } else { node[] children = parent.node1; Array.Resize(ref children, children.Length + 1); parent.node1 = children; } parent.node1[parent.node1.Length - 1] = nod; } } } libgeoms.geometry = geometries.ToArray(); libscenes.visual_scene = new visual_scene[] { new visual_scene { id = "scene", name = "Scene", node = nodes.ToArray() } }; using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) { collada.Save(fs); // Do we need this? // fs.Close(); } return(path); }
private static void Read(FullModelData data, string filepath) { List <SectionHeader> sections = data.sections; Dictionary <UInt32, ISection> parsed_sections = data.parsed_sections; byte[] bytes; using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read)) { bytes = new byte[fs.Length]; int res = fs.Read(bytes, 0, (int)fs.Length); if (res != fs.Length) { throw new Exception($"Failed to read {filepath} all in one go!"); } } using (var ms = new MemoryStream(bytes, 0, bytes.Length, false, true)) using (var br = new BinaryReader(ms)) { sections.Clear(); sections.AddRange(ReadHeaders(br)); foreach (SectionHeader sh in sections) { ISection section; ms.Position = sh.Start; if (SectionMetaInfo.TryGetForTag(sh.type, out var mi)) { section = mi.Deserialise(br, sh); } else { Log.Default.Warn("UNKNOWN Tag {2} at {0} Size: {1}", sh.offset, sh.size, sh.type); ms.Position = sh.offset; section = new Unknown(br, sh); } if (ms.Position != sh.End) { throw new Exception(string.Format("Section of type {2} {0} read more than its length of {1} ", sh.id, sh.size, sh.type)); } Log.Default.Debug("Section {0} at {1} length {2}", section.GetType().Name, sh.offset, sh.size); parsed_sections.Add(sh.id, section); } foreach (var i in parsed_sections) { if (i.Value is IPostLoadable pl) { pl.PostLoad(i.Key, parsed_sections); } } if (ms.Position < ms.Length) { data.leftover_data = br.ReadBytes((int)(ms.Length - ms.Position)); } } }
public override void Import(FullModelData data, string path, bool createModels, Func <string, Sections.Object3D> parentFinder, IOptionReceiver options) => GltfImporter.Import(data, path, createModels, parentFinder, options);
public override string Export(FullModelData data, string path) => Exporters.GltfExporter.ExportFile(data, path, true);
public override void Import(FullModelData data, string path, bool createModels, Func <string, Sections.Object3D> parentFinder, IOptionReceiver options) => throw new Exception("Importing DAE files is not supported.");
public override string Export(FullModelData data, string path) => ColladaExporter.ExportFile(data, path);
public override string Export(FullModelData data, string path) => Exporters.ObjWriter.ExportFile(data, path);
public abstract void Import(FullModelData data, string path, bool createModels, Func <string, Sections.Object3D> parentFinder, IOptionReceiver options);
public abstract string Export(FullModelData data, string path);
public override string Export(FullModelData data, string path) => Exporters.AnimationExporter.ExportFile(data, path);
public override void Import(FullModelData data, string path, bool createModels, Func <string, Sections.Object3D> parentFinder, IOptionReceiver options) => throw new Exception("Please import animation files in the animation section.");