Added circle rendering to Renderer2D

This commit is contained in:
Simon Lübeß
2021-10-14 23:01:08 +02:00
parent 84df8c111b
commit dcb8424c2d
3 changed files with 301 additions and 34 deletions
+205 -5
View File
@@ -57,6 +57,18 @@ namespace GlitchyEngine.Renderer
} }
} }
[CRepr]
struct CircleBatchVertex : BatchVertex
{
public float InnerRadius;
public this(Matrix transform, ColorRGBA color, Vector4 uvTransform, float innerRadius)
: base(transform, color, uvTransform)
{
InnerRadius = innerRadius;
}
}
enum DrawOrder enum DrawOrder
{ {
/// Immediately draw the sprites. /// Immediately draw the sprites.
@@ -80,6 +92,17 @@ namespace GlitchyEngine.Renderer
struct QueueQuad: this(Matrix Transform, ColorRGBA Color, Texture2D Texture, float Depth, Vector4 uvTransform) { } struct QueueQuad: this(Matrix Transform, ColorRGBA Color, Texture2D Texture, float Depth, Vector4 uvTransform) { }
struct QueueCircle : QueueQuad
{
public float InnerRadius;
public this(Matrix Transform, ColorRGBA Color, Texture2D Texture, float Depth, Vector4 uvTransform, float innerRadius)
: base(Transform, Color, Texture, Depth, uvTransform)
{
InnerRadius = innerRadius;
}
}
#if DEBUG #if DEBUG
private static bool s_initialized; private static bool s_initialized;
private static bool s_sceneRunning; private static bool s_sceneRunning;
@@ -87,28 +110,35 @@ namespace GlitchyEngine.Renderer
private static Effect s_textureColorEffect; private static Effect s_textureColorEffect;
private static Effect s_batchEffect; private static Effect s_batchEffect;
private static Effect s_circleBatchEffect;
private static GeometryBinding s_quadGeometry; private static GeometryBinding s_quadGeometry;
private static Texture2D s_whiteTexture; private static Texture2D s_whiteTexture;
private static GeometryBinding s_batchBinding; private static GeometryBinding s_batchBinding;
private static GeometryBinding s_circleBatchBinding;
private static VertexBuffer s_instanceBuffer; private static VertexBuffer s_instanceBuffer;
private static VertexBuffer s_circleInstanceBuffer;
private static BatchVertex[] s_rawInstances; private static BatchVertex[] s_rawInstances;
private static CircleBatchVertex[] s_rawCircleInstances;
private static uint32 s_setInstances; private static uint32 s_setInstances;
private static List<QueueQuad> s_instanceQueue; private static List<QueueQuad> s_instanceQueue;
private static List<QueueCircle> s_circleInstanceQueue;
private static DrawOrder s_drawOrder; private static DrawOrder s_drawOrder;
/// The effect that is currently used to draw the sprites. /// The effect that is currently used to draw the sprites.
private static Effect s_currentEffect; private static Effect s_currentEffect;
private static Effect s_currentCircleEffect;
private static void InitEffect() private static void InitEffect()
{ {
s_textureColorEffect = new Effect("content\\Shaders\\textureColor.hlsl"); s_textureColorEffect = new Effect("content\\Shaders\\textureColor.hlsl");
s_batchEffect = new Effect("content\\Shaders\\spritebatch.hlsl"); s_batchEffect = new Effect("content\\Shaders\\spritebatch.hlsl");
s_circleBatchEffect = new Effect("content\\Shaders\\circlebatch.hlsl");
} }
private static void InitGeometry() private static void InitGeometry()
@@ -146,6 +176,7 @@ namespace GlitchyEngine.Renderer
} }
private static void InitInstancingGeometry() private static void InitInstancingGeometry()
{
{ {
s_instanceBuffer = new VertexBuffer(typeof(BatchVertex), 1024, .Dynamic, .Write); s_instanceBuffer = new VertexBuffer(typeof(BatchVertex), 1024, .Dynamic, .Write);
s_instanceBuffer.SetData(0); s_instanceBuffer.SetData(0);
@@ -179,6 +210,42 @@ namespace GlitchyEngine.Renderer
s_instanceQueue = new List<QueueQuad>(1024); s_instanceQueue = new List<QueueQuad>(1024);
} }
{
s_circleInstanceBuffer = new VertexBuffer(typeof(BatchVertex), 1024, .Dynamic, .Write);
s_circleInstanceBuffer.SetData(0);
VertexElement[] vertexElements = new .(
VertexElement(.R32G32_Float, "POSITION", false, 0, 0, 0, .PerVertexData, 0),
VertexElement(.R32G32_Float, "TEXCOORD", false, 0, 0, (.)-1, .PerVertexData, 0),
VertexElement(.R32G32B32A32_Float, "TRANSFORM", false, 0, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32G32B32A32_Float, "TRANSFORM", false, 1, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32G32B32A32_Float, "TRANSFORM", false, 2, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32G32B32A32_Float, "TRANSFORM", false, 3, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32G32B32A32_Float, "COLOR", false, 0, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32G32B32A32_Float, "TEXCOORD", false, 1, 1, (.)-1, .PerInstanceData, 1),
VertexElement(.R32_Float, "TEXCOORD", false, 2, 1, (.)-1, .PerInstanceData, 1)
);
s_circleBatchBinding = new GeometryBinding();
s_circleBatchBinding.SetPrimitiveTopology(.TriangleList);
using(var circleBatchLayout = new VertexLayout(vertexElements, true, s_circleBatchEffect.VertexShader))
{
s_circleBatchBinding.SetVertexLayout(circleBatchLayout);
}
s_circleBatchBinding.SetVertexBufferSlot(s_quadGeometry.GetVertexBuffer(0), 0);
s_circleBatchBinding.SetIndexBuffer(s_quadGeometry.GetIndexBuffer(), 0);
s_circleBatchBinding.SetVertexBufferSlot(s_circleInstanceBuffer, 1);
s_rawCircleInstances = new CircleBatchVertex[1024];
s_circleInstanceQueue = new List<QueueCircle>(1024);
}
}
private static void InitWhitetexture() private static void InitWhitetexture()
{ {
// Create a texture with a single white pixel // Create a texture with a single white pixel
@@ -225,25 +292,31 @@ namespace GlitchyEngine.Renderer
s_textureColorEffect.ReleaseRef(); s_textureColorEffect.ReleaseRef();
s_batchEffect.ReleaseRef(); s_batchEffect.ReleaseRef();
s_circleBatchEffect.ReleaseRef();
s_quadGeometry.ReleaseRef(); s_quadGeometry.ReleaseRef();
s_whiteTexture.ReleaseRef(); s_whiteTexture.ReleaseRef();
s_batchBinding.ReleaseRef(); s_batchBinding.ReleaseRef();
s_circleBatchBinding.ReleaseRef();
s_instanceBuffer.ReleaseRef(); s_instanceBuffer.ReleaseRef();
s_circleInstanceBuffer.ReleaseRef();
delete s_rawInstances; delete s_rawInstances;
delete s_rawCircleInstances;
delete s_instanceQueue; delete s_instanceQueue;
delete s_circleInstanceQueue;
s_currentEffect?.ReleaseRef(); s_currentEffect?.ReleaseRef();
s_currentCircleEffect?.ReleaseRef();
#if DEBUG #if DEBUG
s_initialized = false; s_initialized = false;
#endif #endif
} }
public static void BeginScene(OrthographicCamera camera, DrawOrder drawOrder = .SortByTexture, Effect effect = null) public static void BeginScene(OrthographicCamera camera, DrawOrder drawOrder = .SortByTexture, Effect effect = null, Effect circleEffect = null)
{ {
Log.EngineLogger.AssertDebug(s_initialized, "Renderer2D was not initialized."); Log.EngineLogger.AssertDebug(s_initialized, "Renderer2D was not initialized.");
Log.EngineLogger.AssertDebug(!s_sceneRunning, "You have to call EndScene before you can make another call to BeginScene."); Log.EngineLogger.AssertDebug(!s_sceneRunning, "You have to call EndScene before you can make another call to BeginScene.");
@@ -260,7 +333,18 @@ namespace GlitchyEngine.Renderer
s_currentEffect = s_batchEffect..AddRef(); s_currentEffect = s_batchEffect..AddRef();
} }
s_currentCircleEffect?.ReleaseRef();
if(circleEffect != null)
{
s_currentCircleEffect = effect..AddRef();
}
else
{
s_currentCircleEffect = s_circleBatchEffect..AddRef();
}
s_currentEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); s_currentEffect.Variables["ViewProjection"].SetData(camera.ViewProjection);
s_currentCircleEffect.Variables["ViewProjection"].SetData(camera.ViewProjection);
s_textureColorEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); s_textureColorEffect.Variables["ViewProjection"].SetData(camera.ViewProjection);
s_drawOrder = drawOrder; s_drawOrder = drawOrder;
@@ -295,6 +379,13 @@ namespace GlitchyEngine.Renderer
s_instanceQueue.Add(QueueQuad(transform, color, texture ?? s_whiteTexture, depth, uvTransform)); s_instanceQueue.Add(QueueQuad(transform, color, texture ?? s_whiteTexture, depth, uvTransform));
} }
/// Adds a circle instance to the instance queue.
[Inline]
private static void QueueCircleInstance(Matrix transform, ColorRGBA color, Texture2D texture, float depth, Vector4 uvTransform, float innerRadius)
{
s_circleInstanceQueue.Add(QueueCircle(transform, color, texture ?? s_whiteTexture, depth, uvTransform, innerRadius));
}
private static void FlushInstances() private static void FlushInstances()
{ {
if(s_setInstances == 0) if(s_setInstances == 0)
@@ -310,6 +401,22 @@ namespace GlitchyEngine.Renderer
s_setInstances = 0; s_setInstances = 0;
} }
private static void FlushCircleInstances()
{
if(s_setInstances == 0)
return;
s_circleInstanceBuffer.SetData<CircleBatchVertex>(s_rawCircleInstances.Ptr, s_setInstances, 0, .WriteDiscard);
s_currentCircleEffect.Bind(Renderer._context);
s_circleBatchBinding.InstanceCount = s_setInstances;
s_circleBatchBinding.Bind();
RenderCommand.DrawIndexedInstanced(s_circleBatchBinding);
s_setInstances = 0;
}
// Quad comparison
private static int TextureComparison(QueueQuad lhs, QueueQuad rhs) private static int TextureComparison(QueueQuad lhs, QueueQuad rhs)
{ {
return (int)Internal.UnsafeCastToPtr(lhs.Texture) - (int)Internal.UnsafeCastToPtr(rhs.Texture); return (int)Internal.UnsafeCastToPtr(lhs.Texture) - (int)Internal.UnsafeCastToPtr(rhs.Texture);
@@ -323,18 +430,33 @@ namespace GlitchyEngine.Renderer
return lhs.Depth <=> rhs.Depth; return lhs.Depth <=> rhs.Depth;
} }
private static void SortQuads() // Circle comparison
private static int TextureComparison(QueueCircle lhs, QueueCircle rhs)
{ {
s_instanceQueue.Sort(scope => TextureComparison); return (int)Internal.UnsafeCastToPtr(lhs.Texture) - (int)Internal.UnsafeCastToPtr(rhs.Texture);
}
private static int BackToFrontComparison(QueueCircle lhs, QueueCircle rhs)
{
return rhs.Depth <=> lhs.Depth;
}
private static int FrontToBackComparison(QueueCircle lhs, QueueCircle rhs)
{
return lhs.Depth <=> rhs.Depth;
}
private static void SortInstances()
{
switch(s_drawOrder) switch(s_drawOrder)
{ {
case .SortByTexture: case .SortByTexture:
s_instanceQueue.Sort(scope => TextureComparison); s_instanceQueue.Sort(scope => TextureComparison);
s_circleInstanceQueue.Sort(scope => TextureComparison);
case .BackToFront: case .BackToFront:
s_instanceQueue.Sort(scope => BackToFrontComparison); s_instanceQueue.Sort(scope => BackToFrontComparison);
s_circleInstanceQueue.Sort(scope => BackToFrontComparison);
case .FrontToBack: case .FrontToBack:
s_instanceQueue.Sort(scope => FrontToBackComparison); s_instanceQueue.Sort(scope => FrontToBackComparison);
s_circleInstanceQueue.Sort(scope => FrontToBackComparison);
case .Immediate: case .Immediate:
default: default:
Log.EngineLogger.Error("Unknown instance draw order."); Log.EngineLogger.Error("Unknown instance draw order.");
@@ -343,10 +465,19 @@ namespace GlitchyEngine.Renderer
private static void DrawDeferred() private static void DrawDeferred()
{ {
if(s_instanceQueue.IsEmpty) if(s_instanceQueue.IsEmpty && s_circleInstanceQueue.IsEmpty)
return; return;
SortQuads(); SortInstances();
DrawDeferredQuads();
DrawDeferredCircles();
}
private static void DrawDeferredQuads()
{
if(s_instanceQueue.IsEmpty)
return;
Texture2D texture = s_instanceQueue[0].Texture; Texture2D texture = s_instanceQueue[0].Texture;
s_currentEffect.SetTexture("Texture", texture); s_currentEffect.SetTexture("Texture", texture);
@@ -379,6 +510,42 @@ namespace GlitchyEngine.Renderer
s_instanceQueue.Clear(); s_instanceQueue.Clear();
} }
private static void DrawDeferredCircles()
{
if(s_circleInstanceQueue.IsEmpty)
return;
Texture2D texture = s_circleInstanceQueue[0].Texture;
s_currentCircleEffect.SetTexture("Texture", texture);
s_setInstances = 0;
for(int i < s_circleInstanceQueue.Count)
{
var circle = ref s_circleInstanceQueue[i];
// flush every time the texture changes
if(circle.Texture != texture)
{
FlushCircleInstances();
texture = circle.Texture;
s_currentCircleEffect.SetTexture("Texture", texture);
}
s_rawCircleInstances[s_setInstances++] = .(circle.Transform, circle.Color, circle.uvTransform, circle.InnerRadius);
if(s_setInstances == s_rawCircleInstances.Count)
{
FlushCircleInstances();
}
}
FlushCircleInstances();
s_circleInstanceQueue.Clear();
}
private static void DrawImmediate(Matrix transform, ColorRGBA color, Texture2D texture, Vector4 uvTransform) private static void DrawImmediate(Matrix transform, ColorRGBA color, Texture2D texture, Vector4 uvTransform)
{ {
s_textureColorEffect.SetTexture("Texture", texture ?? s_whiteTexture); s_textureColorEffect.SetTexture("Texture", texture ?? s_whiteTexture);
@@ -395,6 +562,8 @@ namespace GlitchyEngine.Renderer
// Primitives // Primitives
// Quad
public static void DrawQuad(Vector2 position, Vector2 size, float rotation, ColorRGBA color) public static void DrawQuad(Vector2 position, Vector2 size, float rotation, ColorRGBA color)
{ {
DrawQuad(Vector3(position, 0.0f), size, rotation, s_whiteTexture, color); DrawQuad(Vector3(position, 0.0f), size, rotation, s_whiteTexture, color);
@@ -449,5 +618,36 @@ namespace GlitchyEngine.Renderer
{ {
DrawQuad(position + Vector3(size.X / 2, size.Y / -2, 0), size, rotation, texture, color, uvTransform); DrawQuad(position + Vector3(size.X / 2, size.Y / -2, 0), size, rotation, texture, color, uvTransform);
} }
// Circle
public static void DrawCircle(Vector2 position, Vector2 size, ColorRGBA color, float innerRadius = 1.0f)
{
DrawCircle(Vector3(position, 0.0f), size, s_whiteTexture, color, innerRadius);
}
public static void DrawCircle(Vector3 position, Vector2 size, ColorRGBA color, float innerRadius = 1.0f)
{
DrawCircle(position, size, s_whiteTexture, color, innerRadius);
}
public static void DrawCircle(Vector2 position, Vector2 size, float rotation, Texture2D texture, ColorRGBA color = .White, float innerRadius = 1.0f, Vector4 uvTransform = .(0, 0, 1, 1))
{
DrawCircle(Vector3(position, 0.0f), size, texture, color, innerRadius, uvTransform);
}
public static void DrawCircle(Vector3 position, Vector2 size, Texture2D texture, ColorRGBA color = .White, float innerRadius = 1.0f, Vector4 uvTransform = .(0, 0, 1, 1))
{
Log.EngineLogger.AssertDebug(s_sceneRunning, "Missing call of BeginScene.");
Matrix transform = Matrix.Translation(position) * Matrix.Scaling(size.X, size.Y, 1.0f);
QueueCircleInstance(transform, color, texture, position.Z, uvTransform, innerRadius);
if(s_drawOrder == .Immediate)
{
DrawDeferred();
}
}
} }
} }
+59
View File
@@ -0,0 +1,59 @@
Texture2D Texture : register(t0);
SamplerState Sampler : register(s0);
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
};
struct VS_Input
{
float2 Position : POSITION;
float2 Texcoord : TEXCOORD0;
float4x4 Transform : TRANSFORM;
float4 Color : COLOR;
float4 UVTransform : TEXCOORD1;
float InnerRadius : TEXCOORD2;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 RawPos : TEXCOORD0;
float2 Texcoord : TEXCOORD1;
float4 Color : COLOR;
float InnerRadius : TEXCOORD2;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
output.RawPos = input.Position;
output.Color = input.Color;
output.InnerRadius = input.InnerRadius;
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
float2 uv = input.RawPos * 2;
float distance = 1.0f - length(uv);
// Smoothing edges based on derivative (not sure if RawPos is the best value for this)
float f = fwidth(input.RawPos.x) + fwidth(input.RawPos.y);
float amount = smoothstep(0.0f, f, distance);
amount *= smoothstep(input.InnerRadius + f, input.InnerRadius, distance);
float4 color = Texture.Sample(Sampler, input.Texcoord) * input.Color;
color.a *= amount;
return color;
}
#effect[VS=VS, PS=PS]
+8
View File
@@ -108,11 +108,17 @@ namespace Sandbox
Renderer2D.DrawQuad(Vector3(0, 0, 1), Vector2(10), 0, _checkerTexture, .White, .(0, 0, 1, 1)); Renderer2D.DrawQuad(Vector3(0, 0, 1), Vector2(10), 0, _checkerTexture, .White, .(0, 0, 1, 1));
Renderer2D.DrawCircle(Vector3(0, 0, 0f), Vector2(5), _checkerTexture, .LightBlue, innerRadius, .(0, 0, 1, 1));
Renderer2D.DrawCircle(Vector3(-2, -2, -2f), Vector2(1), .GreenYellow);
FontRenderer.DrawText(fonty, "Hallo! gjy", 0, 0, 64, .White, .White); FontRenderer.DrawText(fonty, "Hallo! gjy", 0, 0, 64, .White, .White);
Renderer2D.EndScene(); Renderer2D.EndScene();
} }
float innerRadius = 0.5f;
public override void OnEvent(Event event) public override void OnEvent(Event event)
{ {
EventDispatcher dispatcher = EventDispatcher(event); EventDispatcher dispatcher = EventDispatcher(event);
@@ -128,6 +134,8 @@ namespace Sandbox
{ {
ImGui.Begin("Test"); ImGui.Begin("Test");
ImGui.DragFloat("Inner Radius", &innerRadius, 0.01f);
ImGui.ColorPicker4("Color", *(float[4]*)&_squareColor0); ImGui.ColorPicker4("Color", *(float[4]*)&_squareColor0);
_squareColor1 = ColorRGBA.White - _squareColor0; _squareColor1 = ColorRGBA.White - _squareColor0;