Cogs.Core
RRMultiSimplifyCommand.cpp
1#include "imgui.h"
2#include <charconv>
3#include "Components/Core/SceneComponent.h"
4#include "Components/Core/LodComponent.h"
5#include "Components/Core/TransformComponent.h"
6#include "Components/Core/MeshRenderComponent.h"
7#include "Resources/MaterialInstance.h"
8#include "Resources/MaterialManager.h"
9#include "EntityStore.h"
10
11#include "Editor/Editor.h"
12#include "Commands/EditorCommand.h"
13#include "Rendering/Common.h"
14
15#include "RRMultiSimplifyCommand.h"
16
17namespace {
18 using namespace Cogs::Core;
19}
20
21Cogs::Core::RRMultiSimplifyCommand::RRMultiSimplifyCommand(EditorState * state)
22 : ModalEditorCommand(state)
23{
24 taskState.context = state->context;
25}
26
27bool Cogs::Core::RRMultiSimplifyCommand::initTaskData()
28{
29 // Extract all entities with an appropriate mesh
30 taskState.items.transforms.clear();
31 taskState.items.meshes.clear();
32 getMeshItems(context, taskState.items, state->selected, 1 << Cogs::PrimitiveType::TriangleList);
33 if (taskState.items.meshes.empty()) return false;
34
35 UnpackOptions opts;
36 opts.discardNormals = true;
37 opts.discardTexCoords = false;
38 opts.discardTangents = true;
39 opts.forceIndexed = false;
40
41 taskState.meshSets.clear();
42 taskState.meshSets.emplace_back();
43 unpackMeshItems(context, originalMeshes, taskState.meshSets.front().meshes, taskState.items, opts);
44
45 return true;
46}
47
48namespace {
49
50 void descendAndColorize(Cogs::ComponentModel::Entity* entity, MaterialInstanceHandle material)
51 {
52 if (auto * sceneComp = entity->getComponent<SceneComponent>()) {
53 for (auto child : sceneComp->children) {
54 descendAndColorize(child.get(), material);
55 }
56 }
57 if (auto * mRndComp = entity->getComponent<MeshRenderComponent>()) {
58 mRndComp->material = material;
59 }
60
61 }
62
63}
64
65void Cogs::Core::RRMultiSimplifyCommand::finishTaskData()
66{
67 if (taskState.cancel) return;
68
69 ComponentModel::Entity * parent = nullptr;
70 if (auto * sceneComp = state->getSelected()->getComponent<SceneComponent>()) {
71 if (sceneComp->parent) {
72 parent = sceneComp->parent.resolve()->getContainer();
73 }
74 }
75
76 // Delete output from previous batch run existing (expect warning first time).
77 // Don't have any "LODs" named entities...
78 auto lodGroup = context->store->getEntity("LODs");
79 if (lodGroup) {
80 const auto id = lodGroup->getId();
81 lodGroup.reset();
82 context->store->destroyEntity(id);
83 }
84
85 lodGroup = context->store->createEntity("LODs", "Lod", parent == nullptr);
86 if (parent) {
87 context->store->addChild(parent, lodGroup);
88 }
89
90 auto * lodComp = lodGroup->getComponent<LodComponent>();
92 lodComp->separateHierarchies = true;
93 lodComp->thresholds.resize(taskState.meshSets.size());
94 for (size_t i = 0; i < taskState.meshSets.size(); i++) {
95 auto & meshSet = taskState.meshSets[i];
96
97 lodComp->thresholds[i] = meshSet.error;
98
99 EntityPtr hierarchy;
100 if (singleMesh) {
101 hierarchy = createAsSingleMeshItem(context, lodGroup.get(), originalMeshes, meshSet.meshes, taskState.items);
102 }
103 else {
104 hierarchy = createNewMeshItems(context, lodGroup.get(), originalMeshes, meshSet.meshes, taskState.items);
105 }
106 hierarchy->setName("Level " + std::to_string(i));
107
108 if (debugColors) {
109 glm::vec4 c((i >> 2) & 1, (i >> 1) & 1, i & 1, 1);
110 auto material = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
111 material->setProperty("diffuseColor", glm::value_ptr(c), sizeof(c));
112 descendAndColorize(hierarchy.get(), material);
113 }
114 }
115 lodComp->setChanged();
116
117 state->selected = EntityIds{ lodGroup->getId() };
118}
119
121{
122 std::vector<float> thresholds;
123
124 for (auto & option : options) {
125 if (option.key == "epsilon") option.asFloat(epsilon);
126 else if (option.key == "thresholds") {
127 auto & val = option.value;
128
129 size_t a = 0;
130 while (a < val.length()) {
131 auto b = val.find(',', a);
132 thresholds.push_back(std::stof(val.substr(a, b == std::string::npos ? b : b - a)));
133 if (b == std::string::npos) break;
134 a = b + 1;
135 }
136 }
137 else if (option.key == "errorGuided") option.asBool(error_guided);
138
139 else if (option.key == "featureAngle") option.asFloat(normalGenArgs.featureAngle);
140 else if (option.key == "protrusionAngle") option.asFloat(normalGenArgs.protrusionAngle);
141 else if (option.key == "flip") option.asBool(normalGenArgs.flip);
142 else if (option.key == "singleMesh") option.asBool(singleMesh);
143
144 }
145
146
147 steps = std::max(1, steps);
148
149 if (initTaskData())
150 {
151 if (error_guided) {
152 RRMultiMinMaxErrorGuidedTask task(&taskState, false, epsilon, thresholds, normalGenArgs);
153 task();
154 }
155 else {
156 RRMultiTriangleGuidedTask task(&taskState, false, epsilon, thresholds, normalGenArgs);
157 task();
158 }
159
160 finishTaskData();
161 }
162}
163
164bool Cogs::Core::RRMultiSimplifyCommand::issueTask()
165{
166 if (initTaskData()) {
167 std::vector<float> thresholds(steps);
168 for (int i = 0; i < steps; i++) {
169 auto t = (i + 1.f) / steps;
170 assert(0 <= t);
171 thresholds[i] = error * t;
172 }
173 taskState.cancel = false;
174 taskState.running = true;
175 if (error_guided) {
176 task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRMultiMinMaxErrorGuidedTask(&taskState, false, epsilon, thresholds, normalGenArgs));
177 }
178 else {
179 std::vector<float> thresholds_(steps);
180 for (int i = 0; i < steps; i++) {
181 auto t = (steps - i - 1.f) / steps;
182 assert(0 <= t);
183 thresholds_[i] = 100.f * t * t * t * t;
184 }
185 task = context->taskManager->enqueue(context->taskManager->GlobalQueue, RRMultiTriangleGuidedTask(&taskState, false, epsilon, thresholds_, normalGenArgs));
186 }
187 return true;
188 }
189 return false;
190}
191
192void Cogs::Core::RRMultiSimplifyCommand::waitForTask()
193{
194 context->taskManager->wait(task);
195 task = NoTask;
196 finishTaskData();
197}
198
199void Cogs::Core::RRMultiSimplifyCommand::showParameterGui()
200{
201 const char * label = "RR multi-level mesh simplification";
202
203 ImGui::OpenPopup(label);
204 if (ImGui::BeginPopupModal(label, NULL)) {
205
206 ImGui::InputFloat("Vertex merge epsilon", &epsilon);
207
208 const char* modes[] = { "Error-driven", "Triangle count-driven" };
209 int mode_current = error_guided ? 0 : 1;
210 ImGui::Combo("Simplification guide", &mode_current, modes, 2);
211 error_guided = mode_current == 0;
212
213 if (error_guided) {
214 ImGui::InputFloat("Min-max error threhold", &error);
215 }
216 ImGui::SliderInt("Steps", &steps, 1, 25);
217
218 ImGui::Separator();
219
220 ImGui::Checkbox("Merge lod level into single mesh", &singleMesh);
221 ImGui::SliderAngle("Feature angle", &normalGenArgs.featureAngle, 0.f, 180.f);
222 ImGui::SliderAngle("Protrusion angle", &normalGenArgs.protrusionAngle, 0.f, 180.f);
223 ImGui::Checkbox("Flip normals", &normalGenArgs.flip);
224
225 ImGui::Separator();
226
227 ImGui::Checkbox("Debug-colorize levels", &debugColors);
228 ImGui::Separator();
229 if (ImGui::Button("Simplify", ImVec2(120, 0))) {
230 modal = issueTask();
231 }
232 //ImGui::SetItemDefaultFocus();
233
234 ImGui::SameLine();
235 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
236 taskState.cancel = true;
237 modal = false;
238 ImGui::CloseCurrentPopup();
239 }
240 ImGui::EndPopup();
241 }
242}
243
244void Cogs::Core::RRMultiSimplifyCommand::showProgressGui()
245{
246 const char * label = "Simplification in progress";
247
248 ImGui::OpenPopup(label);
249 if (ImGui::BeginPopupModal(label, NULL)) {
250 ImGui::ProgressBar(taskState.progress);
251
252 ImGui::Separator();
253 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
254 taskState.cancel = true;
255 }
256 ImGui::EndPopup();
257 }
258}
259
261{
262 modal = true;
263}
264
266{
267 if (taskState.running == false) {
268 showParameterGui();
269 }
270 else {
271 showProgressGui();
272 }
273}
274
276{
277 if (!taskState.running && task.isValid()) {
278 waitForTask();
279 modal = false;
280 }
281 return modal;
282}
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Definition: Entity.h:113
Contains data describing level of detail behavior for the entity the component belongs to.
Definition: LodComponent.h:43
LodPolicy policy
The policy used for determining the level of detail for this entity.
Definition: LodComponent.h:51
Renders the contents of a MeshComponent using the given materials.
Contains information on how the entity behaves in the scene.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
@ GeometricTolerance
Use a geometric error bound to determine how refined geometry needs to be at its current position.
Abstract base class for all editor commands showing GUI in interactive mode.
Definition: EditorCommand.h:40
bool continueModal() override
Shall return true while the GUI should still be shown. False when.
void showGui() override
Display custom ImGUI.
void beginModal() override
Called when the command is initiated.
void apply() override
Run the command.
@ TriangleList
List of triangles.
Definition: Common.h:116