private void CompressVertices() { var compressionInfo = BuildCompressionInfo(); var compressor = new VertexCompressor(compressionInfo); // TODO: Refactor how vertices work, this is just ugly foreach (var mesh in Meshes) { if (mesh.RigidVertices != null) { foreach (var v in mesh.RigidVertices) { v.Position = compressor.CompressPosition(v.Position); v.Texcoord = compressor.CompressUv(v.Texcoord); } } else if (mesh.SkinnedVertices != null) { foreach (var v in mesh.SkinnedVertices) { v.Position = compressor.CompressPosition(v.Position); v.Texcoord = compressor.CompressUv(v.Texcoord); } } } }
/// <summary> /// Decompresses vertex data in-place. /// </summary> /// <param name="vertices">The vertices to decompress in-place.</param> /// <param name="compressor">The compressor to use.</param> private static void DecompressVertices(IEnumerable<ObjVertex> vertices, VertexCompressor compressor) { foreach (var vertex in vertices) { vertex.Position = compressor.DecompressPosition(vertex.Position); vertex.TexCoords = compressor.DecompressUv(vertex.TexCoords); } }
/// <summary> /// Decompresses vertex data in-place. /// </summary> /// <param name="vertices">The vertices to decompress in-place.</param> /// <param name="compressor">The compressor to use.</param> private static void DecompressVertices(IEnumerable <ObjVertex> vertices, VertexCompressor compressor) { foreach (var vertex in vertices) { vertex.Position = compressor.DecompressPosition(vertex.Position); vertex.TexCoords = compressor.DecompressUv(vertex.TexCoords); } }
/// <summary> /// Decompresses vertex data in-place. /// </summary> /// <param name="vertices">The vertices to decompress in-place.</param> /// <param name="compressor">The compressor to use.</param> private static void DecompressVertices(IEnumerable <GenericVertex> vertices, VertexCompressor compressor) { if (compressor == null) { return; } foreach (var vertex in vertices) { vertex.Position = ToVector3D(compressor.DecompressPosition(new RealQuaternion(vertex.Position.X, vertex.Position.Y, vertex.Position.Z, 1))); vertex.TexCoords = ToVector3D(compressor.DecompressUv(new RealVector2d(vertex.TexCoords.X, vertex.TexCoords.Y))); } }
/// <summary> /// Writes mesh data to the .obj. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="compressor">The vertex compressor to use.</param> /// <param name="resourceStream">A stream open on the resource data.</param> public void ExtractMesh(MeshReader reader, VertexCompressor compressor, Stream resourceStream) { // Read the vertex buffer and decompress each vertex var vertices = ReadVertices(reader, resourceStream); DecompressVertices(vertices, compressor); // Write out the vertices WriteVertices(vertices); // Read and write out the triangles for each part foreach (var part in reader.Mesh.Parts) { var indexes = ReadIndexes(reader, part, resourceStream); WriteTriangles(indexes); } _baseIndex += (uint)vertices.Count; }
private Assimp.Mesh ExtractMeshPartGeometry(int meshIndex, int partIndex) { // create new assimp mesh Assimp.Mesh mesh = new Assimp.Mesh(); // Add support for multiple UV layers var textureCoordinateIndex = 0; mesh.UVComponentCount[textureCoordinateIndex] = 2; // prepare vertex extraction var meshReader = new MeshReader(CacheContext.Version, RenderModel.Geometry.Meshes[meshIndex], RenderModelResourceDefinition); var vertexCompressor = new VertexCompressor(RenderModel.Geometry.Compression[0]); var geometryMesh = RenderModel.Geometry.Meshes[meshIndex]; var geometryPart = geometryMesh.Parts[partIndex]; mesh.MaterialIndex = geometryPart.MaterialIndex; // optimize this part to not load and decompress all mesh vertices everytime var vertices = ReadVertices(meshReader, RenderModelResourceStream); DecompressVertices(vertices, vertexCompressor); // get offset in the list of all vertices for the mesh var vertexOffset = GetPartVertexOffset(meshIndex, partIndex); //vertices = vertices.GetRange(vertexOffset, geometryPart.VertexCount); var indices = ReadIndices(meshReader, geometryPart, RenderModelResourceStream); var int_indices = indices.Select(b => (int)b).ToArray(); var indexCount = indices.Length; if (indexCount == 0) { Console.WriteLine($"Failed to extract mesh, no indices."); return(null); } // set index list, maybe require adjustment for vertex buffer offset mesh.SetIndices(int_indices, 3); // build skeleton for each mesh (meh) // create a list of all the mesh bones available in the scene foreach (var node in RenderModel.Nodes) { Bone bone = new Bone(); bone.Name = CacheContext.GetString(node.Name); bone.OffsetMatrix = new Matrix4x4(); mesh.Bones.Add(bone); } for (int i = vertexOffset; i < vertexOffset + geometryPart.VertexCount; i++) { var vertex = vertices[i]; mesh.Vertices.Add(vertex.Position); if (vertex.Normal != null) { mesh.Normals.Add(vertex.Normal); } if (vertex.TexCoords != null) { mesh.TextureCoordinateChannels[textureCoordinateIndex].Add(vertex.TexCoords); } if (vertex.Tangents != null) { mesh.Tangents.Add(vertex.Tangents); } if (vertex.Binormals != null) { mesh.BiTangents.Add(vertex.Binormals); } if (vertex.Indices != null) { for (int j = 0; j < vertex.Indices.Length; j++) { var index = vertex.Indices[j]; var bone = mesh.Bones[index]; Matrix4x4 inverseTransform = new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); var currentNode = BoneNodes[index]; while (currentNode != null) { Matrix4x4 inverse = (currentNode.Transform.DeepClone()); inverse.Inverse(); inverseTransform = inverse * inverseTransform; currentNode = currentNode.Parent; } bone.OffsetMatrix = inverseTransform; bone.VertexWeights.Add(new VertexWeight(i - vertexOffset, vertex.Weights[j])); } } // Add skinned mesh support and more } // create faces mesh.Faces.Clear(); GenerateFaces(int_indices, vertexOffset, mesh.Faces); return(mesh); }
/// <summary> /// Writes mesh data to the .obj. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="compressor">The vertex compressor to use.</param> /// <param name="resourceStream">A stream open on the resource data.</param> /// <param name="name">The name of the mesh.</param> public void ExtractMesh(MeshReader reader, VertexCompressor compressor, Stream resourceStream, string name = null) { var vertices = ReadVertices(reader, resourceStream); DecompressVertices(vertices, compressor); var indicesList = new List <ushort[]>(); var indexCount = 0; for (var i = 0; i < (reader?.Mesh?.Parts?.Count ?? -1); i++) { var part = reader.Mesh.Parts[i]; var indices = ReadIndices(reader, part, resourceStream); indicesList.Add(indices); if (part.IndexCountOld > 0) { indexCount += indices.Length; } } if (indexCount == 0) { return; } foreach (var vertex in vertices) { _writer.WriteLine("v {0} {1} {2}", vertex.Position.I, vertex.Position.J, vertex.Position.K); } foreach (var vertex in vertices) { _writer.WriteLine("vn {0} {1} {2}", vertex.Normal.I, vertex.Normal.J, vertex.Normal.K); } foreach (var vertex in vertices) { _writer.WriteLine("vt {0} {1}", vertex.TexCoords.I, 1 - vertex.TexCoords.J); } var partIndex = 0; foreach (var indices in indicesList) { var triangles = GenerateTriangles(indices); if (triangles.Count() != 0) { _writer.WriteLine($"g {name}_part_{partIndex++}"); } foreach (var triangle in triangles) { _writer.WriteLine(triangle); } } _baseIndex += (uint)vertices.Count; }
public override bool Execute(List<string> args) { if (args.Count != 3) return false; var variantName = args[0]; var fileType = args[1]; var fileName = args[2]; if (fileType != "obj" && fileType != "amf") return false; // Find the variant to extract if (Definition.RenderModel == null) { Console.WriteLine("The model does not have a render model associated with it."); return true; } var variant = Definition.Variants.FirstOrDefault(v => (Info.StringIDs.GetString(v.Name) ?? v.Name.ToString()) == variantName); if (variant == null && Definition.Variants.Count > 0) { Console.WriteLine("Unable to find variant \"{0}\"", variantName); Console.WriteLine("Use \"listvariants\" to list available variants."); return true; } if (fileType == "amf") return ExtractAMF(variant, fileName); // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } // Deserialize the render model tag Console.WriteLine("Reading model data..."); RenderModel renderModel; using (var cacheStream = Info.CacheFile.OpenRead()) { var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Definition.RenderModel); renderModel = Info.Deserializer.Deserialize<RenderModel>(renderModelContext); } if (renderModel.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return true; } // Deserialize the resource definition var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource); var definition = Info.Deserializer.Deserialize<RenderGeometryResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // Extract the resource data resourceManager.Extract(renderModel.Geometry.Resource, resourceStream); Directory.CreateDirectory(Path.GetDirectoryName(fileName)); using (var objFile = new StreamWriter(File.Open(fileName, FileMode.Create, FileAccess.Write))) { var objExtractor = new ObjExtractor(objFile); var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]); // Create a (de)compressor from the first compression block if (variant != null) { // Extract each region in the variant foreach (var region in variant.Regions) { // Get the corresonding region in the render model tag if (region.RenderModelRegionIndex >= renderModel.Regions.Count) continue; var renderModelRegion = renderModel.Regions[region.RenderModelRegionIndex]; // Get the corresponding permutation in the render model tag // (Just extract the first permutation for now) if (region.Permutations.Count == 0) continue; var permutation = region.Permutations[0]; if (permutation.RenderModelPermutationIndex < 0 || permutation.RenderModelPermutationIndex >= renderModelRegion.Permutations.Count) continue; var renderModelPermutation = renderModelRegion.Permutations[permutation.RenderModelPermutationIndex]; // Extract each mesh in the permutation var meshIndex = renderModelPermutation.MeshIndex; var meshCount = renderModelPermutation.MeshCount; var regionName = Info.StringIDs.GetString(region.Name) ?? region.Name.ToString(); var permutationName = Info.StringIDs.GetString(permutation.Name) ?? permutation.Name.ToString(); Console.WriteLine("Extracting {0} mesh(es) for {1}:{2}...", meshCount, regionName, permutationName); for (var i = 0; i < meshCount; i++) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, renderModel.Geometry.Meshes[meshIndex + i], definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } } else { // No variant - just extract every mesh Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Geometry.Meshes.Count); foreach (var mesh in renderModel.Geometry.Meshes) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, mesh, definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } objExtractor.Finish(); } } Console.WriteLine("Done!"); return true; }