1.3.77
 
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 static bool patch_rotate_axis_warning_shown = false;
1142 if (!patch_rotate_axis_warning_shown) {
1143 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1144 patch_rotate_axis_warning_shown = true;
1145 }
1146 return;
1147 }
1148 if (rotation_radians == 0) {
1149 return;
1150 }
1151
1152 if (strcmp(rotation_axis_xyz_string, "z") == 0) {
1153 float Rz[16];
1154 makeRotationMatrix(rotation_radians, "z", Rz);
1155 matmult(Rz, transform, transform);
1156 } else if (strcmp(rotation_axis_xyz_string, "y") == 0) {
1157 float Ry[16];
1158 makeRotationMatrix(rotation_radians, "y", Ry);
1159 matmult(Ry, transform, transform);
1160 } else if (strcmp(rotation_axis_xyz_string, "x") == 0) {
1161 float Rx[16];
1162 makeRotationMatrix(rotation_radians, "x", Rx);
1163 matmult(Rx, transform, transform);
1164 } else {
1165 helios_runtime_error("ERROR (Patch::rotate): Rotation axis should be one of x, y, or z.");
1166 }
1167 dirty_flag = true;
1168}
1169
1170void Patch::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1171 if (parent_object_ID != 0) {
1172 static bool patch_rotate_vec_warning_shown = false;
1173 if (!patch_rotate_vec_warning_shown) {
1174 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1175 patch_rotate_vec_warning_shown = true;
1176 }
1177 return;
1178 }
1179 if (rotation_radians == 0) {
1180 return;
1181 }
1182
1183 float R[16];
1184 makeRotationMatrix(rotation_radians, rotation_axis_vector, R);
1185 matmult(R, transform, transform);
1186 dirty_flag = true;
1187}
1188
1189void Patch::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1190 if (parent_object_ID != 0) {
1191 static bool patch_rotate_origin_warning_shown = false;
1192 if (!patch_rotate_origin_warning_shown) {
1193 std::cerr << "WARNING (Patch::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1194 patch_rotate_origin_warning_shown = true;
1195 }
1196 return;
1197 }
1198 if (rotation_radians == 0) {
1199 return;
1200 }
1201
1202 float R[16];
1203 makeRotationMatrix(rotation_radians, origin, rotation_axis_vector, R);
1204 matmult(R, transform, transform);
1205 dirty_flag = true;
1206}
1207
1208void Triangle::rotate(float rotation_radians, const char *rotation_axis_xyz_string) {
1209 if (parent_object_ID != 0) {
1210 static bool triangle_rotate_axis_warning_shown = false;
1211 if (!triangle_rotate_axis_warning_shown) {
1212 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1213 triangle_rotate_axis_warning_shown = true;
1214 }
1215 return;
1216 }
1217 if (rotation_radians == 0) {
1218 return;
1219 }
1220
1221 if (strcmp(rotation_axis_xyz_string, "z") == 0) {
1222 float Rz[16];
1223 makeRotationMatrix(rotation_radians, "z", Rz);
1224 matmult(Rz, transform, transform);
1225 } else if (strcmp(rotation_axis_xyz_string, "y") == 0) {
1226 float Ry[16];
1227 makeRotationMatrix(rotation_radians, "y", Ry);
1228 matmult(Ry, transform, transform);
1229 } else if (strcmp(rotation_axis_xyz_string, "x") == 0) {
1230 float Rx[16];
1231 makeRotationMatrix(rotation_radians, "x", Rx);
1232 matmult(Rx, transform, transform);
1233 } else {
1234 helios_runtime_error("ERROR (Triangle::rotate): Rotation axis should be one of x, y, or z.");
1235 }
1236 dirty_flag = true;
1237}
1238
1239void Triangle::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1240 if (parent_object_ID != 0) {
1241 static bool triangle_rotate_vec_warning_shown = false;
1242 if (!triangle_rotate_vec_warning_shown) {
1243 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1244 triangle_rotate_vec_warning_shown = true;
1245 }
1246 return;
1247 }
1248 if (rotation_radians == 0) {
1249 return;
1250 }
1251
1252 float R[16];
1253 makeRotationMatrix(rotation_radians, rotation_axis_vector, R);
1254 matmult(R, transform, transform);
1255 dirty_flag = true;
1256}
1257
1258void Triangle::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1259 if (parent_object_ID != 0) {
1260 static bool triangle_rotate_origin_warning_shown = false;
1261 if (!triangle_rotate_origin_warning_shown) {
1262 std::cerr << "WARNING (Triangle::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1263 triangle_rotate_origin_warning_shown = true;
1264 }
1265 return;
1266 }
1267 if (rotation_radians == 0) {
1268 return;
1269 }
1270
1271 float R[16];
1272 makeRotationMatrix(rotation_radians, origin, rotation_axis_vector, R);
1273 matmult(R, transform, transform);
1274 dirty_flag = true;
1275}
1276
1277void Voxel::rotate(float rotation_radians, const char *rotation_axis_xyz_string) {
1278 if (parent_object_ID != 0) {
1279 static bool voxel_compound_rotate_warning_shown = false;
1280 if (!voxel_compound_rotate_warning_shown) {
1281 std::cerr << "WARNING (Voxel::rotate): Cannot rotate individual primitives within a compound object. Use the setter function for objects." << std::endl;
1282 voxel_compound_rotate_warning_shown = true;
1283 }
1284 return;
1285 }
1286 if (rotation_radians == 0) {
1287 return;
1288 }
1289
1290 float Rz[16];
1291 makeRotationMatrix(rotation_radians, "z", Rz);
1292 matmult(Rz, transform, transform);
1293 dirty_flag = true;
1294}
1295
1296void Voxel::rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) {
1297 static bool voxel_rotate_vec3_warning_shown = false;
1298 if (!voxel_rotate_vec3_warning_shown) {
1299 std::cerr << "WARNING (Voxel::rotate) - Voxels can only be rotated about the z-axis. Ignoring this call to rotate()." << std::endl;
1300 voxel_rotate_vec3_warning_shown = true;
1301 }
1302}
1303
1304void Voxel::rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) {
1305 static bool voxel_rotate_vec3_origin_warning_shown = false;
1306 if (!voxel_rotate_vec3_origin_warning_shown) {
1307 std::cerr << "WARNING (Voxel::rotate) - Voxels can only be rotated about the z-axis. Ignoring this call to rotate()." << std::endl;
1308 voxel_rotate_vec3_origin_warning_shown = true;
1309 }
1310}
1311
1312void Triangle::makeTransformationMatrix(const helios::vec3 &vert0, const helios::vec3 &vert1, const helios::vec3 &vert2) {
1313 // We need to construct the Affine transformation matrix that transforms some generic triangle to a triangle with vertices at vertex0, vertex1, vertex2.
1314
1315 // 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.
1316
1317 // We'll assume our generic triangle has vertices
1318 // v0 = (0,0,0)
1319 // v1 = (0,1,0)
1320 // v2 = (1,1,0)
1321 // this needs to match up with the triangle in triangle_intersect() and triangle_bounds() (see primitiveIntersection.cu).
1322 // Note that the matrix is padded with 1's to make it 4x4
1323
1324 float V1[16];
1325
1326 /* [0,0] */
1327 V1[0] = 0.f;
1328 /* [0,1] */
1329 V1[1] = 0.f;
1330 /* [0,2] */
1331 V1[2] = 1.f;
1332
1333 /* [1,0] */
1334 V1[4] = 0.f;
1335 /* [1,1] */
1336 V1[5] = 1.f;
1337 /* [1,2] */
1338 V1[6] = 1.f;
1339
1340 /* [2,0] */
1341 V1[8] = 0.f;
1342 /* [2,1] */
1343 V1[9] = 0.f;
1344 /* [2,2] */
1345 V1[10] = 0.f;
1346
1347 /* [0,3] */
1348 V1[3] = 1.f;
1349 /* [1,3] */
1350 V1[7] = 1.f;
1351 /* [2,3] */
1352 V1[11] = 1.f;
1353 /* [3,0] */
1354 V1[12] = 1.f;
1355 /* [3,1] */
1356 V1[13] = 1.f;
1357 /* [3,2] */
1358 V1[14] = 1.f;
1359 /* [3,3] */
1360 V1[15] = 1.f;
1361
1362 // V2 holds the vertex locations we want to transform to
1363 // Note that the matrix is padded with 1's to make it 4x4
1364
1365 float V2[16];
1366 /* [0,0] */
1367 V2[0] = vert0.x;
1368 /* [0,1] */
1369 V2[1] = vert1.x;
1370 /* [0,2] */
1371 V2[2] = vert2.x;
1372 /* [0,3] */
1373 V2[3] = 1.f;
1374 /* [1,0] */
1375 V2[4] = vert0.y;
1376 /* [1,1] */
1377 V2[5] = vert1.y;
1378 /* [1,2] */
1379 V2[6] = vert2.y;
1380 /* [1,3] */
1381 V2[7] = 1.f;
1382 /* [2,0] */
1383 V2[8] = vert0.z;
1384 /* [2,1] */
1385 V2[9] = vert1.z;
1386 /* [2,2] */
1387 V2[10] = vert2.z;
1388 /* [2,3] */
1389 V2[11] = 1.f;
1390 /* [3,0] */
1391 V2[12] = 1.f;
1392 /* [3,1] */
1393 V2[13] = 1.f;
1394 /* [3,2] */
1395 V2[14] = 1.f;
1396 /* [3,3] */
1397 V2[15] = 1.f;
1398
1399 // Now we just need to solve the linear system for our transform matrix T
1400 // [T][V1] = [V2] -->
1401 // [T] = [V2]([V1]^-1)
1402
1403 double inv[16], det, invV1[16];
1404
1405 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];
1406
1407 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];
1408
1409 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];
1410
1411 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];
1412
1413 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];
1414
1415 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];
1416
1417 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];
1418
1419 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];
1420
1421 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];
1422
1423 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];
1424
1425 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];
1426
1427 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];
1428
1429 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];
1430
1431 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];
1432
1433 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];
1434
1435 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];
1436
1437 det = V1[0] * inv[0] + V1[1] * inv[4] + V1[2] * inv[8] + V1[3] * inv[12];
1438
1439 // if (det == 0)
1440 // return false;
1441
1442 det = 1.0 / det;
1443
1444 for (int i = 0; i < 16; i++)
1445 invV1[i] = inv[i] * det;
1446
1447 for (int i = 0; i < 4; i++) {
1448 for (int j = 0; j < 4; j++) {
1449 transform[j + i * 4] = 0.f;
1450 }
1451 }
1452
1453 // Multiply to get transformation matrix [T] = [V2]([V1]^-1)
1454 for (int i = 0; i < 4; i++) {
1455 for (int j = 0; j < 4; j++) {
1456 for (int k = 0; k < 4; k++) {
1457 transform[j + i * 4] += V2[k + i * 4] * float(invV1[j + k * 4]);
1458 }
1459 }
1460 }
1461 dirty_flag = true;
1462}
1463
1464Patch::Patch(const RGBAcolor &a_color, uint a_parent_objID, uint a_UUID) {
1465 makeIdentityMatrix(transform);
1466
1467 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);
1468 parent_object_ID = a_parent_objID;
1469 UUID = a_UUID;
1470 prim_type = PRIMITIVE_TYPE_PATCH;
1471 solid_fraction = 1.f;
1472 materialID = 0; // Will be set by Context after construction
1473 context_ptr = nullptr; // Will be set by Context after construction
1474 dirty_flag = true;
1475}
1476
1477Patch::Patch(const char *a_texturefile, float a_solid_fraction, uint a_parent_objID, uint a_UUID) {
1478 makeIdentityMatrix(transform);
1479
1480 parent_object_ID = a_parent_objID;
1481 UUID = a_UUID;
1482 prim_type = PRIMITIVE_TYPE_PATCH;
1483 solid_fraction = a_solid_fraction;
1484 materialID = 0; // Will be set by Context after construction
1485 context_ptr = nullptr; // Will be set by Context after construction
1486 dirty_flag = true;
1487}
1488
1489Patch::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) {
1490 makeIdentityMatrix(transform);
1491
1492 parent_object_ID = a_parent_objID;
1493 UUID = a_UUID;
1494 prim_type = PRIMITIVE_TYPE_PATCH;
1495
1496 uv = a_uv;
1497 for (auto &uv_vert: uv) {
1498 uv_vert.x = std::min(uv_vert.x, 1.f);
1499 uv_vert.y = std::min(uv_vert.y, 1.f);
1500 }
1501
1502 solid_fraction = textures.at(a_texturefile).getSolidFraction(uv);
1503 materialID = 0; // Will be set by Context after construction
1504 context_ptr = nullptr; // Will be set by Context after construction
1505 dirty_flag = true;
1506}
1507
1508helios::vec2 Patch::getSize() const {
1509 const std::vector<vec3> &vertices = getVertices();
1510 float l = (vertices.at(1) - vertices.at(0)).magnitude();
1511 float w = (vertices.at(3) - vertices.at(0)).magnitude();
1512 return {l, w};
1513}
1514
1515helios::vec3 Patch::getCenter() const {
1516 return make_vec3(transform[3], transform[7], transform[11]);
1517}
1518
1519Triangle::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) {
1520 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1521 parent_object_ID = a_parent_objID;
1522 UUID = a_UUID;
1523 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1524 solid_fraction = 1.f;
1525 materialID = 0; // Will be set by Context after construction
1526 context_ptr = nullptr; // Will be set by Context after construction
1527 dirty_flag = true;
1528}
1529
1530Triangle::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) {
1531 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1532 parent_object_ID = a_parent_objID;
1533 UUID = a_UUID;
1534 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1535
1536 uv = a_uv;
1537 this->solid_fraction = solid_fraction;
1538 materialID = 0; // Will be set by Context after construction
1539 context_ptr = nullptr; // Will be set by Context after construction
1540 dirty_flag = true;
1541}
1542
1543Triangle::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,
1544 uint a_UUID) {
1545 makeTransformationMatrix(a_vertex0, a_vertex1, a_vertex2);
1546 parent_object_ID = a_parent_objID;
1547 UUID = a_UUID;
1548 prim_type = PRIMITIVE_TYPE_TRIANGLE;
1549
1550 uv = a_uv;
1551 for (auto &uv_vert: uv) {
1552 uv_vert.x = std::min(uv_vert.x, 1.f);
1553 uv_vert.y = std::min(uv_vert.y, 1.f);
1554 }
1555 solid_fraction = 1.f;
1556
1557 solid_fraction = textures.at(a_texturefile).getSolidFraction(uv);
1558 materialID = 0; // Will be set by Context after construction
1559 context_ptr = nullptr; // Will be set by Context after construction
1560 dirty_flag = true;
1561}
1562
1563vec3 Triangle::getVertex(int vertex_index) const {
1564 if (vertex_index < 0 || vertex_index > 2) {
1565 helios_runtime_error("ERROR (Context::getVertex): vertex index must be 1, 2, or 3.");
1566 }
1567
1568 const std::vector<vec3> Y = {{0.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {1.f, 1.f, 0.f}};
1569
1570 vec3 vertex;
1571
1572 vertex.x = transform[0] * Y[vertex_index].x + transform[1] * Y[vertex_index].y + transform[2] * Y[vertex_index].z + transform[3];
1573 vertex.y = transform[4] * Y[vertex_index].x + transform[5] * Y[vertex_index].y + transform[6] * Y[vertex_index].z + transform[7];
1574 vertex.z = transform[8] * Y[vertex_index].x + transform[9] * Y[vertex_index].y + transform[10] * Y[vertex_index].z + transform[11];
1575
1576 return vertex;
1577}
1578
1579vec3 Triangle::getCenter() const {
1580 // Y[0] = make_vec3( 0.f, 0.f, 0.f);
1581 // Y[1] = make_vec3( 0.f, 1.f, 0.f);
1582 // Y[2] = make_vec3( 1.f/3.f, 1.f, 0.f);
1583
1584 vec3 center0(1.f / 3.f, 2.f / 3.f, 0.f);
1585 vec3 center;
1586
1587 center.x = transform[0] * center0.x + transform[1] * center0.y + transform[2] * center0.z + transform[3];
1588 center.y = transform[4] * center0.x + transform[5] * center0.y + transform[6] * center0.z + transform[7];
1589 center.z = transform[8] * center0.x + transform[9] * center0.y + transform[10] * center0.z + transform[11];
1590
1591 return center;
1592}
1593
1594Voxel::Voxel(const RGBAcolor &a_color, uint a_parent_objID, uint a_UUID) {
1595 makeIdentityMatrix(transform);
1596
1597 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);
1598 solid_fraction = 1.f;
1599 parent_object_ID = a_parent_objID;
1600 UUID = a_UUID;
1601 prim_type = PRIMITIVE_TYPE_VOXEL;
1602 materialID = 0; // Will be set by Context after construction
1603 context_ptr = nullptr; // Will be set by Context after construction
1604 dirty_flag = true;
1605}
1606
1607float Voxel::getVolume() {
1608 const vec3 &size = getSize();
1609
1610 return size.x * size.y * size.z;
1611}
1612
1613vec3 Voxel::getCenter() const {
1614 return make_vec3(transform[3], transform[7], transform[11]);
1615}
1616
1617vec3 Voxel::getSize() const {
1618 vec3 n0(0, 0, 0), nx(1, 0, 0), ny(0, 1, 0), nz(0, 0, 1);
1619 vec3 n0_T, nx_T, ny_T, nz_T;
1620
1621 vecmult(transform, n0, n0_T);
1622 vecmult(transform, nx, nx_T);
1623 vecmult(transform, ny, ny_T);
1624 vecmult(transform, nz, nz_T);
1625
1626 float x = (nx_T - n0_T).magnitude();
1627 float y = (ny_T - n0_T).magnitude();
1628 float z = (nz_T - n0_T).magnitude();
1629
1630 return {x, y, z};
1631}