private static VertexElement[] CreateElement(VertexShaderFlags flag) { switch (flag) { case VertexShaderFlags.Position: return(new[] { VertexElement.Position(Format.R32G32B32_Float) }); case VertexShaderFlags.Normal: return(new[] { VertexElement.Normal(Format.R32G32B32_Float) }); case VertexShaderFlags.TextureUV: return(new[] { VertexElement.TextureCoordinate(Format.R32G32_Float) }); case VertexShaderFlags.TextureUVW: return(new[] { VertexElement.TextureCoordinate(Format.R32G32B32_Float) }); case VertexShaderFlags.Color: return(new[] { VertexElement.Color(Format.R32G32B32A32_Float) }); case VertexShaderFlags.Tangent: return(new[] { VertexElement.Tangent(Format.R32G32B32A32_Float) }); case VertexShaderFlags.Barycentric: return(new[] { new VertexElement("BARYCENTRIC", Format.R32G32B32_Float) }); case VertexShaderFlags.InstanceWorld: return(new[] { new VertexElement("INSTANCEWORLD", 0, Format.R32G32B32A32_Float), new VertexElement("INSTANCEWORLD", 1, Format.R32G32B32A32_Float), new VertexElement("INSTANCEWORLD", 2, Format.R32G32B32A32_Float), new VertexElement("INSTANCEWORLD", 3, Format.R32G32B32A32_Float), }); default: throw new ArgumentOutOfRangeException(string.Format("[{0}]: VertexShaderFlag not valid", flag)); } }
public static (VertexElement, int) ConvertVertexElement(KeyValuePair <string, SharpGLTF.Schema2.Accessor> accessor, int offset) { return((accessor.Key, accessor.Value.Format.ByteSize) switch { ("POSITION", 12) => (VertexElement.Position <Vector3>(0, offset), Vector3.SizeInBytes), ("NORMAL", 12) => (VertexElement.Normal <Vector3>(0, offset), Vector3.SizeInBytes), ("TANGENT", 12) => (VertexElement.Tangent <Vector3>(0, offset), Vector3.SizeInBytes), ("COLOR", 16) => (VertexElement.Color <Vector4>(0, offset), Vector4.SizeInBytes), ("TEXCOORD_0", 8) => (VertexElement.TextureCoordinate <Vector2>(0, offset), Vector2.SizeInBytes), ("TEXCOORD_1", 8) => (VertexElement.TextureCoordinate <Vector2>(1, offset), Vector2.SizeInBytes), ("TEXCOORD_2", 8) => (VertexElement.TextureCoordinate <Vector2>(2, offset), Vector2.SizeInBytes), ("TEXCOORD_3", 8) => (VertexElement.TextureCoordinate <Vector2>(3, offset), Vector2.SizeInBytes), ("TEXCOORD_4", 8) => (VertexElement.TextureCoordinate <Vector2>(4, offset), Vector2.SizeInBytes), ("TEXCOORD_5", 8) => (VertexElement.TextureCoordinate <Vector2>(5, offset), Vector2.SizeInBytes), ("TEXCOORD_6", 8) => (VertexElement.TextureCoordinate <Vector2>(6, offset), Vector2.SizeInBytes), ("TEXCOORD_7", 8) => (VertexElement.TextureCoordinate <Vector2>(7, offset), Vector2.SizeInBytes), ("TEXCOORD_8", 8) => (VertexElement.TextureCoordinate <Vector2>(8, offset), Vector2.SizeInBytes), ("TEXCOORD_9", 8) => (VertexElement.TextureCoordinate <Vector2>(9, offset), Vector2.SizeInBytes), ("JOINTS_0", 8) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R16G16B16A16_UInt, offset), 8), ("JOINTS_0", 4) => (new VertexElement(VertexElementUsage.BlendIndices, 0, PixelFormat.R8G8B8A8_UInt, offset), 4), ("WEIGHTS_0", 16) => (new VertexElement(VertexElementUsage.BlendWeight, 0, PixelFormat.R32G32B32A32_Float, offset), Vector4.SizeInBytes), _ => throw new NotImplementedException(), });
/// <summary> /// Generates the tangents and binormals for this mesh data. /// Tangents and bitangents will be encoded as float4: /// float3 for tangent and an additional float for handedness (1 or -1), /// so that bitangent can be reconstructed. /// More info at http://www.terathon.com/code/tangent.html /// </summary> /// <param name="meshData">The mesh data.</param> public static unsafe void GenerateTangentBinormal(this MeshDrawData meshData) { if (!meshData.IsSimple()) { throw new ArgumentException("meshData is not simple."); } if (meshData.PrimitiveType != PrimitiveType.TriangleList && meshData.PrimitiveType != PrimitiveType.TriangleListWithAdjacency) { throw new NotImplementedException(); } var vertexBufferBinding = meshData.VertexBuffers[0]; var indexBufferBinding = meshData.IndexBuffer; var indexData = indexBufferBinding != null ? indexBufferBinding.Buffer.Value.Content : null; var oldVertexStride = vertexBufferBinding.Declaration.VertexStride; var bufferData = vertexBufferBinding.Buffer.Value.Content; // TODO: Usage index in key var offsetMapping = vertexBufferBinding.Declaration .EnumerateWithOffsets() .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset); var positionOffset = offsetMapping["POSITION"]; var uvOffset = offsetMapping[VertexElementUsage.TextureCoordinate]; var normalOffset = offsetMapping[VertexElementUsage.Normal]; // Add tangent to vertex declaration var vertexElements = vertexBufferBinding.Declaration.VertexElements.ToList(); if (!offsetMapping.ContainsKey(VertexElementUsage.Tangent)) { vertexElements.Add(VertexElement.Tangent <Vector4>()); } vertexBufferBinding.Declaration = new VertexDeclaration(vertexElements.ToArray()); var newVertexStride = vertexBufferBinding.Declaration.VertexStride; // Update mapping offsetMapping = vertexBufferBinding.Declaration .EnumerateWithOffsets() .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset); var tangentOffset = offsetMapping[VertexElementUsage.Tangent]; var newBufferData = new byte[vertexBufferBinding.Count * newVertexStride]; var tangents = new Vector3[vertexBufferBinding.Count]; var bitangents = new Vector3[vertexBufferBinding.Count]; fixed(byte *indexBufferStart = indexData) fixed(byte *oldBuffer = &bufferData[vertexBufferBinding.Offset]) fixed(byte *newBuffer = &newBufferData[0]) { var indexBuffer32 = indexBufferBinding != null && indexBufferBinding.Is32Bit ? (int *)indexBufferStart : null; var indexBuffer16 = indexBufferBinding != null && !indexBufferBinding.Is32Bit ? (short *)indexBufferStart : null; var indexCount = indexBufferBinding != null ? indexBufferBinding.Count : vertexBufferBinding.Count; for (int i = 0; i < indexCount; i += 3) { // Get indices int index1 = i + 0; int index2 = i + 1; int index3 = i + 2; if (indexBuffer32 != null) { index1 = indexBuffer32[index1]; index2 = indexBuffer32[index2]; index3 = indexBuffer32[index3]; } else if (indexBuffer16 != null) { index1 = indexBuffer16[index1]; index2 = indexBuffer16[index2]; index3 = indexBuffer16[index3]; } int vertexOffset1 = index1 * oldVertexStride; int vertexOffset2 = index2 * oldVertexStride; int vertexOffset3 = index3 * oldVertexStride; // Get positions var position1 = (Vector3 *)&oldBuffer[vertexOffset1 + positionOffset]; var position2 = (Vector3 *)&oldBuffer[vertexOffset2 + positionOffset]; var position3 = (Vector3 *)&oldBuffer[vertexOffset3 + positionOffset]; // Get texture coordinates var uv1 = (Vector3 *)&oldBuffer[vertexOffset1 + uvOffset]; var uv2 = (Vector3 *)&oldBuffer[vertexOffset2 + uvOffset]; var uv3 = (Vector3 *)&oldBuffer[vertexOffset3 + uvOffset]; // Calculate position and UV vectors from vertex 1 to vertex 2 and 3 var edge1 = *position2 - *position1; var edge2 = *position3 - *position1; var uvEdge1 = *uv2 - *uv1; var uvEdge2 = *uv3 - *uv1; var t = Vector3.Normalize(uvEdge2.Y * edge1 - uvEdge1.Y * edge2); var b = Vector3.Normalize(uvEdge1.X * edge2 - uvEdge2.X * edge1); // Contribute to every vertex tangents[index1] += t; tangents[index2] += t; tangents[index3] += t; bitangents[index1] += b; bitangents[index2] += b; bitangents[index3] += b; } var oldVertexOffset = 0; var newVertexOffset = 0; for (int i = 0; i < vertexBufferBinding.Count; ++i) { Utilities.CopyMemory(new IntPtr(&newBuffer[newVertexOffset]), new IntPtr(&oldBuffer[oldVertexOffset]), oldVertexStride); var normal = *(Vector3 *)&oldBuffer[oldVertexOffset + normalOffset]; var target = ((float *)(&newBuffer[newVertexOffset + tangentOffset])); var tangent = -tangents[i]; var bitangent = bitangents[i]; // Gram-Schmidt orthogonalize *((Vector3 *)target) = Vector3.Normalize(tangent - normal * Vector3.Dot(normal, tangent)); // Calculate handedness target[3] = Vector3.Dot(Vector3.Cross(normal, tangent), bitangent) < 0.0f ? -1.0f : 1.0f; oldVertexOffset += oldVertexStride; newVertexOffset += newVertexStride; } } vertexBufferBinding.Offset = 0; vertexBufferBinding.Buffer = new BufferData(BufferFlags.VertexBuffer, newBufferData); }
private ModelData.MeshPart Process(ModelData.Mesh mesh, Mesh assimpMesh) { var meshPart = new ModelData.MeshPart { PrimitiveTopology = PrimitiveTopology.TriangleList, VertexBufferRange = new ModelData.BufferRange { Slot = mesh.VertexBuffers.Count }, IndexBufferRange = new ModelData.BufferRange { Slot = mesh.IndexBuffers.Count } }; var vertexBuffer = new ModelData.VertexBuffer { Layout = new List <VertexElement>() }; mesh.VertexBuffers.Add(vertexBuffer); var indexBuffer = new ModelData.IndexBuffer(); mesh.IndexBuffers.Add(indexBuffer); var layout = vertexBuffer.Layout; int vertexBufferElementSize = 0; // Add position layout.Add(VertexElement.PositionTransformed(Format.R32G32B32_Float, 0)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector3>(); // Add normals if (assimpMesh.HasNormals) { layout.Add(VertexElement.Normal(0, Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector3>(); } // Add colors if (assimpMesh.VertexColorChannelCount > 0) { for (int localIndex = 0, i = 0; i < assimpMesh.VertexColorChannelCount; i++) { if (assimpMesh.HasVertexColors(i)) { layout.Add(VertexElement.Normal(localIndex, Format.R32G32B32A32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Color4>(); localIndex++; } } } // Add textures if (assimpMesh.TextureCoordsChannelCount > 0) { for (int localIndex = 0, i = 0; i < assimpMesh.TextureCoordsChannelCount; i++) { if (assimpMesh.HasTextureCoords(i)) { var uvCount = assimpMesh.GetUVComponentCount(i); if (uvCount == 2) { layout.Add(VertexElement.TextureCoordinate(localIndex, Format.R32G32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector2>(); } else if (uvCount == 3) { layout.Add(VertexElement.TextureCoordinate(localIndex, Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector3>(); } else { throw new InvalidOperationException("Unexpected uv count"); } localIndex++; } } } if (options.ModelOperations.HasFlag(ModelOperation.CalculateBarycentricCoordinates)) { layout.Add(new VertexElement("BARYCENTRIC", 0, Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector3>(); } else // Add tangent / bitangent if (assimpMesh.HasTangentBasis) { if (!options.ExcludeElements.Contains("Tangent")) { layout.Add(VertexElement.Tangent(Format.R32G32B32A32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector4>(); } if (!options.ExcludeElements.Contains("BiTangent")) { layout.Add(VertexElement.BiTangent(Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += SharpDX.Utilities.SizeOf <Vector3>(); } } if (options.ModelOperations.HasFlag(ModelOperation.CalculateBarycentricCoordinates)) { WriteBarycentricVertices(assimpMesh, meshPart, vertexBuffer, vertexBufferElementSize); } else { WriteVertices(assimpMesh, meshPart, vertexBuffer, vertexBufferElementSize); } WriteIndices(assimpMesh, meshPart, indexBuffer); return(meshPart); }