diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 1917be573..f2cf87562 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -452,9 +452,8 @@ void EditorComponent::Load() ss += wi::version::GetVersionString(); ss += "\nCreated by Turánszki János"; ss += "\n\nWebsite: https://wickedengine.net/"; - ss += "\nGithub page: https://github.com/turanszkij/WickedEngine"; + ss += "\nSource code: https://github.com/turanszkij/WickedEngine"; ss += "\nDiscord chat: https://discord.gg/CFjRYmE"; - ss += "\nYou can support the project on Patreon: https://www.patreon.com/wickedengine"; ss += "\n\nControls\n"; ss += "------------\n"; ss += "Move camera: WASD, or Contoller left stick or D-pad\n"; diff --git a/Editor/IconDefinitions.h b/Editor/IconDefinitions.h index 1cd34da42..693ea81e7 100644 --- a/Editor/IconDefinitions.h +++ b/Editor/IconDefinitions.h @@ -36,3 +36,10 @@ #define ICON_ROTATE ICON_FA_ROTATE_RIGHT #define ICON_TRANSLATE ICON_FA_UP_DOWN_LEFT_RIGHT #define ICON_CHECK ICON_FA_CHECK +#define ICON_DISABLED ICON_FA_BAN + +#define ICON_LEFT_RIGHT ICON_FA_LEFT_RIGHT +#define ICON_UP_DOWN ICON_FA_UP_DOWN +#define ICON_UPRIGHT_DOWNLEFT ICON_FA_UP_RIGHT_AND_DOWN_LEFT_FROM_CENTER +#define ICON_CIRCLE ICON_FA_CIRCLE +#define ICON_SQUARE ICON_FA_SQUARE_FULL diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index 2eb8dc9c1..6ed91bafd 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -96,7 +96,7 @@ void OptionsWindow::Create(EditorComponent* _editor) AddWidget(&fpsCheckBox); fpsCheckBox.SetCheck(editor->main->infoDisplay.fpsinfo); - otherinfoCheckBox.Create("Advanced: "); + otherinfoCheckBox.Create("Info: "); otherinfoCheckBox.SetTooltip("Toggle advanced data in the info display text in top left corner."); otherinfoCheckBox.OnClick([&](wi::gui::EventArgs args) { editor->main->infoDisplay.heap_allocation_counter = args.bValue; diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp index ed52b7e29..eafa20f49 100644 --- a/Editor/PaintToolWindow.cpp +++ b/Editor/PaintToolWindow.cpp @@ -14,7 +14,7 @@ void PaintToolWindow::Create(EditorComponent* _editor) editor = _editor; wi::gui::Window::Create("Paint Tool", wi::gui::Window::WindowControls::COLLAPSE); - SetSize(XMFLOAT2(360, 560)); + SetSize(XMFLOAT2(360, 600)); float x = 105; float y = 0; @@ -29,17 +29,17 @@ void PaintToolWindow::Create(EditorComponent* _editor) modeComboBox.SetTooltip("Choose paint tool mode"); modeComboBox.SetPos(XMFLOAT2(x, y)); modeComboBox.SetSize(XMFLOAT2(wid, hei)); - modeComboBox.AddItem("Disabled"); - modeComboBox.AddItem("Texture"); - modeComboBox.AddItem("Vertexcolor"); - modeComboBox.AddItem("Sculpting - Add"); - modeComboBox.AddItem("Sculpting - Subtract"); - modeComboBox.AddItem("Softbody - Pinning"); - modeComboBox.AddItem("Softbody - Physics"); - modeComboBox.AddItem("Hairparticle - Add Triangle"); - modeComboBox.AddItem("Hairparticle - Remove Triangle"); - modeComboBox.AddItem("Hairparticle - Length (Alpha)"); - modeComboBox.AddItem("Wind weight (Alpha)"); + modeComboBox.AddItem(ICON_DISABLED " Disabled"); + modeComboBox.AddItem(ICON_MATERIAL " Texture"); + modeComboBox.AddItem(ICON_MESH " Vertexcolor"); + modeComboBox.AddItem(ICON_MESH " Sculpting - Add"); + modeComboBox.AddItem(ICON_MESH " Sculpting - Subtract"); + modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Pinning"); + modeComboBox.AddItem(ICON_SOFTBODY " Softbody - Physics"); + modeComboBox.AddItem(ICON_HAIR " Hairparticle - Add Triangle"); + modeComboBox.AddItem(ICON_HAIR " Hairparticle - Remove Triangle"); + modeComboBox.AddItem(ICON_HAIR " Hairparticle - Length (Alpha)"); + modeComboBox.AddItem(ICON_MESH " Wind weight (Alpha)"); modeComboBox.SetSelected(0); modeComboBox.OnSelect([&](wi::gui::EventArgs args) { switch (args.iValue) @@ -115,6 +115,12 @@ void PaintToolWindow::Create(EditorComponent* _editor) spacingSlider.SetPos(XMFLOAT2(x, y += step)); AddWidget(&spacingSlider); + rotationSlider.Create(0, 1, 0, 10000, "Brush Rotation: "); + rotationSlider.SetTooltip("Brush rotation randomness. This will affect the splat mode brush texture."); + rotationSlider.SetSize(XMFLOAT2(wid, hei)); + rotationSlider.SetPos(XMFLOAT2(x, y += step)); + AddWidget(&rotationSlider); + backfaceCheckBox.Create("Backfaces: "); backfaceCheckBox.SetTooltip("Set whether to paint on backfaces of geometry or not"); backfaceCheckBox.SetSize(XMFLOAT2(hei, hei)); @@ -135,6 +141,16 @@ void PaintToolWindow::Create(EditorComponent* _editor) pressureCheckBox.SetCheck(false); AddWidget(&pressureCheckBox); + axisCombo.Create("Axis Lock: "); + axisCombo.SetTooltip("You can lock modification to an axis here."); + axisCombo.SetPos(XMFLOAT2(x, y)); + axisCombo.SetSize(XMFLOAT2(wid, hei)); + axisCombo.AddItem(ICON_DISABLED, (uint64_t)AxisLock::Disabled); + axisCombo.AddItem("X " ICON_LEFT_RIGHT, (uint64_t)AxisLock::X); + axisCombo.AddItem("Y " ICON_UP_DOWN, (uint64_t)AxisLock::Y); + axisCombo.AddItem("Z " ICON_UPRIGHT_DOWNLEFT, (uint64_t)AxisLock::Z); + AddWidget(&axisCombo); + colorPicker.SetPos(XMFLOAT2(5, y += step)); AddWidget(&colorPicker); @@ -159,6 +175,15 @@ void PaintToolWindow::Create(EditorComponent* _editor) textureSlotComboBox.SetSelected(0); AddWidget(&textureSlotComboBox); + brushShapeComboBox.Create("Brush Shape: "); + brushShapeComboBox.SetTooltip("Choose shape for brush masking effect"); + brushShapeComboBox.SetPos(XMFLOAT2(x, y += step)); + brushShapeComboBox.SetSize(XMFLOAT2(wid, hei)); + brushShapeComboBox.AddItem(ICON_CIRCLE); + brushShapeComboBox.AddItem(ICON_SQUARE); + brushShapeComboBox.SetSelected(0); + AddWidget(&brushShapeComboBox); + saveTextureButton.Create("Save Texture"); saveTextureButton.SetTooltip("Save edited texture."); saveTextureButton.SetSize(XMFLOAT2(wid, hei)); @@ -268,19 +293,32 @@ void PaintToolWindow::Update(float dt) if (GetMode() == MODE_TEXTURE) { + rotationSlider.SetVisible(true); textureSlotComboBox.SetVisible(true); + brushShapeComboBox.SetVisible(true); saveTextureButton.SetVisible(true); brushTextureButton.SetVisible(true); revealTextureButton.SetVisible(true); } else { + rotationSlider.SetVisible(false); textureSlotComboBox.SetVisible(false); + brushShapeComboBox.SetVisible(false); saveTextureButton.SetVisible(false); brushTextureButton.SetVisible(false); revealTextureButton.SetVisible(false); } + if (GetMode() == MODE_SCULPTING_ADD || GetMode() == MODE_SCULPTING_SUBTRACT) + { + axisCombo.SetVisible(true); + } + else + { + axisCombo.SetVisible(false); + } + rot -= dt; // by default, paint tool is on center of screen, this makes it easy to tweak radius with GUI: XMFLOAT2 posNew; @@ -333,6 +371,7 @@ void PaintToolWindow::Update(float dt) const XMVECTOR ADD = XMVectorSet(0.5f, 0.5f, 0, 0); const XMVECTOR SCREEN = XMVectorSet((float)editor->GetLogicalWidth(), (float)editor->GetLogicalHeight(), 1, 1); const XMVECTOR F = camera.GetAt(); + const float brush_rotation = wi::random::GetRandom(0.0f, rotationSlider.GetValue() * XM_2PI); switch (mode) { @@ -406,10 +445,16 @@ void PaintToolWindow::Update(float dt) PaintTextureCB cb; cb.xPaintBrushCenter = center; cb.xPaintBrushRadius = (uint32_t)pressure_radius; + if (brushShapeComboBox.GetSelected() == 1) + { + cb.xPaintBrushRadius = (uint)std::ceil((float(cb.xPaintBrushRadius) * 2 / std::sqrt(2.0f))); // square shape, diagonal dispatch size + } cb.xPaintBrushAmount = amount; cb.xPaintBrushFalloff = falloff; cb.xPaintBrushColor = color.rgba; cb.xPaintReveal = revealTex.IsValid() ? 1 : 0; + cb.xPaintBrushRotation = brush_rotation; + cb.xPaintBrushShape = (uint)brushShapeComboBox.GetSelected(); device->PushConstants(&cb, sizeof(cb), cmd); const uint diameter = cb.xPaintBrushRadius * 2; @@ -435,6 +480,8 @@ void PaintToolWindow::Update(float dt) paintrad.uvset = uvset; paintrad.dimensions.x = desc.width; paintrad.dimensions.y = desc.height; + paintrad.rotation = brush_rotation; + paintrad.shape = (uint)brushShapeComboBox.GetSelected(); wi::renderer::DrawPaintRadius(paintrad); } break; @@ -641,11 +688,32 @@ void PaintToolWindow::Update(float dt) const XMMATRIX W = XMLoadFloat4x4(&transform->world); - if (sculpting_normal.x < FLT_EPSILON && sculpting_normal.y < FLT_EPSILON && sculpting_normal.z < FLT_EPSILON) + XMVECTOR sculptDir; + AxisLock axis_lock = (AxisLock)axisCombo.GetItemUserData(axisCombo.GetSelected()); + switch (axis_lock) { - sculpting_normal = editor->hovered.normal; + default: + case PaintToolWindow::AxisLock::Disabled: + if (sculpting_normal.x < FLT_EPSILON && sculpting_normal.y < FLT_EPSILON && sculpting_normal.z < FLT_EPSILON) + { + sculpting_normal = editor->hovered.normal; + } + sculptDir = XMVector3TransformNormal(XMVector3Normalize(XMLoadFloat3(&sculpting_normal)), XMMatrixInverse(nullptr, W)); + break; + case PaintToolWindow::AxisLock::X: + sculpting_normal = XMFLOAT3(1, 0, 0); + sculptDir = XMLoadFloat3(&sculpting_normal); + break; + case PaintToolWindow::AxisLock::Y: + sculpting_normal = XMFLOAT3(0, 1, 0); + sculptDir = XMLoadFloat3(&sculpting_normal); + break; + case PaintToolWindow::AxisLock::Z: + sculpting_normal = XMFLOAT3(0, 0, 1); + sculptDir = XMLoadFloat3(&sculpting_normal); + break; } - XMVECTOR sculptDir = XMVector3TransformNormal(XMVector3Normalize(XMLoadFloat3(&sculpting_normal)), XMMatrixInverse(nullptr, W)); + bool rebuild = false; if (painting) @@ -1462,3 +1530,59 @@ void PaintToolWindow::ReplaceEditTextureSlot(wi::scene::MaterialComponent& mater material.textures[sel].resource.SetTexture(texture); material.SetDirty(); } + +void PaintToolWindow::ResizeLayout() +{ + wi::gui::Window::ResizeLayout(); + const float padding = 4; + const float width = GetWidgetAreaSize().x; + float y = padding; + + auto add = [&](wi::gui::Widget& widget) { + if (!widget.IsVisible()) + return; + const float margin_left = 110; + const float margin_right = 30; + widget.SetPos(XMFLOAT2(margin_left, y)); + widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y)); + y += widget.GetSize().y; + y += padding; + }; + auto add_right = [&](wi::gui::Widget& widget) { + if (!widget.IsVisible()) + return; + const float margin_right = 30; + widget.SetPos(XMFLOAT2(width - margin_right - widget.GetSize().x, y)); + y += widget.GetSize().y; + y += padding; + }; + auto add_fullwidth = [&](wi::gui::Widget& widget) { + if (!widget.IsVisible()) + return; + const float margin_left = padding; + const float margin_right = padding; + widget.SetPos(XMFLOAT2(margin_left, y)); + widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y)); + y += widget.GetSize().y; + y += padding; + }; + + add(modeComboBox); + add_fullwidth(infoLabel); + add(radiusSlider); + add(amountSlider); + add(falloffSlider); + add(spacingSlider); + add(rotationSlider); + add_right(backfaceCheckBox); + add_right(wireCheckBox); + add_right(pressureCheckBox); + add(textureSlotComboBox); + add(brushShapeComboBox); + add(axisCombo); + add(saveTextureButton); + add_right(brushTextureButton); + add_right(revealTextureButton); + + colorPicker.SetPos(XMFLOAT2(padding, y)); +} diff --git a/Editor/PaintToolWindow.h b/Editor/PaintToolWindow.h index 94536765e..9e453101a 100644 --- a/Editor/PaintToolWindow.h +++ b/Editor/PaintToolWindow.h @@ -38,14 +38,17 @@ public: wi::gui::Slider amountSlider; wi::gui::Slider falloffSlider; wi::gui::Slider spacingSlider; + wi::gui::Slider rotationSlider; wi::gui::CheckBox backfaceCheckBox; wi::gui::CheckBox wireCheckBox; wi::gui::CheckBox pressureCheckBox; wi::gui::ColorPicker colorPicker; wi::gui::ComboBox textureSlotComboBox; + wi::gui::ComboBox brushShapeComboBox; wi::gui::Button saveTextureButton; wi::gui::Button brushTextureButton; wi::gui::Button revealTextureButton; + wi::gui::ComboBox axisCombo; void Update(float dt); void DrawBrush(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const; @@ -69,7 +72,17 @@ public: MODE GetMode() const; void SetEntity(wi::ecs::Entity value, int subsetindex = -1); + enum class AxisLock + { + Disabled, + X, + Y, + Z + }; + wi::Archive* currentHistory = nullptr; void RecordHistory(bool start, wi::graphics::CommandList cmd = wi::graphics::CommandList()); void ConsumeHistoryOperation(wi::Archive& archive, bool undo); + + void ResizeLayout() override; }; diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index be5a09579..fb2c4218e 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -870,16 +870,23 @@ struct PaintTextureCB float xPaintBrushFalloff; uint xPaintBrushColor; uint xPaintReveal; + float xPaintBrushRotation; + + uint xPaintBrushShape; uint padding0; + uint padding1; + uint padding2; }; CBUFFER(PaintRadiusCB, CBSLOT_RENDERER_MISC) { uint2 xPaintRadResolution; uint2 xPaintRadCenter; + uint xPaintRadUVSET; float xPaintRadRadius; - uint2 pad; + float xPaintRadBrushRotation; + uint xPaintRadBrushShape; }; struct SkinningPushConstants diff --git a/WickedEngine/shaders/objectPS_paintradius.hlsl b/WickedEngine/shaders/objectPS_paintradius.hlsl index 9fd051acf..beac5b4ea 100644 --- a/WickedEngine/shaders/objectPS_paintradius.hlsl +++ b/WickedEngine/shaders/objectPS_paintradius.hlsl @@ -7,10 +7,28 @@ float4 main(PixelInput PSIn) : SV_TARGET { const float2 pixel = (xPaintRadUVSET == 0 ? PSIn.uvsets.xy : PSIn.uvsets.zw) * xPaintRadResolution; - const float dist = distance(pixel, xPaintRadCenter); - float circle = dist - xPaintRadRadius; - float3 color = circle < 0 ? float3(0, 0, 0) : float3(1, 1, 1); - float alpha = 1 - saturate(abs(circle) * 0.25f); + + const float2x2 rot = float2x2( + cos(xPaintRadBrushRotation), -sin(xPaintRadBrushRotation), + sin(xPaintRadBrushRotation), cos(xPaintRadBrushRotation) + ); + const float2 diff = mul(xPaintRadCenter - pixel, rot); + + float dist = 0; + switch (xPaintRadBrushShape) + { + default: + case 0: + dist = length(diff); + break; + case 1: + dist = max(abs(diff.x), abs(diff.y)); + break; + } + + float shape = dist - xPaintRadRadius; + float3 color = shape < 0 ? float3(0, 0, 0) : float3(1, 1, 1); + float alpha = 1 - saturate(abs(shape) * 0.25f); return float4(color, alpha); } diff --git a/WickedEngine/shaders/paint_textureCS.hlsl b/WickedEngine/shaders/paint_textureCS.hlsl index 9e9b29b38..e91d4aa1d 100644 --- a/WickedEngine/shaders/paint_textureCS.hlsl +++ b/WickedEngine/shaders/paint_textureCS.hlsl @@ -13,7 +13,27 @@ void main( uint3 DTid : SV_DispatchThreadID ) { const uint2 pixel = push.xPaintBrushCenter + DTid.xy - push.xPaintBrushRadius.xx; - const float2 brush_uv = (DTid.xy + 0.5) / (push.xPaintBrushRadius.xx * 2); + const float2x2 rot = float2x2( + cos(push.xPaintBrushRotation), -sin(push.xPaintBrushRotation), + sin(push.xPaintBrushRotation), cos(push.xPaintBrushRotation) + ); + const float2 diff = mul((float2)push.xPaintBrushCenter - (float2)pixel, rot); + + float dist = 0; + float radius = (float)push.xPaintBrushRadius; + switch (push.xPaintBrushShape) + { + default: + case 0: + dist = length(diff); + break; + case 1: + dist = max(abs(diff.x), abs(diff.y)); + radius /= SQRT2; + break; + } + + const float2 brush_uv = (diff / radius) * 0.5 + 0.5; const float2 brush_uv_quad_x = QuadReadAcrossX(brush_uv); const float2 brush_uv_quad_y = QuadReadAcrossY(brush_uv); const float2 brush_uv_dx = brush_uv - brush_uv_quad_x; @@ -29,9 +49,8 @@ void main( uint3 DTid : SV_DispatchThreadID ) const float2 reveal_uv_dy = reveal_uv - reveal_uv_quad_y; float4 reveal_color = texture_reveal.SampleGrad(sampler_linear_clamp, reveal_uv, reveal_uv_dx, reveal_uv_dy); - const float dist = distance((float2)pixel, (float2)push.xPaintBrushCenter); - const float affection = push.xPaintBrushAmount * pow(1 - saturate(dist / (float)push.xPaintBrushRadius), push.xPaintBrushFalloff); - if (affection > 0 && dist < (float)push.xPaintBrushRadius) + const float affection = push.xPaintBrushAmount * pow(1 - saturate(dist / radius), push.xPaintBrushFalloff); + if (affection > 0 && dist < radius) { if (push.xPaintReveal) { @@ -39,4 +58,8 @@ void main( uint3 DTid : SV_DispatchThreadID ) } output[pixel] = lerp(output[pixel], brush_color, affection); } + //else + //{ + // output[pixel] = float4(1,0,0,1); + //} } diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index a95d245aa..851240e82 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -5984,6 +5984,8 @@ void DrawDebugWorld( cb.xPaintRadCenter = x.center; cb.xPaintRadRadius = x.radius; cb.xPaintRadUVSET = x.uvset; + cb.xPaintRadBrushRotation = x.rotation; + cb.xPaintRadBrushShape = x.shape; device->BindDynamicConstantBuffer(cb, CB_GETBINDSLOT(PaintRadiusCB), cmd); ObjectPushConstants push; diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index b871d5c41..95ff1a483 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -920,8 +920,10 @@ namespace wi::renderer int subset = -1; uint32_t uvset = 0; float radius = 0; - XMUINT2 center; - XMUINT2 dimensions; + XMUINT2 center = {}; + XMUINT2 dimensions = {}; + float rotation = 0; + uint shape = 0; // 0: circle, 1 : square }; void DrawPaintRadius(const PaintRadius& paintrad); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index d2fa4c3ea..020e3142d 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 = 2; + const int revision = 3; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);