Files
WickedEngine/Editor/SplineWindow.cpp
T
2025-04-16 09:32:54 +02:00

377 lines
11 KiB
C++

#include "stdafx.h"
#include "SplineWindow.h"
using namespace wi::ecs;
using namespace wi::scene;
void SplineWindow::Create(EditorComponent* _editor)
{
editor = _editor;
wi::gui::Window::Create(ICON_SPLINE " Spline", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE | wi::gui::Window::WindowControls::FIT_ALL_WIDGETS_VERTICAL);
SetSize(XMFLOAT2(670, 440));
closeButton.SetTooltip("Delete SplineComponent");
OnClose([=](wi::gui::EventArgs args) {
wi::Archive& archive = editor->AdvanceHistory();
archive << EditorComponent::HISTORYOP_COMPONENT_DATA;
editor->RecordEntity(archive, entity);
editor->GetCurrentScene().splines.Remove(entity);
editor->RecordEntity(archive, entity);
editor->componentsWnd.RefreshEntityTree();
});
infoLabel.Create("SplineInfo");
infoLabel.SetSize(XMFLOAT2(100, 120));
infoLabel.SetText("The spline is a curve that goes through every specified entity that has a TransformComponent smoothly. A mesh can be generated from it automatically by increasing the subdivisions. It can also modify terrain when the terrain modification slider is used.");
AddWidget(&infoLabel);
loopedCheck.Create("Looped: ");
loopedCheck.OnClick([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->SetLooped(args.bValue);
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->SetLooped(args.bValue);
}
});
AddWidget(&loopedCheck);
alignedCheck.Create("Draw Aligned: ");
alignedCheck.OnClick([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->SetDrawAligned(args.bValue);
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->SetDrawAligned(args.bValue);
}
});
AddWidget(&alignedCheck);
widthSlider.Create(0.001f, 4, 0, 1000, "Width: ");
widthSlider.SetTooltip("Set overall width multiplier for all nodes, used in mesh generation.");
widthSlider.OnSlide([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->width = args.fValue;
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->width = args.fValue;
}
editor->componentsWnd.RefreshEntityTree();
});
AddWidget(&widthSlider);
rotSlider.Create(0, 360, 0, 360, "Rotation: ");
rotSlider.SetTooltip("Set overall rotation for all nodes, used in mesh generation.");
rotSlider.OnSlide([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
float rad = wi::math::DegreesToRadians(args.fValue);
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->rotation = rad;
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->rotation = rad;
}
editor->componentsWnd.RefreshEntityTree();
});
AddWidget(&rotSlider);
subdivSlider.Create(0, 100, 0, 100, "Mesh subdivision: ");
subdivSlider.SetTooltip("Set subdivision count for mesh generation. \nIncreasing this above 0 will enable mesh generation and higher values mean higher quality.");
subdivSlider.OnSlide([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->mesh_generation_subdivision = args.iValue;
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->mesh_generation_subdivision = args.iValue;
}
editor->componentsWnd.RefreshEntityTree();
});
AddWidget(&subdivSlider);
subdivVerticalSlider.Create(0, 36, 0, 36, "Vertical subdivision: ");
subdivVerticalSlider.SetTooltip("Set subdivision count for mesh generation's vertical axis to create a corridoor or a tunnel.");
subdivVerticalSlider.OnSlide([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->mesh_generation_vertical_subdivision = args.iValue;
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->mesh_generation_vertical_subdivision = args.iValue;
}
editor->componentsWnd.RefreshEntityTree();
});
AddWidget(&subdivVerticalSlider);
terrainSlider.Create(0, 1, 0, 100, "Terrain modifier: ");
terrainSlider.SetTooltip("Set terrain modification strength (0 to turn it off, higher values mean more sharpness).");
terrainSlider.OnSlide([this](wi::gui::EventArgs args) {
wi::scene::Scene& scene = editor->GetCurrentScene();
for (auto& x : editor->translator.selected)
{
SplineComponent* spline = scene.splines.GetComponent(x.entity);
if (spline == nullptr)
continue;
spline->terrain_modifier_amount = args.fValue;
}
// indirect set:
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
spline->terrain_modifier_amount = args.fValue;
}
editor->componentsWnd.RefreshEntityTree();
});
AddWidget(&terrainSlider);
addButton.Create("AddNode");
addButton.SetText("+");
addButton.SetTooltip("Add an entity as a node to the spline (it must have TransformComponent). Hotkey: Ctrl + E");
addButton.OnClick([this](wi::gui::EventArgs args) {
NewNode();
});
AddWidget(&addButton);
SetMinimized(true);
SetVisible(false);
SetEntity(INVALID_ENTITY);
}
void SplineWindow::SetEntity(Entity entity)
{
const bool changed = this->entity != entity;
Scene& scene = editor->GetCurrentScene();
const SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline != nullptr)
{
this->entity = entity;
alignedCheck.SetCheck(spline->IsDrawAligned());
loopedCheck.SetCheck(spline->IsLooped());
widthSlider.SetValue(spline->width);
rotSlider.SetValue(wi::math::RadiansToDegrees(spline->rotation));
subdivSlider.SetValue(spline->mesh_generation_subdivision);
subdivVerticalSlider.SetValue(spline->mesh_generation_vertical_subdivision);
terrainSlider.SetValue(spline->terrain_modifier_amount);
if (changed)
{
RefreshEntries();
SetEnabled(true);
}
}
else
{
this->entity = INVALID_ENTITY;
}
}
void SplineWindow::RefreshEntries()
{
for (auto& entry : entries)
{
RemoveWidget(&entry.removeButton);
RemoveWidget(&entry.entityButton);
}
entries.clear();
Scene& scene = editor->GetCurrentScene();
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline == nullptr)
return;
entries.reserve(spline->spline_node_entities.size());
for (size_t i = 0; i < spline->spline_node_entities.size(); ++i)
{
Entity entity = spline->spline_node_entities[i];
const NameComponent* name = scene.names.GetComponent(entity);
Entry& entry = entries.emplace_back();
entry.entityButton.Create("");
if (name != nullptr)
{
entry.entityButton.SetText(name->name);
}
else
{
entry.entityButton.SetText("[unnamed] " + std::to_string(entity));
}
entry.entityButton.OnClick([this, entity](wi::gui::EventArgs args) {
editor->ClearSelected();
editor->AddSelected(entity);
});
AddWidget(&entry.entityButton);
entry.removeButton.Create("");
entry.removeButton.SetText("X");
entry.removeButton.SetSize(XMFLOAT2(entry.removeButton.GetSize().y, entry.removeButton.GetSize().y));
entry.removeButton.OnClick([i, spline, this](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
scene.Entity_Remove(spline->spline_node_entities[i]);
spline->spline_node_entities.erase(spline->spline_node_entities.begin() + i);
spline->spline_node_transforms.erase(spline->spline_node_transforms.begin() + i);
wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [this](uint64_t userdata) {
RefreshEntries();
editor->componentsWnd.RefreshEntityTree();
});
});
AddWidget(&entry.removeButton);
}
editor->generalWnd.themeCombo.SetSelected(editor->generalWnd.themeCombo.GetSelected());
}
void SplineWindow::NewNode()
{
wi::scene::Scene& scene = editor->GetCurrentScene();
SplineComponent* spline = scene.splines.GetComponent(entity);
if (spline == nullptr)
return;
Entity node_entity = CreateEntity();
scene.names.Create(node_entity) = "spline_node_" + std::to_string(spline->spline_node_entities.size());
TransformComponent& transform = scene.transforms.Create(node_entity);
if (!spline->spline_node_transforms.empty())
{
XMVECTOR D = XMVectorSet(1, 0, 0, 0);
if (spline->spline_node_transforms.size() > 1)
{
D = XMVector3Normalize(spline->spline_node_transforms[spline->spline_node_transforms.size() - 1].GetPositionV() - spline->spline_node_transforms[spline->spline_node_transforms.size() - 2].GetPositionV());
}
transform = spline->spline_node_transforms.back();
transform.Translate(D);
transform.UpdateTransform();
}
spline->spline_node_entities.push_back(node_entity);
spline->spline_node_transforms.push_back(transform);
scene.Component_Attach(node_entity, entity);
RefreshEntries();
editor->ClearSelected();
editor->AddSelected(node_entity);
editor->componentsWnd.RefreshEntityTree();
}
void SplineWindow::ResizeLayout()
{
wi::gui::Window::ResizeLayout();
const float padding = 4;
const float width = GetWidgetAreaSize().x;
float y = padding;
float jump = 20;
const float margin_left = 145;
float margin_right = 40;
auto add = [&](wi::gui::Widget& widget) {
if (!widget.IsVisible())
return;
widget.SetPos(XMFLOAT2(margin_left, y));
widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y));
y += widget.GetSize().y;
y += padding;
};
auto add_right = [&](wi::gui::Widget& widget) {
if (!widget.IsVisible())
return;
widget.SetPos(XMFLOAT2(width - margin_right - widget.GetSize().x, y));
y += widget.GetSize().y;
y += padding;
};
auto add_fullwidth = [&](wi::gui::Widget& widget) {
if (!widget.IsVisible())
return;
const float margin_left = padding;
const float margin_right = padding;
widget.SetPos(XMFLOAT2(margin_left, y));
widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y));
y += widget.GetSize().y;
y += padding;
};
add_fullwidth(infoLabel);
add(subdivSlider);
add(subdivVerticalSlider);
add(terrainSlider);
add(widthSlider);
add(rotSlider);
add_right(loopedCheck);
add_right(alignedCheck);
y += padding * 2;
add_fullwidth(addButton);
for (auto& entry : entries)
{
entry.removeButton.SetPos(XMFLOAT2(padding, y));
entry.entityButton.SetSize(XMFLOAT2(width - padding * 3 - entry.removeButton.GetSize().x, entry.entityButton.GetSize().y));
entry.entityButton.SetPos(XMFLOAT2(entry.removeButton.GetPos().x + entry.removeButton.GetSize().x + padding, y));
y += entry.entityButton.GetSize().y;
y += padding;
}
}