private Mesh CreateGltfMesh(string meshName, VMesh vmesh, ModelRoot model) { ProgressDialog.SetProgress($"Creating mesh: {meshName}"); var data = vmesh.GetData(); var vbib = vmesh.VBIB; var mesh = model.CreateMesh(meshName); mesh.Name = meshName; foreach (var sceneObject in data.GetArray("m_sceneObjects")) { foreach (var drawCall in sceneObject.GetArray("m_drawCalls")) { var vertexBufferInfo = drawCall.GetArray("m_vertexBuffers")[0]; // In what situation can we have more than 1 vertex buffer per draw call? var vertexBufferIndex = (int)vertexBufferInfo.GetIntegerProperty("m_hBuffer"); var vertexBuffer = vbib.VertexBuffers[vertexBufferIndex]; var indexBufferInfo = drawCall.GetSubCollection("m_indexBuffer"); var indexBufferIndex = (int)indexBufferInfo.GetIntegerProperty("m_hBuffer"); var indexBuffer = vbib.IndexBuffers[indexBufferIndex]; // Create one primitive per draw call var primitive = mesh.CreatePrimitive(); // Avoid duplicate attribute names var uniqueAttributes = vertexBuffer.Attributes.GroupBy(a => a.Name).Select(g => g.First()); // Set vertex attributes foreach (var attribute in uniqueAttributes) { if (AccessorInfo.TryGetValue(attribute.Name, out var accessorInfo)) { var buffer = ReadAttributeBuffer(vbib, vertexBuffer, attribute); if (accessorInfo.NumComponents == 4) { var vectors = ToVector4Array(buffer); primitive.WithVertexAccessor(accessorInfo.GltfAccessorName, vectors); } else if (attribute.Name == "NORMAL" && DrawCall.IsCompressedNormalTangent(drawCall)) { var vectors = ToVector4Array(buffer); var(normals, tangents) = DecompressNormalTangents(vectors); primitive.WithVertexAccessor("NORMAL", normals); primitive.WithVertexAccessor("TANGENT", tangents); } else if (accessorInfo.NumComponents == 3) { var vectors = ToVector3Array(buffer, true, accessorInfo.Resize); primitive.WithVertexAccessor(accessorInfo.GltfAccessorName, vectors); } else if (accessorInfo.NumComponents == 2) { var vectors = ToVector2Array(buffer); primitive.WithVertexAccessor(accessorInfo.GltfAccessorName, vectors); } } } // Set index buffer var indices = ReadIndices(indexBuffer); // For triangle primitives, the front face has to be in counter-clockwise (CCW) winding order. for (var i = 0; i < indices.Length; i += 3) { var b = indices[i + 2]; indices[i + 2] = indices[i + 1]; indices[i + 1] = b; } primitive.WithIndicesAccessor(PrimitiveType.TRIANGLES, indices); // Add material var materialPath = drawCall.GetProperty <string>("m_material"); ProgressDialog.SetProgress($"Loading material: {materialPath}"); var materialResource = GuiContext.LoadFileByAnyMeansNecessary(materialPath + "_c"); if (materialResource == null) { continue; } var renderMaterial = (VMaterial)materialResource.DataBlock; var materialNameTrimmed = Path.GetFileNameWithoutExtension(materialPath); var bestMaterial = GenerateGLTFMaterialFromRenderMaterial(renderMaterial, model, materialNameTrimmed); primitive.WithMaterial(bestMaterial); } } return(mesh); }
private Mesh CreateGltfMesh(string meshName, VMesh vmesh, ModelRoot model, bool includeJoints) { ProgressDialog.SetProgress($"Creating mesh: {meshName}"); var data = vmesh.GetData(); var vbib = vmesh.VBIB; var mesh = model.CreateMesh(meshName); mesh.Name = meshName; foreach (var sceneObject in data.GetArray("m_sceneObjects")) { foreach (var drawCall in sceneObject.GetArray("m_drawCalls")) { var vertexBufferInfo = drawCall.GetArray("m_vertexBuffers")[0]; // In what situation can we have more than 1 vertex buffer per draw call? var vertexBufferIndex = (int)vertexBufferInfo.GetIntegerProperty("m_hBuffer"); var vertexBuffer = vbib.VertexBuffers[vertexBufferIndex]; var indexBufferInfo = drawCall.GetSubCollection("m_indexBuffer"); var indexBufferIndex = (int)indexBufferInfo.GetIntegerProperty("m_hBuffer"); var indexBuffer = vbib.IndexBuffers[indexBufferIndex]; // Create one primitive per draw call var primitive = mesh.CreatePrimitive(); // Avoid duplicate attribute names var attributeCounters = new Dictionary <string, int>(); // Set vertex attributes foreach (var attribute in vertexBuffer.Attributes) { attributeCounters.TryGetValue(attribute.Name, out var attributeCounter); attributeCounters[attribute.Name] = attributeCounter + 1; var accessorName = GetAccessorName(attribute.Name, attributeCounter); var buffer = ReadAttributeBuffer(vertexBuffer, attribute); var numComponents = buffer.Length / vertexBuffer.Count; if (attribute.Name == "BLENDINDICES") { if (!includeJoints) { continue; } var byteBuffer = buffer.Select(f => (byte)f).ToArray(); var rawBufferData = new byte[buffer.Length]; System.Buffer.BlockCopy(byteBuffer, 0, rawBufferData, 0, rawBufferData.Length); var bufferView = mesh.LogicalParent.UseBufferView(rawBufferData); var accessor = mesh.LogicalParent.CreateAccessor(); accessor.SetVertexData(bufferView, 0, buffer.Length / 4, DimensionType.VEC4, EncodingType.UNSIGNED_BYTE); primitive.SetVertexAccessor(accessorName, accessor); continue; } if (attribute.Name == "NORMAL" && DrawCall.IsCompressedNormalTangent(drawCall)) { var vectors = ToVector4Array(buffer); var(normals, tangents) = DecompressNormalTangents(vectors); primitive.WithVertexAccessor("NORMAL", normals); primitive.WithVertexAccessor("TANGENT", tangents); continue; } if (attribute.Name == "BLENDINDICES") { var byteBuffer = buffer.Select(f => (byte)f).ToArray(); var bufferView = mesh.LogicalParent.UseBufferView(byteBuffer); var accessor = mesh.LogicalParent.CreateAccessor(); accessor.SetVertexData(bufferView, 0, buffer.Length / 4, DimensionType.VEC4, EncodingType.UNSIGNED_BYTE); primitive.SetVertexAccessor(accessorName, accessor); continue; } if (attribute.Name == "TEXCOORD" && numComponents != 2) { // We are ignoring some data, but non-2-component UVs cause failures in gltf consumers continue; } switch (numComponents) { case 4: { var vectors = ToVector4Array(buffer); primitive.WithVertexAccessor(accessorName, vectors); break; } case 3: { var vectors = ToVector3Array(buffer); primitive.WithVertexAccessor(accessorName, vectors); break; } case 2: { var vectors = ToVector2Array(buffer); primitive.WithVertexAccessor(accessorName, vectors); break; } case 1: { primitive.WithVertexAccessor(accessorName, buffer); break; } default: throw new NotImplementedException($"Attribute \"{attribute.Name}\" has {numComponents} components"); } } // For some reason soruce models can have joints but no weights, check if that is the case var jointAccessor = primitive.GetVertexAccessor("JOINTS_0"); if (jointAccessor != null && primitive.GetVertexAccessor("WEIGHTS_0") == null) { // If this occurs, give default weights var defaultWeights = Enumerable.Repeat(Vector4.UnitX, jointAccessor.Count).ToList(); primitive.WithVertexAccessor("WEIGHTS_0", defaultWeights); } // Set index buffer var startIndex = (int)drawCall.GetIntegerProperty("m_nStartIndex"); var indexCount = (int)drawCall.GetIntegerProperty("m_nIndexCount"); var indices = ReadIndices(indexBuffer, startIndex, indexCount); primitive.WithIndicesAccessor(PrimitiveType.TRIANGLES, indices); // Add material var materialPath = drawCall.GetProperty <string>("m_material"); ProgressDialog.SetProgress($"Loading material: {materialPath}"); var materialResource = GuiContext.LoadFileByAnyMeansNecessary(materialPath + "_c"); if (materialResource == null) { continue; } var renderMaterial = (VMaterial)materialResource.DataBlock; var materialNameTrimmed = Path.GetFileNameWithoutExtension(materialPath); var bestMaterial = GenerateGLTFMaterialFromRenderMaterial(renderMaterial, model, materialNameTrimmed); primitive.WithMaterial(bestMaterial); } } return(mesh); }
/// <summary> /// Loads the VMesh into our Objmesh /// </summary> /// <param name="objMesh">The objmesh to load into</param> /// <param name="mesh">The VMesh to load</param> private void LoadVMeshIntoMesh(ObjMesh objMesh, VMesh mesh) { var data = mesh.GetData(); var vbib = mesh.VBIB; foreach (var sceneObject in data.GetArray("m_sceneObjects")) { foreach (var drawCall in sceneObject.GetArray("m_drawCalls")) { var startingVertexCount = objMesh.Positions.Count; // Set this so we can offset the indicies for triangles correctly var vertexBufferInfo = drawCall.GetArray("m_vertexBuffers")[0]; // In what situation can we have more than 1 vertex buffer per draw call? var vertexBufferIndex = (int)vertexBufferInfo.GetIntegerProperty("m_hBuffer"); var vertexBuffer = vbib.VertexBuffers[vertexBufferIndex]; var indexBufferInfo = drawCall.GetSubCollection("m_indexBuffer"); var indexBufferIndex = (int)indexBufferInfo.GetIntegerProperty("m_hBuffer"); var indexBuffer = vbib.IndexBuffers[indexBufferIndex]; // Set vertex attributes foreach (var attribute in vertexBuffer.Attributes) { var buffer = ReadAttributeBuffer(vertexBuffer, attribute); var numComponents = buffer.Length / vertexBuffer.Count; if (attribute.Name == "BLENDINDICES") { var byteBuffer = buffer.Select(f => (byte)f).ToArray(); var rawBufferData = new byte[buffer.Length]; System.Buffer.BlockCopy(byteBuffer, 0, rawBufferData, 0, rawBufferData.Length); var blendIndices = rawBufferData.ChunkBy(4); objMesh.BlendIndices.AddRange(blendIndices); continue; } if (attribute.Name == "BLENDWEIGHT") { var vectors = ToVector4Array(buffer); objMesh.BlendWeights.AddRange(vectors); } if (attribute.Name == "POSITION") { var vectors = ToVector3Array(buffer); objMesh.Positions.AddRange(vectors); } if (attribute.Name == "NORMAL") { if (VMesh.IsCompressedNormalTangent(drawCall)) { var vectors = ToVector4Array(buffer); var(normals, tangents) = DecompressNormalTangents(vectors); objMesh.Normals.AddRange(normals); } else { var vectors = ToVector3Array(buffer); objMesh.Normals.AddRange(vectors); } continue; } if (attribute.Name == "TEXCOORD") { if (numComponents != 2) { // ignore textcoords that arent 2 continue; } var vectors = ToVector2Array(buffer); objMesh.TextureCoords.AddRange(vectors); } } // Set index buffer var startIndex = (int)drawCall.GetIntegerProperty("m_nStartIndex"); var indexCount = (int)drawCall.GetIntegerProperty("m_nIndexCount"); var indices = ReadIndices(indexBuffer, startIndex, indexCount); var newFaces = indices.ChunkBy(3).Select(idxList => idxList.Select(idx => new ObjFaceVertex(idx + startingVertexCount)).ToList()).ToList(); objMesh.Faces.AddRange(newFaces); var materialPath = drawCall.GetProperty <string>("m_material"); var materialResource = VpkLoader.LoadFile(materialPath + "_c"); var renderMaterial = (VMaterial)materialResource.DataBlock; var matName = Path.GetFileNameWithoutExtension(materialPath); if (objMesh.Material == null) { objMesh.Material = ObjMaterial.FromVMaterial(renderMaterial, Path.GetFileNameWithoutExtension(materialPath), VpkLoader); } else if (matName != objMesh.Material.Name) { // throw new Exception("2 different mats in same object"); } } } }