paint tool: axis locking, brush shape, brush rotation

This commit is contained in:
Turánszki János
2022-08-10 10:46:18 +02:00
parent bb1f66ee58
commit 0ee0098924
11 changed files with 225 additions and 30 deletions
+1 -2
View File
@@ -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";
+7
View File
@@ -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
+1 -1
View File
@@ -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;
+139 -15
View File
@@ -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));
}
+13
View File
@@ -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;
};
@@ -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
+22 -4
View File
@@ -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);
}
+27 -4
View File
@@ -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);
//}
}
+2
View File
@@ -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;
+4 -2
View File
@@ -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);
+1 -1
View File
@@ -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);