From 6fdbdf0bc8659bdb648447a0138c77334a0ea31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Sat, 10 Feb 2024 14:33:19 +0100 Subject: [PATCH 1/3] Path tracing: volumetric clouds and debug renderers --- WickedEngine/offlineshadercompiler.cpp | 1 + WickedEngine/shaders/Shaders_SOURCE.vcxitems | 4 + .../shaders/Shaders_SOURCE.vcxitems.filters | 3 + WickedEngine/shaders/lineardepthCS.hlsl | 35 +++++ WickedEngine/wiEnums.h | 1 + WickedEngine/wiRenderPath3D.cpp | 48 +++--- WickedEngine/wiRenderPath3D_PathTracing.cpp | 148 +++++++++++------- WickedEngine/wiRenderPath3D_PathTracing.h | 2 +- WickedEngine/wiRenderer.cpp | 45 ++++++ WickedEngine/wiRenderer.h | 5 + WickedEngine/wiVersion.cpp | 3 +- 11 files changed, 209 insertions(+), 86 deletions(-) create mode 100644 WickedEngine/shaders/lineardepthCS.hlsl diff --git a/WickedEngine/offlineshadercompiler.cpp b/WickedEngine/offlineshadercompiler.cpp index 7700444d6f..663872b6cb 100644 --- a/WickedEngine/offlineshadercompiler.cpp +++ b/WickedEngine/offlineshadercompiler.cpp @@ -134,6 +134,7 @@ wi::vector shaders = { {"emittedparticle_emitCS_volume", wi::graphics::ShaderStage::CS}, {"emittedparticle_finishUpdateCS", wi::graphics::ShaderStage::CS}, {"downsample4xCS", wi::graphics::ShaderStage::CS}, + {"lineardepthCS", wi::graphics::ShaderStage::CS}, {"depthoffield_prepassCS_earlyexit", wi::graphics::ShaderStage::CS}, {"depthoffield_mainCS_cheap", wi::graphics::ShaderStage::CS}, {"depthoffield_mainCS_earlyexit", wi::graphics::ShaderStage::CS }, diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems index fedde26177..872c04c3df 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems @@ -406,6 +406,10 @@ Compute 4.0 + + Compute + 4.0 + diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters index 204bf41713..19963ed2c3 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters @@ -1124,6 +1124,9 @@ PS + + CS + diff --git a/WickedEngine/shaders/lineardepthCS.hlsl b/WickedEngine/shaders/lineardepthCS.hlsl new file mode 100644 index 0000000000..0bb3182d2c --- /dev/null +++ b/WickedEngine/shaders/lineardepthCS.hlsl @@ -0,0 +1,35 @@ +#include "globals.hlsli" +#include "ShaderInterop_Postprocess.h" + +Texture2D input : register(t0); + +RWTexture2D output_lineardepth_mip0 : register(u0); +RWTexture2D output_lineardepth_mip1 : register(u1); +RWTexture2D output_lineardepth_mip2 : register(u2); +RWTexture2D output_lineardepth_mip3 : register(u3); +RWTexture2D output_lineardepth_mip4 : register(u4); + +[numthreads(POSTPROCESS_BLOCKSIZE, POSTPROCESS_BLOCKSIZE, 1)] +void main(uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID) +{ + const uint2 pixel = DTid.xy; + const float depth = input[pixel]; + float lineardepth = compute_lineardepth(depth) * GetCamera().z_far_rcp; + output_lineardepth_mip0[pixel] = lineardepth; + if (GTid.x % 2 == 0 && GTid.y % 2 == 0) + { + output_lineardepth_mip1[pixel / 2] = lineardepth; + } + if (GTid.x % 4 == 0 && GTid.y % 4 == 0) + { + output_lineardepth_mip2[pixel / 4] = lineardepth; + } + if (GTid.x % 8 == 0 && GTid.y % 8 == 0) + { + output_lineardepth_mip3[pixel / 8] = lineardepth; + } + if (GTid.x % 16 == 0 && GTid.y % 16 == 0) + { + output_lineardepth_mip4[pixel / 16] = lineardepth; + } +} diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h index 41779641a5..3c08b0ce6f 100644 --- a/WickedEngine/wiEnums.h +++ b/WickedEngine/wiEnums.h @@ -357,6 +357,7 @@ namespace wi::enums CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UNORM4, CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UINT4, CSTYPE_POSTPROCESS_DOWNSAMPLE4X, + CSTYPE_POSTPROCESS_LINEARDEPTH, CSTYPE_POSTPROCESS_NORMALSFROMDEPTH, CSTYPE_POSTPROCESS_SCREENSPACESHADOW, CSTYPE_POSTPROCESS_RTSHADOW, diff --git a/WickedEngine/wiRenderPath3D.cpp b/WickedEngine/wiRenderPath3D.cpp index bd027a71fb..e5544a2850 100644 --- a/WickedEngine/wiRenderPath3D.cpp +++ b/WickedEngine/wiRenderPath3D.cpp @@ -355,31 +355,31 @@ namespace wi scene->camera = *camera; scene->Update(dt * wi::renderer::GetGameSpeed()); - } - - // Frustum culling for main camera: - visibility_main.layerMask = getLayerMask(); - visibility_main.scene = scene; - visibility_main.camera = camera; - visibility_main.flags = wi::renderer::Visibility::ALLOW_EVERYTHING; - wi::renderer::UpdateVisibility(visibility_main); - if (visibility_main.planar_reflection_visible) - { - // Frustum culling for planar reflections: - camera_reflection = *camera; - camera_reflection.jitter = XMFLOAT2(0, 0); - camera_reflection.Reflect(visibility_main.reflectionPlane); - visibility_reflection.layerMask = getLayerMask(); - visibility_reflection.scene = scene; - visibility_reflection.camera = &camera_reflection; - visibility_reflection.flags = - wi::renderer::Visibility::ALLOW_OBJECTS | - wi::renderer::Visibility::ALLOW_EMITTERS | - wi::renderer::Visibility::ALLOW_HAIRS | - wi::renderer::Visibility::ALLOW_LIGHTS - ; - wi::renderer::UpdateVisibility(visibility_reflection); + // Frustum culling for main camera: + visibility_main.layerMask = getLayerMask(); + visibility_main.scene = scene; + visibility_main.camera = camera; + visibility_main.flags = wi::renderer::Visibility::ALLOW_EVERYTHING; + wi::renderer::UpdateVisibility(visibility_main); + + if (visibility_main.planar_reflection_visible) + { + // Frustum culling for planar reflections: + camera_reflection = *camera; + camera_reflection.jitter = XMFLOAT2(0, 0); + camera_reflection.Reflect(visibility_main.reflectionPlane); + visibility_reflection.layerMask = getLayerMask(); + visibility_reflection.scene = scene; + visibility_reflection.camera = &camera_reflection; + visibility_reflection.flags = + wi::renderer::Visibility::ALLOW_OBJECTS | + wi::renderer::Visibility::ALLOW_EMITTERS | + wi::renderer::Visibility::ALLOW_HAIRS | + wi::renderer::Visibility::ALLOW_LIGHTS + ; + wi::renderer::UpdateVisibility(visibility_reflection); + } } XMUINT2 internalResolution = GetInternalResolution(); diff --git a/WickedEngine/wiRenderPath3D_PathTracing.cpp b/WickedEngine/wiRenderPath3D_PathTracing.cpp index 58de18a90f..bad1a1fb34 100644 --- a/WickedEngine/wiRenderPath3D_PathTracing.cpp +++ b/WickedEngine/wiRenderPath3D_PathTracing.cpp @@ -42,6 +42,7 @@ namespace wi void RenderPath3D_PathTracing::ResizeBuffers() { + DeleteGPUResources(); GraphicsDevice* device = wi::graphics::GetDevice(); @@ -78,10 +79,15 @@ namespace wi desc.format = Format::R32_FLOAT; device->CreateTexture(&desc, nullptr, &traceDepth); device->SetName(&traceDepth, "traceDepth"); + device->CreateTexture(&desc, nullptr, &traceDepth); + device->SetName(&traceDepth, "traceDepth"); desc.format = Format::R8_UINT; device->CreateTexture(&desc, nullptr, &traceStencil); device->SetName(&traceStencil, "traceStencil"); + depthBuffer_Copy = traceDepth; + depthBuffer_Copy1 = traceDepth; + desc.layout = ResourceState::DEPTHSTENCIL; desc.bind_flags = BindFlag::DEPTH_STENCIL; desc.format = wi::renderer::format_depthbuffer_main; @@ -89,6 +95,26 @@ namespace wi device->SetName(&depthBuffer_Main, "depthBuffer_Main"); } } + { + TextureDesc desc; + desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; + desc.format = Format::R32_FLOAT; + desc.width = internalResolution.x; + desc.height = internalResolution.y; + desc.mip_levels = 5; + desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE; + device->CreateTexture(&desc, nullptr, &rtLinearDepth); + device->SetName(&rtLinearDepth, "rtLinearDepth"); + + for (uint32_t i = 0; i < desc.mip_levels; ++i) + { + int subresource_index; + subresource_index = device->CreateSubresource(&rtLinearDepth, SubresourceType::SRV, 0, 1, i, 1); + assert(subresource_index == i); + subresource_index = device->CreateSubresource(&rtLinearDepth, SubresourceType::UAV, 0, 1, i, 1); + assert(subresource_index == i); + } + } { TextureDesc desc; desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; @@ -174,7 +200,6 @@ namespace wi RenderPath3D::Update(dt); - #ifdef OPEN_IMAGE_DENOISE if (sam == target) { @@ -332,8 +357,8 @@ namespace wi wi::renderer::BindCameraCB( *camera, - *camera, - *camera, + camera_previous, + camera_reflection, cmd ); wi::renderer::BindCommonResources(cmd); @@ -412,70 +437,45 @@ namespace wi wi::renderer::BindCameraCB( *camera, - *camera, - *camera, + camera_previous, + camera_reflection, cmd ); wi::renderer::BindCommonResources(cmd); - // Lightshafts: - bool lightshafts = false; - if (depthBuffer_Main.IsValid()) - { - XMVECTOR sunDirection = XMLoadFloat3(&scene->weather.sunDirection); - if (getLightShaftsEnabled() && XMVectorGetX(XMVector3Dot(sunDirection, camera->GetAt())) > 0) - { - device->EventBegin("Light Shafts", cmd); - lightshafts = true; + wi::renderer::Postprocess_Lineardepth(traceDepth, rtLinearDepth, cmd); - // Render sun stencil cutout: - { - RenderPassImage rp[] = { - RenderPassImage::DepthStencil( - &depthBuffer_Main, - RenderPassImage::LoadOp::LOAD, - RenderPassImage::StoreOp::STORE, - ResourceState::DEPTHSTENCIL, - ResourceState::DEPTHSTENCIL, - ResourceState::DEPTHSTENCIL - ), - RenderPassImage::RenderTarget(&rtSun[0], RenderPassImage::LoadOp::CLEAR), - }; - device->RenderPassBegin(rp, arraysize(rp), cmd); - - Viewport vp; - vp.width = (float)depthBuffer_Main.GetDesc().width; - vp.height = (float)depthBuffer_Main.GetDesc().height; - device->BindViewports(1, &vp, cmd); - - Rect scissor = GetScissorInternalResolution(); - device->BindScissorRects(1, &scissor, cmd); - - wi::renderer::DrawSun(cmd); - - device->RenderPassEnd(cmd); - } + if (scene->weather.IsRealisticSky()) + { + wi::renderer::ComputeSkyAtmosphereSkyViewLut(cmd); - // Radial blur on the sun: - { - XMVECTOR sunPos = XMVector3Project(camera->GetEye() + sunDirection * camera->zFarP, 0, 0, - 1.0f, 1.0f, 0.1f, 1.0f, - camera->GetProjection(), camera->GetView(), XMMatrixIdentity()); - { - XMFLOAT2 sun; - XMStoreFloat2(&sun, sunPos); - wi::renderer::Postprocess_LightShafts( - getMSAASampleCount() > 1 ? rtSun_resolved : rtSun[0], - rtSun[1], - cmd, - sun, - getLightShaftsStrength() - ); - } - } - device->EventEnd(cmd); + if (scene->weather.IsRealisticSkyAerialPerspective()) + { + wi::renderer::ComputeSkyAtmosphereCameraVolumeLut(cmd); } } + if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective()) + { + wi::renderer::Postprocess_AerialPerspective( + aerialperspectiveResources, + cmd + ); + } + if (scene->weather.IsVolumetricClouds()) + { + wi::renderer::Postprocess_VolumetricClouds( + volumetriccloudResources, + cmd, + *camera, + camera_previous, + camera_reflection, + wi::renderer::GetTemporalAAEnabled() || getFSR2Enabled(), + scene->weather.volumetricCloudsWeatherMapFirst.IsValid() ? &scene->weather.volumetricCloudsWeatherMapFirst.GetTexture() : nullptr, + scene->weather.volumetricCloudsWeatherMapSecond.IsValid() ? &scene->weather.volumetricCloudsWeatherMapSecond.GetTexture() : nullptr + ); + } + + RenderLightShafts(cmd); // Composite other effects on top: { @@ -517,9 +517,29 @@ namespace wi device->EventEnd(cmd); } + // Blend Aerial Perspective on top: + if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective()) + { + device->EventBegin("Aerial Perspective Blend", cmd); + wi::image::Params fx; + fx.enableFullScreen(); + fx.blendFlag = wi::enums::BLENDMODE_PREMULTIPLIED; + wi::image::Draw(&aerialperspectiveResources.texture_output, fx, cmd); + device->EventEnd(cmd); + } + + // Blend the volumetric clouds on top: + if (scene->weather.IsVolumetricClouds()) + { + wi::renderer::Postprocess_VolumetricClouds_Upsample(volumetriccloudResources, cmd); + } + + wi::renderer::DrawDebugWorld(*scene, *camera, *this, cmd); + wi::renderer::DrawLightVisualizers(visibility_main, cmd); wi::renderer::DrawSpritesAndFonts(*scene, *camera, false, cmd); - if (lightshafts) + XMVECTOR sunDirection = XMLoadFloat3(&scene->weather.sunDirection); + if (getLightShaftsEnabled() && XMVectorGetX(XMVector3Dot(sunDirection, camera->GetAt())) > 0) { device->EventBegin("Contribute LightShafts", cmd); wi::image::Params fx; @@ -528,6 +548,14 @@ namespace wi wi::image::Draw(&rtSun[1], fx, cmd); device->EventEnd(cmd); } + if (getLensFlareEnabled()) + { + wi::renderer::DrawLensFlares( + visibility_main, + cmd, + scene->weather.IsVolumetricClouds() ? &volumetriccloudResources.texture_cloudMask : nullptr + ); + } device->RenderPassEnd(cmd); } diff --git a/WickedEngine/wiRenderPath3D_PathTracing.h b/WickedEngine/wiRenderPath3D_PathTracing.h index c901817e45..1bf9ad2cca 100644 --- a/WickedEngine/wiRenderPath3D_PathTracing.h +++ b/WickedEngine/wiRenderPath3D_PathTracing.h @@ -40,7 +40,7 @@ namespace wi float getDenoiserProgress() const { return denoiserProgress; } bool isDenoiserAvailable() const; - void resetProgress() { sam = -1; denoiserProgress = 0; } + void resetProgress() { sam = -1; denoiserProgress = 0; volumetriccloudResources.frame = 0; } uint8_t instanceInclusionMask_PathTrace = 0xFF; }; diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 9a62cb051d..6f247762d5 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -1015,6 +1015,7 @@ void LoadShaders() wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UNORM4], "upsample_bilateral_unorm4CS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_UPSAMPLE_BILATERAL_UINT4], "upsample_bilateral_uint4CS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_DOWNSAMPLE4X], "downsample4xCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_LINEARDEPTH], "lineardepthCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_NORMALSFROMDEPTH], "normalsfromdepthCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_POSTPROCESS_SCREENSPACESHADOW], "screenspaceshadowCS.cso"); }); @@ -16039,6 +16040,50 @@ void Postprocess_Downsample4x( device->EventEnd(cmd); } +void Postprocess_Lineardepth( + const Texture& input, + const Texture& output, + CommandList cmd +) +{ + device->EventBegin("Postprocess_Lineardepth", cmd); + + device->BindComputeShader(&shaders[CSTYPE_POSTPROCESS_LINEARDEPTH], cmd); + + const TextureDesc& desc = output.GetDesc(); + + device->BindResource(&input, 0, cmd); + + device->BindUAV(&output, 0, cmd, 0); + device->BindUAV(&output, 1, cmd, 1); + device->BindUAV(&output, 2, cmd, 2); + device->BindUAV(&output, 3, cmd, 3); + device->BindUAV(&output, 4, cmd, 4); + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&output, output.desc.layout, ResourceState::UNORDERED_ACCESS), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + device->Dispatch( + (desc.width + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE, + (desc.height + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE, + 1, + cmd + ); + + { + GPUBarrier barriers[] = { + GPUBarrier::Memory(), + GPUBarrier::Image(&output, ResourceState::UNORDERED_ACCESS, output.desc.layout), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + device->EventEnd(cmd); +} void Postprocess_NormalsFromDepth( const Texture& depthbuffer, const Texture& output, diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 730086d74e..2f4b944309 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -860,6 +860,11 @@ namespace wi::renderer const wi::graphics::Texture& output, wi::graphics::CommandList cmd ); + void Postprocess_Lineardepth( + const wi::graphics::Texture& input, + const wi::graphics::Texture& output, + wi::graphics::CommandList cmd + ); void Postprocess_NormalsFromDepth( const wi::graphics::Texture& depthbuffer, const wi::graphics::Texture& output, diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 69d593be7d..b0ca4df1ff 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 371; + const int revision = 372; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); @@ -114,6 +114,7 @@ Titoutan Willow Aldo lokimx +K. Osterman )"; return credits; From f33c4540e7709fd8f4259d24fc1486e797e44ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Sat, 10 Feb 2024 15:00:59 +0100 Subject: [PATCH 2/3] fix --- WickedEngine/wiRenderPath3D_PathTracing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WickedEngine/wiRenderPath3D_PathTracing.cpp b/WickedEngine/wiRenderPath3D_PathTracing.cpp index bad1a1fb34..abc433f94e 100644 --- a/WickedEngine/wiRenderPath3D_PathTracing.cpp +++ b/WickedEngine/wiRenderPath3D_PathTracing.cpp @@ -469,7 +469,7 @@ namespace wi *camera, camera_previous, camera_reflection, - wi::renderer::GetTemporalAAEnabled() || getFSR2Enabled(), + false, scene->weather.volumetricCloudsWeatherMapFirst.IsValid() ? &scene->weather.volumetricCloudsWeatherMapFirst.GetTexture() : nullptr, scene->weather.volumetricCloudsWeatherMapSecond.IsValid() ? &scene->weather.volumetricCloudsWeatherMapSecond.GetTexture() : nullptr ); From cd23be671978610df141ece14aeef8a0a467a1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Sat, 10 Feb 2024 16:07:00 +0100 Subject: [PATCH 3/3] fixes animationwnd --- Editor/AnimationWindow.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Editor/AnimationWindow.cpp b/Editor/AnimationWindow.cpp index e81e11a537..88720aa483 100644 --- a/Editor/AnimationWindow.cpp +++ b/Editor/AnimationWindow.cpp @@ -8,7 +8,7 @@ void AnimationWindow::Create(EditorComponent* _editor) { editor = _editor; wi::gui::Window::Create(ICON_ANIMATION " Animation", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); - SetSize(XMFLOAT2(520, 500)); + SetSize(XMFLOAT2(520, 540)); closeButton.SetTooltip("Delete AnimationComponent"); OnClose([=](wi::gui::EventArgs args) { @@ -975,10 +975,10 @@ void AnimationWindow::Create(EditorComponent* _editor) } // Example function to check if an entity already exists in the list -static bool EntityExistsInList(Entity entity, std::vector entityList) +static bool EntityExistsInList(Entity entity, const wi::vector& entityList) { // Iterate through the list of entities - for (Entity& e : entityList) + for (Entity e : entityList) { if (e == entity) { @@ -1007,7 +1007,7 @@ void AnimationWindow::SetEntity(Entity entity) if (animation != nullptr) { // Define a list of entities - std::vector bone_list; + wi::vector bone_list; // Add items to root bone name combo box. for (const AnimationComponent::AnimationChannel& channel : animation->channels) { @@ -1299,7 +1299,9 @@ void AnimationWindow::ResizeLayout() add(endInput); add(recordCombo); add(retargetCombo); - add(rootMotionCheckBox); + rootMotionCheckBox.SetPos(XMFLOAT2(margin_left, y)); + y += rootMotionCheckBox.GetSize().y; + y += padding; add(rootBoneComboBox); add_fullwidth(keyframesList); }