/// <summary> /// Initializes a new instance of the <see cref="Mesh" /> class. /// </summary> /// <param name="meshDraw">The mesh draw.</param> /// <param name="parameters">The parameters.</param> /// <exception cref="System.ArgumentNullException">parameters</exception> public Mesh(MeshDraw meshDraw, ParameterCollection parameters) { if (meshDraw == null) throw new ArgumentNullException("meshDraw"); if (parameters == null) throw new ArgumentNullException("parameters"); Draw = meshDraw; Parameters = parameters; }
public override MeshDraw CreateDebugPrimitive(GraphicsDevice device) { if (cachedDebugPrimitive != null) return cachedDebugPrimitive; var verts = new VertexPositionNormalTexture[pointsList.Count]; for (var i = 0; i < pointsList.Count; i++) { verts[i].Position = pointsList[i]; verts[i].TextureCoordinate = Vector2.Zero; verts[i].Normal = Vector3.Zero; } var intIndices = indicesList.Select(x => (int)x).ToArray(); ////calculate basic normals ////todo verify, winding order might be wrong? for (var i = 0; i < indicesList.Count; i += 3) { var i1 = intIndices[i]; var i2 = intIndices[i + 1]; var i3 = intIndices[i + 2]; var a = verts[i1]; var b = verts[i2]; var c = verts[i3]; var n = Vector3.Cross((b.Position - a.Position), (c.Position - a.Position)); n.Normalize(); verts[i1].Normal = verts[i2].Normal = verts[i3].Normal = n; } var meshData = new GeometricMeshData<VertexPositionNormalTexture>(verts, intIndices, false); cachedDebugPrimitive = new GeometricPrimitive(device, meshData).ToMeshDraw(); return cachedDebugPrimitive; }
/// <summary> /// Initializes a new instance of the <see cref="Mesh" /> class. /// </summary> /// <param name="meshDraw">The mesh draw.</param> /// <param name="parameters">The parameters.</param> /// <exception cref="System.ArgumentNullException">parameters</exception> public Mesh(MeshDraw meshDraw, ParameterCollection parameters) { if (meshDraw == null) throw new ArgumentNullException("meshDraw"); if (parameters == null) throw new ArgumentNullException("parameters"); Draw = meshDraw; Parameters = parameters; }
protected override async Task LoadContent() { await base.LoadContent(); pipelineState = new MutablePipelineState(GraphicsDevice); var vertices = new Vertex[4]; vertices[0] = new Vertex { Position = new Vector3(-1, -1, 0.5f), TexCoords = new Vector2(0, 0) }; vertices[1] = new Vertex { Position = new Vector3(-1, 1, 0.5f), TexCoords = new Vector2(3, 0) }; vertices[2] = new Vertex { Position = new Vector3(1, 1, 0.5f), TexCoords = new Vector2(3, 3) }; vertices[3] = new Vertex { Position = new Vector3(1, -1, 0.5f), TexCoords = new Vector2(0, 3) }; var indices = new short[] { 0, 1, 2, 0, 2, 3 }; var vertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertices, GraphicsResourceUsage.Default); var indexBuffer = Buffer.Index.New(GraphicsDevice, indices, GraphicsResourceUsage.Default); var meshDraw = new MeshDraw { DrawCount = 4, PrimitiveType = PrimitiveType.TriangleList, VertexBuffers = new[] { new VertexBufferBinding(vertexBuffer, new VertexDeclaration(VertexElement.Position<Vector3>(), VertexElement.TextureCoordinate<Vector2>()), 4) }, IndexBuffer = new IndexBufferBinding(indexBuffer, false, indices.Length), }; mesh = new Mesh { Draw = meshDraw, }; simpleEffect = new EffectInstance(new Effect(GraphicsDevice, SpriteEffect.Bytecode)); simpleEffect.Parameters.Set(TexturingKeys.Texture0, UVTexture); simpleEffect.UpdateEffect(GraphicsDevice); // TODO GRAPHICS REFACTOR //vao = VertexArrayObject.New(GraphicsDevice, mesh.Draw.IndexBuffer, mesh.Draw.VertexBuffers); myDraws = new DrawOptions[3]; myDraws[0] = new DrawOptions { Sampler = GraphicsDevice.SamplerStates.LinearClamp, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(-0.5f, 0.5f, 0f)) }; myDraws[1] = new DrawOptions { Sampler = GraphicsDevice.SamplerStates.LinearWrap, Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, 0.5f, 0f)) }; myDraws[2] = new DrawOptions { Sampler = SamplerState.New(GraphicsDevice, new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Mirror)), Transform = Matrix.Multiply(Matrix.Scaling(0.4f), Matrix.Translation(0.5f, -0.5f, 0f)) }; //var borderDescription = new SamplerStateDescription(TextureFilter.Linear, TextureAddressMode.Border) { BorderColor = Color.Purple }; //var border = SamplerState.New(GraphicsDevice, borderDescription); //myDraws[3] = new DrawOptions { Sampler = border, Transform = Matrix.Multiply(Matrix.Scale(0.3f), Matrix.Translation(-0.5f, -0.5f, 0f)) }; }
/// <inheritdoc/> public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex) { var commandList = context.CommandList; foreach (var renderFeature in RenderFeatures) { renderFeature.Draw(context, renderView, renderViewStage, startIndex, endIndex); } // TODO: stackalloc? var descriptorSetsLocal = descriptorSets.Value; if (descriptorSetsLocal == null || descriptorSetsLocal.Length < EffectDescriptorSetSlotCount) { descriptorSetsLocal = descriptorSets.Value = new DescriptorSet[EffectDescriptorSetSlotCount]; } MeshDraw currentDrawData = null; for (int index = startIndex; index < endIndex; index++) { var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode; var renderNode = GetRenderNode(renderNodeReference); var renderMesh = (RenderMesh)renderNode.RenderObject; var drawData = renderMesh.ActiveMeshDraw; // Get effect // TODO: Use real effect slot var renderEffect = renderNode.RenderEffect; if (renderEffect.Effect == null) { continue; } // Bind VB if (currentDrawData != drawData) { for (int i = 0; i < drawData.VertexBuffers.Length; i++) { var vertexBuffer = drawData.VertexBuffers[i]; commandList.SetVertexBuffer(i, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride); } if (drawData.IndexBuffer != null) { commandList.SetIndexBuffer(drawData.IndexBuffer.Buffer, drawData.IndexBuffer.Offset, drawData.IndexBuffer.Is32Bit); } currentDrawData = drawData; } var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference); // Update cbuffer renderEffect.Reflection.BufferUploader.Apply(context.CommandList, ResourceGroupPool, resourceGroupOffset); // Bind descriptor sets for (int i = 0; i < descriptorSetsLocal.Length; ++i) { var resourceGroup = ResourceGroupPool[resourceGroupOffset++]; if (resourceGroup != null) { descriptorSetsLocal[i] = resourceGroup.DescriptorSet; } } commandList.SetPipelineState(renderEffect.PipelineState); commandList.SetDescriptorSets(0, descriptorSetsLocal); // Draw if (drawData.IndexBuffer == null) { commandList.Draw(drawData.DrawCount, drawData.StartLocation); } else { commandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation); } } }
public override MeshDraw CreateDebugPrimitive(GraphicsDevice device) { return cachedDebugPrimitive ?? (cachedDebugPrimitive = GeometricPrimitive.Sphere.New(device).ToMeshDraw()); }
/// <summary> /// Transform a vertex buffer positions, normals, tangents and bitangents using the given matrix. /// </summary> /// <param name="meshDrawDatas">The mesh draw datas.</param> /// <param name="can32BitIndex">A flag stating if 32 bit index buffers.</param> public unsafe static MeshDraw MergeDrawData(IList<MeshDraw> meshDrawDatas, bool can32BitIndex) { if (meshDrawDatas.Count == 0) throw new ArgumentException("Need at least 1 MeshDrawData.", "meshDrawDatas"); if (meshDrawDatas.Count == 1) return meshDrawDatas[0]; // Check that vertex buffer declarations are matching var firstMeshDrawData = meshDrawDatas[0]; if (!firstMeshDrawData.IsSimple()) throw new InvalidOperationException("Can only merge simple MeshDrawData."); var firstVertexBuffer = firstMeshDrawData.VertexBuffers[0]; var hasIndexBuffer = IsIndexed(meshDrawDatas); int totalVertexCount = 0; int totalIndexCount = 0; //TODO: extend to non-simple vertex declarations, fill with default values it missing declarations etc. ? for (int i = 0; i < meshDrawDatas.Count; i++) { var meshDrawData = meshDrawDatas[i]; // This should not happen anymore if (i != 0) { if (!meshDrawData.IsSimple()) throw new InvalidOperationException("Can only merge simple MeshDrawData."); if (meshDrawData.VertexBuffers.Length != firstMeshDrawData.VertexBuffers.Length) throw new InvalidOperationException("Non-matching vertex buffer declarations."); if (!meshDrawData.VertexBuffers[0].Declaration.Equals(firstMeshDrawData.VertexBuffers[0].Declaration)) throw new InvalidOperationException("Non-matching vertex buffer declarations."); } if (meshDrawData.PrimitiveType != PrimitiveType.TriangleList) throw new InvalidOperationException("Can only merge TriangleList."); // Update vertex/index counts totalVertexCount += meshDrawData.VertexBuffers[0].Count; if (hasIndexBuffer) { if (meshDrawData.IndexBuffer != null) totalIndexCount += meshDrawData.IndexBuffer.Count; else totalIndexCount += meshDrawData.VertexBuffers[0].Count; } } // Allocate vertex buffer var result = new MeshDraw { PrimitiveType = PrimitiveType.TriangleList }; var destBufferData = new byte[firstVertexBuffer.Declaration.VertexStride * totalVertexCount]; result.VertexBuffers = new VertexBufferBinding[] { new VertexBufferBinding( new BufferData(BufferFlags.VertexBuffer, destBufferData).ToSerializableVersion(), firstVertexBuffer.Declaration, totalVertexCount, firstVertexBuffer.Stride)}; // Copy vertex buffers fixed (byte* destBufferDataStart = &destBufferData[0]) { var destBufferDataCurrent = destBufferDataStart; foreach (var meshDrawData in meshDrawDatas) { var sourceBuffer = meshDrawData.VertexBuffers[0].Buffer.GetSerializationData(); fixed (byte* sourceBufferDataStart = &sourceBuffer.Content[0]) { Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart, sourceBuffer.Content.Length); destBufferDataCurrent += sourceBuffer.Content.Length; } } } if (hasIndexBuffer) { var use32BitIndex = can32BitIndex && totalVertexCount > ushort.MaxValue; // 65535 = 0xFFFF is kept for primitive restart in strip // Allocate index buffer destBufferData = new byte[(use32BitIndex ? sizeof(uint) : sizeof(ushort)) * totalIndexCount]; result.IndexBuffer = new IndexBufferBinding( new BufferData(BufferFlags.IndexBuffer, destBufferData).ToSerializableVersion(), use32BitIndex, totalIndexCount); // Copy index buffers fixed (byte* destBufferDataStart = &destBufferData[0]) { var destBufferDataCurrent = destBufferDataStart; var offset = 0; foreach (var meshDrawData in meshDrawDatas) { var indexBuffer = meshDrawData.IndexBuffer; byte[] sourceBufferContent = null; var is32Bit = false; if (indexBuffer != null) { sourceBufferContent = indexBuffer.Buffer.GetSerializationData().Content; is32Bit = indexBuffer.Is32Bit; } if (offset != 0 || (use32BitIndex != meshDrawData.IndexBuffer.Is32Bit)) { if (use32BitIndex) sourceBufferContent = CreateIntIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit); else sourceBufferContent = CreateShortIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit); } fixed (byte* sourceBufferDataStart = &sourceBufferContent[0]) { Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart, sourceBufferContent.Length); destBufferDataCurrent += sourceBufferContent.Length; } offset += meshDrawData.VertexBuffers[0].Count; } } result.DrawCount = totalIndexCount; } else { result.DrawCount = totalVertexCount; } return result; }
/// <summary> /// Split the mesh if it has strictly more than 65535 vertices (max index = 65534) on a plaftorm that does not support 32 bits indices. /// </summary> /// <param name="meshDrawData">The mesh to analyze.</param> /// <param name="can32bitIndex">A flag stating if 32 bit indices are allowed.</param> /// <returns>A list of meshes.</returns> public unsafe static List<MeshDraw> SplitMesh(MeshDraw meshDrawData, bool can32bitIndex) { if (meshDrawData.IndexBuffer == null) return new List<MeshDraw> { meshDrawData }; if (!meshDrawData.IndexBuffer.Is32Bit) // already 16 bits buffer return new List<MeshDraw> { meshDrawData }; var verticesCount = meshDrawData.VertexBuffers[0].Count; if (verticesCount <= ushort.MaxValue) // can be put in a 16 bits buffer - 65535 = 0xFFFF is kept for primitive restart in strip { meshDrawData.CompactIndexBuffer(); return new List<MeshDraw> { meshDrawData }; } // now, we only have a 32 bits buffer that is justified because of a large vertex buffer if (can32bitIndex) // do nothing return new List<MeshDraw> { meshDrawData }; // TODO: handle primitives other than triangle list if (meshDrawData.PrimitiveType != PrimitiveType.TriangleList) return new List<MeshDraw> { meshDrawData }; // Split the mesh var finalList = new List<MeshDraw>(); fixed (byte* indicesByte = &meshDrawData.IndexBuffer.Buffer.GetSerializationData().Content[0]) { var indicesUint = (uint*)indicesByte; var splitInfos = new List<SplitInformation>(); var currentSplit = new SplitInformation(); currentSplit.StartTriangleIndex = 0; var currentIndexUintPtr = indicesUint; for (int triangleIndex = 0; triangleIndex < meshDrawData.IndexBuffer.Count / 3; ++triangleIndex) { var verticesToAdd = 0; var index0 = *currentIndexUintPtr++; var index1 = *currentIndexUintPtr++; var index2 = *currentIndexUintPtr++; if (!currentSplit.UsedIndices.Contains(index0)) ++verticesToAdd; if (!currentSplit.UsedIndices.Contains(index1)) ++verticesToAdd; if (!currentSplit.UsedIndices.Contains(index2)) ++verticesToAdd; if (currentSplit.UsedIndices.Count + verticesToAdd > 65535) // append in the same group { splitInfos.Add(currentSplit); currentSplit = new SplitInformation(); currentSplit.StartTriangleIndex = triangleIndex; } AddTriangle(currentSplit, index0, index1, index2, triangleIndex); } if (currentSplit.UsedIndices.Count > 0) splitInfos.Add(currentSplit); foreach (var splitInfo in splitInfos) { var triangleCount = splitInfo.LastTriangleIndex - splitInfo.StartTriangleIndex + 1; var newMeshDrawData = new MeshDraw { PrimitiveType = PrimitiveType.TriangleList, DrawCount = 3 * triangleCount, VertexBuffers = new VertexBufferBinding[meshDrawData.VertexBuffers.Length] }; // vertex buffers for (int vbIndex = 0; vbIndex < meshDrawData.VertexBuffers.Length; ++ vbIndex) { var stride = meshDrawData.VertexBuffers[vbIndex].Stride; if (stride == 0) stride = meshDrawData.VertexBuffers[vbIndex].Declaration.VertexStride; var newVertexBuffer = new byte[splitInfo.UsedIndices.Count * stride]; fixed (byte* vertexBufferPtr = &meshDrawData.VertexBuffers[vbIndex].Buffer.GetSerializationData().Content[0]) fixed (byte* newVertexBufferPtr = &newVertexBuffer[vbIndex]) { //copy vertex buffer foreach (var index in splitInfo.UsedIndices) Utilities.CopyMemory((IntPtr)(newVertexBufferPtr + stride * splitInfo.IndexRemapping[index]), (IntPtr)(vertexBufferPtr + stride * index), stride); } newMeshDrawData.VertexBuffers[vbIndex] = new VertexBufferBinding( new BufferData(BufferFlags.VertexBuffer, newVertexBuffer).ToSerializableVersion(), meshDrawData.VertexBuffers[vbIndex].Declaration, splitInfo.UsedIndices.Count); } // index buffer var newIndexBuffer = new byte[sizeof(ushort) * 3 * triangleCount]; fixed (byte* newIndexBufferPtr = &newIndexBuffer[0]) { var newIndexBufferUshortPtr = (ushort*)newIndexBufferPtr; var currentIndexPtr = &indicesUint[3 * splitInfo.StartTriangleIndex]; for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { var index0 = *currentIndexPtr++; var index1 = *currentIndexPtr++; var index2 = *currentIndexPtr++; var newIndex0 = splitInfo.IndexRemapping[index0]; var newIndex1 = splitInfo.IndexRemapping[index1]; var newIndex2 = splitInfo.IndexRemapping[index2]; *newIndexBufferUshortPtr++ = newIndex0; *newIndexBufferUshortPtr++ = newIndex1; *newIndexBufferUshortPtr++ = newIndex2; } } newMeshDrawData.IndexBuffer = new IndexBufferBinding( new BufferData(BufferFlags.IndexBuffer, newIndexBuffer).ToSerializableVersion(), false, triangleCount*3); finalList.Add(newMeshDrawData); } } return finalList; }
private InputElementDescription[] PrepareInputElements(PipelineStateDescription pipelineState, MeshDraw drawData) { // Get the input elements already contained in the mesh's vertex buffers var availableInputElements = drawData.VertexBuffers.CreateInputElements(); var inputElements = new List <InputElementDescription>(availableInputElements); // In addition, add input elements for all attributes that are not contained in a bound buffer, but required by the shader foreach (var inputAttribute in pipelineState.EffectBytecode.Reflection.InputAttributes) { var inputElementIndex = FindElementBySemantic(availableInputElements, inputAttribute.SemanticName, inputAttribute.SemanticIndex); // Provided by any vertex buffer? if (inputElementIndex >= 0) { continue; } inputElements.Add(new InputElementDescription { AlignedByteOffset = 0, Format = PixelFormat.R32G32B32A32_Float, InputSlot = drawData.VertexBuffers.Length, InputSlotClass = InputClassification.Vertex, InstanceDataStepRate = 0, SemanticIndex = inputAttribute.SemanticIndex, SemanticName = inputAttribute.SemanticName }); } return(inputElements.ToArray()); }
/// <summary> /// Creates an Entity that contains our dynamic Vertex and Index buffers. /// This Entity will be rendered by the model renderer. /// </summary> /// <param name="verticesCount"></param> /// <param name="indicesCount"></param> private void CreateTerrainModelEntity(int verticesCount, int indicesCount) { // Compute sizes var vertexDeclaration = VertexNormalTexture.VertexDeclaration; var vertexBufferSize = verticesCount * vertexDeclaration.CalculateSize(); var indexBufferSize = indicesCount * sizeof(short); // Create Vertex and Index buffers terrainVertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertexBufferSize, GraphicsResourceUsage.Dynamic); terrainIndexBuffer = Buffer.New(GraphicsDevice, indexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic); // Prepare mesh and entity var meshDraw = new MeshDraw { PrimitiveType = PrimitiveType.TriangleStrip, VertexBuffers = new[] { new VertexBufferBinding(terrainVertexBuffer, vertexDeclaration, verticesCount) }, IndexBuffer = new IndexBufferBinding(terrainIndexBuffer, false, indicesCount), }; // Load the material and set parameters TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture0, WaterTexture); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture1, GrassTexture); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture2, MountainTexture); // Set up material regions TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight0, -10); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight0, 40); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight0, 70); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight1, 60); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight1, 80); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight1, 90); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight2, 85); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight2, 95); TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight2, 125); terrainMesh = new Mesh { Draw = meshDraw, MaterialIndex = 0 }; TerrainEntity.GetOrCreate<ModelComponent>().Model = new Model { terrainMesh, TerrainMaterial }; }