Files
WickedEngine/Editor/VoxelGridWindow.cpp
T
2025-06-01 17:07:12 +02:00

276 lines
10 KiB
C++

#include "stdafx.h"
#include "VoxelGridWindow.h"
using namespace wi::ecs;
using namespace wi::scene;
void VoxelGridWindow::Create(EditorComponent* _editor)
{
editor = _editor;
wi::gui::Window::Create(ICON_VOXELGRID " VoxelGrid", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE | wi::gui::Window::WindowControls::FIT_ALL_WIDGETS_VERTICAL);
SetSize(XMFLOAT2(520, 480));
closeButton.SetTooltip("Delete VoxelGrid");
OnClose([=](wi::gui::EventArgs args) {
wi::Archive& archive = editor->AdvanceHistory();
archive << EditorComponent::HISTORYOP_COMPONENT_DATA;
editor->RecordEntity(archive, entity);
editor->GetCurrentScene().voxel_grids.Remove(entity);
editor->RecordEntity(archive, entity);
editor->componentsWnd.RefreshEntityTree();
});
infoLabel.Create("");
infoLabel.SetFitTextEnabled(true);
AddWidget(&infoLabel);
dimXInput.Create("DimX");
dimXInput.SetDescription("Dimension X: ");
dimXInput.OnInputAccepted([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
voxelgrid->init(uint32_t(std::max(1, args.iValue)), voxelgrid->resolution.y, voxelgrid->resolution.z);
});
AddWidget(&dimXInput);
dimYInput.Create("DimY");
dimYInput.SetDescription("Y: ");
dimYInput.OnInputAccepted([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
voxelgrid->init(voxelgrid->resolution.x, uint32_t(std::max(1, args.iValue)), voxelgrid->resolution.z);
});
AddWidget(&dimYInput);
dimZInput.Create("DimZ");
dimZInput.SetDescription("Z: ");
dimZInput.OnInputAccepted([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
voxelgrid->init(voxelgrid->resolution.x, voxelgrid->resolution.y, uint32_t(std::max(1, args.iValue)));
});
AddWidget(&dimZInput);
clearButton.Create("Clear voxels " ICON_CLEARVOXELS);
clearButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
voxelgrid->cleardata();
});
AddWidget(&clearButton);
voxelizeObjectsButton.Create("Voxelize objects " ICON_VOXELIZE);
voxelizeObjectsButton.SetTooltip("Generate navigation grid including all meshes.");
voxelizeObjectsButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
scene.VoxelizeScene(*voxelgrid, subtractCheckBox.GetCheck(), wi::enums::FILTER_OBJECT_ALL);
});
AddWidget(&voxelizeObjectsButton);
voxelizeNavigationButton.Create("Voxelize navigation " ICON_VOXELIZE);
voxelizeNavigationButton.SetTooltip("Generate navigation grid including all navmeshes (object tagged as navmesh).");
voxelizeNavigationButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
scene.VoxelizeScene(*voxelgrid, subtractCheckBox.GetCheck(), wi::enums::FILTER_NAVIGATION_MESH);
});
AddWidget(&voxelizeNavigationButton);
voxelizeCollidersButton.Create("Voxelize CPU colliders " ICON_VOXELIZE);
voxelizeCollidersButton.SetTooltip("Generate navigation grid including all CPU colliders.");
voxelizeCollidersButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
scene.VoxelizeScene(*voxelgrid, subtractCheckBox.GetCheck(), wi::enums::FILTER_COLLIDER);
});
AddWidget(&voxelizeCollidersButton);
floodfillButton.Create("Flood fill " ICON_VOXELFILL);
floodfillButton.SetTooltip("Fill enclosed empty voxel areas to solid.\nThis can take long if there are large enclosed empty areas.");
floodfillButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
wi::Timer tim;
voxelgrid->flood_fill();
wilog(text, arraysize(text), "Flood filling took %.2f seconds.", tim.elapsed_seconds());
});
AddWidget(&floodfillButton);
fitToSceneButton.Create("Fit bounds to scene " ICON_VOXELBOUNDS);
fitToSceneButton.SetTooltip("Fit the bounds of the voxel grid onto the whole scene.");
fitToSceneButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
if (scene.bounds.getArea() < 0)
return;
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
voxelgrid->from_aabb(scene.bounds);
TransformComponent* transform = scene.transforms.GetComponent(entity);
if (transform != nullptr)
{
// feed back to transform component if it exists:
transform->translation_local = voxelgrid->center;
transform->scale_local = voxelgrid->voxelSize;
transform->SetDirty();
}
});
AddWidget(&fitToSceneButton);
generateMeshButton.Create("Generate Mesh " ICON_MESH);
generateMeshButton.SetTooltip("Generate a mesh from the voxels.");
generateMeshButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
wi::vector<uint32_t> indices;
wi::vector<XMFLOAT3> vertices;
voxelgrid->create_mesh(indices, vertices, false);
if (vertices.empty())
{
wi::backlog::post("VoxelGrid.create_mesh() : no voxels were found, so mesh creation is aborted.", wi::backlog::LogLevel::Warning);
return;
}
scene.Entity_CreateMeshFromData("voxelgrid_to_mesh", indices.size(), indices.data(), vertices.size(), vertices.data());
});
AddWidget(&generateMeshButton);
generateSimplifiedMeshButton.Create("Generate Simplified Mesh " ICON_MESH);
generateSimplifiedMeshButton.SetTooltip("Generate a simplified mesh from the voxels.");
generateSimplifiedMeshButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
wi::vector<uint32_t> indices;
wi::vector<XMFLOAT3> vertices;
voxelgrid->create_mesh(indices, vertices, true);
if (vertices.empty())
{
wi::backlog::post("VoxelGrid.create_mesh() : no voxels were found, so mesh creation is aborted.", wi::backlog::LogLevel::Warning);
return;
}
scene.Entity_CreateMeshFromData("voxelgrid_to_mesh", indices.size(), indices.data(), vertices.size(), vertices.data());
});
AddWidget(&generateSimplifiedMeshButton);
generateNavMeshButton.Create("Generate Navigation Mesh " ICON_NAVIGATION);
generateNavMeshButton.SetTooltip("Generate a simplified navigation mesh from the voxels.");
generateNavMeshButton.OnClick([=](wi::gui::EventArgs args) {
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid == nullptr)
return;
wi::vector<uint32_t> indices;
wi::vector<XMFLOAT3> vertices;
voxelgrid->create_mesh(indices, vertices, true, &scene);
if (vertices.empty())
{
wi::backlog::post("VoxelGrid.create_mesh() : no voxels were found, so mesh creation is aborted.", wi::backlog::LogLevel::Warning);
return;
}
Entity entity = scene.Entity_CreateMeshFromData("voxelgrid_to_navmesh", indices.size(), indices.data(), vertices.size(), vertices.data());
ObjectComponent* object = scene.objects.GetComponent(entity);
if (object != nullptr)
{
object->filterMask |= wi::enums::FILTER_NAVIGATION_MESH;
MeshComponent* mesh = scene.meshes.GetComponent(object->meshID);
if (mesh != nullptr)
{
mesh->SetBVHEnabled(true);
}
}
});
AddWidget(&generateNavMeshButton);
subtractCheckBox.Create("Subtraction mode: ");
subtractCheckBox.SetTooltip("If enabled, voxelization will be subtractive, so it will remove voxels instead of add.");
AddWidget(&subtractCheckBox);
debugAllCheckBox.Create("Debug draw all: ");
debugAllCheckBox.SetTooltip("Draw all voxel grids, whether they are selected or not.");
AddWidget(&debugAllCheckBox);
SetMinimized(true);
SetVisible(false);
}
void VoxelGridWindow::SetEntity(Entity entity)
{
bool changed = this->entity != entity;
this->entity = entity;
Scene& scene = editor->GetCurrentScene();
wi::VoxelGrid* voxelgrid = scene.voxel_grids.GetComponent(entity);
if (voxelgrid != nullptr)
{
std::string infotext = "Voxel grid can be used for navigation. By voxelizing the scene into the grid, path finding can be used on the resulting voxel grid.";
infotext += "\n\nMemory usage: ";
infotext += wi::helper::GetMemorySizeText(voxelgrid->get_memory_size());
infoLabel.SetText(infotext);
dimXInput.SetValue((int)voxelgrid->resolution.x);
dimYInput.SetValue((int)voxelgrid->resolution.y);
dimZInput.SetValue((int)voxelgrid->resolution.z);
}
}
void VoxelGridWindow::ResizeLayout()
{
wi::gui::Window::ResizeLayout();
layout.margin_left = 90;
layout.add_fullwidth(infoLabel);
const float padding2 = 20;
const float l = 95;
const float r = layout.width;
float w = ((r - l) - padding2 * 2) / 3.0f;
dimXInput.SetSize(XMFLOAT2(w, dimXInput.GetSize().y));
dimYInput.SetSize(XMFLOAT2(w, dimYInput.GetSize().y));
dimZInput.SetSize(XMFLOAT2(w, dimZInput.GetSize().y));
dimXInput.SetPos(XMFLOAT2(layout.margin_left, layout.y));
dimYInput.SetPos(XMFLOAT2(dimXInput.GetPos().x + w + padding2, layout.y));
dimZInput.SetPos(XMFLOAT2(dimYInput.GetPos().x + w + padding2, layout.y));
layout.y += dimZInput.GetSize().y;
layout.y += layout.padding;
layout.add_fullwidth(clearButton);
layout.add_fullwidth(voxelizeObjectsButton);
layout.add_fullwidth(voxelizeCollidersButton);
layout.add_fullwidth(voxelizeNavigationButton);
layout.add_fullwidth(floodfillButton);
layout.add_fullwidth(fitToSceneButton);
layout.add_fullwidth(generateMeshButton);
layout.add_fullwidth(generateSimplifiedMeshButton);
layout.add_fullwidth(generateNavMeshButton);
layout.add_right(subtractCheckBox);
layout.add_right(debugAllCheckBox);
}