Compare commits

..

26 Commits

Author SHA1 Message Date
cosmonaut f5cdd0b566 fix crash on 0 instance 2020-12-14 00:52:40 -08:00
cosmonaut 808d50ccc2 more sprite mesh rendering 2020-12-13 22:03:06 -08:00
cosmonaut d537993530 flat sprite lighting in deferred pipeline 2020-12-13 22:03:06 -08:00
cosmonaut 3338bf3b06 experimenting with g buffer sprites 2020-12-13 22:03:06 -08:00
cosmonaut 16ecb5afa6 fix bad palette crush FLT_MAX 2020-12-12 21:07:22 -08:00
cosmonaut 51d7f3e772 fix diffuse lit effect not discarding transparency 2020-12-12 01:23:08 -08:00
cosmonaut 3a0aa8894d draw zero alpha pixels in g buffer 2020-12-12 00:17:10 -08:00
cosmonaut c468d6276d texture atlas stuff 2020-12-11 21:05:54 -08:00
cosmonaut d476f254e4 fix sprite mesh normals 2020-12-11 18:52:05 -08:00
cosmonaut 2ff5cb1ca5 decouple sprite mesh from textures 2020-12-11 18:22:54 -08:00
cosmonaut 40ca0402d6 instance data struct 2020-12-10 17:38:18 -08:00
cosmonaut 0ebad486c5 diffuse sprite effect takes uv data 2020-12-10 13:53:55 -08:00
cosmonaut 8ff6e26887 tweak to mesh sprite draws 2020-12-10 00:34:06 -08:00
cosmonaut 7f0bf47f07 implement vertical mesh sprite flipping 2020-12-09 22:31:09 -08:00
cosmonaut 2f589e584d allow sprite flipping on meshsprite 2020-12-09 22:28:51 -08:00
cosmonaut 7964d8a171 store right vector on the camera 2020-12-09 17:47:54 -08:00
cosmonaut d5d0a38ff1 turn point lights into classes and give them a shadow map 2020-12-09 17:10:41 -08:00
cosmonaut ee909ba94e tweak point light rendering 2020-12-09 16:28:40 -08:00
cosmonaut 0060c22d72 point light sphere optimization 2020-12-09 16:10:53 -08:00
cosmonaut 2c113ed642 palette crush effect 2020-12-09 12:44:53 -08:00
cosmonaut b1395babfe offset texture rendering 2020-12-08 22:20:54 -08:00
cosmonaut ef948cf7fb add quaternion utility function 2020-12-08 17:20:10 -08:00
cosmonaut 9a7fb75ec5 refactor for reusing instance buffers 2020-12-08 16:37:22 -08:00
cosmonaut 0c576668cb add instanced linear depth effect + fix platforms 2020-12-08 16:02:58 -08:00
cosmonaut 367e2795ae decouple billboard constraints from meshsprite 2020-12-08 15:29:28 -08:00
cosmonaut 1723f1dff8 change simpledeptheffect to use texcoord0 2020-12-08 13:33:45 -08:00
44 changed files with 1267 additions and 422 deletions

View File

@ -10,6 +10,7 @@ namespace Kav
public Vector3 Position { get; }
public Vector3 Forward { get; }
public Vector3 Up { get; }
public Vector3 Right { get; }
public float FieldOfView { get; }
public float AspectRatio { get; }
@ -28,6 +29,7 @@ namespace Kav
Position = position;
Forward = forward;
Up = up;
Right = Vector3.Cross(forward, up);
View = Matrix.CreateLookAt(Position, Position + Forward, Up);
FieldOfView = fieldOfView;

14
Data/AtlasAnimation.cs Normal file
View File

@ -0,0 +1,14 @@
namespace Kav
{
public struct AtlasAnimation
{
public UVData[] Frames { get; }
public int Framerate { get; }
public AtlasAnimation(UVData[] frames, int framerate)
{
Frames = frames;
Framerate = framerate;
}
}
}

63
Data/InstanceData.cs Normal file
View File

@ -0,0 +1,63 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class InstanceData<T> where T : struct, IVertexType
{
public T[] InstanceDataArray { get; }
public DynamicVertexBuffer VertexBuffer { get; }
public int InstanceCount { get; private set; }
public InstanceData(GraphicsDevice graphicsDevice, int size)
{
InstanceDataArray = new T[size];
VertexBuffer = new DynamicVertexBuffer(
graphicsDevice,
typeof(T),
size,
BufferUsage.WriteOnly
);
InstanceCount = 0;
}
public void AddAndSetData(IEnumerable<T> data)
{
InstanceCount = 0;
foreach (var datum in data)
{
AddData(datum);
}
if (InstanceCount == 0) { throw new System.Exception(); }
SetData();
}
public void AddData(T datum)
{
InstanceDataArray[InstanceCount] = datum;
InstanceCount += 1;
}
public void SetData()
{
if (InstanceCount > 0)
{
VertexBuffer.SetData(
InstanceDataArray,
0,
InstanceCount,
SetDataOptions.NoOverwrite
);
}
}
public void Clear()
{
InstanceCount = 0;
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav.Data
{
public struct MeshSpriteDrawData : IIndexDrawable, ICullable, ITransformable
{
public SpriteMesh MeshSprite { get; }
public Texture2D Texture { get; }
public Texture2D Normal { get; }
public SpriteBillboardConstraint BillboardConstraint { get; }
public Matrix TransformMatrix { get; }
public UVData UVOffset { get; }
public IndexBuffer IndexBuffer => MeshSprite.IndexBuffer;
public VertexBuffer VertexBuffer => MeshSprite.VertexBuffer;
public BoundingBox BoundingBox => MeshSprite.BoundingBox;
public MeshSpriteDrawData(
SpriteMesh meshSprite,
Texture2D texture,
Texture2D normal,
SpriteBillboardConstraint billboardConstraint,
Matrix transformMatrix,
UVData offset
) {
MeshSprite = meshSprite;
Texture = texture;
Normal = normal;
BillboardConstraint = billboardConstraint;
TransformMatrix = transformMatrix;
UVOffset = offset;
}
}
}

96
Data/TextureAtlas.cs Normal file
View File

@ -0,0 +1,96 @@
using Kav.Data;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.IO;
namespace Kav
{
public struct TextureAtlasSlice
{
public int X { get; }
public int Y { get; }
public int W { get; }
public int H { get; }
public UVData UVData { get; }
// for use with tiling
public Vector2 TiledUVOffset { get; }
public TextureAtlasSlice(int x, int y, int w, int h, int totalW, int totalH)
{
X = x;
Y = y;
W = w;
H = h;
UVData = new UVData(new Vector2(x, y), new Vector2(w, h), new Vector2(totalW, totalH));
TiledUVOffset = new Vector2(x / (float)totalW, y / (float)totalH);
}
}
public class TextureAtlas
{
public Texture2D Texture { get; }
protected List<string> Names { get; } = new List<string>();
protected Dictionary<string, TextureAtlasSlice> Slices { get; } = new Dictionary<string, TextureAtlasSlice>();
public TextureAtlas(GraphicsDevice graphicsDevice, FileInfo atlasMetadataFile)
{
var atlasData = CrunchAtlasReader.ReadTextureAtlas(atlasMetadataFile);
var textureData = atlasData.Textures[0];
using var stream = File.OpenRead(Path.Combine(atlasMetadataFile.DirectoryName, textureData.Name + ".png"));
Texture = Texture2D.FromStream(graphicsDevice, stream);
foreach (var slice in textureData.Images)
{
Names.Add(slice.N);
Slices.Add(
slice.N,
new TextureAtlasSlice(
slice.X,
slice.Y,
slice.W,
slice.H,
Texture.Width,
Texture.Height
)
);
}
}
public TextureAtlasSlice Lookup(string name)
{
return Slices[name];
}
public string Name(int index)
{
return Names[index];
}
public int Count()
{
return Names.Count;
}
}
// Assumes all subimages are the same size
public class TiledTextureAtlas : TextureAtlas
{
public int NumRows { get; }
public int NumColumns { get; }
public TiledTextureAtlas(GraphicsDevice graphicsDevice, FileInfo atlasMetadataFile) : base(graphicsDevice, atlasMetadataFile)
{
var subImageSlice = Slices[Names[0]];
NumRows = Texture.Height / subImageSlice.H;
NumColumns = Texture.Width / subImageSlice.W;
}
}
}

19
Data/UVData.cs Normal file
View File

@ -0,0 +1,19 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public struct UVData
{
public Vector2 Offset { get; }
public Vector2 Percentage { get; }
public UVData(
Vector2 positionInAtlas,
Vector2 subTextureDimensions,
Vector2 atlasDimensions
) {
Percentage = subTextureDimensions / atlasDimensions;
Offset = positionInAtlas / atlasDimensions;
}
}
}

View File

@ -74,32 +74,6 @@ namespace Kav
);
}
protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource)
{
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
EyePosition = cloneSource.EyePosition;
PointLights = new PointLightCollection(
Parameters["LightPositions"],
Parameters["PositionLightColors"],
MaxPointLights
);
for (int i = 0; i < MaxPointLights; i++)
{
PointLights[i] = cloneSource.PointLights[i];
}
}
public override Effect Clone()
{
return new DeferredPBREffect(this);
}
protected override void OnApply()
{
gPositionParam.SetValue(GPosition);

View File

@ -16,6 +16,12 @@ namespace Kav
EffectParameter metallicParam;
EffectParameter roughnessParam;
EffectParameter uvOffsetAndDimensionsParam;
EffectParameter isSpriteParam;
EffectParameter numTextureRowsParam;
EffectParameter numTextureColumnsParam;
EffectParameter vertexShaderIndexParam;
EffectParameter shaderIndexParam;
@ -27,6 +33,14 @@ namespace Kav
float metallic;
float roughness;
Vector2 uvOffset;
Vector2 subTextureDimensions;
bool isSprite = false;
int numTextureRows = 1;
int numTextureColumns = 1;
bool albedoTextureEnabled = false;
bool metallicRoughnessMapEnabled = false;
bool normalMapEnabled = false;
@ -127,6 +141,56 @@ namespace Kav
}
}
public int NumTextureRows
{
get { return numTextureRows; }
set
{
numTextureRows = value;
numTextureRowsParam.SetValue(numTextureRows);
}
}
public int NumTextureColumns
{
get { return numTextureColumns; }
set
{
numTextureColumns = value;
numTextureColumnsParam.SetValue(numTextureColumns);
}
}
public Vector2 UVOffset
{
get { return uvOffset; }
set
{
uvOffset = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public Vector2 SubTextureDimensions
{
get { return subTextureDimensions; }
set
{
subTextureDimensions = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public bool IsSprite
{
get { return isSprite; }
set
{
isSprite = value;
isSpriteParam.SetValue(isSprite ? 1f : 0f);
}
}
public bool HardwareInstancingEnabled
{
get { return hardwareInstancingEnabled; }
@ -184,6 +248,16 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.UVOrDimensions) != 0)
{
uvOffsetAndDimensionsParam.SetValue(new Vector4(
UVOffset.X, UVOffset.Y,
SubTextureDimensions.X, SubTextureDimensions.Y
));
dirtyFlags &= ~EffectDirtyFlags.UVOrDimensions;
}
if ((dirtyFlags & EffectDirtyFlags.VertexShaderIndex) != 0)
{
int vertexShaderIndex = 0;
@ -248,6 +322,12 @@ namespace Kav
metallicParam = Parameters["MetallicValue"];
roughnessParam = Parameters["RoughnessValue"];
numTextureRowsParam = Parameters["NumTextureRows"];
numTextureColumnsParam = Parameters["NumTextureColumns"];
uvOffsetAndDimensionsParam = Parameters["UVOffsetAndDimensions"];
isSpriteParam = Parameters["IsSprite"];
shaderIndexParam = Parameters["PixelShaderIndex"];
vertexShaderIndexParam = Parameters["VertexShaderIndex"];
}

View File

@ -18,6 +18,8 @@ namespace Kav
EffectParameter farPlaneParam;
EffectParameter worldViewProjectionParam;
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; }
@ -31,6 +33,42 @@ namespace Kav
public float FarPlane { get; set; }
Matrix world = Matrix.Identity;
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public Matrix World
{
get { return world; }
set
{
world = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public Matrix View
{
get { return view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public Matrix Projection
{
get { return projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
{
CacheEffectParameters();
@ -39,7 +77,7 @@ namespace Kav
public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource)
{
CacheEffectParameters();
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
@ -73,6 +111,13 @@ namespace Kav
pointLightColorParam.SetValue(PointLightColor);
farPlaneParam.SetValue(FarPlane);
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
worldViewProjectionParam.SetValue(world * view * projection);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
}
void CacheEffectParameters()
@ -89,6 +134,8 @@ namespace Kav
pointLightColorParam = Parameters["PointLightColor"];
farPlaneParam = Parameters["FarPlane"];
worldViewProjectionParam = Parameters["WorldViewProjection"];
}
}
}

View File

@ -1,3 +1,4 @@
using Kav.Data;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@ -10,6 +11,8 @@ namespace Kav
EffectParameter directionalLightDirectionParam;
EffectParameter directionalLightColorParam;
EffectParameter uvOffsetAndDimensionsParam;
EffectParameter worldParam;
EffectParameter worldViewProjectionParam;
EffectParameter worldInverseTransposeParam;
@ -30,6 +33,9 @@ namespace Kav
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
Vector2 uvOffset;
Vector2 subTextureDimensions;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
public bool NormalMapEnabled
@ -109,6 +115,26 @@ namespace Kav
}
}
public Vector2 UVOffset
{
get { return uvOffset; }
set
{
uvOffset = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public Vector2 SubTextureDimensions
{
get { return subTextureDimensions; }
set
{
subTextureDimensions = value;
dirtyFlags |= EffectDirtyFlags.UVOrDimensions;
}
}
public DiffuseLitSpriteEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DiffuseLitSpriteEffect)
{
CacheEffectParameters();
@ -142,6 +168,16 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.UVOrDimensions) != 0)
{
uvOffsetAndDimensionsParam.SetValue(new Vector4(
UVOffset.X, UVOffset.Y,
SubTextureDimensions.X, SubTextureDimensions.Y
));
dirtyFlags &= ~EffectDirtyFlags.UVOrDimensions;
}
if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{
int shaderIndex = 0;
@ -166,6 +202,8 @@ namespace Kav
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"];
uvOffsetAndDimensionsParam = Parameters["UVOffsetAndDimensions"];
shaderIndexParam = Parameters["ShaderIndex"];
}
}

View File

@ -11,6 +11,7 @@ namespace Kav
VertexShaderIndex = 8,
PixelShaderIndex = 16,
ViewProj = 32,
UVOrDimensions = 64,
All = -1
}
}

BIN
Effects/FXB/DeferredPBR_GBufferEffect.fxb (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Effects/FXB/DiffuseLitSpriteEffect.fxb (Stored with Git LFS)

Binary file not shown.

BIN
Effects/FXB/LinearDepthEffectInstanced.fxb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Effects/FXB/PaletteCrushEffect.fxb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Effects/FXB/SimpleDepthEffect.fxb (Stored with Git LFS)

Binary file not shown.

View File

@ -4,12 +4,18 @@ DECLARE_TEXTURE(AlbedoTexture, 0);
DECLARE_TEXTURE(NormalTexture, 1);
DECLARE_TEXTURE(MetallicRoughnessTexture, 2);
float4 UVOffsetAndDimensions;
float IsSprite;
BEGIN_CONSTANTS
float3 AlbedoValue _ps(c0) _cb(c0);
float MetallicValue _ps(c1) _cb(c1);
float RoughnessValue _ps(c2) _cb(c2);
int NumTextureRows _vs(c9) _cb(c3);
int NumTextureColumns _vs(c10) _cb(c4);
MATRIX_CONSTANTS
float4x4 World _vs(c0) _cb(c7);
@ -48,7 +54,11 @@ PixelInput main_vs(VertexInput input)
output.PositionWorld = mul(input.Position, World).xyz;
output.NormalWorld = normalize(mul(input.Normal, World));
output.TexCoord = input.TexCoord;
float2 texCoord;
texCoord.x = (input.TexCoord.x * UVOffsetAndDimensions.z) + UVOffsetAndDimensions.x;
texCoord.y = (input.TexCoord.y * UVOffsetAndDimensions.w) + UVOffsetAndDimensions.y;
output.TexCoord = texCoord;
float4x4 worldViewProjection = mul(World, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
@ -56,8 +66,11 @@ PixelInput main_vs(VertexInput input)
return output;
}
PixelInput instanced_vs(VertexInput input, float3 Translation : TEXCOORD2)
{
PixelInput instanced_vs(
VertexInput input,
float3 Translation : TEXCOORD2,
float2 UVOffset : TEXCOORD5
) {
PixelInput output;
float4x4 world = float4x4(
@ -65,13 +78,17 @@ PixelInput instanced_vs(VertexInput input, float3 Translation : TEXCOORD2)
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;
float2 texCoord;
texCoord.x = (input.TexCoord.x / NumTextureColumns + UVOffset.x);
texCoord.y = (input.TexCoord.y / NumTextureRows + UVOffset.y);
output.TexCoord = texCoord;
output.Position = mul(input.Position, worldViewProjection);
@ -103,7 +120,7 @@ PixelOutput NonePS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gNormal = float4(normalize(input.NormalWorld), IsSprite);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
@ -115,10 +132,12 @@ PixelOutput AlbedoPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gNormal = float4(normalize(input.NormalWorld), IsSprite);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
if (output.gAlbedo.a == 0.0) { discard; }
return output;
}
@ -127,7 +146,7 @@ PixelOutput MetallicRoughnessPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gNormal = float4(normalize(input.NormalWorld), IsSprite);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@ -139,7 +158,7 @@ PixelOutput NormalPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), IsSprite);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
@ -151,10 +170,12 @@ PixelOutput AlbedoMetallicRoughnessPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gNormal = float4(normalize(input.NormalWorld), IsSprite);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
if (output.gAlbedo.a == 0.0) { discard; }
return output;
}
@ -163,10 +184,12 @@ PixelOutput AlbedoNormalPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), IsSprite);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
if (output.gAlbedo.a == 0.0) { discard; }
return output;
}
@ -175,7 +198,7 @@ PixelOutput MetallicRoughnessNormalPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), IsSprite);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@ -187,10 +210,12 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input)
PixelOutput output;
output.gPosition = float4(input.PositionWorld, 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), IsSprite);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
if (output.gAlbedo.a == 0.0) { discard; }
return output;
}

View File

@ -1,5 +1,7 @@
// Assumes you are drawing a sphere!!
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
#include "Lighting.fxh"
#include "Shadow.fxh"
DECLARE_TEXTURE(gPosition, 0);
@ -11,7 +13,7 @@ DECLARE_CUBEMAP(shadowMap, 4);
BEGIN_CONSTANTS
float3 EyePosition _ps(c0) _cb(c0);
float3 PointLightPosition _ps(c1) _cb(c1);
float3 PointLightColor _ps(c2) _cb(c2);
@ -19,27 +21,28 @@ BEGIN_CONSTANTS
MATRIX_CONSTANTS
float4x4 WorldViewProjection _vs(c0) _cb(c4);
END_CONSTANTS
struct VertexInput
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD;
};
struct PixelInput
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
float4 ScreenPosition : TEXCOORD0;
};
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
output.Position = mul(input.Position, WorldViewProjection);
output.ScreenPosition = output.Position;
return output;
}
@ -73,18 +76,36 @@ float4 ComputeColor(
float4 main_ps(PixelInput input) : SV_TARGET0
{
float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 texCoord = 0.5f * float2(input.ScreenPosition.x,-input.ScreenPosition.y) + 0.5f;
return ComputeColor(
worldPosition,
normal,
albedo,
metallicRoughness.r,
metallicRoughness.g
);
float3 worldPosition = SAMPLE_TEXTURE(gPosition, texCoord).rgb;
float4 normalSample = SAMPLE_TEXTURE(gNormal, texCoord);
float3 normal = normalSample.xyz;
float isSprite = normalSample.a;
float3 albedo = SAMPLE_TEXTURE(gAlbedo, texCoord).rgb;
float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, texCoord).rg;
if (isSprite == 1.0)
{
float3 lightDir = PointLightPosition - worldPosition;
float3 L = normalize(lightDir);
float distance = length(lightDir);
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColor * attenuation;
return float4(albedo * radiance * 0.1, 1.0);
}
else
{
return ComputeColor(
worldPosition,
normal,
albedo,
metallicRoughness.r,
metallicRoughness.g
);
}
}
Technique DeferredPBR_Point

View File

@ -5,23 +5,19 @@
DECLARE_TEXTURE(Texture, 0);
DECLARE_TEXTURE(Normal, 1);
BEGIN_CONSTANTS
float3 AmbientColor;
float3 AmbientColor _ps(c0) _cb(c0);
float3 PointLightPositions[8];
float3 PointLightColors[8];
float3 PointLightPositions[8] _ps(c1) _cb(c1);
float3 PointLightColors[8] _ps(c9) _cb(c9);
float3 DirectionalLightDirection;
float3 DirectionalLightColor;
float3 DirectionalLightDirection _ps(c17) _cb(c17);
float3 DirectionalLightColor _ps(c18) _cb(c18);
float4 UVOffsetAndDimensions;
MATRIX_CONSTANTS
float4x4 WorldInverseTranspose _ps(c19) _cb(c19);
float4x4 World _vs(c0) _cb(c23);
float4x4 WorldViewProjection _vs(c4) _cb(c27);
END_CONSTANTS
float4x4 WorldInverseTranspose;
float4x4 World;
float4x4 WorldViewProjection;
struct VertexShaderInput
{
@ -43,10 +39,14 @@ PixelShaderInput main_vs(VertexShaderInput input)
PixelShaderInput output;
output.Position = mul(input.Position, WorldViewProjection);
output.TexCoord = input.TexCoord;
output.NormalWS = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz;
output.NormalWS = normalize(mul(input.Normal, (float3x3)WorldInverseTranspose));
output.PositionWS = mul(input.Position, World).xyz;
float2 texCoord;
texCoord.x = (input.TexCoord.x * UVOffsetAndDimensions.z) + UVOffsetAndDimensions.x;
texCoord.y = (input.TexCoord.y * UVOffsetAndDimensions.w) + UVOffsetAndDimensions.y;
output.TexCoord = texCoord;
return output;
}
@ -98,6 +98,9 @@ float4 LightColor(float3 worldPosition, float3 worldNormal)
float4 WithoutNormalMap(PixelShaderInput input) : COLOR0
{
float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord);
if (tex.a == 0.0) { discard; }
float3 normalWS = normalize(input.NormalWS);
return tex * LightColor(input.PositionWS, normalWS);
@ -106,6 +109,9 @@ float4 WithoutNormalMap(PixelShaderInput input) : COLOR0
float4 WithNormalMap(PixelShaderInput input) : COLOR0
{
float4 tex = SAMPLE_TEXTURE(Texture, input.TexCoord);
if (tex.a == 0.0) { discard; }
float3 normalWS = GetNormalFromMap(input.PositionWS, input.TexCoord, input.NormalWS);
return tex * LightColor(input.PositionWS, normalWS);

View File

@ -0,0 +1,55 @@
#include "Macros.fxh"
BEGIN_CONSTANTS
float4x4 ViewProjection _vs(c4) _cb(c4);
float3 LightPosition _ps(c0) _cb(c8);
float FarPlane _ps(c1) _cb(c9);
END_CONSTANTS
struct VertexShaderInput
{
float4 Position : POSITION;
};
struct VertexShaderOutput
{
float4 Position : SV_Position;
float3 PositionWorld : 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.Position.x *= -1; // otherwise cube map render will be horizontally flipped
output.PositionWorld = mul(input.Position, world);
return output;
}
float4 main_ps(VertexShaderOutput input) : SV_TARGET0
{
float lightDistance = length(input.PositionWorld - LightPosition);
lightDistance /= FarPlane;
return float4(lightDistance, 0.0, 0.0, 0.0);
}
Technique SimpleDepth
{
Pass
{
VertexShader = compile vs_3_0 instanced_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -0,0 +1,65 @@
#include "Macros.fxh"
DECLARE_TEXTURE(Texture, 0);
DECLARE_TEXTURE(Palette, 1);
BEGIN_CONSTANTS
int PaletteWidth _ps(c0) _cb(c0);
END_CONSTANTS
struct VertexInput
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD;
};
struct PixelInput
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
};
PixelInput main_vs(VertexInput input)
{
PixelInput output;
output.Position = input.Position;
output.TexCoord = input.TexCoord;
return output;
}
float4 main_ps(PixelInput input) : SV_TARGET0
{
float4 sampled = SAMPLE_TEXTURE(Texture, input.TexCoord);
float3 sampled_color = sampled.rgb;
float3 closest_color = float3(0, 0, 0);
float closest_dist = 100000;
for (int i = 0; i < PaletteWidth; i++)
{
float texX = (i / (float)PaletteWidth);
float3 palette_color = SAMPLE_TEXTURE(Palette, float2(texX, 0));
float dist = distance(palette_color, sampled_color);
if (dist < closest_dist)
{
closest_dist = dist;
closest_color = palette_color;
}
}
return float4(closest_color, sampled.a);
}
Technique PaletteCrush
{
Pass
{
VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}

View File

@ -17,7 +17,7 @@ struct VertexShaderInput
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float Depth : DEPTH;
float Depth : TEXCOORD0;
};
VertexShaderOutput main_vs(VertexShaderInput input)

View File

@ -20,7 +20,7 @@ VertexShaderOutput instanced_vs(VertexShaderInput input, float3 Translation : TE
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(Translation.x, Translation.y, Translation.z, 1)
);
);
float4x4 worldViewProjection = mul(world, ViewProjection);

View File

@ -0,0 +1,67 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class LinearDepthEffectInstanced : Effect
{
EffectParameter viewProjectionParam;
EffectParameter lightPositionParam;
EffectParameter farPlaneParam;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
Matrix view;
Matrix projection;
public Vector3 LightPosition { get; set; }
public float FarPlane { get; set; }
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 LinearDepthEffectInstanced(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.LinearDepthEffectInstanced)
{
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;
}
lightPositionParam.SetValue(LightPosition);
farPlaneParam.SetValue(FarPlane);
}
private void CacheEffectParameters()
{
viewProjectionParam = Parameters["ViewProjection"];
lightPositionParam = Parameters["LightPosition"];
farPlaneParam = Parameters["FarPlane"];
}
}
}

View File

@ -197,45 +197,6 @@ namespace Kav
);
}
protected PBREffect(PBREffect cloneSource) : base(cloneSource)
{
CacheEffectParameters();
World = cloneSource.World;
View = cloneSource.View;
Projection = cloneSource.Projection;
PointLights = new PointLightCollection(
Parameters["LightPositions"],
Parameters["PositionLightColors"],
MaxPointLights
);
for (int i = 0; i < MaxPointLights; i++)
{
PointLights[i] = cloneSource.PointLights[i];
}
AlbedoTexture = cloneSource.AlbedoTexture;
NormalTexture = cloneSource.NormalTexture;
EmissionTexture = cloneSource.EmissionTexture;
OcclusionTexture = cloneSource.OcclusionTexture;
MetallicRoughnessTexture = cloneSource.MetallicRoughnessTexture;
EnvDiffuseTexture = cloneSource.EnvDiffuseTexture;
BRDFLutTexture = cloneSource.BRDFLutTexture;
EnvSpecularTexture = cloneSource.EnvSpecularTexture;
Albedo = cloneSource.Albedo;
Metallic = cloneSource.Metallic;
Roughness = cloneSource.Roughness;
AO = cloneSource.AO;
}
public override Effect Clone()
{
return new PBREffect(this);
}
protected override void OnApply()
{
if ((dirtyFlags & EffectDirtyFlags.World) != 0)

View File

@ -0,0 +1,51 @@
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class PaletteCrushEffect : Effect
{
EffectParameter textureParam;
EffectParameter paletteParam;
EffectParameter paletteWidthParam;
Texture2D texture;
Texture2D palette;
int paletteWidth;
public Texture2D Texture
{
get { return texture; }
set
{
texture = value;
textureParam.SetValue(texture);
}
}
public Texture2D Palette
{
get { return palette; }
set
{
palette = value;
paletteWidth = palette.Width;
paletteParam.SetValue(palette);
paletteWidthParam.SetValue(paletteWidth);
}
}
public PaletteCrushEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.PaletteCrushEffect)
{
CacheEffectParameters();
}
void CacheEffectParameters()
{
textureParam = Parameters["Texture"];
paletteParam = Parameters["Palette"];
paletteWidthParam = Parameters["PaletteWidth"];
}
}
}

View File

@ -7,41 +7,24 @@ namespace Kav
{
private readonly Vector3[] positions;
private readonly Vector3[] colors;
private readonly float[] intensities;
readonly EffectParameter lightPositionsParam;
readonly EffectParameter lightColorsParam;
public PointLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam, int maxLights)
{
this.positions = new Vector3[maxLights];
this.colors = new Vector3[maxLights];
this.intensities = new float[maxLights];
positions = new Vector3[maxLights];
colors = new Vector3[maxLights];
this.lightPositionsParam = lightPositionsParam;
this.lightColorsParam = lightColorsParam;
}
public PointLight this[int i]
{
get
{
var color = colors[i] / intensities[i];
return new PointLight(
positions[i],
new Color(
color.X,
color.Y,
color.Z,
1f
),
intensities[i]
);
}
set
{
positions[i] = value.Position;
colors[i] = value.Color.ToVector3() * value.Intensity;
intensities[i] = value.Intensity;
colors[i] = value.Color.ToVector3() * value.Radius;
lightPositionsParam.SetValue(positions);
lightColorsParam.SetValue(colors);
}

View File

@ -12,5 +12,8 @@ namespace Kav
Texture2D AlbedoTexture { get; }
Texture2D NormalTexture { get; }
Texture2D MetallicRoughnessTexture { get; }
int NumTextureRows { get; }
int NumTextureColumns { get; }
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface ITransformable
{
Matrix TransformMatrix { get; }
}
}

View File

@ -38,6 +38,9 @@ namespace Kav
public float Metallic { get; set; } = 0.5f;
public float Roughness { get; set; } = 0.5f;
public int NumTextureRows { get; set; } = 1;
public int NumTextureColumns { get; set; } = 1;
public bool DisableAlbedoMap { get; set; } = false;
public bool DisableNormalMap { get; set; } = false;
public bool DisableMetallicRoughnessMap { get; set; } = false;

View File

@ -1,120 +0,0 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class MeshSprite : ICullable, IIndexDrawable
{
private static readonly int PixelScale = 40;
private static readonly short[] Indices = new short[]
{
0,
1,
2,
1,
3,
2
};
public Texture2D Texture { get; }
public Texture2D Normal { get; }
public SpriteBillboardConstraint BillboardConstraint { get; }
public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; }
public BoundingBox BoundingBox { get; }
public MeshSprite(
GraphicsDevice graphicsDevice,
Texture2D texture,
SpriteBillboardConstraint billboardConstraint
) {
Texture = texture;
Normal = null;
BillboardConstraint = billboardConstraint;
IndexBuffer = new IndexBuffer(
graphicsDevice,
IndexElementSize.SixteenBits,
6,
BufferUsage.WriteOnly
);
IndexBuffer.SetData(Indices);
var vertexArray = GenerateVertexArray(Texture);
VertexBuffer = new VertexBuffer(
graphicsDevice,
typeof(VertexPositionNormalTexture),
4,
BufferUsage.WriteOnly
);
VertexBuffer.SetData(vertexArray);
BoundingBox = BoundingBox.CreateFromPoints(Positions(vertexArray));
}
public MeshSprite(
GraphicsDevice graphicsDevice,
Texture2D texture,
Texture2D normal,
SpriteBillboardConstraint billboardConstraint
) {
Texture = texture;
Normal = normal;
BillboardConstraint = billboardConstraint;
IndexBuffer = new IndexBuffer(
graphicsDevice,
IndexElementSize.SixteenBits,
6,
BufferUsage.WriteOnly
);
IndexBuffer.SetData(Indices);
var vertexArray = GenerateVertexArray(Texture);
VertexBuffer = new VertexBuffer(
graphicsDevice,
typeof(VertexPositionNormalTexture),
4,
BufferUsage.WriteOnly
);
VertexBuffer.SetData(vertexArray);
BoundingBox = BoundingBox.CreateFromPoints(Positions(vertexArray));
}
private static VertexPositionNormalTexture[] GenerateVertexArray(Texture2D texture)
{
VertexPositionNormalTexture[] result = new VertexPositionNormalTexture[4];
result[0].Position = new Vector3(-texture.Width / 2, texture.Height / 2, 0) / PixelScale;
result[0].Normal = new Vector3(0, 0, 1);
result[0].TextureCoordinate = new Vector2(0, 0);
result[1].Position = new Vector3(texture.Width / 2, texture.Height / 2, 0) / PixelScale;
result[1].Normal = new Vector3(0, 0, 1);
result[1].TextureCoordinate = new Vector2(1, 0);
result[2].Position = new Vector3(-texture.Width / 2, -texture.Height / 2, 0) / PixelScale;
result[2].Normal = new Vector3(0, 0, 1);
result[2].TextureCoordinate = new Vector2(0, 1);
result[3].Position = new Vector3(texture.Width / 2, -texture.Height / 2, 0) / PixelScale;
result[3].Normal = new Vector3(0, 0, 1);
result[3].TextureCoordinate = new Vector2(1, 1);
return result;
}
private static IEnumerable<Vector3> Positions(IEnumerable<VertexPositionNormalTexture> vertices)
{
foreach (var vertex in vertices)
{
yield return vertex.Position;
}
}
}
}

109
Geometry/SpriteMesh.cs Normal file
View File

@ -0,0 +1,109 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class SpriteMesh : ICullable, IIndexDrawable
{
public enum FlipOptions
{
None,
Horizontal,
Vertical,
Both
}
private static readonly short[] Indices = new short[]
{
0,
1,
2,
1,
3,
2
};
public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; }
public BoundingBox BoundingBox { get; }
public SpriteMesh(
GraphicsDevice graphicsDevice,
int width,
int height,
FlipOptions flipOptions
) {
IndexBuffer = new IndexBuffer(
graphicsDevice,
IndexElementSize.SixteenBits,
6,
BufferUsage.WriteOnly
);
IndexBuffer.SetData(Indices);
var vertexArray = GenerateVertexArray(width, height, flipOptions);
VertexBuffer = new VertexBuffer(
graphicsDevice,
typeof(VertexPositionNormalTexture),
4,
BufferUsage.WriteOnly
);
VertexBuffer.SetData(vertexArray);
BoundingBox = BoundingBox.CreateFromPoints(Positions(vertexArray));
}
private static VertexPositionNormalTexture[] GenerateVertexArray(int pixelWidth, int pixelHeight, FlipOptions flipOptions)
{
var width = pixelWidth / (float)40;
var height = pixelHeight / (float)40;
VertexPositionNormalTexture[] result = new VertexPositionNormalTexture[4];
var xLeft = 0;
var xRight = 1;
var yTop = 0;
var yBottom = 1;
if (flipOptions == FlipOptions.Horizontal || flipOptions == FlipOptions.Both)
{
xLeft = 1;
xRight = 0;
}
if (flipOptions == FlipOptions.Vertical || flipOptions == FlipOptions.Both)
{
yTop = 1;
yBottom = 0;
}
result[0].Position = new Vector3(-width / 2, height / 2, 0);
result[0].Normal = new Vector3(0, 0, -1);
result[0].TextureCoordinate = new Vector2(xLeft, yTop);
result[1].Position = new Vector3(width / 2, height / 2, 0);
result[1].Normal = new Vector3(0, 0, -1);
result[1].TextureCoordinate = new Vector2(xRight, yTop);
result[2].Position = new Vector3(-width / 2, -height / 2, 0);
result[2].Normal = new Vector3(0, 0, -1);
result[2].TextureCoordinate = new Vector2(xLeft, yBottom);
result[3].Position = new Vector3(width / 2, -height / 2, 0);
result[3].Normal = new Vector3(0, 0, -1);
result[3].TextureCoordinate = new Vector2(xRight, yBottom);
return result;
}
private static IEnumerable<Vector3> Positions(IEnumerable<VertexPositionNormalTexture> vertices)
{
foreach (var vertex in vertices)
{
yield return vertex.Position;
}
}
}
}

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace>Kav</RootNamespace>
<Authors>Evan Hemsley</Authors>
<Copyright>Evan Hemsley 2020</Copyright>
@ -49,15 +50,24 @@
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.LinearDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DiffuseLitSpriteEffect.fxb">
<LogicalName>Kav.Resources.DiffuseLitSpriteEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\PaletteCrushEffect.fxb">
<LogicalName>Kav.Resources.PaletteCrushEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitSphere.glb">
<LogicalName>Kav.Resources.UnitSphere.glb</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace>Kav</RootNamespace>
<Authors>Evan Hemsley</Authors>
<Copyright>Evan Hemsley 2020</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyName>Kav</AssemblyName>
<Platforms>x64</Platforms>
<Platforms>x64;x86</Platforms>
</PropertyGroup>
<ItemGroup>
@ -49,15 +50,24 @@
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.LinearDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SkyboxEffect.fxb">
<LogicalName>Kav.Resources.SkyboxEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\DiffuseLitSpriteEffect.fxb">
<LogicalName>Kav.Resources.DiffuseLitSpriteEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\PaletteCrushEffect.fxb">
<LogicalName>Kav.Resources.PaletteCrushEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitCube.glb">
<LogicalName>Kav.Resources.UnitCube.glb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Models\UnitSphere.glb">
<LogicalName>Kav.Resources.UnitSphere.glb</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -1,18 +1,55 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Kav
{
public struct PointLight
public sealed class PointLight : IDisposable
{
public Vector3 Position { get; }
public Color Color { get; }
public float Intensity { get; }
public float Radius { get; }
public PointLight(Vector3 position, Color color, float intensity = 1f)
{
public BoundingSphere BoundingSphere { get; }
public RenderTargetCube ShadowMap { get; }
public PointLight(
GraphicsDevice graphicsDevice,
Vector3 position,
Color color,
float radius,
int shadowMapSize
) {
Position = position;
Color = color;
Intensity = intensity;
Radius = radius;
BoundingSphere = new BoundingSphere(position, Radius);
ShadowMap = new RenderTargetCube(
graphicsDevice,
shadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24,
0,
RenderTargetUsage.PreserveContents
);
var currentRTs = graphicsDevice.GetRenderTargets();
foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
graphicsDevice.SetRenderTarget(ShadowMap, face);
graphicsDevice.Clear(Color.White);
}
graphicsDevice.SetRenderTargets(currentRTs);
}
public void Dispose()
{
ShadowMap.Dispose();
}
}
}

View File

@ -0,0 +1,18 @@
using System.IO;
using System.Text.Json;
namespace Kav
{
public static class CrunchAtlasReader
{
static JsonSerializerOptions options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
public static CrunchTextureAtlasData ReadTextureAtlas(FileInfo file)
{
return JsonSerializer.Deserialize<CrunchTextureAtlasData>(File.ReadAllText(file.FullName), options);
}
}
}

View File

@ -0,0 +1,22 @@
namespace Kav
{
public struct CrunchTextureAtlasData
{
public CrunchTextureAtlasTextureData[] Textures { get; set; }
}
public struct CrunchTextureAtlasTextureData
{
public string Name { get; set; }
public CrunchTextureAtlasImageData[] Images { get; set; }
}
public struct CrunchTextureAtlasImageData
{
public string N { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int W { get; set; }
public int H { get; set; }
}
}

BIN
Models/UnitSphere.glb Normal file

Binary file not shown.

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using Kav.Data;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@ -8,8 +9,6 @@ namespace Kav
{
public class Renderer
{
private const int MAX_INSTANCE_VERTEX_COUNT = 1000000;
private GraphicsDevice GraphicsDevice { get; }
private VertexBuffer FullscreenTriangle { get; }
@ -24,14 +23,13 @@ namespace Kav
private SimpleDepthEffect SimpleDepthEffect { get; }
private SimpleDepthEffectInstanced SimpleDepthEffectInstanced { get; }
private LinearDepthEffect LinearDepthEffect { get; }
private LinearDepthEffectInstanced LinearDepthEffectInstanced { get; }
private Effect ToneMapEffect { get; }
private SkyboxEffect SkyboxEffect { get; }
private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { get; }
private Kav.Model UnitCube { get; }
private DynamicVertexBuffer PositionInstanceVertexBuffer { get; }
private readonly PositionInstanceVertex[] PositionInstanceVertices = new PositionInstanceVertex[MAX_INSTANCE_VERTEX_COUNT];
private Kav.Model UnitSphere { get; }
public Renderer(
GraphicsDevice graphicsDevice
@ -41,6 +39,8 @@ namespace Kav
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
SimpleDepthEffectInstanced = new SimpleDepthEffectInstanced(GraphicsDevice);
LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
LinearDepthEffectInstanced = new LinearDepthEffectInstanced(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice);
@ -64,12 +64,26 @@ namespace Kav
Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel))
);
PositionInstanceVertexBuffer = new DynamicVertexBuffer(
GraphicsDevice,
VertexDeclarations.PositionInstanceDeclaration,
MAX_INSTANCE_VERTEX_COUNT,
UnitSphere = Kav.ModelLoader.Load(
graphicsDevice,
Smuggler.Importer.ImportGLB(graphicsDevice, new MemoryStream(Resources.UnitSphereModel))
);
}
public static (T[], DynamicVertexBuffer) CreateInstanceVertexBuffer<T>(
GraphicsDevice graphicsDevice,
int instanceVertexCount
) where T : IVertexType {
var positionData = new T[instanceVertexCount];
var vertexBuffer = new DynamicVertexBuffer(
graphicsDevice,
typeof(T),
instanceVertexCount,
BufferUsage.WriteOnly
);
return (positionData, vertexBuffer);
}
public static RenderTargetCube CreateShadowCubeMap(
@ -104,14 +118,14 @@ namespace Kav
public void MeshSpriteRender(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(MeshSprite, Matrix)> meshSpriteTransforms,
IEnumerable<MeshSpriteDrawData> meshSpriteDrawDatas,
AmbientLight ambientLight,
IEnumerable<PointLight> pointLights,
DirectionalLight? directionalLight
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
@ -145,40 +159,26 @@ namespace Kav
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var (sprite, transform) in FrustumCull(boundingFrustum, meshSpriteTransforms))
foreach (var data in meshSpriteDrawDatas)
{
DiffuseLitSpriteEffect.NormalMapEnabled = sprite.Normal != null;
var matrix = BillboardTransforms(camera, data.TransformMatrix, data.BillboardConstraint);
if (sprite.BillboardConstraint == SpriteBillboardConstraint.None)
if (FrustumCull(boundingFrustum, data.MeshSprite, matrix))
{
DiffuseLitSpriteEffect.World = transform;
}
else if (sprite.BillboardConstraint == SpriteBillboardConstraint.Horizontal)
{
DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
camera.Forward,
camera.Position - transform.Translation
);
}
else
{
DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
null,
null
);
continue;
}
GraphicsDevice.Textures[0] = sprite.Texture;
GraphicsDevice.Textures[1] = sprite.Normal;
DiffuseLitSpriteEffect.NormalMapEnabled = data.Normal != null;
GraphicsDevice.SetVertexBuffer(sprite.VertexBuffer);
GraphicsDevice.Indices = sprite.IndexBuffer;
DiffuseLitSpriteEffect.World = matrix;
DiffuseLitSpriteEffect.UVOffset = data.UVOffset.Offset;
DiffuseLitSpriteEffect.SubTextureDimensions = data.UVOffset.Percentage;
GraphicsDevice.Textures[0] = data.Texture;
GraphicsDevice.Textures[1] = data.Normal;
GraphicsDevice.SetVertexBuffer(data.MeshSprite.VertexBuffer);
GraphicsDevice.Indices = data.MeshSprite.IndexBuffer;
foreach (var pass in DiffuseLitSpriteEffect.CurrentTechnique.Passes)
{
@ -188,7 +188,7 @@ namespace Kav
PrimitiveType.TriangleList,
0,
0,
sprite.VertexBuffer.VertexCount,
data.MeshSprite.VertexBuffer.VertexCount,
0,
2
);
@ -196,6 +196,82 @@ namespace Kav
}
}
public void RenderMeshSpriteGBuffer(
RenderTargetBinding[] gBuffer,
PerspectiveCamera camera,
IEnumerable<MeshSpriteDrawData> meshSpriteDrawDatas,
DepthStencilState depthStencilState
) {
GraphicsDevice.SetRenderTargets(gBuffer);
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.DepthStencilState = depthStencilState;
GraphicsDevice.BlendState = BlendState.AlphaBlend;
Deferred_GBufferEffect.HardwareInstancingEnabled = false;
Deferred_GBufferEffect.IsSprite = true;
Deferred_GBufferEffect.View = camera.View;
Deferred_GBufferEffect.Projection = camera.Projection;
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var data in meshSpriteDrawDatas)
{
var matrix = BillboardTransforms(camera, data.TransformMatrix, data.BillboardConstraint);
if (FrustumCull(boundingFrustum, data.MeshSprite, matrix))
{
continue;
}
Deferred_GBufferEffect.World = matrix;
Deferred_GBufferEffect.UVOffset = data.UVOffset.Offset;
Deferred_GBufferEffect.SubTextureDimensions = data.UVOffset.Percentage;
Deferred_GBufferEffect.Albedo = Color.White.ToVector3();
Deferred_GBufferEffect.Metallic = 0f;
Deferred_GBufferEffect.Roughness = 1f;
Deferred_GBufferEffect.AlbedoTexture = data.Texture;
Deferred_GBufferEffect.NormalTexture = data.Normal;
Deferred_GBufferEffect.MetallicRoughnessTexture = null;
RenderIndexed(GraphicsDevice, data, Deferred_GBufferEffect);
}
}
private static Matrix BillboardTransforms(
PerspectiveCamera camera,
Matrix transform,
SpriteBillboardConstraint billboardConstraint
) {
if (billboardConstraint == SpriteBillboardConstraint.None)
{
return transform;
}
else if (billboardConstraint == SpriteBillboardConstraint.Horizontal)
{
return Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
camera.Forward,
camera.Position - transform.Translation
);
}
else
{
return Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
null,
null
);
}
}
// Renders a series of drawable-transform pairs using an effect that has a World matrix.
// Effect must be pre-configured!!
public static void CullAndRenderIndexed<T, U>(
@ -203,7 +279,7 @@ namespace Kav
BoundingFrustum boundingFrustum,
IEnumerable<(T, Matrix)> drawableTransformPairs,
U effect
) where T : IIndexDrawable, ICullable where U : Effect, IHasWorldMatrix
) where T : IIndexDrawable, ICullable where U : Effect, IHasWorldMatrix
{
foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransformPairs))
{
@ -241,47 +317,19 @@ namespace Kav
}
}
public static int FillAndSetBuffersForInstancing<T, V>(
public static void RenderInstanced<T>(
GraphicsDevice graphicsDevice,
BoundingFrustum boundingFrustum,
T drawable,
IEnumerable<Matrix> 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))
{
vertexData[numInstances].Translation = transform.Translation;
numInstances += 1;
}
if (numInstances == 0) { return 0; }
dynamicVertexBuffer.SetData(
vertexData,
0,
numInstances,
SetDataOptions.Discard
);
VertexBuffer instanceVertexBuffer,
int numInstances,
Effect effect
) where T : IIndexDrawable {
graphicsDevice.SetVertexBuffers(
drawable.VertexBuffer,
new VertexBufferBinding(dynamicVertexBuffer, 0, 1)
new VertexBufferBinding(instanceVertexBuffer, 0, 1)
);
graphicsDevice.Indices = drawable.IndexBuffer;
return numInstances;
}
public static void RenderInstanced<T, U>(
GraphicsDevice graphicsDevice,
T drawable,
U effect,
int numInstances
) where T : ICullable, IIndexDrawable where U : Effect
{
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
@ -314,7 +362,7 @@ namespace Kav
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(T, Matrix)> drawableTransforms
) where T : ICullable, IIndexDrawable
) where T : ICullable, IIndexDrawable
{
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
@ -330,6 +378,21 @@ namespace Kav
);
}
public void RenderDepthIndexed<T>(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
T drawable,
DepthStencilState depthStencilState
) where T : IIndexDrawable {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = depthStencilState;
SimpleDepthEffect.View = camera.View;
SimpleDepthEffect.Projection = camera.Projection;
RenderIndexed(GraphicsDevice, drawable, SimpleDepthEffect);
}
public void RenderSkybox(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
@ -349,7 +412,7 @@ namespace Kav
RenderIndexed(
GraphicsDevice,
UnitCube.Meshes[0].MeshParts[0],
UnitCube.Meshes[0].MeshParts[0],
SkyboxEffect
);
@ -358,24 +421,30 @@ namespace Kav
/// <summary>
/// GBuffer binding must have 4 render targets.
/// Assumes that vertex buffer has been filled.
/// </summary>
public void RenderGBufferInstanced<T>(
RenderTargetBinding[] gBuffer,
RenderTarget2D depthBuffer,
PerspectiveCamera camera,
T drawable,
IEnumerable<Matrix> transforms
) where T : ICullable, IIndexDrawable, IGBufferDrawable {
VertexBuffer instanceVertexBuffer,
int numInstances
) where T : IGBufferDrawable, IIndexDrawable {
GraphicsDevice.SetRenderTargets(gBuffer);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
Deferred_GBufferEffect.HardwareInstancingEnabled = true;
Deferred_GBufferEffect.IsSprite = false;
Deferred_GBufferEffect.Albedo = drawable.Albedo;
Deferred_GBufferEffect.Metallic = drawable.Metallic;
Deferred_GBufferEffect.Roughness = drawable.Roughness;
Deferred_GBufferEffect.NumTextureRows = drawable.NumTextureRows;
Deferred_GBufferEffect.NumTextureColumns = drawable.NumTextureColumns;
Deferred_GBufferEffect.AlbedoTexture = drawable.AlbedoTexture;
Deferred_GBufferEffect.NormalTexture = drawable.NormalTexture;
Deferred_GBufferEffect.MetallicRoughnessTexture = drawable.MetallicRoughnessTexture;
@ -383,20 +452,12 @@ namespace Kav
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
drawable,
instanceVertexBuffer,
numInstances,
Deferred_GBufferEffect
);
// re-render to get depth
@ -408,8 +469,9 @@ namespace Kav
RenderInstanced(
GraphicsDevice,
drawable,
SimpleDepthEffectInstanced,
numInstances
instanceVertexBuffer,
numInstances,
SimpleDepthEffectInstanced
);
}
@ -422,6 +484,7 @@ namespace Kav
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
Deferred_GBufferEffect.IsSprite = false;
Deferred_GBufferEffect.HardwareInstancingEnabled = false;
Deferred_GBufferEffect.View = camera.View;
Deferred_GBufferEffect.Projection = camera.Projection;
@ -474,7 +537,8 @@ namespace Kav
PointLight pointLight
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
GraphicsDevice.DepthStencilState = DepthStencilState.None;
GraphicsDevice.BlendState = BlendState.Additive;
DeferredPointLightEffect.GPosition = gPosition;
@ -487,11 +551,21 @@ namespace Kav
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
pointLight.Color.ToVector3() * pointLight.Radius;
DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
RenderFullscreenEffect(DeferredPointLightEffect);
DeferredPointLightEffect.World =
Matrix.CreateScale(pointLight.Radius) *
Matrix.CreateTranslation(pointLight.Position);
DeferredPointLightEffect.View = camera.View;
DeferredPointLightEffect.Projection = camera.Projection;
RenderIndexed(
GraphicsDevice,
UnitSphere.Meshes[0].MeshParts[0],
DeferredPointLightEffect
);
}
public void RenderDirectionalLight(
@ -505,6 +579,7 @@ namespace Kav
DirectionalLight directionalLight
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
@ -677,8 +752,8 @@ namespace Kav
for (var i = 0; i < shadowMapData.NumShadowCascades; i++)
{
RenderDirectionalShadowMapIndexed(
shadowMapData,
i,
shadowMapData,
i,
drawableTransforms
);
}
@ -697,9 +772,9 @@ namespace Kav
SimpleDepthEffect.Projection = shadowMapData.LightSpaceProjections[shadowCascadeIndex];
CullAndRenderIndexed(
GraphicsDevice,
new BoundingFrustum(SimpleDepthEffect.View * SimpleDepthEffect.Projection),
drawableTransforms,
GraphicsDevice,
new BoundingFrustum(SimpleDepthEffect.View * SimpleDepthEffect.Projection),
drawableTransforms,
SimpleDepthEffect
);
}
@ -707,8 +782,9 @@ namespace Kav
public void RenderDirectionalShadowsInstanced<T>(
DirectionalShadowMapData shadowMapData,
T drawable,
IEnumerable<Matrix> transforms
) where T : ICullable, IIndexDrawable
VertexBuffer instanceVertexBuffer,
int numInstances
) where T : IIndexDrawable
{
// render the individual shadow cascades
for (var i = 0; i < shadowMapData.NumShadowCascades; i++)
@ -717,7 +793,8 @@ namespace Kav
shadowMapData,
i,
drawable,
transforms
instanceVertexBuffer,
numInstances
);
}
}
@ -726,8 +803,9 @@ namespace Kav
DirectionalShadowMapData shadowMapData,
int shadowCascadeIndex,
T drawable,
IEnumerable<Matrix> transforms
) where T : ICullable, IIndexDrawable
VertexBuffer instanceVertexBuffer,
int numInstances
) where T : IIndexDrawable
{
GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
@ -736,26 +814,17 @@ namespace Kav
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,
GraphicsDevice,
drawable,
SimpleDepthEffectInstanced,
numInstances
instanceVertexBuffer,
numInstances,
SimpleDepthEffectInstanced
);
}
public void RenderPointShadowMapIndexed<T>(
RenderTargetCube pointShadowCubeMap,
PerspectiveCamera camera,
IEnumerable<(T, Matrix)> modelTransforms,
PointLight pointLight
) where T : ICullable, IIndexDrawable {
@ -823,8 +892,8 @@ namespace Kav
CullAndRenderIndexed(
GraphicsDevice,
new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection),
modelTransforms,
new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection),
modelTransforms,
LinearDepthEffect
);
}
@ -832,11 +901,11 @@ namespace Kav
public void RenderPointShadowMapInstanced<T>(
RenderTargetCube pointShadowCubeMap,
PerspectiveCamera camera,
T drawable,
IEnumerable<Matrix> modelTransforms,
VertexBuffer instanceVertexBuffer,
int numInstances,
PointLight pointLight
) where T : ICullable, IIndexDrawable
) where T : ICullable, IIndexDrawable
{
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
@ -885,36 +954,28 @@ namespace Kav
break;
}
LinearDepthEffect.View = Matrix.CreateLookAt(
LinearDepthEffectInstanced.View = Matrix.CreateLookAt(
pointLight.Position,
pointLight.Position + targetDirection,
targetUpDirection
);
LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
LinearDepthEffectInstanced.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver2,
1,
0.1f,
25f // FIXME: magic value
);
LinearDepthEffect.FarPlane = 25f;
LinearDepthEffect.LightPosition = pointLight.Position;
LinearDepthEffectInstanced.FarPlane = 25f;
LinearDepthEffectInstanced.LightPosition = pointLight.Position;
// TODO: set up instancing
// var numInstances = FillAndSetBuffersForInstancing(
// GraphicsDevice,
// camera,
// drawable,
// transforms,
// VertexPos
// );
// RenderInstanced(
// GraphicsDevice,
// camera,
// modelTransforms,
// LinearDepthEffect
// );
RenderInstanced(
GraphicsDevice,
drawable,
instanceVertexBuffer,
numInstances,
LinearDepthEffectInstanced
);
}
}
@ -933,19 +994,27 @@ namespace Kav
}
}
private static IEnumerable<Matrix> FrustumCull<T>(
private static bool FrustumCull<T>(
BoundingFrustum boundingFrustum,
T cullable,
IEnumerable<Matrix> transforms
) where T : ICullable
{
foreach (var transform in transforms)
Matrix transform
) where T : ICullable {
var boundingBox = TransformedBoundingBox(cullable.BoundingBox, transform);
var containment = boundingFrustum.Contains(boundingBox);
return (containment == ContainmentType.Disjoint);
}
private static IEnumerable<T> FrustumCull<T>(
BoundingFrustum boundingFrustum,
IEnumerable<T> cullableTransformables
) where T : ICullable, ITransformable {
foreach (var cullableTransformable in cullableTransformables)
{
var boundingBox = TransformedBoundingBox(cullable.BoundingBox, transform);
var boundingBox = TransformedBoundingBox(cullableTransformable.BoundingBox, cullableTransformable.TransformMatrix);
var containment = boundingFrustum.Contains(boundingBox);
if (containment != ContainmentType.Disjoint)
{
yield return transform;
yield return cullableTransformable;
}
}
}

View File

@ -135,6 +135,18 @@ namespace Kav
}
}
public static byte[] LinearDepthEffectInstanced
{
get
{
if (linearDepthEffectInstanced == null)
{
linearDepthEffectInstanced = GetResource("LinearDepthEffectInstanced.fxb");
}
return linearDepthEffectInstanced;
}
}
public static byte[] SkyboxEffect
{
get
@ -159,6 +171,18 @@ namespace Kav
}
}
public static byte[] PaletteCrushEffect
{
get
{
if (paletteCrushEffect == null)
{
paletteCrushEffect = GetResource("PaletteCrushEffect.fxb");
}
return paletteCrushEffect;
}
}
public static byte[] UnitCubeModel
{
get
@ -171,6 +195,18 @@ namespace Kav
}
}
public static byte[] UnitSphereModel
{
get
{
if (unitSphereModel == null)
{
unitSphereModel = GetResource("UnitSphere.glb");
}
return unitSphereModel;
}
}
private static byte[] ambientLightEffect;
private static byte[] pointLightEffect;
private static byte[] directionalLightEffect;
@ -182,10 +218,13 @@ namespace Kav
private static byte[] simpleDepthEffect;
private static byte[] simpleDepthEffectInstanced;
private static byte[] linearDepthEffect;
private static byte[] linearDepthEffectInstanced;
private static byte[] skyboxEffect;
private static byte[] diffuseLitSpriteEffect;
private static byte[] paletteCrushEffect;
private static byte[] unitCubeModel;
private static byte[] unitSphereModel;
private static byte[] GetResource(string name)
{

18
Utils/QuaternionUtils.cs Normal file
View File

@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
namespace Kav.Utils
{
public static class QuaternionUtils
{
// assumes that the input vectors are normalized and orthogonal
public static Quaternion LookAt(in Vector3 forward, in Vector3 up)
{
Matrix orientation = Matrix.Identity;
orientation.Forward = forward;
orientation.Right = Vector3.Normalize(Vector3.Cross(forward, up));
orientation.Up = Vector3.Cross(orientation.Right, forward);
return Quaternion.CreateFromRotationMatrix(orientation);
}
}
}

View File

@ -8,5 +8,11 @@ namespace Kav
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2)
);
public static VertexDeclaration PositionTextureOffsetInstanceDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2),
new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 5)
);
}
}

View File

@ -5,24 +5,27 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PositionInstanceVertex : IVertexType, IHasTranslation
public struct PositionTextureOffsetInstanceVertex : IVertexType
{
VertexDeclaration IVertexType.VertexDeclaration
{
get
{
return VertexDeclarations.PositionInstanceDeclaration;
return VertexDeclarations.PositionTextureOffsetInstanceDeclaration;
}
}
public Vector3 Translation { get; set; }
public Vector2 UVOffset { get; set; }
public static readonly VertexDeclaration VertexDeclaration;
public PositionInstanceVertex(
Vector3 translation
public PositionTextureOffsetInstanceVertex(
Vector3 translation,
Vector2 uvOffset
) {
Translation = translation;
UVOffset = uvOffset;
}
}
}