diff --git a/D3D11Engine/BaseGraphicsEngine.h b/D3D11Engine/BaseGraphicsEngine.h index 8279ca1d..971037ed 100644 --- a/D3D11Engine/BaseGraphicsEngine.h +++ b/D3D11Engine/BaseGraphicsEngine.h @@ -1,6 +1,7 @@ #pragma once #include "WorldObjects.h" #include "GraphicsEventRecord.h" +#include "RenderToTextureBuffer.h" #include "ShaderCategory.h" class BaseLineRenderer; @@ -210,7 +211,10 @@ class BaseGraphicsEngine { virtual void DrawFrameParticleMeshes( std::unordered_map& progMeshes ) {} /** Draws particle effects */ - virtual void DrawFrameParticles( std::map>& particles, std::map& info ) {} + virtual void DrawFrameParticles(std::map>& particles, + std::map& info, + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion) {} virtual void DrawString( const std::string& str, float x, float y, const zFont* font, zColor& fontColor ) {}; diff --git a/D3D11Engine/ConstantBufferStructs.h b/D3D11Engine/ConstantBufferStructs.h index 12fd2343..bc04ea0e 100644 --- a/D3D11Engine/ConstantBufferStructs.h +++ b/D3D11Engine/ConstantBufferStructs.h @@ -223,6 +223,35 @@ struct VS_ExConstantBuffer_PerInstanceSkeletal { float3 PI_Pad1; }; +// Maximum bones per skeleton (must match shader) +static const int NUM_MAX_BONES = 96; + +// Maximum instances per batch (adjustable based on GPU memory) +static const int MAX_SKELETAL_INSTANCES = 64; + +// Per-instance data for instanced skeletal rendering +// Stored in a StructuredBuffer for GPU access +__declspec(align(16)) struct SkeletalInstanceData { + XMFLOAT4X4 World; // World transform + XMFLOAT4X4 PrevWorld; // Previous World transform + float4 Color; // Model color (RGBA) + float Fatness; // Model fatness for vertex displacement + float Scale; // Uniform scale factor + uint32_t BoneOffset; // Offset into the bone transform buffer + uint32_t Padding; // Alignment padding +}; + +// Instance data for node attachments (weapons, heads, etc.) +// Must be 16-byte aligned for SIMD operations +__declspec(align(16)) struct NodeAttachmentInstanceData { + XMFLOAT4X4 World; // World transform (includes bone transform) + XMFLOAT4X4 PrevWorld; // Previous World transform (includes bone transform) + float4 Color; // Model color + float Fatness; // Fatness for MMS meshes + float Scaling; // Scaling for MMS meshes + float2 Padding; // Alignment padding +}; + struct ScreenFadeConstantBuffer { float GA_Alpha; float3 GA_Pad; diff --git a/D3D11Engine/D3D11Effect.cpp b/D3D11Engine/D3D11Effect.cpp index a0c75ee2..7e401a33 100644 --- a/D3D11Engine/D3D11Effect.cpp +++ b/D3D11Engine/D3D11Effect.cpp @@ -531,6 +531,12 @@ XRESULT D3D11Effect::DrawRainShadowmap() { XMStoreFloat4x4( &cr.ProjectionReplacement, XMMatrixTranspose( crProjectionReplacement ) ); XMStoreFloat3( &cr.PositionReplacement, p ); XMStoreFloat3( &cr.LookAtReplacement, lookAt ); + + cr.frustum.BuildOrthographic( crViewReplacement, + size * legacySingleShadowMapScaleFactor, + size * legacySingleShadowMapScaleFactor, + 1.0f, + 20000.f ); // Replace gothics camera Engine::GAPI->SetCameraReplacementPtr( &cr ); diff --git a/D3D11Engine/D3D11Engine.vcxproj b/D3D11Engine/D3D11Engine.vcxproj index e3b1d579..fc8d1118 100644 --- a/D3D11Engine/D3D11Engine.vcxproj +++ b/D3D11Engine/D3D11Engine.vcxproj @@ -838,6 +838,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + @@ -967,6 +968,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + @@ -974,7 +976,11 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + + + + @@ -1150,6 +1156,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + Create @@ -1181,6 +1188,9 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + + + diff --git a/D3D11Engine/D3D11Engine.vcxproj.filters b/D3D11Engine/D3D11Engine.vcxproj.filters index c5a32b2c..64e07727 100644 --- a/D3D11Engine/D3D11Engine.vcxproj.filters +++ b/D3D11Engine/D3D11Engine.vcxproj.filters @@ -85,6 +85,9 @@ {7b924380-794f-4471-9819-06c3fca71231} + + {e7532fd2-04b8-499d-a2a9-5b64abda0850} + @@ -791,6 +794,23 @@ + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + + + + + Engine\GAPI + @@ -1059,6 +1079,18 @@ Engine\D3D11\PFX\Effects + + Engine\Graph + + + Engine\Graph + + + Engine\Graph + + + Engine\GAPI + diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 38d97147..b725cc71 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -46,7 +46,11 @@ #include "ImGuiShim.h" #include "zCModel.h" +#include "zCMorphMesh.h" + #include "zCOption.h" +#include "RenderGraph.h" +#include "RGBuilder.h" #ifdef BUILD_SPACER #define IS_SPACER_BUILD true @@ -165,6 +169,7 @@ D3D11GraphicsEngine::D3D11GraphicsEngine() { CachedRefreshRate.Numerator = 0; CachedRefreshRate.Denominator = 0; unionCurrentCustomFontMultiplier = 1.0; + SkeletalMeshBatches = std::unordered_map(); } D3D11GraphicsEngine::~D3D11GraphicsEngine() { @@ -520,21 +525,22 @@ XRESULT D3D11GraphicsEngine::Init() { FeatureLevel10Compatibility = (maxFeatureLevel < D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0); FetchDisplayModeList(); - IUnknown* renderdoc = nullptr; - result = Device->QueryInterface( IID_IDXGIDeviceRenderDoc, reinterpret_cast(&renderdoc) ); + ComPtr renderdoc = nullptr; + result = Device.AsIID( IID_IDXGIDeviceRenderDoc, &renderdoc ); if ( SUCCEEDED( result ) ) { // Don't use extensions if they are available // renderdoc doesn't like them DrawMultiIndexedInstancedIndirect = Stub_DrawMultiIndexedInstancedIndirect; BeginUAVOverlap = Stub_BeginUAVOverlap; EndUAVOverlap = Stub_EndUAVOverlap; - renderdoc->Release(); } if ( !DrawMultiIndexedInstancedIndirect ) { DrawMultiIndexedInstancedIndirect = Stub_DrawMultiIndexedInstancedIndirect; } + Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI = DrawMultiIndexedInstancedIndirect != Stub_DrawMultiIndexedInstancedIndirect; + if ( !BeginUAVOverlap || !EndUAVOverlap ) { BeginUAVOverlap = Stub_BeginUAVOverlap; EndUAVOverlap = Stub_EndUAVOverlap; @@ -544,6 +550,7 @@ XRESULT D3D11GraphicsEngine::Init() { hr = Device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &options3, sizeof( options3 ) ); if ( SUCCEEDED( hr ) ) { FeatureRTArrayIndexFromAnyShader = options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer; + Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseLayeredRendering = FeatureRTArrayIndexFromAnyShader; } LogInfo() << "Creating ShaderManager"; @@ -666,6 +673,13 @@ XRESULT D3D11GraphicsEngine::Init() { InverseUnitSphereMesh = new GMesh; InverseUnitSphereMesh->LoadMesh( "system\\GD3D11\\meshes\\icoSphere.obj" ); + // Add to initialization sequence: + if ( FAILED( InitSkeletalInstancingBuffers() ) ) { + LogError() << "Failed to initialize skeletal instancing buffers. Falling back to individual rendering."; + } + + InitNodeAttachmentInstancingBuffer(); + // Create distance-buffers D3D11ConstantBuffer* infiniteRangeConstantBuffer; D3D11ConstantBuffer* outdoorSmallVobsConstantBuffer; @@ -1086,27 +1100,6 @@ XRESULT D3D11GraphicsEngine::OnResize( INT2 newSize ) { PfxRenderer->OnResize( m_scaledResolution ); - GBuffer2_SpecIntens_SpecPower = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R16G16_FLOAT ); - - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetTexture().Get(), "GBuffer2_SpecIntens_SpecPower->TEX" ); - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetShaderResView().Get(), "GBuffer2_SpecIntens_SpecPower->SRV" ); - SetDebugName( GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), "GBuffer2_SpecIntens_SpecPower->RTV" ); - - GBuffer1_Normals = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R8G8B8A8_SNORM ); - - SetDebugName( GBuffer1_Normals->GetTexture().Get(), "GBuffer1_Normals->TEX" ); - SetDebugName( GBuffer1_Normals->GetShaderResView().Get(), "GBuffer1_Normals->SRV" ); - SetDebugName( GBuffer1_Normals->GetRenderTargetView().Get(), "GBuffer1_Normals->RTV" ); - - GBuffer0_Diffuse = std::make_unique( - GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_ENGINE_SWAPCHAIN ); - - SetDebugName( GBuffer0_Diffuse->GetTexture().Get(), "GBuffer0_Diffuse->TEX" ); - SetDebugName( GBuffer0_Diffuse->GetShaderResView().Get(), "GBuffer0_Diffuse->SRV" ); - SetDebugName( GBuffer0_Diffuse->GetRenderTargetView().Get(), "GBuffer0_Diffuse->RTV" ); - VelocityBuffer = std::make_unique( GetDevice().Get(), m_scaledResolution.x, m_scaledResolution.y, DXGI_FORMAT_R16G16_FLOAT ); @@ -1361,14 +1354,9 @@ XRESULT D3D11GraphicsEngine::Clear( const float4& color ) { context->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); context->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); - context->ClearRenderTargetView( GBuffer0_Diffuse->GetRenderTargetView().Get(), reinterpret_cast(&color) ); - context->ClearRenderTargetView( GBuffer1_Normals->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - context->ClearRenderTargetView( GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); context->ClearRenderTargetView( HDRBackBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); context->ClearRenderTargetView( Backbuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - // remove motion information - context->ClearRenderTargetView( VelocityBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); return XR_SUCCESS; } @@ -2199,8 +2187,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalVertexNormals( SkeletalVobInfo* vi, for ( auto& mesh : itm.second ) { WhiteTexture->BindToPixelShader( 0 ); - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -2242,7 +2230,7 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh( SkeletalVobInfo* vi, cb2.PI_ModelColor = color; cb2.PI_ModelFatness = fatness; // Set PrevWorld for motion vectors (use current world if no previous is available) - cb2.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : world; + cb2.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : cb2.World; ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &cb2 ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -2302,8 +2290,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh( SkeletalVobInfo* vi, } for ( auto& mesh : itm.second ) { - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -2394,8 +2382,8 @@ XRESULT D3D11GraphicsEngine::DrawSkeletalMesh_Layered( SkeletalVobInfo* vi, } } - D3D11VertexBuffer* vb = mesh->MeshVertexBuffer; - D3D11VertexBuffer* ib = mesh->MeshIndexBuffer; + auto vb = mesh->MeshVertexBuffer; + auto ib = mesh->MeshIndexBuffer; unsigned int numIndices = mesh->Indices.size(); UINT offset = 0; @@ -2648,6 +2636,12 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { // return XR_SUCCESS; if ( PresentPending ) return XR_SUCCESS; + RenderGraph graph( GetPfxRenderer()->GetTexturePool() ); + + // TODO: Replace global Resources with RenderGraph resource + RGResourceHandle backBufferHandle = graph.ImportResource( L"BackBuffer", HDRBackBuffer.get() ); + RGResourceHandle velocityBufferHandle = graph.ImportResource( L"VelocityBuffer", VelocityBuffer.get() ); + rendererState.RendererInfo.RenderStage = STAGE_DRAW_WORLD; D3D11_VIEWPORT vp; @@ -2659,6 +2653,7 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { vp.Height = static_cast(GetResolution().y); GetContext()->RSSetViewports( 1, &vp ); + UpdateZEngineViewport(); GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); @@ -2694,18 +2689,9 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { rendererState.RendererSettings.DrawSkeletalMeshes = bDrawVobsGlobal; #endif - ID3D11RenderTargetView* rtvs[] = { - GBuffer0_Diffuse->GetRenderTargetView().Get(), - GBuffer1_Normals->GetRenderTargetView().Get(), - GBuffer2_SpecIntens_SpecPower->GetRenderTargetView().Get(), - VelocityBuffer->GetRenderTargetView().Get() }; - GetContext()->OMSetRenderTargets( 4, rtvs, DepthStencilBuffer->GetDepthStencilView().Get() ); - - Engine::GAPI->SetFarPlane( - rendererState.RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE ); - - Clear( float4( rendererState.GraphicsState.FF_FogColor, 0.0f ) ); + Engine::GAPI->SetFarPlane( rendererState.RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE ); + // Clear textures from the last frame RenderedVobs.clear(); FrameWaterSurfaces.clear(); @@ -2731,245 +2717,501 @@ XRESULT D3D11GraphicsEngine::OnStartWorldRendering() { rendererState.RasterizerState.FrontCounterClockwise = false; rendererState.RasterizerState.SetDirty(); + RGResourceHandle colorResource; + graph.AddPass( L"Initialize Buffers", [&]( RGBuilder& builder, RenderPass& pass ) { + auto size = GetResolution(); + colorResource = builder.CreateTexture( { static_cast(size.x), static_cast(size.y), DXGI_FORMAT_ENGINE_DEFAULT, L"GBufferAlbedo" } ); + + builder.Write( colorResource ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, colorResource](const RenderGraph& graph)->void { + const Microsoft::WRL::ComPtr& context = GetContext(); + context->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + context->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + + context->ClearRenderTargetView( HDRBackBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + context->ClearRenderTargetView( Backbuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + + constexpr float black[]{ 0.f, 0.f, 0.f, 0.f }; + float4 fogColor( rendererState.GraphicsState.FF_FogColor, 0.0f ); + GetContext()->ClearRenderTargetView( graph.GetPhysicalTexture( colorResource )->GetRenderTargetView().Get(), reinterpret_cast(&fogColor) ); + }; + }); + if ( rendererState.RendererSettings.DrawSky ) { - auto _ = RecordGraphicsEvent( L"Draw Sky" ); - // Draw back of the sky if outdoor - DrawSky(); - } + graph.AddPass( L"Draw Sky", [&]( RGBuilder& builder, RenderPass& pass ) { + //// Setup / Declare + //RGTextureDesc albedoDesc{ 1920, 1080, 28 /* DXGI_FORMAT_R8G8B8A8_UNORM */, "Albedo" }; + //albedoTarget = builder.CreateTexture( albedoDesc ); + builder.Write( colorResource ); + + pass.m_executeCallback = [this, colorResource](const RenderGraph& graph)->void { + // Draw back of the sky if outdoor + GetContext()->OMSetRenderTargets( 1, graph.GetPhysicalTexture( colorResource )->GetRenderTargetView().GetAddressOf(), nullptr ); + + DrawSky(); + }; + }); + } + + RGResourceHandle normalsResource; + RGResourceHandle specularResource; + graph.AddPass( L"G-Buffer Pass", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + auto size = GetResolution(); + normalsResource = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R8G8B8A8_SNORM, L"GBufferNormals" }); + specularResource = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R16G16_FLOAT, L"GBufferSpecular" }); + + builder.Write( colorResource ); + builder.Write( normalsResource ); + builder.Write( specularResource ); + builder.Write( velocityBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, colorResource, normalsResource, specularResource](const RenderGraph& graph)-> void { + ID3D11RenderTargetView* rtvs[] = { + graph.GetPhysicalTexture(colorResource)->GetRenderTargetView().Get(), + graph.GetPhysicalTexture(normalsResource)->GetRenderTargetView().Get(), + graph.GetPhysicalTexture(specularResource)->GetRenderTargetView().Get(), + VelocityBuffer->GetRenderTargetView().Get(), + }; + + constexpr float black[] { 0.f, 0.f, 0.f, 0.f }; + GetContext()->ClearRenderTargetView( rtvs[1], black ); + GetContext()->ClearRenderTargetView( rtvs[2], black ); + GetContext()->ClearRenderTargetView( rtvs[3], black ); + GetContext()->OMSetRenderTargets( 4, rtvs, DepthStencilBuffer->GetDepthStencilView().Get() ); + + Engine::GAPI->DrawWorldMeshNaive(); + }; + }); + + graph.AddPass( L"Draw ParticleFX", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Write( backBufferHandle ); - // Draw world - { - auto _ = RecordGraphicsEvent( L"Draw WorldMesh Naive" ); - Engine::GAPI->DrawWorldMeshNaive(); - } + pass.m_executeCallback = [this](const RenderGraph&)-> void { + if ( !Engine::GAPI->GetRendererState().RendererSettings.DrawParticleEffects ) { + return; + } + std::vector decals; + zCCamera::GetCamera()->Activate(); + Engine::GAPI->GetVisibleDecalList( decals ); - // Draw HBAO - if ( rendererState.RendererSettings.HbaoSettings.Enabled ) { - auto _ = RecordGraphicsEvent( L"Draw HBAO" ); - PfxRenderer->DrawHBAO( HDRBackBuffer->GetRenderTargetView() ); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); - } + Engine::GAPI->ResetRenderStates(); + DrawDecalList( decals, true ); + DrawQuadMarks(); + }; + }); + + graph.AddPass( L"Draw Lighting", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Read( colorResource ); + builder.Read( normalsResource ); + builder.Read( specularResource ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, colorResource, normalsResource, specularResource](const RenderGraph& graph)-> void { + auto colorTexture = graph.GetPhysicalTexture(colorResource); + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + auto specularTexture = graph.GetPhysicalTexture(specularResource); + + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + // Cascades only get rendered if this is enabled. + ShadowMaps->PrepareRender(); + } + ShadowMaps->DrawLighting(m_FrameLights, + *colorTexture, + *normalsTexture, + *specularTexture, + *GetDepthBufferCopy()); + if ( !Engine::GAPI->GetRendererState().RendererSettings.FixViewFrustum ) { + m_FrameLights.clear(); + } + }; + }); - // PfxRenderer->RenderDistanceBlur(); + graph.AddPass( L"Draw Frame AlphaMeshes", [&]( RGBuilder& builder, RenderPass& pass ) { + // Setup / Declare + builder.Write( backBufferHandle ); - // Draw water surfaces of current frame - { - auto _ = RecordGraphicsEvent( L"DrawWaterSurfaces" ); - DrawWaterSurfaces(); - } + pass.m_executeCallback = [this](const RenderGraph&)-> void { + DrawFrameAlphaMeshes(); + }; + } + ); - // Draw light-shafts - { - auto _ = RecordGraphicsEvent( L"Draw light-shafts" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshes ); - } + // Draw HBAO + if ( rendererState.RendererSettings.HbaoSettings.Enabled ) { + graph.AddPass( L"HBAO+", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( normalsResource ); + builder.Write( backBufferHandle ); - //draw forest / door portals - if ( rendererState.RendererSettings.DrawG1ForestPortals ) { - auto _ = RecordGraphicsEvent( L"DrawForestPortals" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshesPortal ); - } + pass.m_executeCallback = [this, normalsResource, backBufferHandle](const RenderGraph& graph) { + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + auto backBuffer = graph.GetPhysicalTexture(backBufferHandle); - //draw waterfall foam - { - auto _ = RecordGraphicsEvent( L"Draw Waterfall Foam" ); - DrawMeshInfoListAlphablended( FrameTransparencyMeshesWaterfall ); + PfxRenderer->DrawHBAO( backBuffer->GetRenderTargetView(), + GetDepthBuffer()->GetShaderResView(), + normalsTexture->GetShaderResView()); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + }); } + + graph.AddPass( L"DrawWaterSurfaces", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - // Draw ghosts - { - auto _ = RecordGraphicsEvent( L"Draw ghosts" ); - D3D11ENGINE_RENDER_STAGE oldStage = RenderingStage; - SetRenderingStage( DES_GHOST ); - Engine::GAPI->DrawTransparencyVobs(); - SetRenderingStage( oldStage ); - Engine::GAPI->DrawSkeletalVN(); - } + pass.m_executeCallback = [this](const RenderGraph&) { + DrawWaterSurfaces(); + }; + }); + + graph.AddPass( L"Draw light-shafts", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - if ( rendererState.RendererSettings.DrawFog && - Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == - zBSP_MODE_OUTDOOR ) { + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshes ); + }; + }); + + if ( rendererState.RendererSettings.DrawG1ForestPortals ) { + graph.AddPass( L"Draw ForestPortals", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - auto _ = RecordGraphicsEvent( L"RenderHeightfog" ); - PfxRenderer->RenderHeightfog(); + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshesPortal ); + }; + }); } + + graph.AddPass( L"Draw Waterfall Foam", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); - // Draw rain - if ( Engine::GAPI->GetRainFXWeight() > 0.0f ) { - if ( FeatureLevel10Compatibility || rendererState.RendererSettings.DrawRainThroughTransformFeedback ) { - auto _ = RecordGraphicsEvent( L"DrawRain" ); - Effects->DrawRain(); + pass.m_executeCallback = [this](const RenderGraph&) { + DrawMeshInfoListAlphablended( FrameTransparencyMeshesWaterfall ); + }; + }); + + graph.AddPass( L"Draw ghosts", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + D3D11ENGINE_RENDER_STAGE oldStage = RenderingStage; + SetRenderingStage( DES_GHOST ); + Engine::GAPI->DrawTransparencyVobs(); + SetRenderingStage( oldStage ); + Engine::GAPI->DrawSkeletalVN(); + }; + }); + + if (rendererState.RendererSettings.DrawFog && + Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == + zBSP_MODE_OUTDOOR) { + graph.AddPass( L"Draw Heightfog", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + PfxRenderer->RenderHeightfog(); + }; + }); + } + + if (Engine::GAPI->GetRainFXWeight() > 0.0f) { + if ( FeatureLevel10Compatibility || Engine::GAPI->GetRendererState().RendererSettings.DrawRainThroughTransformFeedback ) { + graph.AddPass( L"Draw Rain", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + Effects->DrawRain(); + }; + }); } else { - auto _ = RecordGraphicsEvent( L"DrawRain_CS" ); - Effects->DrawRain_CS(); + graph.AddPass( L"Draw Rain CS", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + Effects->DrawRain_CS(); + }; + }); } } - - GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), - DepthStencilBuffer->GetDepthStencilView().Get() ); - - // Draw unlit decals - // TODO: Only get them once! - if ( rendererState.RendererSettings.DrawParticleEffects ) { - auto _ = RecordGraphicsEvent( L"DrawParticleEffects" ); - std::vector decals; - zCCamera::GetCamera()->Activate(); - Engine::GAPI->GetVisibleDecalList( decals ); - - // Draw stuff like candle-flames - Engine::GAPI->ResetRenderStates(); - DrawDecalList( decals, false ); - DrawMQuadMarks(); + + graph.AddPass( L"Reset RenderTargets", [&]( RGBuilder& builder, RenderPass& pass ) + { + builder.Write( backBufferHandle ); + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backBuffer = graph.GetPhysicalTexture(backBufferHandle); + GetContext()->OMSetRenderTargets( 1, backBuffer->GetRenderTargetView().GetAddressOf(), + DepthStencilBuffer->GetDepthStencilView().Get() ); + }; + }); + + if (rendererState.RendererSettings.DrawParticleEffects) { + graph.AddPass( L"Draw ParticleFX", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + // Draw unlit decals + // TODO: Only get them once! + std::vector decals; + zCCamera::GetCamera()->Activate(); + Engine::GAPI->GetVisibleDecalList( decals ); + + // Draw stuff like candle-flames + DrawDecalList( decals, false ); + DrawMQuadMarks(); + }; + }); } - - // Unbind temporary backbuffer copy - Microsoft::WRL::ComPtr srv; - GetContext()->PSSetShaderResources( 5, 1, srv.GetAddressOf() ); - - // TODO: TODO: GodRays need the GBuffer1 from the scene, but Particles need to - // clear it! - if ( rendererState.RendererSettings.EnableGodRays && + + if (rendererState.RendererSettings.EnableGodRays && Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == - zBSP_MODE_OUTDOOR ) { - - auto _ = RecordGraphicsEvent( L"RenderGodRays" ); - PfxRenderer->RenderGodRays(); + zBSP_MODE_OUTDOOR) { + graph.AddPass( L"Draw Godrays", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( normalsResource ); + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle, normalsResource](const RenderGraph& graph) { + // Unbind temporary backbuffer copy + Microsoft::WRL::ComPtr srv; + GetContext()->PSSetShaderResources( 5, 1, srv.GetAddressOf() ); + + auto backbufferResource = graph.GetPhysicalTexture(backBufferHandle); + auto normalsTexture = graph.GetPhysicalTexture(normalsResource); + + PfxRenderer->RenderGodRays(backbufferResource->GetShaderResView().Get(), normalsTexture->GetShaderResView().Get()); + }; + }); } + + graph.AddPass( L"Draw ParticlesSimple", [&]( RGBuilder& builder, RenderPass& pass ) { + auto size = GetResolution(); - Engine::GAPI->ResetRenderStates(); - // DrawParticleEffects(); - { - auto _ = RecordGraphicsEvent( L"DrawParticlesSimple" ); - Engine::GAPI->DrawParticlesSimple(); - } + auto particleColorHandle = builder.CreateTexture( { static_cast(size.x), static_cast(size.y), DXGI_FORMAT_ENGINE_DEFAULT, L"PfxColor" } ); + auto particleDistortionHandle = builder.CreateTexture({ static_cast(size.x), static_cast(size.y), DXGI_FORMAT_R8G8B8A8_SNORM, L"PfxDistortion" }); + + builder.Write( particleColorHandle ); + builder.Write( particleDistortionHandle ); + builder.Read( particleColorHandle ); + builder.Read( particleDistortionHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [particleColorHandle, particleDistortionHandle](const RenderGraph& graph) { + Engine::GAPI->ResetRenderStates(); + Engine::GAPI->DrawParticlesSimple( + graph.GetPhysicalTexture( particleColorHandle ), + graph.GetPhysicalTexture( particleDistortionHandle ) ); + }; + }); #if (defined BUILD_GOTHIC_2_6_fix || defined BUILD_GOTHIC_1_08k) - // Calc weapon/effect trail mesh data - Engine::GAPI->CalcPolyStripMeshes(); - // Calc lightning flashes mesh data - Engine::GAPI->CalcFlashMeshes(); - // Draw those - { - auto _ = RecordGraphicsEvent( L"DrawPolyStrips" ); - // For some reasons the viewport gets messed up, so set it again - SetViewport( ViewportInfo( 0, 0, GetResolution().x, GetResolution().y)); - DrawPolyStrips(); - } + + graph.AddPass( L"Draw PolyStrips", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + // Calc weapon/effect trail mesh data + Engine::GAPI->CalcPolyStripMeshes(); + // Calc lightning flashes mesh data + Engine::GAPI->CalcFlashMeshes(); + // Draw those + // For some reasons the viewport gets messed up, so set it again + SetViewport( ViewportInfo( 0, 0, GetResolution().x, GetResolution().y ) ); + DrawPolyStrips(); + }; + } ); + #endif // Draw debug lines - { - auto _ = RecordGraphicsEvent( L"Draw Debug Lines" ); - LineRenderer->Flush(); - LineRenderer->FlushScreenSpace(); - } + graph.AddPass( L"Draw Debug Lines", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + LineRenderer->Flush(); + LineRenderer->FlushScreenSpace(); + }; + } ); if ( rendererState.RendererSettings.AntiAliasingMode == GothicRendererSettings::AA_TAA ) { // TAA before any HDR stuff - auto _ = RecordGraphicsEvent( L"RenderTAA" ); - PfxRenderer->RenderTAA( rendererState.RendererSettings.DebugSettings.TAA.DepthMotionVectors - ? nullptr - : VelocityBuffer->GetShaderResView() ); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + graph.AddPass( L"Render TAA", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( velocityBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, velocityBufferHandle](const RenderGraph& graph) { + auto velocityBufferTex = graph.GetPhysicalTexture( velocityBufferHandle ); + PfxRenderer->RenderTAA( rendererState.RendererSettings.DebugSettings.TAA.DepthMotionVectors + ? nullptr + : velocityBufferTex->GetShaderResView() ); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); } - if ( rendererState.RendererSettings.EnableHDR ) { - auto _ = RecordGraphicsEvent( L"RenderHDR" ); - PfxRenderer->RenderHDR(); + if ( rendererState.RendererSettings.EnableHDR ) { + graph.AddPass( L"Render HDR", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->RenderHDR( backbufferTex->GetRenderTargetView().Get(), backbufferTex->GetShaderResView().Get() ); + }; + } ); } - // SMAA should be applied before any sharpening if ( rendererState.RendererSettings.AntiAliasingMode - == GothicRendererSettings::AA_SMAA ) { - // actually we could do TAA + SMAA - auto _ = RecordGraphicsEvent( L"RenderSMAA" ); - PfxRenderer->RenderSMAA(); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + == GothicRendererSettings::AA_SMAA ) { + // SMAA should be applied before any sharpening + graph.AddPass( L"Render SMAA", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->RenderSMAA(backbufferTex->GetShaderResView().Get()); + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); } + + graph.AddPass( L"Reset Viewport", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - PresentPending = true; + pass.m_executeCallback = [this](const RenderGraph&) { + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); - // Set viewport for gothics rendering - vp.TopLeftX = 0.0f; - vp.TopLeftY = 0.0f; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.Width = static_cast(GetResolution().x); - vp.Height = static_cast(GetResolution().y); + PresentPending = true; - GetContext()->RSSetViewports( 1, &vp ); + // Set viewport for gothics rendering + D3D11_VIEWPORT vp; + vp.TopLeftX = 0.0f; + vp.TopLeftY = 0.0f; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.Width = static_cast(GetResolution().x); + vp.Height = static_cast(GetResolution().y); + + GetContext()->RSSetViewports( 1, &vp ); + }; + } ); // If we currently are underwater, then draw underwater effects if ( Engine::GAPI->IsUnderWater() ) { - auto _ = RecordGraphicsEvent( L"DrawUnderwaterEffects" ); - DrawUnderwaterEffects(); + graph.AddPass( L"Draw UnderwaterFX", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this](const RenderGraph&) { + DrawUnderwaterEffects(); + }; + } ); } - // Clear here to get a working depthbuffer but no interferences with world - // geometry for gothic UI-Rendering - GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); + // If we currently are underwater, then draw underwater effects + if ( Engine::GAPI->IsUnderWater() ) { + graph.AddPass( L"Prepare finalize frame", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - // Store the current depth state to the copy buffer before clear - CopyDepthStencil(); + pass.m_executeCallback = [this](const RenderGraph&) { + // Clear here to get a working depthbuffer but no interferences with world + // geometry for gothic UI-Rendering + GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); - GetContext()->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); - GetContext()->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + // Store the current depth state to the copy buffer before clear + CopyDepthStencil(); + + GetContext()->ClearDepthStencilView( DepthStencilBuffer->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + GetContext()->ClearDepthStencilView( m_NativeSizeDepthStencil->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0, 0 ); + + SetDefaultStates(); + }; + } ); + } // Before returning to gothics UI, set render target to backbuffer { // Copy HDR scene to backbuffer - - SetDefaultStates(); - if ( rendererState.RendererSettings.ResolutionScalePercent < 100 && rendererState.RendererSettings.Upscaler == GothicRendererSettings::E_Upscaler::UPSCALER_FSR_1 ) { - auto _ = RecordGraphicsEvent( L"FSR 1" ); - - // Now upscale it to backbuffer with sharpening - auto sharpenFactor = rendererState.RendererSettings.SharpenFactor; - PfxRenderer->GetFSR1()->Apply( - HDRBackBuffer->GetShaderResView(), - Backbuffer->GetRenderTargetView(), - GetResolution(), - GetBackbufferResolution(), - sharpenFactor > 0.001f, - 1.0f - sharpenFactor ); - } else { - - if ( rendererState.RendererSettings.SharpeningMode - && rendererState.RendererSettings.SharpenFactor > 0.0f) { - - { - auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); - } - - switch ( rendererState.RendererSettings.SharpeningMode ) { - case GothicRendererSettings::SHARPEN_SIMPLE: - if ( !FeatureLevel10Compatibility ) { - auto _ = RecordGraphicsEvent( L"ApplySimpleSharpen" ); - PfxRenderer->RenderSimpleSharpen( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + graph.AddPass( L"FSR 1 Upscale", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + + // Now upscale it to backbuffer with sharpening + auto sharpenFactor = rendererState.RendererSettings.SharpenFactor; + PfxRenderer->GetFSR1()->Apply( + backbufferTex->GetShaderResView(), + Backbuffer->GetRenderTargetView(), + GetResolution(), + GetBackbufferResolution(), + sharpenFactor > 0.001f, + 1.0f - sharpenFactor ); + }; + } ); + } else if (rendererState.RendererSettings.SharpeningMode + && rendererState.RendererSettings.SharpenFactor > 0.0f ) { + + graph.AddPass( L"Sharpen", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Read( backBufferHandle ); + builder.Write( backBufferHandle ); + + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + { + auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); + PfxRenderer->CopyTextureToRTV( backbufferTex->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); } - break; - case GothicRendererSettings::SHARPEN_CAS: - if ( !FeatureLevel10Compatibility ) { - auto _ = RecordGraphicsEvent( L"ApplyCAS" ); - PfxRenderer->RenderCAS( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); - GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + switch ( rendererState.RendererSettings.SharpeningMode ) { + case GothicRendererSettings::SHARPEN_SIMPLE: + if ( !FeatureLevel10Compatibility ) { + auto _ = RecordGraphicsEvent( L"ApplySimpleSharpen" ); + PfxRenderer->RenderSimpleSharpen( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); + } + break; + + case GothicRendererSettings::SHARPEN_CAS: + if ( !FeatureLevel10Compatibility ) { + auto _ = RecordGraphicsEvent( L"ApplyCAS" ); + PfxRenderer->RenderCAS( Backbuffer->GetShaderResView(), GetBackbufferResolution(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution(), *GetPfxRenderer()->GetBackbufferTempBuffer()); + } + break; } - break; - } + GetContext()->PSSetSamplers( 0, 1, DefaultSamplerState.GetAddressOf() ); + }; + } ); + } else { + graph.AddPass( L"Copy into native-size backbuffer", [&]( RGBuilder& builder, RenderPass& pass ) { + builder.Write( backBufferHandle ); - } else { - auto _ = RecordGraphicsEvent( L"Copy into native-size backbuffer" ); - PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); - } + pass.m_executeCallback = [this, &rendererState, backBufferHandle](const RenderGraph& graph) { + auto backbufferTex = graph.GetPhysicalTexture( backBufferHandle ); + PfxRenderer->CopyTextureToRTV( backbufferTex->GetShaderResView(), Backbuffer->GetRenderTargetView(), GetBackbufferResolution() ); + }; + } ); } + graph.Compile(); + graph.Execute(); + // Below this, we assume UI/HUD rendering rendererState.RendererInfo.RenderStage = STAGE_DRAW_HUD; @@ -3158,8 +3400,8 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); int lastAlphaFunc = 0; @@ -3285,7 +3527,7 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh_Indirect( bool noTextures ) { Engine::GAPI->CollectVisibleSections( renderList ); MeshInfo* meshInfo = Engine::GAPI->GetWrappedWorldMesh(); - DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer, meshInfo->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer.get(), meshInfo->MeshIndexBuffer.get(), 0, 0); auto& context = GetContext(); context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); @@ -3533,7 +3775,7 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh( bool noTextures ) { Engine::GAPI->CollectVisibleSections( renderList ); MeshInfo* meshInfo = Engine::GAPI->GetWrappedWorldMesh(); - DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer, meshInfo->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( meshInfo->MeshVertexBuffer.get(), meshInfo->MeshIndexBuffer.get(), 0, 0); static std::vector> meshList; auto CompareMesh = []( std::pair& a, std::pair& b ) -> bool { return a.first.Texture < b.first.Texture; }; @@ -3713,8 +3955,8 @@ void D3D11GraphicsEngine::DrawWaterSurfaces() { // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); for ( const auto& [texture, meshes] : FrameWaterSurfaces ) { // Draw surfaces for ( const auto& mesh : meshes ) { @@ -3853,8 +4095,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround( if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { // Bind wrapped mesh vertex buffers - DrawVertexBufferIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + DrawVertexBufferIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -4000,8 +4242,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround( } for ( auto const& meshInfo : materialMesh.second ) { DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -4161,8 +4403,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { // Bind wrapped mesh vertex buffers - DrawVertexBufferInstancedIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0, 0 ); + DrawVertexBufferInstancedIndexedUINT( Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0, 0 ); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); @@ -4309,8 +4551,8 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( } for ( auto const& meshInfo : materialMesh.second ) { DrawVertexBufferInstancedIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size(), 6 ); } } @@ -4395,104 +4637,958 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAround_Layered( } } -/** Draws everything around the given position */ -void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR position, - float sectionRange, - const RenderShadowmapsParams& params ) { - int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); - static const char* timer_labels_world_mesh[MAX_CSM_CASCADES] - { - "World Mesh 0", - "World Mesh 1", - "World Mesh 2", - "World Mesh 3", - }; - static const char* timer_labels_vobs[MAX_CSM_CASCADES] - { - "VOBs 0", - "VOBs 1", - "VOBs 2", - "VOBs 3", - }; - static const char* timer_labels_skeletal[MAX_CSM_CASCADES] - { - "Skeletal Meshes 0", - "Skeletal Meshes 1", - "Skeletal Meshes 2", - "Skeletal Meshes 3", - }; - - // Setup renderstates - Engine::GAPI->GetRendererState().RasterizerState.SetDefault(); - Engine::GAPI->GetRendererState().RasterizerState.CullMode = - params.CullFront ? GothicRasterizerStateInfo::CM_CULL_FRONT - : GothicRasterizerStateInfo::CM_CULL_BACK; - if ( params.DontCull ) - Engine::GAPI->GetRendererState().RasterizerState.CullMode = - GothicRasterizerStateInfo::CM_CULL_NONE; +// Add near other initialization code - Engine::GAPI->GetRendererState().RasterizerState.DepthClipEnable = true; - Engine::GAPI->GetRendererState().RasterizerState.SetDirty(); +XRESULT D3D11GraphicsEngine::InitSkeletalInstancingBuffers() { + // Instance data buffer - holds per-instance transforms, colors, etc. + SkeletalInstanceBuffer = std::make_unique>(); + HRESULT hr = SkeletalInstanceBuffer->Init( GetDevice().Get(), MAX_SKELETAL_INSTANCES * 4 ); // Allow some growth + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance buffer"; + return XR_FAILED; + } - Engine::GAPI->GetRendererState().DepthState.SetDefault(); - Engine::GAPI->GetRendererState().DepthState.DepthBufferCompareFunc = GothicDepthBufferStateInfo::ECompareFunc::CF_COMPARISON_LESS_EQUAL; - Engine::GAPI->GetRendererState().DepthState.SetDirty(); + // Bone transform buffer - holds all bone matrices for all instances + SkeletalBoneBuffer = std::make_unique>(); + hr = SkeletalBoneBuffer->Init( GetDevice().Get(), 512 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal bone buffer"; + return XR_FAILED; + } - XMMATRIX view = Engine::GAPI->GetViewMatrixXM(); + // Instance index indirection buffer - maps SV_InstanceID to actual instance index + SkeletalInstanceIndexBuffer = std::make_unique>(); + hr = SkeletalInstanceIndexBuffer->Init( GetDevice().Get(), MAX_SKELETAL_INSTANCES * 4 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance index buffer"; + return XR_FAILED; + } - Engine::GAPI->ResetWorldTransform(); - Engine::GAPI->SetViewTransformXM( view ); + LogInfo() << "Initialized skeletal instancing buffers"; + return XR_SUCCESS; +} - // Set shader - SetActivePixelShader( "PS_AtmosphereGround" ); - auto nrmPS = ActivePS; - SetActivePixelShader( "PS_DiffuseAlphaTest" ); - auto defaultPS = ActivePS; - SetActiveVertexShader( "VS_Ex" ); +void D3D11GraphicsEngine::BuildSkeletalMeshBatches( const std::vector& vobs ) { + // Clear previous batches + ClearSkeletalMeshBatches(); + + std::vector allBones; + std::vector allInstances; + std::vector allEntries; + + for ( SkeletalVobInfo* vi : vobs ) { + if ( !vi || !vi->VisualInfo || !vi->Vob ) { + continue; + } - bool linearDepth = - (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & - GSWITCH_LINEAR_DEPTH) != 0; - if ( linearDepth ) { - SetActivePixelShader( "PS_LinDepth" ); - } + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model ) { + continue; + } - // Set constant buffer - ActivePS->GetConstantBuffer()[0]->UpdateBuffer( - &Engine::GAPI->GetRendererState().GraphicsState ); - ActivePS->GetConstantBuffer()[0]->BindToPixelShader( 0 ); + // Update textures + model->SetIsVisible( true ); - GSky* sky = Engine::GAPI->GetSky(); - ActivePS->GetConstantBuffer()[1]->UpdateBuffer( &sky->GetAtmosphereCB() ); - ActivePS->GetConstantBuffer()[1]->BindToPixelShader( 1 ); + SkeletalMeshVisualInfo* visualInfo = dynamic_cast(vi->VisualInfo); - // Init drawcalls - SetupVS_ExMeshDrawCall(); - SetupVS_ExConstantBuffer(); + // Skip if no skeletal meshes (these are MOBs like chests) + if ( visualInfo->SkeletalMeshes.empty() ) { + if ( model->GetMeshSoftSkinList()->NumInArray > 0 ) { + // Just in case somehow we end up without skeletal meshes and they are available + WorldConverter::ExtractSkeletalMeshFromVob( model, visualInfo ); + } - ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); - ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); + if ( visualInfo->SkeletalMeshes.empty() ) { + continue; + } + } - // Update and bind buffer of PS - PerObjectState ocb; - ocb.OS_AmbientColor = float3( 1, 1, 1 ); - ActivePS->GetConstantBuffer()[3]->UpdateBuffer( &ocb ); - ActivePS->GetConstantBuffer()[3]->BindToPixelShader( 3 ); + model->UpdateMeshLibTexAniState(); + + // Build instance data + SkeletalInstanceData inst = {}; + + auto boneIndex = allBones.size(); + model->GetBoneTransforms(&allBones); + + // World matrix with scale + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + XMStoreFloat4x4( &inst.World, world ); + inst.PrevWorld = vi->HasValidPrevTransforms ? vi->PrevWorldMatrix : inst.World; + + // Model color + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + inst.Color = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + inst.Color = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + inst.Color = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + inst.Color = float4( 1, 1, 1, 1 ); + } + } - float3 fPosition; XMStoreFloat3( fPosition.toXMFLOAT3(), position ); - INT2 s = WorldConverter::GetSectionOfPos( fPosition ); + inst.Fatness = model->GetModelFatness(); + inst.Scale = 1.0f; + inst.BoneOffset = boneIndex; + inst.Padding = 0; + + auto instIndex = allInstances.size(); + allInstances.push_back(inst); + + for (auto& [mat, meshes] : visualInfo->SkeletalMeshes) { + for (auto mesh : meshes) { + SkeletalMeshBatchEntry entry; + entry.material = mat->GetAniTexture(); + entry.mesh = mesh; + entry.instanceIndex = instIndex; + allEntries.push_back(entry); + } + } + } + + auto& batch = SkeletalMeshBatches[0]; + batch.Entries = std::move(allEntries); + batch.BoneTransforms = std::move(allBones); + batch.Instances = std::move(allInstances); +} - DistortionTexture->BindToPixelShader( 0 ); +XRESULT D3D11GraphicsEngine::DrawSkeletalMeshBatched() { + if ( SkeletalMeshBatches.empty() ) return XR_SUCCESS; - InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); + // Fix the view matrix + Engine::GAPI->GetViewMatrix( &Engine::GAPI->GetRendererState().TransformState.TransformView ); - UpdateRenderStates(); + // Draw each batch (body meshes only) + for ( auto& [name, batch] : SkeletalMeshBatches ) { + // Draw batched body mesh + DrawSkeletalMeshInstanced( batch ); + } - auto enableCulling = Engine::GAPI->GetRendererState().RendererSettings.IsShadowFrustumCullingEnabled(); - bool colorWritesEnabled = + return XR_SUCCESS; +} + +XRESULT D3D11GraphicsEngine::DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch ) { + if ( batch.Instances.empty() ) return XR_SUCCESS; + + if ( SkeletalInstanceBuffer->GetMaxElementCount() < batch.Instances.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.Instances.size() ); + + SkeletalInstanceBuffer = std::make_unique>(); + HRESULT hr = SkeletalInstanceBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal instance buffer"; + return XR_FAILED; + } + } + + // Update structured buffers + SkeletalInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), + static_cast(batch.Instances.size()) ); + + if ( SkeletalBoneBuffer->GetMaxElementCount() < batch.BoneTransforms.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.BoneTransforms.size() ); + + SkeletalBoneBuffer = std::make_unique>(); + HRESULT hr = SkeletalBoneBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create skeletal bone buffer"; + return XR_FAILED; + } + } + SkeletalBoneBuffer->UpdateBuffer( Context.Get(), batch.BoneTransforms.data(), + static_cast(batch.BoneTransforms.size()) ); + + // Bind structured buffers + SkeletalInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); // t10 + SkeletalBoneBuffer->BindToVertexShader( Context.Get(), 11 ); // t11 + SkeletalInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); // t12 - instance index indirection + + // Set shader based on rendering stage + if ( RenderingStage == DES_SHADOWMAP_CUBE ) { + SetActiveVertexShader( "VS_ExSkeletalInstancedCube" ); + } else { + SetActiveVertexShader( "VS_ExSkeletalInstanced" ); + } + + InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); + SetupVS_ExMeshDrawCall(); + SetupVS_ExConstantBuffer(); + + Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + + ActiveVS->Apply(); + + // Setup pixel shader + if ( RenderingStage != DES_GHOST ) { + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & GSWITCH_LINEAR_DEPTH) != 0; + if ( linearDepth ) { + ActivePS = PS_LinDepth; + ActivePS->Apply(); + } else if ( RenderingStage == DES_SHADOWMAP ) { + Context->PSSetShader( nullptr, nullptr, 0 ); + ActivePS = nullptr; + } else { + ActivePS = PS_LinDepth; + } + } + + if ( RenderingStage == DES_MAIN ) { + if ( ActiveHDS ) { + Context->DSSetShader( nullptr, nullptr, 0 ); + Context->HSSetShader( nullptr, nullptr, 0 ); + ActiveHDS = nullptr; + } + } + + bool needsTexture = (RenderingStage != DES_GHOST && RenderingStage != DES_SHADOWMAP); + // Sort by material first, then by mesh pointer for optimal batching + if ( needsTexture ) { + std::sort(batch.Entries.begin(), batch.Entries.end(), [](const SkeletalMeshBatchEntry& a, const SkeletalMeshBatchEntry& b) + { + if ( a.material != b.material ) return a.material < b.material; + return a.mesh->MeshVertexBuffer.get() < b.mesh->MeshVertexBuffer.get(); + }); + } else { + std::sort(batch.Entries.begin(), batch.Entries.end(), [](const SkeletalMeshBatchEntry& a, const SkeletalMeshBatchEntry& b) + { + return a.mesh->MeshVertexBuffer.get() < b.mesh->MeshVertexBuffer.get(); + }); + } + + // Temporary buffer for collecting instance indices per draw call + std::vector drawInstanceIndices; + drawInstanceIndices.reserve( 64 ); + + for ( size_t i = 0; i < batch.Entries.size(); ) { + auto& itm = batch.Entries[i]; + + // Bind texture for this material + if ( itm.material ) { + zCTexture* tex; + if ( ActivePS && (tex = itm.material) != nullptr ) { + if ( !BindTextureNRFX( tex, needsTexture ) ) { + i++; + continue; + } + } + } else { + GetContext()->PSSetShader( nullptr, nullptr, 0 ); + } + + // Find all entries with same material AND same mesh (can be instanced together) + size_t batchStart = i; + size_t batchEnd = i + 1; + while ( batchEnd < batch.Entries.size() + && ( !needsTexture || batch.Entries[batchEnd].material == itm.material) + && batch.Entries[batchEnd].mesh->MeshVertexBuffer.get() == itm.mesh->MeshVertexBuffer.get() ) { + batchEnd++; + } + size_t instanceCount = batchEnd - batchStart; + + // Collect instance indices for this draw call + drawInstanceIndices.clear(); + for ( size_t j = batchStart; j < batchEnd; j++ ) { + drawInstanceIndices.push_back( batch.Entries[j].instanceIndex ); + } + + // Update instance index indirection buffer + if ( SkeletalInstanceIndexBuffer->GetMaxElementCount() < drawInstanceIndices.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( drawInstanceIndices.size() ); + SkeletalInstanceIndexBuffer = std::make_unique>(); + SkeletalInstanceIndexBuffer->Init( GetDevice().Get(), requiredSize ); + SkeletalInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); + } + SkeletalInstanceIndexBuffer->UpdateBuffer( Context.Get(), drawInstanceIndices.data(), + static_cast(drawInstanceIndices.size()) ); + + // Set up vertex/index buffers for this mesh + auto vb = itm.mesh->MeshVertexBuffer; + auto ib = itm.mesh->MeshIndexBuffer; + unsigned int numIndices = static_cast( itm.mesh->Indices.size() ); + + UINT offset = 0; + UINT uStride = sizeof( ExSkelVertexStruct ); + Context->IASetVertexBuffers( 0, 1, vb->GetVertexBuffer().GetAddressOf(), &uStride, &offset ); + Context->IASetIndexBuffer( ib->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + + // Draw instanced - SV_InstanceID will index into InstanceIndexBuffer to get actual instance index + Context->DrawIndexedInstanced( numIndices, static_cast(instanceCount), 0, 0, 0 ); + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + (numIndices / 3) * static_cast(instanceCount); + + i = batchEnd; + } + + // Unbind structured buffers + SkeletalInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); + SkeletalBoneBuffer->UnbindFromVertexShader( Context.Get(), 11 ); + SkeletalInstanceIndexBuffer->UnbindFromVertexShader( Context.Get(), 12 ); + + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::ClearSkeletalMeshBatches() { + for ( auto& [name, batch] : SkeletalMeshBatches ) { + batch.Entries.clear(); + batch.BoneTransforms.clear(); + batch.Instances.clear(); + } + SkeletalMeshBatches.clear(); +} + +void D3D11GraphicsEngine::BuildNodeAttachmentBatches( const std::vector& vobs, bool updateState ) { + // Clear previous batches + ClearNodeAttachmentBatches(); + + XMVECTOR playerPosXm = oCGame::GetPlayer() ? oCGame::GetPlayer()->GetPositionWorldXM() : g_XMZero; + auto isShadowPass = RenderingStage == DES_SHADOWMAP || RenderingStage == DES_SHADOWMAP_CUBE; + + std::vector allInstances; + std::vector allEntries; + + static std::vector transforms; + + for ( const auto& vi : vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model || !vi->VisualInfo ) continue; + + if ( !vi->Vob->GetShowVisual() ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vi->Vob->GetPositionWorldXM() - playerPosXm ) ); + + // Calculate model color + float4 modelColor; + if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + modelColor = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + static const float inv255f = 1.0f / 255.0f; + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + modelColor = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + modelColor = float4( 1, 1, 1, 1 ); + } + } + + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + + float fatness = model->GetModelFatness(); + + // Get bone transforms + transforms.clear(); + model->GetBoneTransforms( &transforms ); + + const std::vector& prevBoneTransforms = (vi->HasValidPrevTransforms && !vi->PrevBoneTransforms.empty()) + ? vi->PrevBoneTransforms + : transforms; + const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms + ? XMLoadFloat4x4( &vi->PrevWorldMatrix ) + : world; + + // Update attachments + if ( updateState ) { + model->UpdateAttachedVobs(); + model->UpdateMeshLibTexAniState(); + } + + std::map>& nodeAttachments = vi->NodeAttachments; + zCModel* mvis = static_cast(vi->Vob->GetVisual()); + + for ( unsigned int i = 0; i < transforms.size(); i++ ) { + zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; + + if ( !node->NodeVisual ) continue; + + if ( updateState ) { + // Load attachment if not yet loaded + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) { + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + + // Check for changed visual + if ( !nodeAttachments[i].empty() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { + if ( !node->NodeVisual ) { + delete nodeAttachments[i][0]; + nodeAttachments[i].clear(); + continue; + } + WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); + } + } + + // Skip non-hand visuals if model requests it + if ( model->GetDrawHandVisualsOnly() ) { + std::string NodeName = node->ProtoNode->NodeName.ToChar(); +#ifdef BUILD_GOTHIC_2_6_fix + if ( NodeName.find( "HAND" ) == std::string::npos && + (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { +#else + if ( NodeName.find( "HAND" ) == std::string::npos ) { +#endif + continue; + } + } + + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) continue; + + XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); + XMMATRIX finalWorld = world * curTransform; + + XMMATRIX prevTransform = XMLoadFloat4x4( &prevBoneTransforms[i] ); + auto prevWorldNode = prevWorldMatrix * prevTransform; + + // Process each attachment visual + for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { + if ( !mvi || !mvi->Visual ) continue; + + // Update animated textures BEFORE getting the texture pointer + bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; + if ( updateState ) { + node->TexAniState.UpdateTexList(); + if ( isMMS ) { + zCMorphMesh* mm = reinterpret_cast(mvi->Visual); + mm->GetTexAniState()->UpdateTexList(); + } + } + + // Skip MorphMeshes at close distance (they need per-vertex animation, can't batch) + if ( dist < 1000 && isMMS && (RenderingStage == DES_MAIN || RenderingStage == DES_GHOST) ) { + // These are handled separately via DrawMorphMesh in GothicAPI + continue; + } + + // Build instance data + NodeAttachmentInstanceData instData = {}; + XMStoreFloat4x4( &instData.World, finalWorld ); + XMStoreFloat4x4( &instData.PrevWorld, prevWorldNode ); + instData.Color = modelColor; + + if ( isMMS ) { + instData.Fatness = std::max( 0.f, fatness * 0.35f ); + instData.Scaling = fatness * 0.02f + 1.f; + } else { + instData.Fatness = 0.f; + instData.Scaling = 1.f; + } + + auto instIndex = allInstances.size(); + allInstances.push_back( instData ); + + // Collect entries by (texture, mesh) for batching + for ( auto const& itm : mvi->Meshes ) { + zCMaterial* mat = itm.first; + if ( !mat ) continue; + + zCTexture* tex = mat->GetAniTexture(); + + for ( MeshInfo* mesh : itm.second ) { + if ( !mesh ) continue; + + NodeAttachmentBatchEntry entry; + entry.texture = tex; + entry.mesh = mesh; + entry.instanceIndex = static_cast(instIndex); + allEntries.push_back( entry ); + } + } + } + } + } + + // Store in the batch + auto& batch = NodeAttachmentBatches[0]; + batch.Entries = std::move( allEntries ); + batch.Instances = std::move( allInstances ); +} + +XRESULT D3D11GraphicsEngine::DrawNodeAttachmentBatched() { + if ( NodeAttachmentBatches.empty() ) return XR_SUCCESS; + + // Fix the view matrix + Engine::GAPI->GetViewMatrix( &Engine::GAPI->GetRendererState().TransformState.TransformView ); + + // Draw each batch + for ( auto& [name, batch] : NodeAttachmentBatches ) { + DrawNodeAttachmentInstanced( batch ); + } + + return XR_SUCCESS; +} + +XRESULT D3D11GraphicsEngine::DrawNodeAttachmentInstanced( NodeAttachmentBatch& batch ) { + if ( batch.Instances.empty() ) return XR_SUCCESS; + + // Resize instance buffer if needed + if ( NodeAttachmentInstanceBuffer->GetMaxElementCount() < batch.Instances.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( batch.Instances.size() ); + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), requiredSize ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return XR_FAILED; + } + } + + // Update instance buffer + NodeAttachmentInstanceBuffer->UpdateBuffer( Context.Get(), batch.Instances.data(), + static_cast(batch.Instances.size()) ); + + // Bind structured buffers + NodeAttachmentInstanceBuffer->BindToVertexShader( Context.Get(), 10 ); // t10 + NodeAttachmentInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); // t12 - instance index indirection + + // Set shader based on rendering stage + if ( RenderingStage == DES_SHADOWMAP_CUBE ) { + SetActiveVertexShader( "VS_ExNodeInstancedCube" ); + } else { + SetActiveVertexShader( "VS_ExNodeInstanced" ); + } + + InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); + SetupVS_ExMeshDrawCall(); + SetupVS_ExConstantBuffer(); + + Context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + + ActiveVS->Apply(); + + // Setup pixel shader + if ( RenderingStage != DES_GHOST ) { + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & GSWITCH_LINEAR_DEPTH) != 0; + if ( linearDepth ) { + ActivePS = PS_LinDepth; + ActivePS->Apply(); + } else if ( RenderingStage == DES_SHADOWMAP ) { + Context->PSSetShader( nullptr, nullptr, 0 ); + ActivePS = nullptr; + } else { + SetActivePixelShader( "PS_DiffuseAlphaTest" ); + BindActivePixelShader(); + } + } + + if ( RenderingStage == DES_MAIN ) { + if ( ActiveHDS ) { + Context->DSSetShader( nullptr, nullptr, 0 ); + Context->HSSetShader( nullptr, nullptr, 0 ); + ActiveHDS = nullptr; + } + } + + // Sort by texture first, then by mesh pointer for optimal batching + bool needsTexture = (RenderingStage != DES_GHOST && RenderingStage != DES_SHADOWMAP); + // Sort by material first, then by mesh pointer for optimal batching + if ( needsTexture ) { + std::sort( batch.Entries.begin(), batch.Entries.end(), []( const NodeAttachmentBatchEntry& a, const NodeAttachmentBatchEntry& b ) { + if ( a.texture != b.texture ) return a.texture < b.texture; + return a.mesh->MeshIndexBuffer.get() < b.mesh->MeshIndexBuffer.get(); + } ); + } else { + std::sort( batch.Entries.begin(), batch.Entries.end(), []( const NodeAttachmentBatchEntry& a, const NodeAttachmentBatchEntry& b ) { + if ( a.texture && a.texture->HasAlphaChannel() && a.texture != b.texture ) + return a.texture < b.texture; + return a.mesh->MeshIndexBuffer.get() < b.mesh->MeshIndexBuffer.get(); + } ); + } + + // Temporary buffer for collecting instance indices per draw call + std::vector drawInstanceIndices; + drawInstanceIndices.reserve( 64 ); + + for ( size_t i = 0; i < batch.Entries.size(); ) { + auto& itm = batch.Entries[i]; + + // Bind texture for this batch + if ( itm.texture ) { + if ( ActivePS ) { + if ( !BindTextureNRFX( itm.texture, needsTexture ) ) { + i++; + continue; + } + } + } else { + GetContext()->PSSetShader( nullptr, nullptr, 0 ); + } + + // Find all entries with same texture AND same mesh (can be instanced together) + size_t batchStart = i; + size_t batchEnd = i + 1; + while ( batchEnd < batch.Entries.size() + && batch.Entries[batchEnd].texture == itm.texture + && batch.Entries[batchEnd].mesh->MeshIndexBuffer.get() == itm.mesh->MeshIndexBuffer.get() ) { + batchEnd++; + } + size_t instanceCount = batchEnd - batchStart; + + // Collect instance indices for this draw call + drawInstanceIndices.clear(); + for ( size_t j = batchStart; j < batchEnd; j++ ) { + drawInstanceIndices.push_back( batch.Entries[j].instanceIndex ); + } + + // Update instance index indirection buffer + if ( NodeAttachmentInstanceIndexBuffer->GetMaxElementCount() < drawInstanceIndices.size() ) { + auto requiredSize = Toolbox::NextPowerOfTwo( drawInstanceIndices.size() ); + NodeAttachmentInstanceIndexBuffer = std::make_unique>(); + NodeAttachmentInstanceIndexBuffer->Init( GetDevice().Get(), requiredSize ); + NodeAttachmentInstanceIndexBuffer->BindToVertexShader( Context.Get(), 12 ); + } + NodeAttachmentInstanceIndexBuffer->UpdateBuffer( Context.Get(), drawInstanceIndices.data(), + static_cast(drawInstanceIndices.size()) ); + + // Set up vertex/index buffers for this mesh + MeshInfo* mesh = itm.mesh; + if ( !mesh || !mesh->MeshVertexBuffer ) { + i = batchEnd; + continue; + } + + UINT offset = 0; + UINT stride = sizeof( ExVertexStruct ); + Context->IASetVertexBuffers( 0, 1, mesh->MeshVertexBuffer->GetVertexBuffer().GetAddressOf(), &stride, &offset ); + + if ( mesh->MeshIndexBuffer ) { + Context->IASetIndexBuffer( mesh->MeshIndexBuffer->GetVertexBuffer().Get(), VERTEX_INDEX_DXGI_FORMAT, 0 ); + Context->DrawIndexedInstanced( static_cast(mesh->Indices.size()), static_cast(instanceCount), 0, 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Indices.size() / 3) * static_cast(instanceCount); + } else { + Context->DrawInstanced( static_cast(mesh->Vertices.size()), static_cast(instanceCount), 0, 0 ); + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + static_cast(mesh->Vertices.size() / 3) * static_cast(instanceCount); + } + + i = batchEnd; + } + + // Unbind structured buffers + NodeAttachmentInstanceBuffer->UnbindFromVertexShader( Context.Get(), 10 ); + NodeAttachmentInstanceIndexBuffer->UnbindFromVertexShader( Context.Get(), 12 ); + + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::ClearNodeAttachmentBatches() { + for ( auto& [name, batch] : NodeAttachmentBatches ) { + batch.Entries.clear(); + batch.Instances.clear(); + } + NodeAttachmentBatches.clear(); +} + + +XRESULT D3D11GraphicsEngine::InitNodeAttachmentInstancingBuffer() { + // Instance buffer for node attachments + NodeAttachmentInstanceBuffer = std::make_unique>(); + HRESULT hr = NodeAttachmentInstanceBuffer->Init( GetDevice().Get(), 128 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance buffer"; + return XR_FAILED; + } + + // Instance index indirection buffer for node attachments + NodeAttachmentInstanceIndexBuffer = std::make_unique>(); + hr = NodeAttachmentInstanceIndexBuffer->Init( GetDevice().Get(), 128 ); + if ( FAILED( hr ) ) { + LogError() << "Failed to create node attachment instance index buffer"; + return XR_FAILED; + } + + LogInfo() << "Initialized node attachment instancing buffers"; + return XR_SUCCESS; +} + +void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections) +{ + float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & + GSWITCH_LINEAR_DEPTH) != 0; + + auto drawMultiIndexedInstancedIndirect = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI + ? DrawMultiIndexedInstancedIndirect + : Stub_DrawMultiIndexedInstancedIndirect; + + if ( Engine::GAPI->GetRendererState().RendererSettings.FastShadows ) + { + if ( !linearDepth ) // Only unbind when not rendering linear depth + { + // Unbind PS + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + if ( section->FullStaticMesh ) { + Engine::GAPI->DrawMeshInfo( nullptr, section->FullStaticMesh ); + } + } + return; + } + // Collect all meshes first, then batch by alpha requirement + static thread_local std::vector opaqueDrawArgs; + static thread_local std::vector> alphaMeshes; + opaqueDrawArgs.clear(); + alphaMeshes.clear(); + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + for ( const auto& meshPair : section->WorldMeshes ) { + // Skip non-standard materials (water, portals, etc.) + if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) + continue; + + zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; + + if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { + // Need alpha testing - cache texture + if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + alphaMeshes.emplace_back( tex, meshPair.second ); + } + } else { + D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS args; + args.IndexCountPerInstance = static_cast(meshPair.second->Indices.size()); + args.InstanceCount = 1; + args.StartIndexLocation = meshPair.second->BaseIndexLocation; + args.BaseVertexLocation = 0; + args.StartInstanceLocation = 0; + opaqueDrawArgs.push_back( args ); + } + + Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += + meshPair.second->Indices.size() / 3; + } + } + + // Draw all opaque meshes without pixel shader (depth only) using MDI + if ( !opaqueDrawArgs.empty() ) { + if ( !linearDepth ) { + // Unbind PS for depth-only rendering + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + // Initialize or resize the indirect buffer if needed + const size_t requiredSize = opaqueDrawArgs.size() * sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ); + + if ( !WorldMeshIndirectBuffer || WorldMeshIndirectBuffer->GetSizeInBytes() < requiredSize ) { + WorldMeshIndirectBuffer.reset( new D3D11IndirectBuffer ); + WorldMeshIndirectBuffer->Init( + opaqueDrawArgs.data(), requiredSize, + D3D11IndirectBuffer::B_INDEXBUFFER, D3D11IndirectBuffer::U_DYNAMIC, + D3D11IndirectBuffer::CA_WRITE ); + } else { + WorldMeshIndirectBuffer->UpdateBuffer( opaqueDrawArgs.data(), requiredSize ); + } + + // Execute multi-draw indirect call for all opaque meshes + // DrawMultiIndexedInstancedIndirect falls back to individual DrawIndexedInstancedIndirect + // calls via Stub_DrawMultiIndexedInstancedIndirect if hardware doesn't support MDI + drawMultiIndexedInstancedIndirect( Context.Get(), + static_cast(opaqueDrawArgs.size()), + WorldMeshIndirectBuffer->GetIndirectBuffer().Get(), + 0, + sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ) ); + } + + // Draw alpha-tested meshes with texture binding + if ( !alphaMeshes.empty() ) { + // Sort by texture to minimize binding changes + std::sort( alphaMeshes.begin(), alphaMeshes.end(), + []( const auto& a, const auto& b ) { return a.first < b.first; } ); + + ActivePS->Apply(); + zCTexture* lastTex = nullptr; + + for ( const auto& [tex, mesh] : alphaMeshes ) { + if ( tex != lastTex ) { + if (tex->CacheIn( 0.6f ) == zRES_CACHED_OUT) { + continue; + } + tex->Bind( 0 ); + lastTex = tex; + } + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } +} + +void D3D11GraphicsEngine::ShadowPass_DrawWorldMesh(const std::vector& visibleSections) +{ + float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + bool linearDepth = (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & + GSWITCH_LINEAR_DEPTH) != 0; + + static thread_local std::vector opaqueMeshes; + static thread_local std::vector> alphaMeshes; + opaqueMeshes.clear(); + alphaMeshes.clear(); + + for ( const WorldMeshSectionInfo* section : visibleSections ) { + for ( const auto& meshPair : section->WorldMeshes ) { + // Skip non-standard materials (water, portals, etc.) + if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) + continue; + + zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; + + if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { + // Need alpha testing - cache texture + if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + alphaMeshes.emplace_back( tex, meshPair.second ); + } + } else { + opaqueMeshes.push_back( meshPair.second ); + } + } + } + + // Draw all opaque meshes without pixel shader (depth only) + if ( !opaqueMeshes.empty() ) { + if ( !linearDepth ) // Only unbind when not rendering linear depth + { + // Unbind PS + Context->PSSetShader( nullptr, nullptr, 0 ); + } + + for ( WorldMeshInfo* mesh : opaqueMeshes ) { + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } + + // Draw alpha-tested meshes with texture binding + if ( !alphaMeshes.empty() ) { + // Sort by texture to minimize binding changes + std::sort( alphaMeshes.begin(), alphaMeshes.end(), + []( const auto& a, const auto& b ) { return a.first < b.first; } ); + + ActivePS->Apply(); + zCTexture* lastTex = nullptr; + + for ( const auto& [tex, mesh] : alphaMeshes ) { + if ( tex != lastTex ) { + tex->Bind( 0 ); + lastTex = tex; + } + DrawVertexBufferIndexedUINT( nullptr, nullptr, + mesh->Indices.size(), mesh->BaseIndexLocation ); + } + } +} + +/** Draws everything around the given position */ +void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR position, + float sectionRange, + const RenderShadowmapsParams& params ) { + int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); + static const char* timer_labels_world_mesh[MAX_CSM_CASCADES] + { + "World Mesh 0", + "World Mesh 1", + "World Mesh 2", + "World Mesh 3", + }; + static const char* timer_labels_vobs[MAX_CSM_CASCADES] + { + "VOBs 0", + "VOBs 1", + "VOBs 2", + "VOBs 3", + }; + static const char* timer_labels_skeletal[MAX_CSM_CASCADES] + { + "Skeletal Meshes 0", + "Skeletal Meshes 1", + "Skeletal Meshes 2", + "Skeletal Meshes 3", + }; + + // Setup renderstates + Engine::GAPI->GetRendererState().RasterizerState.SetDefault(); + Engine::GAPI->GetRendererState().RasterizerState.CullMode = + params.CullFront ? GothicRasterizerStateInfo::CM_CULL_FRONT + : GothicRasterizerStateInfo::CM_CULL_BACK; + if ( params.DontCull ) + Engine::GAPI->GetRendererState().RasterizerState.CullMode = + GothicRasterizerStateInfo::CM_CULL_NONE; + + Engine::GAPI->GetRendererState().RasterizerState.DepthClipEnable = true; + Engine::GAPI->GetRendererState().RasterizerState.SetDirty(); + + Engine::GAPI->GetRendererState().DepthState.SetDefault(); + Engine::GAPI->GetRendererState().DepthState.DepthBufferCompareFunc = GothicDepthBufferStateInfo::ECompareFunc::CF_COMPARISON_LESS_EQUAL; + Engine::GAPI->GetRendererState().DepthState.SetDirty(); + + XMMATRIX view = Engine::GAPI->GetViewMatrixXM(); + + Engine::GAPI->ResetWorldTransform(); + Engine::GAPI->SetViewTransformXM( view ); + + // Set shader + SetActivePixelShader( "PS_AtmosphereGround" ); + auto nrmPS = ActivePS; + SetActivePixelShader( "PS_DiffuseAlphaTest" ); + auto defaultPS = ActivePS; + SetActiveVertexShader( "VS_Ex" ); + + bool linearDepth = + (Engine::GAPI->GetRendererState().GraphicsState.FF_GSwitches & + GSWITCH_LINEAR_DEPTH) != 0; + if ( linearDepth ) { + SetActivePixelShader( "PS_LinDepth" ); + } + + // Set constant buffer + ActivePS->GetConstantBuffer()[0]->UpdateBuffer( + &Engine::GAPI->GetRendererState().GraphicsState ); + ActivePS->GetConstantBuffer()[0]->BindToPixelShader( 0 ); + + GSky* sky = Engine::GAPI->GetSky(); + ActivePS->GetConstantBuffer()[1]->UpdateBuffer( &sky->GetAtmosphereCB() ); + ActivePS->GetConstantBuffer()[1]->BindToPixelShader( 1 ); + + // Init drawcalls + SetupVS_ExMeshDrawCall(); + SetupVS_ExConstantBuffer(); + + ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); + ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); + + // Update and bind buffer of PS + PerObjectState ocb; + ocb.OS_AmbientColor = float3( 1, 1, 1 ); + ActivePS->GetConstantBuffer()[3]->UpdateBuffer( &ocb ); + ActivePS->GetConstantBuffer()[3]->BindToPixelShader( 3 ); + + float3 fPosition; XMStoreFloat3( fPosition.toXMFLOAT3(), position ); + INT2 s = WorldConverter::GetSectionOfPos( fPosition ); + + DistortionTexture->BindToPixelShader( 0 ); + + InfiniteRangeConstantBuffer->BindToPixelShader( 3 ); + + UpdateRenderStates(); + + auto enableCulling = Engine::GAPI->GetRendererState().RendererSettings.IsShadowFrustumCullingEnabled(); + + bool colorWritesEnabled = Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled; float alphaRef = Engine::GAPI->GetRendererState().GraphicsState.FF_AlphaRef; + auto& currentFrustum = params.CascadeIndex != -1 + ? params.CascadeCameraReplacements->at(params.CascadeIndex).frustum + : Engine::GAPI->GetCameraReplacementPtr() != nullptr + ? Engine::GAPI->GetCameraReplacementPtr()->frustum + : Frustum::AlwaysContainingFrustum(); if ( Engine::GAPI->GetRendererState().RendererSettings.DrawWorldMesh ) { auto _ = START_TIMING( timer_labels_world_mesh[timerLabelIndex] ); @@ -4500,130 +5596,62 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p const auto sectionRangeSq = sectionRange * sectionRange; // Bind wrapped mesh vertex buffers DrawVertexBufferIndexedUINT( - Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer, - Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer, 0, 0 ); + Engine::GAPI->GetWrappedWorldMesh()->MeshVertexBuffer.get(), + Engine::GAPI->GetWrappedWorldMesh()->MeshIndexBuffer.get(), 0, 0 ); ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &XMMatrixIdentity() ); ActiveVS->GetConstantBuffer()[1]->BindToVertexShader( 1 ); static thread_local std::vector visibleSections; visibleSections.clear(); - + for ( const auto& itx : Engine::GAPI->GetWorldSections() ) { for ( const auto& ity : itx.second ) { float dx = static_cast(itx.first - s.x); float dy = static_cast(ity.first - s.y); float lenSq = dx * dx + dy * dy; - if ( lenSq < sectionRangeSq ) { - visibleSections.push_back( &ity.second ); + if ( lenSq > sectionRangeSq ) { + continue; } - } - } - - if ( Engine::GAPI->GetRendererState().RendererSettings.FastShadows ) { - if ( !linearDepth ) // Only unbind when not rendering linear depth - { - // Unbind PS - Context->PSSetShader( nullptr, nullptr, 0 ); - } - for ( const WorldMeshSectionInfo* section : visibleSections ) { - if ( section->FullStaticMesh ) { - Engine::GAPI->DrawMeshInfo( nullptr, section->FullStaticMesh ); - } + visibleSections.push_back( &ity.second ); } + } + + if (Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI) { + ShadowPass_DrawWorldMesh_Indirect(visibleSections); } else { - // Collect all meshes first, then batch by alpha requirement - static thread_local std::vector opaqueDrawArgs; - static thread_local std::vector> alphaMeshes; - opaqueDrawArgs.clear(); - alphaMeshes.clear(); - - for ( const WorldMeshSectionInfo* section : visibleSections ) { - for ( const auto& meshPair : section->WorldMeshes ) { - // Skip non-standard materials (water, portals, etc.) - if ( meshPair.first.Info->MaterialType != MaterialInfo::MT_None ) - continue; - - zCTexture* tex = meshPair.first.Material ? meshPair.first.Material->GetTexture() : nullptr; - - if ( tex && tex->HasAlphaChannel() && alphaRef > 0.0f ) { - // Need alpha testing - cache texture - if ( tex->CacheIn( 0.6f ) == zRES_CACHED_IN ) { - alphaMeshes.emplace_back( tex, meshPair.second ); - } - } else { - D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS args; - args.IndexCountPerInstance = static_cast(meshPair.second->Indices.size()); - args.InstanceCount = 1; - args.StartIndexLocation = meshPair.second->BaseIndexLocation; - args.BaseVertexLocation = 0; - args.StartInstanceLocation = 0; - opaqueDrawArgs.push_back( args ); - } - - Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnTriangles += - meshPair.second->Indices.size() / 3; - } - } - - // Draw all opaque meshes without pixel shader (depth only) using MDI - if ( !opaqueDrawArgs.empty() ) { - if ( !linearDepth ) { - // Unbind PS for depth-only rendering - Context->PSSetShader( nullptr, nullptr, 0 ); - } - - // Initialize or resize the indirect buffer if needed - const size_t requiredSize = opaqueDrawArgs.size() * sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ); - - if ( !WorldMeshIndirectBuffer || WorldMeshIndirectBuffer->GetSizeInBytes() < requiredSize ) { - WorldMeshIndirectBuffer.reset( new D3D11IndirectBuffer ); - WorldMeshIndirectBuffer->Init( - opaqueDrawArgs.data(), requiredSize, - D3D11IndirectBuffer::B_INDEXBUFFER, D3D11IndirectBuffer::U_DYNAMIC, - D3D11IndirectBuffer::CA_WRITE ); - } else { - WorldMeshIndirectBuffer->UpdateBuffer( opaqueDrawArgs.data(), requiredSize ); - } - - // Execute multi-draw indirect call for all opaque meshes - // DrawMultiIndexedInstancedIndirect falls back to individual DrawIndexedInstancedIndirect - // calls via Stub_DrawMultiIndexedInstancedIndirect if hardware doesn't support MDI - DrawMultiIndexedInstancedIndirect( Context.Get(), - static_cast(opaqueDrawArgs.size()), - WorldMeshIndirectBuffer->GetIndirectBuffer().Get(), - 0, - sizeof( D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS ) ); - } - - // Draw alpha-tested meshes with texture binding - if ( !alphaMeshes.empty() ) { - // Sort by texture to minimize binding changes - std::sort( alphaMeshes.begin(), alphaMeshes.end(), - []( const auto& a, const auto& b ) { return a.first < b.first; } ); - - ActivePS->Apply(); - zCTexture* lastTex = nullptr; - - for ( const auto& [tex, mesh] : alphaMeshes ) { - if ( tex != lastTex ) { - tex->Bind( 0 ); - lastTex = tex; - } - DrawVertexBufferIndexedUINT( nullptr, nullptr, - mesh->Indices.size(), mesh->BaseIndexLocation ); - } - } + ShadowPass_DrawWorldMesh(visibleSections); } } if ( Engine::GAPI->GetRendererState().RendererSettings.DrawVOBs ) { - auto renderQueue = ShadowMaps->GetRenderQueue( params.CascadeIndex ); - renderQueue->ProcessQueue(); + static std::vector potentialCasters; + std::vector& vobs = potentialCasters; + if (params.CascadeIndex != -1) { + auto renderQueue = ShadowMaps->GetRenderQueue( params.CascadeIndex ); + renderQueue->ProcessQueue(); - auto& vobs = renderQueue->GetVobs(); + vobs = renderQueue->GetVobs(); + } else { + static std::vector _1; + static std::vector _2; + potentialCasters.reserve(1024); + potentialCasters.clear(); + + LegacyRenderQueueProxy q(potentialCasters, _1, _2); + RndCullContext ctx; + ctx.queue = &q; + ctx.cameraPosition = Engine::GAPI->GetCameraPosition(); + ctx.stage = RenderStage::STAGE_DRAW_WORLD; + ctx.frustum = currentFrustum; + ctx.drawDistances.OutdoorVobs = Engine::GAPI->GetRendererState().RendererSettings.OutdoorVobDrawRadius; + ctx.drawDistances.OutdoorVobsSmall = Engine::GAPI->GetRendererState().RendererSettings.OutdoorSmallVobDrawRadius; + ctx.drawDistances.IndoorVobs = Engine::GAPI->GetRendererState().RendererSettings.IndoorVobDrawRadius; + ctx.drawDistances.VisualFX = Engine::GAPI->GetRendererState().RendererSettings.VisualFXDrawRadius; + Engine::GAPI->CollectVisibleVobs( ctx ); + } // clear any residue of main render pass for ( auto const& staticMeshVisual : Engine::GAPI->GetStaticMeshVisuals() ) { @@ -4762,7 +5790,7 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p MeshInfo* mi = mlist[i]; // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), staticMeshVisual->Instances.size(), sizeof( ExVertexStruct ), staticMeshVisual->StartInstanceNum ); @@ -4783,7 +5811,6 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p auto skeletalRadiusSq = Engine::GAPI->GetRendererState().RendererSettings.SkeletalMeshDrawRadius * Engine::GAPI->GetRendererState().RendererSettings.SkeletalMeshDrawRadius; - auto& currentFrustum = params.CascadeCameraReplacements->at(params.CascadeIndex).frustum; XMVECTOR vSkeletalRadiusSq = XMVectorReplicate(skeletalRadiusSq); // Draw skeletal meshes @@ -4791,11 +5818,16 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p static std::vector animatedSkeletalMeshVobs; animatedSkeletalMeshVobs.clear(); - const bool isLastCascade = params.CascadeIndex == params.CascadeSplits.size() - 2; + const bool isLastCascade = params.CascadeSplits.size() == 0 + || params.CascadeIndex == params.CascadeSplits.size() - 2; for ( auto const& skeletalMeshVob : Engine::GAPI->GetSkeletalMeshVobs() ) { if ( !skeletalMeshVob->VisualInfo ) continue; + if ( skeletalMeshVob->IndoorVob ) { + continue; // indoor is handled by dynamic lighting + } + // Ghosts shouldn't have shadows if ( skeletalMeshVob->Vob->GetVisualAlpha() && skeletalMeshVob->Vob->GetVobTransparency() < 0.7f ) { continue; @@ -4806,9 +5838,6 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p } if ( enableCulling && !isLastCascade ) { - // Frustum culling using a bounding sphere - // Use the mesh size as radius, centered at the vob position - if ( currentFrustum.Contains( skeletalMeshVob->Vob->GetBBox()) == DirectX::ContainmentType::DISJOINT) { // Not hitting our frustum and not the active view. continue; @@ -4820,10 +5849,10 @@ void XM_CALLCONV D3D11GraphicsEngine::DrawWorldAroundForWorldShadow( FXMVECTOR p bool drawAttachments = true; if ( Engine::GAPI->GetRendererState().RendererSettings.ShadowFrustumCullingMode == GothicRendererSettings::E_ShadowFrustumCulling::SHD_FRUSTUM_CULLING_AGGRESSIVE ) { - drawAttachments = params.CascadeIndex <= 1; // skip attachments on higher cascades, player won't notice, hopefully + drawAttachments = params.CascadeIndex > 0 && !isLastCascade; // skip attachments on last cascade } // we should not need to update the skeletal meshes again, as they were updated before drawing the main scene - Engine::GAPI->DrawSkeletalMeshVobs( animatedSkeletalMeshVobs, false, drawAttachments ); + Engine::GAPI->DrawSkeletalMeshVobs( animatedSkeletalMeshVobs, true, drawAttachments ); } Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; @@ -4907,24 +5936,17 @@ void D3D11GraphicsEngine::ApplyWindProps( VS_ExConstantBuffer_Wind& windBuff ) { /** Draws the static vobs instanced */ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { static std::vector vobs; - static std::vector lights; static std::vector mobs; - - const auto& renderSettings = Engine::GAPI->GetRendererState().RendererSettings; - if ( RenderingStage == DES_MAIN && renderSettings.EnableShadows ) { + + if (RenderingStage == DES_MAIN) { auto _ = START_TIMING( "Prepare Shadow" ); ShadowMaps->PrepareRender(); // calculate cameras, frusti collect any vobs needed, etc. } - // Need to collect alpha-meshes to render them laterdy - struct alphaMeshData { - MeshKey mk; - MeshInfo* mi; - MeshVisualInfo* vi; - std::vector instances; - }; - std::vector AlphaMeshes; - AlphaMeshes.reserve( 64 ); + const auto& renderSettings = Engine::GAPI->GetRendererState().RendererSettings; + + // Need to collect alpha-meshes to render them later + m_AlphaMeshes.reserve( 64 ); { auto _ = START_TIMING( "VOBs" ); @@ -4970,7 +5992,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { if ( !renderSettings.FixViewFrustum || (renderSettings.FixViewFrustum && vobs.empty()) ) { - Engine::GAPI->CollectVisibleVobs( vobs, lights, mobs ); + Engine::GAPI->CollectVisibleVobs( vobs, m_FrameLights, mobs ); } } @@ -5077,7 +6099,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { if ( !doReset || blendAdd || blendBlend ) { MeshVisualInfo* info = staticMeshVisual; for ( MeshInfo* mesh : mlist ) { - alphaMeshData data = {}; + AlphaMeshData data = {}; data.mk = itt.first; data.vi = info; data.mi = mesh; @@ -5086,7 +6108,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // and the only usage i found was a bug (?) in pirates camp // where there would be cobwebs randomly. data.instances = staticMeshVisual->Instances; - AlphaMeshes.emplace_back( data ); + m_AlphaMeshes.emplace_back( data ); } doReset = false; @@ -5110,7 +6132,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { /*// Apply colors for these meshes MaterialInfo::Buffer b; ZeroMemory(&b, sizeof(b)); - b.Color = itt->first.Material->GetColor(); + b.Color = itt->first.Material->GetColor().ToFloat4(); PS_Diffuse->GetConstantBuffer()[2]->UpdateBuffer(&b); PS_Diffuse->GetConstantBuffer()[2]->BindToPixelShader(2);*/ #endif @@ -5126,7 +6148,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { MaterialInfo::Buffer b = {}; - b.Color = itt.first.Material->GetColor(); + b.Color = itt.first.Material->GetColor().ToFloat4(); PS_DiffuseAlphatest->GetConstantBuffer()[2]->UpdateBuffer( &b ); PS_DiffuseAlphatest->GetConstantBuffer()[2]->BindToPixelShader( 2 ); @@ -5177,7 +6199,7 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { } // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), staticMeshVisual->Instances.size(), sizeof( ExVertexStruct ), staticMeshVisual->StartInstanceNum ); @@ -5199,10 +6221,12 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // Mobs use zengine functions for binding textures so let's reset zengine texture state Engine::GAPI->ResetRenderStates(); + Engine::GAPI->DrawSkeletalMeshVobs( mobs, true, true ); + static std::vector bones = {}; for ( SkeletalVobInfo* mob : mobs ) { - Engine::GAPI->DrawSkeletalMeshVob( mob, FLT_MAX ); - + mob->VisibleInRenderPass = false; // Reset this for the next frame + zCModel* model = static_cast(mob->Vob->GetVisual()); XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); XMMATRIX world = mob->Vob->GetWorldMatrixXM() * scale; @@ -5224,23 +6248,17 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { } } - if ( RenderingStage == DES_MAIN ) { - if ( renderSettings.DrawParticleEffects ) { - auto _ = START_TIMING( "DrawVOBsInstanced->DrawParticleEffects" ); - std::vector decals; - zCCamera::GetCamera()->Activate(); - Engine::GAPI->GetVisibleDecalList( decals ); - - DrawDecalList( decals, true ); - DrawQuadMarks(); - } - - auto _ = START_TIMING( "DrawVOBsInstanced->Lighting" ); - // Draw lighting, since everything is drawn by now and we have the lights - // here - DrawLighting( lights ); + if ( !renderSettings.FixViewFrustum ) { + vobs.clear(); + mobs.clear(); } + return XR_SUCCESS; +} + +/** Draws the static VOBs */ +XRESULT D3D11GraphicsEngine::DrawFrameAlphaMeshes() +{ // Make sure lighting doesn't mess up our state SetDefaultStates(); @@ -5252,86 +6270,77 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { GetContext()->OMSetRenderTargets( 1, HDRBackBuffer->GetRenderTargetView().GetAddressOf(), DepthStencilBuffer->GetDepthStencilView().Get() ); + + // re-setup dynamic instancing buffer with correct instances and values + // shadow pass breaks the "global" state of this. + + byte* data; + UINT size; + UINT loc = 0; + DynamicInstancingBuffer->Map( D3D11VertexBuffer::M_WRITE_DISCARD, + reinterpret_cast(&data), &size ); + for ( auto const& alphaData : m_AlphaMeshes ) { + alphaData.vi->StartInstanceNum = loc; + memcpy( data + loc * sizeof( VobInstanceInfo ), &alphaData.instances[0], + sizeof( VobInstanceInfo ) * alphaData.instances.size() ); + loc += alphaData.instances.size(); + } + DynamicInstancingBuffer->Unmap(); + + for ( auto const& alphaMesh : m_AlphaMeshes ) { + const MeshKey& mk = alphaMesh.mk; + zCTexture* tx = mk.Material->GetAniTexture(); + if ( !tx ) continue; - { - auto _1 = Engine::GraphicsEngine->RecordGraphicsEvent( L"DrawVOBsInstanced->AlphaMeshes" ); + // Check for alphablending on world mesh + bool blendAdd = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_ADD; + bool blendBlend = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_BLEND; - // re-setup dynamic instancing buffer with correct instances and values - // shadow pass breaks the "global" state of this. + // Bind texture + MeshInfo* mi = alphaMesh.mi; + MeshVisualInfo* vi = alphaMesh.vi; + auto& instances = alphaMesh.instances; - byte* data; - UINT size; - UINT loc = 0; - DynamicInstancingBuffer->Map( D3D11VertexBuffer::M_WRITE_DISCARD, - reinterpret_cast(&data), &size ); - for ( auto const& alphaData : AlphaMeshes ) { - alphaData.vi->StartInstanceNum = loc; - memcpy( data + loc * sizeof( VobInstanceInfo ), &alphaData.instances[0], - sizeof( VobInstanceInfo ) * alphaData.instances.size() ); - loc += alphaData.instances.size(); - } - DynamicInstancingBuffer->Unmap(); + if ( tx->CacheIn( 0.6f ) == zRES_CACHED_IN ) { + MyDirectDrawSurface7* surface = tx->GetSurface(); + ID3D11ShaderResourceView* srv[3]; - for ( auto const& alphaMesh : AlphaMeshes ) { - const MeshKey& mk = alphaMesh.mk; - zCTexture* tx = mk.Material->GetAniTexture(); - if ( !tx ) continue; + // Get diffuse and normalmap + srv[0] = surface->GetEngineTexture()->GetShaderResourceView().Get(); + srv[1] = surface->GetNormalmap() + ? surface->GetNormalmap()->GetShaderResourceView().Get() + : nullptr; + srv[2] = surface->GetFxMap() + ? surface->GetFxMap()->GetShaderResourceView().Get() + : nullptr; - // Check for alphablending on world mesh - bool blendAdd = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_ADD; - bool blendBlend = mk.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_BLEND; - - // Bind texture - MeshInfo* mi = alphaMesh.mi; - MeshVisualInfo* vi = alphaMesh.vi; - auto& instances = alphaMesh.instances; - - if ( tx->CacheIn( 0.6f ) == zRES_CACHED_IN ) { - MyDirectDrawSurface7* surface = tx->GetSurface(); - ID3D11ShaderResourceView* srv[3]; - - // Get diffuse and normalmap - srv[0] = surface->GetEngineTexture()->GetShaderResourceView().Get(); - srv[1] = surface->GetNormalmap() - ? surface->GetNormalmap()->GetShaderResourceView().Get() - : nullptr; - srv[2] = surface->GetFxMap() - ? surface->GetFxMap()->GetShaderResourceView().Get() - : nullptr; - - // Bind both - GetContext()->PSSetShaderResources( 0, 3, srv ); - - if ( (blendAdd || blendBlend) && - !Engine::GAPI->GetRendererState().BlendState.BlendEnabled ) { - if ( blendAdd ) - Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); - else if ( blendBlend ) - Engine::GAPI->GetRendererState().BlendState.SetAlphaBlending(); - - Engine::GAPI->GetRendererState().BlendState.SetDirty(); - - Engine::GAPI->GetRendererState().DepthState.DepthWriteEnabled = false; - Engine::GAPI->GetRendererState().DepthState.SetDirty(); + // Bind both + GetContext()->PSSetShaderResources( 0, 3, srv ); - UpdateRenderStates(); - } + if ( (blendAdd || blendBlend) && + !Engine::GAPI->GetRendererState().BlendState.BlendEnabled ) { + if ( blendAdd ) + Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); + else if ( blendBlend ) + Engine::GAPI->GetRendererState().BlendState.SetAlphaBlending(); - MaterialInfo* info = mk.Info; - if ( !info->Constantbuffer ) info->UpdateConstantbuffer(); + Engine::GAPI->GetRendererState().BlendState.SetDirty(); - info->Constantbuffer->BindToPixelShader( 2 ); + Engine::GAPI->GetRendererState().DepthState.DepthWriteEnabled = false; + Engine::GAPI->GetRendererState().DepthState.SetDirty(); + + UpdateRenderStates(); } - g_windBuffer.minHeight = vi->BBox.Min.y; - g_windBuffer.maxHeight = vi->BBox.Max.y; + MaterialInfo* info = mk.Info; + if ( !info->Constantbuffer ) info->UpdateConstantbuffer(); if ( ActiveVS ) { ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &g_windBuffer ); } // Draw batch - DrawInstanced( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), instances.size(), sizeof( ExVertexStruct ), vi->StartInstanceNum ); @@ -5339,19 +6348,28 @@ XRESULT D3D11GraphicsEngine::DrawVOBsInstanced() { // Reset visual vi->StartNewFrame(); } - AlphaMeshes.clear(); - } - if ( !renderSettings.FixViewFrustum ) { - lights.clear(); - vobs.clear(); - mobs.clear(); - } + g_windBuffer.minHeight = vi->BBox.Min.y; + g_windBuffer.maxHeight = vi->BBox.Max.y; + + if ( ActiveVS ) { + ActiveVS->GetConstantBuffer()[1]->UpdateBuffer( &g_windBuffer ); + } + // Draw batch + DrawInstanced( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), + DynamicInstancingBuffer.get(), sizeof( VobInstanceInfo ), + instances.size(), sizeof( ExVertexStruct ), + vi->StartInstanceNum ); + + // Reset visual + vi->StartNewFrame(); + } + m_AlphaMeshes.clear(); + return XR_SUCCESS; } -/** Draws the static VOBs */ XRESULT D3D11GraphicsEngine::DrawVOBs( bool noTextures ) { return DrawVOBsInstanced(); } @@ -5641,11 +6659,6 @@ BaseLineRenderer* D3D11GraphicsEngine::GetLineRenderer() { return LineRenderer.get(); } -/** Applys the lighting to the scene */ -XRESULT D3D11GraphicsEngine::DrawLighting( std::vector& lights ) { - return ShadowMaps->DrawLighting(lights); -} - /** Renders the shadowmaps for a pointlight */ void XM_CALLCONV D3D11GraphicsEngine::RenderShadowCube( FXMVECTOR position, float range, @@ -5740,7 +6753,7 @@ void D3D11GraphicsEngine::DrawVobSingle( VobInfo* vob, zCCamera& camera ) { for ( auto const& itm2nd : itm.second ) { // Draw instances DrawVertexBufferIndexed( - itm2nd->MeshVertexBuffer, itm2nd->MeshIndexBuffer, + itm2nd->MeshVertexBuffer.get(), itm2nd->MeshIndexBuffer.get(), itm2nd->Indices.size() ); } } @@ -6107,7 +7120,7 @@ void D3D11GraphicsEngine::DrawDecalList( const std::vector& decals, if ( !lighting ) { GhostAlphaConstantBuffer gacb; gacb.GA_ViewportSize = float2( Engine::GraphicsEngine->GetResolution().x, Engine::GraphicsEngine->GetResolution().y ); - gacb.GA_Alpha = (material->GetColor() >> 24) * inv255f; + gacb.GA_Alpha = static_cast(material->GetColor().dword >> 24) * inv255f; ActivePS->GetConstantBuffer()[0]->UpdateBuffer( &gacb ); ActivePS->GetConstantBuffer()[0]->BindToPixelShader( 0 ); } @@ -6396,7 +7409,7 @@ void D3D11GraphicsEngine::DrawFrameParticleMeshes( std::unordered_mapMeshVertexBuffer, itm2nd->MeshIndexBuffer, + itm2nd->MeshVertexBuffer.get(), itm2nd->MeshIndexBuffer.get(), itm2nd->Indices.size() ); } } @@ -6406,7 +7419,9 @@ void D3D11GraphicsEngine::DrawFrameParticleMeshes( std::unordered_map>& particles, - std::map& info ) { + std::map& info, + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion ) { if ( particles.empty() ) return; SetDefaultStates(); @@ -6417,8 +7432,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( // TODO: Maybe make particles draw at a lower res and bilinear upsample the result. // Clear GBuffer0 to hold the refraction vectors since it's not needed anymore - Context->ClearRenderTargetView( GBuffer0_Diffuse->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - Context->ClearRenderTargetView( GBuffer1_Normals->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + Context->ClearRenderTargetView( bufferParticleColor->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); + Context->ClearRenderTargetView( bufferParticleDistortion->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); RefractionInfoConstantBuffer ricb = {}; ricb.RI_Projection = Engine::GAPI->GetProjectionMatrix(); @@ -6456,8 +7471,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( } ID3D11RenderTargetView* rtv[] = { - GBuffer0_Diffuse->GetRenderTargetView().Get(), - GBuffer1_Normals->GetRenderTargetView().Get() }; + bufferParticleColor->GetRenderTargetView().Get(), + bufferParticleDistortion->GetRenderTargetView().Get() }; Context->OMSetRenderTargets( 2, rtv, DepthStencilBuffer->GetDepthStencilView().Get() ); // Bind view/proj @@ -6542,8 +7557,8 @@ void D3D11GraphicsEngine::DrawFrameParticles( state.BlendState.SetDefault(); state.BlendState.SetDirty(); - GBuffer0_Diffuse->BindToPixelShader( Context.Get(), 1 ); - GBuffer1_Normals->BindToPixelShader( Context.Get(), 2 ); + bufferParticleColor->BindToPixelShader( Context.Get(), 1 ); + bufferParticleDistortion->BindToPixelShader( Context.Get(), 2 ); // Copy scene behind the particle systems auto tempBuffer = PfxRenderer->GetTempBuffer(); @@ -6624,16 +7639,16 @@ void D3D11GraphicsEngine::SaveScreenshot() { auto Resolution = GetResolution(); // Buffer for scaling down the image auto rt = std::make_unique( - GetDevice().Get(), Resolution.x, Resolution.y, DXGI_FORMAT_ENGINE_SWAPCHAIN ); + GetDevice().Get(), Resolution.x, Resolution.y, DXGI_FORMAT_ENGINE_DEFAULT ); // Downscale to 256x256 PfxRenderer->CopyTextureToRTV( HDRBackBuffer->GetShaderResView(), rt->GetRenderTargetView() ); - D3D11_TEXTURE2D_DESC texDesc = {}; + D3D11_TEXTURE2D_DESC texDesc; texDesc.ArraySize = 1; texDesc.BindFlags = 0; texDesc.CPUAccessFlags = 0; - texDesc.Format = DXGI_FORMAT_ENGINE_SWAPCHAIN ; + texDesc.Format = DXGI_FORMAT_ENGINE_DEFAULT; texDesc.Width = Resolution.x; // must be same as backbuffer texDesc.Height = Resolution.y; // must be same as backbuffer texDesc.MipLevels = 1; diff --git a/D3D11Engine/D3D11GraphicsEngine.h b/D3D11Engine/D3D11GraphicsEngine.h index c1119a30..bcc89b5d 100644 --- a/D3D11Engine/D3D11GraphicsEngine.h +++ b/D3D11Engine/D3D11GraphicsEngine.h @@ -4,6 +4,8 @@ #include "GothicAPI.h" #include "D3D11ShadowMap.h" #include "D3D11ShaderManager.h" +#include "D3D11StructuredBuffer.h" +#include "ConstantBufferStructs.h" struct RenderToDepthStencilBuffer; @@ -28,7 +30,6 @@ const unsigned int PARTICLES_BUFFER_SIZE = 3072 * sizeof( ParticleInstanceInfo ) const unsigned int MORPHEDMESH_SMALL_BUFFER_SIZE = 3072 * sizeof( ExVertexStruct ); const unsigned int MORPHEDMESH_HIGH_BUFFER_SIZE = 20480 * sizeof( ExVertexStruct ); const unsigned int HUD_BUFFER_SIZE = 6 * sizeof( ExVertexStruct ); -const int NUM_MAX_BONES = 96; const int unsigned INSTANCING_BUFFER_SIZE = sizeof( VobInstanceInfo ) * 2048; @@ -47,6 +48,13 @@ struct MeshInfo; struct RenderToTextureBuffer; class D3D11Effect; +struct AlphaMeshData { + MeshKey mk; + MeshInfo* mi; + MeshVisualInfo* vi; + std::vector instances; +}; + class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { public: D3D11GraphicsEngine(); @@ -187,13 +195,6 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { RenderToDepthStencilBuffer* GetDepthBuffer() { return DepthStencilBuffer.get(); } RenderToTextureBuffer* GetDepthBufferCopy() { return DepthStencilBufferCopy.get(); } - /** Returns the first GBuffer */ - RenderToTextureBuffer& GetGBuffer0() { return *GBuffer0_Diffuse; } - - /** Returns the second GBuffer */ - RenderToTextureBuffer& GetGBuffer1() { return *GBuffer1_Normals; } - RenderToTextureBuffer& GetGBuffer2() { return *GBuffer2_SpecIntens_SpecPower; } - /** Returns the HDRBackbuffer */ RenderToTextureBuffer& GetHDRBackBuffer() { return *HDRBackBuffer; } @@ -234,6 +235,9 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { virtual void DrawVobSingle( VobInfo* vob, zCCamera& camera ) override; /** Draws everything around the given position */ + void ShadowPass_DrawWorldMesh_Indirect(const std::vector& visibleSections); + void ShadowPass_DrawWorldMesh(const std::vector& visibleSections); + void XM_CALLCONV DrawWorldAroundForWorldShadow( FXMVECTOR position, float sectionRange, const RenderShadowmapsParams& params ); void XM_CALLCONV DrawWorldAround( FXMVECTOR position, float range, @@ -253,13 +257,11 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { /** Draws the static vobs instanced */ XRESULT DrawVOBsInstanced(); + XRESULT DrawFrameAlphaMeshes(); /** Set wind props in const buffer */ void ApplyWindProps( VS_ExConstantBuffer_Wind& buf ); - /** Applys the lighting to the scene */ - XRESULT DrawLighting( std::vector& lights ); - /** Called when we started to render the world */ virtual XRESULT OnStartWorldRendering(); @@ -331,7 +333,8 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { void DrawFrameParticleMeshes( std::unordered_map& progMeshes ); /** Draws particle effects */ - void DrawFrameParticles( std::map>& particles, std::map& info ); + void DrawFrameParticles(std::map>& particles, std::map& info, RenderToTextureBuffer + * bufferParticleColor, RenderToTextureBuffer* bufferParticleDistortion); /** Returns the settings window availability */ bool HasSettingsWindow(); @@ -359,6 +362,23 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { const XMFLOAT4X4& GetPrevViewProjMatrix() const { return m_PrevViewProjMatrix; } void StorePrevViewProjMatrix(); + + // New methods for instanced skeletal rendering + XRESULT InitSkeletalInstancingBuffers(); + void BuildSkeletalMeshBatches( const std::vector& vobs ); + XRESULT DrawSkeletalMeshBatched(); + XRESULT DrawSkeletalMeshInstanced( SkeletalMeshBatch& batch ); + void ClearSkeletalMeshBatches(); + + // Initialize node attachment instancing buffer + XRESULT InitNodeAttachmentInstancingBuffer(); + + // New methods for node attachment batching (moved from GothicAPI) + void BuildNodeAttachmentBatches( const std::vector& vobs, bool updateState ); + XRESULT DrawNodeAttachmentBatched(); + XRESULT DrawNodeAttachmentInstanced( NodeAttachmentBatch& batch ); + void ClearNodeAttachmentBatches(); + protected: void StoreVobPreviousTransforms(); @@ -374,9 +394,6 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { /** Swapchain buffers */ Microsoft::WRL::ComPtr BackbufferRTV; - std::unique_ptr GBuffer0_Diffuse; - std::unique_ptr GBuffer1_Normals; // Normals - std::unique_ptr GBuffer2_SpecIntens_SpecPower; // SpecIntensity / SpecPower std::unique_ptr DepthStencilBufferCopy; // DummyShadowCubemapTexture moved into ShadowMaps std::unique_ptr ShadowMaps; @@ -424,6 +441,9 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { Microsoft::WRL::ComPtr ReflectionCube; Microsoft::WRL::ComPtr ReflectionCube2; private: + std::vector m_AlphaMeshes; + std::vector m_FrameLights; + /** World-Mesh indirect buffer */ std::unique_ptr WorldMeshIndirectBuffer; @@ -472,4 +492,19 @@ class D3D11GraphicsEngine : public D3D11GraphicsEngineBase { XMFLOAT4X4 m_PrevViewProjMatrix; INT2 NewResolution; + + // Instanced skeletal mesh rendering buffers + std::unique_ptr> SkeletalInstanceBuffer; + std::unique_ptr> SkeletalBoneBuffer; + std::unique_ptr> SkeletalInstanceIndexBuffer; // Indirection buffer for instance lookup + + // Batched skeletal meshes for current frame + std::unordered_map SkeletalMeshBatches; + + // Node attachment instancing buffers + std::unique_ptr> NodeAttachmentInstanceBuffer; + std::unique_ptr> NodeAttachmentInstanceIndexBuffer; // Indirection buffer for instance lookup + + // Batched node attachments for current frame + std::unordered_map NodeAttachmentBatches; }; diff --git a/D3D11Engine/D3D11GraphicsEngineBase.cpp b/D3D11Engine/D3D11GraphicsEngineBase.cpp index 131d5139..25cde757 100644 --- a/D3D11Engine/D3D11GraphicsEngineBase.cpp +++ b/D3D11Engine/D3D11GraphicsEngineBase.cpp @@ -11,7 +11,6 @@ #include "zCView.h" const unsigned int DRAWVERTEXARRAY_BUFFER_SIZE = 4096 * sizeof( ExVertexStruct ); -const int NUM_MAX_BONES = 96; const unsigned int INSTANCING_BUFFER_SIZE = sizeof( VobInstanceInfo ) * 2048; #if defined(BUILD_GOTHIC_1_08k) && !defined(BUILD_1_12F) diff --git a/D3D11Engine/D3D11NVHBAO.cpp b/D3D11Engine/D3D11NVHBAO.cpp index 344b21d5..c863169b 100644 --- a/D3D11Engine/D3D11NVHBAO.cpp +++ b/D3D11Engine/D3D11NVHBAO.cpp @@ -32,7 +32,11 @@ XRESULT D3D11NVHBAO::Init() { } /** Renders the HBAO-Effect onto the given RTV */ -XRESULT D3D11NVHBAO::Render( Microsoft::WRL::ComPtr pOutputColorRTV ) { +XRESULT D3D11NVHBAO::Render( + const Microsoft::WRL::ComPtr& pOutputColorRTV, + const Microsoft::WRL::ComPtr& pFullResDepthTexSRV, + const Microsoft::WRL::ComPtr& pFullResNormalTexSRV + ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); D3D11_VIEWPORT vp; @@ -43,13 +47,13 @@ XRESULT D3D11NVHBAO::Render( Microsoft::WRL::ComPtr pOut GFSDK_SSAO_InputData_D3D11 Input; Input.DepthData.DepthTextureType = GFSDK_SSAO_HARDWARE_DEPTHS; - Input.DepthData.pFullResDepthTextureSRV = engine->GetDepthBuffer()->GetShaderResView(); + Input.DepthData.pFullResDepthTextureSRV = pFullResDepthTexSRV.Get(); Input.DepthData.ProjectionMatrix.Data = GFSDK_SSAO_Float4x4( reinterpret_cast(&Engine::GAPI->GetProjectionMatrix()) ); Input.DepthData.ProjectionMatrix.Layout = GFSDK_SSAO_COLUMN_MAJOR_ORDER; Input.DepthData.MetersToViewSpaceUnits = settings.MetersToViewSpaceUnits; Input.NormalData.Enable = true; - Input.NormalData.pFullResNormalTextureSRV = engine->GetGBuffer1().GetShaderResView(); + Input.NormalData.pFullResNormalTextureSRV = pFullResNormalTexSRV.Get(); auto identity = XMMatrixIdentity(); Input.NormalData.WorldToViewMatrix.Data = GFSDK_SSAO_Float4x4( reinterpret_cast(&identity) ); // We already have them in view-space Input.NormalData.WorldToViewMatrix.Layout = GFSDK_SSAO_COLUMN_MAJOR_ORDER; diff --git a/D3D11Engine/D3D11NVHBAO.h b/D3D11Engine/D3D11NVHBAO.h index a0caa23c..d8b5a5ec 100644 --- a/D3D11Engine/D3D11NVHBAO.h +++ b/D3D11Engine/D3D11NVHBAO.h @@ -11,7 +11,9 @@ class D3D11NVHBAO { XRESULT Init(); /** Renders the HBAO-Effect onto the given RTV */ - XRESULT Render( Microsoft::WRL::ComPtr pOutputColorRTV ); + XRESULT Render(const Microsoft::WRL::ComPtr& pOutputColorRTV, const Microsoft::WRL::ComPtr< + ID3D11ShaderResourceView> + & pFullResDepthTexSRV, const Microsoft::WRL::ComPtr& pFullResNormalTexSRV); private: /** Nvidia HBAO+ context */ GFSDK_SSAO_Context_D3D11* AOContext; diff --git a/D3D11Engine/D3D11OcclusionQuerry.cpp b/D3D11Engine/D3D11OcclusionQuerry.cpp index bc1d35af..a084b3d7 100644 --- a/D3D11Engine/D3D11OcclusionQuerry.cpp +++ b/D3D11Engine/D3D11OcclusionQuerry.cpp @@ -124,7 +124,7 @@ void D3D11OcclusionQuerry::DoOcclusionForBSP( BspInfo* root ) { MeshInfo* mi = root->OcclusionInfo.NodeMesh; g->GetContext()->Begin( p ); - g->DrawVertexBufferIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size() ); + g->DrawVertexBufferIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size()); g->GetContext()->End( p ); root->OcclusionInfo.QueryInProgress = true; diff --git a/D3D11Engine/D3D11PFX_DistanceBlur.cpp b/D3D11Engine/D3D11PFX_DistanceBlur.cpp index 979c82d9..beaf6cbb 100644 --- a/D3D11Engine/D3D11PFX_DistanceBlur.cpp +++ b/D3D11Engine/D3D11PFX_DistanceBlur.cpp @@ -16,7 +16,7 @@ D3D11PFX_DistanceBlur::D3D11PFX_DistanceBlur( D3D11PfxRenderer* rnd ) : D3D11PFX D3D11PFX_DistanceBlur::~D3D11PFX_DistanceBlur() {} /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_DistanceBlur::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_DistanceBlur::Render( ID3D11ShaderResourceView* diffuse ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); // Save old rendertargets @@ -34,7 +34,7 @@ XRESULT D3D11PFX_DistanceBlur::Render( RenderToTextureBuffer* fxbuffer ) { auto tempBuffer = FxRenderer->GetTempBuffer(); engine->GetContext()->ClearRenderTargetView( tempBuffer->GetRenderTargetView().Get(), reinterpret_cast(&float4( 0, 0, 0, 0 )) ); - FxRenderer->CopyTextureToRTV( engine->GetGBuffer0().GetShaderResView(), tempBuffer->GetRenderTargetView(), Engine::GraphicsEngine->GetResolution() ); + FxRenderer->CopyTextureToRTV( diffuse, tempBuffer->GetRenderTargetView(), Engine::GraphicsEngine->GetResolution() ); engine->GetContext()->OMSetRenderTargets( 1, oldRTV.GetAddressOf(), nullptr ); diff --git a/D3D11Engine/D3D11PFX_DistanceBlur.h b/D3D11Engine/D3D11PFX_DistanceBlur.h index 1fb74b66..b7141509 100644 --- a/D3D11Engine/D3D11PFX_DistanceBlur.h +++ b/D3D11Engine/D3D11PFX_DistanceBlur.h @@ -8,6 +8,7 @@ class D3D11PFX_DistanceBlur : ~D3D11PFX_DistanceBlur(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; } + XRESULT Render( ID3D11ShaderResourceView* diffuse ); }; diff --git a/D3D11Engine/D3D11PFX_GodRays.cpp b/D3D11Engine/D3D11PFX_GodRays.cpp index 8877cc28..8ca52bc6 100644 --- a/D3D11Engine/D3D11PFX_GodRays.cpp +++ b/D3D11Engine/D3D11PFX_GodRays.cpp @@ -17,7 +17,9 @@ D3D11PFX_GodRays::D3D11PFX_GodRays( D3D11PfxRenderer* rnd ) : D3D11PFX_Effect( r D3D11PFX_GodRays::~D3D11PFX_GodRays() {} /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_GodRays::Render( + ID3D11ShaderResourceView* backbuffer, + ID3D11ShaderResourceView* normals ) { if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) return XR_SUCCESS; // Don't render the godrays in the night-time @@ -78,8 +80,11 @@ XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { // Draw downscaled mask engine->GetContext()->OMSetRenderTargets( 1, tempBuffer->GetRenderTargetView().GetAddressOf(), nullptr ); - engine->GetHDRBackBuffer().BindToPixelShader( engine->GetContext().Get(), 0 ); - engine->GetGBuffer1().BindToPixelShader( engine->GetContext().Get(), 1 ); + ID3D11ShaderResourceView* srvs[2] { + backbuffer, + normals, + }; + engine->GetContext()->PSSetShaderResources( 0, 2, srvs ); D3D11_VIEWPORT vp = {}; vp.TopLeftX = 0.0f; @@ -112,6 +117,12 @@ XRESULT D3D11PFX_GodRays::Render( RenderToTextureBuffer* fxbuffer ) { engine->GetContext()->RSSetViewports( 1, &vp ); + ID3D11ShaderResourceView* nullSRVs[2] { + nullptr, + nullptr, + }; + engine->GetContext()->PSSetShaderResources( 0, 2, nullSRVs ); + engine->GetContext()->OMSetRenderTargets( 1, oldRTV.GetAddressOf(), oldDSV.Get() ); return XR_SUCCESS; diff --git a/D3D11Engine/D3D11PFX_GodRays.h b/D3D11Engine/D3D11PFX_GodRays.h index 3f7f987e..f5f97fe1 100644 --- a/D3D11Engine/D3D11PFX_GodRays.h +++ b/D3D11Engine/D3D11PFX_GodRays.h @@ -8,6 +8,7 @@ class D3D11PFX_GodRays : ~D3D11PFX_GodRays(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; } + XRESULT Render( ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals ); }; diff --git a/D3D11Engine/D3D11PFX_HDR.cpp b/D3D11Engine/D3D11PFX_HDR.cpp index d6b559eb..7794bf1c 100644 --- a/D3D11Engine/D3D11PFX_HDR.cpp +++ b/D3D11Engine/D3D11PFX_HDR.cpp @@ -37,7 +37,7 @@ D3D11PFX_HDR::~D3D11PFX_HDR() { } /** Draws this effect to the given buffer */ -XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { +XRESULT D3D11PFX_HDR::Render( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ) { D3D11GraphicsEngine* engine = reinterpret_cast(Engine::GraphicsEngine); engine->SetDefaultStates(); Engine::GAPI->GetRendererState().BlendState.BlendEnabled = false; @@ -56,7 +56,7 @@ XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { auto tempBuffer = FxRenderer->GetTempBuffer(); // Copy the original image to our temp-buffer - FxRenderer->CopyTextureToRTV( engine->GetHDRBackBuffer().GetShaderResView(), tempBuffer->GetRenderTargetView(), engine->GetResolution() ); + FxRenderer->CopyTextureToRTV( backbuffer, tempBuffer->GetRenderTargetView(), engine->GetResolution() ); // Bind scene and luminance tempBuffer->BindToPixelShader( engine->GetContext().Get(), 0 ); @@ -77,7 +77,7 @@ XRESULT D3D11PFX_HDR::Render( RenderToTextureBuffer* fxbuffer ) { hps->GetConstantBuffer()[0]->UpdateBuffer( &hcb ); hps->GetConstantBuffer()[0]->BindToPixelShader( 0 ); - FxRenderer->CopyTextureToRTV( tempBuffer->GetShaderResView(), oldRTV, engine->GetResolution(), true ); + FxRenderer->CopyTextureToRTV( tempBuffer->GetShaderResView(), output, engine->GetResolution(), true ); // Show lumBuffer //FxRenderer->CopyTextureToRTV(currentLum->GetShaderResView(), oldRTV, INT2(LUM_SIZE,LUM_SIZE), false); diff --git a/D3D11Engine/D3D11PFX_HDR.h b/D3D11Engine/D3D11PFX_HDR.h index 70583f8a..e69b690f 100644 --- a/D3D11Engine/D3D11PFX_HDR.h +++ b/D3D11Engine/D3D11PFX_HDR.h @@ -9,7 +9,8 @@ class D3D11PFX_HDR : ~D3D11PFX_HDR(); /** Draws this effect to the given buffer */ - XRESULT Render( RenderToTextureBuffer* fxbuffer ); + XRESULT Render( RenderToTextureBuffer* fxbuffer ) override { return XR_FAILED; }; + XRESULT Render( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ); protected: /** Calcualtes the luminance */ diff --git a/D3D11Engine/D3D11PFX_SMAA.cpp b/D3D11Engine/D3D11PFX_SMAA.cpp index 394e2681..bf72b05f 100644 --- a/D3D11Engine/D3D11PFX_SMAA.cpp +++ b/D3D11Engine/D3D11PFX_SMAA.cpp @@ -5,12 +5,7 @@ #include "RenderToTextureBuffer.h" #include "D3D11GraphicsEngine.h" #include "D3D11PfxRenderer.h" -#include "D3D11ShaderManager.h" -#include "D3D11VShader.h" #include "GothicAPI.h" -#include "D3D11PShader.h" -#include -#include D3D11PFX_SMAA::D3D11PFX_SMAA( D3D11PfxRenderer* rnd ) : D3D11PFX_Effect( rnd ) { Init(); @@ -42,22 +37,22 @@ void D3D11PFX_SMAA::RenderPostFX( const Microsoft::WRL::ComPtr OldRTV; Microsoft::WRL::ComPtr OldDSV; - engine->GetContext()->OMGetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.GetAddressOf() ); + pContext->OMGetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.GetAddressOf() ); auto TempRTV = FxRenderer->GetTempBuffer(); // update the temp buffer with the latest backbuffer data FxRenderer->CopyTextureToRTV( renderTargetSRV, TempRTV->GetRenderTargetView(), engine->GetResolution() ); - m_Native->Render( renderTargetSRV.Get(), TempRTV->GetRenderTargetView().Get() ); + m_Native->Render( renderTargetSRV.Get(), TempRTV->GetRenderTargetView().Get(), FxRenderer->GetTexturePool() ); // Copy result back to acutal RTV FxRenderer->CopyTextureToRTV( TempRTV->GetShaderResView(), OldRTV ); - engine->GetContext()->OMSetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.Get() ); + pContext->OMSetRenderTargets( 1, OldRTV.GetAddressOf(), OldDSV.Get() ); ID3D11ShaderResourceView* const NoSRV[1] = { nullptr }; - engine->GetContext()->PSSetShaderResources( 0, 1, NoSRV ); + pContext->PSSetShaderResources( 0, 1, NoSRV ); engine->SetDefaultStates( true ); } diff --git a/D3D11Engine/D3D11PFX_SMAA.h b/D3D11Engine/D3D11PFX_SMAA.h index 2611f360..cd085035 100644 --- a/D3D11Engine/D3D11PFX_SMAA.h +++ b/D3D11Engine/D3D11PFX_SMAA.h @@ -6,8 +6,6 @@ #include "pch.h" #include "d3d11pfx_effect.h" -#include "D3D11PShader.h" -#include "D3D11VShader.h" #include "SMAA/D3D11SMAA.h" diff --git a/D3D11Engine/D3D11PfxRenderer.cpp b/D3D11Engine/D3D11PfxRenderer.cpp index 12eb8679..790ab7f6 100644 --- a/D3D11Engine/D3D11PfxRenderer.cpp +++ b/D3D11Engine/D3D11PfxRenderer.cpp @@ -47,8 +47,8 @@ D3D11PfxRenderer::~D3D11PfxRenderer() { } /** Renders the distance blur effect */ -XRESULT D3D11PfxRenderer::RenderDistanceBlur() { - FX_DistanceBlur->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderDistanceBlur(ID3D11ShaderResourceView* diffuse ) { + FX_DistanceBlur->Render( diffuse ); return XR_SUCCESS; } @@ -64,18 +64,18 @@ XRESULT D3D11PfxRenderer::RenderHeightfog() { } /** Renders the godrays-Effect */ -XRESULT D3D11PfxRenderer::RenderGodRays() { - return FX_GodRays->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderGodRays(ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals) { + return FX_GodRays->Render( backbuffer , normals ); } /** Renders the HDR-Effect */ -XRESULT D3D11PfxRenderer::RenderHDR() { - return FX_HDR->Render( nullptr ); +XRESULT D3D11PfxRenderer::RenderHDR( ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer ) { + return FX_HDR->Render( output, backbuffer ); } /** Renders the SMAA-Effect */ -XRESULT D3D11PfxRenderer::RenderSMAA() { - FX_SMAA->RenderPostFX( reinterpret_cast(Engine::GraphicsEngine)->GetHDRBackBuffer().GetShaderResView() ); +XRESULT D3D11PfxRenderer::RenderSMAA(ID3D11ShaderResourceView* backbuffer) { + FX_SMAA->RenderPostFX( backbuffer ); return XR_SUCCESS; } @@ -222,8 +222,11 @@ XRESULT D3D11PfxRenderer::OnResize( const INT2& newResolution ) { } /** Draws the HBAO-Effect to the given buffer */ -XRESULT D3D11PfxRenderer::DrawHBAO( const Microsoft::WRL::ComPtr& rtv ) { - return NvHBAO->Render( rtv.Get() ); +XRESULT D3D11PfxRenderer::DrawHBAO( + const Microsoft::WRL::ComPtr& rtv, + const Microsoft::WRL::ComPtr& pFullResDepthTexSRV, + const Microsoft::WRL::ComPtr& pFullResNormalTexSRV) { + return NvHBAO->Render( rtv.Get(), pFullResDepthTexSRV, pFullResNormalTexSRV); } TextureHandle D3D11PfxRenderer::GetTempBuffer() diff --git a/D3D11Engine/D3D11PfxRenderer.h b/D3D11Engine/D3D11PfxRenderer.h index 7a3e2cc7..ef51a1f3 100644 --- a/D3D11Engine/D3D11PfxRenderer.h +++ b/D3D11Engine/D3D11PfxRenderer.h @@ -30,20 +30,20 @@ class D3D11PfxRenderer { XRESULT RenderHeightfog(); /** Renders the distance blur effect */ - XRESULT RenderDistanceBlur(); + XRESULT RenderDistanceBlur(ID3D11ShaderResourceView* diffuse ); /** Renders the HDR-Effect */ - XRESULT RenderHDR(); + XRESULT RenderHDR(ID3D11RenderTargetView* output, ID3D11ShaderResourceView* backbuffer); /** Renders the SMAA-Effect */ - XRESULT RenderSMAA(); + XRESULT RenderSMAA(ID3D11ShaderResourceView* backbuffer); XRESULT RenderTAA(const ComPtr& velocityBuffer); XRESULT RenderCAS( const Microsoft::WRL::ComPtr& input, INT2 inputSize, const Microsoft::WRL::ComPtr& output, INT2 outputSize, RenderToTextureBuffer& intermediateBuffer ); XRESULT RenderSimpleSharpen( const Microsoft::WRL::ComPtr& input, INT2 inputSize, const Microsoft::WRL::ComPtr& output, INT2 outputSize, RenderToTextureBuffer& intermediateBuffer ); /** Renders the godrays-Effect */ - XRESULT RenderGodRays(); + XRESULT RenderGodRays(ID3D11ShaderResourceView* backbuffer, ID3D11ShaderResourceView* normals); /** Copies the given texture to the given RTV */ XRESULT CopyTextureToRTV( const Microsoft::WRL::ComPtr& texture, const Microsoft::WRL::ComPtr& rtv, INT2 targetResolution = INT2( 0, 0 ), bool useCustomPS = false, INT2 offset = INT2( 0, 0 ) ); @@ -55,7 +55,8 @@ class D3D11PfxRenderer { XRESULT DrawFullScreenQuad(); /** Draws the HBAO-Effect to the given buffer */ - XRESULT DrawHBAO( const Microsoft::WRL::ComPtr& rtv ); + XRESULT DrawHBAO(const ComPtr& rtv, const ComPtr& pFullResDepthTexSRV, const ComPtr< + ID3D11ShaderResourceView>& pFullResNormalTexSRV); /** Accessors */ TextureHandle GetTempBuffer(); @@ -69,6 +70,8 @@ class D3D11PfxRenderer { void OnEndFrame() { m_texturePool->GiveTick(); } + + TexturePool* GetTexturePool() { return m_texturePool.get(); } private: /** Blur effect referenced here because it's often needed by PFX */ std::unique_ptr FX_Blur; diff --git a/D3D11Engine/D3D11ShaderManager.cpp b/D3D11Engine/D3D11ShaderManager.cpp index 4149f409..77c0476a 100644 --- a/D3D11Engine/D3D11ShaderManager.cpp +++ b/D3D11Engine/D3D11ShaderManager.cpp @@ -115,8 +115,6 @@ namespace }; } -const int NUM_MAX_BONES = 96; - extern bool FeatureLevel10Compatibility; extern bool FeatureRTArrayIndexFromAnyShader; @@ -132,6 +130,17 @@ D3D11ShaderManager::~D3D11ShaderManager() { // Find and compile the specified shader //-------------------------------------------------------------------------------------- HRESULT D3D11ShaderManager::CompileShaderFromFile( const CHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ) { + auto shaderFile = Toolbox::ToWideChar( szFileName ); + + return CompileShaderFromFile( + shaderFile.c_str(), + szEntryPoint, + szShaderModel, + ppBlobOut, + makros); +} + +HRESULT D3D11ShaderManager::CompileShaderFromFile( const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ) { HRESULT hr = S_OK; DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; @@ -155,10 +164,8 @@ HRESULT D3D11ShaderManager::CompileShaderFromFile( const CHAR* szFileName, LPCST m.insert( m.begin(), makros.begin(), makros.end() ); Microsoft::WRL::ComPtr pErrorBlob; - - auto shaderFile = Toolbox::ToWideChar( szFileName ); - std::filesystem::path shaderPath( shaderFile ); + std::filesystem::path shaderPath( szFileName ); // absolute path shaderPath = Engine::GAPI->GetStartDirectory().c_str() / shaderPath; @@ -198,6 +205,12 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceNode ) ); + Shaders.push_back( ShaderInfo( "VS_ExNodeInstanced", "VS_ExNodeInstanced.hlsl", "v", 1 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + + Shaders.push_back( ShaderInfo( "VS_ExNodeInstancedCube", "VS_ExNodeInstancedCube.hlsl", "v", 1 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + Shaders.push_back( ShaderInfo( "VS_Decal", "VS_Decal.hlsl", "v", 1 ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstance ) ); @@ -225,6 +238,12 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); // PrevBoneTransforms for motion vectors + Shaders.push_back( ShaderInfo( "VS_ExSkeletalInstanced", "VS_ExSkeletalInstanced.hlsl", "v", 3 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + + Shaders.push_back( ShaderInfo( "VS_ExSkeletalInstancedCube", "VS_ExSkeletalInstancedCube.hlsl", "v", 3 ) ); + Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); + Shaders.push_back( ShaderInfo( "VS_ExSkeletalVN", "VS_ExSkeletalVN.hlsl", "v", 3 ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerFrame ) ); Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceSkeletal ) ); @@ -534,7 +553,9 @@ XRESULT D3D11ShaderManager::Init() { Shaders.back().cBufferSizes.push_back( sizeof( VS_ExConstantBuffer_PerInstanceSkeletal ) ); Shaders.back().cBufferSizes.push_back( NUM_MAX_BONES * sizeof( XMFLOAT4X4 ) ); Shaders.back().cBufferSizes.push_back( sizeof( CubemapGSConstantBuffer ) ); // cbPerCubeRender for layered rendering - } else { + } + /*else: always compile fallback shaders*/ + { Shaders.push_back( ShaderInfo( "GS_Cubemap", "GS_Cubemap.hlsl", "g" ) ); Shaders.back().cBufferSizes.push_back( sizeof( CubemapGSConstantBuffer ) ); diff --git a/D3D11Engine/D3D11ShaderManager.h b/D3D11Engine/D3D11ShaderManager.h index 062711e3..b6148266 100644 --- a/D3D11Engine/D3D11ShaderManager.h +++ b/D3D11Engine/D3D11ShaderManager.h @@ -48,6 +48,7 @@ class D3D11ShaderManager { /** Compiles the shader from file and outputs error messages if needed */ static HRESULT CompileShaderFromFile( const CHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ); + static HRESULT CompileShaderFromFile( const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, const std::vector& makros ); /** Creates list with ShaderInfos */ XRESULT Init(); diff --git a/D3D11Engine/D3D11ShadowMap.cpp b/D3D11Engine/D3D11ShadowMap.cpp index 11642132..3ccfbf48 100644 --- a/D3D11Engine/D3D11ShadowMap.cpp +++ b/D3D11Engine/D3D11ShadowMap.cpp @@ -20,7 +20,6 @@ using namespace DirectX; extern bool FeatureLevel10Compatibility; -extern bool FeatureRTArrayIndexFromAnyShader; const float NUM_FRAME_SHADOW_UPDATES = 2; // Fraction of lights to update per frame const int NUM_MIN_FRAME_SHADOW_UPDATES = 4; // Minimum lights to update per frame @@ -499,10 +498,14 @@ std::vector D3D11ShadowMap::ComputeCascadeSplits( float nearPlane, float return splits; } -XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { - auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); - auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawLighting" ); +XRESULT D3D11ShadowMap::DrawPointlightShadows( std::vector& lights ) { auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + if (settings.EnablePointlightShadows <= 0) { + return XR_SUCCESS; + } + + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawPointlightShadows" ); static const XMVECTORF32 xmFltMax = { { { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX } } }; graphicsEngine->SetDefaultStates(); @@ -521,97 +524,102 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { bool partialShadowUpdate = settings.PartialDynamicShadowUpdates; // Draw pointlight shadows - if ( settings.EnablePointlightShadows > 0 ) { - std::list importantUpdates; - auto _ = graphicsEngine->RecordGraphicsEvent( L"Pointlight Shadows" ); - - for ( auto const& light : lights ) { - // Create shadowmap in case we should have one but haven't got it yet - if ( !light->LightShadowBuffers && light->UpdateShadows ) { - graphicsEngine->CreateShadowedPointLight( &light->LightShadowBuffers, light ); - } + std::list importantUpdates; - if ( light->LightShadowBuffers ) { - // Check if this lights even needs an update - bool needsUpdate = static_cast(light->LightShadowBuffers)->NeedsUpdate(); - bool isInited = static_cast(light->LightShadowBuffers)->IsInited(); - - // Add to the updatequeue if it does - if ( isInited && (needsUpdate || light->UpdateShadows) ) { - // Always update the light if the light itself moved - if ( partialShadowUpdate && !needsUpdate ) { - // Only add once. This list should never be very big, so it should - // be ok to search it like this This needs to be done to make sure a - // light will get updated only once and won't block the queue - if ( std::find( graphicsEngine->FrameShadowUpdateLights.begin(), - graphicsEngine->FrameShadowUpdateLights.end(), - light ) == graphicsEngine->FrameShadowUpdateLights.end() ) { - // Always render the closest light to the playervob, so the player - // doesn't flicker when moving - float d; - XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); - - float range = light->Vob->GetLightRange(); - if ( d < range * range && - importantUpdates.size() < MAX_IMPORTANT_LIGHT_UPDATES ) { - importantUpdates.emplace_back( light ); - } else { - graphicsEngine->FrameShadowUpdateLights.emplace_back( light ); - } - } - } else { + for ( auto const& light : lights ) { + // Create shadowmap in case we should have one but haven't got it yet + if ( !light->LightShadowBuffers && light->UpdateShadows ) { + graphicsEngine->CreateShadowedPointLight( &light->LightShadowBuffers, light ); + } + + if ( light->LightShadowBuffers ) { + // Check if this lights even needs an update + bool needsUpdate = static_cast(light->LightShadowBuffers)->NeedsUpdate(); + bool isInited = static_cast(light->LightShadowBuffers)->IsInited(); + + // Add to the updatequeue if it does + if ( isInited && (needsUpdate || light->UpdateShadows) ) { + // Always update the light if the light itself moved + if ( partialShadowUpdate && !needsUpdate ) { + // Only add once. This list should never be very big, so it should + // be ok to search it like this This needs to be done to make sure a + // light will get updated only once and won't block the queue + if ( std::find( graphicsEngine->FrameShadowUpdateLights.begin(), + graphicsEngine->FrameShadowUpdateLights.end(), + light ) == graphicsEngine->FrameShadowUpdateLights.end() ) { // Always render the closest light to the playervob, so the player // doesn't flicker when moving float d; XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); - float range = light->Vob->GetLightRange() * 1.5f; - - // If the engine said this light should be updated, then do so. If - // the light said this - if ( needsUpdate || d < range * range ) + float range = light->Vob->GetLightRange(); + if ( d < range * range && + importantUpdates.size() < MAX_IMPORTANT_LIGHT_UPDATES ) { importantUpdates.emplace_back( light ); + } else { + graphicsEngine->FrameShadowUpdateLights.emplace_back( light ); + } } + } else { + // Always render the closest light to the playervob, so the player + // doesn't flicker when moving + float d; + XMStoreFloat( &d, XMVector3LengthSq( light->Vob->GetPositionWorldXM() - vPlayerPosition ) ); + + float range = light->Vob->GetLightRange() * 1.5f; + + // If the engine said this light should be updated, then do so. If + // the light said this + if ( needsUpdate || d < range * range ) + importantUpdates.emplace_back( light ); } } } + } - // Render the closest light - for ( auto const& importantUpdate : importantUpdates ) { - static_cast( importantUpdate->LightShadowBuffers )->RenderCubemap( importantUpdate->UpdateShadows ); - importantUpdate->UpdateShadows = false; - } + // Render the closest light + for ( auto const& importantUpdate : importantUpdates ) { + static_cast( importantUpdate->LightShadowBuffers )->RenderCubemap( importantUpdate->UpdateShadows ); + importantUpdate->UpdateShadows = false; + } - // Update only a fraction of lights, but at least some - int n = std::max( - (UINT)NUM_MIN_FRAME_SHADOW_UPDATES, - (UINT)(graphicsEngine->FrameShadowUpdateLights.size() / NUM_FRAME_SHADOW_UPDATES) ); - while ( !graphicsEngine->FrameShadowUpdateLights.empty() ) { - auto light = graphicsEngine->FrameShadowUpdateLights.front(); - if ( !light ) { - graphicsEngine->FrameShadowUpdateLights.pop_front(); - continue; - } - D3D11PointLight* l = static_cast(light->LightShadowBuffers); - if ( !l ) { - graphicsEngine->FrameShadowUpdateLights.pop_front(); - continue; - } - // Check if we have to force this light to update itself (NPCs moving around, for example) - bool force = light->UpdateShadows; - light->UpdateShadows = false; + // Update only a fraction of lights, but at least some + int n = std::max( + (UINT)NUM_MIN_FRAME_SHADOW_UPDATES, + (UINT)(graphicsEngine->FrameShadowUpdateLights.size() / NUM_FRAME_SHADOW_UPDATES) ); + while ( !graphicsEngine->FrameShadowUpdateLights.empty() ) { + auto light = graphicsEngine->FrameShadowUpdateLights.front(); + if ( !light ) { + graphicsEngine->FrameShadowUpdateLights.pop_front(); + continue; + } + D3D11PointLight* l = static_cast(light->LightShadowBuffers); + if ( !l ) { + graphicsEngine->FrameShadowUpdateLights.pop_front(); + continue; + } + // Check if we have to force this light to update itself (NPCs moving around, for example) + bool force = light->UpdateShadows; + light->UpdateShadows = false; - l->RenderCubemap( force ); - graphicsEngine->DebugPointlight = l; + l->RenderCubemap( force ); + graphicsEngine->DebugPointlight = l; - graphicsEngine->FrameShadowUpdateLights.pop_front(); + graphicsEngine->FrameShadowUpdateLights.pop_front(); - // Only update n lights - n--; - if ( n <= 0 ) break; - } + // Only update n lights + n--; + if ( n <= 0 ) break; } + return XR_SUCCESS; +} +XRESULT D3D11ShadowMap::DrawWorldShadow( ) +{ + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawWorldShadow" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + int numCascades = settings.NumShadowCascades; bool isOutdoor = Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == zBSP_MODE_OUTDOOR; @@ -644,33 +652,44 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { } } - graphicsEngine->SetDefaultStates(); - // Restore gothics camera Engine::GAPI->SetCameraReplacementPtr( nullptr ); + + return XR_SUCCESS; +} +XRESULT D3D11ShadowMap::DrawRainShadowmap() { // Draw rainmap, if raining if ( Engine::GAPI->GetSceneWetness() > 0.00001f ) { - auto _ = graphicsEngine->RecordGraphicsEvent( L"Rain Shadowmap" ); + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawRainShadowmap" ); + graphicsEngine->Effects->DrawRainShadowmap(); } + return XR_SUCCESS; +} + +XRESULT D3D11ShadowMap::DrawPointlightLights( + std::vector& lights, + RenderToTextureBuffer& color, + RenderToTextureBuffer& normals, + RenderToTextureBuffer& specular, + RenderToTextureBuffer& depthCopy + ) { + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawPointlightLights" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; XMMATRIX view = Engine::GAPI->GetViewMatrixXM(); Engine::GAPI->SetViewTransformXM( view ); + view = XMMatrixTranspose( view ); - // ******************************** - // Draw direct lighting - // ******************************** graphicsEngine->SetActiveVertexShader( "VS_ExPointLight" ); graphicsEngine->SetActivePixelShader( "PS_DS_PointLight" ); auto psPointLight = graphicsEngine->GetShaderManager().GetPShader( "PS_DS_PointLight" ); auto psPointLightDynShadow = graphicsEngine->GetShaderManager().GetPShader( "PS_DS_PointLightDynShadow" ); - Engine::GAPI->SetFarPlane( - settings.SectionDrawRadius * - WORLD_SECTION_SIZE ); - Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); if ( settings.LimitLightIntesity ) { Engine::GAPI->GetRendererState().BlendState.BlendOp = GothicBlendStateInfo::BO_BLEND_OP_MAX; @@ -691,9 +710,7 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { // Set the main rendertarget m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); - - view = XMMatrixTranspose( view ); - + DS_PointLightConstantBuffer plcb = {}; XMStoreFloat4x4( &plcb.PL_InvProj, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetProjectionMatrix() ) ) ); @@ -702,10 +719,10 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { auto resolution = graphicsEngine->GetResolution(); plcb.PL_ViewportSize = float2( static_cast(resolution.x), static_cast(resolution.y) ); - graphicsEngine->GetGBuffer0().BindToPixelShader( m_context.Get(), 0 ); - graphicsEngine->GetGBuffer1().BindToPixelShader( m_context.Get(), 1 ); - graphicsEngine->GetGBuffer2().BindToPixelShader( m_context.Get(), 7 ); - graphicsEngine->GetDepthBufferCopy()->BindToPixelShader( m_context.Get(), 2 ); + color.BindToPixelShader( m_context.Get(), 0 ); + normals.BindToPixelShader( m_context.Get(), 1 ); + specular.BindToPixelShader( m_context.Get(), 7 ); + depthCopy.BindToPixelShader( m_context.Get(), 2 ); // Draw all lights for ( auto const& light : lights ) { @@ -811,7 +828,141 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { Engine::GAPI->GetRendererState().RendererInfo.FrameDrawnLights++; } + + return XR_SUCCESS; +} + +XRESULT D3D11ShadowMap::DrawLighting( + std::vector& lights, + RenderToTextureBuffer& color, + RenderToTextureBuffer& normals, + RenderToTextureBuffer& specular, + RenderToTextureBuffer& depthCopy) { + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + + graphicsEngine->SetDefaultStates(); + + // Draw pointlight shadows + DrawPointlightShadows(lights); + + DrawWorldShadow(); + + graphicsEngine->SetDefaultStates(); + + DrawRainShadowmap(); + + Engine::GAPI->SetFarPlane(static_cast(settings.SectionDrawRadius) * WORLD_SECTION_SIZE ); + + DrawPointlightLights(lights, color, normals, specular, depthCopy); + + DrawWorldLights(); + + m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), + graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); + + return XR_SUCCESS; +} + + + +/** Renders the shadowmaps for the sun */ +void D3D11ShadowMap::RenderShadowmaps( const RenderShadowmapsParams& params ) { + + // We now assume that "target" always is something else than the world shadowmap + UINT targetSize = !params.Target + ? m_cascadedShadowMap->GetSize() + : params.Target->GetSizeX(); + + Microsoft::WRL::ComPtr dsvOverwrite = params.DSVOverwrite; + if ( params.Target && !dsvOverwrite.Get() ) dsvOverwrite = params.Target->GetDepthStencilView().Get(); + const bool isNotWorldShadowMap = params.Target != nullptr; + + // todo: remove this dependency at some point + auto graphicsEngine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; + auto _ = graphicsEngine->RecordGraphicsEvent( L"RenderShadowmaps" ); + + D3D11_VIEWPORT oldVP; + UINT n = 1; + m_context->RSGetViewports( &n, &oldVP ); + + // Apply new viewport + D3D11_VIEWPORT vp; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.Width = static_cast(targetSize); + vp.Height = vp.Width; + m_context->RSSetViewports( 1, &vp ); + + // Set the rendering stage + D3D11ENGINE_RENDER_STAGE oldStage = graphicsEngine->GetRenderingStage(); + graphicsEngine->SetRenderingStage( DES_SHADOWMAP ); + + // Clear and Bind the shadowmap + + Microsoft::WRL::ComPtr srv; + m_context->PSSetShaderResources( 3, 1, srv.GetAddressOf() ); + + if ( !params.DebugRTV.Get() ) { + m_context->OMSetRenderTargets( 0, nullptr, dsvOverwrite.Get() ); + Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = false; + } else { + m_context->OMSetRenderTargets( 1, params.DebugRTV.GetAddressOf(), dsvOverwrite.Get() ); + Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; + } + Engine::GAPI->GetRendererState().BlendState.SetDirty(); + + // Dont render shadows from the sun when it isn't on the sky + if ( isNotWorldShadowMap || + (Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y > + 0 && // Only stop rendering if the sun is down on main-shadowmap + // TODO: Take this out of here! + Engine::GAPI->GetRendererState().RendererSettings.DrawShadowGeometry && + Engine::GAPI->GetRendererState().RendererSettings.EnableShadows) ) { + m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0 ); + + // Draw the world mesh without textures + XMVECTOR cameraPosition = XMLoadFloat3( ¶ms.CameraPosition ); + int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); + static const char* timer_labels_cascades[MAX_CSM_CASCADES] + { + "Cascade 0", + "Cascade 1", + "Cascade 2", + "Cascade 3", + }; + auto _1 = START_TIMING(timer_labels_cascades[timerLabelIndex]); + graphicsEngine->DrawWorldAroundForWorldShadow( cameraPosition, 2, params ); + + } else { + if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) { + m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 0.0f, + 0 ); // Always shadow in the night + } else { + m_context->ClearDepthStencilView( + dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, + 0 ); // Clear shadowmap when shadows not enabled + } + } + + // Restore state + graphicsEngine->SetRenderingStage( oldStage ); + m_context->RSSetViewports( 1, &oldVP ); + + Engine::GAPI->SetFarPlane( + Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * + WORLD_SECTION_SIZE ); +} + +XRESULT D3D11ShadowMap::DrawWorldLights() +{ + auto graphicsEngine = reinterpret_cast(Engine::GraphicsEngine); + auto _ = graphicsEngine->RecordGraphicsEvent( L"DrawWorldLights" ); + auto& settings = Engine::GAPI->GetRendererState().RendererSettings; + Engine::GAPI->GetRendererState().BlendState.BlendOp = GothicBlendStateInfo::BO_BLEND_OP_ADD; Engine::GAPI->GetRendererState().BlendState.SetDirty(); @@ -824,6 +975,8 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { // Modify light when raining float rain = Engine::GAPI->GetRainFXWeight(); float wetness = Engine::GAPI->GetSceneWetness(); + + XMMATRIX view = XMMatrixTranspose(Engine::GAPI->GetViewMatrixXM()); bool isSnow = oCGame::GetGame() && oCGame::GetGame()->_zCSession_world @@ -847,8 +1000,8 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { graphicsEngine->GetActivePS()->GetConstantBuffer()[1]->BindToPixelShader( 1 ); DS_ScreenQuadConstantBuffer scb = {}; - scb.SQ_InvProj = plcb.PL_InvProj; - scb.SQ_InvView = plcb.PL_InvView; + XMStoreFloat4x4( &scb.SQ_InvProj, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetProjectionMatrix() ) ) ); + XMStoreFloat4x4( &scb.SQ_InvView, XMMatrixInverse( nullptr, XMLoadFloat4x4( &Engine::GAPI->GetRendererState().TransformState.TransformView ) ) ); scb.SQ_View = Engine::GAPI->GetRendererState().TransformState.TransformView; XMStoreFloat3( scb.SQ_LightDirectionVS.toXMFLOAT3(), @@ -931,106 +1084,10 @@ XRESULT D3D11ShadowMap::DrawLighting( std::vector& lights ) { static ID3D11ShaderResourceView* nullSrv[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; m_context->PSSetShaderResources( 3, ARRAYSIZE( nullSrv ), nullSrv ); - m_context->OMSetRenderTargets( 1, graphicsEngine->GetHDRBackBuffer().GetRenderTargetView().GetAddressOf(), - graphicsEngine->GetDepthBuffer()->GetDepthStencilView().Get() ); - return XR_SUCCESS; } - -/** Renders the shadowmaps for the sun */ -void D3D11ShadowMap::RenderShadowmaps( const RenderShadowmapsParams& params ) { - - // We now assume that "target" always is something else than the world shadowmap - UINT targetSize = !params.Target - ? m_cascadedShadowMap->GetSize() - : params.Target->GetSizeX(); - - Microsoft::WRL::ComPtr dsvOverwrite = params.DSVOverwrite; - if ( params.Target && !dsvOverwrite.Get() ) dsvOverwrite = params.Target->GetDepthStencilView().Get(); - const bool isNotWorldShadowMap = params.Target != nullptr; - - // todo: remove this dependency at some point - auto graphicsEngine = (D3D11GraphicsEngine*)Engine::GraphicsEngine; - auto _ = graphicsEngine->RecordGraphicsEvent( L"RenderShadowmaps" ); - - D3D11_VIEWPORT oldVP; - UINT n = 1; - m_context->RSGetViewports( &n, &oldVP ); - - // Apply new viewport - D3D11_VIEWPORT vp; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.Width = static_cast(targetSize); - vp.Height = vp.Width; - m_context->RSSetViewports( 1, &vp ); - - // Set the rendering stage - D3D11ENGINE_RENDER_STAGE oldStage = graphicsEngine->GetRenderingStage(); - graphicsEngine->SetRenderingStage( DES_SHADOWMAP ); - - // Clear and Bind the shadowmap - - Microsoft::WRL::ComPtr srv; - m_context->PSSetShaderResources( 3, 1, srv.GetAddressOf() ); - - if ( !params.DebugRTV.Get() ) { - m_context->OMSetRenderTargets( 0, nullptr, dsvOverwrite.Get() ); - Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = false; - } else { - m_context->OMSetRenderTargets( 1, params.DebugRTV.GetAddressOf(), dsvOverwrite.Get() ); - Engine::GAPI->GetRendererState().BlendState.ColorWritesEnabled = true; - } - Engine::GAPI->GetRendererState().BlendState.SetDirty(); - - // Dont render shadows from the sun when it isn't on the sky - if ( isNotWorldShadowMap || - (Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y > - 0 && // Only stop rendering if the sun is down on main-shadowmap - // TODO: Take this out of here! - Engine::GAPI->GetRendererState().RendererSettings.DrawShadowGeometry && - Engine::GAPI->GetRendererState().RendererSettings.EnableShadows) ) { - m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0 ); - - // Draw the world mesh without textures - - XMVECTOR cameraPosition = XMLoadFloat3( ¶ms.CameraPosition ); - int timerLabelIndex = std::clamp(params.CascadeIndex, 0, MAX_CSM_CASCADES-1); - static const char* timer_labels_cascades[MAX_CSM_CASCADES] - { - "Cascade 0", - "Cascade 1", - "Cascade 2", - "Cascade 3", - }; - auto _1 = START_TIMING(timer_labels_cascades[timerLabelIndex]); - graphicsEngine->DrawWorldAroundForWorldShadow( cameraPosition, 2, params ); - - } else { - if ( Engine::GAPI->GetSky()->GetAtmoshpereSettings().LightDirection.y <= 0 ) { - m_context->ClearDepthStencilView( dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 0.0f, - 0 ); // Always shadow in the night - } else { - m_context->ClearDepthStencilView( - dsvOverwrite.Get(), D3D11_CLEAR_DEPTH, 1.0f, - 0 ); // Clear shadowmap when shadows not enabled - } - } - - // Restore state - graphicsEngine->SetRenderingStage( oldStage ); - m_context->RSSetViewports( 1, &oldVP ); - - Engine::GAPI->SetFarPlane( - Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * - WORLD_SECTION_SIZE ); -} - - /** Renders the shadowmaps for a pointlight */ void XM_CALLCONV D3D11ShadowMap::RenderShadowCube( FXMVECTOR position, float range, @@ -1058,7 +1115,7 @@ void XM_CALLCONV D3D11ShadowMap::RenderShadowCube( bool useLayeredPath = false; if ( !face.Get() ) { - if ( FeatureRTArrayIndexFromAnyShader ) { + if ( Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseLayeredRendering ) { useLayeredPath = true; face = targetCube.GetDepthStencilView().Get(); diff --git a/D3D11Engine/D3D11ShadowMap.h b/D3D11Engine/D3D11ShadowMap.h index 6230404a..8f4dd79b 100644 --- a/D3D11Engine/D3D11ShadowMap.h +++ b/D3D11Engine/D3D11ShadowMap.h @@ -107,11 +107,18 @@ class D3D11ShadowMap { // For cascade i: near = splits[i], far = splits[i+1] // lambda in [0,1] interpolates between logarithmic (1.0) and uniform (0.0) splits. static std::vector ComputeCascadeSplits( float nearPlane, float farPlane, size_t numCascades, float lambda = 0.95f, float bias = 1.0f ); + XRESULT DrawPointlightShadows(std::vector& lights); + XRESULT DrawWorldShadow(); + XRESULT DrawRainShadowmap(); + XRESULT DrawPointlightLights(std::vector& lights, RenderToTextureBuffer& color, RenderToTextureBuffer& normals, RenderToTextureBuffer + & specular, RenderToTextureBuffer& depthCopy); /** Renders the shadowmaps for the sun using parameter struct */ void RenderShadowmaps( const RenderShadowmapsParams& params ); - XRESULT DrawLighting( std::vector& lights ); + XRESULT DrawWorldLights(); + XRESULT DrawLighting(std::vector& lights, RenderToTextureBuffer& color, RenderToTextureBuffer& normals, RenderToTextureBuffer + & specular, RenderToTextureBuffer& depthCopy); void XM_CALLCONV RenderShadowCube( DirectX::FXMVECTOR position, float range, diff --git a/D3D11Engine/D3D11StructuredBuffer.h b/D3D11Engine/D3D11StructuredBuffer.h new file mode 100644 index 00000000..38edaf64 --- /dev/null +++ b/D3D11Engine/D3D11StructuredBuffer.h @@ -0,0 +1,123 @@ +#pragma once + +#include "pch.h" +#include + +// Templated structured buffer for GPU compute/shader access +template +class D3D11StructuredBuffer { +public: + D3D11StructuredBuffer() : ElementCount(0), MaxElementCount(0) {} + + ~D3D11StructuredBuffer() = default; + + // Initialize the buffer with a maximum capacity + HRESULT Init(ID3D11Device* device, UINT maxElements, bool cpuWrite = true, bool gpuWrite = false) { + MaxElementCount = maxElements; + ElementCount = 0; + + D3D11_BUFFER_DESC desc = {}; + desc.ByteWidth = sizeof(T) * maxElements; + desc.StructureByteStride = sizeof(T); + desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + + if (cpuWrite) { + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + } else if (gpuWrite) { + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; + } else { + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + } + + HRESULT hr = device->CreateBuffer(&desc, nullptr, Buffer.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Create SRV + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = maxElements; + + hr = device->CreateShaderResourceView(Buffer.Get(), &srvDesc, SRV.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Create UAV if GPU writable + if (gpuWrite) { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = DXGI_FORMAT_UNKNOWN; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = maxElements; + + hr = device->CreateUnorderedAccessView(Buffer.Get(), &uavDesc, UAV.GetAddressOf()); + if (FAILED(hr)) return hr; + } + + return S_OK; + } + + // Update buffer contents (for dynamic buffers) + HRESULT UpdateBuffer(ID3D11DeviceContext* context, const T* data, UINT count) { + if (count > MaxElementCount) { + LogError() << "StructuredBuffer overflow: " << count << " > " << MaxElementCount; + count = MaxElementCount; + } + + ElementCount = count; + + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = context->Map(Buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + if (FAILED(hr)) return hr; + + memcpy(mapped.pData, data, sizeof(T) * count); + context->Unmap(Buffer.Get(), 0); + + return S_OK; + } + + // Update buffer contents (for default buffers) + void UpdateBufferDefault(ID3D11DeviceContext* context, const T* data, UINT count) { + if (count > MaxElementCount) { + LogError() << "StructuredBuffer overflow: " << count << " > " << MaxElementCount; + count = MaxElementCount; + } + ElementCount = count; + context->UpdateSubresource(Buffer.Get(), 0, nullptr, data, 0, 0); + } + + // Bind to vertex shader + void BindToVertexShader(ID3D11DeviceContext* context, UINT slot) { + context->VSSetShaderResources(slot, 1, SRV.GetAddressOf()); + } + + // Bind to pixel shader + void BindToPixelShader(ID3D11DeviceContext* context, UINT slot) { + context->PSSetShaderResources(slot, 1, SRV.GetAddressOf()); + } + + // Unbind from vertex shader + void UnbindFromVertexShader(ID3D11DeviceContext* context, UINT slot) { + ID3D11ShaderResourceView* nullSRV = nullptr; + context->VSSetShaderResources(slot, 1, &nullSRV); + } + + UINT GetElementCount() const { return ElementCount; } + UINT GetMaxElementCount() const { return MaxElementCount; } + ID3D11Buffer* GetBuffer() const { return Buffer.Get(); } + ID3D11ShaderResourceView* GetSRV() const { return SRV.Get(); } + ID3D11UnorderedAccessView* GetUAV() const { return UAV.Get(); } + +private: + Microsoft::WRL::ComPtr Buffer; + Microsoft::WRL::ComPtr SRV; + Microsoft::WRL::ComPtr UAV; + UINT ElementCount; + UINT MaxElementCount; +}; diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index 80dab10d..ac7714d0 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -48,6 +48,7 @@ // TODO: REMOVE THIS! #include "D3D11GraphicsEngine.h" +#include "D3D11RenderQueue.h" #ifndef PUBLIC_RELEASE #define OPT_DBG_NOINLINE __declspec(noinline) @@ -1158,14 +1159,12 @@ void GothicAPI::DrawWorldMeshNaive() { { auto _ = START_TIMING( "World Mesh" ); auto _1 = Engine::GraphicsEngine->RecordGraphicsEvent( L"World Mesh" ); - /* - if ( FeatureLevel10Compatibility ) { - Engine::GraphicsEngine->DrawWorldMesh(); - } else { + + if ( Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.FeatureSet.UseMDI ) { Engine::GraphicsEngine->DrawWorldMesh_Indirect(); + } else { + Engine::GraphicsEngine->DrawWorldMesh(); } - */ - Engine::GraphicsEngine->DrawWorldMesh(); } @@ -1186,44 +1185,101 @@ void GothicAPI::DrawWorldMeshNaive() { auto drawRadius = RendererState.RendererSettings.SkeletalMeshDrawRadius; - for ( const auto& vobInfo : AnimatedSkeletalVobs ) { - // Don't render if sleeping and has skeletal meshes available - if ( !vobInfo->VisualInfo ) continue; +#if defined(BUILD_1_12F) + // not implemented in G1 sequel + RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced = false; +#endif + // TODO: Figure out why Batching is not working properly here + // The Batches constantly use wrong textures as if just before "DrawIndexed" the texture is being changed by something else + // this happens on the Body meshes as well as all the Node Attachments. + // We need this to be fixed, so that we can actually draw hundreds of animated characters with good performance. + if ( RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced ) { - float dist; - XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); - if ( dist > drawRadius ) - continue; // Skip out of range + static std::vector batchedVobs; + batchedVobs.clear(); - zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); + for ( const auto& vobInfo : AnimatedSkeletalVobs ) { + // Don't render if sleeping and has skeletal meshes available + if ( !vobInfo->VisualInfo ) continue; - //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); + float dist; + XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); + if ( dist > drawRadius ) + continue; // Skip out of range - int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip - if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) - continue; + zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); - // Indoor? - vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); + int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip + if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) + continue; - zCModel* model = static_cast(vobInfo->Vob->GetVisual()); - if ( !model ) - continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); - // This is important, because gothic only lerps between animation when this distance is set and below ~2000 - model->SetDistanceToCamera( dist ); + // Indoor? + vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); - // Schedule for drawing in later stage if this vob is ghost - if ( vobInfo->Vob->GetVisualAlpha() ) { - TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); - std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); - continue; + zCModel* model = static_cast(vobInfo->Vob->GetVisual()); + if ( !model ) + continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + + // This is important, because gothic only lerps between animation when this distance is set and below ~2000 + model->SetDistanceToCamera( dist ); + + // Schedule for drawing in later stage if this vob is ghost + if ( vobInfo->Vob->GetVisualAlpha() ) { + TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); + std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); + continue; + } + + batchedVobs.push_back( vobInfo ); + if ( RendererState.RendererSettings.ShowSkeletalVertexNormals ) + VNSkeletalVobs.emplace_back( vobInfo ); } - DrawSkeletalMeshVob( vobInfo, dist ); - if( RendererState.RendererSettings.ShowSkeletalVertexNormals ) - VNSkeletalVobs.emplace_back( vobInfo ); + DrawSkeletalMeshVobs_Batched( batchedVobs, true, true ); + } else { + + for ( const auto& vobInfo : AnimatedSkeletalVobs ) { + // Don't render if sleeping and has skeletal meshes available + if ( !vobInfo->VisualInfo ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vobInfo->Vob->GetPositionWorldXM() - cameraPosXm ) ); + if ( dist > drawRadius ) + continue; // Skip out of range + + int clipFlags = EGothicCullFlags::CullSidesNear; // No far clip + + zCCamera::GetCamera()->SetTransform( zCCamera::ETransformType::TT_WORLD, *vobInfo->Vob->GetWorldMatrixPtr() ); + if ( GetCameraBBox3DInFrustum( vobInfo->Vob, clipFlags, true ) == ZTCAM_CLIPTYPE_OUT ) + continue; + //Engine::GraphicsEngine->GetLineRenderer()->AddAABBMinMax(bb.Min, bb.Max, XMFLOAT4(1, 1, 1, 1)); + + // Indoor? + vobInfo->IndoorVob = vobInfo->Vob->IsIndoorVob(); + + zCModel* model = static_cast(vobInfo->Vob->GetVisual()); + if ( !model ) + continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache + + // This is important, because gothic only lerps between animation when this distance is set and below ~2000 + model->SetDistanceToCamera( dist ); + + // Schedule for drawing in later stage if this vob is ghost + if ( vobInfo->Vob->GetVisualAlpha() ) { + TransparencyVobs.emplace_back( dist, vobInfo->Vob->GetVobTransparency(), vobInfo, nullptr ); + std::push_heap( TransparencyVobs.begin(), TransparencyVobs.end(), CompareGhostDistance ); + continue; + } + + DrawSkeletalMeshVob( vobInfo, dist ); + if ( RendererState.RendererSettings.ShowSkeletalVertexNormals ) + VNSkeletalVobs.emplace_back( vobInfo ); + } } + + } // Draw vobs in view @@ -1234,8 +1290,138 @@ void GothicAPI::DrawWorldMeshNaive() { ResetWorldTransform(); } +// Add the new batched rendering method: +void GothicAPI::DrawSkeletalMeshVobs_Batched( + const std::vector& vobs, + bool updateState, + bool drawAttachments ) { + D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); + + Engine::GAPI->GetRendererState().TransformState.TransformView = zCCamera::GetCamera()->GetTransformDX( zCCamera::ETransformType::TT_VIEW ); + + // Build batches from animated skeletal vobs + g->BuildSkeletalMeshBatches( vobs ); + + // Draw all batches + g->DrawSkeletalMeshBatched(); + + if ( !drawAttachments ) { + g->ClearSkeletalMeshBatches(); + return; + } + + // Build and draw node attachment batches (moved to D3D11GraphicsEngine) + g->BuildNodeAttachmentBatches( vobs, updateState ); + g->DrawNodeAttachmentBatched(); + + // Handle MorphMeshes at close distance separately (per-vertex animation, can't batch) + XMVECTOR playerPosXm = oCGame::GetPlayer() ? oCGame::GetPlayer()->GetPositionWorldXM() : g_XMZero; + static std::vector transforms; + + for ( const auto& vi : vobs ) { + zCModel* model = static_cast(vi->Vob->GetVisual()); + if ( !model || !vi->VisualInfo ) continue; + if ( !vi->Vob->GetShowVisual() ) continue; + + float dist; + XMStoreFloat( &dist, XMVector3Length( vi->Vob->GetPositionWorldXM() - playerPosXm ) ); + + // Only process close MorphMeshes in main render pass + if ( dist >= 1000 || (g->GetRenderingStage() != DES_MAIN && g->GetRenderingStage() != DES_GHOST) ) { + continue; + } + + // Calculate model color + float4 modelColor; + if ( RendererState.RendererSettings.EnableShadows ) { + modelColor = float4( 1, 1, 1, 1 ); + } else { + if ( vi->Vob->IsIndoorVob() ) { + modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; + } else if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { + static const float inv255f = 1.0f / 255.0f; + float3 vobPos = vi->Vob->GetPositionWorld(); + float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); + modelColor = float4( polyLightStat.z * inv255f, polyLightStat.y * inv255f, polyLightStat.x * inv255f, 1.f ); + } else { + modelColor = float4( 1, 1, 1, 1 ); + } + } + + XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); + XMMATRIX world = vi->Vob->GetWorldMatrixXM() * scale; + float fatness = model->GetModelFatness(); + + transforms.clear(); + model->GetBoneTransforms( &transforms ); + + const std::vector& prevBoneTransforms = (vi->HasValidPrevTransforms && !vi->PrevBoneTransforms.empty()) + ? vi->PrevBoneTransforms + : transforms; + const XMMATRIX prevWorldMatrix = vi->HasValidPrevTransforms + ? XMLoadFloat4x4( &vi->PrevWorldMatrix ) + : world; + + std::map>& nodeAttachments = vi->NodeAttachments; + zCModel* mvis = static_cast(vi->Vob->GetVisual()); + + for ( unsigned int i = 0; i < transforms.size(); i++ ) { + zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; + if ( !node->NodeVisual ) continue; + if ( nodeAttachments.find( i ) == nodeAttachments.end() ) continue; + + XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); + XMMATRIX finalWorld = world * curTransform; + XMMATRIX prevTransform = XMLoadFloat4x4( &prevBoneTransforms[i] ); + auto prevWorldNode = prevWorldMatrix * prevTransform; + + for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { + if ( !mvi || !mvi->Visual ) continue; + + bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; + if ( !isMMS ) continue; + + zCMorphMesh* mm = reinterpret_cast(mvi->Visual); + + if ( updateState ) { + node->TexAniState.UpdateTexList(); + mm->GetTexAniState()->UpdateTexList(); + } + + SetWorldViewTransform( finalWorld, GetViewMatrixXM() ); + + g->SetActiveVertexShader( "VS_ExNode" ); + g->SetupVS_ExMeshDrawCall(); + g->SetupVS_ExConstantBuffer(); + + VS_ExConstantBuffer_PerInstanceNode instanceInfo; + instanceInfo.Color = modelColor; + instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); + instanceInfo.Scaling = fatness * 0.02f + 1.f; + XMStoreFloat4x4( &instanceInfo.World, finalWorld ); + XMStoreFloat4x4( &instanceInfo.PrevWorld, prevWorldNode ); + + g->GetActiveVS()->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); + g->GetActiveVS()->GetConstantBuffer()[1]->BindToVertexShader( 1 ); + + if ( updateState ) { + mm->AdvanceAnis(); + mm->CalcVertexPositions(); + } + DrawMorphMesh( mm, mvi->Meshes ); + } + } + } + + // Clear batches for next frame + g->ClearSkeletalMeshBatches(); + g->ClearNodeAttachmentBatches(); +} + /** Draws particles, in a simple way */ -void GothicAPI::DrawParticlesSimple() { +void GothicAPI::DrawParticlesSimple( + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion) { ParticleFrameData data; if ( RendererState.RendererSettings.DrawParticleEffects ) { @@ -1252,7 +1438,7 @@ void GothicAPI::DrawParticlesSimple() { } Engine::GraphicsEngine->DrawFrameParticleMeshes( ParticleEffectProgMeshes ); - Engine::GraphicsEngine->DrawFrameParticles( FrameParticles, FrameParticleInfo ); + Engine::GraphicsEngine->DrawFrameParticles( FrameParticles, FrameParticleInfo, bufferParticleColor, bufferParticleDistortion); } } @@ -1497,7 +1683,7 @@ void GothicAPI::GetVisibleDecalList( std::vector& decals ) { if ( dist > RendererState.RendererSettings.VisualFXDrawRadius ) continue; - if ( GetCameraBBox3DInFrustum( it->GetBBox(), EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) { + if ( GetCameraBBox3DInFrustum( it->GetBBox(), EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) { continue; } @@ -1750,9 +1936,9 @@ void GothicAPI::DrawMeshInfo( zCMaterial* mat, MeshInfo* msh ) { } if ( !msh->MeshIndexBuffer ) { - Engine::GraphicsEngine->DrawVertexBuffer( msh->MeshVertexBuffer, msh->Vertices.size() ); + Engine::GraphicsEngine->DrawVertexBuffer( msh->MeshVertexBuffer.get(), msh->Vertices.size() ); } else { - Engine::GraphicsEngine->DrawVertexBufferIndexed( msh->MeshVertexBuffer, msh->MeshIndexBuffer, msh->Indices.size() ); + Engine::GraphicsEngine->DrawVertexBufferIndexed( msh->MeshVertexBuffer.get(), msh->MeshIndexBuffer.get(), msh->Indices.size() ); } } @@ -1768,9 +1954,9 @@ void GothicAPI::DrawMeshInfo_Layered( zCMaterial* mat, MeshInfo* msh ) { D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); if ( !msh->MeshIndexBuffer ) { - g->DrawVertexBufferInstanced( msh->MeshVertexBuffer, msh->Vertices.size(), 6 ); + g->DrawVertexBufferInstanced( msh->MeshVertexBuffer.get(), msh->Vertices.size(), 6 ); } else { - g->DrawVertexBufferInstancedIndexed( msh->MeshVertexBuffer, msh->MeshIndexBuffer, msh->Indices.size(), 6 ); + g->DrawVertexBufferInstancedIndexed( msh->MeshVertexBuffer.get(), msh->MeshIndexBuffer.get(), msh->Indices.size(), 6 ); } } @@ -2303,7 +2489,7 @@ void GothicAPI::UpdateCompressBackBuffer() { } /** Draws a skeletal mesh-vob */ -void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState ) { +void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState, bool drawAttachments ) { // TODO: Put this into the renderer!! D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); @@ -2373,6 +2559,10 @@ void GothicAPI::DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool u WorldConverter::ExtractSkeletalMeshFromVob( model, static_cast(vi->VisualInfo) ); } } + + if (!drawAttachments) { + return; + } if ( g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) g->SetActiveVertexShader( "VS_ExNodeCube" ); @@ -2750,271 +2940,22 @@ void GothicAPI::DrawSkeletalMeshVobs( const std::vector& vis, bool updateState, bool drawAttachments ) { - // TODO: Put this into the renderer!! - D3D11GraphicsEngine* g = reinterpret_cast(Engine::GraphicsEngine); - - constexpr float distance = FLT_MAX; - - struct TempVobDrawInfo { - SkeletalVobInfo* VobInfo; - zCModel* Model; - std::vector BoneTransforms; - float4 ModelColor; - float Fatness; - XMMATRIX World; - }; - - static std::vector tempVobList; - tempVobList.clear(); - for ( SkeletalVobInfo* vi : vis ) { - zCModel* model = static_cast(vi->Vob->GetVisual()); - if ( !model ) { - continue; - } - - model->SetIsVisible( true ); - if ( !vi->VisualInfo ) - continue; // Gothic fortunately sets this to 0 when it throws the model out of the cache - if ( !vi->Vob->GetShowVisual() ) - continue; - - - SkeletalMeshVisualInfo* visual = static_cast(vi->VisualInfo); - - float4 modelColor; - if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { - // Let shadows do the work - modelColor = 0xFFFFFFFF; - } else { - if ( vi->Vob->IsIndoorVob() ) { - // All lightmapped polys have this color, so just use it - modelColor = DEFAULT_LIGHTMAP_POLY_COLOR; - } else { - // Get the color from vob position of the ground poly - if ( zCPolygon* polygon = vi->Vob->GetGroundPoly() ) { - static const float inv255f = (1.0f / 255.0f); - float3 vobPos = vi->Vob->GetPositionWorld(); - float3 polyLightStat = polygon->GetLightStatAtPos( vobPos ); - modelColor.x = polyLightStat.z * inv255f; - modelColor.y = polyLightStat.y * inv255f; - modelColor.z = polyLightStat.x * inv255f; - modelColor.w = 1.f; - } else { - modelColor = 0xFFFFFFFF; - } - } - } - - XMMATRIX scale = XMMatrixScalingFromVector( model->GetModelScaleXM() ); - - XMMATRIX xmWorld = vi->Vob->GetWorldMatrixXM() * scale; - XMFLOAT4X4 world; XMStoreFloat4x4( &world, xmWorld ); - float fatness = model->GetModelFatness(); - - // Get the bone transforms - static std::vector transforms; - transforms.clear(); - model->GetBoneTransforms( &transforms ); - - if ( updateState ) { - // Update attachments - model->UpdateAttachedVobs(); - model->UpdateMeshLibTexAniState(); - } - - if ( !static_cast(vi->VisualInfo)->SkeletalMeshes.empty() ) { -#ifdef BUILD_GOTHIC_2_6_fix - if ( !model->GetDrawHandVisualsOnly() || *reinterpret_cast(0x57A694) == 0x90 ) { -#else - if ( !model->GetDrawHandVisualsOnly() ) { -#endif - Engine::GraphicsEngine->DrawSkeletalMesh( vi, transforms, modelColor, world, fatness ); - } - } else { - if ( model->GetMeshSoftSkinList()->NumInArray > 0 ) { - // Just in case somehow we end up without skeletal meshes and they are available - WorldConverter::ExtractSkeletalMeshFromVob( model, static_cast(vi->VisualInfo) ); - } - } - - if ( drawAttachments ) { - TempVobDrawInfo info = {}; - info.VobInfo = vi; - info.Model = model; - info.BoneTransforms = std::move( transforms ); - info.Fatness = fatness; - info.ModelColor = modelColor; - info.World = xmWorld; - - tempVobList.push_back( info ); - } - - RendererState.RendererInfo.FrameDrawnVobs++; - } - - if ( !drawAttachments ) { - return; - } - - if ( g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) - g->SetActiveVertexShader( "VS_ExNodeCube" ); - else - g->SetActiveVertexShader( "VS_ExNode" ); - - g->SetupVS_ExMeshDrawCall(); - g->SetupVS_ExConstantBuffer(); - - for ( auto& data : tempVobList ) { - - auto vi = data.VobInfo; - auto model = data.Model; - auto modelColor = data.ModelColor; - auto& transforms = data.BoneTransforms; - auto fatness = data.Fatness; - auto& world = data.World; - - SkeletalMeshVisualInfo* visual = static_cast(vi->VisualInfo); - // Set up instance info - VS_ExConstantBuffer_PerInstanceNode instanceInfo; - instanceInfo.Color = modelColor; - - // Init the constantbuffer if not already done - if ( !vi->VobConstantBuffer ) - vi->UpdateVobConstantBuffer(); - - std::map>& nodeAttachments = vi->NodeAttachments; - for ( unsigned int i = 0; i < transforms.size(); i++ ) { - // Check for new visual - zCModel* mvis = static_cast( vi->Vob->GetVisual() ); - zCModelNodeInst* node = mvis->GetNodeList()->Array[i]; - - if ( !node->NodeVisual ) - continue; // Happens when you pull your sword for example - - // Check if this is loaded - if ( node->NodeVisual && nodeAttachments.find( i ) == nodeAttachments.end() ) { - // It's not, extract it - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - - // Check for changed visual - if ( nodeAttachments[i].size() && node->NodeVisual != nodeAttachments[i][0]->Visual ) { - // Check for deleted attachment - if ( !node->NodeVisual ) { - // Remove attachment - delete nodeAttachments[i][0]; - nodeAttachments[i].clear(); - - LogInfo() << "Removed attachment from model " << vi->VisualInfo->VisualName; - - continue; // Go to next attachment - } - // Load the new one - WorldConverter::ExtractNodeVisual( i, node, nodeAttachments ); - } - - if ( model->GetDrawHandVisualsOnly() ) { - std::string NodeName = node->ProtoNode->NodeName.ToChar(); -#ifdef BUILD_GOTHIC_2_6_fix - if ( NodeName.find( "HAND" ) == std::string::npos && (*reinterpret_cast(0x57A694) != 0x90 || NodeName.find( "ARM" ) == std::string::npos) ) { -#else - if ( NodeName.find( "HAND" ) == std::string::npos ) { +#if defined(BUILD_1_12F) + // not implemented in G1 sequel + RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced = false; #endif - continue; - } - } - - if ( nodeAttachments.find( i ) != nodeAttachments.end() ) { - - // Setup pixel shader here so that we get correct normals - // Somehow BindShaderForTexture make normals to be inversed - if ( g->GetRenderingStage() == DES_MAIN ) { - g->SetActivePixelShader( "PS_DiffuseAlphaTest" ); - g->BindActivePixelShader(); - } - - // Go through all attachments this node has - for ( MeshVisualInfo* mvi : nodeAttachments[i] ) { - XMMATRIX curTransform = XMLoadFloat4x4( &transforms[i] ); - XMFLOAT4X4 finalWorld; XMStoreFloat4x4( &finalWorld, world* curTransform ); - - if ( !mvi->Visual ) { - LogWarn() << "Attachment without visual on model: " << model->GetVisualName(); - continue; - } - - // Update animated textures - bool isMMS = strcmp( mvi->Visual->GetFileExtension( 0 ), ".MMS" ) == 0; - if ( updateState ) { - node->TexAniState.UpdateTexList(); - if ( isMMS ) { - zCMorphMesh* mm = reinterpret_cast(mvi->Visual); - mm->GetTexAniState()->UpdateTexList(); - } - } - - if ( isMMS ) { - // Only 0.35f of the fatness wanted by gothic. - // They seem to compensate for that with the scaling. - instanceInfo.Fatness = std::max( 0.f, fatness * 0.35f ); - instanceInfo.Scaling = fatness * 0.02f + 1.f; - } else { - instanceInfo.Fatness = 0.f; - instanceInfo.Scaling = 1.f; - } - - auto& VShader = g->GetActiveVS(); - if ( distance < 1000 && isMMS ) { - zCMorphMesh* mm = reinterpret_cast( mvi->Visual ); - // Only draw this as a morphmesh when rendering the main scene or when rendering as ghost - if ( g->GetRenderingStage() == DES_MAIN || g->GetRenderingStage() == DES_GHOST ) { - // Update constantbuffer - instanceInfo.World = finalWorld; - VShader->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); - VShader->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - - if ( updateState ) { - mm->AdvanceAnis(); - mm->CalcVertexPositions(); - } - DrawMorphMesh( mm, mvi->Meshes ); - continue; - } - } - - instanceInfo.World = finalWorld; - VShader->GetConstantBuffer()[1]->UpdateBuffer( &instanceInfo ); - VShader->GetConstantBuffer()[1]->BindToVertexShader( 1 ); - // Go through all materials registered here - - if ( g->GetRenderingStage() == DES_SHADOWMAP - || g->GetRenderingStage() == DES_SHADOWMAP_CUBE ) { - for ( auto const& itm : mvi->Meshes ) { - // no texture binding for shadowmap + if ( RendererState.RendererSettings.Experimental.DrawSkeletalsInstanced ) { + DrawSkeletalMeshVobs_Batched( vis, updateState, drawAttachments ); + } else { + FXMVECTOR camPos = GetCameraPositionXM(); - // Go through all meshes using that material - for ( unsigned int m = 0; m < itm.second.size(); m++ ) { - DrawMeshInfo( itm.first, itm.second[m] ); - } - } - } else { - for ( auto const& itm : mvi->Meshes ) { - zCTexture* texture; - if ( itm.first && (texture = itm.first->GetAniTexture()) != nullptr ) { - if ( !g->BindTextureNRFX( texture, (g->GetRenderingStage() == DES_MAIN) ) ) - continue; - } + for ( SkeletalVobInfo* vi : vis ) { + float dist; + XMStoreFloat( &dist, XMVector3Length( camPos - vi->Vob->GetPositionWorldXM() ) ); - // Go through all meshes using that material - for ( unsigned int m = 0; m < itm.second.size(); m++ ) { - DrawMeshInfo( itm.first, itm.second[m] ); - } - } - } - } - } + DrawSkeletalMeshVob( vi, dist, updateState, drawAttachments ); } } } @@ -3071,8 +3012,8 @@ void GothicAPI::DrawTransparencyVobs() { for ( auto const& meshInfo : materialMesh.second ) { g->DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -3098,8 +3039,8 @@ void GothicAPI::DrawTransparencyVobs() { for ( auto const& meshInfo : materialMesh.second ) { g->DrawVertexBufferIndexed( - meshInfo->MeshVertexBuffer, - meshInfo->MeshIndexBuffer, + meshInfo->MeshVertexBuffer.get(), + meshInfo->MeshIndexBuffer.get(), meshInfo->Indices.size() ); } } @@ -3992,7 +3933,7 @@ void GothicAPI::DebugDrawBSPTree() { // Recursively go through the tree and draw all nodes DebugDrawTreeNode( root, root->BBox3D ); } - + /** Collects vobs using gothics BSP-Tree */ void GothicAPI::CollectVisibleVobs( std::vector& vobs, @@ -4124,7 +4065,7 @@ void GothicAPI::CollectVisibleVobs( } } } - + /** Collects visible sections from the current camera perspective */ void GothicAPI::CollectVisibleSections( std::vector& sections ) { const XMFLOAT3 camPos = Engine::GAPI->GetCameraPosition(); @@ -4132,7 +4073,6 @@ void GothicAPI::CollectVisibleSections( std::vector& sect auto cullingEnabled = Engine::GAPI->GetRendererState().RendererSettings.DebugSettings.Culling.CullBspSections; if ( Engine::GAPI->GetRendererState().RendererSettings.DrawSectionIntersections ) { - extern const float WORLD_SECTION_SIZE; const float sectionViewDist = Engine::GAPI->GetRendererState().RendererSettings.SectionDrawRadius * WORLD_SECTION_SIZE; for ( auto& itx : WorldSections ) { for ( auto& ity : itx.second ) { @@ -4140,7 +4080,7 @@ void GothicAPI::CollectVisibleSections( std::vector& sect float dist = Toolbox::ComputePointAABBDistance( camPos, section.BoundingBox.Min, section.BoundingBox.Max ); if ( dist < sectionViewDist ) { - if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) + if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) continue; sections.push_back( §ion ); @@ -4160,7 +4100,7 @@ void GothicAPI::CollectVisibleSections( std::vector& sect // Simple range-check if ( abs( ity.first - camSection.y ) < sectionViewDist ) { - if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSides ) == ZTCAM_CLIPTYPE_OUT ) + if ( cullingEnabled && GetCameraBBox3DInFrustum( section.BoundingBox, EGothicCullFlags::CullSidesNear ) == ZTCAM_CLIPTYPE_OUT ) continue; sections.push_back( §ion ); @@ -4309,7 +4249,7 @@ static void CVVH_AddNotDrawnVobToList( callback( ctx, it ); } } - + static void CVVH_AddNotDrawnVobToList( std::vector& source, float dist, const RndCullContext& ctx, @@ -5277,7 +5217,7 @@ void GothicAPI::DrawMorphMesh( zCMorphMesh* msh, std::mapMeshIndex == i ) { mi->MeshVertexBuffer->UpdateBuffer( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); - Engine::GraphicsEngine->DrawVertexBufferIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size() ); + Engine::GraphicsEngine->DrawVertexBufferIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size() ); goto Out_Of_Nested_Loop; } } @@ -5317,7 +5257,7 @@ void GothicAPI::DrawMorphMesh_Layered( zCMorphMesh* msh, std::mapMeshIndex == i ) { mi->MeshVertexBuffer->UpdateBuffer( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); - g->DrawVertexBufferInstancedIndexed( mi->MeshVertexBuffer, mi->MeshIndexBuffer, mi->Indices.size(), 6 ); + g->DrawVertexBufferInstancedIndexed( mi->MeshVertexBuffer.get(), mi->MeshIndexBuffer.get(), mi->Indices.size(), 6 ); goto Out_Of_Nested_Loop; } } @@ -5558,7 +5498,7 @@ void GothicAPI::CreatezCPolygonsForSections() { it->first.Material->SetAlphaFunc( zMAT_ALPHA_FUNC_NONE ); - WorldConverter::ConvertExVerticesTozCPolygons( it->second->Vertices, it->second->Indices, it->first.Material, section.SectionPolygons ); + WorldConverter::ConvertExVerticesTozCPolygons( *it->second->Vertices.data, *it->second->Indices.data, it->first.Material, section.SectionPolygons ); } } } diff --git a/D3D11Engine/GothicAPI.h b/D3D11Engine/GothicAPI.h index 2097b2d6..b66849b7 100644 --- a/D3D11Engine/GothicAPI.h +++ b/D3D11Engine/GothicAPI.h @@ -7,6 +7,7 @@ #include "zCPolyStrip.h" #include "zTypes.h" #include "RenderQueue.h" +#include "RenderToTextureBuffer.h" #define START_TIMING(x) TimerScope( x, &Engine::GAPI->GetRendererState().RendererInfo.Timing.frameRecordings ) @@ -62,6 +63,18 @@ enum EBspTreeCollectFlags : unsigned int { COLLECT_ALL_NO_MUTATE = COLLECT_ALL_MUTATE & ~COLLECT_MUTATE, }; +typedef size_t NodeAttachmentBatchKey; + +// Batch data for node attachments +struct NodeAttachmentBatchData { + std::vector Mesh; // First mesh encountered + std::vector Instances; // All instances + SkeletalVobInfo* vobInfo; + zCModelNodeInst* node; + MeshVisualInfo* MeshVisualInfo; + zCMaterial* Material; +}; + struct BspInfo { BspInfo() { NumStaticLights = 0; @@ -210,6 +223,31 @@ class GVegetationBox; class zCMorphMesh; class zCDecal; +// CPU-side batch information for skeletal mesh rendering +struct SkeletalMeshBatchEntry { + zCTexture* material; + SkeletalMeshInfo* mesh; + uint32_t instanceIndex; +}; + +struct SkeletalMeshBatch { + std::vector Entries; + std::vector BoneTransforms; + std::vector Instances; +}; + +// CPU-side batch information for node attachment rendering +struct NodeAttachmentBatchEntry { + zCTexture* texture; // Texture for this entry + MeshInfo* mesh; // Mesh geometry + uint32_t instanceIndex; // Index into NodeAttachmentInstanceData buffer +}; + +struct NodeAttachmentBatch { + std::vector Entries; + std::vector Instances; +}; + class GothicAPI { friend void CVVH_AddNotDrawnVobToList( std::vector& target, std::vector& source, float dist, int clipFlags, EBspTreeCollectFlags collectFlags, zTCam_ClipType bspClip ); friend void CVVH_AddNotDrawnVobToList( std::vector& target, std::vector& source, float dist, int clipFlags, EBspTreeCollectFlags collectFlags, zTCam_ClipType bspClip ); @@ -306,7 +344,7 @@ class GothicAPI { void DrawWorldMeshNaive(); /** Draws a skeletal mesh-vob */ - void DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState = true ); + void DrawSkeletalMeshVob( SkeletalVobInfo* vi, float distance, bool updateState = true, bool drawAttachments = true ); void DrawSkeletalMeshVob_Layered( SkeletalVobInfo* vi, float distance, bool updateState = true ); @@ -512,7 +550,9 @@ class GothicAPI { void DebugDrawTreeNode( zCBspBase* base, zTBBox3D boxCell, int clipFlags = 63 ); /** Draws particles, in a simple way */ - void DrawParticlesSimple(); + void DrawParticlesSimple( + RenderToTextureBuffer* bufferParticleColor, + RenderToTextureBuffer* bufferParticleDistortion); /** Prepares poly strips for feeding into renderer (weapon and effect trails) */ void CalcPolyStripMeshes(); @@ -594,6 +634,7 @@ class GothicAPI { /** Sets the CameraReplacementPtr */ void SetCameraReplacementPtr( CameraReplacement* ptr ) { CameraReplacementPtr = ptr; } + CameraReplacement* GetCameraReplacementPtr() const { return CameraReplacementPtr; } /** Lets Gothic draw its sky */ void DrawSkyGothicOriginal(); @@ -758,6 +799,9 @@ class GothicAPI { static void ProcessVobAnimation( zCVob* vob, zTAnimationMode aniMode, VobInstanceInfo& vobInstance ); + /** Draw skeletal meshes using batching when possible */ + void DrawSkeletalMeshVobs_Batched( const std::vector& vobs, bool updateState, bool drawAttachments ); + private: /** Collects polygons in the given AABB */ void CollectPolygonsInAABBRec( BspInfo* base, const zTBBox3D& bbox, std::vector& list ); diff --git a/D3D11Engine/GothicGraphicsState.h b/D3D11Engine/GothicGraphicsState.h index 327920bb..6ab3237c 100644 --- a/D3D11Engine/GothicGraphicsState.h +++ b/D3D11Engine/GothicGraphicsState.h @@ -755,6 +755,8 @@ struct GothicRendererSettings { DebugSettings.Culling.CullBspSections = true; DebugSettings.Culling.CullVobs = true; DebugSettings.ShadowCascades.LazyCascadeUpdate = true; + + Experimental = {}; } void SetupOldWorldSpecificValues() { @@ -940,7 +942,15 @@ struct GothicRendererSettings { bool CullVobs; bool CullBspSections; } Culling; + struct { + bool UseMDI; + bool UseLayeredRendering; + } FeatureSet; } DebugSettings; + + struct { + bool DrawSkeletalsInstanced; + } Experimental; }; struct GothicRendererTiming { diff --git a/D3D11Engine/GothicMemoryLocations1_08k.h b/D3D11Engine/GothicMemoryLocations1_08k.h index d6703f45..6f7a4baf 100644 --- a/D3D11Engine/GothicMemoryLocations1_08k.h +++ b/D3D11Engine/GothicMemoryLocations1_08k.h @@ -457,6 +457,7 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + static const unsigned int Offset_ProgId = 0xCC; }; struct zCWorld { @@ -633,6 +634,8 @@ struct GothicMemoryLocations { static const unsigned int zCTriggerList = 0x008d7f68; static const unsigned int zCVobStair = 0x008d7708; static const unsigned int zCTexture = 0x008CF110; + static const unsigned int zCMorphMesh = 0x00873d98; + static const unsigned int zCProgMeshProto = 0x008c5b48; }; struct oCInformationManager diff --git a/D3D11Engine/GothicMemoryLocations1_12f.h b/D3D11Engine/GothicMemoryLocations1_12f.h index ea5d7c9d..bf0c8fa9 100644 --- a/D3D11Engine/GothicMemoryLocations1_12f.h +++ b/D3D11Engine/GothicMemoryLocations1_12f.h @@ -416,6 +416,9 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + + // NOT IMPLEMENTED YET! + static const unsigned int Offset_ProgId = 0x0; }; struct zCWorld { @@ -542,6 +545,11 @@ struct GothicMemoryLocations { static const unsigned int oCNpc = 0x00922830; static const unsigned int zCTexture = 0x00914FE0; static const unsigned int oCVisualFX = 0x008AF438; + + // NOT IMPLEMENTED + static const unsigned int zCMorphMesh = 0; + // NOT IMPLEMENTED + static const unsigned int zCProgMeshProto = 0; }; struct oCInformationManager diff --git a/D3D11Engine/GothicMemoryLocations2_6_fix.h b/D3D11Engine/GothicMemoryLocations2_6_fix.h index 23a17b7b..67d3f3ec 100644 --- a/D3D11Engine/GothicMemoryLocations2_6_fix.h +++ b/D3D11Engine/GothicMemoryLocations2_6_fix.h @@ -453,6 +453,7 @@ struct GothicMemoryLocations { static const unsigned int Offset_NormalsList = 0x3C; static const unsigned int Offset_Submeshes = 0xA4; static const unsigned int Offset_NumSubmeshes = 0xA8; + static const unsigned int Offset_ProgId = 0xD0; }; struct zCVob { @@ -691,6 +692,8 @@ struct GothicMemoryLocations { static const unsigned int zCZoneZFogDefault = 0x009a4618; static const unsigned int oCZoneMusicDefault = 0x009a4938; static const unsigned int zCTexture = 0x0099B2F8; + static const unsigned int zCMorphMesh = 0x008d8c00; + static const unsigned int zCProgMeshProto = 0x00982b48; }; struct oCInformationManager diff --git a/D3D11Engine/ImGuiEditorView.cpp b/D3D11Engine/ImGuiEditorView.cpp index a68c011c..e12f581b 100644 --- a/D3D11Engine/ImGuiEditorView.cpp +++ b/D3D11Engine/ImGuiEditorView.cpp @@ -1092,12 +1092,7 @@ GVegetationBox* ImGuiEditorView::TraceVegetationBoxes(const XMFLOAT3& wPos, cons } void ImGuiEditorView::SmoothMesh(WorldMeshInfo* mesh, bool tesselate) { - // Copy old vertices so we can directly write to the vectors again - std::vector vxOld = mesh->Vertices; - std::vector ixOld = mesh->Indices; - - // Mark dirty - mesh->SaveInfo = true; + // Not implemented } XRESULT ImGuiEditorView::OnVobRemovedFromWorld(zCVob* vob) { diff --git a/D3D11Engine/ImGuiShim.cpp b/D3D11Engine/ImGuiShim.cpp index 371b0bb6..04c447ef 100644 --- a/D3D11Engine/ImGuiShim.cpp +++ b/D3D11Engine/ImGuiShim.cpp @@ -1178,6 +1178,17 @@ void RenderAdvancedColumn2( GothicRendererSettings& settings, GothicAPI* gapi ) ImGui::Checkbox("Vobs", &settings.DebugSettings.Culling.CullVobs ); ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Featureset", nullptr, ImGuiTabItemFlags_::ImGuiTabItemFlags_NoReorder)) { + ImGui::Checkbox("Use MDI", &settings.DebugSettings.FeatureSet.UseMDI ); + ImGui::Checkbox("Use Layered Drawing", &settings.DebugSettings.FeatureSet.UseLayeredRendering ); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Experimental", nullptr, ImGuiTabItemFlags_::ImGuiTabItemFlags_NoReorder)) { + ImGui::Checkbox("Draw Skeletals Instanced", &settings.Experimental.DrawSkeletalsInstanced ); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } diff --git a/D3D11Engine/MeshManager.cpp b/D3D11Engine/MeshManager.cpp new file mode 100644 index 00000000..0581d08f --- /dev/null +++ b/D3D11Engine/MeshManager.cpp @@ -0,0 +1,13 @@ +#include "MeshManager.h" +#include "BaseGraphicsEngine.h" +#include "zCProgMeshProto.h" +#include "zCMeshSoftSkin.h" + +MeshManager* s_MeshManager = new MeshManager(); + +MeshManager::MeshManager() { + m_skeletalMeshInfo = {}; + m_staticMeshInfo = {}; +} + + diff --git a/D3D11Engine/MeshManager.h b/D3D11Engine/MeshManager.h new file mode 100644 index 00000000..b4829263 --- /dev/null +++ b/D3D11Engine/MeshManager.h @@ -0,0 +1,41 @@ +#pragma once +#include "pch.h" +#include "zCProgMeshProto.h" + +class MeshManager { + +public: + MeshManager(); + + std::shared_ptr MeshManager::PutSkeletalData( zCSubMesh* subMesh, SkeletalMeshData* data ) { + auto sharedData = std::shared_ptr( data ); + m_skeletalMeshInfo[subMesh] = sharedData; + return sharedData; + } + + std::shared_ptr MeshManager::GetSkeletalData( zCSubMesh* subMesh ) { + auto f = m_skeletalMeshInfo.find( subMesh ); + if ( f != m_skeletalMeshInfo.end() ) { + return f->second; + } + return nullptr; + } + + std::shared_ptr MeshManager::PutStaticData( zCSubMesh* subMesh, StaticMeshData* data ) { + auto sharedData = std::shared_ptr( data ); + m_staticMeshInfo[subMesh] = sharedData; + return sharedData; + } + + std::shared_ptr MeshManager::GetStaticData( zCSubMesh* subMesh ) { + auto f = m_staticMeshInfo.find( subMesh ); + if ( f != m_staticMeshInfo.end() ) { + return f->second; + } + return nullptr; + } + +private: + std::unordered_map> m_skeletalMeshInfo; + std::unordered_map> m_staticMeshInfo; +}; diff --git a/D3D11Engine/RGBuilder.cpp b/D3D11Engine/RGBuilder.cpp new file mode 100644 index 00000000..ece662f8 --- /dev/null +++ b/D3D11Engine/RGBuilder.cpp @@ -0,0 +1,17 @@ +#include "RGBuilder.h" + +// Implement RGBuilder methods (must be done after RenderGraph is fully defined) +RGResourceHandle RGBuilder::Read( RGResourceHandle handle ) { + m_pass.m_reads.push_back( handle ); + return handle; +} + +RGResourceHandle RGBuilder::Write( RGResourceHandle handle ) { + m_pass.m_writes.push_back( handle ); + return handle; +} + +RGResourceHandle RGBuilder::CreateTexture( const RGTextureDesc& desc ) { + RGResourceHandle handle = m_graph.RegisterResource( desc ); + return Write( handle ); // Creating it implies we are writing to it +} diff --git a/D3D11Engine/RGBuilder.h b/D3D11Engine/RGBuilder.h new file mode 100644 index 00000000..f3a18c4e --- /dev/null +++ b/D3D11Engine/RGBuilder.h @@ -0,0 +1,24 @@ +#pragma once +#include "RGTextureDesc.h" +#include "RenderGraph.h" +#include "RenderPass.h" + +class RGBuilder { +public: + RGBuilder( class RenderGraph& graph, class RenderPass& pass ) + : m_graph( graph ), m_pass( pass ) { + } + + // Declare that this pass READS from a resource (Source) + RGResourceHandle Read( RGResourceHandle handle ); + + // Declare that this pass WRITES to a resource (Sink) + RGResourceHandle Write( RGResourceHandle handle ); + + // Declare a brand new transient resource that lives only for this graph execution + RGResourceHandle CreateTexture( const RGTextureDesc& desc ); + +private: + RenderGraph& m_graph; + RenderPass& m_pass; +}; diff --git a/D3D11Engine/RGTextureDesc.h b/D3D11Engine/RGTextureDesc.h new file mode 100644 index 00000000..145a52e3 --- /dev/null +++ b/D3D11Engine/RGTextureDesc.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include +#include + +// Typedefs to represent our virtual resources +using RGResourceHandle = uint32_t; +constexpr RGResourceHandle RG_INVALID_HANDLE = 0xFFFFFFFF; + +// Metadata to describe a texture's needs without allocating it yet +struct RGTextureDesc { + uint32_t width = 0; + uint32_t height = 0; + int format = 0; // Replace with DXGI_FORMAT in your engine + std::wstring name; +}; diff --git a/D3D11Engine/RenderGraph.cpp b/D3D11Engine/RenderGraph.cpp new file mode 100644 index 00000000..c5e88d21 --- /dev/null +++ b/D3D11Engine/RenderGraph.cpp @@ -0,0 +1 @@ +#include "RenderGraph.h" diff --git a/D3D11Engine/RenderGraph.h b/D3D11Engine/RenderGraph.h new file mode 100644 index 00000000..f9599943 --- /dev/null +++ b/D3D11Engine/RenderGraph.h @@ -0,0 +1,163 @@ +#pragma once +#include "RGTextureDesc.h" +#include + +#include "BaseGraphicsEngine.h" +#include "Engine.h" +#include "RenderPass.h" +#include "RGBuilder.h" +#include "TexturePool.h" + +// Helpers for bit-packing +inline bool IsExternalHandle(RGResourceHandle handle) { + return (handle & 1) != 0; // Check if the first bit is 1 +} + +inline uint32_t GetHandleIndex(RGResourceHandle handle) { + return handle >> 1; // Shift right by 1 to get the actual ID +} + +inline RGResourceHandle MakeHandle(uint32_t index, bool isExternal) { + return (index << 1) | (isExternal ? 1 : 0); +} + +class RenderGraph { +public: + RenderGraph( TexturePool* pool ) : m_texturePool( pool ) {} + + // Bring an existing engine resource (like the DX11 BackBuffer) into the graph + RGResourceHandle ImportResource(const std::wstring& name, RenderToTextureBuffer* externalBuffer) { + uint32_t index = m_nextHandle++; + + // Resize vectors to accommodate the new index + m_externalTextures.resize(m_nextHandle, nullptr); + m_activeTextures.resize(m_nextHandle); + m_resourceDescs.resize(m_nextHandle); + + m_externalTextures[index] = externalBuffer; + m_resourceDescs[index] = {0, 0, 0, name}; // Dummy desc for name tracking + + return MakeHandle(index, true); // Sets the first bit to 1 + } + + // Add a pass using modern C++ lambdas + template + void AddPass( const std::wstring& name, SetupFunc setupFunc ) { + auto pass = std::make_unique( name ); + RGBuilder builder( *this, *pass ); + + // 1. Run the setup function to declare reads/writes + setupFunc( builder, *pass ); + + m_passes.push_back( std::move( pass ) ); + } + + // Called by RGBuilder to register handles + RGResourceHandle RegisterResource(const RGTextureDesc& desc) { + uint32_t index = m_nextHandle++; + + m_externalTextures.resize(m_nextHandle, nullptr); + m_activeTextures.resize(m_nextHandle); + m_resourceDescs.resize(m_nextHandle); + + m_resourceDescs[index] = desc; + + return MakeHandle(index, false); // First bit remains 0 + } + + void Compile() { + m_resourceLifetimes.assign(m_nextHandle, { UINT32_MAX, 0 }); + + for ( size_t passIndex = 0; passIndex < m_passes.size(); ++passIndex ) { + const auto& pass = m_passes[passIndex]; + + // Track writes (creation/modification) + for ( RGResourceHandle writeHandle : pass->m_writes ) { + uint32_t index = GetHandleIndex(writeHandle); + + // If this is the first time we are writing to this resource, record its birth + if ( m_resourceLifetimes[index].firstPass == UINT32_MAX ) { + m_resourceLifetimes[index].firstPass = (uint32_t)passIndex; + } + + // Every write extends its lifetime to this pass + m_resourceLifetimes[index].lastPass = (uint32_t)passIndex; + } + + // Track reads (usage) + for ( RGResourceHandle readHandle : pass->m_reads ) { + uint32_t index = GetHandleIndex(readHandle); + + // Reads extend the lifetime of the resource to this pass + m_resourceLifetimes[index].lastPass = (uint32_t)passIndex; + } + } + + // TODO: remove all passes whose writes are never read. + } + + void Execute() { + for ( size_t i = 0; i < m_passes.size(); ++i ) { + const auto& pass = m_passes[i]; + + AllocateResourcesForPass( i ); + + if ( pass->m_executeCallback ) { + auto _ = Engine::GraphicsEngine->RecordGraphicsEvent( pass->m_name.c_str() ); + pass->m_executeCallback(*this); + } + + ReleaseResourcesForPass( i ); + } + } + + RenderToTextureBuffer* GetPhysicalTexture(RGResourceHandle handle) const { + uint32_t index = GetHandleIndex(handle); + + return IsExternalHandle(handle) + ? m_externalTextures[index] + : m_activeTextures[index].get(); + } +private: + struct Lifetime { uint32_t firstPass; uint32_t lastPass; }; + + TexturePool* m_texturePool; + uint32_t m_nextHandle = 0; + std::vector> m_passes; + std::vector m_resourceDescs; + std::vector m_resourceLifetimes; + + // Physical resource storage mapped by the Handle Index + std::vector m_activeTextures; + std::vector m_externalTextures; + + void AllocateResourcesForPass(size_t passIndex) { + for (uint32_t i = 0; i < m_resourceLifetimes.size(); ++i) { + // We only allocate for Graph-Managed resources + // (Assuming we track external handles in the lifetimes list too, we just skip them) + + if (m_resourceLifetimes[i].firstPass == (uint32_t)passIndex) { + // If this index is meant to be external, m_externalTextures[i] will be populated, + // so we don't allocate it from the pool. + if (m_externalTextures[i] != nullptr) continue; + + const RGTextureDesc& desc = m_resourceDescs[i]; + TexturePool::Description poolDesc{ (int)desc.width, (int)desc.height, static_cast(desc.format) }; + + m_activeTextures[i] = std::move(m_texturePool->Acquire(poolDesc)); + } + } + } + + void ReleaseResourcesForPass(size_t passIndex) { + for (uint32_t i = 0; i < m_resourceLifetimes.size(); ++i) { + if (m_resourceLifetimes[i].lastPass == (uint32_t)passIndex) { + if (m_externalTextures[i] != nullptr) continue; + + // Resetting the unique_ptr triggers + // returning it to the TexturePool automatically. + m_activeTextures[i].reset(); + } + } + } +}; diff --git a/D3D11Engine/RenderPass.cpp b/D3D11Engine/RenderPass.cpp new file mode 100644 index 00000000..1e829a2c --- /dev/null +++ b/D3D11Engine/RenderPass.cpp @@ -0,0 +1 @@ +#include "RenderPass.h" diff --git a/D3D11Engine/RenderPass.h b/D3D11Engine/RenderPass.h new file mode 100644 index 00000000..7995c040 --- /dev/null +++ b/D3D11Engine/RenderPass.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include "RGTextureDesc.h" + +class RenderPass { + // Only the RenderGraph should trigger execution + friend class RenderGraph; + +public: + RenderPass(std::wstring name ) : m_name(std::move(name)) {} + + std::wstring m_name; + std::vector m_reads; // Sources + std::vector m_writes; // Sinks + + // The function that records the actual DX11 commands + // In your engine, pass your DX11DeviceContext or CommandList wrapper here + std::function m_executeCallback; + +}; diff --git a/D3D11Engine/SMAA/D3D11SMAA.cpp b/D3D11Engine/SMAA/D3D11SMAA.cpp index c209ccb7..6c541458 100644 --- a/D3D11Engine/SMAA/D3D11SMAA.cpp +++ b/D3D11Engine/SMAA/D3D11SMAA.cpp @@ -1,7 +1,5 @@ #include "D3D11SMAA.h" -#include -#include -#include +#include "../D3D11ShaderManager.h" // Include DirectXTK or your preferred texture loader #include "DDSTextureLoader.h" // Assuming DirectXTK availability @@ -21,24 +19,6 @@ D3D11SMAA::~D3D11SMAA() { } -HRESULT D3D11SMAA::CompileShader(const std::wstring& path, const std::string& entryPoint, const std::string& profile, ID3DBlob** blob) -{ - ComPtr errorBlob; - UINT flags = D3DCOMPILE_ENABLE_STRICTNESS; -#ifdef _DEBUG - flags |= D3DCOMPILE_DEBUG; -#endif - - HRESULT hr = D3DCompileFromFile(path.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, - entryPoint.c_str(), profile.c_str(), flags, 0, blob, errorBlob.GetAddressOf()); - - if (FAILED(hr) && errorBlob) - { - OutputDebugStringA((char*)errorBlob->GetBufferPointer()); - } - return hr; -} - bool D3D11SMAA::Init(const std::wstring& shaderPath, const std::wstring& areaTexPath, const std::wstring& searchTexPath) { HRESULT hr; @@ -46,24 +26,25 @@ bool D3D11SMAA::Init(const std::wstring& shaderPath, const std::wstring& areaTex // 1. Compile Shaders // Edge Detection - if (FAILED(CompileShader(shaderPath, "EdgeDetectionVS", "vs_5_0", &blob))) return false; + std::vector noMacros{}; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "EdgeDetectionVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsEdge.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "LumaEdgeDetectionPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "LumaEdgeDetectionPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psLumaEdge.GetAddressOf()); // Blending Weight - if (FAILED(CompileShader(shaderPath, "BlendingWeightCalculationVS", "vs_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "BlendingWeightCalculationVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsBlend.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "BlendingWeightCalculationPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "BlendingWeightCalculationPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psBlend.GetAddressOf()); // Neighborhood Blending - if (FAILED(CompileShader(shaderPath, "NeighborhoodBlendingVS", "vs_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "NeighborhoodBlendingVS", "vs_5_0", &blob, noMacros))) return false; m_device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_vsNeighbor.GetAddressOf()); - if (FAILED(CompileShader(shaderPath, "NeighborhoodBlendingPS", "ps_5_0", &blob))) return false; + if (FAILED(D3D11ShaderManager::CompileShaderFromFile(shaderPath.c_str(), "NeighborhoodBlendingPS", "ps_5_0", &blob, noMacros))) return false; m_device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_psNeighbor.GetAddressOf()); // 2. Load Textures @@ -127,44 +108,25 @@ void D3D11SMAA::OnResize(int width, int height) m_width = width; m_height = height; - // Release old - m_edgesTex.Reset(); m_edgesRTV.Reset(); m_edgesSRV.Reset(); - m_blendTex.Reset(); m_blendRTV.Reset(); m_blendSRV.Reset(); - - D3D11_TEXTURE2D_DESC desc = {}; - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // SMAA requires specific formats, RGBA8 UNORM is generally safe - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - - // Create Edges Texture - m_device->CreateTexture2D(&desc, nullptr, m_edgesTex.GetAddressOf()); - m_device->CreateRenderTargetView(m_edgesTex.Get(), nullptr, m_edgesRTV.GetAddressOf()); - m_device->CreateShaderResourceView(m_edgesTex.Get(), nullptr, m_edgesSRV.GetAddressOf()); - - // Create Blend Texture - m_device->CreateTexture2D(&desc, nullptr, m_blendTex.GetAddressOf()); - m_device->CreateRenderTargetView(m_blendTex.Get(), nullptr, m_blendRTV.GetAddressOf()); - m_device->CreateShaderResourceView(m_blendTex.Get(), nullptr, m_blendSRV.GetAddressOf()); - // Update Constant Buffer SMAAConstants constants; constants.RT_Metrics = XMFLOAT4(1.0f / width, 1.0f / height, (float)width, (float)height); m_context->UpdateSubresource(m_constantBuffer.Get(), 0, nullptr, &constants, 0, 0); } -void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV) +void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, + ID3D11RenderTargetView* outputRTV, + TexturePool* pool ) { if (!m_width || !m_height) return; // Save old state (Optional, but good practice in a library) // For performance in an engine, you usually don't save/restore but assume state flow. // Here we just set what we need. - + + auto edgesTex = pool->Acquire({m_width, m_height, DXGI_FORMAT_R8G8B8A8_UNORM}); + auto blendTex = pool->Acquire({m_width, m_height, DXGI_FORMAT_R8G8B8A8_UNORM}); + float clearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ID3D11ShaderResourceView* nullSRVs[3] = { nullptr, nullptr, nullptr }; @@ -188,8 +150,8 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie // Input: Color (t0) // Output: EdgesTex // ----------------------------------------------------------- - m_context->ClearRenderTargetView(m_edgesRTV.Get(), clearColor); - m_context->OMSetRenderTargets(1, m_edgesRTV.GetAddressOf(), nullptr); + m_context->ClearRenderTargetView(edgesTex->GetRenderTargetView().Get(), clearColor); + m_context->OMSetRenderTargets(1, edgesTex->GetRenderTargetView().GetAddressOf(), nullptr); m_context->VSSetShader(m_vsEdge.Get(), nullptr, 0); m_context->PSSetShader(m_psLumaEdge.Get(), nullptr, 0); @@ -207,14 +169,14 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie // Input: EdgesTex (t1), AreaTex (t3), SearchTex (t4) // Output: BlendTex // ----------------------------------------------------------- - m_context->ClearRenderTargetView(m_blendRTV.Get(), clearColor); - m_context->OMSetRenderTargets(1, m_blendRTV.GetAddressOf(), nullptr); + m_context->ClearRenderTargetView(blendTex->GetRenderTargetView().Get(), clearColor); + m_context->OMSetRenderTargets(1, blendTex->GetRenderTargetView().GetAddressOf(), nullptr); m_context->VSSetShader(m_vsBlend.Get(), nullptr, 0); m_context->PSSetShader(m_psBlend.Get(), nullptr, 0); // Bind resources - ID3D11ShaderResourceView* pass2SRVs[] = { nullptr, m_edgesSRV.Get(), nullptr, m_areaTexSRV.Get(), m_searchTexSRV.Get() }; + ID3D11ShaderResourceView* pass2SRVs[] = { nullptr, edgesTex->GetShaderResView().Get(), nullptr, m_areaTexSRV.Get(), m_searchTexSRV.Get() }; // We start at slot 0 to clear slot 0, or just bind specifically // Map: t1=Edges, t3=Area, t4=Search. t0 is unused here. m_context->PSSetShaderResources(0, 5, pass2SRVs); @@ -233,7 +195,7 @@ void D3D11SMAA::Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetVie m_context->VSSetShader(m_vsNeighbor.Get(), nullptr, 0); m_context->PSSetShader(m_psNeighbor.Get(), nullptr, 0); - ID3D11ShaderResourceView* pass3SRVs[] = { inputSRV, nullptr, m_blendSRV.Get() }; + ID3D11ShaderResourceView* pass3SRVs[] = { inputSRV, nullptr, blendTex->GetShaderResView().Get() }; m_context->PSSetShaderResources(0, 3, pass3SRVs); m_context->Draw(3, 0); diff --git a/D3D11Engine/SMAA/D3D11SMAA.h b/D3D11Engine/SMAA/D3D11SMAA.h index 74fc0876..0a4c8767 100644 --- a/D3D11Engine/SMAA/D3D11SMAA.h +++ b/D3D11Engine/SMAA/D3D11SMAA.h @@ -1,9 +1,11 @@ #pragma once +#include "../pch.h" #include #include #include #include +#include "../TexturePool.h" // Forward decl for texture loader if you use DirectXTK, otherwise assume generic // #include "DDSTextureLoader.h" @@ -23,7 +25,7 @@ class D3D11SMAA { // Main Render Function // inputSRV: The scene color texture (Gamma space usually required for Luma Edge Detect) // outputRTV: Where the anti-aliased image will be written - void Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV); + void Render(ID3D11ShaderResourceView* inputSRV, ID3D11RenderTargetView* outputRTV, TexturePool* pool); private: struct SMAAConstants { @@ -51,15 +53,6 @@ class D3D11SMAA { Microsoft::WRL::ComPtr m_areaTexSRV; Microsoft::WRL::ComPtr m_searchTexSRV; - // Render Targets (Intermediate) - Microsoft::WRL::ComPtr m_edgesTex; - Microsoft::WRL::ComPtr m_edgesRTV; - Microsoft::WRL::ComPtr m_edgesSRV; - - Microsoft::WRL::ComPtr m_blendTex; - Microsoft::WRL::ComPtr m_blendRTV; - Microsoft::WRL::ComPtr m_blendSRV; - // States Microsoft::WRL::ComPtr m_constantBuffer; Microsoft::WRL::ComPtr m_samplerLinear; @@ -70,4 +63,4 @@ class D3D11SMAA { int m_width; int m_height; -}; \ No newline at end of file +}; diff --git a/D3D11Engine/Shaders/GS_Cubemap.hlsl b/D3D11Engine/Shaders/GS_Cubemap.hlsl index dc7481df..cf9dd7bf 100644 --- a/D3D11Engine/Shaders/GS_Cubemap.hlsl +++ b/D3D11Engine/Shaders/GS_Cubemap.hlsl @@ -13,8 +13,8 @@ struct PS_INPUT float4 vDiffuse : TEXCOORD2; float3 vNormalVS : TEXCOORD4; float3 vViewPosition : TEXCOORD5; - float4 vCurrClipPos : TEXCOORD6; - float4 vPrevClipPos : TEXCOORD7; + //float4 vCurrClipPos : TEXCOORD6; + //float4 vPrevClipPos : TEXCOORD7; float4 vPosition : SV_POSITION; uint RTIndex : SV_RenderTargetArrayIndex; }; diff --git a/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl new file mode 100644 index 00000000..204f7eea --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExNodeInstanced.hlsl @@ -0,0 +1,79 @@ +//-------------------------------------------------------------------------------------- +// Instanced Node Attachment Vertex Shader +// For weapons, heads, and other attachments +// Uses an indirection buffer to map SV_InstanceID to actual instance index +//-------------------------------------------------------------------------------------- +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data from StructuredBuffer +struct NodeAttachmentInstance +{ + matrix World; + matrix PrevWorld; + float4 Color; + float Fatness; + float Scaling; + float2 Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index + +struct VS_INPUT +{ + float3 vPosition : POSITION; + float3 vNormal : NORMAL; + float2 vTex1 : TEXCOORD0; + float2 vTex2 : TEXCOORD1; + float4 vDiffuse : DIFFUSE; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float4 vCurrClipPos : TEXCOORD6; // Current clip position for velocity + float4 vPrevClipPos : TEXCOORD7; // Previous clip position for velocity + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + NodeAttachmentInstance inst = InstanceBuffer[actualInstanceIndex]; + + // Apply scaling and fatness + float3 position = Input.vPosition * inst.Scaling; + position += Input.vNormal * inst.Fatness; + + // Transform to world space + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex2; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + + // Motion Vectors - use UNJITTERED matrices for correct velocity + Output.vCurrClipPos = mul(float4(positionWorld, 1), frame.M_UnjitteredViewProj); + float3 prevPositionWorld = mul(float4(position, 1), inst.PrevWorld).xyz; + Output.vPrevClipPos = mul(float4(prevPositionWorld, 1), frame.M_PrevViewProj); + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl new file mode 100644 index 00000000..881fe6c5 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExNodeInstancedCube.hlsl @@ -0,0 +1,75 @@ +//-------------------------------------------------------------------------------------- +// Instanced Node Attachment Vertex Shader for Cube Shadow Maps +// For weapons, heads, and other attachments +// Uses an indirection buffer to map SV_InstanceID to actual instance index +//-------------------------------------------------------------------------------------- + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data from StructuredBuffer +struct NodeAttachmentInstance +{ + matrix World; + matrix PrevWorld; + float4 Color; + float Fatness; + float Scaling; + float2 Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index + +struct VS_INPUT +{ + float3 vPosition : POSITION; + float3 vNormal : NORMAL; + float2 vTex1 : TEXCOORD0; + float2 vTex2 : TEXCOORD1; + float4 vDiffuse : DIFFUSE; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float3 vWorldPosition : TEXCOORD6; + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + NodeAttachmentInstance inst = InstanceBuffer[actualInstanceIndex]; + + // Apply scaling and fatness + float3 position = Input.vPosition * inst.Scaling; + position += Input.vNormal * inst.Fatness; + + // Transform to world space + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex2; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + Output.vWorldPosition = positionWorld; // Needed for cube shadow map distance calculation + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl new file mode 100644 index 00000000..35841c50 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstanced.hlsl @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------------- +// Instanced Skeletal Vertex Shader +// Uses StructuredBuffers for per-instance data and bone transforms +// Uses an indirection buffer to map SV_InstanceID to actual instance index +//-------------------------------------------------------------------------------------- + +static const int NUM_MAX_BONES = 96; + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +// Per-instance data stored in StructuredBuffer +struct SkeletalInstanceData +{ + matrix World; + matrix PrevWorld; + float4 Color; + float Fatness; + float Scale; + uint BoneOffset; + uint Padding; +}; + +// Structured buffers for instanced data +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer BoneBuffer : register(t11); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index + +//-------------------------------------------------------------------------------------- +// Input / Output structures +//-------------------------------------------------------------------------------------- +struct VS_INPUT +{ + float4 vPosition[4] : POSITION; + float3 vNormal : NORMAL; + float3 vBindPoseNormal : TEXCOORD0; + float2 vTex1 : TEXCOORD1; + uint4 BoneIndices : BONEIDS; + float4 Weights : WEIGHTS; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float4 vCurrClipPos : TEXCOORD6; // Current clip position for velocity + float4 vPrevClipPos : TEXCOORD7; // Previous clip position for velocity + float4 vPosition : SV_POSITION; +}; + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + SkeletalInstanceData inst = InstanceBuffer[actualInstanceIndex]; + + // Calculate bone indices with offset for this instance + uint bone0 = inst.BoneOffset + Input.BoneIndices.x; + uint bone1 = inst.BoneOffset + Input.BoneIndices.y; + uint bone2 = inst.BoneOffset + Input.BoneIndices.z; + uint bone3 = inst.BoneOffset + Input.BoneIndices.w; + + // Skin the vertex position + float3 position = float3(0, 0, 0); + position += Input.Weights.x * mul(float4(Input.vPosition[0].xyz, 1), BoneBuffer[bone0]).xyz; + position += Input.Weights.y * mul(float4(Input.vPosition[1].xyz, 1), BoneBuffer[bone1]).xyz; + position += Input.Weights.z * mul(float4(Input.vPosition[2].xyz, 1), BoneBuffer[bone2]).xyz; + position += Input.Weights.w * mul(float4(Input.vPosition[3].xyz, 1), BoneBuffer[bone3]).xyz; + + // Skin the normal + float3 normal = float3(0, 0, 0); + normal += Input.Weights.x * mul(Input.vNormal, (float3x3)BoneBuffer[bone0]); + normal += Input.Weights.y * mul(Input.vNormal, (float3x3)BoneBuffer[bone1]); + normal += Input.Weights.z * mul(Input.vNormal, (float3x3)BoneBuffer[bone2]); + normal += Input.Weights.w * mul(Input.vNormal, (float3x3)BoneBuffer[bone3]); + + // Apply fatness displacement + position += inst.Fatness * normalize(normal); + + // Transform to world space using instance world matrix + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + // Output + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex1; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vBindPoseNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + + // Motion Vectors - use UNJITTERED matrices for correct velocity + Output.vCurrClipPos = mul(float4(positionWorld, 1), frame.M_UnjitteredViewProj); + float3 prevPositionWorld = mul(float4(position, 1), inst.PrevWorld).xyz; + Output.vPrevClipPos = mul(float4(prevPositionWorld, 1), frame.M_PrevViewProj); + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl new file mode 100644 index 00000000..a28a6461 --- /dev/null +++ b/D3D11Engine/Shaders/VS_ExSkeletalInstancedCube.hlsl @@ -0,0 +1,91 @@ +//-------------------------------------------------------------------------------------- +// Instanced Skeletal Vertex Shader for Cube Shadow Maps +// Uses an indirection buffer to map SV_InstanceID to actual instance index +//-------------------------------------------------------------------------------------- + +static const int NUM_MAX_BONES = 96; + +#include "Globals_VS_ExConstants.h" + +cbuffer Matrices_PerFrame : register( b0 ) +{ + VS_ExConstantBuffer_PerFrame frame; +}; + +struct SkeletalInstanceData +{ + matrix World; + matrix PrevWorld; + float4 Color; + float Fatness; + float Scale; + uint BoneOffset; + uint Padding; +}; + +StructuredBuffer InstanceBuffer : register(t10); +StructuredBuffer BoneBuffer : register(t11); +StructuredBuffer InstanceIndexBuffer : register(t12); // Indirection: SV_InstanceID -> actual instance index + +struct VS_INPUT +{ + float4 vPosition[4] : POSITION; + float3 vNormal : NORMAL; + float3 vBindPoseNormal : TEXCOORD0; + float2 vTex1 : TEXCOORD1; + uint4 BoneIndices : BONEIDS; + float4 Weights : WEIGHTS; +}; + +struct VS_OUTPUT +{ + float2 vTexcoord : TEXCOORD0; + float2 vTexcoord2 : TEXCOORD1; + float4 vDiffuse : TEXCOORD2; + float3 vNormalVS : TEXCOORD4; + float3 vViewPosition : TEXCOORD5; + float3 vWorldPosition : TEXCOORD6; + float4 vPosition : SV_POSITION; +}; + +VS_OUTPUT VSMain(VS_INPUT Input, uint instanceID : SV_InstanceID) +{ + VS_OUTPUT Output; + + // Use indirection buffer to get actual instance index + uint actualInstanceIndex = InstanceIndexBuffer[instanceID]; + + // Get instance data using the actual index + SkeletalInstanceData inst = InstanceBuffer[actualInstanceIndex]; + + uint bone0 = inst.BoneOffset + Input.BoneIndices.x; + uint bone1 = inst.BoneOffset + Input.BoneIndices.y; + uint bone2 = inst.BoneOffset + Input.BoneIndices.z; + uint bone3 = inst.BoneOffset + Input.BoneIndices.w; + + float3 position = float3(0, 0, 0); + position += Input.Weights.x * mul(float4(Input.vPosition[0].xyz, 1), BoneBuffer[bone0]).xyz; + position += Input.Weights.y * mul(float4(Input.vPosition[1].xyz, 1), BoneBuffer[bone1]).xyz; + position += Input.Weights.z * mul(float4(Input.vPosition[2].xyz, 1), BoneBuffer[bone2]).xyz; + position += Input.Weights.w * mul(float4(Input.vPosition[3].xyz, 1), BoneBuffer[bone3]).xyz; + + float3 normal = float3(0, 0, 0); + normal += Input.Weights.x * mul(Input.vNormal, (float3x3)BoneBuffer[bone0]); + normal += Input.Weights.y * mul(Input.vNormal, (float3x3)BoneBuffer[bone1]); + normal += Input.Weights.z * mul(Input.vNormal, (float3x3)BoneBuffer[bone2]); + normal += Input.Weights.w * mul(Input.vNormal, (float3x3)BoneBuffer[bone3]); + + position += inst.Fatness * normalize(normal); + + float3 positionWorld = mul(float4(position, 1), inst.World).xyz; + + Output.vPosition = mul(float4(positionWorld, 1), frame.M_ViewProj); + Output.vTexcoord = Input.vTex1; + Output.vTexcoord2 = Input.vTex1; + Output.vDiffuse = inst.Color; + Output.vNormalVS = mul(Input.vBindPoseNormal, (float3x3)mul(inst.World, frame.M_View)); + Output.vViewPosition = mul(float4(positionWorld, 1), frame.M_View).xyz; + Output.vWorldPosition = positionWorld; + + return Output; +} \ No newline at end of file diff --git a/D3D11Engine/Toolbox.h b/D3D11Engine/Toolbox.h index 371dc8b7..6c29c1e9 100644 --- a/D3D11Engine/Toolbox.h +++ b/D3D11Engine/Toolbox.h @@ -13,6 +13,26 @@ struct zTBBox3D; struct zTPlane; namespace Toolbox { + + inline UINT NextPowerOfTwo( UINT v ) { + if ( v == 0 ) return 1; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return v + 1; + } + + template + inline size_t hash_combine( size_t& seed, const T& v ) { + std::hash hasher; + // Boost's formula: seed ^= hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2) + seed ^= hasher( v ) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + FORCEINLINE float GetRecommendedWorldShadowRangeScaleForSize( int size ) { constexpr int MAX_SHADOWMAP_SIZE = 16384; return static_cast(MAX_SHADOWMAP_SIZE / size); diff --git a/D3D11Engine/VertexTypes.h b/D3D11Engine/VertexTypes.h index 5cb79946..cc4a9ec1 100644 --- a/D3D11Engine/VertexTypes.h +++ b/D3D11Engine/VertexTypes.h @@ -4,6 +4,47 @@ typedef unsigned short VERTEX_INDEX; +#include +#include + +template +struct SharedVector { + std::shared_ptr> data; + + SharedVector() : data( std::make_shared>() ) {} + explicit SharedVector( std::shared_ptr> p ) : data( p ) {} + + T& operator[]( size_t index ) { return (*data)[index]; } + const T& operator[]( size_t index ) const { return (*data)[index]; } + + T& at( size_t index ) { return data->at( index ); } + const T& at( size_t index ) const { return data->at( index ); } + + T& front() { return data->front(); } + T& back() { return data->back(); } + + size_t size() const { return data ? data->size() : 0; } + bool empty() const { return !data || data->empty(); } + void reserve( size_t n ) { if ( data ) data->reserve( n ); } + void resize( size_t n ) { if ( data ) data->resize( n ); } + + void push_back( const T& value ) { data->push_back( value ); } + void push_back( T&& value ) { data->push_back( std::move( value ) ); } + + template + void emplace_back( Args&&... args ) { data->emplace_back( std::forward( args )... ); } + + void pop_back() { data->pop_back(); } + void clear() { if ( data ) data->clear(); } + + auto begin() { return data->begin(); } + auto end() { return data->end(); } + auto begin() const { return data->begin(); } + auto end() const { return data->end(); } + + T* data_ptr() { return data ? data->data() : nullptr; } +}; + /** We pack most of Gothics FVF-formats into this vertex-struct */ struct ExVertexStruct { float3 Position; diff --git a/D3D11Engine/WorldConverter.cpp b/D3D11Engine/WorldConverter.cpp index e67657ed..4c4a7827 100644 --- a/D3D11Engine/WorldConverter.cpp +++ b/D3D11Engine/WorldConverter.cpp @@ -22,6 +22,9 @@ #include "D3D11Texture.h" #include "D3D7\MyDirectDrawSurface7.h" #include "zCQuadMark.h" +#include "MeshManager.h" + +extern MeshManager* s_MeshManager; WorldConverter::WorldConverter() {} @@ -87,12 +90,17 @@ void WorldConverter::WorldMeshCollectPolyRange( const float3& position, float ra std::vector vertices; IndexVertices( &it->second->Vertices[0], it->second->Vertices.size(), vertices, indices ); - it->second->Vertices = vertices; - it->second->Indices = indices; + it->second->Vertices = SharedVector{ std::make_shared>( vertices ) }; + it->second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it->second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it->second->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it->second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it->second->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them it->second->MeshVertexBuffer->Init( &it->second->Vertices[0], it->second->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -123,7 +131,9 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map for ( unsigned int m = 0; m < meshes.size(); m++ ) { auto& meshData = gm[textures[m]]; - meshData.emplace_back( std::make_pair( meshes[m]->Vertices, meshes[m]->Indices ) ); + meshData.emplace_back( std::make_pair( + std::vector(*meshes[m]->Vertices.data), + std::vector(*meshes[m]->Indices.data) ) ); } CacheMesh( gm, file + ".mcache" ); @@ -245,12 +255,18 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map std::vector indices; IndexVertices( &it.second->Vertices[0], it.second->Vertices.size(), indexedVertices, indices ); - it.second->Vertices = indexedVertices; - it.second->Indices = indices; + it.second->Vertices = SharedVector{ std::make_shared>( indexedVertices ) }; + it.second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshIndexBuffer ); + + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it.second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it.second->MeshIndexBuffer.reset( indexBuffer ); // Optimize faces it.second->MeshVertexBuffer->OptimizeFaces( &it.second->Indices[0], @@ -271,8 +287,8 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map it.second->MeshIndexBuffer->Init( &it.second->Indices[0], it.second->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); // Remember them, to wrap then up later - vertexBuffers.emplace_back( &it.second->Vertices ); - indexBuffers.emplace_back( &it.second->Indices ); + vertexBuffers.emplace_back( it.second->Vertices.data.get() ); + indexBuffers.emplace_back( it.second->Indices.data.get() ); } } } @@ -305,8 +321,13 @@ XRESULT WorldConverter::LoadWorldMeshFromFile( const std::string& file, std::map // Create the buffers for wrapped mesh MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -474,7 +495,7 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol } } - TriangleFanToList( &polyVertices[0], polyVertices.size(), &it->second->Vertices ); + TriangleFanToList( &polyVertices[0], polyVertices.size(), it->second->Vertices.data.get() ); if ( matGroup == zMAT_GROUP_WATER && !mat->HasAlphaTest() ) { #ifdef BUILD_GOTHIC_1_08k MaterialInfo* info = Engine::GAPI->GetMaterialInfoFrom( key.Texture ); @@ -520,15 +541,20 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol std::vector indices; IndexVertices( &it.second->Vertices[0], it.second->Vertices.size(), indexedVertices, indices ); - it.second->Vertices = indexedVertices; - it.second->Indices = indices; + it.second->Vertices = SharedVector{ std::make_shared>( indexedVertices ) }; + it.second->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &it.second->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + it.second->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + it.second->MeshIndexBuffer.reset( indexBuffer ); // Generate normals - GenerateVertexNormals( it.second->Vertices, it.second->Indices ); + GenerateVertexNormals( *it.second->Vertices.data, *it.second->Indices.data ); // Optimize faces it.second->MeshVertexBuffer->OptimizeFaces( &it.second->Indices[0], @@ -549,8 +575,8 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol it.second->MeshIndexBuffer->Init( &it.second->Indices[0], it.second->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); // Remember them, to wrap then up later - vertexBuffers.emplace_back( &it.second->Vertices ); - indexBuffers.emplace_back( &it.second->Indices ); + vertexBuffers.emplace_back( it.second->Vertices.data.get() ); + indexBuffers.emplace_back( it.second->Indices.data.get() ); } } } @@ -576,8 +602,13 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol // Create the buffers for wrapped mesh MeshInfo* wmi = new MeshInfo(); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -662,10 +693,13 @@ void WorldConverter::GenerateFullSectionMesh( WorldMeshSectionInfo& section ) { std::vector indices; section.FullStaticMesh = new MeshInfo; - section.FullStaticMesh->Vertices = vx; + section.FullStaticMesh->Vertices = SharedVector{ std::make_shared>( vx ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( §ion.FullStaticMesh->MeshVertexBuffer ); + + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + section.FullStaticMesh->MeshVertexBuffer.reset( vertexBuffer ); // Init and fill them section.FullStaticMesh->MeshVertexBuffer->Init( §ion.FullStaticMesh->Vertices[0], section.FullStaticMesh->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -755,13 +789,17 @@ void WorldConverter::Extract3DSMeshFromVisual( zCProgMeshProto* visual, MeshVisu zCMaterial* mat = visual->GetSubmesh( i )->Material; MeshInfo* mi = new MeshInfo; - - mi->Vertices = vertices; - mi->Indices = indices; + mi->Vertices = SharedVector{ std::make_shared>( vertices ) }; + mi->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + mi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + mi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill it mi->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); @@ -869,30 +907,69 @@ void WorldConverter::ExtractSkeletalMeshFromVob( zCModel* model, SkeletalMeshVis pvx.Color = 0xFFFFFFFF; } - zCMaterial* mat = s->GetSubmesh( i )->Material; + zCMaterial* mat = m->Material; SkeletalMeshInfo* mi = new SkeletalMeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; mi->visual = s; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + std::shared_ptr skeltalMeshData = s_MeshManager->GetSkeletalData( m ); + if ( skeltalMeshData == nullptr ) { + + auto meshData = new SkeletalMeshData; + // Create the buffers + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + // Init and fill it + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutSkeletalData( m, meshData ); + skeltalMeshData = s_MeshManager->GetSkeletalData( m ); + } + mi->Vertices = skeltalMeshData->Vertices; + mi->Indices = skeltalMeshData->Indices; + mi->MeshIndexBuffer = skeltalMeshData->MeshIndexBuffer; + mi->MeshVertexBuffer = skeltalMeshData->MeshVertexBuffer; - // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExSkelVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); MeshInfo* bmi = new MeshInfo; - bmi->Indices = indices; - bmi->Vertices = bindPoseVertices; - Engine::GraphicsEngine->CreateVertexBuffer( &bmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &bmi->MeshIndexBuffer ); + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + meshData->Vertices = SharedVector{ std::make_shared>( bindPoseVertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - bmi->MeshVertexBuffer->Init( &bmi->Vertices[0], bmi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - bmi->MeshIndexBuffer->Init( &bmi->Indices[0], bmi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + s_MeshManager->PutStaticData( m, meshData ); + staticMeshData = s_MeshManager->GetStaticData( m ); + } + bmi->Vertices = staticMeshData->Vertices; + bmi->Indices = staticMeshData->Indices; + bmi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; + bmi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); @@ -974,8 +1051,6 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn vx.Color = 0xFFFFFFFF; } - // Create the buffers and sort the mesh into the structure - MeshInfo* mi = new MeshInfo; // Create the indexed mesh if ( vertices.empty() ) { @@ -983,30 +1058,52 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn continue; } - mi->Vertices = vertices; - mi->Indices = indices; + // Create the buffers and sort the mesh into the structure + MeshInfo* mi = new MeshInfo; + mi->MeshIndex = i; - // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); - - // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); - - // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( m ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; + + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + // Optimize faces + mi->MeshVertexBuffer->OptimizeFaces( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), + sizeof( ExVertexStruct ) ); + + // Then optimize vertices + mi->MeshVertexBuffer->OptimizeVertices( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), + sizeof( ExVertexStruct ) ); + + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + meshData->MeshVertexBuffer->Init( &meshData->Vertices[0], meshData->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &meshData->Indices[0], meshData->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + s_MeshManager->PutStaticData( m, meshData ); + staticMeshData = s_MeshManager->GetStaticData( m ); + } // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + mi->Vertices = staticMeshData->Vertices; + mi->Indices = staticMeshData->Indices; + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); @@ -1021,8 +1118,8 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn meshInfo->MeshesByTexture[key].emplace_back( mi ); - vertexBuffers.emplace_back( &mi->Vertices ); - indexBuffers.emplace_back( &mi->Indices ); + vertexBuffers.emplace_back( mi->Vertices.data.get() ); + indexBuffers.emplace_back( mi->Indices.data.get() ); meshInfos.emplace_back( mi ); } } @@ -1042,8 +1139,13 @@ void WorldConverter::ExtractProgMeshProtoFromModel( zCModel* model, MeshVisualIn } MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); @@ -1097,12 +1199,17 @@ void WorldConverter::ExtractProgMeshProtoFromMesh( zCMesh* mesh, MeshVisualInfo* } MeshInfo* mi = new MeshInfo; - mi->Vertices = vertices; - mi->Indices = indices; + mi->Vertices = SharedVector{ std::make_shared>( vertices ) }; + mi->Indices = SharedVector{ std::make_shared>( indices ) }; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + mi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + mi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill it mi->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ) ); @@ -1242,8 +1349,6 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis bbmax.z = bbmax.z < vx.Position.z ? vx.Position.z : bbmax.z; } - // Create the buffers and sort the mesh into the structure - MeshInfo* mi = new MeshInfo; // Create the indexed mesh if ( vertices.empty() ) { @@ -1251,44 +1356,60 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis continue; } - mi->Vertices = vertices; - mi->Indices = indices; + // Create the buffers and sort the mesh into the structure + MeshInfo* mi = new MeshInfo; mi->MeshIndex = i; // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &mi->MeshIndexBuffer ); - if ( meshInfo->MorphMeshVisual ) { - // We need to keep original indices so that we can reuse them(we can't optimize them) - // Use dynamic buffer since we'll reupload it every frame we see this visual + std::shared_ptr staticMeshData = s_MeshManager->GetStaticData( s ); + if ( staticMeshData == nullptr ) { + auto meshData = new StaticMeshData; - // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_DYNAMIC, D3D11VertexBuffer::CA_WRITE ); - } else { - // Optimize faces - mi->MeshVertexBuffer->OptimizeFaces(&mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); - - // Then optimize vertices - mi->MeshVertexBuffer->OptimizeVertices( &mi->Indices[0], - reinterpret_cast(&mi->Vertices[0]), - mi->Indices.size(), - mi->Vertices.size(), - sizeof( ExVertexStruct ) ); + { + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + meshData->MeshVertexBuffer.reset( vertexBuffer ); - // Init and fill it - mi->MeshVertexBuffer->Init( &mi->Vertices[0], mi->Vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + meshData->MeshIndexBuffer.reset( indexBuffer ); + } + + if ( !meshInfo->MorphMeshVisual ) { + // Optimize faces + mi->MeshVertexBuffer->OptimizeFaces( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), + sizeof( ExVertexStruct ) ); + + // Then optimize vertices + mi->MeshVertexBuffer->OptimizeVertices( &indices[0], + reinterpret_cast(&vertices[0]), + indices.size(), + vertices.size(), + sizeof( ExVertexStruct ) ); + } + + meshData->MeshVertexBuffer->Init( &vertices[0], vertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->MeshIndexBuffer->Init( &indices[0], indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + meshData->Vertices = SharedVector{ std::make_shared>( vertices ) }; + meshData->Indices = SharedVector{ std::make_shared>( indices ) }; + s_MeshManager->PutStaticData( s, meshData ); + staticMeshData = s_MeshManager->GetStaticData( s ); } - mi->MeshIndexBuffer->Init( &mi->Indices[0], mi->Indices.size() * sizeof( VERTEX_INDEX ), D3D11VertexBuffer::B_INDEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); + + // Init and fill it + mi->MeshVertexBuffer = staticMeshData->MeshVertexBuffer; + mi->MeshIndexBuffer = staticMeshData->MeshIndexBuffer; + mi->Vertices = staticMeshData->Vertices; + mi->Indices = staticMeshData->Indices; Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Vertices.size() * sizeof( ExVertexStruct ); Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize += mi->Indices.size() * sizeof( VERTEX_INDEX ); - zCMaterial* mat = visual->GetSubmesh( i )->Material; + zCMaterial* mat = s->Material; meshInfo->Meshes[mat].emplace_back( mi ); MeshKey key; @@ -1298,8 +1419,8 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis meshInfo->MeshesByTexture[key].emplace_back( mi ); - vertexBuffers.emplace_back( &mi->Vertices ); - indexBuffers.emplace_back( &mi->Indices ); + vertexBuffers.emplace_back( mi->Vertices.data.get() ); + indexBuffers.emplace_back( mi->Indices.data.get() ); meshInfos.emplace_back( mi ); } @@ -1320,8 +1441,13 @@ void WorldConverter::Extract3DSMeshFromVisual2( zCProgMeshProto* visual, MeshVis } MeshInfo* wmi = new MeshInfo; - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &wmi->MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + wmi->MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + wmi->MeshIndexBuffer.reset( indexBuffer ); // Init and fill them wmi->MeshVertexBuffer->Init( &wrappedVertices[0], wrappedVertices.size() * sizeof( ExVertexStruct ), D3D11VertexBuffer::B_VERTEXBUFFER, D3D11VertexBuffer::U_IMMUTABLE ); diff --git a/D3D11Engine/WorldConverter.h b/D3D11Engine/WorldConverter.h index 26f1576b..032eb156 100644 --- a/D3D11Engine/WorldConverter.h +++ b/D3D11Engine/WorldConverter.h @@ -22,6 +22,7 @@ class zCModel; class zCModelPrototype; class zCModelMeshLib; class zCMesh; + class WorldConverter { public: WorldConverter(); diff --git a/D3D11Engine/WorldObjects.cpp b/D3D11Engine/WorldObjects.cpp index d20aceb7..8c227a75 100644 --- a/D3D11Engine/WorldObjects.cpp +++ b/D3D11Engine/WorldObjects.cpp @@ -55,16 +55,16 @@ MeshInfo::~MeshInfo() { //Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize -= Indices.size() * sizeof(VERTEX_INDEX); //Engine::GAPI->GetRendererState().RendererInfo.VOBVerticesDataSize -= Vertices.size() * sizeof(ExVertexStruct); - delete MeshVertexBuffer; - delete MeshIndexBuffer; + MeshVertexBuffer.reset(); + MeshIndexBuffer.reset(); } SkeletalMeshInfo::~SkeletalMeshInfo() { Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize -= Indices.size() * sizeof( VERTEX_INDEX ); Engine::GAPI->GetRendererState().RendererInfo.SkeletalVerticesDataSize -= Vertices.size() * sizeof( ExSkelVertexStruct ); - delete MeshVertexBuffer; - delete MeshIndexBuffer; + MeshVertexBuffer.reset(); + MeshIndexBuffer.reset(); } /** Clears the cache for the given progmesh */ @@ -95,8 +95,13 @@ XRESULT MeshInfo::Create( ExVertexStruct* vertices, unsigned int numVertices, VE memcpy( &Indices[0], indices, numIndices * sizeof( VERTEX_INDEX ) ); // Create the buffers - Engine::GraphicsEngine->CreateVertexBuffer( &MeshVertexBuffer ); - Engine::GraphicsEngine->CreateVertexBuffer( &MeshIndexBuffer ); + D3D11VertexBuffer* vertexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &vertexBuffer ); + MeshVertexBuffer.reset( vertexBuffer ); + + D3D11VertexBuffer* indexBuffer; + Engine::GraphicsEngine->CreateVertexBuffer( &indexBuffer ); + MeshIndexBuffer.reset( indexBuffer ); // Init and fill it MeshVertexBuffer->Init( vertices, numVertices * sizeof( ExVertexStruct ) ); diff --git a/D3D11Engine/WorldObjects.h b/D3D11Engine/WorldObjects.h index 73f3ef98..9cf520d4 100644 --- a/D3D11Engine/WorldObjects.h +++ b/D3D11Engine/WorldObjects.h @@ -22,6 +22,7 @@ struct zCModelNodeInst; struct BspInfo; class zCQuadMark; struct MaterialInfo; +class zCSubMesh; struct ParticleRenderInfo { GothicBlendStateInfo BlendState; @@ -75,6 +76,22 @@ struct cmpMeshKey { } };*/ +// singleton data struct +struct StaticMeshData { + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; +}; + +// singleton data struct +struct SkeletalMeshData { + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; +}; + /** Holds information about a mesh, ready to be loaded into the renderer */ struct MeshInfo { MeshInfo() { @@ -86,13 +103,18 @@ struct MeshInfo { virtual ~MeshInfo(); + static MeshInfo* CreateStatic() { + auto info = new MeshInfo(); + return info; + } + /** Creates buffers for this mesh info */ XRESULT Create( ExVertexStruct* vertices, unsigned int numVertices, VERTEX_INDEX* indices, unsigned int numIndices ); - D3D11VertexBuffer* MeshVertexBuffer; - D3D11VertexBuffer* MeshIndexBuffer; - std::vector Vertices; - std::vector Indices; + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; unsigned int BaseIndexLocation; unsigned int MeshIndex; @@ -135,10 +157,10 @@ struct SkeletalMeshInfo { ~SkeletalMeshInfo(); - D3D11VertexBuffer* MeshVertexBuffer; - D3D11VertexBuffer* MeshIndexBuffer; - std::vector Vertices; - std::vector Indices; + std::shared_ptr MeshVertexBuffer; + std::shared_ptr MeshIndexBuffer; + SharedVector Vertices; + SharedVector Indices; /** Actual visual containing this */ zCMeshSoftSkin* visual; diff --git a/D3D11Engine/oCNPC.h b/D3D11Engine/oCNPC.h index a5c94d2e..4b1d9ae3 100644 --- a/D3D11Engine/oCNPC.h +++ b/D3D11Engine/oCNPC.h @@ -73,10 +73,10 @@ class oCNPC : public zCVob { int IsAPlayer() { return (this == *reinterpret_cast(GothicMemoryLocations::oCGame::Var_Player)); } - zSTRING GetName( int i = 0 ) { + std::string GetName( int i = 0 ) { zSTRING str; reinterpret_cast( GothicMemoryLocations::oCNPC::GetName )( this, 0, str, i ); - return str; + return std::string(str.ToChar()); } #ifndef BUILD_SPACER bool HasFlag( oCNPCFlags flag ) { diff --git a/D3D11Engine/zCMaterial.h b/D3D11Engine/zCMaterial.h index e881e66b..919d069b 100644 --- a/D3D11Engine/zCMaterial.h +++ b/D3D11Engine/zCMaterial.h @@ -90,8 +90,8 @@ class zCMaterial { } /** Returns the color-mod of this material */ - DWORD GetColor() { - return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMaterial::Offset_Color )); + zColor GetColor() { + return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMaterial::Offset_Color )); } /** Returns single texture, because not all seem to be animated and returned by GetAniTexture? */ diff --git a/D3D11Engine/zCMorphMesh.h b/D3D11Engine/zCMorphMesh.h index f6a18556..f2860715 100644 --- a/D3D11Engine/zCMorphMesh.h +++ b/D3D11Engine/zCMorphMesh.h @@ -9,6 +9,10 @@ class zCMorphMesh { public: + static const zCClassDef* GetStaticClassDef() { + return reinterpret_cast(GothicMemoryLocations::zCClassDef::zCMorphMesh); + } + zCProgMeshProto* GetMorphMesh() { return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCMorphMesh::Offset_MorphMesh )); } diff --git a/D3D11Engine/zCObject.h b/D3D11Engine/zCObject.h index 263d48cd..b1b68373 100644 --- a/D3D11Engine/zCObject.h +++ b/D3D11Engine/zCObject.h @@ -20,6 +20,16 @@ class zCObject { fn(this, arg); }*/ + static bool CheckInheritance( const zCClassDef* def, const zCClassDef* target ) { + while ( def ) { + if ( def == target ) { + return true; + } + def = def->baseClassDef; + } + return false; + } + int refCtr; VERTEX_INDEX hashIndex; zCObject* hashNext; diff --git a/D3D11Engine/zCProgMeshProto.h b/D3D11Engine/zCProgMeshProto.h index 70d58204..39e91b01 100644 --- a/D3D11Engine/zCProgMeshProto.h +++ b/D3D11Engine/zCProgMeshProto.h @@ -54,6 +54,10 @@ class zCSubMesh { class zCProgMeshProto : public zCVisual { public: + static const zCClassDef* GetStaticClassDef() { + return reinterpret_cast(GothicMemoryLocations::zCClassDef::zCProgMeshProto); + } + zCArrayAdapt* GetPositionList() { return reinterpret_cast*>(THISPTR_OFFSET( GothicMemoryLocations::zCProgMeshProto::Offset_PositionList )); } @@ -87,4 +91,8 @@ class zCProgMeshProto : public zCVisual { vertices->push_back( vx ); } } + + DWORD GetProgId() { + return *reinterpret_cast(THISPTR_OFFSET( GothicMemoryLocations::zCProgMeshProto::Offset_ProgId )); + } }; diff --git a/D3D11Engine/zCTexture.h b/D3D11Engine/zCTexture.h index 1781f956..e3c685ac 100644 --- a/D3D11Engine/zCTexture.h +++ b/D3D11Engine/zCTexture.h @@ -138,7 +138,6 @@ class zCTexture { return (flags & GothicMemoryLocations::zCTexture::Mask_FlagHasAlpha) != 0; } -private: const zSTRING& __GetName() { return reinterpret_cast( GothicMemoryLocations::zCObject::GetObjectName )( this ); } diff --git a/D3D11Engine/zCVisual.h b/D3D11Engine/zCVisual.h index b03fced0..3a5b0db9 100644 --- a/D3D11Engine/zCVisual.h +++ b/D3D11Engine/zCVisual.h @@ -71,6 +71,15 @@ class zCVisual { return VT_OTHER; } + /** Checks the inheritance chain and casts to T* if possible. Returns nullptr otherwise */ + template + T* As() { + zCClassDef* classDef = reinterpret_cast(this)->_GetClassDef(); + if ( zCObject::CheckInheritance( classDef, T::GetStaticClassDef() ) ) { + return reinterpret_cast(this); + } + return nullptr; + } private: zSTRING& __GetObjectName() { diff --git a/D3D11Engine/zCVob.h b/D3D11Engine/zCVob.h index 808f31d8..57db152f 100644 --- a/D3D11Engine/zCVob.h +++ b/D3D11Engine/zCVob.h @@ -369,24 +369,13 @@ class zCVob { template T* As() { zCClassDef* classDef = reinterpret_cast(this)->_GetClassDef(); - if ( CheckInheritance( classDef, T::GetStaticClassDef() ) ) { + if ( zCObject::CheckInheritance( classDef, T::GetStaticClassDef() ) ) { return reinterpret_cast(this); } return nullptr; } -protected: - - bool CheckInheritance( const zCClassDef* def, const zCClassDef* target ) { - while ( def ) { - if ( def == target ) { - return true; - } - def = def->baseClassDef; - } - return false; - } zSTRING& __GetObjectName() { - return reinterpret_cast( GothicMemoryLocations::zCObject::GetObjectName )( this ); + return reinterpret_cast(GothicMemoryLocations::zCObject::GetObjectName)(this); } };