Cogs.Core
MeshOpCommands.cpp
1#include "MeshOpCommands.h"
2#include "Rendering/Common.h"
3
4#include "Platform/Instrumentation.h"
5#include "../Extensions/GeometryProcessing/GeometryProcessing.h"
6
7#include "imgui.h"
8
10{
11 items.transforms.clear();
12 items.meshes.clear();
13 getMeshItems(context, items, state->selected, ~0u);
14 if (items.meshes.empty()) return;
15
16 unpackMeshItems(context, originalMeshes, meshes, items, opts);
17 // Do nothing with meshes...
18 packMeshItems(context, originalMeshes, meshes, items, true);
19}
20
22{
23 items.transforms.clear();
24 items.meshes.clear();
25 getMeshItems(context, items, state->selected, ~0u);
26 if (items.meshes.empty()) return;
27
28 unpackMeshItems(context, originalMeshes, meshes, items, opts);
29
30 modal = true;
31}
33{
34 return modal;
35}
36
38{
39 assert(modal);
40
41 ImGui::OpenPopup("No operation");
42 if (ImGui::BeginPopupModal("No operation", nullptr)) {
43 if (ImGui::Button("OK", ImVec2(120, 0))) { modal = false; ImGui::CloseCurrentPopup(); }
44 //ImGui::SetItemDefaultFocus();
45 ImGui::SameLine();
46 if (ImGui::Button("Cancel", ImVec2(120, 0))) { cancel = true; modal = false; ImGui::CloseCurrentPopup(); }
47 ImGui::EndPopup();
48 }
49 if (!modal) {
50 // Do nothing with meshes...
51 packMeshItems(context, originalMeshes, meshes, items, cancel == false);
52 }
53}
54
55void Cogs::Core::NoOpModalCommand::undo()
56{
57 packMeshItems(context, originalMeshes, meshes, items, false);
58}
59
60
61
62// ---------------------------------------------------------------------------
63
64
66{
67 items.transforms.clear();
68 items.meshes.clear();
69 getMeshItems(context, items, state->selected, primitiveMask);
70 if (items.meshes.empty()) return;
71
72 unpackMeshItems(context, originalMeshes, meshes, items, opts);
73 newMeshes.resize(meshes.size());
74
75 running = true;
76 modal = issueTask();
77}
78
80{
81 if (running == false) {
82 if (task.isValid()) {
83 context->taskManager->wait(task);
84 task = NoTask;
85 packMeshItems(context, originalMeshes, newMeshes, items, cancel == false);
86 }
87 if (modal && !cancel && dirty) {
88 running = true;
89 dirty = false;
90 modal = issueTask();
91 }
92 }
93
94 return modal;
95}
96
97void Cogs::Core::AbstractLiveUpdateCommand::undo()
98{
99 packMeshItems(context, originalMeshes, newMeshes, items, false);
100}
101
102
104{
105 if (!modal) {
106 if (task.isValid()) {
107 context->taskManager->wait(task);
108 task = NoTask;
109 packMeshItems(context, originalMeshes, newMeshes, items, cancel == false);
110 }
111 else if (cancel) {
112 packMeshItems(context, originalMeshes, newMeshes, items, false);
113 }
114 }
115}
116
117
118// ---------------------------------------------------------------------------
119
120
121Cogs::Core::GenerateNormalsCommand::GenerateNormalsCommand(EditorState * state)
122 : AbstractLiveUpdateCommand(state, 1 << Cogs::PrimitiveType::TriangleList)
123{
124 opts.discardNormals = true;
125 opts.discardTexCoords = true;
126 opts.forceIndexed = true;
127}
128
129
131{
132 for (auto & option : options) {
133 if (option.key == "featureAngle") option.asFloat(featureAngle);
134 else if (option.key == "protrusionAngle") option.asFloat(protrusionAngle);
135 else if (option.key == "flip") option.asBool(flip);
136 }
137
138 items.transforms.clear();
139 items.meshes.clear();
140 getMeshItems(context, items, state->selected, primitiveMask);
141 if (items.meshes.empty()) return;
142
143 unpackMeshItems(context, originalMeshes, meshes, items, opts);
144 newMeshes.resize(meshes.size());
145 if (issueTask() && task.isValid()) {
146 context->taskManager->wait(task);
147 task = NoTask;
148 packMeshItems(context, originalMeshes, newMeshes, items, true);
149 }
150}
151
152
154{
155 assert(modal);
156
157 ImGui::OpenPopup("Generate normals");
158 if (ImGui::BeginPopupModal("Generate normals", nullptr)) {
159
160 if (ImGui::SliderAngle("Feature angle", &featureAngle, 0.f, 180.f)) {
161 dirty = true;
162 }
163
164 if (ImGui::SliderAngle("Protrusion angle", &protrusionAngle, 0.f, 180.f)) {
165 dirty = true;
166 }
167
168 if (ImGui::Checkbox("Flip normals", &flip)) {
169 dirty = true;
170 }
171
172 ImGui::Separator();
173
174 if (ImGui::Button("OK", ImVec2(120, 0))) { modal = false; ImGui::CloseCurrentPopup(); }
175 //ImGui::SetItemDefaultFocus();
176 ImGui::SameLine();
177 if (ImGui::Button("Cancel", ImVec2(120, 0))) { cancel = true; modal = false; ImGui::CloseCurrentPopup(); }
178 ImGui::EndPopup();
179 }
181}
182
183bool Cogs::Core::GenerateNormalsCommand::issueTask()
184{
185
186 task = context->taskManager->enqueue(context->taskManager->GlobalQueue,
187 [that = this, fa = featureAngle, pa = protrusionAngle, fl =flip]()
188 {
189 CpuInstrumentationScope(SCOPE_EDITOR_COMMAND, "MeshOpCmd::GenNrmTask");
190
191 for (size_t i = 0; i<that->meshes.size(); i++) {
192 auto & mesh = that->newMeshes[i];
193 mesh.copy(that->meshes[i]);
194
195 std::vector<uint32_t> remap;
196 std::vector<uint32_t> newIndices;
197 std::vector<glm::vec3> N;
198
199 GeometryProcessing::normalsFromIndexedTriangles(that->context,
200 N, remap, newIndices,
201 reinterpret_cast<const float*>(mesh.positions.data()), 3 * sizeof(float), static_cast<uint32_t>(mesh.positions.size()),
202 mesh.indices.data(), static_cast<uint32_t>(mesh.indices.size()), fa, pa, fl);
203 assert(N.size() == remap.size());
204
205 remapVertices(that->context, mesh, remap);
206
207 mesh.indices.resize(newIndices.size());
208 std::memcpy(mesh.indices.data(), newIndices.data(), sizeof(uint32_t)*newIndices.size());
209
210 mesh.normals.resize(remap.size(), false);
211 std::memcpy(mesh.normals.data(), N.data(), mesh.normals.byteSize());
212 }
213 that->running = false;
214 });
215 return true;
216}
217
218// ---------------------------------------------------------------------------
219
220
221Cogs::Core::UniqueVerticesCommand::UniqueVerticesCommand(EditorState * state)
222 : AbstractLiveUpdateCommand(state)
223{
224 opts.forceIndexed = true;
225}
226
227
229{
230 for (auto & option : options) {
231 if (option.key == "keepNormals") option.asBool(keepNormals);
232 else if (option.key == "keepTexCoords") option.asBool(keepTexCoords);
233 else if (option.key == "keepTangents") option.asBool(keepTangents);
234 }
235
236 items.transforms.clear();
237 items.meshes.clear();
238 getMeshItems(context, items, state->selected, primitiveMask);
239 if (items.meshes.empty()) return;
240
241 unpackMeshItems(context, originalMeshes, meshes, items, opts);
242 newMeshes.resize(meshes.size());
243 if (issueTask() && task.isValid()) {
244 context->taskManager->wait(task);
245 task = NoTask;
246 packMeshItems(context, originalMeshes, newMeshes, items, true);
247 }
248}
249
250
252{
253 assert(modal);
254
255 ImGui::OpenPopup("Remove duplicate vertices");
256 if (ImGui::BeginPopupModal("Remove duplicate vertices", NULL)) {
257
258 if (ImGui::InputFloat("Epsilon", &epsilon)) { dirty = true; }
259 if (hasNormals && ImGui::Checkbox("Keep normals", &keepNormals)) { dirty = true; }
260 if (hasTexCoords && ImGui::Checkbox("Keep texcoords", &keepTexCoords)) { dirty = true; }
261 if (hasTangents && ImGui::Checkbox("Keep tangents", &keepTangents)) { dirty = true; }
262
263 ImGui::Separator();
264
265 if (ImGui::Button("OK", ImVec2(120, 0))) { modal = false; ImGui::CloseCurrentPopup(); }
266 //ImGui::SetItemDefaultFocus();
267 ImGui::SameLine();
268 if (ImGui::Button("Cancel", ImVec2(120, 0))) { cancel = true; modal = false; ImGui::CloseCurrentPopup(); }
269 ImGui::EndPopup();
270 }
272}
273
274bool Cogs::Core::UniqueVerticesCommand::issueTask()
275{
276 hasNormals = false;
277 hasTexCoords = false;
278 hasTangents = false;
279 for (auto & mesh : meshes) {
280 hasNormals = hasNormals || mesh.normals.size();
281 hasTexCoords = hasTexCoords || mesh.texcoords.size();
282 hasTangents = hasTangents || mesh.tangents.size();
283 }
284
285 task = context->taskManager->enqueue(context->taskManager->GlobalQueue,
286 [that = this, keepN = keepNormals, keepTC = keepTexCoords, keepT = keepTangents, e = epsilon]()
287 {
288 CpuInstrumentationScope(SCOPE_EDITOR_COMMAND, "MeshOpCmd::UniqVtxTask");
289
290 for (size_t i = 0; i < that->meshes.size(); i++) {
291 auto & mesh = that->newMeshes[i];
292 mesh.copy(that->meshes[i]);
293
294 if (!keepN) mesh.normals.clear();
295 if (!keepTC) mesh.texcoords.clear();
296 if (!keepT) mesh.tangents.clear();
297
298 std::vector<uint32_t> unique;
299 std::vector<uint32_t> map;
300 GeometryProcessing::uniqueVertexSubset(that->context, unique, map,
301 (float*)mesh.positions.data(),
302 3 * sizeof(float),
303 mesh.normals.size() ? (float*)mesh.normals.data() : nullptr,
304 3 * sizeof(float),
305 mesh.texcoords.size() ? (float*)mesh.texcoords.data() : nullptr,
306 2 * sizeof(float),
307 static_cast<uint32_t>(mesh.positions.size()), e);
308
309 remapVertices(that->context, mesh, unique);
310 remapIndices(that->context, mesh, map);
311 }
312 that->running = false;
313 });
314 return true;
315}
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
void beginModal() override
Called when the command is initiated.
bool continueModal() override
Shall return true while the GUI should still be shown. False when.
void showGui() override
Display custom ImGUI.
void showGui() override
Display custom ImGUI.
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 apply() override
Run the command.
void showGui() override
Display custom ImGUI.
void apply() override
Run the command.
void showGui() override
Display custom ImGUI.
Primitive types for interpreting vertex data sent to the graphics pipeline.
Definition: Common.h:111