/// <summary> /// Draws a collection of instances. Requires an HLSL effect designed for hardware instancing. /// </summary> /// <param name="device"></param> /// <param name="camera"></param> /// <param name="instances"></param> protected virtual void drawInstances(RenderParameters parameters, Matrix transform) { if (this.Instances.Length == 0) { return; } bool recalculate = this.instanceVertexBuffer == null || this.instanceVertexBuffer.IsContentLost || (parameters.IsMainRender && (this.instancesChanged || this.lastInstancesChanged)); if (recalculate) { // If we have more instances than room in our vertex buffer, grow it to the neccessary size. if (this.instanceVertexBuffer == null || this.instanceVertexBuffer.IsContentLost || this.Instances.Length > this.instanceVertexBuffer.VertexCount) { if (this.instanceVertexBuffer != null) { this.instanceVertexBuffer.Dispose(); } int bufferSize = (int)Math.Pow(2.0, Math.Ceiling(Math.Log(this.Instances.Length, 2.0))); this.instanceVertexBuffer = new DynamicVertexBuffer ( this.main.GraphicsDevice, Model.instanceVertexDeclaration, bufferSize, BufferUsage.WriteOnly ); InstanceVertex[] newData = new InstanceVertex[bufferSize]; if (this.instanceVertexData != null) { Array.Copy(this.instanceVertexData, newData, Math.Min(bufferSize, this.instanceVertexData.Length)); for (int i = this.instanceVertexData.Length; i < this.Instances.Length; i++) { newData[i].LastTransform = this.Instances[i].Transform; } } this.instanceVertexData = newData; } for (int i = 0; i < this.Instances.Length; i++) { this.instanceVertexData[i].LastTransform = this.instanceVertexData[i].Transform; Instance instance = this.Instances[i]; this.instanceVertexData[i].Transform = instance.Transform; this.instanceVertexData[i].Param = instance.Param; } // Transfer the latest instance transform matrices into the instanceVertexBuffer. this.instanceVertexBuffer.SetData <InstanceVertex>(this.instanceVertexData, 0, this.Instances.Length, SetDataOptions.Discard); this.lastInstancesChanged = this.instancesChanged; this.instancesChanged = false; } #if !MONOGAME // TODO: enable hardware instancing for MonoGame // Set up the instance rendering effect. if (this.setParameters(transform, parameters)) { this.main.LightingManager.SetRenderParameters(this.effect, parameters); RasterizerState originalState = this.main.GraphicsDevice.RasterizerState; RasterizerState noCullState = null; if (recalculate && this.DisableCulling) { noCullState = new RasterizerState { CullMode = CullMode.None }; this.main.GraphicsDevice.RasterizerState = noCullState; } for (int i = 0; i < this.model.Meshes.Count; i++) { ModelMesh mesh = this.model.Meshes[i]; for (int j = 0; j < mesh.MeshParts.Count; j++) { ModelMeshPart meshPart = mesh.MeshParts[j]; // Tell the GPU to read from both the model vertex buffer plus our instanceVertexBuffer. // TODO: Monogame support for GraphicsDevice.SetVertexBuffers() this.main.GraphicsDevice.SetVertexBuffers ( new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0), new VertexBufferBinding(instanceVertexBuffer, 0, 1) ); this.main.GraphicsDevice.Indices = meshPart.IndexBuffer; // Draw all the instance copies in a single call. this.effect.CurrentTechnique.Passes[0].Apply(); // TODO: Monogame support for GraphicsDevice.DrawInstancedPrimitives() this.main.GraphicsDevice.DrawInstancedPrimitives ( PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount, this.Instances.Length ); Model.DrawCallCounter++; Model.TriangleCounter += meshPart.PrimitiveCount * this.Instances.Length; } } if (noCullState != null) { this.main.GraphicsDevice.RasterizerState = originalState; } } #endif if (parameters.IsMainRender) { this.lastTransform = transform; this.lastWorldViewProjection = transform * parameters.Camera.ViewProjection; } }
/// <summary> /// Draws a collection of instances. Requires an HLSL effect designed for hardware instancing. /// </summary> /// <param name="device"></param> /// <param name="camera"></param> /// <param name="instances"></param> protected virtual void drawInstances(RenderParameters parameters, Matrix transform) { if (this.Instances.Count == 0) return; bool recalculate = this.instanceVertexBuffer == null || this.instanceVertexBuffer.IsContentLost || (parameters.IsMainRender && (this.instancesChanged || this.lastInstancesChanged)); if (recalculate) { // If we have more instances than room in our vertex buffer, grow it to the neccessary size. if (this.instanceVertexBuffer == null || this.instanceVertexBuffer.IsContentLost || this.Instances.Count > this.instanceVertexBuffer.VertexCount) { if (this.instanceVertexBuffer != null) this.instanceVertexBuffer.Dispose(); int bufferSize = (int)Math.Pow(2.0, Math.Ceiling(Math.Log(this.Instances.Count, 2.0))); this.instanceVertexBuffer = new DynamicVertexBuffer ( this.main.GraphicsDevice, Model.instanceVertexDeclaration, bufferSize, BufferUsage.WriteOnly ); InstanceVertex[] newData = new InstanceVertex[bufferSize]; if (this.instanceVertexData != null) { Array.Copy(this.instanceVertexData, newData, Math.Min(bufferSize, this.instanceVertexData.Length)); for (int i = this.instanceVertexData.Length; i < this.Instances.Count; i++) newData[i].LastTransform = this.Instances[i]; } this.instanceVertexData = newData; } for (int i = 0; i < this.Instances.Count; i++) { this.instanceVertexData[i].LastTransform = this.instanceVertexData[i].Transform; this.instanceVertexData[i].Transform = this.Instances[i]; this.instanceVertexData[i].InstanceIndex = i; } // Transfer the latest instance transform matrices into the instanceVertexBuffer. this.instanceVertexBuffer.SetData<InstanceVertex>(this.instanceVertexData, 0, this.Instances.Count, SetDataOptions.Discard); this.lastInstancesChanged = this.instancesChanged; this.instancesChanged = false; } #if !MONOGAME // TODO: enable hardware instancing for MonoGame // Set up the instance rendering effect. if (this.setParameters(transform, parameters)) { this.main.LightingManager.SetRenderParameters(this.effect, parameters); RasterizerState originalState = this.main.GraphicsDevice.RasterizerState; RasterizerState noCullState = null; if (recalculate && this.DisableCulling) { noCullState = new RasterizerState { CullMode = CullMode.None }; this.main.GraphicsDevice.RasterizerState = noCullState; } foreach (ModelMesh mesh in this.model.Meshes) { foreach (ModelMeshPart meshPart in mesh.MeshParts) { // Tell the GPU to read from both the model vertex buffer plus our instanceVertexBuffer. // TODO: Monogame support for GraphicsDevice.SetVertexBuffers() this.main.GraphicsDevice.SetVertexBuffers ( new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0), new VertexBufferBinding(instanceVertexBuffer, 0, 1) ); this.main.GraphicsDevice.Indices = meshPart.IndexBuffer; // Draw all the instance copies in a single call. foreach (EffectPass pass in this.effect.CurrentTechnique.Passes) { pass.Apply(); // TODO: Monogame support for GraphicsDevice.DrawInstancedPrimitives() this.main.GraphicsDevice.DrawInstancedPrimitives ( PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount, this.Instances.Count ); } } } if (noCullState != null) this.main.GraphicsDevice.RasterizerState = originalState; } #endif if (parameters.IsMainRender) { this.lastTransform = transform; this.lastWorldViewProjection = transform * parameters.Camera.ViewProjection; } }