diff --git a/DirectionalShadowMapData.cs b/DirectionalShadowMapData.cs new file mode 100644 index 0000000..455d7d1 --- /dev/null +++ b/DirectionalShadowMapData.cs @@ -0,0 +1,59 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DirectionalShadowMapData + { + public static readonly int MAX_SHADOW_CASCADES = 4; + + public RenderTarget2D[] ShadowMaps { get; } + + public Matrix[] LightSpaceViews { get; } + public Matrix[] LightSpaceProjections { get; } + + public float[] CascadeFarPlanes { get; } + + public int ShadowMapSize { get; } + public int NumShadowCascades { get; } + + internal DirectionalShadowMapData( + GraphicsDevice graphicsDevice, + int shadowMapSize, + int numCascades + ) { + ShadowMapSize = shadowMapSize; + NumShadowCascades = (int)MathHelper.Clamp(numCascades, 1, MAX_SHADOW_CASCADES); + + LightSpaceViews = new Matrix[4]; + LightSpaceProjections = new Matrix[4]; + + ShadowMaps = new RenderTarget2D[NumShadowCascades]; + + for (var i = 0; i < NumShadowCascades; i++) + { + ShadowMaps[i] = new RenderTarget2D( + graphicsDevice, + shadowMapSize, + shadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24, + 0, + RenderTargetUsage.PreserveContents + ); + } + + CascadeFarPlanes = new float[MAX_SHADOW_CASCADES]; + } + + public void Clear(GraphicsDevice graphicsDevice) + { + foreach (var shadowMap in ShadowMaps) + { + graphicsDevice.SetRenderTarget(shadowMap); + graphicsDevice.Clear(Color.White); + } + } + } +} diff --git a/EffectInterfaces/IHasTranslation.cs b/EffectInterfaces/IHasTranslation.cs new file mode 100644 index 0000000..969cfdb --- /dev/null +++ b/EffectInterfaces/IHasTranslation.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public interface IHasTranslation + { + Vector3 Translation { get; set; } + } +} diff --git a/EffectInterfaces/IHasWorldMatrix.cs b/EffectInterfaces/IHasWorldMatrix.cs new file mode 100644 index 0000000..e7d2bae --- /dev/null +++ b/EffectInterfaces/IHasWorldMatrix.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public interface IHasWorldMatrix + { + Matrix World { get; set; } + } +} diff --git a/Effects/DeferredPBR_GBufferEffect.cs b/Effects/DeferredPBR_GBufferEffect.cs index 26a9c8f..335a592 100644 --- a/Effects/DeferredPBR_GBufferEffect.cs +++ b/Effects/DeferredPBR_GBufferEffect.cs @@ -6,8 +6,7 @@ namespace Kav public class DeferredPBR_GBufferEffect : Effect, TransformEffect { EffectParameter worldParam; - EffectParameter worldViewProjectionParam; - EffectParameter worldInverseTransposeParam; + EffectParameter viewProjectionParam; EffectParameter albedoTextureParam; EffectParameter normalTextureParam; @@ -17,6 +16,7 @@ namespace Kav EffectParameter metallicParam; EffectParameter roughnessParam; + EffectParameter vertexShaderIndexParam; EffectParameter shaderIndexParam; Matrix world = Matrix.Identity; @@ -30,6 +30,7 @@ namespace Kav bool albedoTextureEnabled = false; bool metallicRoughnessMapEnabled = false; bool normalMapEnabled = false; + bool hardwareInstancingEnabled = false; EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; @@ -39,7 +40,7 @@ namespace Kav set { world = value; - dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj; + dirtyFlags |= EffectDirtyFlags.World; } } @@ -49,7 +50,7 @@ namespace Kav set { view = value; - dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition; + dirtyFlags |= EffectDirtyFlags.ViewProj | EffectDirtyFlags.EyePosition; } } @@ -59,7 +60,7 @@ namespace Kav set { projection = value; - dirtyFlags |= EffectDirtyFlags.WorldViewProj; + dirtyFlags |= EffectDirtyFlags.ViewProj; } } @@ -100,7 +101,7 @@ namespace Kav { albedoTextureParam.SetValue(value); albedoTextureEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } @@ -111,7 +112,7 @@ namespace Kav { normalTextureParam.SetValue(value); normalMapEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } @@ -122,7 +123,20 @@ namespace Kav { metallicRoughnessTextureParam.SetValue(value); metallicRoughnessMapEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; + } + } + + public bool HardwareInstancingEnabled + { + get { return hardwareInstancingEnabled; } + set + { + if (value != hardwareInstancingEnabled) + { + hardwareInstancingEnabled = value; + dirtyFlags |= EffectDirtyFlags.VertexShaderIndex; + } } } @@ -159,30 +173,30 @@ namespace Kav { worldParam.SetValue(world); - Matrix.Invert(ref world, out Matrix worldInverse); - Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose); - worldInverseTransposeParam.SetValue(worldInverseTranspose); - dirtyFlags &= ~EffectDirtyFlags.World; } - if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0) { - Matrix.Multiply(ref world, ref view, out Matrix worldView); - Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj); - worldViewProjectionParam.SetValue(worldViewProj); + Matrix.Multiply(ref view, ref projection, out Matrix viewProj); + viewProjectionParam.SetValue(viewProj); - dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + dirtyFlags &= ~EffectDirtyFlags.ViewProj; } - if ((dirtyFlags & EffectDirtyFlags.EyePosition) != 0) + if ((dirtyFlags & EffectDirtyFlags.VertexShaderIndex) != 0) { - Matrix.Invert(ref view, out Matrix inverseView); + int vertexShaderIndex = 0; - dirtyFlags &= ~EffectDirtyFlags.EyePosition; + if (hardwareInstancingEnabled) + { + vertexShaderIndex = 1; + } + + vertexShaderIndexParam.SetValue(vertexShaderIndex); } - if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) + if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0) { int shaderIndex = 0; @@ -217,15 +231,14 @@ namespace Kav shaderIndexParam.SetValue(shaderIndex); - dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; + dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex; } } void CacheEffectParameters() { worldParam = Parameters["World"]; - worldViewProjectionParam = Parameters["WorldViewProjection"]; - worldInverseTransposeParam = Parameters["WorldInverseTranspose"]; + viewProjectionParam = Parameters["ViewProjection"]; albedoTextureParam = Parameters["AlbedoTexture"]; normalTextureParam = Parameters["NormalTexture"]; @@ -235,7 +248,8 @@ namespace Kav metallicParam = Parameters["MetallicValue"]; roughnessParam = Parameters["RoughnessValue"]; - shaderIndexParam = Parameters["ShaderIndex"]; + shaderIndexParam = Parameters["PixelShaderIndex"]; + vertexShaderIndexParam = Parameters["VertexShaderIndex"]; } } } diff --git a/Effects/DiffuseLitSpriteEffect.cs b/Effects/DiffuseLitSpriteEffect.cs index faf05b3..ae8618c 100644 --- a/Effects/DiffuseLitSpriteEffect.cs +++ b/Effects/DiffuseLitSpriteEffect.cs @@ -40,7 +40,7 @@ namespace Kav if (normalMapEnabled != value) { normalMapEnabled = value; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } } @@ -142,7 +142,7 @@ namespace Kav dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; } - if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) + if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0) { int shaderIndex = 0; diff --git a/Effects/EffectHelpers.cs b/Effects/EffectHelpers.cs index 5a23251..56a0cec 100644 --- a/Effects/EffectHelpers.cs +++ b/Effects/EffectHelpers.cs @@ -8,7 +8,9 @@ namespace Kav WorldViewProj = 1, World = 2, EyePosition = 4, - ShaderIndex = 8, + VertexShaderIndex = 8, + PixelShaderIndex = 16, + ViewProj = 32, All = -1 } } diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb index 3aff2ad..2012ecb 100644 --- a/Effects/FXB/DeferredPBR_GBufferEffect.fxb +++ b/Effects/FXB/DeferredPBR_GBufferEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1208a4d715117867d4bd2b877526b526eadd5b47bf99c6e8dfbf2d8d3487478c -size 7476 +oid sha256:240a1d0911f6bba0da34d5a158c5868e52f6612dc055cdc42d8cef2a5f3febaa +size 8908 diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb index 8281157..fee70bc 100644 --- a/Effects/FXB/SimpleDepthEffect.fxb +++ b/Effects/FXB/SimpleDepthEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0984ae92245afe3e2bda5fab0135293b5121c3ee7b5f146e7d4ddd1684ee74b -size 848 +oid sha256:41083a1789c08363517ab1279e80e985e8fcfc3439411d13df4a5ea4667491e9 +size 1340 diff --git a/Effects/FXB/SimpleDepthEffectInstanced.fxb b/Effects/FXB/SimpleDepthEffectInstanced.fxb new file mode 100644 index 0000000..9c19b29 --- /dev/null +++ b/Effects/FXB/SimpleDepthEffectInstanced.fxb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f5eb937af7db2fd9a149ae06981af2a33043abaa5fbb72c8382b4b72097f98f +size 1008 diff --git a/Effects/HLSL/DeferredPBR_GBufferEffect.fx b/Effects/HLSL/DeferredPBR_GBufferEffect.fx index f24da19..52f911c 100644 --- a/Effects/HLSL/DeferredPBR_GBufferEffect.fx +++ b/Effects/HLSL/DeferredPBR_GBufferEffect.fx @@ -13,8 +13,7 @@ BEGIN_CONSTANTS MATRIX_CONSTANTS float4x4 World _vs(c0) _cb(c7); - float4x4 WorldInverseTranspose _vs(c4) _cb(c11); - float4x4 WorldViewProjection _vs(c8) _cb(c15); + float4x4 ViewProjection _vs(c4) _cb(c11); END_CONSTANTS @@ -22,7 +21,7 @@ struct VertexInput { float4 Position : POSITION; float3 Normal : NORMAL; - float2 TexCoord : TEXCOORD0; + float2 TexCoord : TEXCOORD; }; struct PixelInput @@ -48,9 +47,33 @@ PixelInput main_vs(VertexInput input) PixelInput output; output.PositionWorld = mul(input.Position, World).xyz; - output.NormalWorld = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz; + output.NormalWorld = normalize(mul(input.Normal, World)); output.TexCoord = input.TexCoord; - output.Position = mul(input.Position, WorldViewProjection); + + float4x4 worldViewProjection = mul(World, ViewProjection); + output.Position = mul(input.Position, worldViewProjection); + + return output; +} + +PixelInput instanced_vs(VertexInput input, float3 Translation : TEXCOORD2) +{ + PixelInput output; + + float4x4 world = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(Translation.x, Translation.y, Translation.z, 1) + ); + + float4x4 worldViewProjection = mul(world, ViewProjection); + + output.PositionWorld = mul(input.Position, world); + output.NormalWorld = mul(input.Normal, world); + output.TexCoord = input.TexCoord; + + output.Position = mul(input.Position, worldViewProjection); return output; } @@ -171,6 +194,19 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input) return output; } +VertexShader VSArray[2] = +{ + compile vs_3_0 main_vs(), + compile vs_3_0 instanced_vs() +}; + +int VSIndices[2] = +{ + 0, 1 +}; + +int VertexShaderIndex = 0; + PixelShader PSArray[8] = { compile ps_3_0 NonePS(), @@ -191,13 +227,13 @@ int PSIndices[8] = 0, 1, 2, 3, 4, 5, 6, 7 }; -int ShaderIndex = 0; +int PixelShaderIndex = 0; Technique GBuffer { Pass { - VertexShader = compile vs_3_0 main_vs(); - PixelShader = (PSArray[PSIndices[ShaderIndex]]); + VertexShader = (VSArray[VSIndices[VertexShaderIndex]]); + PixelShader = (PSArray[PSIndices[PixelShaderIndex]]); } } diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index f813bd7..a4c3bbc 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -2,10 +2,11 @@ BEGIN_CONSTANTS - float4x4 ModelViewProjection _vs(c0) _cb(c0); - MATRIX_CONSTANTS + float4x4 World _vs(c0) _cb(c0); + float4x4 ViewProjection _vs(c4) _cb(c4); + END_CONSTANTS struct VertexShaderInput @@ -15,15 +16,16 @@ struct VertexShaderInput struct VertexShaderOutput { - float4 Position : SV_Position; - float Depth : TEXCOORD0; + float4 Position : SV_POSITION; + float Depth : DEPTH; }; VertexShaderOutput main_vs(VertexShaderInput input) { VertexShaderOutput output; - output.Position = mul(input.Position, ModelViewProjection); + float4x4 worldViewProjection = mul(World, ViewProjection); + output.Position = mul(input.Position, worldViewProjection); output.Depth = output.Position.z / output.Position.w; return output; diff --git a/Effects/HLSL/SimpleDepthEffectInstanced.fx b/Effects/HLSL/SimpleDepthEffectInstanced.fx new file mode 100644 index 0000000..f35471e --- /dev/null +++ b/Effects/HLSL/SimpleDepthEffectInstanced.fx @@ -0,0 +1,45 @@ +float4x4 ViewProjection; + +struct VertexShaderInput +{ + float4 Position : POSITION; +}; + +struct VertexShaderOutput +{ + float4 Position : POSITION0; + float4 ProjectedPosition : TEXCOORD0; +}; + +VertexShaderOutput instanced_vs(VertexShaderInput input, float3 Translation : TEXCOORD2) +{ + VertexShaderOutput output; + + float4x4 world = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(Translation.x, Translation.y, Translation.z, 1) + ); + + float4x4 worldViewProjection = mul(world, ViewProjection); + + output.Position = mul(input.Position, worldViewProjection); + output.ProjectedPosition = output.Position; + + return output; +} + +float4 main_ps(VertexShaderOutput input) : COLOR0 +{ + return float4(input.ProjectedPosition.z / input.ProjectedPosition.w, 0.0, 0.0, 0.0); +} + +Technique SimpleDepth +{ + Pass + { + VertexShader = compile vs_3_0 instanced_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/LinearDepthEffect.cs b/Effects/LinearDepthEffect.cs index 2a6a76a..5b62977 100644 --- a/Effects/LinearDepthEffect.cs +++ b/Effects/LinearDepthEffect.cs @@ -3,28 +3,28 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class LinearDepthEffect : Effect + public class LinearDepthEffect : Effect, IHasWorldMatrix { - EffectParameter modelParam; - EffectParameter modelViewProjectionParam; + EffectParameter worldParam; + EffectParameter worldViewProjectionParam; EffectParameter lightPositionParam; EffectParameter farPlaneParam; EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; - Matrix model; + Matrix world; Matrix view; Matrix projection; public Vector3 LightPosition { get; set; } public float FarPlane { get; set; } - public Matrix Model + public Matrix World { - get { return model; } + get { return world; } set { - model = value; + world = value; dirtyFlags |= EffectDirtyFlags.World; dirtyFlags |= EffectDirtyFlags.WorldViewProj; } @@ -60,16 +60,16 @@ namespace Kav if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) { Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); - Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj); + Matrix.Multiply(ref world, ref viewProjection, out Matrix worldViewProj); - modelViewProjectionParam.SetValue(worldViewProj); + worldViewProjectionParam.SetValue(worldViewProj); dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; } if ((dirtyFlags & EffectDirtyFlags.World) != 0) { - modelParam.SetValue(model); + worldParam.SetValue(world); dirtyFlags &= ~EffectDirtyFlags.World; } @@ -80,8 +80,8 @@ namespace Kav private void CacheEffectParameters() { - modelParam = Parameters["Model"]; - modelViewProjectionParam = Parameters["ModelViewProjection"]; + worldParam = Parameters["Model"]; + worldViewProjectionParam = Parameters["ModelViewProjection"]; lightPositionParam = Parameters["LightPosition"]; farPlaneParam = Parameters["FarPlane"]; diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs index 2df74cd..4b80ecb 100644 --- a/Effects/PBREffect.cs +++ b/Effects/PBREffect.cs @@ -130,7 +130,7 @@ namespace Kav { albedoTextureParam.SetValue(value); albedoTextureEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } @@ -141,7 +141,7 @@ namespace Kav { normalTextureParam.SetValue(value); normalMapEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } @@ -164,7 +164,7 @@ namespace Kav { metallicRoughnessTextureParam.SetValue(value); metallicRoughnessMapEnabled = value != null; - dirtyFlags |= EffectDirtyFlags.ShaderIndex; + dirtyFlags |= EffectDirtyFlags.PixelShaderIndex; } } @@ -266,7 +266,7 @@ namespace Kav dirtyFlags &= ~EffectDirtyFlags.EyePosition; } - if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) + if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0) { int shaderIndex = 0; @@ -301,7 +301,7 @@ namespace Kav shaderIndexParam.SetValue(shaderIndex); - dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; + dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex; } } diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs index 1854e3c..2020c11 100644 --- a/Effects/SimpleDepthEffect.cs +++ b/Effects/SimpleDepthEffect.cs @@ -3,23 +3,24 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class SimpleDepthEffect : Effect + public class SimpleDepthEffect : Effect, IHasWorldMatrix { - EffectParameter modelViewProjectionParam; + EffectParameter worldParam; + EffectParameter viewProjectionParam; - Matrix model; + Matrix world; Matrix view; Matrix projection; EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; - public Matrix Model + public Matrix World { - get { return model; } + get { return world; } set { - model = value; - dirtyFlags |= EffectDirtyFlags.WorldViewProj; + world = value; + dirtyFlags |= EffectDirtyFlags.World; } } @@ -29,7 +30,7 @@ namespace Kav set { view = value; - dirtyFlags |= EffectDirtyFlags.WorldViewProj; + dirtyFlags |= EffectDirtyFlags.ViewProj; } } @@ -39,7 +40,7 @@ namespace Kav set { projection = value; - dirtyFlags |= EffectDirtyFlags.WorldViewProj; + dirtyFlags |= EffectDirtyFlags.ViewProj; } } @@ -50,20 +51,26 @@ namespace Kav protected override void OnApply() { - if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + if ((dirtyFlags & EffectDirtyFlags.World) != 0) + { + worldParam.SetValue(world); + + dirtyFlags &= ~EffectDirtyFlags.World; + } + + if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0) { Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); - Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj); + viewProjectionParam.SetValue(viewProjection); - modelViewProjectionParam.SetValue(worldViewProj); - - dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + dirtyFlags &= ~EffectDirtyFlags.ViewProj; } } private void CacheEffectParameters() { - modelViewProjectionParam = Parameters["ModelViewProjection"]; + worldParam = Parameters["World"]; + viewProjectionParam = Parameters["ViewProjection"]; } } } diff --git a/Effects/SimpleDepthEffectInstanced.cs b/Effects/SimpleDepthEffectInstanced.cs new file mode 100644 index 0000000..52fc597 --- /dev/null +++ b/Effects/SimpleDepthEffectInstanced.cs @@ -0,0 +1,56 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class SimpleDepthEffectInstanced : Effect + { + EffectParameter viewProjectionParam; + + Matrix view; + Matrix projection; + + EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; + + public Matrix View + { + get { return view; } + set + { + view = value; + dirtyFlags |= EffectDirtyFlags.ViewProj; + } + } + + public Matrix Projection + { + get { return projection; } + set + { + projection = value; + dirtyFlags |= EffectDirtyFlags.ViewProj; + } + } + + public SimpleDepthEffectInstanced(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffectInstanced) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0) + { + Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); + viewProjectionParam.SetValue(viewProjection); + + dirtyFlags &= ~EffectDirtyFlags.ViewProj; + } + } + + private void CacheEffectParameters() + { + viewProjectionParam = Parameters["ViewProjection"]; + } + } +} diff --git a/Geometry/Interfaces/IGBufferDrawable.cs b/Geometry/Interfaces/IGBufferDrawable.cs new file mode 100644 index 0000000..bf84905 --- /dev/null +++ b/Geometry/Interfaces/IGBufferDrawable.cs @@ -0,0 +1,16 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public interface IGBufferDrawable + { + Vector3 Albedo { get; } + float Metallic { get; } + float Roughness { get; } + + Texture2D AlbedoTexture { get; } + Texture2D NormalTexture { get; } + Texture2D MetallicRoughnessTexture { get; } + } +} diff --git a/Geometry/Interfaces/IHasVertexPositions.cs b/Geometry/Interfaces/IHasVertexPositions.cs new file mode 100644 index 0000000..e39483b --- /dev/null +++ b/Geometry/Interfaces/IHasVertexPositions.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public interface IHasVertexPositions + { + Vector3[] Positions { get; } + } +} diff --git a/Geometry/Interfaces/IIndexDrawable.cs b/Geometry/Interfaces/IIndexDrawable.cs new file mode 100644 index 0000000..f59f8d4 --- /dev/null +++ b/Geometry/Interfaces/IIndexDrawable.cs @@ -0,0 +1,10 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public interface IIndexDrawable + { + VertexBuffer VertexBuffer { get; } + IndexBuffer IndexBuffer { get; } + } +} diff --git a/Geometry/MeshPart.cs b/Geometry/MeshPart.cs index 25cb93c..80f52de 100644 --- a/Geometry/MeshPart.cs +++ b/Geometry/MeshPart.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class MeshPart + public class MeshPart : IIndexDrawable, IGBufferDrawable, ICullable, IHasVertexPositions { public IndexBuffer IndexBuffer { get; } public VertexBuffer VertexBuffer { get; } @@ -11,27 +11,27 @@ namespace Kav public Vector3[] Positions { get; } public BoundingBox BoundingBox { get; } - + private Texture2D albedoTexture = null; private Texture2D normalTexture = null; private Texture2D metallicRoughnessTexture = null; - public Texture2D AlbedoTexture - { + public Texture2D AlbedoTexture + { get { return DisableAlbedoMap ? null : albedoTexture; } set { albedoTexture = value; } } - public Texture2D NormalTexture - { - get { return DisableNormalMap ? null : normalTexture; } + public Texture2D NormalTexture + { + get { return DisableNormalMap ? null : normalTexture; } set { normalTexture = value; } } - public Texture2D MetallicRoughnessTexture - { + public Texture2D MetallicRoughnessTexture + { get { return DisableMetallicRoughnessMap ? null : metallicRoughnessTexture; } - set { metallicRoughnessTexture = value; } + set { metallicRoughnessTexture = value; } } public Vector3 Albedo { get; set; } = Vector3.One; diff --git a/Geometry/MeshSprite.cs b/Geometry/MeshSprite.cs index ac50ab7..dce9752 100644 --- a/Geometry/MeshSprite.cs +++ b/Geometry/MeshSprite.cs @@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class MeshSprite : ICullable + public class MeshSprite : ICullable, IIndexDrawable { private static readonly int PixelScale = 40; private static readonly short[] Indices = new short[] diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 9cb9564..6077186 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -43,6 +43,9 @@ Kav.Resources.SimpleDepthEffect.fxb + + Kav.Resources.SimpleDepthEffectInstanced.fxb + Kav.Resources.LinearDepthEffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index d5c6c57..60a7849 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -43,6 +43,9 @@ Kav.Resources.SimpleDepthEffect.fxb + + Kav.Resources.SimpleDepthEffectInstanced.fxb + Kav.Resources.LinearDepthEffect.fxb diff --git a/Renderer.cs b/Renderer.cs index 08c673e..5eabb74 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -8,19 +8,11 @@ namespace Kav { public class Renderer { - private const int MAX_SHADOW_CASCADES = 4; - private int ShadowMapSize { get; } + private const int MAX_INSTANCE_VERTEX_COUNT = 1000000; private GraphicsDevice GraphicsDevice { get; } - private int RenderDimensionsX { get; } - private int RenderDimensionsY { get; } private VertexBuffer FullscreenTriangle { get; } - private int NumShadowCascades { get; } - - private RenderTarget2D ColorRenderTarget { get; } - private RenderTarget2D DirectionalRenderTarget { get; } - private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } /* FIXME: these next two dont actually have anything to do with PBR */ @@ -30,125 +22,24 @@ namespace Kav private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } private Deferred_ToonEffect Deferred_ToonEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } + private SimpleDepthEffectInstanced SimpleDepthEffectInstanced { get; } private LinearDepthEffect LinearDepthEffect { get; } private Effect ToneMapEffect { get; } private SkyboxEffect SkyboxEffect { get; } private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { get; } - private RenderTarget2D gPosition { get; } - private RenderTarget2D gNormal { get; } - private RenderTarget2D gAlbedo { get; } - private RenderTarget2D gMetallicRoughness { get; } - private RenderTargetCube PointShadowCubeMap { get; } - - private RenderTargetBinding[] GBuffer { get; } - private Kav.Model UnitCube { get; } - private SpriteBatch SpriteBatch { get; } + private DynamicVertexBuffer PositionInstanceVertexBuffer { get; } + private readonly PositionInstanceVertex[] PositionInstanceVertices = new PositionInstanceVertex[MAX_INSTANCE_VERTEX_COUNT]; public Renderer( - GraphicsDevice graphicsDevice, - int renderDimensionsX, - int renderDimensionsY, - int numShadowCascades, - int shadowMapSize + GraphicsDevice graphicsDevice ) { GraphicsDevice = graphicsDevice; - RenderDimensionsX = renderDimensionsX; - RenderDimensionsY = renderDimensionsY; - - ShadowMapSize = shadowMapSize; - - NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); - ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; - - for (var i = 0; i < numShadowCascades; i++) - { - ShadowRenderTargets[i] = new RenderTarget2D( - GraphicsDevice, - ShadowMapSize, - ShadowMapSize, - false, - SurfaceFormat.Single, - DepthFormat.Depth24 - ); - } - - ColorRenderTarget = new RenderTarget2D( - graphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Color, - DepthFormat.Depth24, - 0, - RenderTargetUsage.PreserveContents - ); - - DirectionalRenderTarget = new RenderTarget2D( - graphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Color, - DepthFormat.None, - 0, - RenderTargetUsage.PreserveContents - ); - - gPosition = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Vector4, - DepthFormat.Depth24 - ); - - gNormal = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Vector4, - DepthFormat.None - ); - - gAlbedo = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Color, - DepthFormat.None - ); - - gMetallicRoughness = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.HalfVector2, - DepthFormat.None - ); - - GBuffer = new RenderTargetBinding[4] { - new RenderTargetBinding(gPosition), - new RenderTargetBinding(gNormal), - new RenderTargetBinding(gAlbedo), - new RenderTargetBinding(gMetallicRoughness) - }; - - PointShadowCubeMap = new RenderTargetCube( - GraphicsDevice, - shadowMapSize, - false, - SurfaceFormat.Single, - DepthFormat.Depth24 - ); SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); + SimpleDepthEffectInstanced = new SimpleDepthEffectInstanced(GraphicsDevice); LinearDepthEffect = new LinearDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); @@ -156,7 +47,6 @@ namespace Kav DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice); DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); - DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); SkyboxEffect = new SkyboxEffect(GraphicsDevice); @@ -174,77 +64,39 @@ namespace Kav Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel)) ); - SpriteBatch = new SpriteBatch(graphicsDevice); + PositionInstanceVertexBuffer = new DynamicVertexBuffer( + GraphicsDevice, + VertexDeclarations.PositionInstanceDeclaration, + MAX_INSTANCE_VERTEX_COUNT, + BufferUsage.WriteOnly + ); } - public void DeferredRender( - RenderTarget2D renderTarget, - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - AmbientLight ambientLight, - IEnumerable pointLights, - DirectionalLight? directionalLight + public static RenderTargetCube CreateShadowCubeMap( + GraphicsDevice graphicsDevice, + int shadowMapSize ) { - GBufferRender(camera, modelTransforms); - - GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.Clear(Color.Black); - - AmbientLightRender(ambientLight); - - DeferredPointLightEffect.EyePosition = camera.Position; - - foreach (var pointLight in pointLights) - { - PointLightRender(camera, modelTransforms, pointLight); - } - - if (directionalLight.HasValue) - { - DirectionalLightRender(camera, modelTransforms, directionalLight.Value); - } - - GraphicsDevice.SetRenderTarget(renderTarget); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect); - SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); - SpriteBatch.End(); + return new RenderTargetCube( + graphicsDevice, + shadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24, + 0, + RenderTargetUsage.PreserveContents + ); } - public void DeferredToonRender( - RenderTarget2D renderTarget, - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - AmbientLight ambientLight, - IEnumerable pointLights, - DirectionalLight? directionalLight, - TextureCube skybox + public static DirectionalShadowMapData CreateDirectionalShadowMaps( + GraphicsDevice graphicsDevice, + int shadowMapSize, + int numCascades ) { - GBufferRender(camera, modelTransforms); - - GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - - DepthRender(camera, modelTransforms); - GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; - - AmbientLightRender(ambientLight); - foreach (var pointLight in pointLights) - { - PointLightRender(camera, modelTransforms, pointLight); - } - - if (directionalLight.HasValue) - { - DirectionalLightToonRender(camera, modelTransforms, directionalLight.Value); - } - - SkyboxRender(camera, skybox); - - GraphicsDevice.SetRenderTarget(renderTarget); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null); - SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); - SpriteBatch.End(); + return new DirectionalShadowMapData( + graphicsDevice, + shadowMapSize, + numCascades + ); } // TODO: we could make this a lot more efficient probably @@ -252,18 +104,12 @@ namespace Kav public void MeshSpriteRender( RenderTarget2D renderTarget, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable<(MeshSprite, Matrix)> meshSpriteTransforms, AmbientLight ambientLight, IEnumerable pointLights, DirectionalLight? directionalLight ) { - GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - - DepthRender(camera, modelTransforms); - GraphicsDevice.Clear(ClearOptions.Target, new Color(0, 0, 0, 0), 1f, 0); + GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.RasterizerState = RasterizerState.CullNone; @@ -348,143 +194,115 @@ namespace Kav ); } } - - GraphicsDevice.SetRenderTarget(renderTarget); - GraphicsDevice.Clear(new Color(0, 0, 0, 0)); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null); - SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); - SpriteBatch.End(); } - private void DepthRender( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms - ) { - var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection); - - foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms)) + // Renders a series of drawable-transform pairs using an effect that has a World matrix. + // Effect must be pre-configured!! + public static void CullAndRenderIndexed( + GraphicsDevice graphicsDevice, + BoundingFrustum boundingFrustum, + IEnumerable<(T, Matrix)> drawableTransformPairs, + U effect + ) where T : IIndexDrawable, ICullable where U : Effect, IHasWorldMatrix + { + foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransformPairs)) { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - SimpleDepthEffect.Model = transform; - SimpleDepthEffect.View = camera.View; - SimpleDepthEffect.Projection = camera.Projection; + effect.World = transform; - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; - - foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) - { - pass.Apply(); - - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } + RenderIndexed( + graphicsDevice, + drawable, + effect + ); } } - private void SkyboxRender( - PerspectiveCamera camera, - TextureCube skybox - ) { - GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace; - SkyboxEffect.Skybox = skybox; + public static void RenderIndexed( + GraphicsDevice graphicsDevice, + T drawable, + U effect + ) where T : IIndexDrawable where U : Effect + { + graphicsDevice.SetVertexBuffer(drawable.VertexBuffer); + graphicsDevice.Indices = drawable.IndexBuffer; - var view = camera.View; - view.Translation = Vector3.Zero; - SkyboxEffect.View = view; - - SkyboxEffect.Projection = camera.Projection; - - GraphicsDevice.SetVertexBuffer(UnitCube.Meshes[0].MeshParts[0].VertexBuffer); - GraphicsDevice.Indices = UnitCube.Meshes[0].MeshParts[0].IndexBuffer; - - foreach (var pass in SkyboxEffect.CurrentTechnique.Passes) + foreach (var pass in effect.CurrentTechnique.Passes) { pass.Apply(); - GraphicsDevice.DrawIndexedPrimitives( + graphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, - UnitCube.Meshes[0].MeshParts[0].VertexBuffer.VertexCount, + drawable.VertexBuffer.VertexCount, 0, - UnitCube.Meshes[0].MeshParts[0].Triangles.Length + drawable.IndexBuffer.IndexCount / 3 ); } - GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace; } - private void GBufferRender( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms - ) { - GraphicsDevice.SetRenderTargets(GBuffer); - GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - GraphicsDevice.BlendState = BlendState.Opaque; - - var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection); - - foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms)) + public static int FillAndSetBuffersForInstancing( + GraphicsDevice graphicsDevice, + BoundingFrustum boundingFrustum, + T drawable, + IEnumerable transforms, + V[] vertexData, + DynamicVertexBuffer dynamicVertexBuffer + ) where T : ICullable, IIndexDrawable where V : struct, IVertexType, IHasTranslation + { + int numInstances = 0; + foreach (var transform in FrustumCull(boundingFrustum, drawable, transforms)) { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - Deferred_GBufferEffect.World = transform; - Deferred_GBufferEffect.View = camera.View; - Deferred_GBufferEffect.Projection = camera.Projection; + vertexData[numInstances].Translation = transform.Translation; + numInstances += 1; + } - Deferred_GBufferEffect.Albedo = meshPart.Albedo; - Deferred_GBufferEffect.Metallic = meshPart.Metallic; - Deferred_GBufferEffect.Roughness = meshPart.Roughness; + if (numInstances == 0) { return 0; } - Deferred_GBufferEffect.AlbedoTexture = meshPart.AlbedoTexture; - Deferred_GBufferEffect.NormalTexture = meshPart.NormalTexture; - Deferred_GBufferEffect.MetallicRoughnessTexture = meshPart.MetallicRoughnessTexture; + dynamicVertexBuffer.SetData( + vertexData, + 0, + numInstances, + SetDataOptions.Discard + ); - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; + graphicsDevice.SetVertexBuffers( + drawable.VertexBuffer, + new VertexBufferBinding(dynamicVertexBuffer, 0, 1) + ); + graphicsDevice.Indices = drawable.IndexBuffer; - foreach (var pass in Deferred_GBufferEffect.CurrentTechnique.Passes) - { - pass.Apply(); + return numInstances; + } - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } + public static void RenderInstanced( + GraphicsDevice graphicsDevice, + T drawable, + U effect, + int numInstances + ) where T : ICullable, IIndexDrawable where U : Effect + { + foreach (var pass in effect.CurrentTechnique.Passes) + { + pass.Apply(); + + graphicsDevice.DrawInstancedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + drawable.VertexBuffer.VertexCount, + 0, + drawable.IndexBuffer.IndexCount / 3, + numInstances + ); } } - private void AmbientLightRender(AmbientLight ambientLight) - { - GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.BlendState = BlendState.Opaque; - - DeferredAmbientLightEffect.GPosition = gPosition; - DeferredAmbientLightEffect.GAlbedo = gAlbedo; - DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3(); - - foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes) + // TODO: can probably make this static somehow + public void RenderFullscreenEffect( + Effect effect + ) { + foreach (var pass in effect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.SetVertexBuffer(FullscreenTriangle); @@ -492,14 +310,170 @@ namespace Kav } } - private void PointLightRender( + public void RenderDepthIndexed( + RenderTarget2D renderTarget, + PerspectiveCamera camera, + IEnumerable<(T, Matrix)> drawableTransforms + ) where T : ICullable, IIndexDrawable + { + GraphicsDevice.SetRenderTarget(renderTarget); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + + SimpleDepthEffect.View = camera.View; + SimpleDepthEffect.Projection = camera.Projection; + + CullAndRenderIndexed( + GraphicsDevice, + new BoundingFrustum(camera.View * camera.Projection), + drawableTransforms, + SimpleDepthEffect + ); + } + + public void RenderSkybox( + RenderTarget2D renderTarget, + PerspectiveCamera camera, + TextureCube skybox + ) { + GraphicsDevice.SetRenderTarget(renderTarget); + GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace; + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + + SkyboxEffect.Skybox = skybox; + + var view = camera.View; + view.Translation = Vector3.Zero; + + SkyboxEffect.View = view; + SkyboxEffect.Projection = camera.Projection; + + RenderIndexed( + GraphicsDevice, + UnitCube.Meshes[0].MeshParts[0], + SkyboxEffect + ); + + GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace; + } + + /// + /// GBuffer binding must have 4 render targets. + /// + public void RenderGBufferInstanced( + RenderTargetBinding[] gBuffer, + RenderTarget2D depthBuffer, + PerspectiveCamera camera, + T drawable, + IEnumerable transforms + ) where T : ICullable, IIndexDrawable, IGBufferDrawable { + GraphicsDevice.SetRenderTargets(gBuffer); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + Deferred_GBufferEffect.HardwareInstancingEnabled = true; + + Deferred_GBufferEffect.Albedo = drawable.Albedo; + Deferred_GBufferEffect.Metallic = drawable.Metallic; + Deferred_GBufferEffect.Roughness = drawable.Roughness; + + Deferred_GBufferEffect.AlbedoTexture = drawable.AlbedoTexture; + Deferred_GBufferEffect.NormalTexture = drawable.NormalTexture; + Deferred_GBufferEffect.MetallicRoughnessTexture = drawable.MetallicRoughnessTexture; + + Deferred_GBufferEffect.View = camera.View; + Deferred_GBufferEffect.Projection = camera.Projection; + + var numInstances = FillAndSetBuffersForInstancing( + GraphicsDevice, + new BoundingFrustum(camera.View * camera.Projection), + drawable, + transforms, + PositionInstanceVertices, + PositionInstanceVertexBuffer + ); + + RenderInstanced( + GraphicsDevice, + drawable, + Deferred_GBufferEffect, + numInstances + ); + + // re-render to get depth + GraphicsDevice.SetRenderTargets(depthBuffer); + + SimpleDepthEffectInstanced.View = camera.View; + SimpleDepthEffectInstanced.Projection = camera.Projection; + + RenderInstanced( + GraphicsDevice, + drawable, + SimpleDepthEffectInstanced, + numInstances + ); + } + + public void RenderGBufferIndexed( + RenderTargetBinding[] gBuffer, + PerspectiveCamera camera, + IEnumerable<(T, Matrix)> drawableTransforms + ) where T : ICullable, IIndexDrawable, IGBufferDrawable { + GraphicsDevice.SetRenderTargets(gBuffer); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + Deferred_GBufferEffect.HardwareInstancingEnabled = false; + Deferred_GBufferEffect.View = camera.View; + Deferred_GBufferEffect.Projection = camera.Projection; + + var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection); + + foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransforms)) + { + Deferred_GBufferEffect.World = transform; + + Deferred_GBufferEffect.HardwareInstancingEnabled = false; + + Deferred_GBufferEffect.Albedo = drawable.Albedo; + Deferred_GBufferEffect.Metallic = drawable.Metallic; + Deferred_GBufferEffect.Roughness = drawable.Roughness; + + Deferred_GBufferEffect.AlbedoTexture = drawable.AlbedoTexture; + Deferred_GBufferEffect.NormalTexture = drawable.NormalTexture; + Deferred_GBufferEffect.MetallicRoughnessTexture = drawable.MetallicRoughnessTexture; + + RenderIndexed(GraphicsDevice, drawable, Deferred_GBufferEffect); + } + } + + public void RenderAmbientLight( + RenderTarget2D renderTarget, + Texture2D gPosition, + Texture2D gAlbedo, + AmbientLight ambientLight + ) { + GraphicsDevice.SetRenderTarget(renderTarget); + GraphicsDevice.BlendState = BlendState.Opaque; + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + + DeferredAmbientLightEffect.GPosition = gPosition; + DeferredAmbientLightEffect.GAlbedo = gAlbedo; + DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3(); + + RenderFullscreenEffect(DeferredAmbientLightEffect); + } + + public void RenderPointLight( + RenderTarget2D renderTarget, + Texture2D gPosition, + Texture2D gAlbedo, + Texture2D gNormal, + Texture2D gMetallicRoughness, + TextureCube shadowMap, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, PointLight pointLight ) { - RenderPointShadows(camera, modelTransforms, pointLight); - - GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; @@ -507,7 +481,9 @@ namespace Kav DeferredPointLightEffect.GAlbedo = gAlbedo; DeferredPointLightEffect.GNormal = gNormal; DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness; - DeferredPointLightEffect.ShadowMap = PointShadowCubeMap; + DeferredPointLightEffect.ShadowMap = shadowMap; + + DeferredPointLightEffect.EyePosition = camera.Position; DeferredPointLightEffect.PointLightPosition = pointLight.Position; DeferredPointLightEffect.PointLightColor = @@ -515,38 +491,51 @@ namespace Kav DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value - foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes) - { - pass.Apply(); - GraphicsDevice.SetVertexBuffer(FullscreenTriangle); - GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); - } + RenderFullscreenEffect(DeferredPointLightEffect); } - private void DirectionalLightRender( + public void RenderDirectionalLight( + RenderTarget2D renderTarget, + Texture2D gPosition, + Texture2D gAlbedo, + Texture2D gNormal, + Texture2D gMetallicRoughness, + DirectionalShadowMapData shadowMapData, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); + GraphicsDevice.SetRenderTarget(renderTarget); + GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + GraphicsDevice.BlendState = BlendState.Additive; DeferredDirectionalLightEffect.GPosition = gPosition; DeferredDirectionalLightEffect.GAlbedo = gAlbedo; DeferredDirectionalLightEffect.GNormal = gNormal; DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness; - DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0]; - if (NumShadowCascades > 1) + DeferredDirectionalLightEffect.ShadowMapSize = shadowMapData.ShadowMapSize; + + DeferredDirectionalLightEffect.ShadowMapOne = shadowMapData.ShadowMaps[0]; + DeferredDirectionalLightEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceViews[0] * shadowMapData.LightSpaceProjections[0]; + DeferredDirectionalLightEffect.CascadeFarPlanes[0] = shadowMapData.CascadeFarPlanes[0]; + + if (shadowMapData.NumShadowCascades > 1) { - DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1]; + DeferredDirectionalLightEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1]; + DeferredDirectionalLightEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[1]; + DeferredDirectionalLightEffect.CascadeFarPlanes[1] = shadowMapData.CascadeFarPlanes[1]; } - if (NumShadowCascades > 2) + if (shadowMapData.NumShadowCascades > 2) { - DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2]; + DeferredDirectionalLightEffect.ShadowMapThree = shadowMapData.ShadowMaps[2]; + DeferredDirectionalLightEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceViews[2] * shadowMapData.LightSpaceProjections[2]; + DeferredDirectionalLightEffect.CascadeFarPlanes[2] = shadowMapData.CascadeFarPlanes[2]; } - if (NumShadowCascades > 3) + if (shadowMapData.NumShadowCascades > 3) { - DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3]; + DeferredDirectionalLightEffect.ShadowMapFour = shadowMapData.ShadowMaps[3]; + DeferredDirectionalLightEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceViews[3] * shadowMapData.LightSpaceProjections[3]; + DeferredDirectionalLightEffect.CascadeFarPlanes[3] = shadowMapData.CascadeFarPlanes[3]; } DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction; @@ -556,25 +545,21 @@ namespace Kav DeferredDirectionalLightEffect.ViewMatrix = camera.View; DeferredDirectionalLightEffect.EyePosition = camera.Position; - GraphicsDevice.SetRenderTarget(ColorRenderTarget); - GraphicsDevice.BlendState = BlendState.Additive; - - foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes) - { - pass.Apply(); - GraphicsDevice.SetVertexBuffer(FullscreenTriangle); - GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); - } + RenderFullscreenEffect(DeferredDirectionalLightEffect); } - private void DirectionalLightToonRender( + public void RenderDirectionalLightToon( + RenderTarget2D renderTarget, + Texture2D gPosition, + Texture2D gAlbedo, + Texture2D gNormal, + Texture2D gMetallicRoughness, + DirectionalShadowMapData shadowMapData, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight + DirectionalLight directionalLight, + bool ditheredShadows ) { - RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); - - GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; @@ -583,7 +568,7 @@ namespace Kav Deferred_ToonEffect.GNormal = gNormal; Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness; - Deferred_ToonEffect.DitheredShadows = false; + Deferred_ToonEffect.DitheredShadows = ditheredShadows; Deferred_ToonEffect.EyePosition = camera.Position; @@ -591,41 +576,39 @@ namespace Kav Deferred_ToonEffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; - Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0]; - if (NumShadowCascades > 1) + Deferred_ToonEffect.ShadowMapOne = shadowMapData.ShadowMaps[0]; + Deferred_ToonEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceViews[0] * shadowMapData.LightSpaceProjections[0]; + + if (shadowMapData.NumShadowCascades > 1) { - Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1]; + Deferred_ToonEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1]; + Deferred_ToonEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[1]; } - if (NumShadowCascades > 2) + if (shadowMapData.NumShadowCascades > 2) { - Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2]; + Deferred_ToonEffect.ShadowMapThree = shadowMapData.ShadowMaps[2]; + Deferred_ToonEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[2]; } - if (NumShadowCascades > 3) + if (shadowMapData.NumShadowCascades > 3) { - Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3]; + Deferred_ToonEffect.ShadowMapFour = shadowMapData.ShadowMaps[3]; + Deferred_ToonEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceViews[2] * shadowMapData.LightSpaceProjections[3]; } Deferred_ToonEffect.ViewMatrix = camera.View; - foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) - { - pass.Apply(); - GraphicsDevice.SetVertexBuffer(FullscreenTriangle); - GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); - } + RenderFullscreenEffect(Deferred_ToonEffect); } - private void RenderDirectionalShadows( + public void PrepareDirectionalShadowData( + DirectionalShadowMapData shadowMapData, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight, - ShadowCascadeEffect effect + DirectionalLight directionalLight ) { - // render the individual shadow cascades var previousFarPlane = camera.NearPlane; - for (var i = 0; i < NumShadowCascades; i++) + for (var i = 0; i < shadowMapData.NumShadowCascades; i++) { - var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f)); + var farPlane = camera.FarPlane / (MathHelper.Max((shadowMapData.NumShadowCascades - i - 1) * 2f, 1f)); // divide the view frustum var shadowCamera = new PerspectiveCamera( @@ -638,27 +621,25 @@ namespace Kav farPlane ); - // TODO: This is tightly coupled to the effect and it sucks - RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i); + PrepareDirectionalShadowCascade( + shadowMapData, + i, + shadowCamera, + directionalLight + ); - effect.CascadeFarPlanes[i] = farPlane; + shadowMapData.CascadeFarPlanes[i] = farPlane; previousFarPlane = farPlane; } } - private void RenderDirectionalShadowMap( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight, - ShadowCascadeEffect effect, - int shadowCascadeIndex + private void PrepareDirectionalShadowCascade( + DirectionalShadowMapData shadowMapData, + int shadowCascadeIndex, + PerspectiveCamera shadowCamera, + DirectionalLight directionalLight ) { - GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); - GraphicsDevice.Clear(Color.White); - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - GraphicsDevice.BlendState = BlendState.Opaque; - - var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); + var cameraBoundingFrustum = new BoundingFrustum(shadowCamera.View * shadowCamera.Projection); Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); Vector3 frustumCenter = Vector3.Zero; @@ -677,8 +658,8 @@ namespace Kav BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); - SimpleDepthEffect.View = lightView; - SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( + shadowMapData.LightSpaceViews[shadowCascadeIndex] = lightView; + shadowMapData.LightSpaceProjections[shadowCascadeIndex] = Matrix.CreateOrthographicOffCenter( lightBox.Min.X, lightBox.Max.X, lightBox.Min.Y, @@ -686,77 +667,104 @@ namespace Kav -lightBox.Max.Z - 10f, // TODO: near clip plane needs scene AABB info to get rid of this magic value -lightBox.Min.Z ); + } - var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; - - if (shadowCascadeIndex == 0) + public void RenderDirectionalShadowsIndexed( + DirectionalShadowMapData shadowMapData, + IEnumerable<(T, Matrix)> drawableTransforms + ) where T : ICullable, IIndexDrawable { + // render the individual shadow cascades + for (var i = 0; i < shadowMapData.NumShadowCascades; i++) { - effect.LightSpaceMatrixOne = lightSpaceMatrix; - } - else if (shadowCascadeIndex == 1) - { - effect.LightSpaceMatrixTwo = lightSpaceMatrix; - } - else if (shadowCascadeIndex == 2) - { - effect.LightSpaceMatrixThree = lightSpaceMatrix; - } - else if (shadowCascadeIndex == 3) - { - effect.LightSpaceMatrixFour = lightSpaceMatrix; - } - - var boundingFrustum = new BoundingFrustum(lightSpaceMatrix); - - foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms)) - { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; - - SimpleDepthEffect.Model = transform; - - foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) - { - pass.Apply(); - - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } + RenderDirectionalShadowMapIndexed( + shadowMapData, + i, + drawableTransforms + ); } } - private void RenderPointShadows( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - PointLight pointLight - ) { + private void RenderDirectionalShadowMapIndexed( + DirectionalShadowMapData shadowMapData, + int shadowCascadeIndex, + IEnumerable<(T, Matrix)> drawableTransforms + ) where T : ICullable, IIndexDrawable { + GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; - LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView( - MathHelper.PiOver2, - 1, - 0.1f, - 25f // FIXME: magic value + SimpleDepthEffect.View = shadowMapData.LightSpaceViews[shadowCascadeIndex]; + SimpleDepthEffect.Projection = shadowMapData.LightSpaceProjections[shadowCascadeIndex]; + + CullAndRenderIndexed( + GraphicsDevice, + new BoundingFrustum(SimpleDepthEffect.View * SimpleDepthEffect.Projection), + drawableTransforms, + SimpleDepthEffect ); - LinearDepthEffect.FarPlane = 25f; - LinearDepthEffect.LightPosition = pointLight.Position; + } + + public void RenderDirectionalShadowsInstanced( + DirectionalShadowMapData shadowMapData, + T drawable, + IEnumerable transforms + ) where T : ICullable, IIndexDrawable + { + // render the individual shadow cascades + for (var i = 0; i < shadowMapData.NumShadowCascades; i++) + { + RenderDirectionalShadowMapInstanced( + shadowMapData, + i, + drawable, + transforms + ); + } + } + + private void RenderDirectionalShadowMapInstanced( + DirectionalShadowMapData shadowMapData, + int shadowCascadeIndex, + T drawable, + IEnumerable transforms + ) where T : ICullable, IIndexDrawable + { + GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + SimpleDepthEffectInstanced.View = shadowMapData.LightSpaceViews[shadowCascadeIndex]; + SimpleDepthEffectInstanced.Projection = shadowMapData.LightSpaceProjections[shadowCascadeIndex]; + + var numInstances = FillAndSetBuffersForInstancing( + GraphicsDevice, + new BoundingFrustum(SimpleDepthEffectInstanced.View * SimpleDepthEffectInstanced.Projection), + drawable, + transforms, + PositionInstanceVertices, + PositionInstanceVertexBuffer + ); + + RenderInstanced( + GraphicsDevice, + drawable, + SimpleDepthEffectInstanced, + numInstances + ); + } + + public void RenderPointShadowMapIndexed( + RenderTargetCube pointShadowCubeMap, + PerspectiveCamera camera, + IEnumerable<(T, Matrix)> modelTransforms, + PointLight pointLight + ) where T : ICullable, IIndexDrawable { + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace))) { - GraphicsDevice.SetRenderTarget(PointShadowCubeMap, face); + GraphicsDevice.SetRenderTarget(pointShadowCubeMap, face); Vector3 targetDirection; Vector3 targetUpDirection; @@ -803,36 +811,110 @@ namespace Kav pointLight.Position + targetDirection, targetUpDirection ); + LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView( + MathHelper.PiOver2, + 1, + 0.1f, + 25f // FIXME: magic value + ); + LinearDepthEffect.FarPlane = 25f; - var boundingFrustum = new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection); + LinearDepthEffect.LightPosition = pointLight.Position; - foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms)) + CullAndRenderIndexed( + GraphicsDevice, + new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection), + modelTransforms, + LinearDepthEffect + ); + } + } + + public void RenderPointShadowMapInstanced( + RenderTargetCube pointShadowCubeMap, + PerspectiveCamera camera, + T drawable, + IEnumerable modelTransforms, + PointLight pointLight + ) where T : ICullable, IIndexDrawable + { + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + + foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace))) + { + GraphicsDevice.SetRenderTarget(pointShadowCubeMap, face); + + Vector3 targetDirection; + Vector3 targetUpDirection; + switch(face) { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; + case CubeMapFace.PositiveX: + targetDirection = Vector3.Right; + targetUpDirection = Vector3.Up; + break; - LinearDepthEffect.Model = transform; + case CubeMapFace.NegativeX: + targetDirection = Vector3.Left; + targetUpDirection = Vector3.Up; + break; - foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes) - { - pass.Apply(); + case CubeMapFace.PositiveY: + targetDirection = Vector3.Up; + targetUpDirection = Vector3.Forward; + break; - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } + case CubeMapFace.NegativeY: + targetDirection = Vector3.Down; + targetUpDirection = Vector3.Backward; + break; + + case CubeMapFace.PositiveZ: + targetDirection = Vector3.Backward; + targetUpDirection = Vector3.Up; + break; + + case CubeMapFace.NegativeZ: + targetDirection = Vector3.Forward; + targetUpDirection = Vector3.Up; + break; + + default: + targetDirection = Vector3.Right; + targetUpDirection = Vector3.Up; + break; } + + LinearDepthEffect.View = Matrix.CreateLookAt( + pointLight.Position, + pointLight.Position + targetDirection, + targetUpDirection + ); + LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView( + MathHelper.PiOver2, + 1, + 0.1f, + 25f // FIXME: magic value + ); + LinearDepthEffect.FarPlane = 25f; + + LinearDepthEffect.LightPosition = pointLight.Position; + + // TODO: set up instancing + // var numInstances = FillAndSetBuffersForInstancing( + // GraphicsDevice, + // camera, + // drawable, + // transforms, + // VertexPos + // ); + + // RenderInstanced( + // GraphicsDevice, + // camera, + // modelTransforms, + // LinearDepthEffect + // ); } } @@ -851,6 +933,23 @@ namespace Kav } } + private static IEnumerable FrustumCull( + BoundingFrustum boundingFrustum, + T cullable, + IEnumerable transforms + ) where T : ICullable + { + foreach (var transform in transforms) + { + var boundingBox = TransformedBoundingBox(cullable.BoundingBox, transform); + var containment = boundingFrustum.Contains(boundingBox); + if (containment != ContainmentType.Disjoint) + { + yield return transform; + } + } + } + private static BoundingBox TransformedBoundingBox(BoundingBox boundingBox, Matrix matrix) { var center = (boundingBox.Min + boundingBox.Max) / 2f; diff --git a/Resources.cs b/Resources.cs index 44c28f7..2924998 100644 --- a/Resources.cs +++ b/Resources.cs @@ -111,6 +111,18 @@ namespace Kav } } + public static byte[] SimpleDepthEffectInstanced + { + get + { + if (simpleDepthEffectInstanced == null) + { + simpleDepthEffectInstanced = GetResource("SimpleDepthEffectInstanced.fxb"); + } + return simpleDepthEffectInstanced; + } + } + public static byte[] LinearDepthEffect { get @@ -168,6 +180,7 @@ namespace Kav private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect; + private static byte[] simpleDepthEffectInstanced; private static byte[] linearDepthEffect; private static byte[] skyboxEffect; private static byte[] diffuseLitSpriteEffect; diff --git a/VertexDeclarations.cs b/VertexDeclarations.cs new file mode 100644 index 0000000..f8b073f --- /dev/null +++ b/VertexDeclarations.cs @@ -0,0 +1,12 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public static class VertexDeclarations + { + public static VertexDeclaration PositionInstanceDeclaration = new VertexDeclaration + ( + new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2) + ); + } +} diff --git a/Vertices/PositionInstanceVertex.cs b/Vertices/PositionInstanceVertex.cs new file mode 100644 index 0000000..f173cef --- /dev/null +++ b/Vertices/PositionInstanceVertex.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct PositionInstanceVertex : IVertexType, IHasTranslation + { + VertexDeclaration IVertexType.VertexDeclaration + { + get + { + return VertexDeclarations.PositionInstanceDeclaration; + } + } + + public Vector3 Translation { get; set; } + + public static readonly VertexDeclaration VertexDeclaration; + + public PositionInstanceVertex( + Vector3 translation + ) { + Translation = translation; + } + } +}