/// <summary> /// Gets vertex data from a given <see cref="AssimpMesh"/>. /// </summary> /// <param name="sceneMesh">The <see cref="AssimpMesh"/> to get vertex information from.</param> /// <param name="boneIndexByName">A lookup of bone index by name information.</param> /// <returns>An output list of vertices.</returns> private Vertex[] GetMeshVertices(AssimpMesh sceneMesh, Dictionary <string, uint> boneIndexByName) { var positions = sceneMesh.Vertices; var normals = sceneMesh.Normals; var tangents = sceneMesh.Tangents; var colors = sceneMesh.HasVertexColors(0) ? sceneMesh.VertexColorChannels[0] : null; var textureCoordinates = sceneMesh.HasTextureCoords(0) ? sceneMesh.TextureCoordinateChannels[0] : null; var boneWeightsByVertexIndex = GetBoneWeightsByVertexIndex(boneIndexByName, sceneMesh); var vertices = new List <Vertex>(); for (var i = 0; i < positions.Count; i++) { var position = positions[i]; var normal = normals[i]; var tangent = tangents[i]; var color = colors?[i]; var textureCoordinate = textureCoordinates?[i]; TryGetBoneIndicesAndWeights(boneWeightsByVertexIndex, i, out var boneIndices, out var boneWeights); vertices.Add(new Vertex(new Vector3(position.X, position.Y, position.Z), new Vector3(normal.X, normal.Y, normal.Z), new Vector3(tangent.X, tangent.Y, tangent.Z), color != null ? new Color(color.Value.R, color.Value.G, color.Value.B, color.Value.A) : new Color(1.0f, 1.0f, 1.0f, 1.0f), textureCoordinate != null ? new Vector2(textureCoordinate.Value.X, textureCoordinate.Value.Y) : Vector2.Zero, new ReadOnlyCollection <uint>(boneIndices), new ReadOnlyCollection <float>(boneWeights))); } return(vertices.ToArray()); }
IEnumerable <AssimpVertex> GetVertex(Assimp.Mesh m) { for (int i = 0; i < m.VertexCount; ++i) { Vector3 cc = m.Vertices[i].ToVector3(); var bw = GetWAndB(m, i); yield return(new AssimpVertex() { position = cc, uv = m.HasTextureCoords(0) ? m.TextureCoordinateChannels[0][i].ToVector3() : new Vector3(), tangent = m.HasTangentBasis ? m.Tangents[i].ToVector3() : new Vector3(), biTangent = m.HasTangentBasis ? m.BiTangents[i].ToVector3() : new Vector3(), normal = m.HasNormals ? m.Normals[i].ToVector3() : new Vector3(), BoneID = HasAnimation && m.HasBones ? GetBoneID(bw) : new Vector4(), BoneWheight = HasAnimation && m.HasBones ? GetWheight(bw) : new Vector4(), Color = m.HasVertexColors(0) ? m.VertexColorChannels[0][i].ToColor4() : new Color4(0.5f, 0.5f, 0.5f, 1) }); } yield break; }
//determine the number of elements in the vertex private int GetNoofInputElements(Assimp.Mesh mesh) { bool hasTexCoords = mesh.HasTextureCoords(0); bool hasColors = mesh.HasVertexColors(0); bool hasNormals = mesh.HasNormals; bool hasTangents = mesh.Tangents != null && mesh.Tangents.Count > 0; bool hasBitangents = mesh.BiTangents != null && mesh.BiTangents.Count > 0; int noofElements = 1; if (hasColors) { noofElements++; } if (hasNormals) { noofElements++; } if (hasTangents) { noofElements++; } if (hasBitangents) { noofElements++; } if (hasTexCoords) { noofElements++; } return(noofElements); }
public void SetMesh(MainWindow host, Mesh mesh, string meshName) { Debug.Assert(mesh != null); Debug.Assert(host != null); Debug.Assert(meshName != null); _mesh = mesh; _host = host; labelVertexCount.Text = mesh.VertexCount + " Vertices"; labelFaceCount.Text = mesh.FaceCount + " Faces"; Text = meshName + " - Details"; checkedListBoxPerFace.CheckOnClick = false; checkedListBoxPerFace.SetItemCheckState(0, mesh.PrimitiveType.HasFlag(PrimitiveType.Triangle) ? CheckState.Checked : CheckState.Unchecked); checkedListBoxPerFace.SetItemCheckState(1, mesh.PrimitiveType.HasFlag(PrimitiveType.Line) ? CheckState.Checked : CheckState.Unchecked); checkedListBoxPerFace.SetItemCheckState(2, mesh.PrimitiveType.HasFlag(PrimitiveType.Point) ? CheckState.Checked : CheckState.Unchecked); checkedListBoxPerVertex.CheckOnClick = false; checkedListBoxPerVertex.SetItemCheckState(0, CheckState.Checked); checkedListBoxPerVertex.SetItemCheckState(1, mesh.HasNormals ? CheckState.Checked : CheckState.Unchecked); checkedListBoxPerVertex.SetItemCheckState(2, mesh.HasTangentBasis ? CheckState.Checked : CheckState.Unchecked); Debug.Assert(mesh.TextureCoordinateChannels.Length >= 4); for (var i = 0; i < 4; ++i) { checkedListBoxPerVertex.SetItemCheckState(3 + i, mesh.HasTextureCoords(i) ? CheckState.Checked : CheckState.Unchecked); } Debug.Assert(mesh.VertexColorChannels.Length >= 4); for (var i = 0; i < 4; ++i) { checkedListBoxPerVertex.SetItemCheckState(7 + i, mesh.HasVertexColors(i) ? CheckState.Checked : CheckState.Unchecked); } checkedListBoxPerVertex.SetItemCheckState(11, mesh.HasBones ? CheckState.Checked : CheckState.Unchecked); }
//Create meshes and add vertex and index buffers private void AddVertexData(Model model, Scene scene, Node node, Device device, ref Matrix transform) { Matrix previousTransform = transform; transform = Matrix.Multiply(previousTransform, FromMatrix(node.Transform)); //also calculate inverse transpose matrix for normal/tangent/bitagent transformation Matrix invTranspose = transform; invTranspose.Invert(); invTranspose.Transpose(); if (node.HasMeshes) { foreach (int index in node.MeshIndices) { //get a mesh from the scene Assimp.Mesh mesh = scene.Meshes[index]; //create new mesh to add to model ModelMesh modelMesh = new ModelMesh(); model.AddMesh(ref modelMesh); //if mesh has a material extract the diffuse texture, if present Material material = scene.Materials[mesh.MaterialIndex]; if (material != null && material.GetMaterialTextureCount(TextureType.Diffuse) > 0) { material.GetMaterialTexture(TextureType.Diffuse, 0, out TextureSlot texture); //create new texture for mesh modelMesh.AddTextureDiffuse(device, m_modelPath + "\\" + texture.FilePath); } //determine the elements in the vertex bool hasTexCoords = mesh.HasTextureCoords(0); bool hasColors = mesh.HasVertexColors(0); bool hasNormals = mesh.HasNormals; bool hasTangents = mesh.Tangents != null && mesh.Tangents.Count > 0; bool hasBitangents = mesh.BiTangents != null && mesh.BiTangents.Count > 0; //create vertex element list InputElement[] vertexElements = new InputElement[GetNoofInputElements(mesh)]; uint elementIndex = 0; vertexElements[elementIndex++] = new InputElement("POSITION", 0, Format.R32G32B32_Float, 0, 0); short vertexSize = (short)Utilities.SizeOf <Vector3>(); if (hasColors) { vertexElements[elementIndex++] = new InputElement("COLOR", 0, Format.R8G8B8A8_UInt, vertexSize, 0); vertexSize += (short)Utilities.SizeOf <Color>(); } if (hasNormals) { vertexElements[elementIndex++] = new InputElement("NORMAL", 0, Format.R32G32B32_Float, vertexSize, 0); vertexSize += (short)Utilities.SizeOf <Vector3>(); } if (hasTangents) { vertexElements[elementIndex++] = new InputElement("TANGENT", 0, Format.R32G32B32_Float, vertexSize, 0); vertexSize += (short)Utilities.SizeOf <Vector3>(); } if (hasBitangents) { vertexElements[elementIndex++] = new InputElement("BITANGENT", 0, Format.R32G32B32_Float, vertexSize, 0); vertexSize += (short)Utilities.SizeOf <Vector3>(); } if (hasTexCoords) { vertexElements[elementIndex++] = new InputElement("TEXCOORD", 0, Format.R32G32_Float, vertexSize, 0); vertexSize += (short)Utilities.SizeOf <Vector2>(); } //set the vertex elements and size modelMesh.InputElements = vertexElements; modelMesh.VertexSize = vertexSize; //get pointers to vertex data var positions = mesh.Vertices; var texCoords = mesh.TextureCoordinateChannels[0]; var normals = mesh.Normals; var tangents = mesh.Tangents; var biTangents = mesh.BiTangents; var colours = mesh.VertexColorChannels[0]; //also determine primitive type switch (mesh.PrimitiveType) { case PrimitiveType.Point: modelMesh.PrimitiveTopology = PrimitiveTopology.PointList; break; case PrimitiveType.Line: modelMesh.PrimitiveTopology = PrimitiveTopology.LineList; break; case PrimitiveType.Triangle: modelMesh.PrimitiveTopology = PrimitiveTopology.TriangleList; break; default: throw new Exception("ModelLoader::AddVertexData(): Unknown primitive type"); } //create data stream for vertices DataStream vertexStream = new DataStream(mesh.VertexCount * vertexSize, true, true); for (int i = 0; i < mesh.VertexCount; i++) { //add position, after transforming it with accumulated node transform { Vector4 result; Vector3 pos = FromVector(positions[i]); Vector3.Transform(ref pos, ref transform, out result); vertexStream.Write <Vector3>(new Vector3(result.X, result.Y, result.Z)); } if (hasColors) { Color vertColor = FromColor(mesh.VertexColorChannels[0][i]); vertexStream.Write <Color>(vertColor); } if (hasNormals) { Vector4 result; Vector3 normal = FromVector(normals[i]); Vector3.Transform(ref normal, ref invTranspose, out result); vertexStream.Write <Vector3>(new Vector3(result.X, result.Y, result.Z)); } if (hasTangents) { Vector4 result; Vector3 tangent = FromVector(tangents[i]); Vector3.Transform(ref tangent, ref invTranspose, out result); vertexStream.Write <Vector3>(new Vector3(result.X, result.Y, result.Z)); } if (hasBitangents) { Vector4 result; Vector3 biTangent = FromVector(biTangents[i]); Vector3.Transform(ref biTangent, ref invTranspose, out result); vertexStream.Write <Vector3>(new Vector3(result.X, result.Y, result.Z)); } if (hasTexCoords) { vertexStream.Write <Vector2>(new Vector2(texCoords[i].X, 1 - texCoords[i].Y)); } } vertexStream.Position = 0; //create new vertex buffer var vertexBuffer = new Buffer(device, vertexStream, new BufferDescription() { BindFlags = BindFlags.VertexBuffer, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None, SizeInBytes = mesh.VertexCount * vertexSize, Usage = ResourceUsage.Default } ); //add it to the mesh modelMesh.VertexBuffer = vertexBuffer; modelMesh.VertexCount = mesh.VertexCount; modelMesh.PrimitiveCount = mesh.FaceCount; //get pointer to indices data uint[] indices = mesh.GetIndices().Select(i => (uint)i).ToArray(); //create data stream for indices DataStream indexStream = new DataStream(indices.GetLength(0) * sizeof(uint), true, true); for (int i = 0; i < indices.GetLength(0); i++) { indexStream.Write <uint>(indices[i]); } indexStream.Position = 0; //create new index buffer var indexBuffer = new Buffer(device, indexStream, new BufferDescription() { BindFlags = BindFlags.IndexBuffer, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None, SizeInBytes = indices.GetLength(0) * sizeof(uint), Usage = ResourceUsage.Default } ); //add it to the mesh modelMesh.IndexBuffer = indexBuffer; modelMesh.IndexCount = indices.GetLength(0); } } //if node has more children process them as well for (int i = 0; i < node.ChildCount; i++) { AddVertexData(model, scene, node.Children[i], device, ref transform); } transform = previousTransform; }
/// <summary> /// Draw a mesh using either its given material or a transparent "ghost" material. /// </summary> /// <param name="node">Current node</param> /// <param name="animated">Specifies whether animations should be played</param> /// <param name="showGhost">Indicates whether to substitute the mesh' material with a /// "ghost" surrogate material that allows looking through the geometry.</param> /// <param name="index">Mesh index in the scene</param> /// <param name="mesh">Mesh instance</param> /// <param name="flags"> </param> /// <returns></returns> protected override bool InternDrawMesh(Node node, bool animated, bool showGhost, int index, Mesh mesh, RenderFlags flags) { if (showGhost) { Owner.MaterialMapper.ApplyGhostMaterial(mesh, Owner.Raw.Materials[mesh.MaterialIndex], flags.HasFlag(RenderFlags.Shaded)); } else { Owner.MaterialMapper.ApplyMaterial(mesh, Owner.Raw.Materials[mesh.MaterialIndex], flags.HasFlag(RenderFlags.Textured), flags.HasFlag(RenderFlags.Shaded)); } if (GraphicsSettings.Default.BackFaceCulling) { GL.FrontFace(FrontFaceDirection.Ccw); GL.CullFace(CullFaceMode.Back); GL.Enable(EnableCap.CullFace); } else { GL.Disable(EnableCap.CullFace); } var hasColors = mesh.HasVertexColors(0); var hasTexCoords = mesh.HasTextureCoords(0); var skinning = mesh.HasBones && animated; foreach (var face in mesh.Faces) { BeginMode faceMode; switch (face.IndexCount) { case 1: faceMode = BeginMode.Points; break; case 2: faceMode = BeginMode.Lines; break; case 3: faceMode = BeginMode.Triangles; break; default: faceMode = BeginMode.Polygon; break; } GL.Begin(faceMode); for (var i = 0; i < face.IndexCount; i++) { var indice = face.Indices[i]; if (hasColors) { var vertColor = AssimpToOpenTk.FromColor(mesh.VertexColorChannels[0][indice]); GL.Color4(vertColor); } if (mesh.HasNormals) { Vector3 normal; if (skinning) { Skinner.GetTransformedVertexNormal(node, mesh, (uint)indice, out normal); } else { normal = AssimpToOpenTk.FromVector(mesh.Normals[indice]); } GL.Normal3(normal); } if (hasTexCoords) { var uvw = AssimpToOpenTk.FromVector(mesh.TextureCoordinateChannels[0][indice]); GL.TexCoord2(uvw.X, 1 - uvw.Y); } Vector3 pos; if (skinning) { Skinner.GetTransformedVertexPosition(node, mesh, (uint)indice, out pos); } else { pos = AssimpToOpenTk.FromVector(mesh.Vertices[indice]); } GL.Vertex3(pos); } GL.End(); } GL.Disable(EnableCap.CullFace); return skinning; }
/// <summary> /// Determine the number of elements in the vertex. /// </summary> private static int GetNumberOfInputElements(Mesh mesh) { bool hasTexCoords = mesh.HasTextureCoords(0); bool hasColors = mesh.HasVertexColors(0); bool hasNormals = mesh.HasNormals; bool hasTangents = mesh.Tangents != null; bool hasBitangents = mesh.BiTangents != null; int noofElements = 1; if (hasColors) noofElements++; if (hasNormals) noofElements++; if (hasTangents) noofElements++; if (hasBitangents) noofElements++; if (hasTexCoords) noofElements++; return noofElements; }
private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) { var meshPart = new ModelData.MeshPart() { MaterialIndex = assimpMesh.MaterialIndex, 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 += Utilities.SizeOf <SharpDX.Vector3>(); // Add normals if (assimpMesh.HasNormals) { layout.Add(VertexElement.Normal(0, Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Vector3>(); } // Add colors if (assimpMesh.VertexColorChannelCount > 0) { for (int localIndex = 0, i = 0; i < assimpMesh.VertexColorChannelCount; i++) { if (assimpMesh.HasVertexColors(i)) { layout.Add(VertexElement.Color(localIndex, Format.R32G32B32A32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.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 += Utilities.SizeOf <SharpDX.Vector2>(); } else if (uvCount == 3) { layout.Add(VertexElement.TextureCoordinate(localIndex, Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Vector3>(); } else { throw new InvalidOperationException("Unexpected uv count"); } localIndex++; } } } // Add tangent / bitangent if (assimpMesh.HasTangentBasis) { layout.Add(VertexElement.Tangent(Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Vector3>(); layout.Add(VertexElement.BiTangent(Format.R32G32B32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Vector3>(); } // Extract Skinning Indices / Weights bool hasWeights = false; var skinningCount = new int[assimpMesh.VertexCount]; var skinningIndices = new Int4[assimpMesh.VertexCount]; var skinningWeights = new Vector4[assimpMesh.VertexCount]; if (assimpMesh.HasBones) { meshPart.BoneOffsetMatrices = new Matrix[assimpMesh.BoneCount]; for (int i = 0; i < assimpMesh.Bones.Length; i++) { var bone = assimpMesh.Bones[i]; meshPart.BoneOffsetMatrices[i] = ConvertMatrix(bone.OffsetMatrix); if (bone.HasVertexWeights) { var boneNode = scene.RootNode.FindNode(bone.Name); var boneIndex = skinnedBones[boneNode]; for (int j = 0; j < bone.VertexWeightCount; j++) { var weights = bone.VertexWeights[j]; var vertexSkinningCount = skinningCount[weights.VertexID]; skinningIndices[weights.VertexID][vertexSkinningCount] = boneIndex; skinningWeights[weights.VertexID][vertexSkinningCount] = weights.Weight; skinningCount[weights.VertexID] = ++vertexSkinningCount; } hasWeights = true; } } if (hasWeights) { layout.Add(VertexElement.BlendIndices(Format.R16G16B16A16_SInt, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Int4>(); layout.Add(VertexElement.BlendWeights(Format.R32G32B32A32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf <SharpDX.Vector4>(); } } // Write all vertices meshPart.VertexBufferRange.Count = assimpMesh.VertexCount; vertexBuffer.Count = assimpMesh.VertexCount; vertexBuffer.Buffer = new byte[vertexBufferElementSize * assimpMesh.VertexCount]; // Update the MaximumBufferSizeInBytes needed to load this model if (vertexBuffer.Buffer.Length > model.MaximumBufferSizeInBytes) { model.MaximumBufferSizeInBytes = vertexBuffer.Buffer.Length; } var vertexStream = DataStream.Create(vertexBuffer.Buffer, true, true); for (int i = 0; i < assimpMesh.VertexCount; i++) { var position = assimpMesh.Vertices[i]; vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation boundingPoints[currentBoundingPointIndex++] = new Vector3(position.X, position.Y, position.Z); // Add normals if (assimpMesh.HasNormals) { vertexStream.Write(assimpMesh.Normals[i]); } // Add colors if (assimpMesh.VertexColorChannelCount > 0) { for (int j = 0; j < assimpMesh.VertexColorChannelCount; j++) { if (assimpMesh.HasVertexColors(j)) { vertexStream.Write(assimpMesh.GetVertexColors(j)[i]); } } } // Add textures if (assimpMesh.TextureCoordsChannelCount > 0) { for (int j = 0; j < assimpMesh.TextureCoordsChannelCount; j++) { if (assimpMesh.HasTextureCoords(j)) { var uvCount = assimpMesh.GetUVComponentCount(j); var uv = assimpMesh.GetTextureCoords(j)[i]; if (uvCount == 2) { vertexStream.Write(new Vector2(uv.X, uv.Y)); } else { vertexStream.Write(uv); } } } } // Add tangent / bitangent if (assimpMesh.HasTangentBasis) { vertexStream.Write(assimpMesh.Tangents[i]); vertexStream.Write(assimpMesh.BiTangents[i]); } // Add Skinning Indices/Weights if (assimpMesh.HasBones && hasWeights) { vertexStream.Write(skinningIndices[i]); vertexStream.Write(skinningWeights[i]); } } vertexStream.Dispose(); // Write all indices var indices = assimpMesh.GetIntIndices(); indexBuffer.Count = indices.Length; meshPart.IndexBufferRange.Count = indices.Length; if (meshPart.VertexBufferRange.Count < 65536) { // Write only short indices if count is less than the size of a short indexBuffer.Buffer = new byte[indices.Length * 2]; using (var indexStream = DataStream.Create(indexBuffer.Buffer, true, true)) foreach (int index in indices) { indexStream.Write((ushort)index); } } else { // Otherwise, use full 32-bit precision to store indices indexBuffer.Buffer = new byte[indices.Length * 4]; using (var indexStream = DataStream.Create(indexBuffer.Buffer, true, true)) indexStream.WriteRange(indices); } // Update the MaximumBufferSizeInBytes needed to load this model if (indexBuffer.Buffer.Length > model.MaximumBufferSizeInBytes) { model.MaximumBufferSizeInBytes = indexBuffer.Buffer.Length; } return(meshPart); }
private void LoadFromNode(Assimp.Scene _scene, Assimp.Node _node, GraphicsDevice _device, SharpDX.Toolkit.Content.ContentManager _content, Matrix _transform) { // Sum up transformations recursively _transform = FromMatrix(_node.Transform) * _transform; Matrix transformInvTr = Helpers.CreateInverseTranspose(ref _transform); // Recursive load from scene if (_node.HasChildren) { foreach (Assimp.Node node in _node.Children) { LoadFromNode(_scene, node, _device, _content, _transform); } } if (_node.HasMeshes) { foreach (int meshIndex in _node.MeshIndices) { Assimp.Mesh mesh = _scene.Meshes[meshIndex]; ModelMesh modelMesh = new ModelMesh(); // if mesh has a diffuse texture extract it Assimp.Material material = _scene.Materials[mesh.MaterialIndex]; if (material != null && material.GetTextureCount(TextureType.Diffuse) > 0) { TextureSlot texture = material.GetTexture(TextureType.Diffuse, 0); // Create new texture for mesh var dxtexture = _content.Load <Texture2D>(texture.FilePath); modelMesh.DiffuseTexture = dxtexture; } // Position is mandarory if (!mesh.HasVertices) { throw new Exception("Model::Model(): Model has no vertices."); } // Determine the elements in the vertex bool hasTexCoords = mesh.HasTextureCoords(0); bool hasColors = mesh.HasVertexColors(0); bool hasNormals = mesh.HasNormals; bool hasTangents = mesh.Tangents != null; bool hasBitangents = mesh.BiTangents != null; int numElements = 1 + (hasTexCoords ? 1 : 0) + (hasColors ? 1 : 0) + (hasNormals ? 1 : 0) + (hasTangents ? 1 : 0) + (hasBitangents ? 1 : 0); // Create vertex element list: Here starts the section of creating SharpDX stuff VertexElement[] vertexElements = new VertexElement[numElements]; uint elementIndex = 0; vertexElements[elementIndex++] = new VertexElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0); int vertexSize = Utilities.SizeOf <Vector3>(); if (hasColors) { vertexElements[elementIndex++] = new VertexElement("COLOR", 0, SharpDX.DXGI.Format.R8G8B8A8_UInt, vertexSize); vertexSize += Utilities.SizeOf <Color>(); } if (hasNormals) { vertexElements[elementIndex++] = new VertexElement("NORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, vertexSize); vertexSize += Utilities.SizeOf <Vector3>(); } if (hasTangents) { vertexElements[elementIndex++] = new VertexElement("TANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, vertexSize); vertexSize += Utilities.SizeOf <Vector3>(); } if (hasBitangents) { vertexElements[elementIndex++] = new VertexElement("BITANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, vertexSize); vertexSize += Utilities.SizeOf <Vector3>(); } if (hasTexCoords) { vertexElements[elementIndex++] = new VertexElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, vertexSize); vertexSize += Utilities.SizeOf <Vector2>(); } // Set the vertex elements and size modelMesh.InputLayout = VertexInputLayout.New(VertexBufferLayout.New(0, vertexElements)); modelMesh.VertexSize = vertexSize; // Determine primitive type switch (mesh.PrimitiveType) { case Assimp.PrimitiveType.Point: modelMesh.PrimitiveTopology = SharpDX.Toolkit.Graphics.PrimitiveType.PointList; break; case Assimp.PrimitiveType.Line: modelMesh.PrimitiveTopology = SharpDX.Toolkit.Graphics.PrimitiveType.LineList; break; case Assimp.PrimitiveType.Triangle: modelMesh.PrimitiveTopology = SharpDX.Toolkit.Graphics.PrimitiveType.TriangleList; break; default: throw new Exception("Model::Model(): Unknown primitive type"); } // Create data stream for vertices //System.IO.MemoryStream vertexStream = new System.IO.MemoryStream(mesh.VertexCount * vertexSize); DataStream vertexStream = new DataStream(mesh.VertexCount * vertexSize, true, true); for (int i = 0; i < mesh.VertexCount; i++) { vertexStream.Write <Vector3>(Helpers.Transform(FromVector(mesh.Vertices[i]), ref _transform)); if (hasColors) { vertexStream.Write <Color>(FromColor(mesh.GetVertexColors(0)[i])); } if (hasNormals) { vertexStream.Write <Vector3>(Helpers.Transform(FromVector(mesh.Normals[i]), ref transformInvTr)); } if (hasTangents) { vertexStream.Write <Vector3>(Helpers.Transform(FromVector(mesh.Tangents[i]), ref transformInvTr)); } if (hasBitangents) { vertexStream.Write <Vector3>(Helpers.Transform(FromVector(mesh.BiTangents[i]), ref transformInvTr)); } if (hasTexCoords) { vertexStream.Write <Vector2>(new Vector2(mesh.GetTextureCoords(0)[i].X, mesh.GetTextureCoords(0)[i].Y)); } } vertexStream.Position = 0; // Create new vertex buffer var vertexBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New(_device, vertexStream); // Add it to the mesh modelMesh.VertexBuffer = vertexBuffer; modelMesh.VertexCount = mesh.VertexCount; modelMesh.PrimitiveCount = mesh.FaceCount; // Create new index buffer var indexBuffer = SharpDX.Toolkit.Graphics.Buffer.Index.New(_device, mesh.GetIndices()); // Add it to the mesh modelMesh.IndexBuffer = indexBuffer; modelMesh.IndexCount = mesh.GetIndices().GetLength(0); m_meshes.Add(modelMesh); } } }
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); }
void WriteVertices(Mesh assimpMesh, ModelData.MeshPart meshPart, ModelData.VertexBuffer vertexBuffer, int vertexBufferElementSize) { // Write all vertices meshPart.VertexBufferRange.Count = assimpMesh.VertexCount; vertexBuffer.Count = assimpMesh.VertexCount; vertexBuffer.Buffer = new byte[vertexBufferElementSize * assimpMesh.VertexCount]; // Update the MaximumBufferSizeInBytes needed to load this model if (vertexBuffer.Buffer.Length > model.MaximumBufferSizeInBytes) { model.MaximumBufferSizeInBytes = vertexBuffer.Buffer.Length; } var vertexStream = DataStream.Create(vertexBuffer.Buffer, true, true); for (int i = 0; i < assimpMesh.VertexCount; i++) { var position = assimpMesh.Vertices[i]; vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation boundingPoints[currentBoundingPointIndex++] = new Vector3(position.X, position.Y, position.Z); // Add normals if (assimpMesh.HasNormals) { vertexStream.Write(assimpMesh.Normals[i]); } // Add colors if (assimpMesh.VertexColorChannelCount > 0) { for (int j = 0; j < assimpMesh.VertexColorChannelCount; j++) { if (assimpMesh.HasVertexColors(j)) { vertexStream.Write(assimpMesh.GetVertexColors(j)[i]); } } } // Add textures if (assimpMesh.TextureCoordsChannelCount > 0) { for (int j = 0; j < assimpMesh.TextureCoordsChannelCount; j++) { if (assimpMesh.HasTextureCoords(j)) { var uvCount = assimpMesh.GetUVComponentCount(j); var uv = assimpMesh.GetTextureCoords(j)[i]; if (uvCount == 2) { vertexStream.Write(new Vector2(uv.X, uv.Y)); } else { vertexStream.Write(uv); } } } } // Add tangent / bitangent if (assimpMesh.HasTangentBasis) { if (!options.ExcludeElements.Contains("Tangent")) { double w = Vector3D.Dot(assimpMesh.Normals[i], Vector3D.Cross(assimpMesh.Tangents[i], assimpMesh.Tangents[i])); Vector3D t = assimpMesh.Tangents[i]; Vector4 t4D = new Vector4(t.X, t.Y, t.Z, (float)w); vertexStream.Write(t4D); } if (!options.ExcludeElements.Contains("BiTangent")) { vertexStream.Write(assimpMesh.BiTangents[i]); } } } vertexStream.Dispose(); }
private void ApplyFixedFunctionMaterial(Mesh mesh, Material mat, bool textured, bool shaded) { shaded = shaded && (mesh == null || mesh.HasNormals); if (shaded) { GL.Enable(EnableCap.Lighting); } else { GL.Disable(EnableCap.Lighting); } var hasColors = mesh != null && mesh.HasVertexColors(0); if (hasColors) { GL.Enable(EnableCap.ColorMaterial); GL.ColorMaterial(MaterialFace.FrontAndBack, ColorMaterialParameter.AmbientAndDiffuse); } else { GL.Disable(EnableCap.ColorMaterial); } // note: keep semantics of hasAlpha consistent with IsAlphaMaterial() var hasAlpha = false; var hasTexture = false; // note: keep this up-to-date with the code in UploadTextures() if (textured && mat.GetMaterialTextureCount(TextureType.Diffuse) > 0) { hasTexture = true; TextureSlot tex; mat.GetMaterialTexture(TextureType.Diffuse, 0, out tex); var gtex = _scene.TextureSet.GetOriginalOrReplacement(tex.FilePath); hasAlpha = hasAlpha || gtex.HasAlpha == Texture.AlphaState.HasAlpha; if(gtex.State == Texture.TextureState.GlTextureCreated) { GL.ActiveTexture(TextureUnit.Texture0); gtex.BindGlTexture(); GL.Enable(EnableCap.Texture2D); } else { GL.Disable(EnableCap.Texture2D); } } else { GL.Disable(EnableCap.Texture2D); } GL.Enable(EnableCap.Normalize); var alpha = 1.0f; if (mat.HasOpacity) { alpha = mat.Opacity; if (alpha < AlphaSuppressionThreshold) // suppress zero opacity, this is likely wrong input data { alpha = 1.0f; } } var color = new Color4(.8f, .8f, .8f, 1.0f); if (mat.HasColorDiffuse) { color = AssimpToOpenTk.FromColor(mat.ColorDiffuse); if (color.A < AlphaSuppressionThreshold) // s.a. { color.A = 1.0f; } } color.A *= alpha; hasAlpha = hasAlpha || color.A < 1.0f; if (shaded) { // if the material has a texture but the diffuse color texture is all black, // then heuristically assume that this is an import/export flaw and substitute // white. if (hasTexture && color.R < 1e-3f && color.G < 1e-3f && color.B < 1e-3f) { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, Color4.White); } else { GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, color); } color = new Color4(0, 0, 0, 1.0f); if (mat.HasColorSpecular) { color = AssimpToOpenTk.FromColor(mat.ColorSpecular); } GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, color); color = new Color4(.2f, .2f, .2f, 1.0f); if (mat.HasColorAmbient) { color = AssimpToOpenTk.FromColor(mat.ColorAmbient); } GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, color); color = new Color4(0, 0, 0, 1.0f); if (mat.HasColorEmissive) { color = AssimpToOpenTk.FromColor(mat.ColorEmissive); } GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, color); float shininess = 1; float strength = 1; if (mat.HasShininess) { shininess = mat.Shininess; } // todo: I don't even remember how shininess strength was supposed to be handled in assimp if (mat.HasShininessStrength) { strength = mat.ShininessStrength; } var exp = shininess*strength; if (exp >= 128.0f) // 128 is the maximum exponent as per the Gl spec { exp = 128.0f; } GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, exp); } else if (!hasColors) { GL.Color3(color.R, color.G, color.B); } if (hasAlpha) { GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.DepthMask(false); } else { GL.Disable(EnableCap.Blend); GL.DepthMask(true); } }
void GetVertexFormat (Mesh mesh, int boneCount, out VertexFlags flags, out int stride) { stride = 3; flags = 0; if (mesh.HasNormals) { stride += 3; flags |= VertexFlags.Normal; } if (mesh.HasTextureCoords(0)) { stride += 2; flags |= VertexFlags.TexCoord0; } if (mesh.HasTextureCoords(1)) { stride += 2; flags |= VertexFlags.TexCoord0; } if (mesh.HasTextureCoords(2)) { stride += 2; flags |= VertexFlags.TexCoord0; } if (mesh.HasTextureCoords(3)) { stride += 2; flags |= VertexFlags.TexCoord0; } if (mesh.HasVertexColors(0)) { stride += 4; flags |= VertexFlags.Color0; } if (mesh.HasVertexColors(1)) { stride += 4; flags |= VertexFlags.Color1; } if (mesh.HasVertexColors(2)) { stride += 4; flags |= VertexFlags.Color2; } if (mesh.HasVertexColors(3)) { stride += 4; flags |= VertexFlags.Color3; } stride += boneCount * 2; }
unsafe void WriteMesh (Mesh mesh, BinaryWriter writer) { var boneSlotCount = this.GetBoneSlotCount(mesh); VertexFlags flags; int stride; this.GetVertexFormat(mesh, boneSlotCount, out flags, out stride); var count = mesh.VertexCount; writer.Write(mesh.Name); writer.Write((uint)flags); writer.Write(boneSlotCount); writer.Write(mesh.MaterialIndex); var vertices = new float[stride * count]; int idx = 0, offset = 0; foreach (var pos in mesh.Vertices) { vertices[idx] = pos.X; vertices[idx + 1] = pos.Y; vertices[idx + 2] = pos.Z; idx += stride; } offset = idx = 3; if (mesh.HasNormals) { foreach (var norm in mesh.Normals) { vertices[idx] = norm.X; vertices[idx + 1] = norm.Y; vertices[idx + 2] = norm.Z; idx += stride; } offset += 3; idx = offset; } for (var i = 0; i < 4; i++) { if (mesh.HasTextureCoords(i)) { if (mesh.UVComponentCount[i] != 2) Console.WriteLine("WARNING: texture coordinates should have 2 components, but this channel has " + mesh.UVComponentCount[i]); foreach (var uv in mesh.TextureCoordinateChannels[i]) { vertices[idx] = uv.X; vertices[idx + 1] = uv.Y; idx += stride; } offset += 2; idx = offset; } } for (var i = 0; i < 4; i++) { if (mesh.HasVertexColors(i)) { foreach (var c in mesh.VertexColorChannels[i]) { vertices[idx] = c.R; vertices[idx + 1] = c.G; vertices[idx + 2] = c.B; vertices[idx + 3] = c.A; idx += stride; } offset += 4; idx = offset; } } // add bone information to every vertex for(var i = 0; i < mesh.BoneCount; i++) { foreach (var weight in mesh.Bones[i].VertexWeights) { if (weight.Weight > 0f) { idx = (stride * weight.VertexID) + offset; while (vertices[idx] != 0f) idx++; vertices[idx] = weight.Weight; vertices[idx + boneSlotCount] = i; } } } writer.Write(vertices.Length); writer.Flush(); fixed(float *fp = &vertices[0]) { byte* bp = (byte*)fp; using (var ms = new UnmanagedMemoryStream(bp, vertices.Length * sizeof(float))) { ms.CopyTo(writer.BaseStream); } } count = mesh.FaceCount; var indices = new int[count * 3]; idx = 0; for (var i = 0; i < count; i++) { var face = mesh.Faces[i]; var faceIndices = face.Indices; if (face.IndexCount != 3) throw new InvalidOperationException("Polygonal faces are not supported, faces must be triangles."); indices[idx++] = faceIndices[0]; indices[idx++] = faceIndices[1]; indices[idx++] = faceIndices[2]; } writer.Write(indices.Length); writer.Flush(); fixed(int *ip = &indices[0]) { byte* bp = (byte*)ip; using (var ms = new UnmanagedMemoryStream(bp, indices.Length * sizeof(float))) { ms.CopyTo(writer.BaseStream); } } count = mesh.BoneCount; writer.Write(count); for (var i = 0; i < count; i++) { var bone = mesh.Bones[i]; writer.Write(bone.Name); writer.Write(bone.OffsetMatrix); var weightCount = bone.VertexWeightCount; writer.Write(weightCount); for (var j = 0; j < weightCount; j++) { var weight = bone.VertexWeights[i]; writer.Write(weight.VertexID); writer.Write(weight.Weight); } } }
private static Mesh ConvertAssimpMeshToGeometry(Ai.Mesh aiMesh, Ai.Material material, Dictionary <string, NodeInfo> nodeLookup, ref int nextBoneIndex, Dictionary <int, List <int> > nodeToBoneIndices, List <Matrix4x4> boneInverseBindMatrices, ref Matrix4x4 nodeWorldTransform, ref Matrix4x4 nodeInverseWorldTransform, List <Vector3> transformedVertices, ModelConverterOptions options) { if (!aiMesh.HasVertices) { throw new Exception("Assimp mesh has no vertices"); } var geometry = new Mesh(); var geometryTransformedVertices = new Vector3[aiMesh.VertexCount]; geometry.Vertices = aiMesh.Vertices .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); for (int i = 0; i < geometry.Vertices.Length; i++) { geometryTransformedVertices[i] = Vector3.Transform(geometry.Vertices[i], nodeWorldTransform); } transformedVertices.AddRange(geometryTransformedVertices); if (aiMesh.HasNormals) { geometry.Normals = aiMesh.Normals .Select(x => new Vector3(x.X, x.Y, x.Z)) .ToArray(); } if (aiMesh.HasTextureCoords(0)) { geometry.TexCoordsChannel0 = aiMesh.TextureCoordinateChannels[0] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(1)) { geometry.TexCoordsChannel1 = aiMesh.TextureCoordinateChannels[1] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasTextureCoords(2)) { geometry.TexCoordsChannel2 = aiMesh.TextureCoordinateChannels[2] .Select(x => new Vector2(x.X, x.Y)) .ToArray(); } if (aiMesh.HasVertexColors(0)) { geometry.ColorChannel0 = aiMesh.VertexColorChannels[0] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } else if (options.GenerateVertexColors) { geometry.ColorChannel0 = new uint[geometry.VertexCount]; for (int i = 0; i < geometry.ColorChannel0.Length; i++) { geometry.ColorChannel0[i] = 0xFFFFFFFF; } } if (aiMesh.HasVertexColors(1)) { geometry.ColorChannel1 = aiMesh.VertexColorChannels[1] .Select(x => ( uint )(( byte )(x.B * 255f) | ( byte )(x.G * 255f) << 8 | ( byte )(x.R * 255f) << 16 | ( byte )(x.A * 255f) << 24)) .ToArray(); } if (aiMesh.HasFaces) { geometry.TriangleIndexFormat = aiMesh.VertexCount <= ushort.MaxValue ? TriangleIndexFormat.UInt16 : TriangleIndexFormat.UInt32; geometry.Triangles = aiMesh.Faces .Select(x => new Triangle(( uint )x.Indices[0], ( uint )x.Indices[1], ( uint )x.Indices[2])) .ToArray(); } if (aiMesh.HasBones) { geometry.VertexWeights = new VertexWeight[geometry.VertexCount]; for (int i = 0; i < geometry.VertexWeights.Length; i++) { geometry.VertexWeights[i].Indices = new byte[4]; geometry.VertexWeights[i].Weights = new float[4]; } var vertexWeightCounts = new int[geometry.VertexCount]; for (var i = 0; i < aiMesh.Bones.Count; i++) { var aiMeshBone = aiMesh.Bones[i]; // Find node index for the bone var boneLookupData = nodeLookup[AssimpConverterCommon.UnescapeName(aiMeshBone.Name)]; int nodeIndex = boneLookupData.Index; // Calculate inverse bind matrix var boneNode = boneLookupData.Node; var bindMatrix = boneNode.WorldTransform * nodeInverseWorldTransform; if (options.ConvertSkinToZUp) { bindMatrix *= YToZUpMatrix; } Matrix4x4.Invert(bindMatrix, out var inverseBindMatrix); // Get bone index int boneIndex; if (!nodeToBoneIndices.TryGetValue(nodeIndex, out var boneIndices)) { // No entry for the node was found, so we add a new one boneIndex = nextBoneIndex++; nodeToBoneIndices.Add(nodeIndex, new List <int>() { boneIndex }); boneInverseBindMatrices.Add(inverseBindMatrix); } else { // Entry for the node was found // Try to find the bone index based on whether the inverse bind matrix matches boneIndex = -1; foreach (int index in boneIndices) { if (boneInverseBindMatrices[index].Equals(inverseBindMatrix)) { boneIndex = index; } } if (boneIndex == -1) { // None matching inverse bind matrix was found, so we add a new entry boneIndex = nextBoneIndex++; nodeToBoneIndices[nodeIndex].Add(boneIndex); boneInverseBindMatrices.Add(inverseBindMatrix); } } foreach (var aiVertexWeight in aiMeshBone.VertexWeights) { int vertexWeightCount = vertexWeightCounts[aiVertexWeight.VertexID]++; geometry.VertexWeights[aiVertexWeight.VertexID].Indices[vertexWeightCount] = ( byte )boneIndex; geometry.VertexWeights[aiVertexWeight.VertexID].Weights[vertexWeightCount] = aiVertexWeight.Weight; } } } geometry.MaterialName = AssimpConverterCommon.UnescapeName(material.Name); geometry.BoundingBox = BoundingBox.Calculate(geometry.Vertices); geometry.BoundingSphere = BoundingSphere.Calculate(geometry.BoundingBox.Value, geometry.Vertices); geometry.Flags |= GeometryFlags.Flag80000000; return(geometry); }
// Load a model from file using the ASSIMP model loader and generate all resources required to render the model void loadModel(string filename) { // Load the model from file using ASSIMP // Flags for loading the mesh PostProcessSteps assimpFlags = PostProcessSteps.FlipWindingOrder | PostProcessSteps.Triangulate | PostProcessSteps.PreTransformVertices; var scene = new AssimpContext().ImportFile(filename, assimpFlags); // Generate vertex buffer from ASSIMP scene data float scale = 1.0f; var vertexBuffer = new List <Vertex>(); // Iterate through all meshes in the file and extract the vertex components for (int m = 0; m < scene.MeshCount; m++) { for (int v = 0; v < scene.Meshes[(int)m].VertexCount; v++) { Vertex vertex; Assimp.Mesh mesh = scene.Meshes[m]; // Use glm make_* functions to convert ASSIMP vectors to glm vectors vertex.pos = new Vector3(mesh.Vertices[v].X, mesh.Vertices[v].Y, mesh.Vertices[v].Z) * scale; vertex.normal = new Vector3(mesh.Normals[v].X, mesh.Normals[v].Y, mesh.Normals[v].Z); // Texture coordinates and colors may have multiple channels, we only use the first [0] one vertex.uv = new Vector2(mesh.TextureCoordinateChannels[0][v].X, mesh.TextureCoordinateChannels[0][v].Y); // Mesh may not have vertex colors if (mesh.HasVertexColors(0)) { vertex.color = new Vector3(mesh.VertexColorChannels[0][v].R, mesh.VertexColorChannels[0][v].G, mesh.VertexColorChannels[0][v].B); } else { vertex.color = new Vector3(1f); } // Vulkan uses a right-handed NDC (contrary to OpenGL), so simply flip Y-Axis vertex.pos.Y *= -1.0f; vertexBuffer.Add(vertex); } } ulong vertexBufferSize = (ulong)(vertexBuffer.Count * sizeof(Vertex)); // Generate index buffer from ASSIMP scene data var indexBuffer = new List <uint>(); for (int m = 0; m < scene.MeshCount; m++) { uint indexBase = (uint)indexBuffer.Count; for (int f = 0; f < scene.Meshes[m].FaceCount; f++) { // We assume that all faces are triangulated for (int i = 0; i < 3; i++) { indexBuffer.Add((uint)scene.Meshes[m].Faces[f].Indices[i] + indexBase); } } } ulong indexBufferSize = (ulong)(indexBuffer.Count * sizeof(uint)); model_indices_count = (int)indexBuffer.Count; // Static mesh should always be Device local bool useStaging = true; if (useStaging) { VkBuffer vertexStaging_buffer; VkDeviceMemory vertexStaging_memory; VkBuffer indexStaging_buffer; VkDeviceMemory indexStaging_memory; // Create staging buffers // Vertex data fixed(Vertex *pointer = vertexBuffer.ToArray()) { vulkanDevice.createBuffer( VkBufferUsageFlagBits.TransferSrc, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.HostCoherent, vertexBufferSize, &vertexStaging_buffer, &vertexStaging_memory, pointer); } // Index data fixed(uint *pointer = indexBuffer.ToArray()) { vulkanDevice.createBuffer( VkBufferUsageFlagBits.TransferSrc, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.HostCoherent, indexBufferSize, &indexStaging_buffer, &indexStaging_memory, pointer); } // Create Device local buffers // Vertex buffer vulkanDevice.createBuffer( VkBufferUsageFlagBits.VertexBuffer | VkBufferUsageFlagBits.TransferDst, VkMemoryPropertyFlagBits.DeviceLocal, vertexBufferSize, out model_vertices_buffer, out model_vertices_memory); // Index buffer vulkanDevice.createBuffer( VkBufferUsageFlagBits.IndexBuffer | VkBufferUsageFlagBits.TransferDst, VkMemoryPropertyFlagBits.DeviceLocal, indexBufferSize, out model_indices_buffer, out model_indices_memory); // Copy from staging buffers VkCommandBuffer copyCmd = createCommandBuffer(VkCommandBufferLevel.Primary, true); VkBufferCopy copyRegion = new VkBufferCopy(); copyRegion.size = vertexBufferSize; vkCmdCopyBuffer( copyCmd, vertexStaging_buffer, model_vertices_buffer, 1, ©Region); copyRegion.size = indexBufferSize; vkCmdCopyBuffer( copyCmd, indexStaging_buffer, model_indices_buffer, 1, ©Region); flushCommandBuffer(copyCmd, queue, true); vkDestroyBuffer(device, vertexStaging_buffer, null); vkFreeMemory(device, vertexStaging_memory, null); vkDestroyBuffer(device, indexStaging_buffer, null); vkFreeMemory(device, indexStaging_memory, null); } else { // Vertex buffer fixed(Vertex *pointer = vertexBuffer.ToArray()) { vulkanDevice.createBuffer( VkBufferUsageFlagBits.VertexBuffer, VkMemoryPropertyFlagBits.HostVisible, vertexBufferSize, out model_vertices_buffer, out model_vertices_memory, pointer); } // Index buffer fixed(uint *pointer = indexBuffer.ToArray()) { vulkanDevice.createBuffer( VkBufferUsageFlagBits.IndexBuffer, VkMemoryPropertyFlagBits.HostVisible, indexBufferSize, out model_indices_buffer, out model_indices_memory, pointer); } } }
//private const PostProcessSteps DefaultPostProcessSteps // = PostProcessSteps.None; //TODO: add support for VertexPosition Type public static byte[] GenerateVertexBytesArrayFromAssimp(VertexRuntimeTypes henzaiVertexType, Assimp.Mesh aiMesh, int index) { Vector3D pPos = aiMesh.Vertices[index]; Vector3D pNormal = aiMesh.Normals[index]; Vector3D pTexCoord = aiMesh.HasTextureCoords(0) ? aiMesh.TextureCoordinateChannels[0][index] : Zero3D; Color4D pColor = aiMesh.HasVertexColors(0) ? aiMesh.VertexColorChannels[0][index] : Nocolor; Vector3D pTangent = aiMesh.HasTangentBasis ? aiMesh.Tangents[index] : Zero3D; Vector3D pBiTangent = aiMesh.HasTangentBasis ? aiMesh.BiTangents[index] : Zero3D; byte[] bytes; byte[] posAsBytes = ByteMarshal.StructToBytes(pPos); byte[] colorAsBytes = ByteMarshal.StructToBytes(pColor); byte[] normalAsBytes = ByteMarshal.StructToBytes(pNormal); byte[] texCoordAsBytes = ByteMarshal.StructToBytes(pTexCoord.ToVector2()); byte[] tangentAsBytes = ByteMarshal.StructToBytes(pTangent); byte[] bitangentAsBytes = ByteMarshal.StructToBytes(pBiTangent); switch (henzaiVertexType) { case VertexRuntimeTypes.VertexPosition: bytes = new byte[VertexPosition.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPosition.PositionOffset, posAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionColor: bytes = new byte[VertexPositionColor.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionColor.PositionOffset, posAsBytes.Length); Array.Copy(colorAsBytes, 0, bytes, VertexPositionColor.ColorOffset, colorAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionTexture: bytes = new byte[VertexPositionTexture.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionTexture.PositionOffset, posAsBytes.Length); Array.Copy(texCoordAsBytes, 0, bytes, VertexPositionTexture.TextureCoordinatesOffset, texCoordAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionNormalTexture: bytes = new byte[VertexPositionNormalTexture.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionNormalTexture.PositionOffset, posAsBytes.Length); Array.Copy(normalAsBytes, 0, bytes, VertexPositionNormalTexture.NormalOffset, normalAsBytes.Length); Array.Copy(texCoordAsBytes, 0, bytes, VertexPositionNormalTexture.TextureCoordinatesOffset, texCoordAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionNormal: bytes = new byte[VertexPositionNormal.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionNormal.PositionOffset, posAsBytes.Length); Array.Copy(normalAsBytes, 0, bytes, VertexPositionNormal.NormalOffset, normalAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionNormalTextureTangent: bytes = new byte[VertexPositionNormalTextureTangent.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionNormalTextureTangent.PositionOffset, posAsBytes.Length); Array.Copy(normalAsBytes, 0, bytes, VertexPositionNormalTextureTangent.NormalOffset, normalAsBytes.Length); Array.Copy(texCoordAsBytes, 0, bytes, VertexPositionNormalTextureTangent.TextureCoordinatesOffset, texCoordAsBytes.Length); Array.Copy(tangentAsBytes, 0, bytes, VertexPositionNormalTextureTangent.TangentOffset, tangentAsBytes.Length); break; case VertexRuntimeTypes.VertexPositionNormalTextureTangentBitangent: bytes = new byte[VertexPositionNormalTextureTangentBitangent.SizeInBytes]; Array.Copy(posAsBytes, 0, bytes, VertexPositionNormalTextureTangentBitangent.PositionOffset, posAsBytes.Length); Array.Copy(normalAsBytes, 0, bytes, VertexPositionNormalTextureTangentBitangent.NormalOffset, normalAsBytes.Length); Array.Copy(texCoordAsBytes, 0, bytes, VertexPositionNormalTextureTangentBitangent.TextureCoordinatesOffset, texCoordAsBytes.Length); Array.Copy(tangentAsBytes, 0, bytes, VertexPositionNormalTextureTangentBitangent.TangentOffset, tangentAsBytes.Length); Array.Copy(bitangentAsBytes, 0, bytes, VertexPositionNormalTextureTangentBitangent.BitangentOffset, bitangentAsBytes.Length); break; default: throw new NotImplementedException($"{henzaiVertexType.ToString("g")} not implemented"); } return(bytes); }
private void AddVertexData(CModel model, Assimp.Scene scene, Node node, Device device, ref Matrix transform, string modelPath, CShaderResource shaderResource) { // TODO henning With this we don't preserve hiearchy in the models meshes, for now this is the wanted behavior but maybe we want to think about this in the future Matrix previousTransform = transform; transform = Matrix.Multiply(previousTransform, FromAssimpMatrix(node.Transform)); if (node.HasMeshes) { foreach (int index in node.MeshIndices) { // Get the mesh from the scene Assimp.Mesh assimpMesh = scene.Meshes[index]; // Create a mesh in our engine format CMesh mesh = new CMesh(); mesh.Transform.SetFromMatrix(in transform); model.AddMesh(ref mesh); //Extract diffuse texture from Assimp Material // TODO henning extract other textures if we want Material material = scene.Materials[assimpMesh.MaterialIndex]; if (material != null && material.GetMaterialTextureCount(TextureType.Diffuse) > 0) { TextureSlot texture; if (material.GetMaterialTexture(TextureType.Diffuse, 0, out texture)) { // Create texture asset mesh.Material = CMaterial.CreateDefaultMaterial(); CTextureSampler textureSampler = new CTextureSampler(device, device.ImmediateContext, modelPath + "\\" + texture.FilePath); mesh.Material.SetTextureParameter(new SHashedName("DiffuseTexture"), textureSampler); mesh.Material.FinishLoading(); } else { mesh.Material = CRenderer.Instance.ResourceManager.DefaultTextureMaterial; } } else { mesh.Material = CRenderer.Instance.ResourceManager.DefaultTextureMaterial; } mesh.Material.SetColorParameter(new SHashedName("tintColor"), new Vector4(1, 1, 1, 1)); int numInputElements = 6; // We always provide all data to the vertex buffer so shaders can rely on them being available if it is not in the asset we will add a default value bool hasTexCoords = assimpMesh.HasTextureCoords(0); bool hasColors = assimpMesh.HasVertexColors(0); bool hasNormals = assimpMesh.HasNormals; bool hasTangents = assimpMesh.Tangents != null && assimpMesh.Tangents.Count > 0; bool hasBiTangents = assimpMesh.BiTangents != null && assimpMesh.BiTangents.Count > 0; // Create InputElement list InputElement[] vertexElements = new InputElement[numInputElements]; uint elementIndex = 0; vertexElements[elementIndex] = new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0); elementIndex++; int vertexSize = Utilities.SizeOf <Vector3>(); // TODO henning evaluate if we need 32bit vertex color range vertexElements[elementIndex] = new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 0); elementIndex++; vertexSize += Utilities.SizeOf <Vector4>(); vertexElements[elementIndex] = new InputElement("NORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0); elementIndex++; vertexSize += Utilities.SizeOf <Vector3>(); vertexElements[elementIndex] = new InputElement("TANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0); elementIndex++; vertexSize += Utilities.SizeOf <Vector3>(); vertexElements[elementIndex] = new InputElement("BITANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0); elementIndex++; vertexSize += Utilities.SizeOf <Vector3>(); vertexElements[elementIndex] = new InputElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, 0); elementIndex++; vertexSize += Utilities.SizeOf <Vector2>(); // Set InputElements and Vertex Size on mesh mesh.m_sizePerVertex = vertexSize; List <Vector3D> positions = assimpMesh.Vertices; List <Vector3D> texCoords = assimpMesh.TextureCoordinateChannels[0]; // TODO henning support multiple UV channels if wanted List <Vector3D> normals = assimpMesh.Normals; List <Vector3D> tangents = assimpMesh.Tangents; List <Vector3D> biTangents = assimpMesh.BiTangents; List <Color4D> colors = assimpMesh.VertexColorChannels[0]; switch (assimpMesh.PrimitiveType) { case PrimitiveType.Point: mesh.m_primitiveTopology = SharpDX.Direct3D.PrimitiveTopology.PointList; break; case PrimitiveType.Line: mesh.m_primitiveTopology = SharpDX.Direct3D.PrimitiveTopology.LineList; break; case PrimitiveType.Triangle: mesh.m_primitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList; break; default: throw new NotImplementedException("Primitive Type not supported: " + assimpMesh.PrimitiveType.ToString()); } DataStream vertexStream = new DataStream(assimpMesh.VertexCount * vertexSize, true, true); for (int i = 0; i < assimpMesh.VertexCount; i++) { //add position, after transforming it with accumulated node transform { Vector3 pos = FromAssimpVector(positions[i]); vertexStream.Write(pos); } if (hasColors) { Vector4 vertColor = FromAssimpColor(colors[i]); vertexStream.Write(vertColor); } else { vertexStream.Write(new Vector4(1, 1, 1, 1)); } if (hasNormals) { Vector3 normal = FromAssimpVector(normals[i]); vertexStream.Write(normal); } else { vertexStream.Write(new Vector3(0, 0, 0)); } if (hasTangents) { Vector3 tangent = FromAssimpVector(tangents[i]); vertexStream.Write(tangent); } else { vertexStream.Write(new Vector3(0, 0, 0)); } if (hasBiTangents) { Vector3 biTangent = FromAssimpVector(biTangents[i]); vertexStream.Write(biTangent); } else { vertexStream.Write(new Vector3(0, 0, 0)); } if (hasTexCoords) { vertexStream.Write(new Vector2(texCoords[i].X, texCoords[i].Y)); } else { vertexStream.Write(new Vector2(0, 0)); } } vertexStream.Position = 0; BufferDescription vertexDescription = new BufferDescription { BindFlags = BindFlags.VertexBuffer, Usage = ResourceUsage.Default, CpuAccessFlags = CpuAccessFlags.None, SizeInBytes = assimpMesh.VertexCount * vertexSize, OptionFlags = ResourceOptionFlags.None }; mesh.m_vertexBuffer = new Buffer(device, vertexStream, vertexDescription); vertexStream.Dispose(); mesh.m_vertexCount = assimpMesh.VertexCount; mesh.m_primitiveCount = assimpMesh.FaceCount; int[] indices = assimpMesh.GetIndices(); mesh.m_indexBuffer = Buffer.Create(device, BindFlags.IndexBuffer, indices); mesh.m_indexCount = indices.Length; } } for (int i = 0; i < node.ChildCount; i++) { AddVertexData(model, scene, node.Children[i], device, ref transform, modelPath, shaderResource); } transform = previousTransform; }
private void CreateBuffers(Node assimpnode, ModelNode parent_node, ref Matrix4x4 model_matrix) { // create new mesh ModelNode node = new ModelNode(_openGLFactory); if (parent_node != null) { parent_node.AddChild(node); } else { _model._root_node = node; } // set model matrix Matrix4x4 mm = assimpnode.Transform; node.ModelMatrix = new Matrix4( mm.A1, mm.A2, mm.A3, mm.A4, mm.B1, mm.B2, mm.B3, mm.B4, mm.C1, mm.C2, mm.C3, mm.C4, mm.D1, mm.D2, mm.D3, mm.D4); // combined model matrix Matrix4x4 prev_model = model_matrix; Matrix4x4 new_transform = assimpnode.Transform; model_matrix = new_transform * model_matrix; // ? has this to be reversed link in OpneTK? if (assimpnode.HasMeshes) { foreach (int index in assimpnode.MeshIndices) { Assimp.Mesh assimpmesh = _assimpmodel.Meshes[index]; AABB mesh_box = new AABB(); // extend bounding box by vertices for (int i = 0; i < assimpmesh.VertexCount; i++) { Vector3D tmp = assimpmesh.Vertices[i]; mesh_box = mesh_box | new Vector3(tmp.X, tmp.Y, tmp.Z); tmp = model_matrix * tmp; _model._scene_box = _model._scene_box | new Vector3(tmp.X, tmp.Y, tmp.Z); } // create Mesh uint tuple_index = 0; List <TVertexFormat> formalist = new List <TVertexFormat>(); OpenTK_library.Scene.Mesh mesh = new OpenTK_library.Scene.Mesh(); mesh.Box = mesh_box; node.Add(mesh); // specify vertices mesh.VertexAttribute = (tuple_index, 3); formalist.Add(new TVertexFormat(0, vertex_index, 3, (int)tuple_index, false)); tuple_index += 3; // specify normals if (assimpmesh.HasNormals) { mesh.NormalAttribute = (tuple_index, 3); formalist.Add(new TVertexFormat(0, normal_index, 3, (int)tuple_index, false)); tuple_index += 3; } // specify bi-normals and tangents if (assimpmesh.HasTangentBasis) { mesh.BinormalAttribute = (tuple_index, 3); formalist.Add(new TVertexFormat(0, binormal_index, 3, (int)tuple_index, false)); tuple_index += 3; mesh.TangentAttribute = (tuple_index, 3); formalist.Add(new TVertexFormat(0, tangent_index, 3, (int)tuple_index, false)); tuple_index += 3; } // specify texture channels for (int textur_channel = 0; assimpmesh.HasTextureCoords(textur_channel); ++textur_channel) { mesh.AddTextureAttrib((tuple_index, 3)); int attr_i = textur_channel == 0 ? texture0_index : (textureN_index + textur_channel - 1); formalist.Add(new TVertexFormat(0, attr_i, 3, (int)tuple_index, false)); tuple_index += 3; } // specify color channels for (int color_channel = 0; assimpmesh.HasVertexColors(color_channel); ++color_channel) { mesh.AddColorAttrib((tuple_index, 4)); int attr_i = color_channel == 0 ? color0_index : (colorN_index + color_channel - 1); formalist.Add(new TVertexFormat(0, attr_i, 4, (int)tuple_index, false)); tuple_index += 4; } // TODO $$$ bones if (assimpmesh.HasBones) { // [...] Console.WriteLine("bones not yet implemented"); } // set tuple size mesh.TupleSize = tuple_index; // setup index buffer List <float> attributes = new List <float>(); List <uint> indices = new List <uint>(); uint elem_index = 0; foreach (Face face in assimpmesh.Faces) { if (face.IndexCount < 3) { continue; // lines? } for (uint i = 2; i < (uint)face.IndexCount; i++) { indices.Add(elem_index); indices.Add(elem_index + 1); indices.Add(elem_index + i); } elem_index += (uint)face.IndexCount; for (int i = 0; i < face.IndexCount; i++) { int ei = face.Indices[i]; // add vertex attribute var vertex = assimpmesh.Vertices[ei]; attributes.Add(vertex.X); attributes.Add(vertex.Y); attributes.Add(vertex.Z); // add normals if (assimpmesh.HasNormals) { var normal = assimpmesh.Normals[ei]; attributes.Add(normal.X); attributes.Add(normal.Y); attributes.Add(normal.Z); } // add bi-normals and tangents if (assimpmesh.HasTangentBasis) { var binormal = assimpmesh.BiTangents[ei]; attributes.Add(binormal.X); attributes.Add(binormal.Y); attributes.Add(binormal.Z); var tangent = assimpmesh.Tangents[ei]; attributes.Add(tangent.X); attributes.Add(tangent.Y); attributes.Add(tangent.Z); } // add texture coordinates for (int textur_channel = 0; assimpmesh.HasTextureCoords(textur_channel); ++textur_channel) { var uvw = assimpmesh.TextureCoordinateChannels[textur_channel][ei]; attributes.Add(uvw.X); attributes.Add(uvw.Y); attributes.Add(uvw.Z); } // add color attributes for (int color_channel = 0; assimpmesh.HasVertexColors(color_channel); ++color_channel) { var vertColor = assimpmesh.VertexColorChannels[color_channel][ei]; attributes.Add(vertColor.R); attributes.Add(vertColor.G); attributes.Add(vertColor.B); attributes.Add(vertColor.A); } } } // setup vertex arrays and index array TVertexFormat[] format = formalist.ToArray(); var vao = _openGLFactory.NewVertexArrayObject(); vao.AppendVertexBuffer(0, (int)tuple_index, attributes.ToArray()); vao.Create(format, indices.ToArray()); mesh.FaceSize = 3; mesh.VertexArray = vao; } } for (int i = 0; i < assimpnode.ChildCount; i++) { CreateBuffers(assimpnode.Children[i], node, ref model_matrix); } model_matrix = prev_model; }
private void LoadMeshInternal(int meshIndex, CMeshAsset asset, CMeshLoadingJob loadingJob, string assetPath, string nameOverride = null, bool bAlwaysImport = false) { Assimp.Mesh assimpMesh = loadingJob.Scene.Meshes[meshIndex]; // Load texture and material from the file if present //todo henning extract more textures Material material = loadingJob.Scene.Materials[assimpMesh.MaterialIndex]; if (material != null && material.GetMaterialTextureCount(TextureType.Diffuse) > 0) { if (material.GetMaterialTexture(TextureType.Diffuse, 0, out TextureSlot texture)) { if (loadingJob.LoadedMaterials == null || !loadingJob.LoadedMaterials.TryGetValue(material.Name, out CMaterialAsset materialAsset)) { materialAsset = new CMaterialAsset(); loadingJob.LoadedMaterials?.Add(material.Name, materialAsset); // Make sure we only load each referenced texture once if (loadingJob.LoadedTextures == null || !loadingJob.LoadedTextures.TryGetValue(texture.FilePath, out CTextureAsset textureAsset)) { textureAsset = CImportManager.Instance.TextureImporter.ImportTextureAsync(loadingJob.BasePath + "\\" + texture.FilePath, assetPath + "Textures/"); loadingJob.LoadedTextures?.Add(texture.FilePath, textureAsset); } SShaderParameter textureParameter = new SShaderParameter() { parameterData = new CAssetReference <CTextureAsset>(textureAsset), parameterType = EShaderParameterType.Texture }; materialAsset.MaterialParameters.Add(new SMaterialParameterEntry(new SHashedName("DiffuseTexture"), textureParameter)); materialAsset.Name = material.Name; if (CAssetRegistry.Instance.RequestRegisterAsset(materialAsset, assetPath + "Materials/", out CMaterialAsset existingMaterial)) { existingMaterial.WaitUntilLoaded(); existingMaterial.CopyFrom(existingMaterial); } materialAsset.LoadFinished(); } asset.MaterialAsset = materialAsset; } } bool hasTexCoords = assimpMesh.HasTextureCoords(0); bool hasColors = assimpMesh.HasVertexColors(0); bool hasNormals = assimpMesh.HasNormals; bool hasTangents = assimpMesh.Tangents != null && assimpMesh.Tangents.Count > 0; bool hasBiTangents = assimpMesh.BiTangents != null && assimpMesh.BiTangents.Count > 0; switch (assimpMesh.PrimitiveType) { case PrimitiveType.Point: asset.PrimitiveTopology = PrimitiveTopology.PointList; break; case PrimitiveType.Line: asset.PrimitiveTopology = PrimitiveTopology.LineList; break; case PrimitiveType.Triangle: asset.PrimitiveTopology = PrimitiveTopology.TriangleList; break; default: throw new ArgumentOutOfRangeException("Primtive Type not supported: " + assimpMesh.PrimitiveType.ToString()); } asset.FaceCount = assimpMesh.FaceCount; asset.VertexData = new SVertexInfo[assimpMesh.VertexCount]; Vector3 boundingBoxMin = new Vector3(1e10f, 1e10f, 1e10f); Vector3 boundingBoxMax = new Vector3(-1e10f, -1e10f, -1e10f); for (int i = 0; i < assimpMesh.VertexCount; i++) { SVertexInfo vertexInfo = new SVertexInfo(); vertexInfo.position = FromAssimpVector(assimpMesh.Vertices[i]); boundingBoxMin.X = Math.Min(vertexInfo.position.X, boundingBoxMin.X); boundingBoxMin.Y = Math.Min(vertexInfo.position.Y, boundingBoxMin.Y); boundingBoxMin.Z = Math.Min(vertexInfo.position.Z, boundingBoxMin.Z); boundingBoxMax.X = Math.Max(vertexInfo.position.X, boundingBoxMax.X); boundingBoxMax.Y = Math.Max(vertexInfo.position.Y, boundingBoxMax.Y); boundingBoxMax.Z = Math.Max(vertexInfo.position.Z, boundingBoxMax.Z); if (hasColors) { vertexInfo.color = FromAssimpColor(assimpMesh.VertexColorChannels[0][i]); } else { vertexInfo.color = Vector4.One; } if (hasNormals) { vertexInfo.normal = FromAssimpVector(assimpMesh.Normals[i]); } if (hasBiTangents) { vertexInfo.biTangent = FromAssimpVector(assimpMesh.BiTangents[i]); } if (hasTangents) { vertexInfo.tangent = FromAssimpVector(assimpMesh.Tangents[i]); } if (hasTexCoords) { Vector3D assimpTexCoord = assimpMesh.TextureCoordinateChannels[0][i]; vertexInfo.texCoord = new Vector2(assimpTexCoord.X, assimpTexCoord.Y); } asset.VertexData[i] = vertexInfo; } asset.AABBMin = boundingBoxMin; asset.AABBMax = boundingBoxMax; asset.IndexData = assimpMesh.GetIndices(); asset.Name = nameOverride ?? assimpMesh.Name; if (CAssetRegistry.Instance.RequestRegisterAsset(asset, assetPath, out CMeshAsset existingAsset, true)) { existingAsset.WaitUntilLoaded(); asset.CopyFrom(existingAsset); } }