Cogs.Core
RRTasks.cpp
1#include <rrapi/rrapi.h>
2
3#include "Platform/Instrumentation.h"
4
5#include "../Extensions/GeometryProcessing/GeometryProcessing.h"
6#include "RRTasks.h"
7
8#include "Foundation/Logging/Logger.h"
9
10#include <glm/gtc/type_ptr.hpp>
11
12namespace {
13 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RationalReducer");
14
15 struct TriangleData
16 {
17 size_t meshIndex;
19 };
20
21 rr_bool_t extractPrimitives(void * handle, void * userdata)
22 {
23 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRextractPrimitives");
24
25 auto * task = static_cast<Cogs::Core::RRTaskBase *>(userdata);
26 auto * state = task->state;
27 auto & inputSet = state->meshSets.front();
28
29 for (size_t m = 0; m < inputSet.meshes.size(); m++) {
30 auto & mesh = inputSet.meshes[m];
31
32 auto transverts = rr_add_vertices(handle, (int)mesh.positions.size(), reinterpret_cast<const float *>(mesh.positions.data()));
33
34 auto triangleData = static_cast<TriangleData*>(rr_malloc(handle, static_cast<unsigned int>(sizeof(TriangleData))));
35 triangleData->meshIndex = m;
36 triangleData->material = state->items.meshes[m].matlInst;
37
38 glm::vec2 * tex = nullptr;
39 if (task->textured) {
40 tex = static_cast<glm::vec2*>(rr_malloc(handle, static_cast<unsigned int>(sizeof(glm::vec2) * 3 * mesh.indices.size())));
41 if (mesh.texcoords.size() != 0) {
42 for (size_t i = 0; i < mesh.indices.size(); i++) {
43 tex[i] = mesh.texcoords[mesh.indices[i]];
44 }
45 }
46 else {
47 for (size_t i = 0; i < mesh.indices.size(); i++) {
48 tex[i] = glm::vec2(0.f);
49 }
50 }
51 }
52
53 for (size_t i = 0; i < mesh.indices.size(); i += 3) {
54 rr_add_indexed_polygon(handle, 3, reinterpret_cast<int*>(mesh.indices.data() + i), transverts, triangleData, reinterpret_cast<float*>(tex ? tex + i : nullptr), RR_REDUCABLE);
55 }
56 }
57 return TRUE;
58 }
59
60
61 rr_bool_t stuffPrimitives(void *handle, void *userdata)
62 {
63 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRstuffPrimitives");
64
65 auto * task = static_cast<Cogs::Core::RRTaskBase *>(userdata);
66 auto * state = task->state;
67
68 auto Nt = static_cast<unsigned>(rr_get_num_triangles(handle));
69
70 // If identical, just skip.
71 if (state->meshSets.back().triangles == Nt || Nt == 0) return TRUE;
72
73
74 // Group triangles by mesh
75 state->meshSets.emplace_back();
76 auto & currentSet = state->meshSets.back();
77
78 currentSet.triangles = Nt;
79 currentSet.error = rr_get_approximation_error(handle);
80 currentSet.bboxMin = state->meshSets.front().bboxMin;
81 currentSet.bboxMax = state->meshSets.front().bboxMax;
82 currentSet.meshes.resize(state->meshSets.front().meshes.size());
83
84 if (Nt == 0) {
85 currentSet.error = glm::distance(currentSet.bboxMin, currentSet.bboxMax);
86 }
87
88 LOG_TRACE(logger, "Stuff primitives invoked error=%f, triangles=%d", currentSet.error, currentSet.triangles);
89
90 std::vector<std::vector<rr_triangle_t*>> groupedTriangles(currentSet.meshes.size());
91 for (unsigned i = 0; i < currentSet.triangles; i++) {
92 auto rr_triangle = rr_get_triangle(handle, i);
93 auto * triangleData = static_cast<TriangleData*>(rr_triangle_get_data(rr_triangle));
94 groupedTriangles[triangleData->meshIndex].push_back(rr_triangle);
95 }
96
97 for (size_t l = 0; l < currentSet.meshes.size(); l++) {
98 auto & mesh = currentSet.meshes[l];
99 auto & triangles = groupedTriangles[l];
100
101 mesh.indices.clear();
102 if (triangles.empty()) {
103 mesh.positions.clear();
104 mesh.normals.clear();
105 mesh.texcoords.clear();
106 mesh.tangents.clear();
107 continue;
108 }
109
110 mesh.positions.resize(3 * triangles.size(), false);
111 mesh.normals.clear();
112
113 for (size_t j = 0; j < triangles.size(); j++) {
114 for (unsigned i = 0; i < 3; i++) {
115 auto * vertex = rr_triangle_get_vertex(triangles[j], i);
116 mesh.positions[3 * j + i] = glm::make_vec3(rr_vertex_get_coord(vertex));
117 }
118 }
119
120 if (mesh.texcoords.size() != 0 || task->textured) {
121 mesh.texcoords.resize(3 * triangles.size(), false);
122 for (size_t j = 0; j < triangles.size(); j++) {
123 auto * tex = rr_triangle_get_texcoords(triangles[j]);
124 for (size_t i = 0; i < 3; i++) {
125 mesh.texcoords[3 * j + i] = tex ? glm::make_vec2(tex + 2 * i) : glm::vec2(0.f);
126 }
127 }
128 }
129 }
130
131 // Mesh is non-indexed with crappy faceted normals. But we have other EditorCommands to fix that.
132
133 return TRUE;
134 }
135
136
137 void progressModal(float fraction, void *userdata)
138 {
139 auto * task = static_cast<Cogs::Core::RRTaskBase *>(userdata);
140 auto * state = task->state;
141
142 state->progress = fraction;
143 if (state->cancel) {
144 rr_abort(task->rrHandle);
145 }
146 }
147
148 void progressBatch(float fraction, void *userdata)
149 {
150 auto * task = static_cast<Cogs::Core::RRTaskBase *>(userdata);
151 auto * state = task->state;
152
153 if (floor(10.f*(state->progress)) != floor(10.f*fraction)) {
154 float appError = rr_get_approximation_error(task->rrHandle);
155 LOG_TRACE(logger, "Progress=%.1f%%, approximation error=%f", 100.f*fraction, appError);
156 }
157
158 state->progress = fraction;
159
160 if (state->cancel) {
161 rr_abort(task->rrHandle);
162 }
163 }
164
165 float sameMaterial(void * triangle1data, void * triangle2data, void * /*userdata*/)
166 {
167 auto tri1 = static_cast<TriangleData *>(triangle1data);
168 auto tri2 = static_cast<TriangleData *>(triangle2data);
169 return tri1->material == tri2->material ? 0.0f : 1.0f;
170 }
171}
172
173void Cogs::Core::RRTaskBase::initRR()
174{
175 assert(state->meshSets.size() == 1);
176
177 auto & inputSet = state->meshSets.front();
178 inputSet.triangles = 0;
179 inputSet.error = 0.f;
180 textured = false;
181
182 inputSet.bboxMin = glm::vec3(std::numeric_limits<float>::max());
183 inputSet.bboxMax = glm::vec3(-std::numeric_limits<float>::max());
184
185 for (auto & mesh : inputSet.meshes) {
186 for (size_t i = 0; i < mesh.positions.size(); i++) {
187 inputSet.bboxMin = glm::min(inputSet.bboxMin, mesh.positions[i]);
188 inputSet.bboxMax = glm::max(inputSet.bboxMax, mesh.positions[i]);
189 }
190
191 textured = textured || mesh.texcoords.size() != 0;
192 inputSet.triangles += static_cast<int>(mesh.indices.size() / 3);
193 }
194
195 //LOG_DEBUG(logger, "bbox: [%f, %f, %f]x[%f, %f, %f]",
196 // inputSet.bboxMin.x, inputSet.bboxMin.y, inputSet.bboxMin.z,
197 // inputSet.bboxMax.x, inputSet.bboxMax.y, inputSet.bboxMax.z);
198
199 rrHandle = rr_init(textured);
200
201 state->progress = 0.f;
202 rr_set_extract_primitives(rrHandle, ::extractPrimitives, &state);
203 rr_set_stuff_primitives(rrHandle, ::stuffPrimitives, &state);
204 rr_set_simple_progress(rrHandle, modal ? ::progressModal : ::progressBatch, &state);
205 rr_set_same_texture(rrHandle, ::sameMaterial, &state);
206 rr_set_same_material(rrHandle, ::sameMaterial, &state);
207 rr_set_same_surfaceappearance(rrHandle, ::sameMaterial, &state);
208 rr_set_num_native_triangles(rrHandle, inputSet.triangles);
209 rr_set_epsilon(rrHandle, epsilon);
210
211 if (edgeLengthWeight != 1.0f) {
212 rr_set_edgelength_weight(rrHandle, edgeLengthWeight);
213 }
214}
215
216void Cogs::Core::RRTaskBase::runRR()
217{
218 auto & inputSet = state->meshSets.front();
219 if (inputSet.bboxMin.x == inputSet.bboxMax.x && inputSet.bboxMin.y == inputSet.bboxMax.y) return;
220
221 auto ret = rr_reduce(rrHandle);
222 rr_end(rrHandle);
223
224 switch (ret)
225 {
226 case RR_OK: {
227 std::vector<glm::vec3> N;
228 std::vector<uint32_t> map;
229 std::vector<uint32_t> unique;
230
231 std::vector<uint32_t> newIndices;
232
233
234 for (auto & meshSet : state->meshSets) {
235 for (auto & mesh : meshSet.meshes) {
236
237 // Create indexed mesh
238 if (!textured) mesh.texcoords.clear();
239 mesh.normals.clear();
240
241 map.clear();
242 unique.clear();
243 GeometryProcessing::uniqueVertexSubset(state->context, unique, map,
244 (float*)mesh.positions.data(),
245 3 * sizeof(float),
246 mesh.normals.size() ? (float*)mesh.normals.data() : nullptr,
247 3 * sizeof(float),
248 mesh.texcoords.size() ? (float*)mesh.texcoords.data() : nullptr,
249 2 * sizeof(float),
250 static_cast<uint32_t>(mesh.positions.size()), std::numeric_limits<float>::epsilon());
251
252 if (mesh.indices.size() == 0) {
253 mesh.indices.resize(mesh.positions.size(), false);
254 for (size_t i = 0; i < mesh.positions.size(); i++) {
255 mesh.indices[i] = uint32_t(i);
256 }
257 }
258
259 remapVertices(state->context, mesh, unique);
260 remapIndices(state->context, mesh, map);
261
262 // Add normals
263 N.clear();
264 unique.clear();
265 newIndices.clear();
266 GeometryProcessing::normalsFromIndexedTriangles(state->context,
267 N, unique, newIndices,
268 reinterpret_cast<const float*>(mesh.positions.data()), 3 * sizeof(float), static_cast<uint32_t>(mesh.positions.size()),
269 mesh.indices.data(), static_cast<uint32_t>(mesh.indices.size()),
270 normalGenArgs.featureAngle,
271 normalGenArgs.protrusionAngle,
272 normalGenArgs.flip);
273 assert(N.size() == unique.size());
274
275 remapVertices(state->context, mesh, unique);
276
277 mesh.indices.resize(newIndices.size());
278 std::memcpy(mesh.indices.data(), newIndices.data(), sizeof(uint32_t)*newIndices.size());
279
280 mesh.normals.resize(unique.size(), false);
281 std::memcpy(mesh.normals.data(), N.data(), mesh.normals.byteSize());
282 }
283 }
284
285 state->running = false;
286 return;
287 break;
288 }
289 case RR_ABORT: LOG_WARNING(logger, "rr_reduce returned RR_ABORT"); break;
290 case RR_CALLBACK_ERROR: LOG_ERROR(logger, "rr_reduce returned RR_CALLBACK_ERROR"); break;
291 case RR_ERROR:
292 // RR Fails. Will have invalid data. No normals. Remove failing meshes.
293 for (auto& meshSet : state->meshSets) {
294 meshSet.triangles = 0;
295 meshSet.meshes.clear();
296 }
297 LOG_ERROR(logger, "rr_reduce returned RR_ERROR. Meshes deleted");
298 break;
299 case RR_NODATA: LOG_ERROR(logger, "rr_reduce returned RR_NODATA"); break;
300 case RR_NONUMTRIS: LOG_ERROR(logger, "rr_reduce returned RR_NONUMTRIS"); break;
301 default:
302 assert(0);
303 break;
304 }
305 state->cancel = true;
306 state->running = false;
307}
308
309
310void Cogs::Core::RRTriangleGuidedTask::operator()()
311{
312 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRtask");
313 initRR();
314 rr_set_percentage(rrHandle, percentage);
315 runRR();
316}
317
318void Cogs::Core::RRMinMaxErrorGuidedTask::operator()()
319{
320 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRtask");
321 initRR();
322 rr_set_minmax_error(rrHandle, error_threshold);
323 runRR();
324}
325
326void Cogs::Core::RRMultiTriangleGuidedTask::operator()()
327{
328 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRtask");
329 initRR();
330
331 assert(!thresholds.empty());
332 auto steps = thresholds.size();
333
334 auto Nt = state->meshSets.front().triangles;
335 std::vector<unsigned> lodLevels(steps);
336 for (size_t i = 0; i < steps; i++) {
337 lodLevels[i] = unsigned(std::max(0.f, 0.01f*Nt*thresholds[i]));
338 }
339
340 if (1 < steps) {
341 rr_set_lod_levels(rrHandle, unsigned(steps - 1), lodLevels.data());
342 }
343 rr_set_target_tris(rrHandle, lodLevels.back());
344 runRR();
345}
346
347void Cogs::Core::RRMultiMinMaxErrorGuidedTask::operator()()
348{
349 CpuInstrumentationScope(SCOPE_RATIONALREDUCER, "RRtask");
350 initRR();
351
352 assert(!thresholds.empty());
353 auto steps = thresholds.size();
354
355 if (1 < steps) {
356 rr_set_error_lod_levels(rrHandle, unsigned(steps - 1), thresholds.data());
357 }
358 rr_set_minmax_error(rrHandle, thresholds.back());
359 runRR();
360}
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Material instances represent a specialized Material combined with state for all its buffers and prope...