From 730fcd058741592809259239e1011991203a7e9e Mon Sep 17 00:00:00 2001 From: turanszkij Date: Thu, 26 Mar 2020 22:43:20 +0000 Subject: [PATCH] editor: paint tool - vertexpaint, hairlength #94; hairparticle update; --- Editor/Editor.cpp | 197 +----------- Editor/MaterialWindow.cpp | 17 +- Editor/MaterialWindow.h | 1 - Editor/PaintToolWindow.cpp | 354 +++++++++++++++++++++- Editor/PaintToolWindow.h | 12 +- WickedEngine/hairparticleVS.hlsl | 1 - WickedEngine/hairparticle_simulateCS.hlsl | 7 +- WickedEngine/wiHairParticle.cpp | 20 ++ WickedEngine/wiHairParticle.h | 2 +- WickedEngine/wiVersion.cpp | 2 +- WickedEngine/wiWidget.cpp | 19 +- WickedEngine/wiWidget.h | 1 + 12 files changed, 408 insertions(+), 225 deletions(-) diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 02a0f6146..cb62d0b9b 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -793,6 +793,7 @@ void EditorComponent::Update(float dt) animWnd->Update(); weatherWnd->Update(); + paintToolWnd->Update(dt); selectionOutlineTimer += dt; @@ -888,16 +889,8 @@ void EditorComponent::Update(float dt) cinemaModeCheckBox->SetCheck(false); } - // by default, paint tool is on center of screen, this makes it easy to tweak radius with GUI: - paintToolWnd->pos.x = wiRenderer::GetDevice()->GetScreenWidth() * 0.5f; - paintToolWnd->pos.y = wiRenderer::GetDevice()->GetScreenHeight() * 0.5f; - if (!wiBackLog::isActive() && !GetGUI().HasFocus()) { - XMFLOAT4 currentMouse = wiInput::GetPointer(); - paintToolWnd->pos.x = currentMouse.x; - paintToolWnd->pos.y = currentMouse.y; - // Camera control: static XMFLOAT4 originalMouse = XMFLOAT4(0, 0, 0, 0); static bool camControlStart = true; @@ -906,6 +899,7 @@ void EditorComponent::Update(float dt) originalMouse = wiInput::GetPointer(); } + XMFLOAT4 currentMouse = wiInput::GetPointer(); float xDif = 0, yDif = 0; if (wiInput::Down(wiInput::MOUSE_BUTTON_MIDDLE)) @@ -1195,189 +1189,8 @@ void EditorComponent::Update(float dt) } - // Use Paint Tool or interact, but not both: - if (paintToolWnd->GetMode() != PaintToolWindow::MODE_DISABLED) - { - auto mode = paintToolWnd->GetMode(); - switch (mode) - { - case PaintToolWindow::MODE_SOFTBODY_PINNING: - case PaintToolWindow::MODE_SOFTBODY_PHYSICS: - { - for (size_t i = 0; i < scene.softbodies.GetCount(); ++i) - { - SoftBodyPhysicsComponent& softbody = scene.softbodies[i]; - if (softbody.vertex_positions_simulation.empty()) - continue; - - // Painting: - if (wiInput::Down(wiInput::MOUSE_BUTTON_LEFT)) - { - const float radius = paintToolWnd->GetRadius(); - const XMVECTOR C = XMLoadFloat2(&paintToolWnd->pos); - const XMMATRIX VP = camera.GetViewProjection(); - const XMVECTOR MUL = XMVectorSet(0.5f, -0.5f, 1, 1); - const XMVECTOR ADD = XMVectorSet(0.5f, 0.5f, 0, 0); - const XMVECTOR SCREEN = XMVectorSet((float)wiRenderer::GetDevice()->GetScreenWidth(), (float)wiRenderer::GetDevice()->GetScreenHeight(), 1, 1); - size_t j = 0; - for (auto& ind : softbody.physicsToGraphicsVertexMapping) - { - XMVECTOR P = softbody.vertex_positions_simulation[ind].LoadPOS(); - P = XMVector3TransformCoord(P, VP); - P = P * MUL + ADD; - P = P * SCREEN; - const float z = XMVectorGetZ(P); - if (z >= 0 && z <= 1 && XMVectorGetX(XMVector2Length(C - P)) <= radius) - { - softbody.weights[j] = (mode == PaintToolWindow::MODE_SOFTBODY_PINNING ? 0.0f : 1.0f); - softbody._flags |= SoftBodyPhysicsComponent::FORCE_RESET; - } - j++; - } - } - - // Visualizing: - Entity entity = scene.softbodies.GetEntity(i); - const MeshComponent& mesh = *scene.meshes.GetComponent(entity); - const XMMATRIX W = XMLoadFloat4x4(&softbody.worldMatrix); - for (size_t j = 0; j < mesh.indices.size(); j += 3) - { - const uint32_t graphicsIndex0 = mesh.indices[j + 0]; - const uint32_t graphicsIndex1 = mesh.indices[j + 1]; - const uint32_t graphicsIndex2 = mesh.indices[j + 2]; - const uint32_t physicsIndex0 = softbody.graphicsToPhysicsVertexMapping[graphicsIndex0]; - const uint32_t physicsIndex1 = softbody.graphicsToPhysicsVertexMapping[graphicsIndex1]; - const uint32_t physicsIndex2 = softbody.graphicsToPhysicsVertexMapping[graphicsIndex2]; - const float weight0 = softbody.weights[physicsIndex0]; - const float weight1 = softbody.weights[physicsIndex1]; - const float weight2 = softbody.weights[physicsIndex2]; - wiRenderer::RenderableTriangle tri; - if (softbody.vertex_positions_simulation.empty()) - { - XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh.vertex_positions[graphicsIndex0]), W)); - XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh.vertex_positions[graphicsIndex1]), W)); - XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh.vertex_positions[graphicsIndex2]), W)); - } - else - { - tri.positionA = softbody.vertex_positions_simulation[graphicsIndex0].pos; - tri.positionB = softbody.vertex_positions_simulation[graphicsIndex1].pos; - tri.positionC = softbody.vertex_positions_simulation[graphicsIndex2].pos; - } - if (weight0 == 0) - tri.colorA = XMFLOAT4(1, 1, 0, 1); - else - tri.colorA = XMFLOAT4(1, 1, 1, 0.5f); - if (weight1 == 0) - tri.colorB = XMFLOAT4(1, 1, 0, 1); - else - tri.colorB = XMFLOAT4(1, 1, 1, 0.5f); - if (weight2 == 0) - tri.colorC = XMFLOAT4(1, 1, 0, 1); - else - tri.colorC = XMFLOAT4(1, 1, 1, 0.5f); - wiRenderer::DrawTriangle(tri, true); - if (weight0 == 0 && weight1 == 0 && weight2 == 0) - { - tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 0, 0.8f); - wiRenderer::DrawTriangle(tri); - } - } - } - } - break; - - case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE: - case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE: - { - for (size_t i = 0; i < scene.hairs.GetCount(); ++i) - { - wiHairParticle& hair = scene.hairs[i]; - if (hair.meshID == INVALID_ENTITY) - continue; - - MeshComponent* mesh = scene.meshes.GetComponent(hair.meshID); - if (mesh == nullptr) - continue; - - Entity entity = scene.hairs.GetEntity(i); - const TransformComponent* transform = scene.transforms.GetComponent(entity); - if (transform == nullptr) - continue; - - const XMMATRIX W = XMLoadFloat4x4(&transform->world); - const bool painting = wiInput::Down(wiInput::MOUSE_BUTTON_LEFT); - - const float radius = paintToolWnd->GetRadius(); - const XMVECTOR C = XMLoadFloat2(&paintToolWnd->pos); - const XMMATRIX VP = camera.GetViewProjection(); - const XMVECTOR MUL = XMVectorSet(0.5f, -0.5f, 1, 1); - const XMVECTOR ADD = XMVectorSet(0.5f, 0.5f, 0, 0); - const XMVECTOR SCREEN = XMVectorSet((float)wiRenderer::GetDevice()->GetScreenWidth(), (float)wiRenderer::GetDevice()->GetScreenHeight(), 1, 1); - for (size_t j = 0; j < mesh->indices.size(); j += 3) - { - const uint32_t triangle[] = { - mesh->indices[j + 0], - mesh->indices[j + 1], - mesh->indices[j + 2], - }; - - if (painting) - { - bool hit = false; - for (int k = 0; k < arraysize(triangle); ++k) - { - const XMFLOAT3& pos = mesh->vertex_positions[triangle[k]]; - XMVECTOR P = XMLoadFloat3(&pos); - P = XMVector3Transform(P, W); - P = XMVector3TransformCoord(P, VP); - P = P * MUL + ADD; - P = P * SCREEN; - const float z = XMVectorGetZ(P); - if (z >= 0 && z <= 1 && XMVectorGetX(XMVector2Length(C - P)) <= radius) - { - if (mode == PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE) - { - hair.vertex_lengths[triangle[k]] = 1.0f; - } - else if (mode == PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE) - { - hair.vertex_lengths[triangle[k]] = 0; - } - hair._flags |= wiHairParticle::REBUILD_BUFFERS; - } - } - } - - wiRenderer::RenderableTriangle tri; - XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[0]]), W)); - XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[1]]), W)); - XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[2]]), W)); - wiRenderer::DrawTriangle(tri, true); - } - - for (size_t j = 0; j < hair.indices.size(); j += 3) - { - const uint32_t triangle[] = { - hair.indices[j + 0], - hair.indices[j + 1], - hair.indices[j + 2], - }; - - wiRenderer::RenderableTriangle tri; - XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[0]]), W)); - XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[1]]), W)); - XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[2]]), W)); - tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 1, 0.9f); - wiRenderer::DrawTriangle(tri, false); - } - } - } - break; - - } - } - else + // Interactions only when paint tool is disabled: + if (paintToolWnd->GetMode() == PaintToolWindow::MODE_DISABLED) { // Interact: if (hovered.entity != INVALID_ENTITY) @@ -1625,6 +1438,7 @@ void EditorComponent::Update(float dt) envProbeWnd->SetEntity(INVALID_ENTITY); forceFieldWnd->SetEntity(INVALID_ENTITY); cameraWnd->SetEntity(INVALID_ENTITY); + paintToolWnd->SetEntity(INVALID_ENTITY); } else { @@ -1650,6 +1464,7 @@ void EditorComponent::Update(float dt) envProbeWnd->SetEntity(picked.entity); forceFieldWnd->SetEntity(picked.entity); cameraWnd->SetEntity(picked.entity); + paintToolWnd->SetEntity(picked.entity); if (picked.subsetIndex >= 0) { diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp index 91bd70bd2..1d93d5c93 100644 --- a/Editor/MaterialWindow.cpp +++ b/Editor/MaterialWindow.cpp @@ -158,17 +158,6 @@ MaterialWindow::MaterialWindow(wiGUI* gui) : GUI(gui) }); materialWindow->AddWidget(metalnessSlider); - alphaSlider = new wiSlider(0, 1, 1.0f, 1000, "Alpha: "); - alphaSlider->SetTooltip("Adjusts the overall transparency of the surface. No effect when BlendMode is set to OPAQUE."); - alphaSlider->SetSize(XMFLOAT2(100, 30)); - alphaSlider->SetPos(XMFLOAT2(x, y += step)); - alphaSlider->OnSlide([&](wiEventArgs args) { - MaterialComponent* material = wiScene::GetScene().materials.GetComponent(entity); - if (material != nullptr) - material->SetOpacity(args.fValue); - }); - materialWindow->AddWidget(alphaSlider); - alphaRefSlider = new wiSlider(0, 1, 1.0f, 1000, "AlphaRef: "); alphaRefSlider->SetTooltip("Adjust the alpha cutoff threshold. Some performance optimizations will be disabled."); alphaRefSlider->SetSize(XMFLOAT2(100, 30)); @@ -311,8 +300,7 @@ MaterialWindow::MaterialWindow(wiGUI* gui) : GUI(gui) MaterialComponent* material = wiScene::GetScene().materials.GetComponent(entity); if (material != nullptr) { - XMFLOAT3 col = args.color.toFloat3(); - material->SetBaseColor(XMFLOAT4(col.x, col.y, col.z, material->GetOpacity())); + material->SetBaseColor(args.color.toFloat4()); } }); materialWindow->AddWidget(baseColorPicker); @@ -791,7 +779,6 @@ void MaterialWindow::SetEntity(Entity entity) roughnessSlider->SetValue(material->roughness); reflectanceSlider->SetValue(material->reflectance); metalnessSlider->SetValue(material->metalness); - alphaSlider->SetValue(material->GetOpacity()); refractionIndexSlider->SetValue(material->refractionIndex); emissiveSlider->SetValue(material->emissiveColor.w); sssSlider->SetValue(material->subsurfaceScattering); @@ -807,7 +794,7 @@ void MaterialWindow::SetEntity(Entity entity) baseColorPicker->SetEnabled(true); baseColorPicker->SetPickColor(wiColor::fromFloat4(material->baseColor)); emissiveColorPicker->SetEnabled(true); - emissiveColorPicker->SetPickColor(wiColor::fromFloat4(material->emissiveColor)); + emissiveColorPicker->SetPickColor(wiColor::fromFloat3(XMFLOAT3(material->emissiveColor.x, material->emissiveColor.y, material->emissiveColor.z))); blendModeComboBox->SetSelected((int)material->userBlendMode); shaderTypeComboBox->SetSelected(max(0, material->GetCustomShaderID() + 1)); diff --git a/Editor/MaterialWindow.h b/Editor/MaterialWindow.h index ef2146b0c..002395df7 100644 --- a/Editor/MaterialWindow.h +++ b/Editor/MaterialWindow.h @@ -35,7 +35,6 @@ public: wiSlider* roughnessSlider; wiSlider* reflectanceSlider; wiSlider* metalnessSlider; - wiSlider* alphaSlider; wiSlider* refractionIndexSlider; wiSlider* emissiveSlider; wiSlider* sssSlider; diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp index 79723eedf..631fea8a6 100644 --- a/Editor/PaintToolWindow.cpp +++ b/Editor/PaintToolWindow.cpp @@ -1,7 +1,8 @@ #include "stdafx.h" #include "PaintToolWindow.h" -#include "wiRenderer.h" +using namespace wiECS; +using namespace wiScene; using namespace wiGraphics; PaintToolWindow::PaintToolWindow(wiGUI* gui) : GUI(gui) @@ -9,7 +10,7 @@ PaintToolWindow::PaintToolWindow(wiGUI* gui) : GUI(gui) assert(GUI && "Invalid GUI!"); window = new wiWindow(GUI, "Paint Tool Window"); - window->SetSize(XMFLOAT2(400, 300)); + window->SetSize(XMFLOAT2(400, 500)); GUI->AddWidget(window); float x = 100; @@ -21,10 +22,12 @@ PaintToolWindow::PaintToolWindow(wiGUI* gui) : GUI(gui) modeComboBox->SetPos(XMFLOAT2(x, y += step)); modeComboBox->SetSize(XMFLOAT2(200, 28)); modeComboBox->AddItem("Disabled"); + modeComboBox->AddItem("Vertexcolor"); modeComboBox->AddItem("Softbody - Pinning"); modeComboBox->AddItem("Softbody - Physics"); modeComboBox->AddItem("Hairparticle - Add Triangle"); modeComboBox->AddItem("Hairparticle - Remove Triangle"); + modeComboBox->AddItem("Hairparticle - Length (Alpha)"); modeComboBox->SetSelected(0); window->AddWidget(modeComboBox); @@ -36,6 +39,27 @@ PaintToolWindow::PaintToolWindow(wiGUI* gui) : GUI(gui) radiusSlider->SetPos(XMFLOAT2(x, y += step)); window->AddWidget(radiusSlider); + amountSlider = new wiSlider(0, 1, 1, 10000, "Brush Amount: "); + amountSlider->SetTooltip("Set the brush amount. 0 = minimum affection, 1 = maximum affection"); + amountSlider->SetSize(XMFLOAT2(200, 20)); + amountSlider->SetPos(XMFLOAT2(x, y += step)); + window->AddWidget(amountSlider); + + falloffSlider = new wiSlider(0, 16, 0, 10000, "Brush Falloff: "); + falloffSlider->SetTooltip("Set the brush power. 0 = no falloff, 1 = linear falloff, more = falloff power"); + falloffSlider->SetSize(XMFLOAT2(200, 20)); + falloffSlider->SetPos(XMFLOAT2(x, y += step)); + window->AddWidget(falloffSlider); + + backfaceCheckBox = new wiCheckBox("Backfaces: "); + backfaceCheckBox->SetTooltip("Set whether to paint on backfaces of geometry or not"); + backfaceCheckBox->SetPos(XMFLOAT2(x, y += step)); + window->AddWidget(backfaceCheckBox); + + colorPicker = new wiColorPicker(GUI, "Color", false); + colorPicker->SetPos(XMFLOAT2(10, y += step)); + window->AddWidget(colorPicker); + window->Translate(XMFLOAT3((float)wiRenderer::GetDevice()->GetScreenWidth() - 550, 50, 0)); window->SetVisible(false); } @@ -46,12 +70,326 @@ PaintToolWindow::~PaintToolWindow() delete window; } -void PaintToolWindow::DrawBrush() const +void PaintToolWindow::Update(float dt) { - if (GetMode() == MODE_DISABLED) + rot -= dt; + // by default, paint tool is on center of screen, this makes it easy to tweak radius with GUI: + pos.x = wiRenderer::GetDevice()->GetScreenWidth() * 0.5f; + pos.y = wiRenderer::GetDevice()->GetScreenHeight() * 0.5f; + if (GUI->HasFocus() || wiBackLog::isActive() || entity == INVALID_ENTITY) return; - const float radius = GetRadius(); + auto pointer = wiInput::GetPointer(); + pos.x = pointer.x; + pos.y = pointer.y; + + const bool pointer_moved = wiMath::Distance(pos, posPrev) > 1.0f; + const bool painting = pointer_moved && wiInput::Down(wiInput::MOUSE_BUTTON_LEFT); + + if (wiInput::Down(wiInput::MOUSE_BUTTON_LEFT)) + { + posPrev = pos; + } + else + { + posPrev = XMFLOAT2(0, 0); + } + + const MODE mode = GetMode(); + const float radius = radiusSlider->GetValue(); + const float amount = amountSlider->GetValue(); + const float falloff = falloffSlider->GetValue(); + const wiColor color = colorPicker->GetPickColor(); + const XMFLOAT4 color_float = color.toFloat4(); + const bool backfaces = backfaceCheckBox->GetCheck(); + + Scene& scene = wiScene::GetScene(); + const CameraComponent& camera = wiRenderer::GetCamera(); + const XMVECTOR C = XMLoadFloat2(&pos); + const XMMATRIX VP = camera.GetViewProjection(); + const XMVECTOR MUL = XMVectorSet(0.5f, -0.5f, 1, 1); + const XMVECTOR ADD = XMVectorSet(0.5f, 0.5f, 0, 0); + const XMVECTOR SCREEN = XMVectorSet((float)wiRenderer::GetDevice()->GetScreenWidth(), (float)wiRenderer::GetDevice()->GetScreenHeight(), 1, 1); + + switch (mode) + { + case MODE_VERTEXCOLOR: + { + ObjectComponent* object = scene.objects.GetComponent(entity); + if (object == nullptr || object->meshID == INVALID_ENTITY) + break; + + MeshComponent* mesh = scene.meshes.GetComponent(object->meshID); + if (mesh == nullptr) + break; + + const TransformComponent* transform = scene.transforms.GetComponent(entity); + if (transform == nullptr) + break; + + const XMMATRIX W = XMLoadFloat4x4(&transform->world); + + bool rebuild = false; + for (size_t j = 0; j < mesh->indices.size(); j += 3) + { + const uint32_t triangle[] = { + mesh->indices[j + 0], + mesh->indices[j + 1], + mesh->indices[j + 2], + }; + + if (painting) + { + XMVECTOR P[arraysize(triangle)]; + for (int k = 0; k < arraysize(triangle); ++k) + { + const XMFLOAT3& pos = mesh->vertex_positions[triangle[k]]; + P[k] = XMLoadFloat3(&pos); + P[k] = XMVector3Transform(P[k], W); + P[k] = XMVector3TransformCoord(P[k], VP); + P[k] = P[k] * MUL + ADD; + P[k] = P[k] * SCREEN; + } + + bool culled = false; + if (!backfaces) + { + const XMVECTOR N = XMVector3Normalize(XMVector3Cross(P[1] - P[0], P[2] - P[1])); + culled = XMVectorGetZ(N) > 0; + } + + if (!culled) + { + for (int k = 0; k < arraysize(triangle); ++k) + { + const float z = XMVectorGetZ(P[k]); + const float dist = XMVectorGetX(XMVector2Length(C - P[k])); + if (z >= 0 && z <= 1 && dist <= radius) + { + if (mesh->vertex_colors.empty()) + { + mesh->vertex_colors.resize(mesh->vertex_positions.size()); + std::fill(mesh->vertex_colors.begin(), mesh->vertex_colors.end(), 0xFFFFFFFF); // fill white + } + wiColor vcol = mesh->vertex_colors[triangle[k]]; + const float affection = amount * std::powf(1 - (dist / radius), falloff); + vcol = wiColor::lerp(vcol, color, affection); + mesh->vertex_colors[triangle[k]] = vcol.rgba; + rebuild = true; + } + } + } + } + + wiRenderer::RenderableTriangle tri; + XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[0]]), W)); + XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[1]]), W)); + XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[2]]), W)); + tri.colorA.w = 0.8f; + tri.colorB.w = 0.8f; + tri.colorC.w = 0.8f; + wiRenderer::DrawTriangle(tri, true); + } + + if (rebuild) + { + mesh->CreateRenderData(); + } + } + break; + + case MODE_SOFTBODY_PINNING: + case MODE_SOFTBODY_PHYSICS: + { + ObjectComponent* object = scene.objects.GetComponent(entity); + if (object == nullptr || object->meshID == INVALID_ENTITY) + break; + + const MeshComponent* mesh = scene.meshes.GetComponent(object->meshID); + if (mesh == nullptr) + break; + + SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID); + if (softbody == nullptr || softbody->vertex_positions_simulation.empty()) + break; + + // Painting: + if (pointer_moved && wiInput::Down(wiInput::MOUSE_BUTTON_LEFT)) + { + size_t j = 0; + for (auto& ind : softbody->physicsToGraphicsVertexMapping) + { + XMVECTOR P = softbody->vertex_positions_simulation[ind].LoadPOS(); + P = XMVector3TransformCoord(P, VP); + P = P * MUL + ADD; + P = P * SCREEN; + const float z = XMVectorGetZ(P); + if (z >= 0 && z <= 1 && XMVectorGetX(XMVector2Length(C - P)) <= radius) + { + softbody->weights[j] = (mode == MODE_SOFTBODY_PINNING ? 0.0f : 1.0f); + softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET; + } + j++; + } + } + + // Visualizing: + const XMMATRIX W = XMLoadFloat4x4(&softbody->worldMatrix); + for (size_t j = 0; j < mesh->indices.size(); j += 3) + { + const uint32_t graphicsIndex0 = mesh->indices[j + 0]; + const uint32_t graphicsIndex1 = mesh->indices[j + 1]; + const uint32_t graphicsIndex2 = mesh->indices[j + 2]; + const uint32_t physicsIndex0 = softbody->graphicsToPhysicsVertexMapping[graphicsIndex0]; + const uint32_t physicsIndex1 = softbody->graphicsToPhysicsVertexMapping[graphicsIndex1]; + const uint32_t physicsIndex2 = softbody->graphicsToPhysicsVertexMapping[graphicsIndex2]; + const float weight0 = softbody->weights[physicsIndex0]; + const float weight1 = softbody->weights[physicsIndex1]; + const float weight2 = softbody->weights[physicsIndex2]; + wiRenderer::RenderableTriangle tri; + if (softbody->vertex_positions_simulation.empty()) + { + XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[graphicsIndex0]), W)); + XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[graphicsIndex1]), W)); + XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[graphicsIndex2]), W)); + } + else + { + tri.positionA = softbody->vertex_positions_simulation[graphicsIndex0].pos; + tri.positionB = softbody->vertex_positions_simulation[graphicsIndex1].pos; + tri.positionC = softbody->vertex_positions_simulation[graphicsIndex2].pos; + } + if (weight0 == 0) + tri.colorA = XMFLOAT4(1, 1, 0, 1); + else + tri.colorA = XMFLOAT4(1, 1, 1, 1); + if (weight1 == 0) + tri.colorB = XMFLOAT4(1, 1, 0, 1); + else + tri.colorB = XMFLOAT4(1, 1, 1, 1); + if (weight2 == 0) + tri.colorC = XMFLOAT4(1, 1, 0, 1); + else + tri.colorC = XMFLOAT4(1, 1, 1, 1); + wiRenderer::DrawTriangle(tri, true); + if (weight0 == 0 && weight1 == 0 && weight2 == 0) + { + tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 0, 0.8f); + wiRenderer::DrawTriangle(tri); + } + } + } + break; + + case MODE_HAIRPARTICLE_ADD_TRIANGLE: + case MODE_HAIRPARTICLE_REMOVE_TRIANGLE: + case MODE_HAIRPARTICLE_LENGTH: + { + wiHairParticle* hair = scene.hairs.GetComponent(entity); + if (hair == nullptr || hair->meshID == INVALID_ENTITY) + break; + + MeshComponent* mesh = scene.meshes.GetComponent(hair->meshID); + if (mesh == nullptr) + break; + + const TransformComponent* transform = scene.transforms.GetComponent(entity); + if (transform == nullptr) + break; + + const XMMATRIX W = XMLoadFloat4x4(&transform->world); + + for (size_t j = 0; j < mesh->indices.size(); j += 3) + { + const uint32_t triangle[] = { + mesh->indices[j + 0], + mesh->indices[j + 1], + mesh->indices[j + 2], + }; + + if (painting) + { + XMVECTOR P[arraysize(triangle)]; + for (int k = 0; k < arraysize(triangle); ++k) + { + const XMFLOAT3& pos = mesh->vertex_positions[triangle[k]]; + P[k] = XMLoadFloat3(&pos); + P[k] = XMVector3Transform(P[k], W); + P[k] = XMVector3TransformCoord(P[k], VP); + P[k] = P[k] * MUL + ADD; + P[k] = P[k] * SCREEN; + } + + bool culled = false; + if (!backfaces) + { + const XMVECTOR N = XMVector3Normalize(XMVector3Cross(P[1] - P[0], P[2] - P[1])); + culled = XMVectorGetZ(N) > 0; + } + + if (!culled) + { + for (int k = 0; k < arraysize(triangle); ++k) + { + const float z = XMVectorGetZ(P[k]); + const float dist = XMVectorGetX(XMVector2Length(C - P[k])); + if (z >= 0 && z <= 1 && dist <= radius) + { + switch (mode) + { + case MODE_HAIRPARTICLE_ADD_TRIANGLE: + hair->vertex_lengths[triangle[k]] = 1.0f; + break; + case MODE_HAIRPARTICLE_REMOVE_TRIANGLE: + hair->vertex_lengths[triangle[k]] = 0; + break; + case MODE_HAIRPARTICLE_LENGTH: + const float affection = amount * std::powf(1 - (dist / radius), falloff); + hair->vertex_lengths[triangle[k]] = wiMath::Lerp(hair->vertex_lengths[triangle[k]], color_float.w, affection); + // don't let it "remove" the vertex by keeping its length above zero: + // (because if removed, distribution also changes which might be distracting) + hair->vertex_lengths[triangle[k]] = wiMath::Clamp(hair->vertex_lengths[triangle[k]], 1.0f / 255.0f, 1.0f); + break; + } + hair->_flags |= wiHairParticle::REBUILD_BUFFERS; + } + } + } + } + + wiRenderer::RenderableTriangle tri; + XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[0]]), W)); + XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[1]]), W)); + XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[2]]), W)); + wiRenderer::DrawTriangle(tri, true); + } + + for (size_t j = 0; j < hair->indices.size(); j += 3) + { + const uint32_t triangle[] = { + hair->indices[j + 0], + hair->indices[j + 1], + hair->indices[j + 2], + }; + + wiRenderer::RenderableTriangle tri; + XMStoreFloat3(&tri.positionA, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[0]]), W)); + XMStoreFloat3(&tri.positionB, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[1]]), W)); + XMStoreFloat3(&tri.positionC, XMVector3Transform(XMLoadFloat3(&mesh->vertex_positions[triangle[2]]), W)); + tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 1, 0.9f); + wiRenderer::DrawTriangle(tri, false); + } + } + break; + + } +} +void PaintToolWindow::DrawBrush() const +{ + if (GetMode() == MODE_DISABLED || entity == INVALID_ENTITY || wiBackLog::isActive()) + return; + + const float radius = radiusSlider->GetValue(); const int segmentcount = 36; for (int i = 0; i < segmentcount; i += 1) { @@ -66,8 +404,6 @@ void PaintToolWindow::DrawBrush() const wiRenderer::DrawLine(line); } - static float rot = 0; - rot -= 0.008f; for (int i = 0; i < segmentcount; i += 2) { const float angle0 = rot + (float)i / (float)segmentcount * XM_2PI; @@ -85,7 +421,7 @@ PaintToolWindow::MODE PaintToolWindow::GetMode() const { return (MODE)modeComboBox->GetSelected(); } -float PaintToolWindow::GetRadius() const +void PaintToolWindow::SetEntity(wiECS::Entity value) { - return radiusSlider->GetValue(); + entity = value; } diff --git a/Editor/PaintToolWindow.h b/Editor/PaintToolWindow.h index 28a67c339..478a150e8 100644 --- a/Editor/PaintToolWindow.h +++ b/Editor/PaintToolWindow.h @@ -12,16 +12,24 @@ class wiButton; class PaintToolWindow { + float rot = 0; + XMFLOAT2 posPrev = XMFLOAT2(0, 0); public: PaintToolWindow(wiGUI* gui); ~PaintToolWindow(); wiGUI* GUI; + wiECS::Entity entity = wiECS::INVALID_ENTITY; wiWindow* window; wiComboBox* modeComboBox; wiSlider* radiusSlider; + wiSlider* amountSlider; + wiSlider* falloffSlider; + wiCheckBox* backfaceCheckBox; + wiColorPicker* colorPicker; + void Update(float dt); void DrawBrush() const; XMFLOAT2 pos = XMFLOAT2(0, 0); @@ -29,11 +37,13 @@ public: enum MODE { MODE_DISABLED, + MODE_VERTEXCOLOR, MODE_SOFTBODY_PINNING, MODE_SOFTBODY_PHYSICS, MODE_HAIRPARTICLE_ADD_TRIANGLE, MODE_HAIRPARTICLE_REMOVE_TRIANGLE, + MODE_HAIRPARTICLE_LENGTH, }; MODE GetMode() const; - float GetRadius() const; + void SetEntity(wiECS::Entity value); }; diff --git a/WickedEngine/hairparticleVS.hlsl b/WickedEngine/hairparticleVS.hlsl index 9cdc32d16..28401ad1c 100644 --- a/WickedEngine/hairparticleVS.hlsl +++ b/WickedEngine/hairparticleVS.hlsl @@ -57,7 +57,6 @@ VertexToPixel main(uint fakeIndex : SV_VERTEXID) float len = (binormal_length >> 24) & 0x000000FF; len /= 255.0f; - len += 1; len *= xLength; // expand the particle into a billboard cross section, the patch: diff --git a/WickedEngine/hairparticle_simulateCS.hlsl b/WickedEngine/hairparticle_simulateCS.hlsl index 35e13d4fd..7019825f8 100644 --- a/WickedEngine/hairparticle_simulateCS.hlsl +++ b/WickedEngine/hairparticle_simulateCS.hlsl @@ -6,6 +6,7 @@ RWSTRUCTUREDBUFFER(simulationBuffer, PatchSimulationData, 1); TYPEDBUFFER(meshIndexBuffer, uint, TEXSLOT_ONDEMAND0); RAWBUFFER(meshVertexBuffer_POS, TEXSLOT_ONDEMAND1); +TYPEDBUFFER(meshVertexBuffer_length, float, TEXSLOT_ONDEMAND2); #define NUM_LDS_FORCEFIELDS 32 struct LDS_ForceField @@ -55,6 +56,9 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 nor0 = unpack_unitvector(asuint(pos_nor0.w)); float3 nor1 = unpack_unitvector(asuint(pos_nor1.w)); float3 nor2 = unpack_unitvector(asuint(pos_nor2.w)); + float length0 = meshVertexBuffer_length[i0]; + float length1 = meshVertexBuffer_length[i1]; + float length2 = meshVertexBuffer_length[i2]; // random barycentric coords: float f = rand(seed, uv); @@ -71,6 +75,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 target = normalize(nor0 + f * (nor1 - nor0) + g * (nor2 - nor0)); float3 tangent = normalize((rand(seed, uv) < 0.5f ? pos_nor0.xyz : pos_nor2.xyz) - pos_nor1.xyz); float3 binormal = cross(target, tangent); + float strand_length = length0 + f * (length1 - length0) + g * (length2 - length0); uint tangent_random = 0; tangent_random |= (uint)((uint)(tangent.x * 127.5f + 127.5f) << 0); @@ -82,7 +87,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn binormal_length |= (uint)((uint)(binormal.x * 127.5f + 127.5f) << 0); binormal_length |= (uint)((uint)(binormal.y * 127.5f + 127.5f) << 8); binormal_length |= (uint)((uint)(binormal.z * 127.5f + 127.5f) << 16); - binormal_length |= (uint)(rand(seed, uv) * 255 * saturate(xHairRandomness)) << 24; + binormal_length |= (uint)(lerp(1, rand(seed, uv), saturate(xHairRandomness)) * strand_length * 255) << 24; // Identifies the hair strand root particle: const uint strandID = DTid.x * xHairSegmentCount; diff --git a/WickedEngine/wiHairParticle.cpp b/WickedEngine/wiHairParticle.cpp index d425521f0..3df3dc81b 100644 --- a/WickedEngine/wiHairParticle.cpp +++ b/WickedEngine/wiHairParticle.cpp @@ -107,8 +107,27 @@ void wiHairParticle::UpdateCPU(const TransformComponent& transform, const MeshCo } } + if (!vertex_lengths.empty()) + { + std::vector ulengths; + ulengths.reserve(vertex_lengths.size()); + for (auto& x : vertex_lengths) + { + ulengths.push_back(uint8_t(wiMath::Clamp(x, 0, 1) * 255.0f)); + } + + bd.MiscFlags = 0; + bd.BindFlags = BIND_SHADER_RESOURCE; + bd.Format = FORMAT_R8_UNORM; + bd.StructureByteStride = sizeof(uint8_t); + bd.ByteWidth = bd.StructureByteStride * (uint32_t)ulengths.size(); + SubresourceData initData; + initData.pSysMem = ulengths.data(); + device->CreateBuffer(&bd, &initData, &vertexBuffer_length); + } if (!indices.empty()) { + bd.MiscFlags = 0; bd.BindFlags = BIND_SHADER_RESOURCE; bd.Format = FORMAT_R32_UINT; bd.StructureByteStride = sizeof(uint32_t); @@ -163,6 +182,7 @@ void wiHairParticle::UpdateGPU(const MeshComponent& mesh, const MaterialComponen const GPUResource* res[] = { indexBuffer.IsValid() ? &indexBuffer : &mesh.indexBuffer, mesh.streamoutBuffer_POS.IsValid() ? &mesh.streamoutBuffer_POS : &mesh.vertexBuffer_POS, + &vertexBuffer_length }; device->BindResources(CS, res, TEXSLOT_ONDEMAND0, arraysize(res), cmd); diff --git a/WickedEngine/wiHairParticle.h b/WickedEngine/wiHairParticle.h index 2ea09d557..50f63dafd 100644 --- a/WickedEngine/wiHairParticle.h +++ b/WickedEngine/wiHairParticle.h @@ -51,7 +51,7 @@ public: XMFLOAT4X4 world; XMFLOAT4X4 worldPrev; AABB aabb; - std::vector indices; + std::vector indices; // it is dependent on vertex_lengths and contains triangles with non-zero lengths void Serialize(wiArchive& archive, uint32_t seed = 0); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 76f79d2de..fabc9eddc 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 39; // minor bug fixes, alterations, refactors, updates - const int revision = 27; + const int revision = 28; long GetVersion() diff --git a/WickedEngine/wiWidget.cpp b/WickedEngine/wiWidget.cpp index 1eba3f0e1..aea2f6df1 100644 --- a/WickedEngine/wiWidget.cpp +++ b/WickedEngine/wiWidget.cpp @@ -1800,6 +1800,16 @@ wiColorPicker::wiColorPicker(wiGUI* gui, const std::string& name, bool window_co FireEvents(); }); AddWidget(text_V); + + alphaSlider = new wiSlider(0, 255, 255, 255, ""); + alphaSlider->SetPos(XMFLOAT2(20, 230)); + alphaSlider->SetSize(XMFLOAT2(150, 18)); + alphaSlider->SetText("A: "); + alphaSlider->SetTooltip("Value for ALPHA - TRANSPARENCY channel (0-255)"); + alphaSlider->OnSlide([this](wiEventArgs args) { + FireEvents(); + }); + AddWidget(alphaSlider); } static const float colorpicker_center = 120; static const float colorpicker_radius_triangle = 68; @@ -2132,11 +2142,9 @@ void wiColorPicker::Render(const wiGUI* gui, CommandList cmd) const float _width = 20; Vertex vertices[] = { { XMFLOAT4(-_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) }, + { XMFLOAT4(_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) }, { XMFLOAT4(-_width, _width, 0, 1),XMFLOAT4(1,1,1,1) }, { XMFLOAT4(_width, _width, 0, 1),XMFLOAT4(1,1,1,1) }, - { XMFLOAT4(-_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) }, - { XMFLOAT4(_width, _width, 0, 1),XMFLOAT4(1,1,1,1) }, - { XMFLOAT4(_width, -_width, 0, 1),XMFLOAT4(1,1,1,1) }, }; GPUBufferDesc desc; @@ -2326,7 +2334,7 @@ wiColor wiColorPicker::GetPickColor() const source.s = saturation; source.v = luminance; rgb result = hsv2rgb(source); - return wiColor::fromFloat3(XMFLOAT3(result.r, result.g, result.b)); + return wiColor::fromFloat4(XMFLOAT4(result.r, result.g, result.b, alphaSlider->GetValue() / 255.0f)); } void wiColorPicker::SetPickColor(wiColor value) { @@ -2344,9 +2352,12 @@ void wiColorPicker::SetPickColor(wiColor value) } saturation = result.s; luminance = result.v; + alphaSlider->SetValue((float)value.getA()); } void wiColorPicker::FireEvents() { + if (onColorChanged == nullptr) + return; wiEventArgs args; args.color = GetPickColor(); onColorChanged(args); diff --git a/WickedEngine/wiWidget.h b/WickedEngine/wiWidget.h index eb16ec233..d6ca89eff 100644 --- a/WickedEngine/wiWidget.h +++ b/WickedEngine/wiWidget.h @@ -317,6 +317,7 @@ protected: wiTextInputField* text_H = nullptr; wiTextInputField* text_S = nullptr; wiTextInputField* text_V = nullptr; + wiSlider* alphaSlider = nullptr; void FireEvents(); public: