/// <summary> /// Reads the index buffer data and converts it into a triangle list if necessary. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="part">The mesh part to read.</param> /// <param name="resourceStream">A stream open on the resource data.</param> /// <returns>The index buffer converted into a triangle list.</returns> public static uint[] ReadIndices(MeshReader reader, Mesh.Part part, Stream resourceStream) { // Use index buffer 0 var indexBuffer = reader.IndexBuffers[0]; if (indexBuffer == null) { throw new InvalidOperationException("Index buffer 0 is null"); } // Read the indexes var indexStream = reader.OpenIndexBufferStream(indexBuffer, resourceStream); indexStream.Position = part.FirstIndex; switch (indexBuffer.Type) { case PrimitiveType.TriangleList: return(indexStream.ReadIndices(part.IndexCount)); case PrimitiveType.TriangleStrip: return(indexStream.ReadTriangleStrip(part.IndexCount)); default: throw new InvalidOperationException("Unsupported index buffer type: " + indexBuffer.Type); } }
/// <summary> /// Reads the vertex data for a mesh into a format-independent list. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="resourceStream">A stream open on the resource data.</param> /// <returns>The list of vertices that were read.</returns> public static List <ObjVertex> ReadVertices(MeshReader reader, Stream resourceStream) { // Open a vertex reader on stream 0 (main vertex data) var mainBuffer = reader.VertexStreams[0]; if (mainBuffer == null) { throw new InvalidOperationException("Mesh does not have a vertex buffer bound to stream 0"); } var vertexReader = reader.OpenVertexStream(mainBuffer, resourceStream); switch (reader.Mesh.Type) { case VertexType.Rigid: return(ReadRigidVertices(vertexReader, mainBuffer.Count)); case VertexType.Skinned: return(ReadSkinnedVertices(vertexReader, mainBuffer.Count)); case VertexType.DualQuat: return(ReadDualQuatVertices(vertexReader, mainBuffer.Count)); case VertexType.World: return(ReadWorldVertices(vertexReader, mainBuffer.Count)); default: throw new InvalidOperationException("Only Rigid, Skinned, and DualQuat meshes are supported"); } }
/// <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; }
/// <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 = ReadIndices(reader, part, resourceStream); WriteTriangles(indexes); } _baseIndex += (uint)vertices.Count; }
/// <summary> /// Reads the vertex data for a mesh into a format-independent list. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="resourceStream">A stream open on the resource data.</param> /// <returns>The list of vertices that were read.</returns> private static List<ObjVertex> ReadVertices(MeshReader reader, Stream resourceStream) { // Open a vertex reader on stream 0 (main vertex data) var mainBuffer = reader.VertexStreams[0]; if (mainBuffer == null) throw new InvalidOperationException("Mesh does not have a vertex buffer bound to stream 0"); var vertexReader = reader.OpenVertexStream(mainBuffer, resourceStream); switch (reader.Mesh.Type) { case VertexType.Rigid: return ReadRigidVertices(vertexReader, mainBuffer.Count); case VertexType.Skinned: return ReadSkinnedVertices(vertexReader, mainBuffer.Count); case VertexType.DualQuat: return ReadDualQuatVertices(vertexReader, mainBuffer.Count); default: throw new InvalidOperationException("Only Rigid, Skinned, and DualQuat meshes are supported"); } }
public ObjMesh ReadMesh(MeshReader reader, VertexCompressor compressor, Stream resourceStream) { var vertices = ReadVertices(reader, resourceStream); if (compressor != null) { DecompressVertices(vertices, compressor); } var indices = new List <uint>(); foreach (var part in reader.Mesh.Parts) { var partIndices = ReadIndices(reader, part, resourceStream); for (var i = 0; i < partIndices.Length; i += 3) { var a = partIndices[i] + _baseIndex; var b = partIndices[i + 1] + _baseIndex; var c = partIndices[i + 2] + _baseIndex; if (a == b || a == c || b == c) { continue; } indices.AddRange(new uint[] { a, b, c }); } } _baseIndex += (uint)vertices.Count; return(new ObjMesh { Vertices = vertices, Indices = indices }); }
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") return false; // Find the variant to extract if (_model.RenderModel == null) { Console.Error.WriteLine("The model does not have a render model associated with it."); return true; } var variant = _model.Variants.FirstOrDefault(v => (_stringIds.GetString(v.Name) ?? v.Name.ToString()) == variantName); if (variant == null && _model.Variants.Count > 0) { Console.Error.WriteLine("Unable to find variant \"{0}\"", variantName); Console.Error.WriteLine("Use \"listvariants\" to list available variants."); return true; } // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(_fileInfo.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 = _fileInfo.OpenRead()) { var renderModelContext = new TagSerializationContext(cacheStream, _cache, _model.RenderModel); renderModel = TagDeserializer.Deserialize<RenderModel>(renderModelContext); } if (renderModel.Resource == null) { Console.Error.WriteLine("Render model does not have a resource associated with it"); return true; } // Deserialize the resource definition var resourceContext = new ResourceSerializationContext(renderModel.Resource); var definition = TagDeserializer.Deserialize<RenderGeometryResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // Extract the resource data resourceManager.Extract(renderModel.Resource, resourceStream); using (var objFile = new StreamWriter(File.Open(fileName, FileMode.Create, FileAccess.Write))) { var objExtractor = new ObjExtractor(objFile); var vertexCompressor = new VertexCompressor(renderModel.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 >= 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 = _stringIds.GetString(region.Name) ?? region.Name.ToString(); var permutationName = _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(renderModel.Meshes[meshIndex + i], definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } } else { // No variant - just extract every mesh Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Meshes.Count); foreach (var mesh in renderModel.Meshes) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(mesh, definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } objExtractor.Finish(); } } Console.WriteLine("Done!"); return true; }
/// <summary> /// Reads the index buffer data and converts it into a triangle list if necessary. /// </summary> /// <param name="reader">The mesh reader to use.</param> /// <param name="part">The mesh part to read.</param> /// <param name="resourceStream">A stream open on the resource data.</param> /// <returns>The index buffer converted into a triangle list.</returns> private static uint[] ReadIndexes(MeshReader reader, Mesh.Part part, Stream resourceStream) { // Use index buffer 0 var indexBuffer = reader.IndexBuffers[0]; if (indexBuffer == null) throw new InvalidOperationException("Index buffer 0 is null"); // Read the indexes var indexStream = reader.OpenIndexBufferStream(indexBuffer, resourceStream); indexStream.Position = part.FirstIndex; switch (indexBuffer.Type) { case PrimitiveType.TriangleList: return indexStream.ReadIndexes(part.IndexCount); case PrimitiveType.TriangleStrip: return indexStream.ReadTriangleStrip(part.IndexCount); default: throw new InvalidOperationException("Unsupported index buffer type: " + indexBuffer.Type); } }