377 lines
11 KiB
C++
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;
|
|
}
|
|
}
|