diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp
index 3278f6d3a..4bb98daeb 100644
--- a/Editor/Editor.cpp
+++ b/Editor/Editor.cpp
@@ -346,7 +346,6 @@ void EditorComponent::Load()
wi::scene::Scene& scene = GetCurrentScene();
wi::renderer::ClearWorld(scene);
optionsWnd.cameraWnd.SetEntity(INVALID_ENTITY);
- optionsWnd.paintToolWnd.SetEntity(INVALID_ENTITY);
componentsWnd.objectWnd.SetEntity(INVALID_ENTITY);
componentsWnd.meshWnd.SetEntity(INVALID_ENTITY, -1);
componentsWnd.lightWnd.SetEntity(INVALID_ENTITY);
@@ -1294,7 +1293,6 @@ void EditorComponent::Update(float dt)
if (translator.selected.empty())
{
optionsWnd.cameraWnd.SetEntity(INVALID_ENTITY);
- optionsWnd.paintToolWnd.SetEntity(INVALID_ENTITY);
componentsWnd.objectWnd.SetEntity(INVALID_ENTITY);
componentsWnd.emitterWnd.SetEntity(INVALID_ENTITY);
componentsWnd.hairWnd.SetEntity(INVALID_ENTITY);
@@ -1329,7 +1327,6 @@ void EditorComponent::Update(float dt)
}
optionsWnd.cameraWnd.SetEntity(picked.entity);
- optionsWnd.paintToolWnd.SetEntity(picked.entity, picked.subsetIndex);
componentsWnd.emitterWnd.SetEntity(picked.entity);
componentsWnd.hairWnd.SetEntity(picked.entity);
componentsWnd.lightWnd.SetEntity(picked.entity);
diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems
index 2cea43ad1..d7f154681 100644
--- a/Editor/Editor_SOURCE.vcxitems
+++ b/Editor/Editor_SOURCE.vcxitems
@@ -162,6 +162,10 @@
+
+ true
+ true
+
true
true
diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters
index c1db4dfcb..fc3bd904b 100644
--- a/Editor/Editor_SOURCE.vcxitems.filters
+++ b/Editor/Editor_SOURCE.vcxitems.filters
@@ -128,6 +128,7 @@
terrain
+
diff --git a/Editor/Editor_UWP.vcxproj b/Editor/Editor_UWP.vcxproj
index 7d98a3262..21fab2a9e 100644
--- a/Editor/Editor_UWP.vcxproj
+++ b/Editor/Editor_UWP.vcxproj
@@ -150,10 +150,6 @@
-
- true
- true
-
diff --git a/Editor/Editor_UWP.vcxproj.filters b/Editor/Editor_UWP.vcxproj.filters
index abc655120..a6218e95d 100644
--- a/Editor/Editor_UWP.vcxproj.filters
+++ b/Editor/Editor_UWP.vcxproj.filters
@@ -37,6 +37,5 @@
-
\ No newline at end of file
diff --git a/Editor/Editor_Windows.vcxproj b/Editor/Editor_Windows.vcxproj
index 65fdc5c4a..90cde43d4 100644
--- a/Editor/Editor_Windows.vcxproj
+++ b/Editor/Editor_Windows.vcxproj
@@ -139,10 +139,6 @@
Document
-
- Discomap
- true
-
diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp
index 986bc5d53..046704af6 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, 600));
+ SetSize(XMFLOAT2(360, 800));
float x = 105;
float y = 0;
@@ -98,16 +98,16 @@ void PaintToolWindow::Create(EditorComponent* _editor)
AddWidget(&radiusSlider);
amountSlider.Create(0, 1, 1, 10000, "Power: ");
- amountSlider.SetTooltip("Set the brush amount. 0 = minimum affection, 1 = maximum affection");
+ amountSlider.SetTooltip("Set the brush power amount. 0 = minimum affection, 1 = maximum affection");
amountSlider.SetSize(XMFLOAT2(wid, hei));
amountSlider.SetPos(XMFLOAT2(x, y += step));
AddWidget(&amountSlider);
- falloffSlider.Create(0, 16, 0, 10000, "Smoothness: ");
- falloffSlider.SetTooltip("Set the brush power. 0 = hard, greater values will be smoother");
- falloffSlider.SetSize(XMFLOAT2(wid, hei));
- falloffSlider.SetPos(XMFLOAT2(x, y += step));
- AddWidget(&falloffSlider);
+ smoothnessSlider.Create(0, 2, 1, 10000, "Smoothness: ");
+ smoothnessSlider.SetTooltip("Set the brush smoothness. 0 = hard, increase for more smoothness");
+ smoothnessSlider.SetSize(XMFLOAT2(wid, hei));
+ smoothnessSlider.SetPos(XMFLOAT2(x, y += step));
+ AddWidget(&smoothnessSlider);
spacingSlider.Create(0, 500, 1, 500, "Spacing: ");
spacingSlider.SetTooltip("Brush spacing means how much brush movement (in pixels) starts a new stroke. 0 = new stroke every frame, 100 = every 100 pixel movement since last stroke will start a new stroke.");
@@ -121,6 +121,20 @@ void PaintToolWindow::Create(EditorComponent* _editor)
rotationSlider.SetPos(XMFLOAT2(x, y += step));
AddWidget(&rotationSlider);
+ stabilizerSlider.Create(1, 15, 8, 14, "Stabilizer: ");
+ stabilizerSlider.SetTooltip("The stabilizer generates a small delay between user input and painting, which will be used to compute a smoother paint stroke..");
+ stabilizerSlider.SetSize(XMFLOAT2(wid, hei));
+ stabilizerSlider.SetPos(XMFLOAT2(x, y += step));
+ if (editor->main->config.GetSection("paint_tool").Has("stabilizer"))
+ {
+ stabilizerSlider.SetValue((float)editor->main->config.GetSection("paint_tool").GetInt("stabilizer"));
+ }
+ stabilizerSlider.OnSlide([=](wi::gui::EventArgs args) {
+ editor->main->config.GetSection("paint_tool").Set("stabilizer", args.iValue);
+ editor->main->config.Commit();
+ });
+ AddWidget(&stabilizerSlider);
+
backfaceCheckBox.Create("Backfaces: ");
backfaceCheckBox.SetTooltip("Set whether to paint on backfaces of geometry or not");
backfaceCheckBox.SetSize(XMFLOAT2(hei, hei));
@@ -215,30 +229,33 @@ void PaintToolWindow::Create(EditorComponent* _editor)
saveTextureButton.OnClick([this] (wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- return;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- return;
-
- MaterialComponent* material = subset >= 0 && subset < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[subset].materialID) : nullptr;
- if (material == nullptr)
- return;
-
- Texture editTexture = GetEditTextureSlot(*material);
-
- uint64_t sel = textureSlotComboBox.GetItemUserData(textureSlotComboBox.GetSelected());
-
- wi::vector texturefiledata;
- if (wi::helper::saveTextureToMemoryFile(editTexture, "PNG", texturefiledata))
+ for (auto& selected : editor->translator.selected)
{
- material->textures[sel].resource.SetFileData(texturefiledata);
- }
- else
- {
- wi::helper::messageBox("Saving texture failed! :(");
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ continue;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ continue;
+
+ MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ if (material == nullptr)
+ continue;
+
+ Texture editTexture = GetEditTextureSlot(*material);
+
+ uint64_t sel = textureSlotComboBox.GetItemUserData(textureSlotComboBox.GetSelected());
+
+ wi::vector texturefiledata;
+ if (wi::helper::saveTextureToMemoryFile(editTexture, "PNG", texturefiledata))
+ {
+ material->textures[sel].resource.SetFileData(texturefiledata);
+ }
+ else
+ {
+ wi::helper::messageBox("Saving texture failed! :(");
+ }
}
});
@@ -348,9 +365,10 @@ void PaintToolWindow::Update(float dt)
XMFLOAT2 posNew;
posNew.x = editor->GetLogicalWidth() * 0.5f;
posNew.y = editor->GetLogicalHeight() * 0.5f;
- if (editor->GetGUI().HasFocus() || wi::backlog::isActive() || entity == INVALID_ENTITY)
+ if (editor->GetGUI().HasFocus() || wi::backlog::isActive())
{
pos = posNew;
+ strokes.clear();
return;
}
@@ -359,175 +377,243 @@ void PaintToolWindow::Update(float dt)
{
stroke_dist = FLT_MAX;
sculpting_normal = XMFLOAT3(0, 0, 0);
+ if (!strokes.empty())
+ {
+ strokes.pop_front();
+ }
}
auto pointer = wi::input::GetPointer();
posNew = XMFLOAT2(pointer.x, pointer.y);
- stroke_dist += wi::math::Distance(pos, posNew);
- const float pressure = pressureCheckBox.GetCheck() ? pointer.w : 1.0f;
-
- const float spacing = spacingSlider.GetValue();
- const bool pointer_moved = stroke_dist >= spacing;
- const bool painting = pointer_moved && pointer_down;
-
- if (painting)
- {
- stroke_dist = 0;
- }
-
- pos = posNew;
+ float pressureNew = pressureCheckBox.GetCheck() ? pointer.w : 1.0f;
const MODE mode = GetMode();
const float radius = radiusSlider.GetValue();
- const float pressure_radius = radius * pressure;
const float amount = amountSlider.GetValue();
- const float falloff = falloffSlider.GetValue();
+ const float smoothness = smoothnessSlider.GetValue();
const wi::Color color = colorPicker.GetPickColor();
const XMFLOAT4 color_float = color.toFloat4();
const bool backfaces = backfaceCheckBox.GetCheck();
const bool wireframe = wireCheckBox.GetCheck();
+ const float spacing = spacingSlider.GetValue();
+ const size_t stabilizer = (size_t)stabilizerSlider.GetValue();
+ CommandList cmd;
- Scene& scene = editor->GetCurrentScene();
- const CameraComponent& camera = editor->GetCurrentEditorScene().camera;
- 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)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)
+ if (pointer_down)
{
- case MODE_TEXTURE:
- {
- const wi::scene::PickResult& intersect = editor->hovered;
- if (intersect.entity != entity)
- break;
-
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
-
- MaterialComponent* material = subset >= 0 && subset < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[subset].materialID) : nullptr;
- if (material == nullptr)
- break;
-
- int uvset = 0;
- Texture editTexture = GetEditTextureSlot(*material, &uvset);
- if (!editTexture.IsValid())
- break;
- const TextureDesc& desc = editTexture.GetDesc();
- auto& vertex_uvset = uvset == 0 ? mesh->vertex_uvset_0 : mesh->vertex_uvset_1;
-
- const float u = intersect.bary.x;
- const float v = intersect.bary.y;
- const float w = 1 - u - v;
- XMFLOAT2 uv;
- uv.x = vertex_uvset[intersect.vertexID0].x * w +
- vertex_uvset[intersect.vertexID1].x * u +
- vertex_uvset[intersect.vertexID2].x * v;
- uv.y = vertex_uvset[intersect.vertexID0].y * w +
- vertex_uvset[intersect.vertexID1].y * u +
- vertex_uvset[intersect.vertexID2].y * v;
- uint2 center = XMUINT2(uint32_t(uv.x * desc.width), uint32_t(uv.y * desc.height));
-
- if (painting)
- {
- GraphicsDevice* device = wi::graphics::GetDevice();
- CommandList cmd = device->BeginCommandList();
-
- RecordHistory(true, cmd);
-
- // Need to requery this because RecordHistory might swap textures on material:
- editTexture = GetEditTextureSlot(*material, &uvset);
-
- device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_PAINT_TEXTURE), cmd);
-
- if (brushTex.IsValid())
- {
- device->BindResource(&brushTex.GetTexture(), 0, cmd);
- }
- else
- {
- device->BindResource(wi::texturehelper::getWhite(), 0, cmd);
- }
- if (revealTex.IsValid())
- {
- device->BindResource(&revealTex.GetTexture(), 1, cmd);
- }
- else
- {
- device->BindResource(wi::texturehelper::getWhite(), 1, cmd);
- }
- device->BindUAV(&editTexture, 0, cmd);
-
- 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;
- const uint dispatch_dim = (diameter + PAINT_TEXTURE_BLOCKSIZE - 1) / PAINT_TEXTURE_BLOCKSIZE;
- device->Dispatch(dispatch_dim, dispatch_dim, 1, cmd);
-
- GPUBarrier barriers[] = {
- GPUBarrier::Memory(),
- };
- device->Barrier(barriers, arraysize(barriers), cmd);
-
- if (editTexture.desc.mip_levels > 1)
- {
- wi::renderer::GenerateMipChain(editTexture, wi::renderer::MIPGENFILTER::MIPGENFILTER_LINEAR, cmd);
- }
- }
-
- wi::renderer::PaintRadius paintrad;
- paintrad.objectEntity = entity;
- paintrad.subset = subset;
- paintrad.radius = radius;
- paintrad.center = center;
- 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);
+ Stroke stroke;
+ stroke.position = posNew;
+ stroke.pressure = pressureNew;
+ strokes.push_back(stroke);
}
- break;
- case MODE_VERTEXCOLOR:
- case MODE_WIND:
+ if (strokes.empty())
{
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
+ pos = posNew;
+ }
+ else
+ {
+ pos = strokes.front().position;
+ }
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr)
- break;
+ const XMVECTOR spline_p0 = strokes.empty() ? XMVectorSet(posNew.x, posNew.y, pressureNew, 0) : XMVectorSet(strokes[0].position.x, strokes[0].position.y, strokes[0].pressure, 0);
+ const XMVECTOR spline_p1 = strokes.size() < 2 ? spline_p0 : XMVectorSet(strokes[1].position.x, strokes[1].position.y, strokes[1].pressure, 0);
+ const XMVECTOR spline_p2 = strokes.size() < 3 ? spline_p1 : XMVectorSet(strokes[2].position.x, strokes[2].position.y, strokes[2].pressure, 0);
+ const XMVECTOR spline_p3 = strokes.size() < 4 ? spline_p2 : XMVectorSet(strokes[3].position.x, strokes[3].position.y, strokes[3].pressure, 0);
- MaterialComponent* material = subset >= 0 && subset < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[subset].materialID) : nullptr;
- if (material == nullptr)
+ int substep_count = (int)std::ceil(wi::math::Distance(pos, posNew) / (radius * pressureNew));
+ substep_count = std::max(1, std::min(100, substep_count));
+
+ for (int substep = 0; substep < substep_count; ++substep)
+ {
+ const float t = float(substep) / float(substep_count);
+
+ float pressure = 1;
+ const XMVECTOR spline_p = XMVectorCatmullRom(
+ spline_p0,
+ spline_p1,
+ spline_p2,
+ spline_p3,
+ t
+ );
+ XMFLOAT2 pos_eval;
+ XMStoreFloat2(&pos_eval, spline_p);
+ stroke_dist += wi::math::Distance(pos, pos_eval);
+ pos = pos_eval;
+ pressure = XMVectorGetZ(spline_p);
+ const float pressure_radius = radius * pressure;
+ bool pointer_moved = false;
+ if (stroke_dist >= spacing)
{
- for (auto& x : mesh->subsets)
+ pointer_moved = true;
+ stroke_dist = 0;
+ }
+ const bool painting = pointer_moved && strokes.size() >= stabilizer;
+
+ Scene& scene = editor->GetCurrentScene();
+ const CameraComponent& camera = editor->GetCurrentEditorScene().camera;
+ 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)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);
+
+ for (auto& selected : editor->translator.selected)
+ {
+ switch (mode)
{
- material = scene.materials.GetComponent(x.materialID);
- if (material != nullptr)
+ case MODE_TEXTURE:
+ {
+ wi::primitive::Ray pickRay = wi::renderer::GetPickRay((long)pos.x, (long)pos.y, *editor, camera);
+ const wi::scene::PickResult intersect = wi::scene::Pick(pickRay, ~0u, ~0u, scene);
+ if (intersect.entity != selected.entity)
+ break;
+
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ break;
+
+ MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ if (material == nullptr)
+ break;
+
+ int uvset = 0;
+ Texture editTexture = GetEditTextureSlot(*material, &uvset);
+ if (!editTexture.IsValid())
+ break;
+ const TextureDesc& desc = editTexture.GetDesc();
+ auto& vertex_uvset = uvset == 0 ? mesh->vertex_uvset_0 : mesh->vertex_uvset_1;
+
+ const float u = intersect.bary.x;
+ const float v = intersect.bary.y;
+ const float w = 1 - u - v;
+ XMFLOAT2 uv;
+ uv.x = vertex_uvset[intersect.vertexID0].x * w +
+ vertex_uvset[intersect.vertexID1].x * u +
+ vertex_uvset[intersect.vertexID2].x * v;
+ uv.y = vertex_uvset[intersect.vertexID0].y * w +
+ vertex_uvset[intersect.vertexID1].y * u +
+ vertex_uvset[intersect.vertexID2].y * v;
+ uint2 center = XMUINT2(uint32_t(uv.x * desc.width), uint32_t(uv.y * desc.height));
+
+ if (painting)
+ {
+ GraphicsDevice* device = wi::graphics::GetDevice();
+ if (!cmd.IsValid())
+ {
+ cmd = device->BeginCommandList();
+ }
+
+ RecordHistory(true, cmd);
+
+ // Need to requery this because RecordHistory might swap textures on material:
+ editTexture = GetEditTextureSlot(*material, &uvset);
+
+ device->BindComputeShader(wi::renderer::GetShader(wi::enums::CSTYPE_PAINT_TEXTURE), cmd);
+
+ if (brushTex.IsValid())
+ {
+ device->BindResource(&brushTex.GetTexture(), 0, cmd);
+ }
+ else
+ {
+ device->BindResource(wi::texturehelper::getWhite(), 0, cmd);
+ }
+ if (revealTex.IsValid())
+ {
+ device->BindResource(&revealTex.GetTexture(), 1, cmd);
+ }
+ else
+ {
+ device->BindResource(wi::texturehelper::getWhite(), 1, cmd);
+ }
+ device->BindUAV(&editTexture, 0, cmd);
+
+ 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.xPaintBrushSmoothness = smoothness;
+ 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;
+ const uint dispatch_dim = (diameter + PAINT_TEXTURE_BLOCKSIZE - 1) / PAINT_TEXTURE_BLOCKSIZE;
+ device->Dispatch(dispatch_dim, dispatch_dim, 1, cmd);
+
+ GPUBarrier barriers[] = {
+ GPUBarrier::Memory(),
+ };
+ device->Barrier(barriers, arraysize(barriers), cmd);
+
+ if (editTexture.desc.mip_levels > 1)
+ {
+ wi::renderer::GenerateMipChain(editTexture, wi::renderer::MIPGENFILTER::MIPGENFILTER_LINEAR, cmd);
+ }
+ }
+ if(substep == substep_count - 1)
+ {
+ wi::renderer::PaintRadius paintrad;
+ paintrad.objectEntity = selected.entity;
+ paintrad.subset = selected.subsetIndex;
+ paintrad.radius = radius;
+ paintrad.center = center;
+ 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;
+
+ case MODE_VERTEXCOLOR:
+ case MODE_WIND:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ if (material == nullptr)
+ {
+ for (auto& x : mesh->subsets)
+ {
+ material = scene.materials.GetComponent(x.materialID);
+ if (material != nullptr)
+ {
+ switch (mode)
+ {
+ case MODE_VERTEXCOLOR:
+ material->SetUseVertexColors(true);
+ break;
+ case MODE_WIND:
+ material->SetUseWind(true);
+ break;
+ }
+ }
+ }
+ material = nullptr;
+ }
+ else
{
switch (mode)
{
@@ -539,537 +625,529 @@ void PaintToolWindow::Update(float dt)
break;
}
}
- }
- material = nullptr;
- }
- else
- {
- switch (mode)
- {
- case MODE_VERTEXCOLOR:
- material->SetUseVertexColors(true);
- break;
- case MODE_WIND:
- material->SetUseWind(true);
- break;
- }
- }
-
- const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
- const TransformComponent* transform = scene.transforms.GetComponent(entity);
- if (transform == nullptr)
- break;
+ const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
- const XMMATRIX W = XMLoadFloat4x4(&transform->world);
+ const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ if (transform == nullptr)
+ break;
- bool rebuild = false;
+ const XMMATRIX W = XMLoadFloat4x4(&transform->world);
- switch (mode)
- {
- case MODE_VERTEXCOLOR:
- if (mesh->vertex_colors.empty())
- {
- mesh->vertex_colors.resize(mesh->vertex_positions.size());
- std::fill(mesh->vertex_colors.begin(), mesh->vertex_colors.end(), wi::Color::White().rgba); // fill white
- rebuild = true;
- }
- break;
- case MODE_WIND:
- if (mesh->vertex_windweights.empty())
- {
- mesh->vertex_windweights.resize(mesh->vertex_positions.size());
- std::fill(mesh->vertex_windweights.begin(), mesh->vertex_windweights.end(), 0xFF); // fill max affection
- rebuild = true;
- }
- break;
- }
+ bool rebuild = false;
- if (painting)
- {
- for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
- {
- XMVECTOR P, N;
- if (armature == nullptr)
+ switch (mode)
{
- P = XMLoadFloat3(&mesh->vertex_positions[j]);
- N = XMLoadFloat3(&mesh->vertex_normals[j]);
- }
- else
- {
- P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
- }
- P = XMVector3Transform(P, W);
- N = XMVector3Normalize(XMVector3TransformNormal(N, W));
-
- if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
- continue;
-
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- if (subset >= 0 && mesh->vertex_subsets[j] != subset)
- continue;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
- {
- RecordHistory(true);
- rebuild = true;
- const float affection = amount * std::pow(1.0f - (dist / pressure_radius), falloff);
-
- switch (mode)
+ case MODE_VERTEXCOLOR:
+ if (mesh->vertex_colors.empty())
{
- case MODE_VERTEXCOLOR:
- {
- wi::Color vcol = mesh->vertex_colors[j];
- vcol = wi::Color::lerp(vcol, color, affection);
- mesh->vertex_colors[j] = vcol.rgba;
+ mesh->vertex_colors.resize(mesh->vertex_positions.size());
+ std::fill(mesh->vertex_colors.begin(), mesh->vertex_colors.end(), wi::Color::White().rgba); // fill white
+ rebuild = true;
}
break;
- case MODE_WIND:
+ case MODE_WIND:
+ if (mesh->vertex_windweights.empty())
{
- wi::Color vcol = wi::Color(0, 0, 0, mesh->vertex_windweights[j]);
- vcol = wi::Color::lerp(vcol, color, affection);
- mesh->vertex_windweights[j] = vcol.getA();
+ mesh->vertex_windweights.resize(mesh->vertex_positions.size());
+ std::fill(mesh->vertex_windweights.begin(), mesh->vertex_windweights.end(), 0xFF); // fill max affection
+ rebuild = true;
}
break;
- }
}
- }
- }
- if (wireframe)
- {
- uint32_t first_subset = 0;
- uint32_t last_subset = 0;
- mesh->GetLODSubsetRange(0, first_subset, last_subset);
- for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
- {
- const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
- for (size_t j = 0; j < subset.indexCount; j += 3)
+ if (painting)
{
- const uint32_t triangle[] = {
- mesh->indices[j + 0],
- mesh->indices[j + 1],
- mesh->indices[j + 2],
- };
- if (this->subset >= 0 && (mesh->vertex_subsets[triangle[0]] != this->subset || mesh->vertex_subsets[triangle[1]] != this->subset || mesh->vertex_subsets[triangle[2]] != this->subset))
- continue;
-
- const XMVECTOR P[arraysize(triangle)] = {
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
- };
-
- wi::renderer::RenderableTriangle tri;
- XMStoreFloat3(&tri.positionA, P[0]);
- XMStoreFloat3(&tri.positionB, P[1]);
- XMStoreFloat3(&tri.positionC, P[2]);
- if (mode == MODE_WIND)
+ for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
{
- tri.colorA = wi::Color(mesh->vertex_windweights[triangle[0]], 0, 0, 255);
- tri.colorB = wi::Color(mesh->vertex_windweights[triangle[1]], 0, 0, 255);
- tri.colorC = wi::Color(mesh->vertex_windweights[triangle[2]], 0, 0, 255);
+ XMVECTOR P, N;
+ if (armature == nullptr)
+ {
+ P = XMLoadFloat3(&mesh->vertex_positions[j]);
+ N = XMLoadFloat3(&mesh->vertex_normals[j]);
+ }
+ else
+ {
+ P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
+ }
+ P = XMVector3Transform(P, W);
+ N = XMVector3Normalize(XMVector3TransformNormal(N, W));
+
+ if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
+ continue;
+
+ P = XMVector3TransformCoord(P, VP);
+ P = P * MUL + ADD;
+ P = P * SCREEN;
+
+ if (selected.subsetIndex >= 0 && mesh->vertex_subsets[j] != selected.subsetIndex)
+ continue;
+
+ const float z = XMVectorGetZ(P);
+ const float dist = XMVectorGetX(XMVector2Length(C - P));
+ if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ {
+ RecordHistory(true);
+ rebuild = true;
+ const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
+
+ switch (mode)
+ {
+ case MODE_VERTEXCOLOR:
+ {
+ wi::Color vcol = mesh->vertex_colors[j];
+ vcol = wi::Color::lerp(vcol, color, affection);
+ mesh->vertex_colors[j] = vcol.rgba;
+ }
+ break;
+ case MODE_WIND:
+ {
+ wi::Color vcol = wi::Color(0, 0, 0, mesh->vertex_windweights[j]);
+ vcol = wi::Color::lerp(vcol, color, affection);
+ mesh->vertex_windweights[j] = vcol.getA();
+ }
+ break;
+ }
+ }
}
- else
- {
- tri.colorA.w = 0.8f;
- tri.colorB.w = 0.8f;
- tri.colorC.w = 0.8f;
- }
- wi::renderer::DrawTriangle(tri, true);
- }
- }
- }
-
- if (rebuild)
- {
- mesh->CreateRenderData();
- }
- }
- break;
-
- case MODE_SCULPTING_ADD:
- case MODE_SCULPTING_SUBTRACT:
- {
- 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 ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
-
- const TransformComponent* transform = scene.transforms.GetComponent(entity);
- if (transform == nullptr)
- break;
-
- const XMMATRIX W = XMLoadFloat4x4(&transform->world);
-
- XMVECTOR sculptDir;
- AxisLock axis_lock = (AxisLock)axisCombo.GetItemUserData(axisCombo.GetSelected());
- switch (axis_lock)
- {
- 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;
- }
-
-
- bool rebuild = false;
- if (painting)
- {
- sculpting_indices.clear();
- sculpting_indices.reserve(mesh->vertex_positions.size());
-
- for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
- {
- XMVECTOR P, N;
- if (armature == nullptr)
- {
- P = XMLoadFloat3(&mesh->vertex_positions[j]);
- N = XMLoadFloat3(&mesh->vertex_normals[j]);
- }
- else
- {
- P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
- }
- P = XMVector3Transform(P, W);
- N = XMVector3Normalize(XMVector3TransformNormal(N, W));
-
- const float dotN = XMVectorGetX(XMVector3Dot(F, N));
- if (!backfaces && dotN > 0)
- continue;
-
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
- {
- const float affection = amount * std::pow(1.0f - (dist / pressure_radius), falloff);
- sculpting_indices.push_back({ j, affection });
- }
- }
-
- if (!sculpting_indices.empty())
- {
- RecordHistory(true);
- rebuild = true;
-
- for (auto& x : sculpting_indices)
- {
- XMVECTOR PL = XMLoadFloat3(&mesh->vertex_positions[x.ind]);
- switch (mode)
- {
- case MODE_SCULPTING_ADD:
- PL += sculptDir * x.affection;
- break;
- case MODE_SCULPTING_SUBTRACT:
- PL -= sculptDir * x.affection;
- break;
- }
- XMStoreFloat3(&mesh->vertex_positions[x.ind], PL);
- }
- }
- }
-
- if (wireframe)
- {
- uint32_t first_subset = 0;
- uint32_t last_subset = 0;
- mesh->GetLODSubsetRange(0, first_subset, last_subset);
- for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
- {
- const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
- for (size_t j = 0; j < subset.indexCount; j += 3)
- {
- const uint32_t triangle[] = {
- mesh->indices[j + 0],
- mesh->indices[j + 1],
- mesh->indices[j + 2],
- };
- const XMVECTOR P[arraysize(triangle)] = {
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
- };
-
- wi::renderer::RenderableTriangle tri;
- XMStoreFloat3(&tri.positionA, P[0]);
- XMStoreFloat3(&tri.positionB, P[1]);
- XMStoreFloat3(&tri.positionC, P[2]);
- tri.colorA.w = 0.8f;
- tri.colorB.w = 0.8f;
- tri.colorC.w = 0.8f;
- wi::renderer::DrawTriangle(tri, true);
}
- wi::renderer::RenderableLine sculpt_dir_line;
- sculpt_dir_line.color_start = XMFLOAT4(0, 1, 0, 1);
- sculpt_dir_line.color_end = XMFLOAT4(0, 1, 0, 1);
- sculpt_dir_line.start = editor->hovered.position;
- XMStoreFloat3(
- &sculpt_dir_line.end,
- XMLoadFloat3(&sculpt_dir_line.start) +
- XMVector3Normalize(XMLoadFloat3(&sculpting_normal))
- );
- wi::renderer::DrawLine(sculpt_dir_line);
- }
- }
-
- if (rebuild)
- {
- mesh->ComputeNormals(MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST);
- }
- }
- 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 && wi::input::Down(wi::input::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)) <= pressure_radius)
- {
- RecordHistory(true);
- softbody->weights[j] = (mode == MODE_SOFTBODY_PINNING ? 0.0f : 1.0f);
- softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET;
- }
- j++;
- }
- }
-
- // Visualizing:
- const XMMATRIX W = XMLoadFloat4x4(&softbody->worldMatrix);
- uint32_t first_subset = 0;
- uint32_t last_subset = 0;
- mesh->GetLODSubsetRange(0, first_subset, last_subset);
- for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
- {
- const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
- for (size_t j = 0; j < subset.indexCount; 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];
- wi::renderer::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);
if (wireframe)
{
- wi::renderer::DrawTriangle(tri, true);
- }
- if (weight0 == 0 && weight1 == 0 && weight2 == 0)
- {
- tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 0, 0.8f);
- wi::renderer::DrawTriangle(tri);
- }
- }
- }
- }
- break;
-
- case MODE_HAIRPARTICLE_ADD_TRIANGLE:
- case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- case MODE_HAIRPARTICLE_LENGTH:
- {
- wi::HairParticleSystem* 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 ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
-
- const TransformComponent* transform = scene.transforms.GetComponent(entity);
- if (transform == nullptr)
- break;
-
- const XMMATRIX W = XMLoadFloat4x4(&transform->world);
-
- if (painting)
- {
- for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
- {
- XMVECTOR P, N;
- if (armature == nullptr)
- {
- P = XMLoadFloat3(&mesh->vertex_positions[j]);
- N = XMLoadFloat3(&mesh->vertex_normals[j]);
- }
- else
- {
- P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
- }
- P = XMVector3Transform(P, W);
- N = XMVector3Normalize(XMVector3TransformNormal(N, W));
-
- if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
- continue;
-
- P = XMVector3TransformCoord(P, VP);
- P = P * MUL + ADD;
- P = P * SCREEN;
-
- const float z = XMVectorGetZ(P);
- const float dist = XMVectorGetX(XMVector2Length(C - P));
- if (z >= 0 && z <= 1 && dist <= pressure_radius)
- {
- RecordHistory(true);
- switch (mode)
+ uint32_t first_subset = 0;
+ uint32_t last_subset = 0;
+ mesh->GetLODSubsetRange(0, first_subset, last_subset);
+ for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
{
- case MODE_HAIRPARTICLE_ADD_TRIANGLE:
- hair->vertex_lengths[j] = 1.0f;
- break;
- case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- hair->vertex_lengths[j] = 0;
- break;
- case MODE_HAIRPARTICLE_LENGTH:
- if (hair->vertex_lengths[j] > 0) // don't change distribution
+ const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
+ for (size_t j = 0; j < subset.indexCount; j += 3)
{
- const float affection = amount * std::pow(1.0f - (dist / pressure_radius), falloff);
- hair->vertex_lengths[j] = wi::math::Lerp(hair->vertex_lengths[j], 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[j] = wi::math::Clamp(hair->vertex_lengths[j], 1.0f / 255.0f, 1.0f);
+ const uint32_t triangle[] = {
+ mesh->indices[j + 0],
+ mesh->indices[j + 1],
+ mesh->indices[j + 2],
+ };
+ if (selected.subsetIndex >= 0 && (mesh->vertex_subsets[triangle[0]] != selected.subsetIndex || mesh->vertex_subsets[triangle[1]] != selected.subsetIndex || mesh->vertex_subsets[triangle[2]] != selected.subsetIndex))
+ continue;
+
+ const XMVECTOR P[arraysize(triangle)] = {
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
+ };
+
+ wi::renderer::RenderableTriangle tri;
+ XMStoreFloat3(&tri.positionA, P[0]);
+ XMStoreFloat3(&tri.positionB, P[1]);
+ XMStoreFloat3(&tri.positionC, P[2]);
+ if (mode == MODE_WIND)
+ {
+ tri.colorA = wi::Color(mesh->vertex_windweights[triangle[0]], 0, 0, 255);
+ tri.colorB = wi::Color(mesh->vertex_windweights[triangle[1]], 0, 0, 255);
+ tri.colorC = wi::Color(mesh->vertex_windweights[triangle[2]], 0, 0, 255);
+ }
+ else
+ {
+ tri.colorA.w = 0.8f;
+ tri.colorB.w = 0.8f;
+ tri.colorC.w = 0.8f;
+ }
+ wi::renderer::DrawTriangle(tri, true);
}
- break;
}
- hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
}
- }
- }
- if (wireframe)
- {
- uint32_t first_subset = 0;
- uint32_t last_subset = 0;
- mesh->GetLODSubsetRange(0, first_subset, last_subset);
- for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
- {
- const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
- for (size_t j = 0; j < subset.indexCount; j += 3)
+ if (rebuild)
{
- const uint32_t triangle[] = {
- mesh->indices[j + 0],
- mesh->indices[j + 1],
- mesh->indices[j + 2],
- };
- const XMVECTOR P[arraysize(triangle)] = {
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
- };
-
- wi::renderer::RenderableTriangle tri;
- XMStoreFloat3(&tri.positionA, P[0]);
- XMStoreFloat3(&tri.positionB, P[1]);
- XMStoreFloat3(&tri.positionC, P[2]);
- wi::renderer::DrawTriangle(tri, true);
+ mesh->CreateRenderData();
}
}
+ break;
- for (size_t j = 0; j < hair->indices.size() && wireframe; j += 3)
+ case MODE_SCULPTING_ADD:
+ case MODE_SCULPTING_SUBTRACT:
{
- const uint32_t triangle[] = {
- hair->indices[j + 0],
- hair->indices[j + 1],
- hair->indices[j + 2],
- };
- const XMVECTOR P[arraysize(triangle)] = {
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
- XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
- };
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
+
+ const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ if (transform == nullptr)
+ break;
+
+ const XMMATRIX W = XMLoadFloat4x4(&transform->world);
+
+ XMVECTOR sculptDir;
+ AxisLock axis_lock = (AxisLock)axisCombo.GetItemUserData(axisCombo.GetSelected());
+ switch (axis_lock)
+ {
+ 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;
+ }
+
+
+ bool rebuild = false;
+ if (painting)
+ {
+ sculpting_indices.clear();
+ sculpting_indices.reserve(mesh->vertex_positions.size());
+
+ for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
+ {
+ XMVECTOR P, N;
+ if (armature == nullptr)
+ {
+ P = XMLoadFloat3(&mesh->vertex_positions[j]);
+ N = XMLoadFloat3(&mesh->vertex_normals[j]);
+ }
+ else
+ {
+ P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
+ }
+ P = XMVector3Transform(P, W);
+ N = XMVector3Normalize(XMVector3TransformNormal(N, W));
+
+ const float dotN = XMVectorGetX(XMVector3Dot(F, N));
+ if (!backfaces && dotN > 0)
+ continue;
+
+ P = XMVector3TransformCoord(P, VP);
+ P = P * MUL + ADD;
+ P = P * SCREEN;
+
+ const float z = XMVectorGetZ(P);
+ const float dist = XMVectorGetX(XMVector2Length(C - P));
+ if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ {
+ const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
+ sculpting_indices.push_back({ j, affection });
+ }
+ }
+
+ if (!sculpting_indices.empty())
+ {
+ RecordHistory(true);
+ rebuild = true;
+
+ for (auto& x : sculpting_indices)
+ {
+ XMVECTOR PL = XMLoadFloat3(&mesh->vertex_positions[x.ind]);
+ switch (mode)
+ {
+ case MODE_SCULPTING_ADD:
+ PL += sculptDir * x.affection;
+ break;
+ case MODE_SCULPTING_SUBTRACT:
+ PL -= sculptDir * x.affection;
+ break;
+ }
+ XMStoreFloat3(&mesh->vertex_positions[x.ind], PL);
+ }
+ }
+ }
+
+ if (wireframe)
+ {
+ uint32_t first_subset = 0;
+ uint32_t last_subset = 0;
+ mesh->GetLODSubsetRange(0, first_subset, last_subset);
+ for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
+ {
+ const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
+ for (size_t j = 0; j < subset.indexCount; j += 3)
+ {
+ const uint32_t triangle[] = {
+ mesh->indices[j + 0],
+ mesh->indices[j + 1],
+ mesh->indices[j + 2],
+ };
+ const XMVECTOR P[arraysize(triangle)] = {
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
+ };
+
+ wi::renderer::RenderableTriangle tri;
+ XMStoreFloat3(&tri.positionA, P[0]);
+ XMStoreFloat3(&tri.positionB, P[1]);
+ XMStoreFloat3(&tri.positionC, P[2]);
+ tri.colorA.w = 0.8f;
+ tri.colorB.w = 0.8f;
+ tri.colorC.w = 0.8f;
+ wi::renderer::DrawTriangle(tri, true);
+ }
+
+ wi::renderer::RenderableLine sculpt_dir_line;
+ sculpt_dir_line.color_start = XMFLOAT4(0, 1, 0, 1);
+ sculpt_dir_line.color_end = XMFLOAT4(0, 1, 0, 1);
+ sculpt_dir_line.start = editor->hovered.position;
+ XMStoreFloat3(
+ &sculpt_dir_line.end,
+ XMLoadFloat3(&sculpt_dir_line.start) +
+ XMVector3Normalize(XMLoadFloat3(&sculpting_normal))
+ );
+ wi::renderer::DrawLine(sculpt_dir_line);
+ }
+ }
+
+ if (rebuild)
+ {
+ mesh->ComputeNormals(MeshComponent::COMPUTE_NORMALS_SMOOTH_FAST);
+ }
+ }
+ break;
+
+ case MODE_SOFTBODY_PINNING:
+ case MODE_SOFTBODY_PHYSICS:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.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 (painting)
+ {
+ 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)) <= pressure_radius)
+ {
+ RecordHistory(true);
+ softbody->weights[j] = (mode == MODE_SOFTBODY_PINNING ? 0.0f : 1.0f);
+ softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET;
+ }
+ j++;
+ }
+ }
+
+ // Visualizing:
+ const XMMATRIX W = XMLoadFloat4x4(&softbody->worldMatrix);
+ uint32_t first_subset = 0;
+ uint32_t last_subset = 0;
+ mesh->GetLODSubsetRange(0, first_subset, last_subset);
+ for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
+ {
+ const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
+ for (size_t j = 0; j < subset.indexCount; 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];
+ wi::renderer::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);
+ if (wireframe)
+ {
+ wi::renderer::DrawTriangle(tri, true);
+ }
+ if (weight0 == 0 && weight1 == 0 && weight2 == 0)
+ {
+ tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 0, 0.8f);
+ wi::renderer::DrawTriangle(tri);
+ }
+ }
+ }
+ }
+ break;
+
+ case MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ case MODE_HAIRPARTICLE_LENGTH:
+ {
+ wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
+ if (hair == nullptr || hair->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(hair->meshID);
+ if (mesh == nullptr)
+ break;
+
+ const ArmatureComponent* armature = mesh->IsSkinned() ? scene.armatures.GetComponent(mesh->armatureID) : nullptr;
+
+ const TransformComponent* transform = scene.transforms.GetComponent(selected.entity);
+ if (transform == nullptr)
+ break;
+
+ const XMMATRIX W = XMLoadFloat4x4(&transform->world);
+
+ if (painting)
+ {
+ for (size_t j = 0; j < mesh->vertex_positions.size(); ++j)
+ {
+ XMVECTOR P, N;
+ if (armature == nullptr)
+ {
+ P = XMLoadFloat3(&mesh->vertex_positions[j]);
+ N = XMLoadFloat3(&mesh->vertex_normals[j]);
+ }
+ else
+ {
+ P = wi::scene::SkinVertex(*mesh, *armature, (uint32_t)j, &N);
+ }
+ P = XMVector3Transform(P, W);
+ N = XMVector3Normalize(XMVector3TransformNormal(N, W));
+
+ if (!backfaces && XMVectorGetX(XMVector3Dot(F, N)) > 0)
+ continue;
+
+ P = XMVector3TransformCoord(P, VP);
+ P = P * MUL + ADD;
+ P = P * SCREEN;
+
+ const float z = XMVectorGetZ(P);
+ const float dist = XMVectorGetX(XMVector2Length(C - P));
+ if (z >= 0 && z <= 1 && dist <= pressure_radius)
+ {
+ RecordHistory(true);
+ switch (mode)
+ {
+ case MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ hair->vertex_lengths[j] = 1.0f;
+ break;
+ case MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ hair->vertex_lengths[j] = 0;
+ break;
+ case MODE_HAIRPARTICLE_LENGTH:
+ if (hair->vertex_lengths[j] > 0) // don't change distribution
+ {
+ const float affection = amount * wi::math::SmoothStep(0, smoothness, 1 - dist / pressure_radius);
+ hair->vertex_lengths[j] = wi::math::Lerp(hair->vertex_lengths[j], 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[j] = wi::math::Clamp(hair->vertex_lengths[j], 1.0f / 255.0f, 1.0f);
+ }
+ break;
+ }
+ hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
+ }
+ }
+ }
+
+ if (wireframe)
+ {
+ uint32_t first_subset = 0;
+ uint32_t last_subset = 0;
+ mesh->GetLODSubsetRange(0, first_subset, last_subset);
+ for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex)
+ {
+ const MeshComponent::MeshSubset& subset = mesh->subsets[subsetIndex];
+ for (size_t j = 0; j < subset.indexCount; j += 3)
+ {
+ const uint32_t triangle[] = {
+ mesh->indices[j + 0],
+ mesh->indices[j + 1],
+ mesh->indices[j + 2],
+ };
+ const XMVECTOR P[arraysize(triangle)] = {
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
+ };
+
+ wi::renderer::RenderableTriangle tri;
+ XMStoreFloat3(&tri.positionA, P[0]);
+ XMStoreFloat3(&tri.positionB, P[1]);
+ XMStoreFloat3(&tri.positionC, P[2]);
+ wi::renderer::DrawTriangle(tri, true);
+ }
+ }
+
+ for (size_t j = 0; j < hair->indices.size() && wireframe; j += 3)
+ {
+ const uint32_t triangle[] = {
+ hair->indices[j + 0],
+ hair->indices[j + 1],
+ hair->indices[j + 2],
+ };
+ const XMVECTOR P[arraysize(triangle)] = {
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[0]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[0]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[1]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[1]), W),
+ XMVector3Transform(armature == nullptr ? XMLoadFloat3(&mesh->vertex_positions[triangle[2]]) : wi::scene::SkinVertex(*mesh, *armature, triangle[2]), W),
+ };
+
+ wi::renderer::RenderableTriangle tri;
+ XMStoreFloat3(&tri.positionA, P[0]);
+ XMStoreFloat3(&tri.positionB, P[1]);
+ XMStoreFloat3(&tri.positionC, P[2]);
+ tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 1, 0.9f);
+ wi::renderer::DrawTriangle(tri, false);
+ }
+ }
+ }
+ break;
- wi::renderer::RenderableTriangle tri;
- XMStoreFloat3(&tri.positionA, P[0]);
- XMStoreFloat3(&tri.positionB, P[1]);
- XMStoreFloat3(&tri.positionC, P[2]);
- tri.colorA = tri.colorB = tri.colorC = XMFLOAT4(1, 0, 1, 0.9f);
- wi::renderer::DrawTriangle(tri, false);
}
}
}
- break;
+ while (strokes.size() > stabilizer)
+ {
+ strokes.pop_front();
}
}
@@ -1108,7 +1186,7 @@ using namespace PaintTool_Internal;
void PaintToolWindow::DrawBrush(const wi::Canvas& canvas, CommandList cmd) const
{
const MODE mode = GetMode();
- if (mode == MODE_DISABLED || mode == MODE_TEXTURE || entity == INVALID_ENTITY || wi::backlog::isActive())
+ if (mode == MODE_DISABLED || mode == MODE_TEXTURE || wi::backlog::isActive())
return;
static bool shaders_loaded = false;
@@ -1177,20 +1255,6 @@ PaintToolWindow::MODE PaintToolWindow::GetMode() const
{
return (MODE)modeComboBox.GetSelected();
}
-void PaintToolWindow::SetEntity(wi::ecs::Entity value, int subsetindex)
-{
- entity = value;
- subset = subsetindex;
-
- if (entity == INVALID_ENTITY)
- {
- saveTextureButton.SetEnabled(false);
- }
- else if (GetMode() == MODE_TEXTURE)
- {
- saveTextureButton.SetEnabled(true);
- }
-}
void PaintToolWindow::RecordHistory(bool start, CommandList cmd)
{
@@ -1206,11 +1270,11 @@ void PaintToolWindow::RecordHistory(bool start, CommandList cmd)
wi::Archive& archive = *currentHistory;
archive << EditorComponent::HISTORYOP_PAINTTOOL;
archive << (uint32_t)GetMode();
- archive << entity;
+ history_redo_jump_position = archive.WriteUnknownJumpPosition();
}
else
{
- if (!history_needs_recording_end || wi::input::Down(wi::input::MOUSE_BUTTON_LEFT))
+ if (!history_needs_recording_end || !strokes.empty())
return;
history_needs_recording_end = false;
history_needs_recording_start = false;
@@ -1221,320 +1285,278 @@ void PaintToolWindow::RecordHistory(bool start, CommandList cmd)
wi::Archive& archive = *currentHistory;
Scene& scene = editor->GetCurrentScene();
- switch (GetMode())
+ for (auto& selected : editor->translator.selected)
{
- case PaintToolWindow::MODE_TEXTURE:
- {
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
-
- MaterialComponent* material = subset >= 0 && subset < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[subset].materialID) : nullptr;
- if (material == nullptr)
- break;
-
- auto editTexture = GetEditTextureSlot(*material);
-
- archive << textureSlotComboBox.GetSelected();
-
- if (history_textureIndex >= history_textures.size())
+ switch (GetMode())
{
- history_textures.resize((history_textureIndex + 1) * 2);
- }
- history_textures[history_textureIndex] = editTexture;
- archive << history_textureIndex;
- history_textureIndex++;
-
- if (start)
+ case PaintToolWindow::MODE_TEXTURE:
{
- // Make a copy of texture to edit and replace material resource:
- GraphicsDevice* device = wi::graphics::GetDevice();
- Texture newTex;
- TextureDesc desc = editTexture.GetDesc();
- desc.format = Format::R8G8B8A8_UNORM; // force format to one that is writable by GPU
- desc.bind_flags |= BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
- device->CreateTexture(&desc, nullptr, &newTex);
- for (uint32_t i = 0; i < newTex.GetDesc().mip_levels; ++i)
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ break;
+
+ MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ if (material == nullptr)
+ break;
+
+ auto editTexture = GetEditTextureSlot(*material);
+
+ archive << textureSlotComboBox.GetSelected();
+
+ if (history_textureIndex >= history_textures.size())
{
- int subresource_index;
- subresource_index = device->CreateSubresource(&newTex, SubresourceType::SRV, 0, 1, i, 1);
- assert(subresource_index == i);
- subresource_index = device->CreateSubresource(&newTex, SubresourceType::UAV, 0, 1, i, 1);
- assert(subresource_index == i);
+ history_textures.resize((history_textureIndex + 1) * 2);
}
- assert(cmd.IsValid());
- wi::renderer::CopyTexture2D(newTex, -1, 0, 0, editTexture, 0, cmd);
- ReplaceEditTextureSlot(*material, newTex);
+ history_textures[history_textureIndex] = editTexture;
+ archive << history_textureIndex;
+ history_textureIndex++;
+
+ if (start)
+ {
+ // Make a copy of texture to edit and replace material resource:
+ GraphicsDevice* device = wi::graphics::GetDevice();
+ Texture newTex;
+ TextureDesc desc = editTexture.GetDesc();
+ desc.format = Format::R8G8B8A8_UNORM; // force format to one that is writable by GPU
+ desc.bind_flags |= BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
+ device->CreateTexture(&desc, nullptr, &newTex);
+ for (uint32_t i = 0; i < newTex.GetDesc().mip_levels; ++i)
+ {
+ int subresource_index;
+ subresource_index = device->CreateSubresource(&newTex, SubresourceType::SRV, 0, 1, i, 1);
+ assert(subresource_index == i);
+ subresource_index = device->CreateSubresource(&newTex, SubresourceType::UAV, 0, 1, i, 1);
+ assert(subresource_index == i);
+ }
+ assert(cmd.IsValid());
+ wi::renderer::CopyTexture2D(newTex, -1, 0, 0, editTexture, 0, cmd);
+ ReplaceEditTextureSlot(*material, newTex);
+ }
+
}
-
- }
- break;
- case PaintToolWindow::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;
-
- archive << mesh->vertex_colors;
- }
- break;
- case PaintToolWindow::MODE_WIND:
- {
- 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;
-
- archive << mesh->vertex_windweights;
- }
- break;
- case PaintToolWindow::MODE_SCULPTING_ADD:
- case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
- {
- 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;
-
- archive << mesh->vertex_positions;
- archive << mesh->vertex_normals;
- }
- break;
- case PaintToolWindow::MODE_SOFTBODY_PINNING:
- case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
- {
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
- if (softbody == nullptr)
- break;
-
- archive << softbody->weights;
- }
- break;
- case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
- {
- wi::HairParticleSystem* hair = scene.hairs.GetComponent(entity);
- if (hair == nullptr)
- break;
-
- archive << hair->vertex_lengths;
- }
- break;
- default:
- assert(0);
break;
+ case PaintToolWindow::MODE_VERTEXCOLOR:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ archive << mesh->vertex_colors;
+ }
+ break;
+ case PaintToolWindow::MODE_WIND:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ archive << mesh->vertex_windweights;
+ }
+ break;
+ case PaintToolWindow::MODE_SCULPTING_ADD:
+ case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ archive << mesh->vertex_positions;
+ archive << mesh->vertex_normals;
+ }
+ break;
+ case PaintToolWindow::MODE_SOFTBODY_PINNING:
+ case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
+ if (softbody == nullptr)
+ break;
+
+ archive << softbody->weights;
+ }
+ break;
+ case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
+ {
+ wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
+ if (hair == nullptr)
+ break;
+
+ archive << hair->vertex_lengths;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ if (start)
+ {
+ archive.PatchUnknownJumpPosition(history_redo_jump_position);
}
}
void PaintToolWindow::ConsumeHistoryOperation(wi::Archive& archive, bool undo)
{
MODE historymode;
archive >> (uint32_t&)historymode;
- archive >> entity;
+
+ uint64_t jump_pos = 0;
+ archive >> jump_pos;
+ if (!undo)
+ {
+ archive.Jump(jump_pos);
+ }
modeComboBox.SetSelected(historymode);
Scene& scene = editor->GetCurrentScene();
- switch (historymode)
+ for (auto& selected : editor->translator.selected)
{
- case PaintToolWindow::MODE_TEXTURE:
- {
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
- if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
- break;
-
- MaterialComponent* material = subset >= 0 && subset < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[subset].materialID) : nullptr;
- if (material == nullptr)
- break;
-
- int undo_slot;
- archive >> undo_slot;
- size_t undo_textureindex;
- archive >> undo_textureindex;
- int redo_slot;
- archive >> redo_slot;
- size_t redo_textureindex;
- archive >> redo_textureindex;
-
- if (undo)
+ switch (historymode)
{
- textureSlotComboBox.SetSelected(undo_slot);
- history_textureIndex = undo_textureindex;
- }
- else
+ case PaintToolWindow::MODE_TEXTURE:
{
- textureSlotComboBox.SetSelected(redo_slot);
- history_textureIndex = redo_textureindex;
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr || (mesh->vertex_uvset_0.empty() && mesh->vertex_uvset_1.empty()))
+ break;
+
+ MaterialComponent* material = selected.subsetIndex >= 0 && selected.subsetIndex < (int)mesh->subsets.size() ? scene.materials.GetComponent(mesh->subsets[selected.subsetIndex].materialID) : nullptr;
+ if (material == nullptr)
+ break;
+
+ int slot;
+ archive >> slot;
+ size_t textureindex;
+ archive >> textureindex;
+
+ textureSlotComboBox.SetSelected(slot);
+ history_textureIndex = textureindex;
+ ReplaceEditTextureSlot(*material, history_textures[history_textureIndex]);
+
}
- ReplaceEditTextureSlot(*material, history_textures[history_textureIndex]);
-
- }
- break;
- case PaintToolWindow::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;
-
- MeshComponent undo_mesh;
- archive >> undo_mesh.vertex_colors;
- MeshComponent redo_mesh;
- archive >> redo_mesh.vertex_colors;
-
- if (undo)
- {
- mesh->vertex_colors = undo_mesh.vertex_colors;
- }
- else
- {
- mesh->vertex_colors = redo_mesh.vertex_colors;
- }
-
- mesh->CreateRenderData();
- }
- break;
- case PaintToolWindow::MODE_WIND:
- {
- 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;
-
- MeshComponent undo_mesh;
- archive >> undo_mesh.vertex_windweights;
- MeshComponent redo_mesh;
- archive >> redo_mesh.vertex_windweights;
-
- if (undo)
- {
- mesh->vertex_windweights = undo_mesh.vertex_windweights;
- }
- else
- {
- mesh->vertex_windweights = redo_mesh.vertex_windweights;
- }
-
- mesh->CreateRenderData();
- }
- break;
- case PaintToolWindow::MODE_SCULPTING_ADD:
- case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
- {
- 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;
-
- MeshComponent undo_mesh;
- archive >> undo_mesh.vertex_positions;
- archive >> undo_mesh.vertex_normals;
- MeshComponent redo_mesh;
- archive >> redo_mesh.vertex_positions;
- archive >> redo_mesh.vertex_normals;
-
- if (undo)
- {
- mesh->vertex_positions = undo_mesh.vertex_positions;
- mesh->vertex_normals = undo_mesh.vertex_normals;
- }
- else
- {
- mesh->vertex_positions = redo_mesh.vertex_positions;
- mesh->vertex_normals = redo_mesh.vertex_normals;
- }
-
- mesh->CreateRenderData();
- }
- break;
- case PaintToolWindow::MODE_SOFTBODY_PINNING:
- case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
- {
- ObjectComponent* object = scene.objects.GetComponent(entity);
- if (object == nullptr || object->meshID == INVALID_ENTITY)
- break;
-
- SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
- if (softbody == nullptr)
- break;
-
- SoftBodyPhysicsComponent undo_softbody;
- archive >> undo_softbody.weights;
- SoftBodyPhysicsComponent redo_softbody;
- archive >> redo_softbody.weights;
-
- if (undo)
- {
- softbody->weights = undo_softbody.weights;
- }
- else
- {
- softbody->weights = redo_softbody.weights;
- }
-
- softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET;
- }
- break;
- case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
- case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
- {
- wi::HairParticleSystem* hair = scene.hairs.GetComponent(entity);
- if (hair == nullptr)
- break;
-
- wi::HairParticleSystem undo_hair;
- archive >> undo_hair.vertex_lengths;
- wi::HairParticleSystem redo_hair;
- archive >> redo_hair.vertex_lengths;
-
- if (undo)
- {
- hair->vertex_lengths = undo_hair.vertex_lengths;
- }
- else
- {
- hair->vertex_lengths = redo_hair.vertex_lengths;
- }
-
- hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
- }
- break;
- default:
- assert(0);
break;
+ case PaintToolWindow::MODE_VERTEXCOLOR:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ MeshComponent archive_mesh;
+ archive >> archive_mesh.vertex_colors;
+
+ mesh->vertex_colors = archive_mesh.vertex_colors;
+
+ mesh->CreateRenderData();
+ }
+ break;
+ case PaintToolWindow::MODE_WIND:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ MeshComponent archive_mesh;
+ archive >> archive_mesh.vertex_windweights;
+
+ mesh->vertex_windweights = archive_mesh.vertex_windweights;
+
+ mesh->CreateRenderData();
+ }
+ break;
+ case PaintToolWindow::MODE_SCULPTING_ADD:
+ case PaintToolWindow::MODE_SCULPTING_SUBTRACT:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
+ if (mesh == nullptr)
+ break;
+
+ MeshComponent archive_mesh;
+ archive >> archive_mesh.vertex_positions;
+ archive >> archive_mesh.vertex_normals;
+
+ mesh->vertex_positions = archive_mesh.vertex_positions;
+ mesh->vertex_normals = archive_mesh.vertex_normals;
+
+ mesh->CreateRenderData();
+ }
+ break;
+ case PaintToolWindow::MODE_SOFTBODY_PINNING:
+ case PaintToolWindow::MODE_SOFTBODY_PHYSICS:
+ {
+ ObjectComponent* object = scene.objects.GetComponent(selected.entity);
+ if (object == nullptr || object->meshID == INVALID_ENTITY)
+ break;
+
+ SoftBodyPhysicsComponent* softbody = scene.softbodies.GetComponent(object->meshID);
+ if (softbody == nullptr)
+ break;
+
+ SoftBodyPhysicsComponent archive_softbody;
+ archive >> archive_softbody.weights;
+
+ softbody->weights = archive_softbody.weights;
+
+ softbody->_flags |= SoftBodyPhysicsComponent::FORCE_RESET;
+ }
+ break;
+ case PaintToolWindow::MODE_HAIRPARTICLE_ADD_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_REMOVE_TRIANGLE:
+ case PaintToolWindow::MODE_HAIRPARTICLE_LENGTH:
+ {
+ wi::HairParticleSystem* hair = scene.hairs.GetComponent(selected.entity);
+ if (hair == nullptr)
+ break;
+
+ wi::HairParticleSystem archive_hair;
+ archive >> archive_hair.vertex_lengths;
+
+ hair->vertex_lengths = archive_hair.vertex_lengths;
+
+ hair->_flags |= wi::HairParticleSystem::REBUILD_BUFFERS;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
}
}
Texture PaintToolWindow::GetEditTextureSlot(const MaterialComponent& material, int* uvset)
@@ -1595,11 +1617,12 @@ void PaintToolWindow::ResizeLayout()
add_fullwidth(infoLabel);
add(radiusSlider);
add(amountSlider);
- add(falloffSlider);
+ add(smoothnessSlider);
add(spacingSlider);
add(rotationSlider);
+ add(stabilizerSlider);
add_right(backfaceCheckBox);
- add_right(wireCheckBox);
+ wireCheckBox.SetPos(XMFLOAT2(backfaceCheckBox.GetPos().x - wireCheckBox.GetSize().x - 100, backfaceCheckBox.GetPos().y));
add_right(pressureCheckBox);
add(textureSlotComboBox);
add(brushShapeComboBox);
diff --git a/Editor/PaintToolWindow.h b/Editor/PaintToolWindow.h
index 9e453101a..4f0d01fea 100644
--- a/Editor/PaintToolWindow.h
+++ b/Editor/PaintToolWindow.h
@@ -1,6 +1,8 @@
#pragma once
#include "WickedEngine.h"
+#include
+
class EditorComponent;
class PaintToolWindow : public wi::gui::Window
@@ -9,6 +11,7 @@ class PaintToolWindow : public wi::gui::Window
float stroke_dist = 0;
bool history_needs_recording_start = false;
bool history_needs_recording_end = false;
+ size_t history_redo_jump_position = 0;
size_t history_textureIndex = 0;
wi::vector history_textures; // we'd like to keep history textures in GPU memory to avoid GPU readback
wi::graphics::Texture GetEditTextureSlot(const wi::scene::MaterialComponent& material, int* uvset = nullptr);
@@ -25,20 +28,26 @@ class PaintToolWindow : public wi::gui::Window
wi::Resource brushTex;
wi::Resource revealTex;
+ struct Stroke
+ {
+ XMFLOAT2 position;
+ float pressure;
+ };
+ std::deque strokes;
+
public:
void Create(EditorComponent* editor);
EditorComponent* editor = nullptr;
- wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY;
- int subset = -1;
wi::gui::ComboBox modeComboBox;
wi::gui::Label infoLabel;
wi::gui::Slider radiusSlider;
wi::gui::Slider amountSlider;
- wi::gui::Slider falloffSlider;
+ wi::gui::Slider smoothnessSlider;
wi::gui::Slider spacingSlider;
wi::gui::Slider rotationSlider;
+ wi::gui::Slider stabilizerSlider;
wi::gui::CheckBox backfaceCheckBox;
wi::gui::CheckBox wireCheckBox;
wi::gui::CheckBox pressureCheckBox;
@@ -70,7 +79,6 @@ public:
MODE_WIND,
};
MODE GetMode() const;
- void SetEntity(wi::ecs::Entity value, int subsetindex = -1);
enum class AxisLock
{
diff --git a/Editor/config.ini b/Editor/config.ini
index 8eedeb200..526267549 100644
--- a/Editor/config.ini
+++ b/Editor/config.ini
@@ -78,3 +78,4 @@ fps = true
pressure = true
backfaces = false
wireframe = true
+stabilizer = 8
diff --git a/Editor/main_Windows.cpp b/Editor/main_Windows.cpp
index 481e81daf..52e570550 100644
--- a/Editor/main_Windows.cpp
+++ b/Editor/main_Windows.cpp
@@ -261,7 +261,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_INPUT:
wi::input::rawinput::ParseMessage((void*)lParam);
break;
- case WM_POINTERDOWN:
case WM_POINTERUPDATE:
{
POINTER_PEN_INFO pen_info = {};
diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h
index cedadee3e..529a6eb2d 100644
--- a/WickedEngine/shaders/ShaderInterop_Renderer.h
+++ b/WickedEngine/shaders/ShaderInterop_Renderer.h
@@ -879,7 +879,7 @@ struct PaintTextureCB
uint xPaintBrushRadius;
float xPaintBrushAmount;
- float xPaintBrushFalloff;
+ float xPaintBrushSmoothness;
uint xPaintBrushColor;
uint xPaintReveal;
float xPaintBrushRotation;
diff --git a/WickedEngine/shaders/paint_textureCS.hlsl b/WickedEngine/shaders/paint_textureCS.hlsl
index e91d4aa1d..e9823fdac 100644
--- a/WickedEngine/shaders/paint_textureCS.hlsl
+++ b/WickedEngine/shaders/paint_textureCS.hlsl
@@ -49,7 +49,7 @@ 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 affection = push.xPaintBrushAmount * pow(1 - saturate(dist / radius), push.xPaintBrushFalloff);
+ const float affection = push.xPaintBrushAmount * smoothstep(0, push.xPaintBrushSmoothness, 1 - dist / radius);
if (affection > 0 && dist < radius)
{
if (push.xPaintReveal)
diff --git a/WickedEngine/wiECS.h b/WickedEngine/wiECS.h
index 18f1bea5e..580a2bf9b 100644
--- a/WickedEngine/wiECS.h
+++ b/WickedEngine/wiECS.h
@@ -101,8 +101,10 @@ namespace wi::ecs
};
// The ComponentManager is a container that stores components and matches them with entities
+ // Note: final keyword is used to indicate this is a final implementation.
+ // This allows function inlining and avoid calls, improves performance considerably
template
- class ComponentManager : public ComponentManager_Interface
+ class ComponentManager final : public ComponentManager_Interface
{
public:
diff --git a/WickedEngine/wiInput.h b/WickedEngine/wiInput.h
index d03b37f40..05b05a715 100644
--- a/WickedEngine/wiInput.h
+++ b/WickedEngine/wiInput.h
@@ -140,8 +140,8 @@ namespace wi::input
// これはペンです
struct Pen
{
- XMFLOAT2 position;
- float pressure;
+ XMFLOAT2 position = {};
+ float pressure = 0;
};
// Set pen params, this will override mouse params
void SetPen(const Pen& pen);
diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp
index b5a8675e0..f522e957b 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 = 19;
+ const int revision = 20;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);