3#include "Commands/CommandHelpers.h"
5#include "RRSimplifyCommand.h"
6#include "Rendering/Common.h"
8#include "Resources/Mesh.h"
9#include "Resources/MeshManager.h"
11#include "Platform/Instrumentation.h"
13#include <rrapi/rrapi.h>
16 const char * label1 =
"RR Mesh Simplification";
17 const char * label2 =
"Simplification in progress";
35Cogs::Core::RRSimplifiyCommand::RRSimplifiyCommand(EditorState * state)
36: ModalEditorCommand(state)
38 taskState.context = state->context;
39 opts.forceIndexed =
true;
40 opts.discardNormals =
true;
47 if (option.key ==
"epsilon") option.asFloat(epsilon);
48 else if (option.key ==
"reduction") option.asFloat(reduction);
49 else if (option.key ==
"error") option.asFloat(error);
50 else if (option.key ==
"errorGuided") option.asBool(error_guided);
52 else if (option.key ==
"featureAngle") option.asFloat(normalGenArgs.featureAngle);
53 else if (option.key ==
"protrusionAngle") option.asFloat(normalGenArgs.protrusionAngle);
54 else if (option.key ==
"flip") option.asBool(normalGenArgs.flip);
56 else if (option.key ==
"edgeLengthWeight") option.asFloat(edgeLengthWeight);
59 taskState.items.transforms.clear();
60 taskState.items.meshes.clear();
62 if (taskState.items.meshes.empty())
return;
66 taskState.meshSets.clear();
67 taskState.meshSets.emplace_back();
68 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
71 taskState.running =
true;
80 task.edgeLengthWeight = edgeLengthWeight;
85 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel ==
false);
88void Cogs::Core::RRSimplifiyCommand::undo()
90 packMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items,
false);
93void Cogs::Core::RRSimplifiyCommand::redo()
95 if (taskState.cancel)
return;
97 taskState.items.transforms.clear();
98 taskState.items.meshes.clear();
100 if (taskState.items.meshes.empty())
return;
102 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel ==
false);
120 taskState.items.transforms.clear();
121 taskState.items.meshes.clear();
129 if (taskState.running ==
false) {
132 ImGui::OpenPopup(label1);
133 if (ImGui::BeginPopupModal(label1, NULL)) {
135 ImGui::InputFloat(
"Vertex merge epsilon", &epsilon);
137 const char* modes[] = {
"Error-driven",
"Triangle count-driven" };
138 int mode_current = error_guided ? 0 : 1;
140 ImGui::Combo(
"Simplification guide", &mode_current, modes, 2);
141 error_guided = mode_current == 0;
144 ImGui::InputFloat(
"Min-max error threhold", &error);
147 ImGui::SliderFloat(
"Reduction", &reduction, 0.f, 100.f,
"%.1f %%");
152 ImGui::SliderAngle(
"Feature angle", &normalGenArgs.featureAngle, 0.f, 180.f);
153 ImGui::SliderAngle(
"Protrusion angle", &normalGenArgs.protrusionAngle, 0.f, 180.f);
154 ImGui::Checkbox(
"Flip normals", &normalGenArgs.flip);
158 if (ImGui::Button(
"Simplify", ImVec2(120, 0))) {
159 taskState.items.transforms.clear();
160 taskState.items.meshes.clear();
162 if (!taskState.items.meshes.empty()) {
164 taskState.meshSets.clear();
165 taskState.meshSets.emplace_back();
166 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
168 taskState.running =
true;
171 task = context->taskManager->enqueue(context->taskManager->GlobalQueue,
RRMinMaxErrorGuidedTask(&taskState,
false, epsilon, error, normalGenArgs));
174 task = context->taskManager->enqueue(context->taskManager->GlobalQueue,
RRTriangleGuidedTask(&taskState,
false, epsilon, reduction, normalGenArgs));
183 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
184 taskState.cancel =
true;
186 ImGui::CloseCurrentPopup();
195 ImGui::OpenPopup(label2);
196 if (ImGui::BeginPopupModal(label2, NULL)) {
197 ImGui::ProgressBar(taskState.progress);
200 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
201 taskState.cancel =
true;
211 if (!taskState.running && task.isValid()) {
212 context->taskManager->wait(task);
215 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel ==
false);
222 for (
auto & option : options) {
224 if (option.key ==
"epsilon") option.asFloat(epsilon);
225 else if (option.key ==
"reduction") option.asFloat(reduction);
226 else if (option.key ==
"error") option.asFloat(error);
227 else if (option.key ==
"errorGuided") option.asBool(error_guided);
229 else if (option.key ==
"featureAngle") option.asFloat(normalGenArgs.featureAngle);
230 else if (option.key ==
"protrusionAngle") option.asFloat(normalGenArgs.protrusionAngle);
231 else if (option.key ==
"flip") option.asBool(normalGenArgs.flip);
233 else if (option.key ==
"edgeLengthWeight") option.asFloat(edgeLengthWeight);
236 taskState.items.transforms.clear();
237 taskState.items.meshes.clear();
243 if (((1 <<
static_cast<uint32_t
>(mesh->primitiveType)) & primitiveTypeMask) != 0) {
244 taskState.items.meshes.emplace_back(
MeshItem{ mesh,
nullptr,
nullptr,
nullptr, 0u, ~0u, ~0u });
249 if (taskState.items.meshes.empty())
return mesh;
253 taskState.meshSets.clear();
254 taskState.meshSets.emplace_back();
255 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
258 taskState.running =
true;
267 task.edgeLengthWeight = edgeLengthWeight;
274 auto & meshes = taskState.meshSets.back().meshes;
276 assert(meshes.size() <= 1);
277 if (meshes.size() == 0) {
280 const auto & meshRep = meshes[0];
282 if (meshRep.positions.size() == 0) {
286 auto newMeshHandle = context->meshManager->create();
288 auto * newMesh = newMeshHandle.resolve();
290 auto bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
291 for (
size_t i = 0; i < meshRep.positions.size(); i++) {
292 bbox.min = glm::min(bbox.min, meshRep.positions[i]);
293 bbox.max = glm::max(bbox.max, meshRep.positions[i]);
295 newMesh->setBounds(bbox);
297 newMesh->setPositions(meshRep.positions.data(), meshRep.positions.data() + meshRep.positions.size());
298 if (meshRep.normals.size() != 0) {
299 newMesh->setNormals(meshRep.normals.data(), meshRep.normals.data() + meshRep.normals.size());
301 if (meshRep.texcoords.size() != 0) {
302 newMesh->setTexCoords(meshRep.texcoords.data(), meshRep.texcoords.data() + meshRep.texcoords.size());
304 if (meshRep.tangents.size() != 0) {
305 newMesh->setTangents(meshRep.tangents.data(), meshRep.tangents.data() + meshRep.tangents.size());
307 if (meshRep.indices.size() != 0) {
308 newMesh->setIndexData(meshRep.indices.data(), meshRep.indices.size());
311 auto * originalMesh = originalMeshes[0].resolve();
319 newMesh->primitiveType = originalMesh->primitiveType;
321 return newMeshHandle;
@ TriangleList
List of triangles.
std::vector< ParsedValue > options
Options passed to the command when running in batch mode.
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
void apply() override
Run the command.
bool continueModal() override
Shall return true while the GUI should still be shown. False when.
void beginModal() override
Called when the command is initiated.
void showGui() override
Display custom ImGUI.
MeshHandle applyMesh(MeshHandle mesh) override
Workaround for having extendable mesh processing available without linking (e.g command -> RR).