static void Main(string[] args) { Printer.WriteLine("INIT", "---------------------------"); Printer.WriteLine("INIT", "ModelMerger by Scobalula"); Printer.WriteLine("INIT", "Merges SEModels into 1"); Printer.WriteLine("INIT", string.Format("Version {0}", Assembly.GetExecutingAssembly().GetName().Version)); Printer.WriteLine("INIT", "---------------------------"); try { var models = LoadModels(args); if (models.Count == 0) { Printer.WriteLine("USAGE", "Simply drag and drop supported model files onto the exe"); } else { var rootModel = GetRootModel(models); if (rootModel == null) { throw new Exception("Failed to obtain root model"); } Printer.WriteLine("MERGER", string.Format("Using {0} as root model", rootModel.Name)); var merged = new List <Model>(models.Count) { rootModel }; var outputFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Merged Models"); // Keep looping until we've resolved all models // We do this because some models connect to other // so we need to wait until we've processed that model while (merged.Count < models.Count) { foreach (var model in models) { // Check if we've processed, also considers root as it's added to merged if (merged.Contains(model)) { continue; } // If we have a model that doesn't exist, and can be connected, we must wait // for it's parent model to be connected if (!rootModel.HasBone(model.Bones[0].Name) && CanBeConnected(model, models)) { continue; } // Add to the group merged.Add(model); Printer.WriteLine("MERGER", string.Format("Merging {0}", model.Name)); foreach (var bone in model.Bones) { if (!rootModel.HasBone(bone.Name)) { var nBone = new Model.Bone(bone.Name, bone.ParentIndex, bone.LocalPosition, bone.LocalRotation); if (bone.ParentIndex > -1) { nBone.ParentIndex = rootModel.Bones.FindIndex(x => x.Name == model.Bones[nBone.ParentIndex].Name); } rootModel.Bones.Add(nBone); } } // Compute global positions (we need them for offsetting) rootModel.GenerateGlobalBoneData(); model.GenerateGlobalBoneData(); // Get root and the new root, to compute offsets var root = model.Bones[0]; var nRoot = rootModel.Bones.Find(x => x.Name == root.Name); // TODO: compute this for each bone and utilize weights // but as an option, as it may cause severe deformations // if bones have moved var translation = nRoot.GlobalPosition - root.GlobalPosition; var rotation = (nRoot.GlobalRotation * root.GlobalRotation.Inverse()).ToMatrix(); foreach (var material in model.Materials) { if (rootModel.Materials.Find(x => x.Name == material.Name) == null) { rootModel.Materials.Add(material); } } foreach (var mesh in model.Meshes) { var nMesh = new Model.Mesh(mesh.Vertices.Count, 0) { Faces = new List <Model.Face>(mesh.Faces) }; foreach (var material in mesh.MaterialIndices) { nMesh.MaterialIndices.Add(rootModel.Materials.FindIndex(x => x.Name == model.Materials[material].Name)); } foreach (var vertex in mesh.Vertices) { var nVertex = new Model.Vertex(vertex.Position, vertex.Normal, vertex.Tangent) { Color = vertex.Color, Weights = new List <Model.Vertex.Weight>(vertex.Weights.Count), UVs = new List <Vector2>(vertex.UVs) }; foreach (var weight in vertex.Weights) { nVertex.Weights.Add(new Model.Vertex.Weight() { Influence = weight.Influence, BoneIndex = rootModel.Bones.FindIndex(x => x.Name == model.Bones[weight.BoneIndex].Name), }); } // Now move it to the new position nVertex.Position = rotation.TransformVector(vertex.Position); nVertex.Normal = rotation.TransformVector(vertex.Normal); nVertex.Position += translation; nMesh.Vertices.Add(nVertex); } rootModel.Meshes.Add(nMesh); } Printer.WriteLine("MERGER", string.Format("Merged {0}", model.Name)); } } Printer.WriteLine("MERGER", string.Format("Saving {0}", rootModel.Name)); Directory.CreateDirectory(outputFolder); rootModel.Save(Path.Combine(outputFolder, rootModel.Name + ".semodel")); Printer.WriteLine("MERGER", string.Format("Saved {0}", rootModel.Name)); } } catch (Exception e) { Printer.WriteLine("ERROR", "An unhandled exception has occured:", ConsoleColor.DarkRed); Console.WriteLine(e); } Printer.WriteLine("DONE", "Execution complete, press Enter to exit"); Console.ReadLine(); }
private static Model LoadSEModel(string filePath) { var model = new Model(Path.GetFileNameWithoutExtension(filePath)); var input = SEModel.Read(filePath); Printer.WriteLine("LOADER", string.Format("Loading {0}", model.Name)); foreach (var bone in input.Bones) { model.Bones.Add(new Model.Bone( bone.BoneName, bone.BoneParent, new Vector3( (float)bone.LocalPosition.X, (float)bone.LocalPosition.Y, (float)bone.LocalPosition.Z), new Quaternion( (float)bone.LocalRotation.X, (float)bone.LocalRotation.Y, (float)bone.LocalRotation.Z, (float)bone.LocalRotation.W), new Vector3( (float)bone.GlobalPosition.X, (float)bone.GlobalPosition.Y, (float)bone.GlobalPosition.Z), new Quaternion( (float)bone.GlobalRotation.X, (float)bone.GlobalRotation.Y, (float)bone.GlobalRotation.Z, (float)bone.GlobalRotation.W))); } foreach (var semesh in input.Meshes) { var mesh = new Model.Mesh((int)semesh.VertexCount, (int)semesh.FaceCount); foreach (var mtl in semesh.MaterialReferenceIndicies) { mesh.MaterialIndices.Add(mtl); } foreach (var severtex in semesh.Verticies) { var vertex = new Model.Vertex( new Vector3((float)severtex.Position.X, (float)severtex.Position.Y, (float)severtex.Position.Z), new Vector3((float)severtex.VertexNormal.X, (float)severtex.VertexNormal.Y, (float)severtex.VertexNormal.Z)); foreach (var uv in severtex.UVSets) { vertex.UVs.Add(new Vector2((float)uv.X, (float)uv.Y)); } foreach (var weight in severtex.Weights) { vertex.Weights.Add(new Model.Vertex.Weight() { BoneIndex = (int)weight.BoneIndex, Influence = weight.BoneWeight }); } vertex.Color = new Vector4( severtex.VertexColor.R / 255.0f, severtex.VertexColor.G / 255.0f, severtex.VertexColor.B / 255.0f, severtex.VertexColor.A / 255.0f); mesh.Vertices.Add(vertex); } foreach (var face in semesh.Faces) { mesh.Faces.Add(new Model.Face((int)face.FaceIndex1, (int)face.FaceIndex2, (int)face.FaceIndex3)); } model.Meshes.Add(mesh); } foreach (var material in input.Materials) { model.Materials.Add(new Model.Material(material.Name)); } Printer.WriteLine("LOADER", string.Format("Loaded {0}", model.Name)); return(model); }
/// <summary> /// Loads Meshes from a Resident Evil 2 Mesh /// </summary> private static List <List <Model> > ConvertRE2(BinaryReader reader) { var results = new List <List <Model> >(3); var header = reader.ReadStruct <MeshHeaderRE2>(); var boneDataHeader = new BoneDataHeaderRE2(); var geometryHeader = new GeometryHeaderRE2(); var vertexBlocks = new Dictionary <short, long>(); short[] skinnedBones = null; if (header.BoneDataHeaderPointer > 0) { boneDataHeader = reader.ReadStruct <BoneDataHeaderRE2>(header.BoneDataHeaderPointer); skinnedBones = reader.ReadArray <short>(header.BoneDataHeaderPointer + 48, boneDataHeader.SkinnedBoneCount); } if (header.GeometryPointer > 0) { geometryHeader = reader.ReadStruct <GeometryHeaderRE2>(header.GeometryPointer); var blocks = reader.ReadArray <VertexBlockRE2>(geometryHeader.VertexBlocksOffset, geometryHeader.VertexBlockCount); foreach (var block in blocks) { vertexBlocks[block.ID] = geometryHeader.VertexDataOffset + block.Offset; } } // Parse all strings for bones, materials, etc. into lists to make it easier to pass around, etc. var strings = new List <string>(header.StringCount); foreach (var offset in reader.ReadArray <long>(header.StringTablePointer, header.StringCount)) { strings.Add(reader.ReadNullTerminatedString(offset)); } var boneNames = new List <string>(boneDataHeader.BoneCount); foreach (var boneNameIndex in reader.ReadArray <ushort>(header.BoneNamesPointer, boneDataHeader.BoneCount)) { boneNames.Add(strings[boneNameIndex]); } var bones = new List <Model.Bone>(boneDataHeader.BoneCount); if (header.BoneDataHeaderPointer > 0) { var boneDatas = reader.ReadArray <BoneDataRE7>(boneDataHeader.BoneTablePointer, boneDataHeader.BoneCount); var boneMatrices = reader.ReadArray <Matrix4x4>(boneDataHeader.MatricesPointer, boneDataHeader.BoneCount); for (ushort i = 0; i < boneDataHeader.BoneCount; i++) { var bone = new Model.Bone(boneNames[i], boneDatas[i].ParentIndex, new Vector3( boneMatrices[i].W.X, boneMatrices[i].W.Y, boneMatrices[i].W.Z), boneMatrices[i].ToQuaternion()); bones.Add(bone); } } bool firstMdlProcessed = false; for (int mdl = 0; mdl < 3; mdl++) { if (header.ModelPointers[mdl] == 0) { continue; } if (firstMdlProcessed) { break; } var lods = new List <Model>(); var modelHeader = reader.ReadStruct <ModelHeaderRE7>(header.ModelPointers[mdl]); reader.BaseStream.Position = header.ModelPointers[mdl] + (firstMdlProcessed ? 16 : 64); var materialIndices = reader.ReadArray <short>(header.MaterialNamesPointer, modelHeader.MaterialCount); firstMdlProcessed = true; var lodPointers = reader.ReadArray <long>(reader.ReadInt64(), modelHeader.LODCount); foreach (var lodPointer in lodPointers) { var model = new Model() { Bones = bones }; var uniqueMaterials = new List <string>(modelHeader.MaterialCount); var lodHeader = reader.ReadStruct <LODHeaderRE7>(lodPointer); var meshPointers = reader.ReadArray <long>(lodHeader.MeshesPointer, lodHeader.MeshCount); foreach (var meshPointer in meshPointers) { var mesh = reader.ReadStruct <LODMeshRE7>(meshPointer); var subMeshes = reader.ReadArray <LODSubMeshRE7>(meshPointer + 16, mesh.SubMeshCount); int verticesRead = 0; for (int i = 0; i < subMeshes.Length; i++) { var materialName = strings[materialIndices[subMeshes[i].MaterialIndex]]; if (!uniqueMaterials.Contains(materialName)) { uniqueMaterials.Add(materialName); } int subMeshVertexCount = 0; int subMeshFaceCount = subMeshes[i].FaceCount; // Since the counts aren't stored in each, we can use this to determine the counts if (i != subMeshes.Length - 1) { subMeshVertexCount = subMeshes[i + 1].VertexIndex - subMeshes[i].VertexIndex; } else { subMeshVertexCount = mesh.VertexCount - verticesRead; } verticesRead += subMeshVertexCount; var subMesh = new Model.Mesh(subMeshVertexCount, subMeshFaceCount); subMesh.MaterialIndices.Add(uniqueMaterials.IndexOf(materialName)); // Positions if (vertexBlocks.TryGetValue(0, out var positionsOffset)) { reader.BaseStream.Position = positionsOffset + (12 * subMeshes[i].VertexIndex); for (int v = 0; v < subMeshVertexCount; v++) { subMesh.Vertices.Add(new Model.Vertex(reader.ReadStruct <Vector3>())); } } // Normals/Tangents if (vertexBlocks.TryGetValue(1, out var normalsTangentsOffset)) { reader.BaseStream.Position = normalsTangentsOffset + (8 * subMeshes[i].VertexIndex); for (int v = 0; v < subMeshVertexCount; v++) { subMesh.Vertices[v].Normal = reader.ReadStruct <PackedVector3>().Unpack(); subMesh.Vertices[v].Tangent = reader.ReadStruct <PackedVector3>().Unpack(); } } // UVs if (vertexBlocks.TryGetValue(2, out var uvsOffset)) { reader.BaseStream.Position = uvsOffset + (4 * subMeshes[i].VertexIndex); for (int v = 0; v < subMeshVertexCount; v++) { subMesh.Vertices[v].UVs.Add(new Vector2(reader.ReadStruct <Half>(), reader.ReadStruct <Half>())); } } // Weights if (vertexBlocks.TryGetValue(4, out var weightsOffset)) { reader.BaseStream.Position = weightsOffset + (16 * subMeshes[i].VertexIndex); for (int v = 0; v < subMeshVertexCount; v++) { var localBoneIndices = reader.ReadBytes(8); var weights = reader.ReadBytes(8); var weightSum = 0.0f; for (int w = 0; w < 8 && weights[w] != 0; w++) { subMesh.Vertices[v].Weights.Add(new Model.Vertex.Weight() { BoneIndex = skinnedBones[localBoneIndices[w]], Influence = weights[w] / 255.0f }); weightSum += subMesh.Vertices[v].Weights[w].Influence; } var multiplier = 1.0f / weightSum; foreach (var weight in subMesh.Vertices[v].Weights) { weight.Influence *= multiplier; } } } switch (lodHeader.Flags) { case 0: reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (2 * subMeshes[i].FaceIndex); for (int f = 0; f < subMeshes[i].FaceCount / 3; f++) { var v1 = reader.ReadUInt16(); var v2 = reader.ReadUInt16(); var v3 = reader.ReadUInt16(); if (v1 != v2 && v2 != v3 && v3 != v1) { subMesh.Faces.Add(new Model.Face(v1, v2, v3)); } } break; case 1: reader.BaseStream.Position = geometryHeader.FaceBufferOffset + (4 * subMeshes[i].FaceIndex); for (int f = 0; f < subMeshes[i].FaceCount / 3; f++) { var v1 = reader.ReadInt32(); var v2 = reader.ReadInt32(); var v3 = reader.ReadInt32(); if (v1 != v2 && v2 != v3 && v3 != v1) { subMesh.Faces.Add(new Model.Face(v1, v2, v3)); } } break; } model.Meshes.Add(subMesh); } } foreach (var materialName in uniqueMaterials) { model.Materials.Add(new Model.Material(materialName)); } lods.Add(model); } results.Add(lods); } return(results); }