1.3.64
 
Loading...
Searching...
No Matches
Context_primitive.cpp
Go to the documentation of this file.
1
16#include "Context.h"
17
18using namespace helios;
19
21 return addPatch(make_vec3(0, 0, 0), make_vec2(1, 1), make_SphericalCoord(0, 0), make_RGBAcolor(0, 0, 0, 1));
22}
23
24uint Context::addPatch(const vec3 &center, const vec2 &size) {
25 return addPatch(center, size, make_SphericalCoord(0, 0), make_RGBAcolor(0, 0, 0, 1));
26}
27
28uint Context::addPatch(const vec3 &center, const vec2 &size, const SphericalCoord &rotation) {
29 return addPatch(center, size, rotation, make_RGBAcolor(0, 0, 0, 1));
30}
31
32uint Context::addPatch(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color) {
33 return addPatch(center, size, rotation, make_RGBAcolor(color, 1));
34}
35
36uint Context::addPatch(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBAcolor &color) {
37 if (size.x < 1e-6f || size.y < 1e-6f) {
38 helios_runtime_error("ERROR (Context::addPatch): Size of patch must be greater than 1e-6 to avoid numerical precision issues.");
39 }
40
41 auto *patch_new = (new Patch(color, 0, currentUUID));
42
43 // if( patch_new->getArea()==0 ){
44 // helios_runtime_error("ERROR (Context::addPatch): Patch has area of zero.");
45 // }
46
47 patch_new->scale(make_vec3(size.x, size.y, 1));
48
49 if (rotation.elevation != 0) {
50 patch_new->rotate(-rotation.elevation, "x");
51 }
52 if (rotation.azimuth != 0) {
53 patch_new->rotate(-rotation.azimuth, "z");
54 }
55
56 patch_new->translate(center);
57
58 primitives[currentUUID] = patch_new;
59
60 // Set context pointer
61 patch_new->context_ptr = this;
62
63 // Create or reuse material with de-duplication
64 std::string mat_label = generateMaterialLabel(color, "", false);
65 if (!doesMaterialExist(mat_label)) {
66 patch_new->materialID = addMaterial_internal(mat_label, color, "");
67 } else {
68 patch_new->materialID = getMaterialIDFromLabel(mat_label);
69 }
70 // Increment material reference count
71 materials[patch_new->materialID].reference_count++;
72
73 currentUUID++;
74 invalidateAllUUIDsCache();
75 return currentUUID - 1;
76}
77
78uint Context::addPatch(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const char *texture_file) {
79 addTexture(texture_file);
80
81 // Default (u, v) mapping
82 const std::vector<helios::vec2> uv = {{0.f, 0.f}, {1.f, 0.f}, {1.f, 1.f}, {0.f, 1.f}};
83
84 auto *patch_new = (new Patch(texture_file, uv, textures, 0, currentUUID));
85
86 // if( patch_new->getArea()==0 ){
87 // helios_runtime_error("ERROR (Context::addPatch): Patch has area of zero.");
88 // }
89
90 assert(size.x > 0.f && size.y > 0.f);
91 patch_new->scale(make_vec3(size.x, size.y, 1));
92
93 if (rotation.elevation != 0) {
94 patch_new->rotate(-rotation.elevation, "x");
95 }
96 if (rotation.azimuth != 0) {
97 patch_new->rotate(-rotation.azimuth, "z");
98 }
99
100 patch_new->translate(center);
101
102 primitives[currentUUID] = patch_new;
103
104 // Set context pointer
105 patch_new->context_ptr = this;
106
107 // Create or reuse material with de-duplication
108 std::string mat_label = generateMaterialLabel(make_RGBAcolor(0, 0, 0, 1), texture_file, false);
109 if (!doesMaterialExist(mat_label)) {
110 patch_new->materialID = addMaterial_internal(mat_label, make_RGBAcolor(0, 0, 0, 1), texture_file);
111 } else {
112 patch_new->materialID = getMaterialIDFromLabel(mat_label);
113 }
114 // Increment material reference count
115 materials[patch_new->materialID].reference_count++;
116
117 currentUUID++;
118 invalidateAllUUIDsCache();
119 return currentUUID - 1;
120}
121
122uint Context::addPatch(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const char *texture_file, const helios::vec2 &uv_center, const helios::vec2 &uv_size) {
123 if (size.x < 1e-6f || size.y < 1e-6f) {
124 helios_runtime_error("ERROR (Context::addPatch): Size of patch must be greater than 1e-6 to avoid numerical precision issues.");
125 }
126
127 if (uv_center.x - 0.5 * uv_size.x < -1e-3 || uv_center.y - 0.5 * uv_size.y < -1e-3 || uv_center.x + 0.5 * uv_size.x - 1.f > 1e-3 || uv_center.y + 0.5 * uv_size.y - 1.f > 1e-3) {
128 helios_runtime_error("ERROR (Context::addPatch): Invalid texture coordinates. uv_center-0.5*uv_size should be >=0 and uv_center+0.5*uv_size should be <=1.");
129 }
130
131 addTexture(texture_file);
132
133 const std::vector<helios::vec2> uv = {uv_center + make_vec2(-0.5f * uv_size.x, -0.5f * uv_size.y), uv_center + make_vec2(+0.5f * uv_size.x, -0.5f * uv_size.y), uv_center + make_vec2(+0.5f * uv_size.x, +0.5f * uv_size.y),
134 uv_center + make_vec2(-0.5f * uv_size.x, +0.5f * uv_size.y)};
135
136 auto *patch_new = (new Patch(texture_file, uv, textures, 0, currentUUID));
137
138 // if( patch_new->getArea()==0 ){
139 // helios_runtime_error("ERROR (Context::addPatch): Patch has area of zero.");
140 // }
141
142 assert(size.x > 0.f && size.y > 0.f);
143 patch_new->scale(make_vec3(size.x, size.y, 1));
144
145 if (rotation.elevation != 0) {
146 patch_new->rotate(-rotation.elevation, "x");
147 }
148 if (rotation.azimuth != 0) {
149 patch_new->rotate(-rotation.azimuth, "z");
150 }
151
152 patch_new->translate(center);
153
154 primitives[currentUUID] = patch_new;
155
156 // Set context pointer
157 patch_new->context_ptr = this;
158
159 // Create or reuse material with de-duplication (texture-based)
160 std::string mat_label = generateMaterialLabel(make_RGBAcolor(0, 0, 0, 1), texture_file, false);
161 if (!doesMaterialExist(mat_label)) {
162 patch_new->materialID = addMaterial_internal(mat_label, make_RGBAcolor(0, 0, 0, 1), texture_file);
163 } else {
164 patch_new->materialID = getMaterialIDFromLabel(mat_label);
165 }
166 // Increment material reference count
167 materials[patch_new->materialID].reference_count++;
168
169 currentUUID++;
170 invalidateAllUUIDsCache();
171 return currentUUID - 1;
172}
173
174uint Context::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2) {
175 return addTriangle(vertex0, vertex1, vertex2, make_RGBAcolor(0, 0, 0, 1));
176}
177
178uint Context::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const RGBcolor &color) {
179 return addTriangle(vertex0, vertex1, vertex2, make_RGBAcolor(color, 1));
180}
181
182uint Context::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const RGBAcolor &color) {
183 auto *tri_new = (new Triangle(vertex0, vertex1, vertex2, color, 0, currentUUID));
184
185#ifdef HELIOS_DEBUG
186 if (calculateTriangleArea(vertex0, vertex1, vertex2) < 1e-10) {
187 api_warnings.addWarning("addTriangle_malformed_triangle", "Triangle has near-zero surface area (< 1e-10).");
188 }
189#endif
190
191 primitives[currentUUID] = tri_new;
192
193 // Set context pointer
194 tri_new->context_ptr = this;
195
196 // Create or reuse material with de-duplication
197 std::string mat_label = generateMaterialLabel(color, "", false);
198 if (!doesMaterialExist(mat_label)) {
199 tri_new->materialID = addMaterial_internal(mat_label, color, "");
200 } else {
201 tri_new->materialID = getMaterialIDFromLabel(mat_label);
202 }
203 // Increment material reference count
204 materials[tri_new->materialID].reference_count++;
205
206 currentUUID++;
207 invalidateAllUUIDsCache();
208 return currentUUID - 1;
209}
210
211uint Context::addTriangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const char *texture_file, const helios::vec2 &uv0, const helios::vec2 &uv1, const helios::vec2 &uv2) {
212 addTexture(texture_file);
213
214 const std::vector<helios::vec2> uv{uv0, uv1, uv2};
215
216 auto *tri_new = (new Triangle(vertex0, vertex1, vertex2, texture_file, uv, textures, 0, currentUUID));
217
218#ifdef HELIOS_DEBUG
219 if (calculateTriangleArea(vertex0, vertex1, vertex2) < 1e-10) {
220 api_warnings.addWarning("addTriangle_malformed_triangle", "Triangle has near-zero surface area (< 1e-10).");
221 }
222#endif
223
224 primitives[currentUUID] = tri_new;
225
226 // Set context pointer
227 tri_new->context_ptr = this;
228
229 // Create or reuse material with de-duplication (texture-based)
230 std::string mat_label = generateMaterialLabel(make_RGBAcolor(0, 0, 0, 1), texture_file, false);
231 if (!doesMaterialExist(mat_label)) {
232 tri_new->materialID = addMaterial_internal(mat_label, make_RGBAcolor(0, 0, 0, 1), texture_file);
233 } else {
234 tri_new->materialID = getMaterialIDFromLabel(mat_label);
235 }
236 // Increment material reference count
237 materials[tri_new->materialID].reference_count++;
238
239 currentUUID++;
240 invalidateAllUUIDsCache();
241 return currentUUID - 1;
242}
243
244uint Context::addVoxel(const vec3 &center, const vec3 &size) {
245 return addVoxel(center, size, 0, make_RGBAcolor(0, 0, 0, 1));
246}
247
248uint Context::addVoxel(const vec3 &center, const vec3 &size, const float &rotation) {
249 return addVoxel(center, size, rotation, make_RGBAcolor(0, 0, 0, 1));
250}
251
252uint Context::addVoxel(const vec3 &center, const vec3 &size, const float &rotation, const RGBcolor &color) {
253 return addVoxel(center, size, rotation, make_RGBAcolor(color, 1));
254}
255
256uint Context::addVoxel(const vec3 &center, const vec3 &size, const float &rotation, const RGBAcolor &color) {
257 auto *voxel_new = (new Voxel(color, 0, currentUUID));
258
259 if (size.x * size.y * size.z == 0) {
260 helios_runtime_error("ERROR (Context::addVoxel): Voxel has size of zero.");
261 }
262
263 voxel_new->scale(size);
264
265 if (rotation != 0) {
266 voxel_new->rotate(rotation, "z");
267 }
268
269 voxel_new->translate(center);
270
271 primitives[currentUUID] = voxel_new;
272
273 // Set context pointer
274 voxel_new->context_ptr = this;
275
276 // Create or reuse material with de-duplication
277 std::string mat_label = generateMaterialLabel(color, "", false);
278 if (!doesMaterialExist(mat_label)) {
279 voxel_new->materialID = addMaterial_internal(mat_label, color, "");
280 } else {
281 voxel_new->materialID = getMaterialIDFromLabel(mat_label);
282 }
283 // Increment material reference count
284 materials[voxel_new->materialID].reference_count++;
285
286 currentUUID++;
287 invalidateAllUUIDsCache();
288 return currentUUID - 1;
289}
290
291void Context::translatePrimitive(uint UUID, const vec3 &shift) {
292 getPrimitivePointer_private(UUID)->translate(shift);
293}
294
295void Context::translatePrimitive(const std::vector<uint> &UUIDs, const vec3 &shift) {
296 float T[16];
297 makeTranslationMatrix(shift, T);
298
299 for (uint UUID: UUIDs) {
300 getPrimitivePointer_private(UUID)->applyTransform(T);
301 }
302}
303
304void Context::rotatePrimitive(uint UUID, float rotation_rad, const char *axis) {
305 getPrimitivePointer_private(UUID)->rotate(rotation_rad, axis);
306}
307
308void Context::rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const char *axis) {
309 if (rotation_rad == 0) {
310 return;
311 }
312
313 float T[16];
314 if (strcmp(axis, "z") == 0) {
315 makeRotationMatrix(rotation_rad, "z", T);
316 } else if (strcmp(axis, "y") == 0) {
317 makeRotationMatrix(rotation_rad, "y", T);
318 } else if (strcmp(axis, "x") == 0) {
319 makeRotationMatrix(rotation_rad, "x", T);
320 } else {
321 helios_runtime_error("ERROR (Context::rotatePrimitive): Rotation axis should be one of x, y, or z.");
322 }
323
324 WarningAggregator warnings;
325 for (uint UUID: UUIDs) {
326 if (strcmp(axis, "z") != 0 && getPrimitivePointer_private(UUID)->getType() == PRIMITIVE_TYPE_VOXEL) {
327 warnings.addWarning("voxel_rotation_z_only", "Voxels can only be rotated about the z-axis. Ignoring this rotation.");
328 }
329 getPrimitivePointer_private(UUID)->applyTransform(T);
330 }
331 warnings.report(std::cerr);
332}
333
334void Context::rotatePrimitive(uint UUID, float rotation_rad, const helios::vec3 &axis) {
335 getPrimitivePointer_private(UUID)->rotate(rotation_rad, axis);
336}
337
338void Context::rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const vec3 &axis) {
339 if (rotation_rad == 0) {
340 return;
341 }
342
343 float T[16];
344 makeRotationMatrix(rotation_rad, axis, T);
345
346 WarningAggregator warnings;
347 for (uint UUID: UUIDs) {
348 if (getPrimitivePointer_private(UUID)->getType() == PRIMITIVE_TYPE_VOXEL) {
349 warnings.addWarning("voxel_rotation_z_only", "Voxels can only be rotated about the z-axis. Ignoring this rotation.");
350 }
351 getPrimitivePointer_private(UUID)->applyTransform(T);
352 }
353 warnings.report(std::cerr);
354}
355
356void Context::rotatePrimitive(uint UUID, float rotation_rad, const helios::vec3 &origin, const helios::vec3 &axis) {
357 getPrimitivePointer_private(UUID)->rotate(rotation_rad, origin, axis);
358}
359
360void Context::rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const helios::vec3 &origin, const vec3 &axis) {
361 if (rotation_rad == 0) {
362 return;
363 }
364
365 float T[16];
366 makeRotationMatrix(rotation_rad, origin, axis, T);
367
368 WarningAggregator warnings;
369 for (uint UUID: UUIDs) {
370 if (getPrimitivePointer_private(UUID)->getType() == PRIMITIVE_TYPE_VOXEL) {
371 warnings.addWarning("voxel_rotation_z_only", "Voxels can only be rotated about the z-axis. Ignoring this rotation.");
372 }
373 getPrimitivePointer_private(UUID)->applyTransform(T);
374 }
375 warnings.report(std::cerr);
376}
377
378void Context::setPrimitiveNormal(uint UUID, const helios::vec3 &origin, const helios::vec3 &new_normal) {
379#ifdef HELIOS_DEBUG
380 if (!doesPrimitiveExist(UUID)) {
381 helios_runtime_error("ERROR (Context::setPrimitiveNormal): UUID of " + std::to_string(UUID) + " not found in the context.");
382 }
383#endif
384
385 auto *prim = getPrimitivePointer_private(UUID);
386
387 // old and new normals, unitized
388 helios::vec3 oldN = normalize(prim->getNormal());
389 helios::vec3 newN = normalize(new_normal);
390
391 // minimal rotation axis/angle
392 float d = std::clamp(oldN * newN, -1.f, 1.f);
393 float angle = acosf(d);
394 helios::vec3 axis = cross(oldN, newN);
395 if (axis.magnitude() < 1e-6f) {
396 axis = (std::fabs(oldN.x) < std::fabs(oldN.z)) ? cross(oldN, {1, 0, 0}) : cross(oldN, {0, 0, 1});
397 }
398 axis = axis.normalize();
399
400 // build M_delta about 'origin'
401 float M_delta[16];
402 makeRotationMatrix(angle, origin, axis, M_delta);
403
404 // grab existing world‐space model matrix
405 float M_old[16];
406 prim->getTransformationMatrix(M_old);
407
408 // preserve the rectangle's forward (local X) direction:
409 // - t0 is the world‐space image of (1,0,0) under M_old
410 helios::vec3 t0{
411 M_old[0], // row0·[1,0,0,0]
412 M_old[4], // row1·[1,0,0,0]
413 M_old[8] // row2·[1,0,0,0]
414 };
415 t0 = normalize(t0);
416
417 // apply M_delta to that direction (w=0)
418 helios::vec3 t1{M_delta[0] * t0.x + M_delta[1] * t0.y + M_delta[2] * t0.z, M_delta[4] * t0.x + M_delta[5] * t0.y + M_delta[6] * t0.z, M_delta[8] * t0.x + M_delta[9] * t0.y + M_delta[10] * t0.z};
419 t1 = normalize(t1);
420
421 // desired forward is world‐X projected onto the new plane
422 helios::vec3 worldX{1.f, 0.f, 0.f};
423 helios::vec3 targ = worldX - newN * (newN * worldX);
424 targ = normalize(targ);
425
426 // compute the twist about newN that carries t1 → targ
427 // using signed angle in that plane
428 float twist = std::atan2(newN * cross(t1, targ), // dot(newN, t1×targ)
429 t1 * targ // dot(t1, targ)
430 );
431
432 // build that correction rotation
433 float M_twist[16];
434 makeRotationMatrix(twist, origin, newN, M_twist);
435
436 // now combine: M_new = M_twist * (M_delta * M_old)
437 float temp[16], M_new[16];
438 matmult(M_delta, M_old, temp);
439 matmult(M_twist, temp, M_new);
440
441 // write it back
442 prim->setTransformationMatrix(M_new);
443}
444
445void Context::setPrimitiveNormal(const std::vector<uint> &UUIDs, const helios::vec3 &origin, const vec3 &new_normal) {
446 for (uint UUID: UUIDs) {
447 setPrimitiveNormal(UUID, origin, new_normal);
448 }
449}
450
451void Context::setPrimitiveElevation(uint UUID, const vec3 &origin, float elevation_rad) {
452#ifdef HELIOS_DEBUG
453 if (!doesPrimitiveExist(UUID))
454 helios_runtime_error("setPrimitiveElevation: invalid UUID");
455#endif
456
457 // pull the existing normal
458 auto *prim = getPrimitivePointer_private(UUID);
459 vec3 oldN = prim->getNormal();
460
461 // convert to spherical coords, extract azimuth
462 SphericalCoord sc = cart2sphere(oldN);
463 float az = sc.azimuth;
464
465 // build the new unit‐normal with desired elevation, same azimuth
466 SphericalCoord targetSC(1.0f, elevation_rad, az);
467 vec3 targetN = sphere2cart(targetSC);
468
469 // delegate to your normal‐setting routine
470 setPrimitiveNormal(UUID, origin, targetN);
471}
472
473void Context::setPrimitiveAzimuth(uint UUID, const vec3 &origin, float azimuth_rad) {
474#ifdef HELIOS_DEBUG
475 if (!doesPrimitiveExist(UUID))
476 helios_runtime_error("setPrimitiveAzimuth: invalid UUID");
477#endif
478
479 // pull the existing normal
480 auto *prim = getPrimitivePointer_private(UUID);
481 vec3 oldN = prim->getNormal();
482
483 // convert to spherical coords, extract elevation
484 SphericalCoord sc = cart2sphere(oldN);
485 float elev = sc.elevation;
486
487 // build the new unit‐normal with same elevation, desired azimuth
488 SphericalCoord targetSC(1.0f, elev, azimuth_rad);
489 vec3 targetN = sphere2cart(targetSC);
490
491 // delegate to your normal‐setting routine
492 setPrimitiveNormal(UUID, origin, targetN);
493}
494
496#ifdef HELIOS_DEBUG
497 if (!doesPrimitiveExist(UUID)) {
498 helios_runtime_error("ERROR (Context::scalePrimitive): UUID of " + std::to_string(UUID) + " not found in the context.");
499 }
500#endif
501 if (S.x == 1 && S.y == 1 && S.z == 1) {
502 return;
503 }
504
505 float T[16];
506 makeScaleMatrix(S, T);
507
508 getPrimitivePointer_private(UUID)->applyTransform(T);
509}
510
511void Context::scalePrimitive(const std::vector<uint> &UUIDs, const helios::vec3 &S) {
512 for (uint UUID: UUIDs) {
513 scalePrimitive(UUID, S);
514 }
515}
516
518#ifdef HELIOS_DEBUG
519 if (!doesPrimitiveExist(UUID)) {
520 helios_runtime_error("ERROR (Context::scalePrimitiveAboutPoint): UUID of " + std::to_string(UUID) + " not found in the context.");
521 }
522#endif
523 if (S.x == 1 && S.y == 1 && S.z == 1) {
524 return;
525 }
526
527 getPrimitivePointer_private(UUID)->scale(S, point);
528}
529
530void Context::scalePrimitiveAboutPoint(const std::vector<uint> &UUIDs, const helios::vec3 &S, const helios::vec3 &point) {
531 for (uint UUID: UUIDs) {
532 scalePrimitiveAboutPoint(UUID, S, point);
533 }
534}
535
536void Context::deletePrimitive(const std::vector<uint> &UUIDs) {
537 for (uint UUID: UUIDs) {
538 deletePrimitive(UUID);
539 }
540}
541
543 if (primitives.find(UUID) == primitives.end()) {
544 helios_runtime_error("ERROR (Context::deletePrimitive): UUID of " + std::to_string(UUID) + " not found in the context.");
545 }
546
547 Primitive *prim = primitives.at(UUID);
548
549 for (const auto &[label, type]: prim->primitive_data_types) {
550 decrementPrimitiveDataLabelCounter(label);
551 }
552
553 if (prim->getParentObjectID() != 0) { // primitive belongs to an object
554
555 uint ObjID = prim->getParentObjectID();
556 if (doesObjectExist(ObjID)) {
557 objects.at(ObjID)->deleteChildPrimitive(UUID);
558 if (getObjectPointer_private(ObjID)->getPrimitiveUUIDs().empty()) {
559 CompoundObject *obj = objects.at(ObjID);
560 delete obj;
561 objects.erase(ObjID);
562 }
563 }
564 }
565
566 // Decrement material reference count before deleting primitive
567 materials[prim->materialID].reference_count--;
568
569 delete prim;
570 primitives.erase(UUID);
571 dirty_deleted_primitives.push_back(UUID);
572 invalidateAllUUIDsCache();
573}
574
575std::vector<uint> Context::copyPrimitive(const std::vector<uint> &UUIDs) {
576 std::vector<uint> UUIDs_copy(UUIDs.size());
577 size_t i = 0;
578 for (uint UUID: UUIDs) {
579 UUIDs_copy.at(i) = copyPrimitive(UUID);
580 i++;
581 }
582
583 return UUIDs_copy;
584}
585
587 if (primitives.find(UUID) == primitives.end()) {
588 helios_runtime_error("ERROR (Context::copyPrimitive): UUID of " + std::to_string(UUID) + " not found in the context.");
589 }
590
591 PrimitiveType type = primitives.at(UUID)->getType();
592 uint parentID = primitives.at(UUID)->getParentObjectID();
593 bool textureoverride = primitives.at(UUID)->isTextureColorOverridden();
594
595 if (type == PRIMITIVE_TYPE_PATCH) {
596 Patch *p = getPatchPointer_private(UUID);
597 const std::vector<vec2> &uv = p->getTextureUV();
598 const vec2 &size = p->getSize();
599 float solid_fraction = p->getArea() / (size.x * size.y);
600 Patch *patch_new;
601 if (!p->hasTexture()) {
602 patch_new = (new Patch(p->getColorRGBA(), parentID, currentUUID));
603 } else {
604 const std::string &texture_file = p->getTextureFile();
605 if (uv.size() == 4) {
606 patch_new = (new Patch(texture_file.c_str(), solid_fraction, parentID, currentUUID));
607 patch_new->setTextureUV(uv);
608 } else {
609 patch_new = (new Patch(texture_file.c_str(), solid_fraction, parentID, currentUUID));
610 }
611 // Color will be preserved by copying the material below
612 }
613 float transform[16];
614 p->getTransformationMatrix(transform);
615 patch_new->setTransformationMatrix(transform);
616 primitives[currentUUID] = patch_new;
617 } else if (type == PRIMITIVE_TYPE_TRIANGLE) {
618 Triangle *p = getTrianglePointer_private(UUID);
619 const std::vector<vec3> &vertices = p->getVertices();
620 const std::vector<vec2> &uv = p->getTextureUV();
621 Triangle *tri_new;
622 if (!p->hasTexture()) {
623 tri_new = (new Triangle(vertices.at(0), vertices.at(1), vertices.at(2), p->getColorRGBA(), parentID, currentUUID));
624 } else {
625 const std::string &texture_file = p->getTextureFile();
626 float solid_fraction = p->getSolidFraction();
627 tri_new = (new Triangle(vertices.at(0), vertices.at(1), vertices.at(2), texture_file.c_str(), uv, solid_fraction, parentID, currentUUID));
628 // Color will be preserved by copying the material below
629 }
630 float transform[16];
631 p->getTransformationMatrix(transform);
632 tri_new->setTransformationMatrix(transform);
633 primitives[currentUUID] = tri_new;
634 } else if (type == PRIMITIVE_TYPE_VOXEL) {
635 Voxel *p = getVoxelPointer_private(UUID);
636 Voxel *voxel_new;
637 // if( !p->hasTexture() ){
638 voxel_new = (new Voxel(p->getColorRGBA(), parentID, currentUUID));
639 //}else{
640 // voxel_new = (new Voxel( p->getColorRGBA(), currentUUID ));
641 /* \todo Texture-mapped voxels constructor here */
642 //}
643 float transform[16];
644 p->getTransformationMatrix(transform);
645 voxel_new->setTransformationMatrix(transform);
646 primitives[currentUUID] = voxel_new;
647 }
648
649 // Set context pointer and copy material from source primitive
650 Primitive *new_prim = getPrimitivePointer_private(currentUUID);
651 new_prim->context_ptr = this;
652 new_prim->materialID = primitives.at(UUID)->materialID;
653 // Increment material reference count (another primitive now uses this material)
654 materials[new_prim->materialID].reference_count++;
655
656 copyPrimitiveData(UUID, currentUUID);
657
658 if (textureoverride) {
659 getPrimitivePointer_private(currentUUID)->overrideTextureColor();
660 }
661
662 currentUUID++;
663 invalidateAllUUIDsCache();
664 return currentUUID - 1;
665}
666
667Primitive *Context::getPrimitivePointer_private(uint UUID) const {
668#ifdef HELIOS_DEBUG
669 if (primitives.find(UUID) == primitives.end()) {
670 helios_runtime_error("ERROR (Context::getPrimitivePointer_private): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
671 }
672#endif
673 return primitives.at(UUID);
674}
675
677 return primitives.find(UUID) != primitives.end();
678}
679
680bool Context::doesPrimitiveExist(const std::vector<uint> &UUIDs) const {
681 if (UUIDs.empty()) {
682 return false;
683 }
684 for (uint UUID: UUIDs) {
685 if (!doesPrimitiveExist(UUID)) {
686 return false;
687 }
688 }
689 return true;
690}
691
692Patch *Context::getPatchPointer_private(uint UUID) const {
693#ifdef HELIOS_DEBUG
694 if (primitives.find(UUID) == primitives.end()) {
695 helios_runtime_error("ERROR (Context::getPatchPointer_private): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
696 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_PATCH) {
697 helios_runtime_error("ERROR (Context::getPatchPointer_private): UUID of " + std::to_string(UUID) + " is not a patch.");
698 }
699#endif
700 return dynamic_cast<Patch *>(primitives.at(UUID));
701}
702
704#ifdef HELIOS_DEBUG
705 if (primitives.find(UUID) == primitives.end()) {
706 helios_runtime_error("ERROR (Context::getPatchSize): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
707 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_PATCH) {
708 helios_runtime_error("ERROR (Context::getPatchSize): UUID of " + std::to_string(UUID) + " is not a patch.");
709 }
710#endif
711 return dynamic_cast<Patch *>(primitives.at(UUID))->getSize();
712}
713
715#ifdef HELIOS_DEBUG
716 if (primitives.find(UUID) == primitives.end()) {
717 helios_runtime_error("ERROR (Context::getPatchCenter): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
718 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_PATCH) {
719 helios_runtime_error("ERROR (Context::getPatchCenter): UUID of " + std::to_string(UUID) + " is not a patch.");
720 }
721#endif
722 return dynamic_cast<Patch *>(primitives.at(UUID))->getCenter();
723}
724
725Triangle *Context::getTrianglePointer_private(uint UUID) const {
726#ifdef HELIOS_DEBUG
727 if (primitives.find(UUID) == primitives.end()) {
728 helios_runtime_error("ERROR (Context::getTrianglePointer_private): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
729 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_TRIANGLE) {
730 helios_runtime_error("ERROR (Context::getTrianglePointer_private): UUID of " + std::to_string(UUID) + " is not a triangle.");
731 }
732#endif
733 return dynamic_cast<Triangle *>(primitives.at(UUID));
734}
735
737#ifdef HELIOS_DEBUG
738 if (primitives.find(UUID) == primitives.end()) {
739 helios_runtime_error("ERROR (Context::getTriangleVertex): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
740 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_TRIANGLE) {
741 helios_runtime_error("ERROR (Context::getTriangleVertex): UUID of " + std::to_string(UUID) + " is not a triangle.");
742 } else if (number > 2) {
743 helios_runtime_error("ERROR (Context::getTriangleVertex): Vertex index must be one of 0, 1, or 2.");
744 }
745#endif
746 return dynamic_cast<Triangle *>(primitives.at(UUID))->getVertex(number);
747}
748
749void Context::setTriangleVertices(uint UUID, const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2) {
750#ifdef HELIOS_DEBUG
751 if (primitives.find(UUID) == primitives.end()) {
752 helios_runtime_error("ERROR (Context::setTriangleVertices): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
753 }
754#endif
755 dynamic_cast<Triangle *>(primitives.at(UUID))->setVertices(vertex0, vertex1, vertex2);
756}
757
758Voxel *Context::getVoxelPointer_private(uint UUID) const {
759#ifdef HELIOS_DEBUG
760 if (primitives.find(UUID) == primitives.end()) {
761 helios_runtime_error("ERROR (Context::getVoxelPointer): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
762 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_VOXEL) {
763 helios_runtime_error("ERROR (Context::getVoxelPointer): UUID of " + std::to_string(UUID) + " is not a voxel.");
764 }
765#endif
766 return dynamic_cast<Voxel *>(primitives.at(UUID));
767}
768
770#ifdef HELIOS_DEBUG
771 if (primitives.find(UUID) == primitives.end()) {
772 helios_runtime_error("ERROR (Context::getVoxelSize): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
773 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_VOXEL) {
774 helios_runtime_error("ERROR (Context::getVoxelSize): UUID of " + std::to_string(UUID) + " is not a patch.");
775 }
776#endif
777 return dynamic_cast<Voxel *>(primitives.at(UUID))->getSize();
778}
779
781#ifdef HELIOS_DEBUG
782 if (primitives.find(UUID) == primitives.end()) {
783 helios_runtime_error("ERROR (Context::getVoxelCenter): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
784 } else if (primitives.at(UUID)->getType() != PRIMITIVE_TYPE_VOXEL) {
785 helios_runtime_error("ERROR (Context::getVoxelCenter): UUID of " + std::to_string(UUID) + " is not a patch.");
786 }
787#endif
788 return dynamic_cast<Voxel *>(primitives.at(UUID))->getCenter();
789}
790
791size_t Context::getPrimitiveCount(bool include_hidden_primitives) const {
792 if (include_hidden_primitives) {
793 return primitives.size();
794 } else {
795 size_t count = 0;
796 for (const auto &[UUID, primitive]: primitives) {
797 if (!primitive->ishidden) {
798 count++;
799 }
800 }
801 return count;
802 }
803}
804
805size_t Context::getTriangleCount(bool include_hidden_primitives) const {
806 size_t count = 0;
807 for (const auto &[UUID, primitive]: primitives) {
808 if (primitive->getType() == PRIMITIVE_TYPE_TRIANGLE && (include_hidden_primitives || !primitive->ishidden)) {
809 count++;
810 }
811 }
812 return count;
813}
814
815size_t Context::getPatchCount(bool include_hidden_primitives) const {
816 size_t count = 0;
817 for (const auto &[UUID, primitive]: primitives) {
818 if (primitive->getType() == PRIMITIVE_TYPE_PATCH && (include_hidden_primitives || !primitive->ishidden)) {
819 count++;
820 }
821 }
822 return count;
823}
824
825// ===== PRIMITIVE, PATCH, TRIANGLE, AND VOXEL CLASS METHODS =====
826// Moved from Context.cpp
827
828Primitive::~Primitive() = default;
829
830uint Primitive::getUUID() const {
831 return UUID;
832}
833
834PrimitiveType Primitive::getType() const {
835 return prim_type;
836}
837
838void Primitive::setParentObjectID(uint objID) {
839 parent_object_ID = objID;
840}
841
842uint Primitive::getParentObjectID() const {
843 return parent_object_ID;
844}
845
846void Primitive::getTransformationMatrix(float (&T)[16]) const {
847 std::memcpy(T, transform, 16 * sizeof(float));
848}
849
850void Primitive::setTransformationMatrix(float (&T)[16]) {
851 std::memcpy(transform, T, 16 * sizeof(float));
852 dirty_flag = true;
853}
854
855float Patch::getArea() const {
856 const vec2 &size = getSize();
857
858 return size.x * size.y * solid_fraction;
859}
860
861float Triangle::getArea() const {
862 const std::vector<vec3> &vertices = getVertices();
863
864 float area = calculateTriangleArea(vertices[0], vertices[1], vertices[2]);
865
866 return area * solid_fraction;
867}
868
869float Voxel::getArea() const {
870 const vec3 size(transform[0], transform[5], transform[10]);
871
872 return 2.f * size.x * size.y + 2.f * size.x * size.z + 2.f * size.y * size.z;
873}
874
875vec3 Patch::getNormal() const {
876 return normalize(make_vec3(transform[2], transform[6], transform[10]));
877}
878
879vec3 Triangle::getNormal() const {
880 const std::vector<vec3> &vertices = getVertices();
881 return normalize(cross(vertices[1] - vertices[0], vertices[2] - vertices[1]));
882}
883
884vec3 Voxel::getNormal() const {
885 return nullorigin;
886}
887
888std::vector<vec3> Patch::getVertices() const {
889 std::vector<vec3> vertices(4);
890
891 const std::vector<vec3> Y = {{-0.5f, -0.5f, 0.f}, {0.5f, -0.5f, 0.f}, {0.5f, 0.5f, 0.f}, {-0.5f, 0.5f, 0.f}};
892
893 for (int i = 0; i < 4; i++) {
894 vertices[i].x = transform[0] * Y[i].x + transform[1] * Y[i].y + transform[2] * Y[i].z + transform[3];
895 vertices[i].y = transform[4] * Y[i].x + transform[5] * Y[i].y + transform[6] * Y[i].z + transform[7];
896 vertices[i].z = transform[8] * Y[i].x + transform[9] * Y[i].y + transform[10] * Y[i].z + transform[11];
897 }
898 return vertices;
899}
900
901std::vector<vec3> Triangle::getVertices() const {
902 std::vector<vec3> vertices(3);
903
904 const std::vector<vec3> Y = {{0.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {1.f, 1.f, 0.f}};
905
906 for (int i = 0; i < 3; i++) {
907 vertices[i].x = transform[0] * Y[i].x + transform[1] * Y[i].y + transform[2] * Y[i].z + transform[3];
908 vertices[i].y = transform[4] * Y[i].x + transform[5] * Y[i].y + transform[6] * Y[i].z + transform[7];
909 vertices[i].z = transform[8] * Y[i].x + transform[9] * Y[i].y + transform[10] * Y[i].z + transform[11];
910 }
911 return vertices;
912}
913
914std::vector<vec3> Voxel::getVertices() const {
915 std::vector<vec3> vertices(8);
916
917 const std::vector<vec3> Y = {{-0.5f, -0.5f, -0.5f}, {0.5f, -0.5f, -0.5f}, {0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}};
918
919
920 for (int i = 0; i < 8; i++) {
921 vertices[i].x = transform[0] * Y[i].x + transform[1] * Y[i].y + transform[2] * Y[i].z + transform[3];
922 vertices[i].y = transform[4] * Y[i].x + transform[5] * Y[i].y + transform[6] * Y[i].z + transform[7];
923 vertices[i].z = transform[8] * Y[i].x + transform[9] * Y[i].y + transform[10] * Y[i].z + transform[11];
924 }
925 return vertices;
926}
927
928RGBcolor Primitive::getColor() const {
929 if (context_ptr == nullptr) {
930 helios_runtime_error("ERROR (Primitive::getColor): Primitive not associated with a Context. Use Context::getPrimitiveColor() instead.");
931 }
932 const Material &mat = context_ptr->materials.at(materialID);
933 return {mat.color.r, mat.color.g, mat.color.b};
934}
935
936RGBcolor Primitive::getColorRGB() const {
937 if (context_ptr == nullptr) {
938 helios_runtime_error("ERROR (Primitive::getColorRGB): Primitive not associated with a Context. Use Context::getPrimitiveColor() instead.");
939 }
940 const Material &mat = context_ptr->materials.at(materialID);
941 return {mat.color.r, mat.color.g, mat.color.b};
942}
943
944RGBAcolor Primitive::getColorRGBA() const {
945 if (context_ptr == nullptr) {
946 helios_runtime_error("ERROR (Primitive::getColorRGBA): Primitive not associated with a Context. Use Context::getPrimitiveColorRGBA() instead.");
947 }
948 const Material &mat = context_ptr->materials.at(materialID);
949 return mat.color;
950}
951
952void Primitive::setColor(const helios::RGBcolor &newcolor) {
953 if (context_ptr == nullptr) {
954 helios_runtime_error("ERROR (Primitive::setColor): Primitive not associated with a Context. Use Context::setPrimitiveColor() instead.");
955 }
956 // Copy-on-write: create new material if shared
957 if (context_ptr->isMaterialShared(materialID)) {
958 materialID = context_ptr->copyMaterialForPrimitive(UUID);
959 }
960 context_ptr->materials.at(materialID).color = make_RGBAcolor(newcolor, 1.f);
961 dirty_flag = true;
962}
963
964void Primitive::setColor(const helios::RGBAcolor &newcolor) {
965 if (context_ptr == nullptr) {
966 helios_runtime_error("ERROR (Primitive::setColor): Primitive not associated with a Context. Use Context::setPrimitiveColor() instead.");
967 }
968 // Copy-on-write: create new material if shared
969 if (context_ptr->isMaterialShared(materialID)) {
970 materialID = context_ptr->copyMaterialForPrimitive(UUID);
971 }
972 context_ptr->materials.at(materialID).color = newcolor;
973 dirty_flag = true;
974}
975
976bool Primitive::hasTexture() const {
977 if (context_ptr == nullptr) {
978 helios_runtime_error("ERROR (Primitive::hasTexture): Primitive not associated with a Context.");
979 }
980 const Material &mat = context_ptr->materials.at(materialID);
981 return !mat.texture_file.empty();
982}
983
984std::string Primitive::getTextureFile() const {
985 if (context_ptr == nullptr) {
986 helios_runtime_error("ERROR (Primitive::getTextureFile): Primitive not associated with a Context. Use Context::getPrimitiveTextureFile() instead.");
987 }
988 const Material &mat = context_ptr->materials.at(materialID);
989 return mat.texture_file;
990}
991
992void Primitive::setTextureFile(const char *texture) {
993 if (context_ptr == nullptr) {
994 helios_runtime_error("ERROR (Primitive::setTextureFile): Primitive not associated with a Context. Use Context::setPrimitiveTextureFile() instead.");
995 }
996 // Copy-on-write: create new material if shared
997 if (context_ptr->isMaterialShared(materialID)) {
998 materialID = context_ptr->copyMaterialForPrimitive(UUID);
999 }
1000 context_ptr->materials.at(materialID).texture_file = texture;
1001 dirty_flag = true;
1002}
1003
1004std::vector<vec2> Primitive::getTextureUV() {
1005 return uv;
1006}
1007
1008void Primitive::setTextureUV(const std::vector<vec2> &a_uv) {
1009 uv = a_uv;
1010 dirty_flag = true;
1011}
1012
1013void Primitive::overrideTextureColor() {
1014 if (context_ptr == nullptr) {
1015 helios_runtime_error("ERROR (Primitive::overrideTextureColor): Primitive not associated with a Context. Use Context::overridePrimitiveTextureColor() instead.");
1016 }
1017 // Copy-on-write: create new material if shared
1018 if (context_ptr->isMaterialShared(materialID)) {
1019 materialID = context_ptr->copyMaterialForPrimitive(UUID);
1020 }
1021 context_ptr->materials.at(materialID).texture_color_overridden = true;
1022 dirty_flag = true;
1023}
1024
1025void Primitive::useTextureColor() {
1026 if (context_ptr == nullptr) {
1027 helios_runtime_error("ERROR (Primitive::useTextureColor): Primitive not associated with a Context. Use Context::usePrimitiveTextureColor() instead.");
1028 }
1029 // Copy-on-write: create new material if shared
1030 if (context_ptr->isMaterialShared(materialID)) {
1031 materialID = context_ptr->copyMaterialForPrimitive(UUID);
1032 }
1033 context_ptr->materials.at(materialID).texture_color_overridden = false;
1034 dirty_flag = true;
1035}
1036
1037bool Primitive::isTextureColorOverridden() const {
1038 if (context_ptr == nullptr) {
1039 helios_runtime_error("ERROR (Primitive::isTextureColorOverridden): Primitive not associated with a Context. Use Context::isPrimitiveTextureColorOverridden() instead.");
1040 }
1041 const Material &mat = context_ptr->materials.at(materialID);
1042 return mat.texture_color_overridden;
1043}
1044
1045float Primitive::getSolidFraction() const {
1046 return solid_fraction;
1047}
1048
1049void Primitive::setSolidFraction(float solidFraction) {
1050 solid_fraction = solidFraction;
1051 dirty_flag = true;
1052}
1053
1054bool Triangle::edgeFunction(const helios::vec2 &a, const helios::vec2 &b, const helios::vec2 &c) {
1055 return ((c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y) >= 0);
1056}
1057
1058void Triangle::setVertices(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2) {
1059 makeTransformationMatrix(vertex0, vertex1, vertex2);
1060 dirty_flag = true;
1061}
1062
1063void Primitive::applyTransform(float (&T)[16]) {
1064 if (parent_object_ID != 0) {
1065 static bool compound_transform_warning_shown = false;
1066 if (!compound_transform_warning_shown) {
1067 std::cerr << "WARNING (Primitive::applyTransform): Cannot transform individual primitives within a compound object. Use the setter function for objects." << std::endl;
1068 compound_transform_warning_shown = true;
1069 }
1070 return;
1071 }
1072
1073 matmult(T, transform, transform);
1074 dirty_flag = true;
1075}
1076
1077void Primitive::scale(const vec3 &S) {
1078 if (parent_object_ID != 0) {
1079 static bool compound_scale_warning_shown = false;
1080 if (!compound_scale_warning_shown) {
1081 std::cerr << "WARNING (Primitive::scale): Cannot scale individual primitives within a compound object. Use the setter function for objects." << std::endl;
1082 compound_scale_warning_shown = true;
1083 }
1084 return;
1085 }
1086 if (S.x == 0 || S.y == 0 || S.z == 0) {
1087 helios_runtime_error("ERROR (Primitive::scale): Scaling factor cannot be zero.");
1088 } else if (S.x == 1 && S.y == 1 && S.z == 1) {
1089 return;
1090 }
1091
1092 float T[16];
1093 makeScaleMatrix(S, T);
1094 matmult(T, transform, transform);
1095 dirty_flag = true;
1096}
1097
1098void Primitive::scale(const vec3 &S, const vec3 &point) {
1099 if (parent_object_ID != 0) {
1100 static bool compound_scale_point_warning_shown = false;
1101 if (!compound_scale_point_warning_shown) {
1102 std::cerr << "WARNING (Primitive::scale): Cannot scale individual primitives within a compound object. Use the setter function for objects." << std::endl;
1103 compound_scale_point_warning_shown = true;
1104 }
1105 return;
1106 }
1107 if (S.x == 0 || S.y == 0 || S.z == 0) {
1108 helios_runtime_error("ERROR (Primitive::scale): Scaling factor cannot be zero.");
1109 } else if (S.x == 1 && S.y == 1 && S.z == 1) {
1110 return;
1111 }
1112
1113 float T[16];
1114 makeScaleMatrix(S, point, T);
1115 matmult(T, transform, transform);
1116 dirty_flag = true;
1117}
1118
1119void Primitive::translate(const helios::vec3 &shift) {
1120 if (parent_object_ID != 0) {
1121 static bool compound_translate_warning_shown = false;
1122 if (!compound_translate_warning_shown) {
1123 std::cerr << "WARNING (Primitive::translate): Cannot translate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1124 compound_translate_warning_shown = true;
1125 }
1126 return;
1127 }
1128
1129 if (shift == nullorigin) {
1130 return;
1131 }
1132
1133 float T[16];
1134 makeTranslationMatrix(shift, T);
1135 matmult(T, transform, transform);
1136 dirty_flag = true;
1137}
1138
1139void Patch::rotate(float rotation_radians, const char *rotation_axis_xyz_string) {
1140 if (parent_object_ID != 0) {
1141 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1142 return;
1143 }
1144 if (rotation_radians == 0) {
1145 return;
1146 }
1147
1148 if (strcmp(rotation_axis_xyz_string, "z") == 0) {
1149 float Rz[16];
1150 makeRotationMatrix(rotation_radians, "z", Rz);
1151 matmult(Rz, transform, transform);
1152 } else if (strcmp(rotation_axis_xyz_string, "y") == 0) {
1153 float Ry[16];
1154 makeRotationMatrix(rotation_radians, "y", Ry);
1155 matmult(Ry, transform, transform);
1156 } else if (strcmp(rotation_axis_xyz_string, "x") == 0) {
1157 float Rx[16];
1158 makeRotationMatrix(rotation_radians, "x", Rx);
1159 matmult(Rx, transform, transform);
1160 } else {
1161 helios_runtime_error("ERROR (Patch::rotate): Rotation axis should be one of x, y, or z.");
1162 }
1163 dirty_flag = true;
1164}
1165
1166void Patch::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1167 if (parent_object_ID != 0) {
1168 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1169 return;
1170 }
1171 if (rotation_radians == 0) {
1172 return;
1173 }
1174
1175 float R[16];
1176 makeRotationMatrix(rotation_radians, rotation_axis_vector, R);
1177 matmult(R, transform, transform);
1178 dirty_flag = true;
1179}
1180
1181void Patch::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1182 if (parent_object_ID != 0) {
1183 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1184 return;
1185 }
1186 if (rotation_radians == 0) {
1187 return;
1188 }
1189
1190 float R[16];
1191 makeRotationMatrix(rotation_radians, origin, rotation_axis_vector, R);
1192 matmult(R, transform, transform);
1193 dirty_flag = true;
1194}
1195
1196void Triangle::rotate(float rotation_radians, const char *rotation_axis_xyz_string) {
1197 if (parent_object_ID != 0) {
1198 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1199 return;
1200 }
1201 if (rotation_radians == 0) {
1202 return;
1203 }
1204
1205 if (strcmp(rotation_axis_xyz_string, "z") == 0) {
1206 float Rz[16];
1207 makeRotationMatrix(rotation_radians, "z", Rz);
1208 matmult(Rz, transform, transform);
1209 } else if (strcmp(rotation_axis_xyz_string, "y") == 0) {
1210 float Ry[16];
1211 makeRotationMatrix(rotation_radians, "y", Ry);
1212 matmult(Ry, transform, transform);
1213 } else if (strcmp(rotation_axis_xyz_string, "x") == 0) {
1214 float Rx[16];
1215 makeRotationMatrix(rotation_radians, "x", Rx);
1216 matmult(Rx, transform, transform);
1217 } else {
1218 helios_runtime_error("ERROR (Triangle::rotate): Rotation axis should be one of x, y, or z.");
1219 }
1220 dirty_flag = true;
1221}
1222
1223void Triangle::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1224 if (parent_object_ID != 0) {
1225 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1226 return;
1227 }
1228 if (rotation_radians == 0) {
1229 return;
1230 }
1231
1232 float R[16];
1233 makeRotationMatrix(rotation_radians, rotation_axis_vector, R);
1234 matmult(R, transform, transform);
1235 dirty_flag = true;
1236}
1237
1238void Triangle::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1239 if (parent_object_ID != 0) {
1240 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1241 return;
1242 }
1243 if (rotation_radians == 0) {
1244 return;
1245 }
1246
1247 float R[16];
1248 makeRotationMatrix(rotation_radians, origin, rotation_axis_vector, R);
1249 matmult(R, transform, transform);
1250 dirty_flag = true;
1251}
1252
1253void Voxel::rotate(float rotation_radians, const char *rotation_axis_xyz_string) {
1254 if (parent_object_ID != 0) {
1255 static bool voxel_compound_rotate_warning_shown = false;
1256 if (!voxel_compound_rotate_warning_shown) {
1257 std::cerr << "WARNING (Voxel::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1258 voxel_compound_rotate_warning_shown = true;
1259 }
1260 return;
1261 }
1262 if (rotation_radians == 0) {
1263 return;
1264 }
1265
1266 float Rz[16];
1267 makeRotationMatrix(rotation_radians, "z", Rz);
1268 matmult(Rz, transform, transform);
1269 dirty_flag = true;
1270}
1271
1272void Voxel::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1273 static bool voxel_rotate_vec3_warning_shown = false;
1274 if (!voxel_rotate_vec3_warning_shown) {
1275 std::cerr << "WARNING (Voxel::rotate) - Voxels can only be rotated about the z-axis. Ignoring this call to rotate()." << std::endl;
1276 voxel_rotate_vec3_warning_shown = true;
1277 }
1278}
1279
1280void Voxel::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1281 static bool voxel_rotate_vec3_origin_warning_shown = false;
1282 if (!voxel_rotate_vec3_origin_warning_shown) {
1283 std::cerr << "WARNING (Voxel::rotate) - Voxels can only be rotated about the z-axis. Ignoring this call to rotate()." << std::endl;
1284 voxel_rotate_vec3_origin_warning_shown = true;
1285 }
1286}
1287
1288void Triangle::makeTransformationMatrix(const helios::vec3 &vert0, const helios::vec3 &vert1, const helios::vec3 &vert2) {
1289 // We need to construct the Affine transformation matrix that transforms some generic triangle to a triangle with vertices at vertex0, vertex1, vertex2.
1290
1291 // V1 is going to be our generic triangle. This is the triangle that we'll intersect in the OptiX ray intersection program. We just need to pass the transformation matrix to OptiX so that we'll end up with the right triangle.
1292
1293 // We'll assume our generic triangle has vertices
1294 // v0 = (0,0,0)
1295 // v1 = (0,1,0)
1296 // v2 = (1,1,0)
1297 // this needs to match up with the triangle in triangle_intersect() and triangle_bounds() (see primitiveIntersection.cu).
1298 // Note that the matrix is padded with 1's to make it 4x4
1299
1300 float V1[16];
1301
1302 /* [0,0] */
1303 V1[0] = 0.f;
1304 /* [0,1] */
1305 V1[1] = 0.f;
1306 /* [0,2] */
1307 V1[2] = 1.f;
1308
1309 /* [1,0] */
1310 V1[4] = 0.f;
1311 /* [1,1] */
1312 V1[5] = 1.f;
1313 /* [1,2] */
1314 V1[6] = 1.f;
1315
1316 /* [2,0] */
1317 V1[8] = 0.f;
1318 /* [2,1] */
1319 V1[9] = 0.f;
1320 /* [2,2] */
1321 V1[10] = 0.f;
1322
1323 /* [0,3] */
1324 V1[3] = 1.f;
1325 /* [1,3] */
1326 V1[7] = 1.f;
1327 /* [2,3] */
1328 V1[11] = 1.f;
1329 /* [3,0] */
1330 V1[12] = 1.f;
1331 /* [3,1] */
1332 V1[13] = 1.f;
1333 /* [3,2] */
1334 V1[14] = 1.f;
1335 /* [3,3] */
1336 V1[15] = 1.f;
1337
1338 // V2 holds the vertex locations we want to transform to
1339 // Note that the matrix is padded with 1's to make it 4x4
1340
1341 float V2[16];
1342 /* [0,0] */
1343 V2[0] = vert0.x;
1344 /* [0,1] */
1345 V2[1] = vert1.x;
1346 /* [0,2] */
1347 V2[2] = vert2.x;
1348 /* [0,3] */
1349 V2[3] = 1.f;
1350 /* [1,0] */
1351 V2[4] = vert0.y;
1352 /* [1,1] */
1353 V2[5] = vert1.y;
1354 /* [1,2] */
1355 V2[6] = vert2.y;
1356 /* [1,3] */
1357 V2[7] = 1.f;
1358 /* [2,0] */
1359 V2[8] = vert0.z;
1360 /* [2,1] */
1361 V2[9] = vert1.z;
1362 /* [2,2] */
1363 V2[10] = vert2.z;
1364 /* [2,3] */
1365 V2[11] = 1.f;
1366 /* [3,0] */
1367 V2[12] = 1.f;
1368 /* [3,1] */
1369 V2[13] = 1.f;
1370 /* [3,2] */
1371 V2[14] = 1.f;
1372 /* [3,3] */
1373 V2[15] = 1.f;
1374
1375 // Now we just need to solve the linear system for our transform matrix T
1376 // [T][V1] = [V2] -->
1377 // [T] = [V2]([V1]^-1)
1378
1379 double inv[16], det, invV1[16];
1380
1381 inv[0] = V1[5] * V1[10] * V1[15] - V1[5] * V1[11] * V1[14] - V1[9] * V1[6] * V1[15] + V1[9] * V1[7] * V1[14] + V1[13] * V1[6] * V1[11] - V1[13] * V1[7] * V1[10];
1382
1383 inv[4] = -V1[4] * V1[10] * V1[15] + V1[4] * V1[11] * V1[14] + V1[8] * V1[6] * V1[15] - V1[8] * V1[7] * V1[14] - V1[12] * V1[6] * V1[11] + V1[12] * V1[7] * V1[10];
1384
1385 inv[8] = V1[4] * V1[9] * V1[15] - V1[4] * V1[11] * V1[13] - V1[8] * V1[5] * V1[15] + V1[8] * V1[7] * V1[13] + V1[12] * V1[5] * V1[11] - V1[12] * V1[7] * V1[9];
1386
1387 inv[12] = -V1[4] * V1[9] * V1[14] + V1[4] * V1[10] * V1[13] + V1[8] * V1[5] * V1[14] - V1[8] * V1[6] * V1[13] - V1[12] * V1[5] * V1[10] + V1[12] * V1[6] * V1[9];
1388
1389 inv[1] = -V1[1] * V1[10] * V1[15] + V1[1] * V1[11] * V1[14] + V1[9] * V1[2] * V1[15] - V1[9] * V1[3] * V1[14] - V1[13] * V1[2] * V1[11] + V1[13] * V1[3] * V1[10];
1390
1391 inv[5] = V1[0] * V1[10] * V1[15] - V1[0] * V1[11] * V1[14] - V1[8] * V1[2] * V1[15] + V1[8] * V1[3] * V1[14] + V1[12] * V1[2] * V1[11] - V1[12] * V1[3] * V1[10];
1392
1393 inv[9] = -V1[0] * V1[9] * V1[15] + V1[0] * V1[11] * V1[13] + V1[8] * V1[1] * V1[15] - V1[8] * V1[3] * V1[13] - V1[12] * V1[1] * V1[11] + V1[12] * V1[3] * V1[9];
1394
1395 inv[13] = V1[0] * V1[9] * V1[14] - V1[0] * V1[10] * V1[13] - V1[8] * V1[1] * V1[14] + V1[8] * V1[2] * V1[13] + V1[12] * V1[1] * V1[10] - V1[12] * V1[2] * V1[9];
1396
1397 inv[2] = V1[1] * V1[6] * V1[15] - V1[1] * V1[7] * V1[14] - V1[5] * V1[2] * V1[15] + V1[5] * V1[3] * V1[14] + V1[13] * V1[2] * V1[7] - V1[13] * V1[3] * V1[6];
1398
1399 inv[6] = -V1[0] * V1[6] * V1[15] + V1[0] * V1[7] * V1[14] + V1[4] * V1[2] * V1[15] - V1[4] * V1[3] * V1[14] - V1[12] * V1[2] * V1[7] + V1[12] * V1[3] * V1[6];
1400
1401 inv[10] = V1[0] * V1[5] * V1[15] - V1[0] * V1[7] * V1[13] - V1[4] * V1[1] * V1[15] + V1[4] * V1[3] * V1[13] + V1[12] * V1[1] * V1[7] - V1[12] * V1[3] * V1[5];
1402
1403 inv[14] = -V1[0] * V1[5] * V1[14] + V1[0] * V1[6] * V1[13] + V1[4] * V1[1] * V1[14] - V1[4] * V1[2] * V1[13] - V1[12] * V1[1] * V1[6] + V1[12] * V1[2] * V1[5];
1404
1405 inv[3] = -V1[1] * V1[6] * V1[11] + V1[1] * V1[7] * V1[10] + V1[5] * V1[2] * V1[11] - V1[5] * V1[3] * V1[10] - V1[9] * V1[2] * V1[7] + V1[9] * V1[3] * V1[6];
1406
1407 inv[7] = V1[0] * V1[6] * V1[11] - V1[0] * V1[7] * V1[10] - V1[4] * V1[2] * V1[11] + V1[4] * V1[3] * V1[10] + V1[8] * V1[2] * V1[7] - V1[8] * V1[3] * V1[6];
1408
1409 inv[11] = -V1[0] * V1[5] * V1[11] + V1[0] * V1[7] * V1[9] + V1[4] * V1[1] * V1[11] - V1[4] * V1[3] * V1[9] - V1[8] * V1[1] * V1[7] + V1[8] * V1[3] * V1[5];
1410
1411 inv[15] = V1[0] * V1[5] * V1[10] - V1[0] * V1[6] * V1[9] - V1[4] * V1[1] * V1[10] + V1[4] * V1[2] * V1[9] + V1[8] * V1[1] * V1[6] - V1[8] * V1[2] * V1[5];
1412
1413 det = V1[0] * inv[0] + V1[1] * inv[4] + V1[2] * inv[8] + V1[3] * inv[12];
1414
1415 // if (det == 0)
1416 // return false;
1417
1418 det = 1.0 / det;
1419
1420 for (int i = 0; i < 16; i++)
1421 invV1[i] = inv[i] * det;
1422
1423 for (int i = 0; i < 4; i++) {
1424 for (int j = 0; j < 4; j++) {
1425 transform[j + i * 4] = 0.f;
1426 }
1427 }
1428
1429 // Multiply to get transformation matrix [T] = [V2]([V1]^-1)
1430 for (int i = 0; i < 4; i++) {
1431 for (int j = 0; j < 4; j++) {
1432 for (int k = 0; k < 4; k++) {
1433 transform[j + i * 4] += V2[k + i * 4] * float(invV1[j + k * 4]);
1434 }
1435 }
1436 }
1437 dirty_flag = true;
1438}
1439
1440Patch::Patch(const RGBAcolor &a_color, uint a_parent_objID, uint a_UUID) {
1441 makeIdentityMatrix(transform);
1442
1443 assert(a_color.r >= 0 && a_color.r <= 1 && a_color.g >= 0 && a_color.g <= 1 && a_color.b >= 0 && a_color.b <= 1);
1444 parent_object_ID = a_parent_objID;
1445 UUID = a_UUID;
1446 prim_type = PRIMITIVE_TYPE_PATCH;
1447 solid_fraction = 1.f;
1448 materialID = 0; // Will be set by Context after construction
1449 context_ptr = nullptr; // Will be set by Context after construction
1450 dirty_flag = true;
1451}
1452
1453Patch::Patch(const char *a_texturefile, float a_solid_fraction, uint a_parent_objID, uint a_UUID) {
1454 makeIdentityMatrix(transform);
1455
1456 parent_object_ID = a_parent_objID;
1457 UUID = a_UUID;
1458 prim_type = PRIMITIVE_TYPE_PATCH;
1459 solid_fraction = a_solid_fraction;
1460 materialID = 0; // Will be set by Context after construction
1461 context_ptr = nullptr; // Will be set by Context after construction
1462 dirty_flag = true;
1463}
1464
1465Patch::Patch(const char *a_texturefile, const std::vector<helios::vec2> &a_uv, std::map<std::string, Texture> &textures, uint a_parent_objID, uint a_UUID) {
1466 makeIdentityMatrix(transform);
1467
1468 parent_object_ID = a_parent_objID;
1469 UUID = a_UUID;
1470 prim_type = PRIMITIVE_TYPE_PATCH;
1471
1472 uv = a_uv;
1473 for (auto &uv_vert: uv) {
1474 uv_vert.x = std::min(uv_vert.x, 1.f);
1475 uv_vert.y = std::min(uv_vert.y, 1.f);
1476 }
1477
1478 solid_fraction = textures.at(a_texturefile).getSolidFraction(uv);
1479 materialID = 0; // Will be set by Context after construction
1480 context_ptr = nullptr; // Will be set by Context after construction
1481 dirty_flag = true;
1482}
1483
1484helios::vec2 Patch::getSize() const {
1485 const std::vector<vec3> &vertices = getVertices();
1486 float l = (vertices.at(1) - vertices.at(0)).magnitude();
1487 float w = (vertices.at(3) - vertices.at(0)).magnitude();
1488 return {l, w};
1489}
1490
1491helios::vec3 Patch::getCenter() const {
1492 return make_vec3(transform[3], transform[7], transform[11]);
1493}
1494
1495Triangle::Triangle(const helios::vec3 &a_vertex0, const helios::vec3 &a_vertex1, const helios::vec3 &a_vertex2, const helios::RGBAcolor &a_color, uint a_parent_objID, uint a_UUID) {
1496 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1497 parent_object_ID = a_parent_objID;
1498 UUID = a_UUID;
1499 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1500 solid_fraction = 1.f;
1501 materialID = 0; // Will be set by Context after construction
1502 context_ptr = nullptr; // Will be set by Context after construction
1503 dirty_flag = true;
1504}
1505
1506Triangle::Triangle(const helios::vec3 &a_vertex0, const helios::vec3 &a_vertex1, const helios::vec3 &a_vertex2, const char *a_texturefile, const std::vector<helios::vec2> &a_uv, float solid_fraction, uint a_parent_objID, uint a_UUID) {
1507 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1508 parent_object_ID = a_parent_objID;
1509 UUID = a_UUID;
1510 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1511
1512 uv = a_uv;
1513 this->solid_fraction = solid_fraction;
1514 materialID = 0; // Will be set by Context after construction
1515 context_ptr = nullptr; // Will be set by Context after construction
1516 dirty_flag = true;
1517}
1518
1519Triangle::Triangle(const helios::vec3 &a_vertex0, const helios::vec3 &a_vertex1, const helios::vec3 &a_vertex2, const char *a_texturefile, const std::vector<helios::vec2> &a_uv, std::map<std::string, Texture> &textures, uint a_parent_objID,
1520 uint a_UUID) {
1521 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1522 parent_object_ID = a_parent_objID;
1523 UUID = a_UUID;
1524 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1525
1526 uv = a_uv;
1527 for (auto &uv_vert: uv) {
1528 uv_vert.x = std::min(uv_vert.x, 1.f);
1529 uv_vert.y = std::min(uv_vert.y, 1.f);
1530 }
1531 solid_fraction = 1.f;
1532
1533 solid_fraction = textures.at(a_texturefile).getSolidFraction(uv);
1534 materialID = 0; // Will be set by Context after construction
1535 context_ptr = nullptr; // Will be set by Context after construction
1536 dirty_flag = true;
1537}
1538
1539vec3 Triangle::getVertex(int vertex_index) const {
1540 if (vertex_index < 0 || vertex_index > 2) {
1541 helios_runtime_error("ERROR (Context::getVertex): vertex index must be 1, 2, or 3.");
1542 }
1543
1544 const std::vector<vec3> Y = {{0.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {1.f, 1.f, 0.f}};
1545
1546 vec3 vertex;
1547
1548 vertex.x = transform[0] * Y[vertex_index].x + transform[1] * Y[vertex_index].y + transform[2] * Y[vertex_index].z + transform[3];
1549 vertex.y = transform[4] * Y[vertex_index].x + transform[5] * Y[vertex_index].y + transform[6] * Y[vertex_index].z + transform[7];
1550 vertex.z = transform[8] * Y[vertex_index].x + transform[9] * Y[vertex_index].y + transform[10] * Y[vertex_index].z + transform[11];
1551
1552 return vertex;
1553}
1554
1555vec3 Triangle::getCenter() const {
1556 // Y[0] = make_vec3( 0.f, 0.f, 0.f);
1557 // Y[1] = make_vec3( 0.f, 1.f, 0.f);
1558 // Y[2] = make_vec3( 1.f/3.f, 1.f, 0.f);
1559
1560 vec3 center0(1.f / 3.f, 2.f / 3.f, 0.f);
1561 vec3 center;
1562
1563 center.x = transform[0] * center0.x + transform[1] * center0.y + transform[2] * center0.z + transform[3];
1564 center.y = transform[4] * center0.x + transform[5] * center0.y + transform[6] * center0.z + transform[7];
1565 center.z = transform[8] * center0.x + transform[9] * center0.y + transform[10] * center0.z + transform[11];
1566
1567 return center;
1568}
1569
1570Voxel::Voxel(const RGBAcolor &a_color, uint a_parent_objID, uint a_UUID) {
1571 makeIdentityMatrix(transform);
1572
1573 assert(a_color.r >= 0 && a_color.r <= 1 && a_color.g >= 0 && a_color.g <= 1 && a_color.b >= 0 && a_color.b <= 1);
1574 solid_fraction = 1.f;
1575 parent_object_ID = a_parent_objID;
1576 UUID = a_UUID;
1577 prim_type = PRIMITIVE_TYPE_VOXEL;
1578 materialID = 0; // Will be set by Context after construction
1579 context_ptr = nullptr; // Will be set by Context after construction
1580 dirty_flag = true;
1581}
1582
1583float Voxel::getVolume() {
1584 const vec3 &size = getSize();
1585
1586 return size.x * size.y * size.z;
1587}
1588
1589vec3 Voxel::getCenter() const {
1590 return make_vec3(transform[3], transform[7], transform[11]);
1591}
1592
1593vec3 Voxel::getSize() const {
1594 vec3 n0(0, 0, 0), nx(1, 0, 0), ny(0, 1, 0), nz(0, 0, 1);
1595 vec3 n0_T, nx_T, ny_T, nz_T;
1596
1597 vecmult(transform, n0, n0_T);
1598 vecmult(transform, nx, nx_T);
1599 vecmult(transform, ny, ny_T);
1600 vecmult(transform, nz, nz_T);
1601
1602 float x = (nx_T - n0_T).magnitude();
1603 float y = (ny_T - n0_T).magnitude();
1604 float z = (nz_T - n0_T).magnitude();
1605
1606 return {x, y, z};
1607}