Cogs.Core
RRSimplifyCommand.cpp
1#include <cassert>
2#include "imgui.h"
3#include "Commands/CommandHelpers.h"
4
5#include "RRSimplifyCommand.h"
6#include "Rendering/Common.h"
7
8#include "Resources/Mesh.h"
9#include "Resources/MeshManager.h"
10
11#include "Platform/Instrumentation.h"
12
13#include <rrapi/rrapi.h>
14
15namespace {
16 const char * label1 = "RR Mesh Simplification";
17 const char * label2 = "Simplification in progress";
18
19
20 //struct RRTaskState
21 //{
22 // bool textured;
23 // void * rrHandle;
24 // Cogs::Core::MeshItems* items;
25 // Cogs::Core::MeshReps* meshes;
26 // std::atomic<float>* progress;
27 // std::atomic<bool>* cancel;
28 //};
29
30
31
32
33}
34
35Cogs::Core::RRSimplifiyCommand::RRSimplifiyCommand(EditorState * state)
36: ModalEditorCommand(state)
37{
38 taskState.context = state->context;
39 opts.forceIndexed = true;
40 opts.discardNormals = true;
41}
42
44{
45 for (auto & option : options) {
46
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);
51
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);
55
56 else if (option.key == "edgeLengthWeight") option.asFloat(edgeLengthWeight);
57 }
58
59 taskState.items.transforms.clear();
60 taskState.items.meshes.clear();
61 getMeshItems(context, taskState.items, state->selected, 1 << static_cast<uint32_t>(Cogs::PrimitiveType::TriangleList));
62 if (taskState.items.meshes.empty()) return;
63
64
65 // Pull input meshes from Cogs into temporary structs
66 taskState.meshSets.clear();
67 taskState.meshSets.emplace_back();
68 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
69
70 modal = false;
71 taskState.running = true;
72 task = NoTask;
73
74 if (error_guided) {
75 RRMinMaxErrorGuidedTask task(&taskState, false, epsilon, error, normalGenArgs);
76 task();
77 }
78 else {
79 RRTriangleGuidedTask task(&taskState, false, epsilon, reduction, normalGenArgs);
80 task.edgeLengthWeight = edgeLengthWeight;
81 task();
82 }
83
84 // Push meshes back to Cogs.
85 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel == false);
86}
87
88void Cogs::Core::RRSimplifiyCommand::undo()
89{
90 packMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, false);
91}
92
93void Cogs::Core::RRSimplifiyCommand::redo()
94{
95 if (taskState.cancel) return;
96
97 taskState.items.transforms.clear();
98 taskState.items.meshes.clear();
99 getMeshItems(context, taskState.items, state->selected, 1 << static_cast<uint32_t>(Cogs::PrimitiveType::TriangleList));
100 if (taskState.items.meshes.empty()) return;
101
102 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel == false);
103
104 //unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
105
106 //modal = true;
107 //taskState.running = true;
109 //if (error_guided) {
110 // task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRMinMaxErrorGuidedTask(&taskState, false, epsilon, error));
111 //}
112 //else {
113 // task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRTriangleGuidedTask(&taskState, false, epsilon, reduction));
114 //}
115}
116
117
119{
120 taskState.items.transforms.clear();
121 taskState.items.meshes.clear();
122 getMeshItems(context, taskState.items, state->selected, 1 << static_cast<uint32_t>(Cogs::PrimitiveType::TriangleList));
123
124 modal = true;
125}
126
128{
129 if (taskState.running == false) {
130
131 // Config gui
132 ImGui::OpenPopup(label1);
133 if (ImGui::BeginPopupModal(label1, NULL)) {
134
135 ImGui::InputFloat("Vertex merge epsilon", &epsilon);
136
137 const char* modes[] = { "Error-driven", "Triangle count-driven" };
138 int mode_current = error_guided ? 0 : 1;
139
140 ImGui::Combo("Simplification guide", &mode_current, modes, 2);
141 error_guided = mode_current == 0;
142
143 if (error_guided) {
144 ImGui::InputFloat("Min-max error threhold", &error);
145 }
146 else {
147 ImGui::SliderFloat("Reduction", &reduction, 0.f, 100.f, "%.1f %%");
148 }
149
150 ImGui::Separator();
151
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);
155
156 ImGui::Separator();
157
158 if (ImGui::Button("Simplify", ImVec2(120, 0))) {
159 taskState.items.transforms.clear();
160 taskState.items.meshes.clear();
161 getMeshItems(context, taskState.items, state->selected, 1 << static_cast<uint32_t>(Cogs::PrimitiveType::TriangleList));
162 if (!taskState.items.meshes.empty()) {
163
164 taskState.meshSets.clear();
165 taskState.meshSets.emplace_back();
166 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
167
168 taskState.running = true;
169 //task = context->taskManager->enqueue(context->taskManager->GlobalQueue, std::bind(&RRSimplifiyCommand::taskFunc, this));
170 if (error_guided) {
171 task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRMinMaxErrorGuidedTask(&taskState, false, epsilon, error, normalGenArgs));
172 }
173 else {
174 task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRTriangleGuidedTask(&taskState, false, epsilon, reduction, normalGenArgs));
175 }
176 }
177 else {
178 modal = false;
179 }
180 }
181 //ImGui::SetItemDefaultFocus();
182 ImGui::SameLine();
183 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
184 taskState.cancel = true;
185 modal = false;
186 ImGui::CloseCurrentPopup();
187 }
188 ImGui::EndPopup();
189 }
190
191 }
192 else {
193
194 // Progress gui
195 ImGui::OpenPopup(label2);
196 if (ImGui::BeginPopupModal(label2, NULL)) {
197 ImGui::ProgressBar(taskState.progress);
198
199 ImGui::Separator();
200 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
201 taskState.cancel = true;
202 }
203 ImGui::EndPopup();
204 }
205 }
206
207}
208
210{
211 if (!taskState.running && task.isValid()) {
212 context->taskManager->wait(task);
213 task = NoTask;
214 modal = false;
215 packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel == false);
216 }
217 return modal;
218}
219
221{
222 for (auto & option : options) {
223
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);
228
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);
232
233 else if (option.key == "edgeLengthWeight") option.asFloat(edgeLengthWeight);
234 }
235
236 taskState.items.transforms.clear();
237 taskState.items.meshes.clear();
238 //getMeshItems(context, taskState.items, state->selected, 1 << Cogs::PrimitiveType::TriangleList);
239
240 auto primitiveTypeMask = 1 << static_cast<uint32_t>(Cogs::PrimitiveType::TriangleList);
241
242 if (mesh) {
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 });
245 }
246 }
247
248
249 if (taskState.items.meshes.empty()) return mesh;
250
251
252 // Pull input meshes from Cogs into temporary structs
253 taskState.meshSets.clear();
254 taskState.meshSets.emplace_back();
255 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
256
257 modal = false;
258 taskState.running = true;
259 task = NoTask;
260
261 if (error_guided) {
262 RRMinMaxErrorGuidedTask task(&taskState, false, epsilon, error, normalGenArgs);
263 task();
264 }
265 else {
266 RRTriangleGuidedTask task(&taskState, false, epsilon, reduction, normalGenArgs);
267 task.edgeLengthWeight = edgeLengthWeight;
268 task();
269 }
270
271 // Push meshes back to Cogs.
272 //packMeshItems(context, originalMeshes, taskState.meshSets.back().meshes, taskState.items, taskState.cancel == false);
273
274 auto & meshes = taskState.meshSets.back().meshes;
275
276 assert(meshes.size() <= 1);
277 if (meshes.size() == 0) {
278 return {};
279 }
280 const auto & meshRep = meshes[0];
281
282 if (meshRep.positions.size() == 0) {
283 return {};
284 }
285
286 auto newMeshHandle = context->meshManager->create();
287
288 auto * newMesh = newMeshHandle.resolve();
289
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]);
294 }
295 newMesh->setBounds(bbox);
296
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());
300 }
301 if (meshRep.texcoords.size() != 0) {
302 newMesh->setTexCoords(meshRep.texcoords.data(), meshRep.texcoords.data() + meshRep.texcoords.size());
303 }
304 if (meshRep.tangents.size() != 0) {
305 newMesh->setTangents(meshRep.tangents.data(), meshRep.tangents.data() + meshRep.tangents.size());
306 }
307 if (meshRep.indices.size() != 0) {
308 newMesh->setIndexData(meshRep.indices.data(), meshRep.indices.size());
309 }
310
311 auto * originalMesh = originalMeshes[0].resolve();
312 if (originalMesh->isMeshFlagSet(MeshFlags::ClockwiseWinding)) {
313 newMesh->setMeshFlag(MeshFlags::ClockwiseWinding);
314 }
315 else {
316 newMesh->unsetMeshFlag(MeshFlags::ClockwiseWinding);
317 }
318
319 newMesh->primitiveType = originalMesh->primitiveType;
320
321 return newMeshHandle;
322}
@ TriangleList
List of triangles.
std::vector< ParsedValue > options
Options passed to the command when running in batch mode.
Definition: EditorCommand.h:61
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
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).