diff --git a/GlitchyEditor/content/Models/plane.glb b/GlitchyEditor/content/Models/plane.glb new file mode 100644 index 0000000..33643af Binary files /dev/null and b/GlitchyEditor/content/Models/plane.glb differ diff --git a/GlitchyEditor/content/Shaders/myEffect.hlsl b/GlitchyEditor/content/Shaders/myEffect.hlsl new file mode 100644 index 0000000..81829ca --- /dev/null +++ b/GlitchyEditor/content/Shaders/myEffect.hlsl @@ -0,0 +1,73 @@ +cbuffer SceneConstants +{ + float4x4 ViewProjection = float4x4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); +} + +cbuffer ObjectConstants +{ + float4x4 Transform; +} + +cbuffer Constants +{ + float4 BaseColor = float4(1, 0, 1, 1); + //float3 LightDir = float3(0, 1, 0); +} + +struct VS_IN +{ + float3 Position : POSITION; + float3 Normal : NORMAL; +}; + +struct PS_IN +{ + float4 Position : SV_POSITION; + float3 WorldPosition : TEXCOORD0; + float3 Normal : NORMAL; +}; + +PS_IN VS(VS_IN input) +{ + PS_IN output; + + float4 worldPosition = mul(Transform, float4(input.Position, 1)); + + output.Position = mul(ViewProjection, worldPosition); + output.WorldPosition = worldPosition.xyz / worldPosition.w; + output.Normal = mul((float3x3)Transform, input.Normal); + + return output; +} + +struct PS_OUT +{ + float4 Color : SV_TARGET0; + float4 Normal : SV_TARGET1; + float4 Position : SV_TARGET2; +}; + +PS_OUT PS(PS_IN input) +{ + input.Normal = normalize(input.Normal); + + float f = distance(input.Normal, input.Normal); + + //float shading = dot(LightDir, input.Normal); + //float shading = dot(LightDir, LightDir) + 1; + //shading = clamp(shading, 0.0f, 1.0f); + + //return float4(BaseColor.rgb * shading, 1.0f); + + PS_OUT output = (PS_OUT)0; + output.Color = BaseColor; + output.Normal = float4(input.Normal, 1); + output.Position = float4(input.WorldPosition, 1); + + return output; +} + +#effect[VS=VS,PS=PS] diff --git a/GlitchyEditor/content/Shaders/simpleLight.hlsl b/GlitchyEditor/content/Shaders/simpleLight.hlsl new file mode 100644 index 0000000..0f7fef5 --- /dev/null +++ b/GlitchyEditor/content/Shaders/simpleLight.hlsl @@ -0,0 +1,46 @@ +Texture2D Colors : register(t0); +Texture2D Normals : register(t1); +Texture2D Positions : register(t2); + +SamplerState Sampler : register(s0); + +cbuffer Constants +{ + float3 LightDir = float3(0, 1, 0); +} + +struct VS_IN +{ + float2 Position : POSITION; + float2 TexCoord : TEXCOORD0; +}; + +struct PS_IN +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD; +}; + +PS_IN VS(VS_IN input) +{ + PS_IN output; + + output.Position = float4(input.Position, 0, 1); + output.TexCoord = input.TexCoord; + + return output; +} + +float4 PS(PS_IN input) : SV_TARGET +{ + float4 color = Colors.Sample(Sampler, input.TexCoord); + float3 normal = Normals.Sample(Sampler, input.TexCoord); + + //float shading = dot(LightDir, normal) + 100; + float shading = dot(float3(0, 1, 0), normal); + shading = clamp(shading, 0.0f, 1.0f); + + return float4(color.rgb * shading, 1); +} + +#effect[VS=VS,PS=PS] diff --git a/GlitchyEditor/content/Shaders/textureViewerShader.hlsl b/GlitchyEditor/content/Shaders/textureViewerShader.hlsl new file mode 100644 index 0000000..afd6945 --- /dev/null +++ b/GlitchyEditor/content/Shaders/textureViewerShader.hlsl @@ -0,0 +1,52 @@ +Texture2D Texture : register(t0); +SamplerState Sampler : register(s0); + +cbuffer Constants +{ + float4x4 ViewProjection; + + float ColorOffset = 0.5f; + float AlphaOffset = 0.0f; + float ColorScale = 0.5f; + float AlphaScale = 1.0f; + + float2 TextureSizeInPixels; +} + +struct VS_Input +{ + float2 Position : POSITION; + float2 Texcoord : TEXCOORD0; + float4x4 Transform : TRANSFORM; + float4 Color : COLOR; + float4 UVTransform : TEXCOORD1; +}; + +struct PS_Input +{ + float4 Position : SV_Position; + float2 Texcoord : TEXCOORD0; + float4 Color : COLOR; +}; + +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.Color = input.Color; + + return output; +} + +float4 PS(PS_Input input) : SV_Target0 +{ + float4 color = Texture.Sample(Sampler, input.Texcoord); + + float4 final = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale); + + return final; +} + +#effect[VS=VS, PS=PS] diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 20a2b29..2092ed2 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -7,6 +7,9 @@ using GlitchyEngine.ImGui; using GlitchyEngine.Math; using GlitchyEngine.Renderer; using GlitchyEngine.World; +using GlitchyEngine.Content; +using System.Collections; +using GlitchyEngine.Renderer.Animation; namespace GlitchyEditor { @@ -83,6 +86,7 @@ namespace GlitchyEditor camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f); camera.Primary = true; camera.FixedAspectRatio = false; + camera.RenderTarget = _viewportTarget; let transform = _cameraEntity.GetComponent(); transform.Position = .(0, 0, -5); @@ -96,6 +100,7 @@ namespace GlitchyEditor camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 1000.0f); camera.Primary = false; camera.FixedAspectRatio = false; + camera.RenderTarget = _viewportTarget; let transform = _otherCameraEntity.GetComponent(); transform.Position = .(0, 0, -5); @@ -104,6 +109,31 @@ namespace GlitchyEditor } InitEditor(); + + TestEntitiesWithModels(); + } + + private void TestEntitiesWithModels() + { + Effect myEffect = new Effect("content/Shaders/myEffect.hlsl")..ReleaseRefNoDelete(); + + Material mat = new Material(myEffect)..ReleaseRefNoDelete(); + mat.SetVariable("BaseColor", Vector4(1, 0, 1, 1)); + //mat.SetVariable("LightDir", Vector3(1, 1, 0).Normalized()); + + List clips = scope .(); + + ModelLoader.LoadModel("content/Models/plane.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips); + + mat = new Material(myEffect)..ReleaseRefNoDelete(); + mat.SetVariable("BaseColor", Vector4(1, 1, 0, 1)); + //mat.SetVariable("LightDir", Vector3(0, 1, 0).Normalized()); + + clips = scope .(); + + ModelLoader.LoadModel("content/Models/plane.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips); + + ClearAndReleaseItems!(clips); } private void InitGraphics() @@ -184,9 +214,13 @@ namespace GlitchyEditor } ImGui.ID _mainDockspaceId; + + TextureViewer viewer = new TextureViewer() ~ delete _; private bool OnImGuiRender(ImGuiRenderEvent event) { + viewer.ViewTexture(Renderer.[Friend]_gBuffer.Normal); + ImGui.Begin("Test"); static bool cameraA = true; diff --git a/GlitchyEditor/src/TextureViewer.bf b/GlitchyEditor/src/TextureViewer.bf new file mode 100644 index 0000000..022234b --- /dev/null +++ b/GlitchyEditor/src/TextureViewer.bf @@ -0,0 +1,246 @@ +using GlitchyEngine.Renderer; +using GlitchyEngine; +using ImGui; +using GlitchyEngine.Math; +using System; + +namespace GlitchyEditor +{ + class TextureViewer + { + enum BackgroundMode : int32 + { + White, + Black, + Checkerboard + } + + enum SampleMode : int32 + { + Point, + Linear + } + + GraphicsContext _context ~ _.ReleaseRef(); + + Effect _effect ~ _.ReleaseRef(); + + float _zoom = 1.0f; + + BackgroundMode _backgroundMode = .Checkerboard; + + SampleMode _sampleMode = .Linear; + + RenderTarget2D _target ~ _?.ReleaseRef(); + // TODO: we don't need depth! + DepthStencilTarget _depth ~ _?.ReleaseRef(); + + SamplerState _samplerPoint ~ _.ReleaseRef(); + SamplerState _samplerLinear ~ _.ReleaseRef(); + + public this() + { + _context = Renderer.[Friend]_context..AddRef(); + + InitEffect(); + InitState(); + // TODO: rasterizerstate and depthstencilstate + } + + private void InitEffect() + { + _effect = new Effect("content\\Shaders\\textureViewerShader.hlsl"); + } + + private void InitState() + { + SamplerStateDescription desc = .(); + desc.MagFilter = .Linear; + desc.MinFilter = .Linear; + _samplerLinear = SamplerStateManager.GetSampler(desc); + + desc.MagFilter = .Point; + desc.MinFilter = .Point; + _samplerPoint = SamplerStateManager.GetSampler(desc); + } + + Vector2 _position; + + bool _moving; + + float _colorOffset = 0; + float _colorScale = 1; + float _alphaOffset = 0; + float _alphaScale = 1; + + public void ViewTexture(Texture viewedTexture) + { + ImGui.Begin("Texture Viewer"); + + ImGui.SliderFloat("Zoom", &_zoom, 0.01f, 100.0f); + + char8*[] items = scope .("White", "Black", "Checkerboard"); + + ImGui.Combo("Background", (.)&_backgroundMode, items.Ptr, (.)items.Count); + + items = scope .("Point", "Linear"); + + ImGui.Combo("Sampler", (.)&_sampleMode, items.Ptr, (.)items.Count); + + ImGui.SliderFloat2("Color offset and scale", *(float[2]*)&_colorOffset, -1.0f, 1.0f); + ImGui.SliderFloat2("Alpha offset and scale", *(float[2]*)&_alphaOffset, -1.0f, 1.0f); + + ImGui.SliderFloat2("Position", *(float[2]*)&_position, 2 * -Math.Max(viewedTexture.Width, viewedTexture.Height) * _zoom, 2 * Math.Max(viewedTexture.Width, viewedTexture.Height) * _zoom); + + ImGui.BeginChild("imageChild"); + + UpdateInput(); + + var viewportSize = ImGui.GetContentRegionAvail(); + + viewportSize.x = Math.Max(viewportSize.x, 1); + viewportSize.y = Math.Max(viewportSize.y, 1); + + if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height) + { + _target?.ReleaseRef(); + _target = new RenderTarget2D(.(.R8G8B8A8_UNorm, (.)viewportSize.x, (.)viewportSize.y)); + _depth?.ReleaseRef(); + _depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm); + } + + RenderTexture(viewedTexture); + + ImGui.Image(_target, viewportSize); + + ImGui.EndChild(); + + ImGui.End(); + } + + float lastWheel; + + private void UpdateInput() + { + var windowPos = ImGui.GetWindowPos(); + var mousePos = ImGui.GetIO().MousePos; + Vector2 mouseInWindow = .(mousePos.x - windowPos.x, mousePos.y - windowPos.y); + + bool windowHovered = ImGui.IsWindowHovered(); + + if(windowHovered && Input.IsMouseButtonPressing(.MiddleButton)) + { + _moving = true; + } + else if(Input.IsMouseButtonReleased(.MiddleButton)) + { + _moving = false; + } + + if(windowHovered || _moving) + { + float mouseWheel = ImGui.GetIO().MouseWheel; + + float delta = mouseWheel - lastWheel; + + if(delta != 0) + { + _position -= mouseInWindow; + _position /= _zoom; + + _zoom *= Math.Pow(1.1f, delta); + + _position *= _zoom; + _position += mouseInWindow; + } + + } + + if(_moving) + { + Point movement = Input.GetMouseMovement(); + + _position.X += movement.X; + _position.Y += movement.Y; + } + } + + OrthographicCamera _camera = new OrthographicCamera() ~ delete _; + + private void RenderTexture(Texture viewedTexture) + { + Viewport vp = .(0, 0, _target.Width, _target.Height); + RenderCommand.SetViewport(vp); + + // TODO: don't clear pink! + RenderCommand.Clear(_target, .Pink); + RenderCommand.Clear(_depth, .Depth, 1.0f, 0); + + //_target.Bind(); + _context.SetRenderTarget(_target); + _context.SetDepthStencilTarget(_depth); + _context.BindRenderTargets(); + + Vector2 textureSize = Vector2(viewedTexture.Width, viewedTexture.Height); + Vector2 zoomedTextureSize = textureSize * _zoom; + + Vector2 targetSize = Vector2(_target.Width, _target.Height); + + _effect.Variables["ColorOffset"].SetData(_colorOffset); + _effect.Variables["ColorScale"].SetData(_colorScale); + _effect.Variables["AlphaOffset"].SetData(_alphaOffset); + _effect.Variables["AlphaScale"].SetData(_alphaScale); + + _camera.Left = 0; + _camera.Top = 0; + _camera.Right = targetSize.X; + _camera.Bottom = -targetSize.Y; + _camera.NearPlane = -5; + _camera.FarPlane = 5; + _camera.Update(); + + Renderer2D.BeginScene(_camera); + + switch(_backgroundMode) + { + case .Black: + Renderer2D.DrawQuadPivotCorner(Vector3(0, 0, 1), targetSize, 0, .Black); + case .White: + Renderer2D.DrawQuadPivotCorner(Vector3(0, 0, 1), targetSize, 0, .White); + case .Checkerboard: + float quadSize = 50.0f; + + Vector2 numQuads = (targetSize / 500f) * 10f; + + for(float x = 0; x < numQuads.X; x++) + { + for(float y = 0; y < numQuads.Y; y++) + { + Renderer2D.DrawQuadPivotCorner(Vector3(x * quadSize, -y * quadSize, 1), quadSize.XX, 0, ((x + y) % 2 == 0) ? .White : .Gray); + } + } + break; + } + + Renderer2D.EndScene(); + + var sampler = viewedTexture.SamplerState; + + switch(_sampleMode) + { + case .Point: + viewedTexture.SamplerState = _samplerPoint; + case .Linear: + viewedTexture.SamplerState = _samplerLinear; + } + + Renderer2D.BeginScene(_camera, .SortByTexture, _effect); + + Renderer2D.DrawQuad(Vector3(_position * .(1, -1), 0), zoomedTextureSize, 0, viewedTexture); + + Renderer2D.EndScene(); + + viewedTexture.SamplerState = sampler; + } + } +} diff --git a/GlitchyEngine/src/Content/ModelLoader.bf b/GlitchyEngine/src/Content/ModelLoader.bf index 8cfe94f..7ced41d 100644 --- a/GlitchyEngine/src/Content/ModelLoader.bf +++ b/GlitchyEngine/src/Content/ModelLoader.bf @@ -37,7 +37,7 @@ namespace GlitchyEngine.Content { EcsEntity entity = world.NewEntity(); -#if DEBUG +//#if DEBUG var nameComponent = world.AssignComponent(entity); if (node.Name != null) @@ -48,7 +48,7 @@ namespace GlitchyEngine.Content { nameComponent.SetName("Unnamed Node"); } -#endif +//#endif if(parentEntity.HasValue) { diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf index 71ee6fc..be0da5d 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf @@ -129,6 +129,14 @@ namespace GlitchyEngine.Renderer SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget); } } + + public override void UnbindRenderTargets() + { + for (var rt in ref _renderTargets) + { + rt = null; + } + } public override void BindRenderTargets() { @@ -193,7 +201,7 @@ namespace GlitchyEngine.Renderer for(let entry in shader.Textures) { - _textures[entry.Index] = entry.Texture?.nativeResourceView; + _textures[entry.Index] = entry.Texture?._nativeResourceView; _samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState; if(entry.Index >= _textureCount) diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf index 893d526..3b3bbde 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf @@ -75,6 +75,13 @@ namespace GlitchyEngine.Renderer _context.SetDepthStencilTarget(target); } + public override void UnbindRenderTargets() + { + Debug.Profiler.ProfileRendererFunction!(); + + _context.UnbindRenderTargets(); + } + public override void BindRenderTargets() { Debug.Profiler.ProfileRendererFunction!(); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf index 684479f..1b6baf6 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11Shader.bf @@ -49,6 +49,8 @@ namespace GlitchyEngine.Renderer StringView str = StringView((char8*)errorBlob.GetBufferPointer(), (int)errorBlob.GetBufferSize()); Log.EngineLogger.Error($"Failed to compile Shader: Error Code({(int)result}): {result} | Error Message: {str}"); } + + Log.EngineLogger.Assert(shaderBlob != null, "Shader compilation failed."); } protected internal void Reflect(ID3DBlob* shaderCode) diff --git a/GlitchyEngine/src/Renderer/GraphicsContext.bf b/GlitchyEngine/src/Renderer/GraphicsContext.bf index a5de076..5d5f7b3 100644 --- a/GlitchyEngine/src/Renderer/GraphicsContext.bf +++ b/GlitchyEngine/src/Renderer/GraphicsContext.bf @@ -34,6 +34,11 @@ namespace GlitchyEngine.Renderer * @param setDepthTarget If set to true the depth stencil target of the given renderTarget will be bound (only applies if slot is 0). */ public extern void SetRenderTarget(RenderTarget2D renderTarget, int slot = 0, bool setDepthTarget = true); + + /** + * Unbinds all rendertargets. + */ + public extern void UnbindRenderTargets(); /** * Binds all. diff --git a/GlitchyEngine/src/Renderer/RenderCommand.bf b/GlitchyEngine/src/Renderer/RenderCommand.bf index bf4b234..130c4bb 100644 --- a/GlitchyEngine/src/Renderer/RenderCommand.bf +++ b/GlitchyEngine/src/Renderer/RenderCommand.bf @@ -63,6 +63,11 @@ namespace GlitchyEngine.Renderer _rendererAPI.SetDepthStencilTarget(target); } + public static void UnbindRenderTargets() + { + _rendererAPI.UnbindRenderTargets(); + } + public static void BindRenderTargets() { _rendererAPI.BindRenderTargets(); diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index e3f462d..23e3e67 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -1,5 +1,7 @@ using GlitchyEngine.Math; using GlitchyEngine.World; +using System.Collections; +using System; namespace GlitchyEngine.Renderer { @@ -8,6 +10,8 @@ namespace GlitchyEngine.Renderer struct SceneConstants { public Matrix ViewProjection; + public Vector3 CameraPosition; + public RenderTarget2D CameraTarget; } struct ObjectConstants @@ -15,6 +19,57 @@ namespace GlitchyEngine.Renderer public Matrix Transform; } + class GBuffer + { + private uint32 _width; + private uint32 _height; + + public DepthStencilTarget DepthStencil; + public RenderTarget2D Color ~ _?.ReleaseRef(); + public RenderTarget2D Normal ~ _?.ReleaseRef(); + public RenderTarget2D Position ~ _?.ReleaseRef(); + + public void EnsureSize(uint32 width, uint32 height) + { + if (width <= _width && height <= _height) + return; + + if (_width == 0 || _height == 0) + { + // Note: Depth-Buffer in Color-Target for convenience + RenderTarget2DDescription colorDesc = .(.R8G8B8A8_UNorm, width, height, 1, 1, .D24_UNorm_S8_UInt); + Color = new RenderTarget2D(colorDesc); + + RenderTarget2DDescription normalDesc = .(.R32G32B32A32_Float, width, height); + Normal = new RenderTarget2D(normalDesc); + + RenderTarget2DDescription positionDesc = .(.R32G32B32A32_Float, width, height); + Position = new RenderTarget2D(positionDesc); + } + + _width = width; + _height = height; + + Color.Resize(_width, _height); + Normal.Resize(_width, _height); + Position.Resize(_width, _height); + + DepthStencil = Color.DepthStencilTarget; + } + + public void Bind() + { + RenderCommand.SetDepthStencilTarget(DepthStencil); + + RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTarget(Color, 0); + RenderCommand.SetRenderTarget(Normal, 1); + RenderCommand.SetRenderTarget(Position, 2); + + RenderCommand.BindRenderTargets(); + } + } + static internal GraphicsContext _context ~ _?.ReleaseRef(); //static Buffer _sceneConstants ~ _?.ReleaseRef(); @@ -27,6 +82,9 @@ namespace GlitchyEngine.Renderer static VertexBuffer LineVertices ~ _?.ReleaseRef(); static GeometryBinding LineGeometry ~ _?.ReleaseRef(); + static GBuffer _gBuffer ~ delete _; + static Effect TestFullscreenEffect ~ _?.ReleaseRef(); + public static void Init(GraphicsContext context, EffectLibrary effectLibrary) { Debug.Profiler.ProfileFunction!(); @@ -44,6 +102,9 @@ namespace GlitchyEngine.Renderer Renderer2D.Init(); InitLineRenderer(effectLibrary); + InitDeferredRenderer(effectLibrary); + + _gBuffer = new GBuffer(); } public static void Deinit() @@ -78,6 +139,50 @@ namespace GlitchyEngine.Renderer VertexLayout layout = new VertexLayout(vertexElements, true, LineEffect.VertexShader); LineGeometry.SetVertexLayout(layout..ReleaseRefNoDelete()); } + + private static GeometryBinding s_quadGeometry ~ _?.ReleaseRef(); + + static void InitDeferredRenderer(EffectLibrary effectLibrary) + { + TestFullscreenEffect = effectLibrary.Load("content\\Shaders\\simpleLight.hlsl"); + + s_quadGeometry = new GeometryBinding(); + s_quadGeometry.SetPrimitiveTopology(.TriangleList); + + using(var quadVertices = new VertexBuffer(typeof(Vector4), 4, .Immutable)) + { + Vector4[4] vertices = .( + .(-0.5f,-0.5f, 0, 1), + .(-0.5f, 0.5f, 0, 0), + .( 0.5f, 0.5f, 1, 0), + .( 0.5f,-0.5f, 1, 1) + ); + + quadVertices.SetData(vertices); + s_quadGeometry.SetVertexBufferSlot(quadVertices, 0); + } + + using(var quadIndices = new IndexBuffer(6, .Immutable)) + { + uint16[6] indices = .( + 0, 1, 2, + 2, 3, 0 + ); + + quadIndices.SetData(indices); + s_quadGeometry.SetIndexBuffer(quadIndices); + } + + VertexElement[] vertexElements = new .( + VertexElement(.R32G32_Float, "POSITION", false, 0, 0, 0, .PerVertexData, 0), + VertexElement(.R32G32_Float, "TEXCOORD", false, 0, 0, (.)-1, .PerVertexData, 0) + ); + + using (var quadBatchLayout = new VertexLayout(vertexElements, true, TestFullscreenEffect.VertexShader)) + { + s_quadGeometry.SetVertexLayout(quadBatchLayout); + } + } // TODO /*public static void BeginScene(EcsWorld world, EcsEntity cameraEntity) @@ -103,9 +208,98 @@ namespace GlitchyEngine.Renderer //_sceneConstants.Update(); } + public static void BeginScene(Camera camera, Matrix transform, RenderTarget2D renderTarget) + { + Debug.Profiler.ProfileRendererFunction!(); + + Matrix viewProjection = camera.Projection * Matrix.Invert(transform); + _sceneConstants.ViewProjection = viewProjection; + _sceneConstants.CameraPosition = transform.Translation; + _sceneConstants.CameraTarget = renderTarget; + } + + public static int SortMeshes(SubmittedMesh left, SubmittedMesh right) + { + // TODO: Once Material "inheritance" is ready we could perhaps check how similar materials are (e.g. shared textures/variables/etc...) + // Similar thing could be done for Meshes. Both would probably require a different way to sort howerver?... + + int cmp = (int)Internal.UnsafeCastToPtr(left.Material) <=> (int)Internal.UnsafeCastToPtr(right.Material); + + // Material equal: Sort by Mesh + if (cmp == 0) + { + cmp = (int)Internal.UnsafeCastToPtr(left.Mesh) <=> (int)Internal.UnsafeCastToPtr(right.Mesh); + + // Material and Mesh equal: sort by distance + if (cmp == 0) + { + float distLeftSq = Vector3.DistanceSquared(_sceneConstants.CameraPosition, left.Transform.Translation); + float distRightSq = Vector3.DistanceSquared(_sceneConstants.CameraPosition, right.Transform.Translation); + + // Whether or not the values are squared doesn't affect the order (because square(-root) is a monotonic function) + cmp = distLeftSq <=> distRightSq; + } + } + + return 0; + } + public static void EndScene() { Debug.Profiler.ProfileRendererFunction!(); + + _queue.Sort(scope => SortMeshes); + + // Deferred renderer: + + // TODO: foreach light: draw shadow map + + // foreach camera: + // { + _gBuffer.EnsureSize(_sceneConstants.CameraTarget.Width, _sceneConstants.CameraTarget.Height); + _gBuffer.Bind(); + RenderCommand.Clear(_gBuffer.Color, .Color | .Depth, .Blue, 1, 0); + RenderCommand.Clear(_gBuffer.Normal, Color.Beige); + RenderCommand.Clear(_gBuffer.Position, Color.Black); + + /*RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTarget(_sceneConstants.CameraTarget, 0, true); + RenderCommand.BindRenderTargets();*/ + + // TODO: Draw into GBuffer + for (SubmittedMesh entry in _queue) + { + entry.Material.SetVariable("ViewProjection", _sceneConstants.ViewProjection); + entry.Material.SetVariable("Transform", entry.Transform); + + entry.Material.Bind(_context); + + entry.Mesh.Bind(); + RenderCommand.DrawIndexed(entry.Mesh); + } + + RenderCommand.UnbindRenderTargets(); + RenderCommand.SetRenderTarget(_sceneConstants.CameraTarget, 0, true); + RenderCommand.BindRenderTargets(); + + _gBuffer.Color.SamplerState = SamplerStateManager.PointClamp; + + TestFullscreenEffect.SetTexture("Colors", _gBuffer.Color); + TestFullscreenEffect.SetTexture("Normals", _gBuffer.Normal); + //TestFullscreenEffect.SetTexture("Positions", _gBuffer.Position); + + //TestFullscreenEffect.SetVariable("LightDir"); + + TestFullscreenEffect.Bind(_context); + + s_quadGeometry.Bind(); + RenderCommand.DrawIndexed(s_quadGeometry); + + // TODO: Draw lights to camera target + // } + + // Queue entries increase the reference counter of the mesh/material thus we have to dispose of them. + ClearAndDisposeItems!(_queue); } public static void Submit(GeometryBinding geometry, Effect effect, Matrix transform = .Identity) @@ -130,17 +324,33 @@ namespace GlitchyEngine.Renderer RenderCommand.DrawIndexed(geometry); } + struct SubmittedMesh : IDisposable + { + public GeometryBinding Mesh; + public Material Material; + public Matrix Transform; + + public this(GeometryBinding mesh, Material material, Matrix transform) + { + Mesh = mesh..AddRef(); + Material = material..AddRef(); + Transform = transform; + } + + public void Dispose() + { + Mesh.ReleaseRef(); + Material.ReleaseRef(); + } + } + + private static List _queue = new .(10000) ~ DeleteContainerAndDisposeItems!(_); + public static void Submit(GeometryBinding geometry, Material material, Matrix transform = .Identity) { Debug.Profiler.ProfileRendererFunction!(); - material.SetVariable("ViewProjection", _sceneConstants.ViewProjection); - material.SetVariable("Transform", transform); - - material.Bind(_context); - - geometry.Bind(); - RenderCommand.DrawIndexed(geometry); + _queue.Add(SubmittedMesh(geometry, material, transform)); } /** @brief Draws a line. diff --git a/GlitchyEngine/src/Renderer/RendererAPI.bf b/GlitchyEngine/src/Renderer/RendererAPI.bf index 948c15d..459e40c 100644 --- a/GlitchyEngine/src/Renderer/RendererAPI.bf +++ b/GlitchyEngine/src/Renderer/RendererAPI.bf @@ -38,6 +38,8 @@ namespace GlitchyEngine.Renderer public extern void SetDepthStencilTarget(DepthStencilTarget target); + public extern void UnbindRenderTargets(); + public extern void BindRenderTargets(); public extern void SetRasterizerState(RasterizerState rasterizerState); diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index 28e6b6b..ff00721 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -249,16 +249,31 @@ namespace GlitchyEngine.World } } - struct CameraComponent + struct CameraComponent : IDisposableComponent { public SceneCamera Camera; public bool Primary = true; // Todo: probably move into scene public bool FixedAspectRatio = false; + private RenderTarget2D _renderTarget = null; + + public RenderTarget2D RenderTarget + { + get => _renderTarget; + set mut + { + SetReference!(_renderTarget, value); + } + } public this() { Camera = .(); - } + } + + public void Dispose() + { + _renderTarget?.ReleaseRef(); + } } struct NativeScriptComponent : IDisposableComponent diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index f0ea702..9dc60ee 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -52,6 +52,7 @@ namespace GlitchyEngine.World Camera* primaryCamera = null; Matrix primaryCameraTransform = default; + RenderTarget2D renderTarget = null; for (var (entity, transform, camera) in _ecsWorld.Enumerate()) { @@ -59,12 +60,23 @@ namespace GlitchyEngine.World { primaryCamera = &camera.Camera; primaryCameraTransform = transform.WorldTransform; + renderTarget = camera.RenderTarget..AddRef(); } } - // Sprite renderer if (primaryCamera != null) - { + { + // 3D render + Renderer.BeginScene(*primaryCamera, primaryCameraTransform, renderTarget); + + for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate()) + { + Renderer.Submit(mesh.Mesh, meshRenderer.Material, transform.WorldTransform); + } + + Renderer.EndScene(); + + // Sprite renderer Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform); for (var (entity, transform, sprite) in _ecsWorld.Enumerate()) @@ -73,6 +85,8 @@ namespace GlitchyEngine.World } Renderer2D.EndScene(); + + renderTarget?.ReleaseRef(); } }