18using namespace helios;
22static constexpr float MIN_TRIANGLE_AREA_THRESHOLD = 1e-8f;
25 std::string data_str = node_data.child_value();
27 if (!data_str.empty()) {
28 std::istringstream data_stream(data_str);
31 while (data_stream >> tmp_s) {
33 data.push_back(tmp_f);
46 std::string data_str = node_data.child_value();
48 if (!data_str.empty()) {
49 std::istringstream data_stream(data_str);
52 while (data_stream >> tmp_s) {
54 data.push_back(tmp_f);
67 std::string data_str = node_data.child_value();
69 if (!data_str.empty()) {
70 std::istringstream data_stream(data_str);
73 while (data_stream >> tmp_s) {
75 data.push_back(tmp_f);
88 std::string data_str = node_data.child_value();
90 if (!data_str.empty()) {
91 std::istringstream data_stream(data_str);
94 while (data_stream >> tmp_s) {
96 data.push_back(tmp_f);
109 std::string data_str = node_data.child_value();
111 if (!data_str.empty()) {
112 std::istringstream data_stream(data_str);
114 while (data_stream >> tmp_s) {
115 data.push_back(tmp_s);
125 std::string data_str = node_data.child_value();
127 if (!data_str.empty()) {
128 std::istringstream data_stream(data_str);
129 std::vector<std::string> tmp_s(2);
131 while (data_stream >> tmp_s[0]) {
132 data_stream >> tmp_s[1];
147 std::string data_str = node_data.child_value();
149 if (!data_str.empty()) {
150 std::istringstream data_stream(data_str);
151 std::vector<std::string> tmp_s(3);
153 while (data_stream >> tmp_s[0]) {
154 data_stream >> tmp_s[1];
155 data_stream >> tmp_s[2];
170 std::string data_str = node_data.child_value();
172 if (!data_str.empty()) {
173 std::istringstream data_stream(data_str);
174 std::vector<std::string> tmp_s(4);
176 while (data_stream >> tmp_s[0]) {
177 data_stream >> tmp_s[1];
178 data_stream >> tmp_s[2];
179 data_stream >> tmp_s[3];
194 std::string data_str = node_data.child_value();
196 if (!data_str.empty()) {
197 std::istringstream data_stream(data_str);
198 std::vector<std::string> tmp_s(2);
200 while (data_stream >> tmp_s[0]) {
201 data_stream >> tmp_s[1];
216 std::string data_str = node_data.child_value();
218 if (!data_str.empty()) {
219 std::istringstream data_stream(data_str);
220 std::vector<std::string> tmp_s(3);
222 while (data_stream >> tmp_s[0]) {
223 data_stream >> tmp_s[1];
224 data_stream >> tmp_s[2];
239 std::string data_str = node_data.child_value();
241 if (!data_str.empty()) {
242 std::istringstream data_stream(data_str);
243 std::vector<std::string> tmp_s(4);
245 while (data_stream >> tmp_s[0]) {
246 data_stream >> tmp_s[1];
247 data_stream >> tmp_s[2];
248 data_stream >> tmp_s[3];
263 pugi::xml_node objID_node = node.child(
"objID");
278 pugi::xml_node transform_node = node.child(
"transform");
280 std::string transform_str = transform_node.child_value();
281 if (transform_str.empty()) {
285 std::istringstream stream(transform_str);
289 while (stream >> tmp_s) {
305 pugi::xml_node texture_node = node.child(
"texture");
307 if (texfile.empty()) {
317 pugi::xml_node uv_node = node.child(
"textureUV");
318 std::string texUV = uv_node.child_value();
319 if (!texUV.empty()) {
320 std::istringstream uv_stream(texUV);
321 std::vector<std::string> tmp_s(2);
323 while (uv_stream >> tmp_s[0]) {
324 uv_stream >> tmp_s[1];
339 pugi::xml_node sfrac_node = node.child(
"solid_fraction");
341 if (!sfrac.empty()) {
354 pugi::xml_node vertices_node = node.child(
"vertices");
356 std::string vertices_str = vertices_node.child_value();
357 if (!vertices_str.empty()) {
358 std::istringstream stream(vertices_str);
362 while (stream >> tmp_s) {
366 vertices.at(i) = tmp;
381 pugi::xml_node subdiv_node = node.child(
"subdivisions");
383 if (!subdiv.empty()) {
394 pugi::xml_node subdiv_node = node.child(
"subdivisions");
396 if (!subdiv.empty()) {
397 std::istringstream data_stream(subdiv);
398 std::vector<std::string> tmp_s(2);
399 data_stream >> tmp_s[0];
400 data_stream >> tmp_s[1];
411 pugi::xml_node subdiv_node = node.child(
"subdivisions");
413 if (!subdiv.empty()) {
414 std::istringstream data_stream(subdiv);
415 std::vector<std::string> tmp_s(3);
416 data_stream >> tmp_s[0];
417 data_stream >> tmp_s[1];
418 data_stream >> tmp_s[2];
429 pugi::xml_node node_data = node.child(
"nodes");
430 std::string data_str = node_data.child_value();
432 if (!data_str.empty()) {
433 std::istringstream data_stream(data_str);
434 std::vector<std::string> tmp_s(3);
436 while (data_stream >> tmp_s[0]) {
437 data_stream >> tmp_s[1];
438 data_stream >> tmp_s[2];
442 nodes.push_back(tmp);
453 pugi::xml_node node_data = node.child(
"radius");
454 std::string data_str = node_data.child_value();
456 if (!data_str.empty()) {
457 std::istringstream data_stream(data_str);
460 while (data_stream >> tmp_s) {
462 radius.push_back(tmp_f);
474void Context::loadPData(pugi::xml_node p,
uint UUID) {
475 for (pugi::xml_node data = p.child(
"data_int"); data; data = data.next_sibling(
"data_int")) {
476 const char *label = data.attribute(
"label").value();
478 std::vector<int> datav;
480 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int> with label " + std::string(label) +
" contained invalid data.");
483 if (datav.size() == 1) {
485 }
else if (datav.size() > 1) {
490 for (pugi::xml_node data = p.child(
"data_uint"); data; data = data.next_sibling(
"data_uint")) {
491 const char *label = data.attribute(
"label").value();
493 std::vector<uint> datav;
495 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_uint> with label " + std::string(label) +
" contained invalid data.");
498 if (datav.size() == 1) {
500 }
else if (datav.size() > 1) {
505 for (pugi::xml_node data = p.child(
"data_float"); data; data = data.next_sibling(
"data_float")) {
506 const char *label = data.attribute(
"label").value();
508 std::vector<float> datav;
510 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_float> with label " + std::string(label) +
" contained invalid data.");
513 if (datav.size() == 1) {
515 }
else if (datav.size() > 1) {
520 for (pugi::xml_node data = p.child(
"data_double"); data; data = data.next_sibling(
"data_double")) {
521 const char *label = data.attribute(
"label").value();
523 std::vector<double> datav;
525 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_double> with label " + std::string(label) +
" contained invalid data.");
528 if (datav.size() == 1) {
530 }
else if (datav.size() > 1) {
535 for (pugi::xml_node data = p.child(
"data_vec2"); data; data = data.next_sibling(
"data_vec2")) {
536 const char *label = data.attribute(
"label").value();
538 std::vector<vec2> datav;
540 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec2> with label " + std::string(label) +
" contained invalid data.");
543 if (datav.size() == 1) {
545 }
else if (datav.size() > 1) {
550 for (pugi::xml_node data = p.child(
"data_vec3"); data; data = data.next_sibling(
"data_vec3")) {
551 const char *label = data.attribute(
"label").value();
553 std::vector<vec3> datav;
555 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec3> with label " + std::string(label) +
" contained invalid data.");
558 if (datav.size() == 1) {
560 }
else if (datav.size() > 1) {
565 for (pugi::xml_node data = p.child(
"data_vec4"); data; data = data.next_sibling(
"data_vec4")) {
566 const char *label = data.attribute(
"label").value();
568 std::vector<vec4> datav;
570 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec4> with label " + std::string(label) +
" contained invalid data.");
573 if (datav.size() == 1) {
575 }
else if (datav.size() > 1) {
580 for (pugi::xml_node data = p.child(
"data_int2"); data; data = data.next_sibling(
"data_int2")) {
581 const char *label = data.attribute(
"label").value();
583 std::vector<int2> datav;
585 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int2> with label " + std::string(label) +
" contained invalid data.");
588 if (datav.size() == 1) {
590 }
else if (datav.size() > 1) {
595 for (pugi::xml_node data = p.child(
"data_int3"); data; data = data.next_sibling(
"data_int3")) {
596 const char *label = data.attribute(
"label").value();
598 std::vector<int3> datav;
600 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int3> with label " + std::string(label) +
" contained invalid data.");
603 if (datav.size() == 1) {
605 }
else if (datav.size() > 1) {
610 for (pugi::xml_node data = p.child(
"data_int4"); data; data = data.next_sibling(
"data_int4")) {
611 const char *label = data.attribute(
"label").value();
613 std::vector<int4> datav;
615 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int4> with label " + std::string(label) +
" contained invalid data.");
618 if (datav.size() == 1) {
620 }
else if (datav.size() > 1) {
625 for (pugi::xml_node data = p.child(
"data_string"); data; data = data.next_sibling(
"data_string")) {
626 const char *label = data.attribute(
"label").value();
628 std::vector<std::string> datav;
630 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_string> with label " + std::string(label) +
" contained invalid data.");
633 if (datav.size() == 1) {
635 }
else if (datav.size() > 1) {
641void Context::loadOData(pugi::xml_node p,
uint ID) {
644 for (pugi::xml_node data = p.child(
"data_int"); data; data = data.next_sibling(
"data_int")) {
645 const char *label = data.attribute(
"label").value();
647 std::vector<int> datav;
649 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int> with label " + std::string(label) +
" contained invalid data.");
652 if (datav.size() == 1) {
654 }
else if (datav.size() > 1) {
659 for (pugi::xml_node data = p.child(
"data_uint"); data; data = data.next_sibling(
"data_uint")) {
660 const char *label = data.attribute(
"label").value();
662 std::vector<uint> datav;
664 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_uint> with label " + std::string(label) +
" contained invalid data.");
667 if (datav.size() == 1) {
669 }
else if (datav.size() > 1) {
674 for (pugi::xml_node data = p.child(
"data_float"); data; data = data.next_sibling(
"data_float")) {
675 const char *label = data.attribute(
"label").value();
677 std::vector<float> datav;
679 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_float> with label " + std::string(label) +
" contained invalid data.");
682 if (datav.size() == 1) {
684 }
else if (datav.size() > 1) {
689 for (pugi::xml_node data = p.child(
"data_double"); data; data = data.next_sibling(
"data_double")) {
690 const char *label = data.attribute(
"label").value();
692 std::vector<double> datav;
694 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_double> with label " + std::string(label) +
" contained invalid data.");
697 if (datav.size() == 1) {
699 }
else if (datav.size() > 1) {
704 for (pugi::xml_node data = p.child(
"data_vec2"); data; data = data.next_sibling(
"data_vec2")) {
705 const char *label = data.attribute(
"label").value();
707 std::vector<vec2> datav;
709 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec2> with label " + std::string(label) +
" contained invalid data.");
712 if (datav.size() == 1) {
714 }
else if (datav.size() > 1) {
719 for (pugi::xml_node data = p.child(
"data_vec3"); data; data = data.next_sibling(
"data_vec3")) {
720 const char *label = data.attribute(
"label").value();
722 std::vector<vec3> datav;
724 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec3> with label " + std::string(label) +
" contained invalid data.");
727 if (datav.size() == 1) {
729 }
else if (datav.size() > 1) {
734 for (pugi::xml_node data = p.child(
"data_vec4"); data; data = data.next_sibling(
"data_vec4")) {
735 const char *label = data.attribute(
"label").value();
737 std::vector<vec4> datav;
739 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec4> with label " + std::string(label) +
" contained invalid data.");
742 if (datav.size() == 1) {
744 }
else if (datav.size() > 1) {
749 for (pugi::xml_node data = p.child(
"data_int2"); data; data = data.next_sibling(
"data_int2")) {
750 const char *label = data.attribute(
"label").value();
752 std::vector<int2> datav;
754 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int2> with label " + std::string(label) +
" contained invalid data.");
757 if (datav.size() == 1) {
759 }
else if (datav.size() > 1) {
764 for (pugi::xml_node data = p.child(
"data_int3"); data; data = data.next_sibling(
"data_int3")) {
765 const char *label = data.attribute(
"label").value();
767 std::vector<int3> datav;
769 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int3> with label " + std::string(label) +
" contained invalid data.");
772 if (datav.size() == 1) {
774 }
else if (datav.size() > 1) {
779 for (pugi::xml_node data = p.child(
"data_int4"); data; data = data.next_sibling(
"data_int4")) {
780 const char *label = data.attribute(
"label").value();
782 std::vector<int4> datav;
784 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int4> with label " + std::string(label) +
" contained invalid data.");
787 if (datav.size() == 1) {
789 }
else if (datav.size() > 1) {
794 for (pugi::xml_node data = p.child(
"data_string"); data; data = data.next_sibling(
"data_string")) {
795 const char *label = data.attribute(
"label").value();
797 std::vector<std::string> datav;
799 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_string> with label " + std::string(label) +
" contained invalid data.");
802 if (datav.size() == 1) {
804 }
else if (datav.size() > 1) {
810void Context::loadOsubPData(pugi::xml_node p,
uint ID) {
817 for (pugi::xml_node prim_data = p.child(
"primitive_data_int"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int")) {
818 const char *label = prim_data.attribute(
"label").value();
821 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
822 if (u >= prim_UUIDs.size()) {
823 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
824 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
828 std::vector<int> datav;
830 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int> with label " + std::string(label) +
" contained invalid data.");
834 if (datav.size() == 1) {
836 }
else if (datav.size() > 1) {
844 for (pugi::xml_node prim_data = p.child(
"primitive_data_uint"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_uint")) {
845 const char *label = prim_data.attribute(
"label").value();
848 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
849 if (u >= prim_UUIDs.size()) {
850 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
851 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
855 std::vector<uint> datav;
857 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_uint> with label " + std::string(label) +
" contained invalid data.");
861 if (datav.size() == 1) {
863 }
else if (datav.size() > 1) {
871 for (pugi::xml_node prim_data = p.child(
"primitive_data_float"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_float")) {
872 const char *label = prim_data.attribute(
"label").value();
875 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
876 if (u >= prim_UUIDs.size()) {
877 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
878 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
882 std::vector<float> datav;
884 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_float> with label " + std::string(label) +
" contained invalid data.");
888 if (datav.size() == 1) {
890 }
else if (datav.size() > 1) {
898 for (pugi::xml_node prim_data = p.child(
"primitive_data_double"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_double")) {
899 const char *label = prim_data.attribute(
"label").value();
902 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
903 if (u >= prim_UUIDs.size()) {
904 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
905 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
909 std::vector<double> datav;
911 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_double> with label " + std::string(label) +
" contained invalid data.");
915 if (datav.size() == 1) {
917 }
else if (datav.size() > 1) {
925 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec2"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec2")) {
926 const char *label = prim_data.attribute(
"label").value();
929 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
930 if (u >= prim_UUIDs.size()) {
931 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
932 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
936 std::vector<vec2> datav;
938 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec2> with label " + std::string(label) +
" contained invalid data.");
942 if (datav.size() == 1) {
944 }
else if (datav.size() > 1) {
952 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec3"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec3")) {
953 const char *label = prim_data.attribute(
"label").value();
956 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
957 if (u >= prim_UUIDs.size()) {
958 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
959 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
963 std::vector<vec3> datav;
965 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec3> with label " + std::string(label) +
" contained invalid data.");
969 if (datav.size() == 1) {
971 }
else if (datav.size() > 1) {
979 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec4"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec4")) {
980 const char *label = prim_data.attribute(
"label").value();
983 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
984 if (u >= prim_UUIDs.size()) {
985 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
986 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
990 std::vector<vec4> datav;
992 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec4> with label " + std::string(label) +
" contained invalid data.");
996 if (datav.size() == 1) {
998 }
else if (datav.size() > 1) {
1006 for (pugi::xml_node prim_data = p.child(
"primitive_data_int2"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int2")) {
1007 const char *label = prim_data.attribute(
"label").value();
1010 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1011 if (u >= prim_UUIDs.size()) {
1012 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1013 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1017 std::vector<int2> datav;
1019 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int2> with label " + std::string(label) +
" contained invalid data.");
1023 if (datav.size() == 1) {
1025 }
else if (datav.size() > 1) {
1033 for (pugi::xml_node prim_data = p.child(
"primitive_data_int3"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int3")) {
1034 const char *label = prim_data.attribute(
"label").value();
1037 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1038 if (u >= prim_UUIDs.size()) {
1039 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1040 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1044 std::vector<int3> datav;
1046 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int3> with label " + std::string(label) +
" contained invalid data.");
1050 if (datav.size() == 1) {
1052 }
else if (datav.size() > 1) {
1060 for (pugi::xml_node prim_data = p.child(
"primitive_data_int4"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int4")) {
1061 const char *label = prim_data.attribute(
"label").value();
1064 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1065 if (u >= prim_UUIDs.size()) {
1066 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1067 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1071 std::vector<int4> datav;
1073 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int4> with label " + std::string(label) +
" contained invalid data.");
1077 if (datav.size() == 1) {
1079 }
else if (datav.size() > 1) {
1087 for (pugi::xml_node prim_data = p.child(
"primitive_data_string"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_string")) {
1088 const char *label = prim_data.attribute(
"label").value();
1091 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1092 if (u >= prim_UUIDs.size()) {
1093 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1094 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1098 std::vector<std::string> datav;
1100 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_string> with label " + std::string(label) +
" contained invalid data.");
1104 if (datav.size() == 1) {
1106 }
else if (datav.size() > 1) {
1117 std::cout <<
"Loading XML file: " << filename <<
"..." << std::flush;
1120 std::string fn = filename;
1122 if (ext !=
".xml" && ext !=
".XML") {
1128 std::string resolved_filename = resolved_path.string();
1130 XMLfiles.emplace_back(resolved_filename);
1133 std::vector<uint> UUID;
1136 pugi::xml_document xmldoc;
1139 pugi::xml_parse_result load_result = xmldoc.load_file(resolved_filename.c_str());
1143 helios_runtime_error(
"failed.\n XML [" + std::string(filename) +
"] parsed with errors, attr value: [" + xmldoc.child(
"node").attribute(
"attr").value() +
"]\nError description: " + load_result.description() +
1144 "\nError offset: " + std::to_string(load_result.offset) +
" (error at [..." + (filename + load_result.offset) +
"]\n");
1147 pugi::xml_node helios = xmldoc.child(
"helios");
1149 if (helios.empty()) {
1151 std::cout <<
"failed." << std::endl;
1153 helios_runtime_error(
"ERROR (Context::loadXML): XML file must have tag '<helios> ... </helios>' bounding all other tags.");
1157 std::map<uint, std::vector<uint>> object_prim_UUIDs;
1161 for (pugi::xml_node p = helios.child(
"date"); p; p = p.next_sibling(
"date")) {
1162 pugi::xml_node year_node = p.child(
"year");
1163 const char *year_str = year_node.child_value();
1166 helios_runtime_error(
"ERROR (Context::loadXML): Year given in 'date' block must be an integer value.");
1169 pugi::xml_node month_node = p.child(
"month");
1170 const char *month_str = month_node.child_value();
1173 helios_runtime_error(
"ERROR (Context::loadXML): Month given in 'date' block must be an integer value.");
1176 pugi::xml_node day_node = p.child(
"day");
1177 const char *day_str = day_node.child_value();
1180 helios_runtime_error(
"ERROR (Context::loadXML): Day given in 'date' block must be an integer value.");
1186 for (pugi::xml_node p = helios.child(
"time"); p; p = p.next_sibling(
"time")) {
1187 pugi::xml_node hour_node = p.child(
"hour");
1188 const char *hour_str = hour_node.child_value();
1191 helios_runtime_error(
"ERROR (Context::loadXML): Hour given in 'time' block must be an integer value.");
1194 pugi::xml_node minute_node = p.child(
"minute");
1195 const char *minute_str = minute_node.child_value();
1198 helios_runtime_error(
"ERROR (Context::loadXML): Minute given in 'time' block must be an integer value.");
1201 pugi::xml_node second_node = p.child(
"second");
1202 const char *second_str = second_node.child_value();
1205 helios_runtime_error(
"ERROR (Context::loadXML): Second given in 'time' block must be an integer value.");
1208 setTime(second, minute, hour);
1212 for (pugi::xml_node p = helios.child(
"patch"); p; p = p.next_sibling(
"patch")) {
1216 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'patch' block must be a non-negative integer value.");
1220 float transform[16];
1223 helios_runtime_error(
"ERROR (Context::loadXML): Patch <transform> node contains less than 16 data values.");
1224 }
else if (result == 2) {
1225 helios_runtime_error(
"ERROR (Context::loadXML): Patch <transform> node contains invalid data.");
1229 std::string texture_file;
1233 std::vector<vec2> uv;
1235 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'patch' block contain invalid data.");
1239 float solid_fraction = -1;
1241 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'patch' block contains invalid data.");
1246 pugi::xml_node color_node = p.child(
"color");
1248 const char *color_str = color_node.child_value();
1249 if (strlen(color_str) == 0) {
1256 if (strcmp(texture_file.c_str(),
"none") == 0) {
1259 std::string texture_file_copy;
1260 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1261 texture_file_copy = texture_file;
1262 texture_file =
"lib/images/solid.jpg";
1269 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1270 getPrimitivePointer_private(ID)->setTextureFile(texture_file_copy.c_str());
1271 addTexture(texture_file_copy.c_str());
1272 getPrimitivePointer_private(ID)->setSolidFraction(solid_fraction);
1275 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1278 object_prim_UUIDs[objID].push_back(ID);
1293 for (pugi::xml_node tri = helios.child(
"triangle"); tri; tri = tri.next_sibling(
"triangle")) {
1297 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'triangle' block must be a non-negative integer value.");
1301 float transform[16];
1304 helios_runtime_error(
"ERROR (Context::loadXML): Triangle <transform> node contains less than 16 data values.");
1305 }
else if (result == 2) {
1306 helios_runtime_error(
"ERROR (Context::loadXML): Triangle <transform> node contains invalid data.");
1310 std::string texture_file;
1314 std::vector<vec2> uv;
1316 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'triangle' block contain invalid data.");
1320 float solid_fraction = -1;
1322 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'triangle' block contains invalid data.");
1327 pugi::xml_node color_node = tri.child(
"color");
1329 const char *color_str = color_node.child_value();
1330 if (strlen(color_str) == 0) {
1336 std::vector<vec3> vert_pos;
1338 vert_pos.at(0) =
make_vec3(0.f, 0.f, 0.f);
1339 vert_pos.at(1) =
make_vec3(0.f, 1.f, 0.f);
1340 vert_pos.at(2) =
make_vec3(1.f, 1.f, 0.f);
1343 if (strcmp(texture_file.c_str(),
"none") == 0 || uv.empty()) {
1344 ID =
addTriangle(vert_pos.at(0), vert_pos.at(1), vert_pos.at(2), color);
1346 std::string texture_file_copy;
1347 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1348 texture_file_copy = texture_file;
1349 texture_file =
"lib/images/solid.jpg";
1351 ID =
addTriangle(vert_pos.at(0), vert_pos.at(1), vert_pos.at(2), texture_file.c_str(), uv.at(0), uv.at(1), uv.at(2));
1352 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1353 getPrimitivePointer_private(ID)->setTextureFile(texture_file_copy.c_str());
1354 addTexture(texture_file_copy.c_str());
1355 getPrimitivePointer_private(ID)->setSolidFraction(solid_fraction);
1358 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1361 object_prim_UUIDs[objID].push_back(ID);
1374 for (pugi::xml_node p = helios.child(
"voxel"); p; p = p.next_sibling(
"voxel")) {
1378 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'voxel' block must be a non-negative integer value.");
1382 float transform[16];
1385 helios_runtime_error(
"ERROR (Context::loadXML): Voxel <transform> node contains less than 16 data values.");
1386 }
else if (result == 2) {
1387 helios_runtime_error(
"ERROR (Context::loadXML): Voxel <transform> node contains invalid data.");
1391 float solid_fraction = 1;
1393 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'voxel' block contains invalid data.");
1398 pugi::xml_node color_node = p.child(
"color");
1400 const char *color_str = color_node.child_value();
1401 if (strlen(color_str) == 0) {
1409 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1412 object_prim_UUIDs[objID].push_back(ID);
1427 for (pugi::xml_node p = helios.child(
"tile"); p; p = p.next_sibling(
"tile")) {
1431 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'tile' block must be a non-negative integer value.");
1435 float transform[16];
1438 helios_runtime_error(
"ERROR (Context::loadXML): Tile <transform> node contains less than 16 data values.");
1439 }
else if (result == 2) {
1440 helios_runtime_error(
"ERROR (Context::loadXML): Tile <transform> node contains invalid data.");
1444 std::string texture_file;
1448 std::vector<vec2> uv;
1450 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'tile' block contain invalid data.");
1455 pugi::xml_node color_node = p.child(
"color");
1457 const char *color_str = color_node.child_value();
1458 if (strlen(color_str) != 0) {
1465 if (result_subdiv == 1) {
1466 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for tile was not provided. Assuming 1x1." << std::endl;
1468 }
else if (result_subdiv == 2) {
1469 helios_runtime_error(
"ERROR (Context::loadXML): Tile <subdivisions> node contains invalid data. ");
1474 patch.setTransformationMatrix(transform);
1489 if (strcmp(texture_file.c_str(),
"none") == 0) {
1490 if (strlen(color_str) == 0) {
1504 if (objID > 0 && !object_prim_UUIDs.empty() && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1515 loadOsubPData(p, ID);
1522 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1526 for (pugi::xml_node p = helios.child(
"sphere"); p; p = p.next_sibling(
"sphere")) {
1530 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'sphere' block must be a non-negative integer value.");
1534 objID = currentObjectID;
1539 float transform[16];
1542 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <transform> node contains less than 16 data values.");
1543 }
else if (result == 2) {
1544 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <transform> node contains invalid data.");
1548 std::string texture_file;
1553 pugi::xml_node color_node = p.child(
"color");
1555 const char *color_str = color_node.child_value();
1556 if (strlen(color_str) != 0) {
1563 if (result_subdiv == 1) {
1564 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for sphere was not provided. Assuming 1x1." << std::endl;
1566 }
else if (result_subdiv == 2) {
1567 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <subdivisions> node contains invalid data. ");
1571 std::vector<uint> empty;
1572 Sphere sphere(0, empty, 3,
"",
this);
1576 if (strcmp(texture_file.c_str(),
"none") == 0) {
1577 if (strlen(color_str) == 0) {
1587 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1600 loadOsubPData(p, ID);
1607 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1611 for (pugi::xml_node p = helios.child(
"tube"); p; p = p.next_sibling(
"tube")) {
1615 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'tube' block must be a non-negative integer value.");
1619 objID = currentObjectID;
1624 float transform[16];
1627 helios_runtime_error(
"ERROR (Context::loadXML): Tube <transform> node contains less than 16 data values.");
1628 }
else if (result == 2) {
1629 helios_runtime_error(
"ERROR (Context::loadXML): Tube <transform> node contains invalid data.");
1633 std::string texture_file;
1639 if (result_subdiv == 1) {
1640 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for tube was not provided. Assuming 1x1." << std::endl;
1642 }
else if (result_subdiv == 2) {
1643 helios_runtime_error(
"ERROR (Context::loadXML): Tube <subdivisions> node contains invalid data. ");
1647 std::vector<vec3> nodes;
1648 pugi::xml_node nodes_node = p.child(
"nodes");
1654 std::vector<float> radii;
1655 pugi::xml_node radii_node = p.child(
"radius");
1662 pugi::xml_node color_node = p.child(
"color");
1663 const char *color_str = color_node.child_value();
1665 std::vector<RGBcolor> colors;
1666 if (strlen(color_str) > 0) {
1667 std::istringstream data_stream(color_str);
1668 std::vector<float> tmp;
1671 while (data_stream >> tmp.at(c)) {
1674 colors.push_back(
make_RGBcolor(tmp.at(0), tmp.at(1), tmp.at(2)));
1681 if (texture_file ==
"none") {
1684 ID =
addTubeObject(subdiv, nodes, radii, texture_file.c_str());
1690 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1703 loadOsubPData(p, ID);
1710 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1714 for (pugi::xml_node p = helios.child(
"box"); p; p = p.next_sibling(
"box")) {
1718 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'box' block must be a non-negative integer value.");
1722 objID = currentObjectID;
1727 float transform[16];
1730 helios_runtime_error(
"ERROR (Context::loadXML): Box <transform> node contains less than 16 data values.");
1731 }
else if (result == 2) {
1736 std::string texture_file;
1741 pugi::xml_node color_node = p.child(
"color");
1743 const char *color_str = color_node.child_value();
1744 if (strlen(color_str) != 0) {
1751 if (result_subdiv == 1) {
1752 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for box was not provided. Assuming 1x1." << std::endl;
1754 }
else if (result_subdiv == 2) {
1755 helios_runtime_error(
"ERROR (Context::loadXML): Box <subdivisions> node contains invalid data. ");
1759 std::vector<uint> empty;
1764 if (strcmp(texture_file.c_str(),
"none") == 0) {
1765 if (strlen(color_str) == 0) {
1775 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1788 loadOsubPData(p, ID);
1795 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1799 for (pugi::xml_node p = helios.child(
"disk"); p; p = p.next_sibling(
"disk")) {
1803 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'disk' block must be a non-negative integer value.");
1807 objID = currentObjectID;
1812 float transform[16];
1815 helios_runtime_error(
"ERROR (Context::loadXML): Disk <transform> node contains less than 16 data values.");
1816 }
else if (result == 2) {
1817 helios_runtime_error(
"ERROR (Context::loadXML): Disk <transform> node contains invalid data.");
1821 std::string texture_file;
1826 pugi::xml_node color_node = p.child(
"color");
1828 const char *color_str = color_node.child_value();
1829 if (strlen(color_str) != 0) {
1836 if (result_subdiv == 1) {
1837 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for disk was not provided. Assuming 1x1." << std::endl;
1839 }
else if (result_subdiv == 2) {
1840 helios_runtime_error(
"ERROR (Context::loadXML): Disk <subdivisions> node contains invalid data. ");
1844 std::vector<uint> empty;
1849 if (strcmp(texture_file.c_str(),
"none") == 0) {
1850 if (strlen(color_str) == 0) {
1860 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1873 loadOsubPData(p, ID);
1880 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1884 for (pugi::xml_node p = helios.child(
"cone"); p; p = p.next_sibling(
"cone")) {
1888 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'cone' block must be a non-negative integer value.");
1892 objID = currentObjectID;
1897 float transform[16];
1900 helios_runtime_error(
"ERROR (Context::loadXML): Cone <transform> node contains less than 16 data values.");
1901 }
else if (result == 2) {
1902 helios_runtime_error(
"ERROR (Context::loadXML): Cone <transform> node contains invalid data.");
1906 std::string texture_file;
1911 pugi::xml_node color_node = p.child(
"color");
1913 const char *color_str = color_node.child_value();
1914 if (strlen(color_str) != 0) {
1921 if (result_subdiv == 1) {
1922 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for cone was not provided. Assuming 1x1." << std::endl;
1924 }
else if (result_subdiv == 2) {
1925 helios_runtime_error(
"ERROR (Context::loadXML): Cone <subdivisions> node contains invalid data. ");
1929 std::vector<vec3> nodes;
1930 pugi::xml_node nodes_node = p.child(
"nodes");
1936 std::vector<float> radii;
1937 pugi::xml_node radii_node = p.child(
"radius");
1943 if (texture_file ==
"none") {
1946 ID =
addConeObject(subdiv, nodes.at(0), nodes.at(1), radii.at(0), radii.at(1), texture_file.c_str());
1952 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1965 loadOsubPData(p, ID);
1972 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1976 for (pugi::xml_node p = helios.child(
"polymesh"); p; p = p.next_sibling(
"polymesh")) {
1980 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'polymesh' block must be a non-negative integer value.");
1984 objID = currentObjectID;
1994 loadOsubPData(p, ID);
2000 std::vector<uint> childUUIDs = object_prim_UUIDs.at(objID);
2001 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2006 for (pugi::xml_node data = helios.child(
"globaldata_int"); data; data = data.next_sibling(
"globaldata_int")) {
2007 const char *label = data.attribute(
"label").value();
2009 std::vector<int> datav;
2011 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int> with label " + std::string(label) +
" contained invalid data.");
2014 if (datav.size() == 1) {
2016 }
else if (datav.size() > 1) {
2021 for (pugi::xml_node data = helios.child(
"globaldata_uint"); data; data = data.next_sibling(
"globaldata_uint")) {
2022 const char *label = data.attribute(
"label").value();
2024 std::vector<uint> datav;
2026 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_uint> with label " + std::string(label) +
" contained invalid data.");
2029 if (datav.size() == 1) {
2031 }
else if (datav.size() > 1) {
2036 for (pugi::xml_node data = helios.child(
"globaldata_float"); data; data = data.next_sibling(
"globaldata_float")) {
2037 const char *label = data.attribute(
"label").value();
2039 std::vector<float> datav;
2041 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_float> with label " + std::string(label) +
" contained invalid data.");
2044 if (datav.size() == 1) {
2046 }
else if (datav.size() > 1) {
2051 for (pugi::xml_node data = helios.child(
"globaldata_double"); data; data = data.next_sibling(
"globaldata_double")) {
2052 const char *label = data.attribute(
"label").value();
2054 std::vector<double> datav;
2056 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_double> with label " + std::string(label) +
" contained invalid data.");
2059 if (datav.size() == 1) {
2061 }
else if (datav.size() > 1) {
2066 for (pugi::xml_node data = helios.child(
"globaldata_vec2"); data; data = data.next_sibling(
"globaldata_vec2")) {
2067 const char *label = data.attribute(
"label").value();
2069 std::vector<vec2> datav;
2071 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec2> with label " + std::string(label) +
" contained invalid data.");
2074 if (datav.size() == 1) {
2076 }
else if (datav.size() > 1) {
2081 for (pugi::xml_node data = helios.child(
"globaldata_vec3"); data; data = data.next_sibling(
"globaldata_vec3")) {
2082 const char *label = data.attribute(
"label").value();
2084 std::vector<vec3> datav;
2086 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec3> with label " + std::string(label) +
" contained invalid data.");
2089 if (datav.size() == 1) {
2091 }
else if (datav.size() > 1) {
2096 for (pugi::xml_node data = helios.child(
"globaldata_vec4"); data; data = data.next_sibling(
"globaldata_vec4")) {
2097 const char *label = data.attribute(
"label").value();
2099 std::vector<vec4> datav;
2101 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec4> with label " + std::string(label) +
" contained invalid data.");
2104 if (datav.size() == 1) {
2106 }
else if (datav.size() > 1) {
2111 for (pugi::xml_node data = helios.child(
"globaldata_int2"); data; data = data.next_sibling(
"globaldata_int2")) {
2112 const char *label = data.attribute(
"label").value();
2114 std::vector<int2> datav;
2116 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int2> with label " + std::string(label) +
" contained invalid data.");
2119 if (datav.size() == 1) {
2121 }
else if (datav.size() > 1) {
2126 for (pugi::xml_node data = helios.child(
"globaldata_int3"); data; data = data.next_sibling(
"globaldata_int3")) {
2127 const char *label = data.attribute(
"label").value();
2129 std::vector<int3> datav;
2131 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int3> with label " + std::string(label) +
" contained invalid data.");
2134 if (datav.size() == 1) {
2136 }
else if (datav.size() > 1) {
2141 for (pugi::xml_node data = helios.child(
"globaldata_int4"); data; data = data.next_sibling(
"globaldata_int4")) {
2142 const char *label = data.attribute(
"label").value();
2144 std::vector<int4> datav;
2146 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int4> with label " + std::string(label) +
" contained invalid data.");
2149 if (datav.size() == 1) {
2151 }
else if (datav.size() > 1) {
2156 for (pugi::xml_node data = helios.child(
"globaldata_string"); data; data = data.next_sibling(
"globaldata_string")) {
2157 const char *label = data.attribute(
"label").value();
2159 std::vector<std::string> datav;
2161 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_string> with label " + std::string(label) +
" contained invalid data.");
2164 if (datav.size() == 1) {
2166 }
else if (datav.size() > 1) {
2172 for (pugi::xml_node p = helios.child(
"timeseries"); p; p = p.next_sibling(
"timeseries")) {
2173 const char *label = p.attribute(
"label").value();
2175 for (pugi::xml_node d = p.child(
"datapoint"); d; d = d.next_sibling(
"datapoint")) {
2177 pugi::xml_node time_node = d.child(
"time");
2178 const char *time_str = time_node.child_value();
2179 if (strlen(time_str) > 0) {
2181 if (time_.
x < 0 || time_.
x > 23) {
2182 helios_runtime_error(
"ERROR (Context::loadXML): Invalid hour of " + std::to_string(time_.
x) +
" given in timeseries. Hour must be positive and not greater than 23.");
2183 }
else if (time_.
y < 0 || time_.
y > 59) {
2184 helios_runtime_error(
"ERROR (Context::loadXML): Invalid minute of " + std::to_string(time_.
y) +
" given in timeseries. Minute must be positive and not greater than 59.");
2185 }
else if (time_.
z < 0 || time_.
z > 59) {
2186 helios_runtime_error(
"ERROR (Context::loadXML): Invalid second of " + std::to_string(time_.
z) +
" given in timeseries. Second must be positive and not greater than 59.");
2190 helios_runtime_error(
"ERROR (Context::loadXML): No time was specified for timeseries datapoint.");
2194 bool date_flag =
false;
2196 pugi::xml_node date_node = d.child(
"date");
2197 const char *date_str = date_node.child_value();
2198 if (strlen(date_str) > 0) {
2200 if (date_.
x < 1 || date_.
x > 31) {
2201 helios_runtime_error(
"ERROR (Context::loadXML): Invalid day of month " + std::to_string(date_.
x) +
" given in timeseries. Day must be greater than zero and not greater than 31.");
2202 }
else if (date_.
y < 1 || date_.
y > 12) {
2203 helios_runtime_error(
"ERROR (Context::loadXML): Invalid month of " + std::to_string(date_.
y) +
" given in timeseries. Month must be greater than zero and not greater than 12.");
2204 }
else if (date_.
z < 1000 || date_.
z > 10000) {
2205 helios_runtime_error(
"ERROR (Context::loadXML): Invalid year of " + std::to_string(date_.
z) +
" given in timeseries. Year should be in YYYY format.");
2211 pugi::xml_node Jdate_node = d.child(
"dateJulian");
2212 const char *Jdate_str = Jdate_node.child_value();
2213 if (strlen(Jdate_str) > 0) {
2215 if (date_.
x < 1 || date_.
x > 366) {
2216 helios_runtime_error(
"ERROR (Context::loadXML): Invalid Julian day of year " + std::to_string(date_.
x) +
" given in timeseries. Julian day must be greater than zero and not greater than 366.");
2217 }
else if (date_.
y < 1000 || date_.
y > 10000) {
2218 helios_runtime_error(
"ERROR (Context::loadXML): Invalid year of " + std::to_string(date_.
y) +
" given in timeseries. Year should be in YYYY format.");
2225 helios_runtime_error(
"ERROR (Context::loadXML): No date was specified for timeseries datapoint.");
2229 pugi::xml_node value_node = d.child(
"value");
2230 const char *value_str = value_node.child_value();
2231 if (strlen(value_str) > 0) {
2233 helios_runtime_error(
"ERROR (Context::loadXML): Datapoint value in 'timeseries' block must be a float value.");
2236 helios_runtime_error(
"ERROR (Context::loadXML): No value was specified for timeseries datapoint.");
2244 std::cout <<
"done." << std::endl;
2255 const std::string &fn = filename;
2257 if (ext !=
".xml" && ext !=
".XML") {
2262 pugi::xml_document xmldoc;
2265 pugi::xml_parse_result load_result = xmldoc.load_file(filename.c_str());
2269 helios_runtime_error(
"failed.\n XML [" + filename +
"] parsed with errors, attr value: [" + xmldoc.child(
"node").attribute(
"attr").value() +
"]\nError description: " + load_result.description() +
2270 "\nError offset: " + std::to_string(load_result.offset) +
" (error at [..." + (filename.c_str() + load_result.offset) +
"]\n");
2273 pugi::xml_node helios = xmldoc.child(
"helios");
2275 if (helios.empty()) {
2279 for (pugi::xml_node p = helios.child(tag.c_str()); p; p = p.next_sibling(tag.c_str())) {
2280 const char *labelquery = p.attribute(
"label").value();
2282 if (labelquery == label || label.empty()) {
2290void Context::writeDataToXMLstream(
const char *data_group,
const std::vector<std::string> &data_labels,
void *ptr, std::ofstream &outfile)
const {
2291 for (
const auto &label: data_labels) {
2294 if (strcmp(data_group,
"primitive") == 0) {
2295 dtype = ((Primitive *) ptr)->getPrimitiveDataType(label.c_str());
2296 }
else if (strcmp(data_group,
"object") == 0) {
2297 dtype = ((
CompoundObject *) ptr)->getObjectDataType(label.c_str());
2298 }
else if (strcmp(data_group,
"global") == 0) {
2301 helios_runtime_error(
"ERROR (Context::writeDataToXMLstream): unknown data group argument of " + std::string(data_group) +
". Must be one of primitive, object, or global.");
2304 if (dtype == HELIOS_TYPE_UINT) {
2305 outfile <<
"\t<data_uint label=\"" << label <<
"\">" << std::flush;
2306 std::vector<uint> data;
2307 if (strcmp(data_group,
"primitive") == 0) {
2308 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2309 }
else if (strcmp(data_group,
"object") == 0) {
2314 for (
int j = 0; j < data.size(); j++) {
2315 outfile << data.at(j) << std::flush;
2316 if (j != data.size() - 1) {
2317 outfile <<
" " << std::flush;
2320 outfile <<
"</data_uint>" << std::endl;
2321 }
else if (dtype == HELIOS_TYPE_INT) {
2322 outfile <<
"\t<data_int label=\"" << label <<
"\">" << std::flush;
2323 std::vector<int> data;
2324 if (strcmp(data_group,
"primitive") == 0) {
2325 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2326 }
else if (strcmp(data_group,
"object") == 0) {
2331 for (
int j = 0; j < data.size(); j++) {
2332 outfile << data.at(j) << std::flush;
2333 if (j != data.size() - 1) {
2334 outfile <<
" " << std::flush;
2337 outfile <<
"</data_int>" << std::endl;
2338 }
else if (dtype == HELIOS_TYPE_FLOAT) {
2339 outfile <<
"\t<data_float label=\"" << label <<
"\">" << std::flush;
2340 std::vector<float> data;
2341 if (strcmp(data_group,
"primitive") == 0) {
2342 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2343 }
else if (strcmp(data_group,
"object") == 0) {
2348 for (
int j = 0; j < data.size(); j++) {
2349 outfile << data.at(j) << std::flush;
2350 if (j != data.size() - 1) {
2351 outfile <<
" " << std::flush;
2354 outfile <<
"</data_float>" << std::endl;
2355 }
else if (dtype == HELIOS_TYPE_DOUBLE) {
2356 outfile <<
"\t<data_double label=\"" << label <<
"\">" << std::flush;
2357 std::vector<double> data;
2358 if (strcmp(data_group,
"primitive") == 0) {
2359 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2360 }
else if (strcmp(data_group,
"object") == 0) {
2365 for (
int j = 0; j < data.size(); j++) {
2366 outfile << data.at(j) << std::flush;
2367 if (j != data.size() - 1) {
2368 outfile <<
" " << std::flush;
2371 outfile <<
"</data_double>" << std::endl;
2372 }
else if (dtype == HELIOS_TYPE_VEC2) {
2373 outfile <<
"\t<data_vec2 label=\"" << label <<
"\">" << std::flush;
2374 std::vector<vec2> data;
2375 if (strcmp(data_group,
"primitive") == 0) {
2376 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2377 }
else if (strcmp(data_group,
"object") == 0) {
2382 for (
int j = 0; j < data.size(); j++) {
2383 outfile << data.at(j).x <<
" " << data.at(j).y << std::flush;
2384 if (j != data.size() - 1) {
2385 outfile <<
" " << std::flush;
2388 outfile <<
"</data_vec2>" << std::endl;
2389 }
else if (dtype == HELIOS_TYPE_VEC3) {
2390 outfile <<
"\t<data_vec3 label=\"" << label <<
"\">" << std::flush;
2391 std::vector<vec3> data;
2392 if (strcmp(data_group,
"primitive") == 0) {
2393 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2394 }
else if (strcmp(data_group,
"object") == 0) {
2399 for (
int j = 0; j < data.size(); j++) {
2400 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z << std::flush;
2401 if (j != data.size() - 1) {
2402 outfile <<
" " << std::flush;
2405 outfile <<
"</data_vec3>" << std::endl;
2406 }
else if (dtype == HELIOS_TYPE_VEC4) {
2407 outfile <<
"\t<data_vec4 label=\"" << label <<
"\">" << std::flush;
2408 std::vector<vec4> data;
2409 if (strcmp(data_group,
"primitive") == 0) {
2410 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2411 }
else if (strcmp(data_group,
"object") == 0) {
2416 for (
int j = 0; j < data.size(); j++) {
2417 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z <<
" " << data.at(j).w << std::flush;
2418 if (j != data.size() - 1) {
2419 outfile <<
" " << std::flush;
2422 outfile <<
"</data_vec4>" << std::endl;
2423 }
else if (dtype == HELIOS_TYPE_INT2) {
2424 outfile <<
"\t<data_int2 label=\"" << label <<
"\">" << std::flush;
2425 std::vector<int2> data;
2426 if (strcmp(data_group,
"primitive") == 0) {
2427 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2428 }
else if (strcmp(data_group,
"object") == 0) {
2433 for (
int j = 0; j < data.size(); j++) {
2434 outfile << data.at(j).x <<
" " << data.at(j).y << std::flush;
2435 if (j != data.size() - 1) {
2436 outfile <<
" " << std::flush;
2439 outfile <<
"</data_int2>" << std::endl;
2440 }
else if (dtype == HELIOS_TYPE_INT3) {
2441 outfile <<
"\t<data_int3 label=\"" << label <<
"\">" << std::flush;
2442 std::vector<int3> data;
2443 if (strcmp(data_group,
"primitive") == 0) {
2444 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2445 }
else if (strcmp(data_group,
"object") == 0) {
2450 for (
int j = 0; j < data.size(); j++) {
2451 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z << std::flush;
2452 if (j != data.size() - 1) {
2453 outfile <<
" " << std::flush;
2456 outfile <<
"</data_int3>" << std::endl;
2457 }
else if (dtype == HELIOS_TYPE_INT4) {
2458 outfile <<
"\t<data_int3 label=\"" << label <<
"\">" << std::flush;
2459 std::vector<int4> data;
2460 if (strcmp(data_group,
"primitive") == 0) {
2461 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2462 }
else if (strcmp(data_group,
"object") == 0) {
2467 for (
int j = 0; j < data.size(); j++) {
2468 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z <<
" " << data.at(j).w << std::flush;
2469 if (j != data.size() - 1) {
2470 outfile <<
" " << std::flush;
2473 outfile <<
"</data_int4>" << std::endl;
2474 }
else if (dtype == HELIOS_TYPE_STRING) {
2475 outfile <<
"\t<data_string label=\"" << label <<
"\">" << std::flush;
2476 std::vector<std::string> data;
2477 if (strcmp(data_group,
"primitive") == 0) {
2478 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2479 }
else if (strcmp(data_group,
"object") == 0) {
2484 for (
int j = 0; j < data.size(); j++) {
2485 outfile << data.at(j) << std::flush;
2486 if (j != data.size() - 1) {
2487 outfile <<
" " << std::flush;
2490 outfile <<
"</data_string>" << std::endl;
2500 for (
uint objID: objIDs) {
2502 helios_runtime_error(
"ERROR (Context::writeXML_byobject): Object with ID of " + std::to_string(objID) +
" does not exist.");
2510 std::cout <<
"Writing XML file " << filename <<
"..." << std::flush;
2513 std::string xmlfilename = filename;
2520 helios_runtime_error(
"ERROR (Context::writeXML): Invalid output file " + xmlfilename +
". No file name was provided.");
2524 if (file_extension !=
".xml" && file_extension !=
".XML") {
2525 xmlfilename.append(
".xml");
2530 std::ofstream outfile;
2531 outfile.open(xmlfilename);
2533 outfile <<
"<?xml version=\"1.0\"?>\n\n";
2535 outfile <<
"<helios>\n\n";
2541 outfile <<
" <date>" << std::endl;
2543 outfile <<
"\t<day>" << date.
day <<
"</day>" << std::endl;
2544 outfile <<
"\t<month>" << date.
month <<
"</month>" << std::endl;
2545 outfile <<
"\t<year>" << date.
year <<
"</year>" << std::endl;
2547 outfile <<
" </date>" << std::endl;
2551 outfile <<
" <time>" << std::endl;
2553 outfile <<
"\t<hour>" << time.
hour <<
"</hour>" << std::endl;
2554 outfile <<
"\t<minute>" << time.
minute <<
"</minute>" << std::endl;
2555 outfile <<
"\t<second>" << time.
second <<
"</second>" << std::endl;
2557 outfile <<
" </time>" << std::endl;
2561 for (
uint UUID: UUIDs) {
2566 helios_runtime_error(
"ERROR (Context::writeXML): Primitive with UUID of " + std::to_string(p) +
" does not exist. There is a compound object with this ID - did you mean to call Context::writeXML_byobject()?");
2568 helios_runtime_error(
"ERROR (Context::writeXML): Primitive with UUID of " + std::to_string(p) +
" does not exist.");
2572 Primitive *prim = getPrimitivePointer_private(p);
2574 uint parent_objID = prim->getParentObjectID();
2578 std::string texture_file = prim->getTextureFile();
2580 std::vector<std::string> pdata = prim->listPrimitiveData();
2589 outfile <<
" <patch>" << std::endl;
2591 outfile <<
" <triangle>" << std::endl;
2593 outfile <<
" <voxel>" << std::endl;
2596 outfile <<
"\t<UUID>" << p <<
"</UUID>" << std::endl;
2598 if (parent_objID > 0) {
2599 outfile <<
"\t<objID>" << parent_objID <<
"</objID>" << std::endl;
2602 outfile <<
"\t<color>" << color.
r <<
" " << color.
g <<
" " << color.
b <<
" " << color.
a <<
"</color>" << std::endl;
2603 if (prim->hasTexture()) {
2604 outfile <<
"\t<texture>" << texture_file <<
"</texture>" << std::endl;
2607 if (!pdata.empty()) {
2608 writeDataToXMLstream(
"primitive", pdata, prim, outfile);
2613 Patch *patch = getPatchPointer_private(p);
2614 float transform[16];
2615 prim->getTransformationMatrix(transform);
2617 outfile <<
"\t<transform>";
2618 for (
float i: transform) {
2619 outfile << i <<
" ";
2621 outfile <<
"</transform>" << std::endl;
2622 std::vector<vec2> uv = patch->getTextureUV();
2624 outfile <<
"\t<textureUV>" << std::flush;
2625 for (
int i = 0; i < uv.size(); i++) {
2626 outfile << uv.at(i).x <<
" " << uv.at(i).y << std::flush;
2627 if (i != uv.size() - 1) {
2628 outfile <<
" " << std::flush;
2631 outfile <<
"</textureUV>" << std::endl;
2636 outfile <<
" </patch>" << std::endl;
2640 float transform[16];
2641 prim->getTransformationMatrix(transform);
2643 outfile <<
"\t<transform>";
2644 for (
float i: transform) {
2645 outfile << i <<
" ";
2647 outfile <<
"</transform>" << std::endl;
2649 std::vector<vec2> uv = getTrianglePointer_private(p)->getTextureUV();
2651 outfile <<
"\t<textureUV>" << std::flush;
2652 for (
int i = 0; i < uv.size(); i++) {
2653 outfile << uv.at(i).x <<
" " << uv.at(i).y << std::flush;
2654 if (i != uv.size() - 1) {
2655 outfile <<
" " << std::flush;
2658 outfile <<
"</textureUV>" << std::endl;
2663 outfile <<
" </triangle>" << std::endl;
2667 float transform[16];
2668 prim->getTransformationMatrix(transform);
2670 outfile <<
"\t<transform>";
2671 for (
float i: transform) {
2672 outfile << i <<
" ";
2674 outfile <<
"</transform>" << std::endl;
2679 outfile <<
" </voxel>" << std::endl;
2685 for (
auto o: objectIDs) {
2688 std::string texture_file =
obj->getTextureFile();
2690 std::vector<std::string> odata =
obj->listObjectData();
2693 outfile <<
" <tile>" << std::endl;
2695 outfile <<
" <box>" << std::endl;
2697 outfile <<
" <cone>" << std::endl;
2699 outfile <<
" <disk>" << std::endl;
2701 outfile <<
" <sphere>" << std::endl;
2703 outfile <<
" <tube>" << std::endl;
2705 outfile <<
" <polymesh>" << std::endl;
2708 outfile <<
"\t<objID>" << o <<
"</objID>" << std::endl;
2709 if (
obj->hasTexture()) {
2710 outfile <<
"\t<texture>" << texture_file <<
"</texture>" << std::endl;
2713 if (!odata.empty()) {
2714 writeDataToXMLstream(
"object", odata,
obj, outfile);
2717 std::vector<std::string> pdata_labels;
2718 std::vector<HeliosDataType> pdata_types;
2719 std::vector<uint> primitiveUUIDs =
obj->getPrimitiveUUIDs();
2720 for (
uint UUID: primitiveUUIDs) {
2721 std::vector<std::string> labels = getPrimitivePointer_private(UUID)->listPrimitiveData();
2722 for (
const auto &label: labels) {
2723 if (find(pdata_labels.begin(), pdata_labels.end(), label) == pdata_labels.end()) {
2724 pdata_labels.push_back(label);
2729 for (
size_t l = 0; l < pdata_labels.size(); l++) {
2731 outfile <<
"\t<primitive_data_float " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2732 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2734 std::vector<float> data;
2736 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2737 for (
float i: data) {
2738 outfile << i << std::flush;
2740 outfile <<
" </data>" << std::endl;
2743 outfile <<
"\t</primitive_data_float>" << std::endl;
2745 outfile <<
"\t<primitive_data_double " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2746 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2748 std::vector<double> data;
2750 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2751 for (
double i: data) {
2752 outfile << i << std::flush;
2754 outfile <<
" </data>" << std::endl;
2757 outfile <<
"\t</primitive_data_double>" << std::endl;
2759 outfile <<
"\t<primitive_data_uint " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2760 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2762 std::vector<uint> data;
2764 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2765 for (
unsigned int i: data) {
2766 outfile << i << std::flush;
2768 outfile <<
" </data>" << std::endl;
2771 outfile <<
"\t</primitive_data_uint>" << std::endl;
2773 outfile <<
"\t<primitive_data_int " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2774 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2776 std::vector<int> data;
2778 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2780 outfile << i << std::flush;
2782 outfile <<
" </data>" << std::endl;
2785 outfile <<
"\t</primitive_data_int>" << std::endl;
2787 outfile <<
"\t<primitive_data_int2 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2788 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2790 std::vector<int2> data;
2792 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2793 for (
auto &i: data) {
2794 outfile << i.x <<
" " << i.y << std::flush;
2796 outfile <<
" </data>" << std::endl;
2799 outfile <<
"\t</primitive_data_int2>" << std::endl;
2801 outfile <<
"\t<primitive_data_int3 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2802 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2804 std::vector<int3> data;
2806 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2807 for (
auto &i: data) {
2808 outfile << i.x <<
" " << i.y <<
" " << i.z << std::flush;
2810 outfile <<
" </data>" << std::endl;
2813 outfile <<
"\t</primitive_data_int3>" << std::endl;
2815 outfile <<
"\t<primitive_data_int4 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2816 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2818 std::vector<int4> data;
2820 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2821 for (
auto &i: data) {
2822 outfile << i.x <<
" " << i.y <<
" " << i.z <<
" " << i.w << std::flush;
2824 outfile <<
" </data>" << std::endl;
2827 outfile <<
"\t</primitive_data_int4>" << std::endl;
2829 outfile <<
"\t<primitive_data_vec2 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2830 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2832 std::vector<vec2> data;
2834 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2835 for (
auto &i: data) {
2836 outfile << i.x <<
" " << i.y << std::flush;
2838 outfile <<
" </data>" << std::endl;
2841 outfile <<
"\t</primitive_data_vec2>" << std::endl;
2843 outfile <<
"\t<primitive_data_vec3 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2844 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2846 std::vector<vec3> data;
2848 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2849 for (
auto &i: data) {
2850 outfile << i.x <<
" " << i.y <<
" " << i.z << std::flush;
2852 outfile <<
" </data>" << std::endl;
2855 outfile <<
"\t</primitive_data_vec3>" << std::endl;
2857 outfile <<
"\t<primitive_data_vec4 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2858 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2860 std::vector<vec4> data;
2862 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2863 for (
auto &i: data) {
2864 outfile << i.x <<
" " << i.y <<
" " << i.z <<
" " << i.w << std::flush;
2866 outfile <<
" </data>" << std::endl;
2869 outfile <<
"\t</primitive_data_vec4>" << std::endl;
2871 outfile <<
"\t<primitive_data_string " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
2872 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
2874 std::vector<std::string> data;
2876 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
2877 for (
const auto &i: data) {
2878 outfile << i << std::flush;
2880 outfile <<
" </data>" << std::endl;
2883 outfile <<
"\t</primitive_data_string>" << std::endl;
2891 float transform[16];
2895 outfile <<
"\t<subdivisions>" << subdiv.
x <<
" " << subdiv.
y <<
"</subdivisions>" << std::endl;
2897 outfile <<
"\t<transform> ";
2898 for (
float i: transform) {
2899 outfile << i <<
" ";
2901 outfile <<
"</transform>" << std::endl;
2903 outfile <<
" </tile>" << std::endl;
2909 float transform[16];
2912 outfile <<
"\t<transform> ";
2913 for (
float i: transform) {
2914 outfile << i <<
" ";
2916 outfile <<
"</transform>" << std::endl;
2919 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
2921 outfile <<
" </sphere>" << std::endl;
2927 float transform[16];
2930 outfile <<
"\t<transform> ";
2931 for (
float i: transform) {
2932 outfile << i <<
" ";
2934 outfile <<
"</transform>" << std::endl;
2937 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
2939 std::vector<vec3> nodes = tube->
getNodes();
2942 assert(nodes.size() == radius.size());
2943 outfile <<
"\t<nodes> " << std::endl;
2944 for (
auto &node: nodes) {
2945 outfile <<
"\t\t" << node.x <<
" " << node.y <<
" " << node.z << std::endl;
2947 outfile <<
"\t</nodes> " << std::endl;
2948 outfile <<
"\t<radius> " << std::endl;
2949 for (
float radiu: radius) {
2950 outfile <<
"\t\t" << radiu << std::endl;
2952 outfile <<
"\t</radius> " << std::endl;
2954 if (texture_file.empty()) {
2957 outfile <<
"\t<color> " << std::endl;
2958 for (
auto &color: colors) {
2959 outfile <<
"\t\t" << color.r <<
" " << color.g <<
" " << color.b << std::endl;
2961 outfile <<
"\t</color> " << std::endl;
2964 outfile <<
" </tube>" << std::endl;
2970 float transform[16];
2973 outfile <<
"\t<transform> ";
2974 for (
float i: transform) {
2975 outfile << i <<
" ";
2977 outfile <<
"</transform>" << std::endl;
2980 outfile <<
"\t<subdivisions> " << subdiv.
x <<
" " << subdiv.
y <<
" " << subdiv.
z <<
" </subdivisions>" << std::endl;
2982 outfile <<
" </box>" << std::endl;
2988 float transform[16];
2991 outfile <<
"\t<transform> ";
2992 for (
float i: transform) {
2993 outfile << i <<
" ";
2995 outfile <<
"</transform>" << std::endl;
2998 outfile <<
"\t<subdivisions> " << subdiv.
x <<
" " << subdiv.
y <<
" </subdivisions>" << std::endl;
3000 outfile <<
" </disk>" << std::endl;
3006 float transform[16];
3009 outfile <<
"\t<transform> ";
3010 for (
float i: transform) {
3011 outfile << i <<
" ";
3013 outfile <<
"</transform>" << std::endl;
3016 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
3021 assert(nodes.size() == radius.size());
3022 outfile <<
"\t<nodes> " << std::endl;
3023 for (
auto &node: nodes) {
3024 outfile <<
"\t\t" << node.x <<
" " << node.y <<
" " << node.z << std::endl;
3026 outfile <<
"\t</nodes> " << std::endl;
3027 outfile <<
"\t<radius> " << std::endl;
3028 for (
float radiu: radius) {
3029 outfile <<
"\t\t" << radiu << std::endl;
3031 outfile <<
"\t</radius> " << std::endl;
3033 outfile <<
" </cone>" << std::endl;
3037 outfile <<
" </polymesh>" << std::endl;
3044 for (
const auto &iter: globaldata) {
3045 std::string label = iter.first;
3049 outfile <<
" <globaldata_uint label=\"" << label <<
"\">" << std::flush;
3050 for (
size_t i = 0; i < data.
size; i++) {
3052 if (i != data.
size - 1) {
3053 outfile <<
" " << std::flush;
3056 outfile <<
"</globaldata_uint>" << std::endl;
3058 outfile <<
" <globaldata_int label=\"" << label <<
"\">" << std::flush;
3059 for (
size_t i = 0; i < data.
size; i++) {
3061 if (i != data.
size - 1) {
3062 outfile <<
" " << std::flush;
3065 outfile <<
"</globaldata_int>" << std::endl;
3067 outfile <<
" <globaldata_float label=\"" << label <<
"\">" << std::flush;
3068 for (
size_t i = 0; i < data.
size; i++) {
3070 if (i != data.
size - 1) {
3071 outfile <<
" " << std::flush;
3074 outfile <<
"</globaldata_float>" << std::endl;
3076 outfile <<
" <globaldata_double label=\"" << label <<
"\">" << std::flush;
3077 for (
size_t i = 0; i < data.
size; i++) {
3079 if (i != data.
size - 1) {
3080 outfile <<
" " << std::flush;
3083 outfile <<
"</globaldata_double>" << std::endl;
3085 outfile <<
" <globaldata_vec2 label=\"" << label <<
"\">" << std::flush;
3086 for (
size_t i = 0; i < data.
size; i++) {
3088 if (i != data.
size - 1) {
3089 outfile <<
" " << std::flush;
3092 outfile <<
"</globaldata_vec2>" << std::endl;
3094 outfile <<
" <globaldata_vec3 label=\"" << label <<
"\">" << std::flush;
3095 for (
size_t i = 0; i < data.
size; i++) {
3097 if (i != data.
size - 1) {
3098 outfile <<
" " << std::flush;
3101 outfile <<
"</globaldata_vec3>" << std::endl;
3103 outfile <<
" <globaldata_vec4 label=\"" << label <<
"\">" << std::flush;
3104 for (
size_t i = 0; i < data.
size; i++) {
3106 if (i != data.
size - 1) {
3107 outfile <<
" " << std::flush;
3110 outfile <<
"</globaldata_vec4>" << std::endl;
3112 outfile <<
" <globaldata_int2 label=\"" << label <<
"\">" << std::flush;
3113 for (
size_t i = 0; i < data.
size; i++) {
3115 if (i != data.
size - 1) {
3116 outfile <<
" " << std::flush;
3119 outfile <<
"</globaldata_int2>" << std::endl;
3121 outfile <<
" <globaldata_int3 label=\"" << label <<
"\">" << std::flush;
3122 for (
size_t i = 0; i < data.
size; i++) {
3124 if (i != data.
size - 1) {
3125 outfile <<
" " << std::flush;
3128 outfile <<
"</globaldata_int3>" << std::endl;
3130 outfile <<
" <globaldata_int4 label=\"" << label <<
"\">" << std::flush;
3131 for (
size_t i = 0; i < data.
size; i++) {
3133 if (i != data.
size - 1) {
3134 outfile <<
" " << std::flush;
3137 outfile <<
"</globaldata_int4>" << std::endl;
3139 outfile <<
" <globaldata_string label=\"" << label <<
"\">" << std::flush;
3140 for (
size_t i = 0; i < data.
size; i++) {
3142 if (i != data.
size - 1) {
3143 outfile <<
" " << std::flush;
3146 outfile <<
"</globaldata_string>" << std::endl;
3152 for (
const auto &iter: timeseries_data) {
3153 std::string label = iter.first;
3155 std::vector<float> data = iter.second;
3156 std::vector<double> dateval = timeseries_datevalue.at(label);
3158 assert(data.size() == dateval.size());
3160 outfile <<
" <timeseries label=\"" << label <<
"\">" << std::endl;
3162 for (
size_t i = 0; i < data.size(); i++) {
3166 outfile <<
"\t<datapoint>" << std::endl;
3168 outfile <<
"\t <date>" << a_date.
day <<
" " << a_date.
month <<
" " << a_date.
year <<
"</date>" << std::endl;
3170 outfile <<
"\t <time>" << a_time.
hour <<
" " << a_time.
minute <<
" " << a_time.
second <<
"</time>" << std::endl;
3172 outfile <<
"\t <value>" << data.at(i) <<
"</value>" << std::endl;
3174 outfile <<
"\t</datapoint>" << std::endl;
3177 outfile <<
" </timeseries>" << std::endl;
3182 outfile <<
"\n</helios>\n";
3187 std::cout <<
"done." << std::endl;
3195std::vector<uint>
Context::loadPLY(
const char *filename,
const vec3 &origin,
float height,
const std::string &upaxis,
bool silent) {
3200 return loadPLY(filename, origin, height, rotation, RGB::blue, upaxis, silent);
3203std::vector<uint>
Context::loadPLY(
const char *filename,
const vec3 &origin,
float height,
const RGBcolor &default_color,
const std::string &upaxis,
bool silent) {
3209 std::cout <<
"Reading PLY file " << filename <<
"..." << std::flush;
3212 std::string fn = filename;
3214 if (ext !=
".ply" && ext !=
".PLY") {
3218 if (upaxis !=
"XUP" && upaxis !=
"YUP" && upaxis !=
"ZUP") {
3219 helios_runtime_error(
"ERROR (Context::loadPLY): " + upaxis +
" is not a valid up-axis. Please specify a value of XUP, YUP, or ZUP.");
3222 std::string line, prop;
3224 uint vertexCount = 0, faceCount = 0;
3226 std::vector<vec3> vertices;
3227 std::vector<std::vector<int>> faces;
3228 std::vector<RGBcolor> colors;
3229 std::vector<std::string> properties;
3231 bool ifColor =
false;
3235 std::string resolved_filename = resolved_path.string();
3237 std::ifstream inputPly;
3238 inputPly.open(resolved_filename);
3240 if (!inputPly.is_open()) {
3248 if (
"ply" != line) {
3249 helios_runtime_error(
"ERROR (Context::loadPLY): " + std::string(filename) +
" is not a PLY file.");
3254 if (
"format" != line) {
3255 helios_runtime_error(
"ERROR (Context::loadPLY): could not determine data format of " + std::string(filename));
3259 if (
"ascii" != line) {
3263 std::string temp_string;
3265 while (
"end_header" != line) {
3268 if (
"comment" == line) {
3269 getline(inputPly, line);
3270 }
else if (
"element" == line) {
3273 if (
"vertex" == line) {
3274 inputPly >> temp_string;
3276 helios_runtime_error(
"ERROR (Context::loadPLY): PLY file read failed. Vertex count value should be a non-negative integer.");
3278 }
else if (
"face" == line) {
3279 inputPly >> temp_string;
3281 helios_runtime_error(
"ERROR (Context::loadPLY): PLY file read failed. Face count value should be a non-negative integer.");
3284 }
else if (
"property" == line) {
3287 if (
"list" != line) {
3289 properties.push_back(prop);
3294 for (
auto &property: properties) {
3295 if (property ==
"red") {
3300 std::cout <<
"forming " << faceCount <<
" triangles..." << std::flush;
3303 vertices.resize(vertexCount);
3304 colors.resize(vertexCount);
3305 faces.resize(faceCount);
3310 for (
uint row = 0; row < vertexCount; row++) {
3311 for (
auto &property: properties) {
3312 if (property ==
"x") {
3313 inputPly >> temp_string;
3316 helios_runtime_error(
"ERROR (Context::loadPLY): X value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3318 if (upaxis ==
"XUP") {
3319 vertices.at(row).z = x;
3320 }
else if (upaxis ==
"YUP") {
3321 vertices.at(row).y = x;
3322 }
else if (upaxis ==
"ZUP") {
3323 vertices.at(row).x = x;
3325 }
else if (property ==
"y") {
3326 inputPly >> temp_string;
3329 helios_runtime_error(
"ERROR (Context::loadPLY): Y value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3331 if (upaxis ==
"XUP") {
3332 vertices.at(row).x = y;
3333 }
else if (upaxis ==
"YUP") {
3334 vertices.at(row).z = y;
3335 }
else if (upaxis ==
"ZUP") {
3336 vertices.at(row).y = y;
3338 }
else if (property ==
"z") {
3339 inputPly >> temp_string;
3342 helios_runtime_error(
"ERROR (Context::loadPLY): Z value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3344 if (upaxis ==
"XUP") {
3345 vertices.at(row).y = z;
3346 }
else if (upaxis ==
"YUP") {
3347 vertices.at(row).x = z;
3348 }
else if (upaxis ==
"ZUP") {
3349 vertices.at(row).z = z;
3351 }
else if (property ==
"red") {
3352 inputPly >> temp_string;
3353 if (!
parse_float(temp_string, colors.at(row).r)) {
3354 helios_runtime_error(
"ERROR (Context::loadPLY): red color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3356 colors.at(row).r /= 255.f;
3357 }
else if (property ==
"green") {
3358 inputPly >> temp_string;
3359 if (!
parse_float(temp_string, colors.at(row).g)) {
3360 helios_runtime_error(
"ERROR (Context::loadPLY): green color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3362 colors.at(row).g /= 255.f;
3363 }
else if (property ==
"blue") {
3364 inputPly >> temp_string;
3365 if (!
parse_float(temp_string, colors.at(row).b)) {
3366 helios_runtime_error(
"ERROR (Context::loadPLY): blue color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3368 colors.at(row).b /= 255.f;
3374 if (inputPly.eof()) {
3375 helios_runtime_error(
"ERROR (Context::loadPLY): Read past end of file while reading vertices. Vertex count specified in header may be incorrect.");
3384 for (
uint row = 0; row < vertexCount; row++) {
3385 if (vertices.at(row).x < boxmin.
x) {
3386 boxmin.
x = vertices.at(row).x;
3388 if (vertices.at(row).y < boxmin.
y) {
3389 boxmin.
y = vertices.at(row).y;
3391 if (vertices.at(row).z < boxmin.
z) {
3392 boxmin.
z = vertices.at(row).z;
3395 if (vertices.at(row).x > boxmax.
x) {
3396 boxmax.
x = vertices.at(row).x;
3398 if (vertices.at(row).y > boxmax.
y) {
3399 boxmax.
y = vertices.at(row).y;
3401 if (vertices.at(row).z > boxmax.
z) {
3402 boxmax.
z = vertices.at(row).z;
3409 scl = height / (boxmax.
z - boxmin.
z);
3411 for (
uint row = 0; row < vertexCount; row++) {
3412 vertices.at(row).z -= boxmin.
z;
3414 vertices.at(row).x *= scl;
3415 vertices.at(row).y *= scl;
3416 vertices.at(row).z *= scl;
3418 vertices.at(row) =
rotatePoint(vertices.at(row), rotation) + origin;
3424 std::vector<uint> UUID;
3425 for (
uint row = 0; row < faceCount; row++) {
3426 inputPly >> temp_string;
3429 helios_runtime_error(
"ERROR (Context::loadPLY): Vertex count for face " + std::to_string(row) +
" should be a non-negative integer.");
3432 faces.at(row).resize(v);
3434 for (
uint i = 0; i < v; i++) {
3435 inputPly >> temp_string;
3436 if (!
parse_int(temp_string, faces.at(row).at(i))) {
3437 helios_runtime_error(
"ERROR (Context::loadPLY): Vertex index for face " + std::to_string(row) +
" is invalid and could not be read.");
3443 for (
uint t = 2; t < v; t++) {
3446 color = colors.at(faces.at(row).front());
3448 color = default_color;
3451 vec3 v0 = vertices.at(faces.at(row).front());
3452 vec3 v1 = vertices.at(faces.at(row).at(t - 1));
3453 vec3 v2 = vertices.at(faces.at(row).at(t));
3455 if ((v0 - v1).magnitude() < 1e-10f || (v0 - v2).magnitude() < 1e-10f || (v1 - v2).magnitude() < 1e-10f) {
3461 if (triangle_area < MIN_TRIANGLE_AREA_THRESHOLD) {
3470 if (inputPly.eof()) {
3471 helios_runtime_error(
"ERROR (Context::loadPLY): Read past end of file while reading faces. Face count specified in header may be incorrect.");
3476 std::cout <<
"done." << std::endl;
3488 std::string fname{filename ? filename :
""};
3490 const auto dotPos = fname.find_last_of(
'.');
3491 const std::string ext = (dotPos != std::string::npos) ? fname.substr(dotPos) :
"";
3493 auto ciEqual = [](
const char a,
const char b) {
return std::tolower(a) == std::tolower(b); };
3494 bool isPly = (ext.size() == 4) && ciEqual(ext[1],
'p') && ciEqual(ext[2],
'l') && ciEqual(ext[3],
'y');
3497 helios_runtime_error(
"ERROR (Context::writePLY) Invalid file extension for " + fname +
". Expected a file ending in '.ply'.");
3501 std::ofstream PLYfile;
3502 PLYfile.open(fname, std::ios::out | std::ios::trunc);
3504 if (!PLYfile.is_open()) {
3508 PLYfile <<
"ply" << std::endl <<
"format ascii 1.0" << std::endl <<
"comment Helios generated" << std::endl;
3510 std::vector<int3> faces;
3511 std::vector<vec3> verts;
3512 std::vector<RGBcolor> colors;
3514 size_t vertex_count = 0;
3516 for (
auto UUID: UUIDs) {
3517 std::vector<vec3> vertices = getPrimitivePointer_private(UUID)->getVertices();
3518 PrimitiveType type = getPrimitivePointer_private(UUID)->getType();
3519 RGBcolor C = getPrimitivePointer_private(UUID)->getColor();
3523 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2));
3524 for (
int i = 0; i < 3; i++) {
3525 verts.push_back(vertices.at(i));
3526 colors.push_back(C);
3530 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2));
3531 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 2, (
int) vertex_count + 3));
3532 for (
int i = 0; i < 4; i++) {
3533 verts.push_back(vertices.at(i));
3534 colors.push_back(C);
3540 PLYfile <<
"element vertex " << verts.size() << std::endl;
3541 PLYfile <<
"property float x" << std::endl <<
"property float y" << std::endl <<
"property float z" << std::endl;
3542 PLYfile <<
"property uchar red" << std::endl <<
"property uchar green" << std::endl <<
"property uchar blue" << std::endl;
3543 PLYfile <<
"element face " << faces.size() << std::endl;
3544 PLYfile <<
"property list uchar int vertex_indices" << std::endl <<
"end_header" << std::endl;
3546 for (
size_t v = 0; v < verts.size(); v++) {
3547 PLYfile << verts.at(v).x <<
" " << verts.at(v).y <<
" " << verts.at(v).z <<
" " << round(colors.at(v).r) <<
" " << round(colors.at(v).g) <<
" " << round(colors.at(v).b) << std::endl;
3550 for (
auto &face: faces) {
3551 PLYfile <<
"3 " << face.x <<
" " << face.y <<
" " << face.z << std::endl;
3562 return loadOBJ(filename, origin,
make_vec3(0, 0, height), rotation, default_color,
"ZUP", silent);
3566 return loadOBJ(filename, origin,
make_vec3(0, 0, height), rotation, default_color, upaxis, silent);
3572 std::cout <<
"Reading OBJ file " << filename <<
"..." << std::flush;
3575 std::string fn = filename;
3577 if (ext !=
".obj" && ext !=
".OBJ") {
3581 if (strcmp(upaxis,
"XUP") != 0 && strcmp(upaxis,
"YUP") != 0 && strcmp(upaxis,
"ZUP") != 0) {
3582 helios_runtime_error(
"ERROR (Context::loadOBJ): Up axis of " + std::string(upaxis) +
" is not valid. Should be one of 'XUP', 'YUP', or 'ZUP'.");
3585 std::string line, prop;
3587 std::vector<vec3> vertices;
3588 std::vector<std::string> objects;
3589 std::vector<vec2> texture_uv;
3590 std::map<std::string, std::vector<std::vector<int>>> face_inds, texture_inds;
3592 std::map<std::string, OBJmaterial> materials;
3594 std::vector<uint> UUID;
3598 std::string resolved_filename = resolved_path.string();
3600 std::ifstream inputOBJ, inputMTL;
3601 inputOBJ.open(resolved_filename);
3603 if (!inputOBJ.is_open()) {
3608 std::string filebase =
getFilePath(resolved_filename);
3611 float boxmin = 100000;
3612 float boxmax = -100000;
3614 std::string current_material =
"none";
3615 std::string current_object =
"none";
3618 while (inputOBJ.good()) {
3625 getline(inputOBJ, line);
3628 }
else if (line ==
"mtllib") {
3629 getline(inputOBJ, line);
3631 materials = loadMTL(filebase, material_file);
3634 }
else if (line ==
"o") {
3635 getline(inputOBJ, line);
3639 }
else if (line ==
"v") {
3640 getline(inputOBJ, line);
3643 vertices.emplace_back(verts);
3644 objects.emplace_back(current_object);
3646 if (verts.
z < boxmin) {
3649 if (verts.
z > boxmax) {
3654 }
else if (line ==
"vt") {
3655 getline(inputOBJ, line);
3659 texture_uv.emplace_back(uv);
3662 }
else if (line ==
"usemtl") {
3663 getline(inputOBJ, line);
3667 }
else if (line ==
"f") {
3668 getline(inputOBJ, line);
3670 std::istringstream stream(line);
3671 std::string tmp, digitf, digitu;
3672 std::vector<int> f, u;
3673 while (stream.good()) {
3680 digitf.push_back(i);
3688 for (
int i = ic + 1; i < tmp.size(); i++) {
3689 if (isdigit(tmp[i])) {
3690 digitu.push_back(tmp[i]);
3696 if (!digitf.empty()) {
3699 helios_runtime_error(
"ERROR (Context::loadOBJ): Face index on line " + std::to_string(lineno) +
" must be a non-negative integer value.");
3702 if (face <= 0 || face > vertices.size()) {
3703 helios_runtime_error(
"ERROR (Context::loadOBJ): Face vertex index " + std::to_string(face) +
" on line " + std::to_string(lineno) +
" is out of range. Valid range is 1-" + std::to_string(vertices.size()) +
3704 ". Check that vertex indices in face definitions reference existing vertices.");
3708 if (!digitu.empty()) {
3711 helios_runtime_error(
"ERROR (Context::loadOBJ): u,v index on line " + std::to_string(lineno) +
" must be a non-negative integer value.");
3714 if (uv <= 0 || uv > texture_uv.size()) {
3715 helios_runtime_error(
"ERROR (Context::loadOBJ): Texture coordinate index " + std::to_string(uv) +
" on line " + std::to_string(lineno) +
" is out of range. Valid range is 1-" + std::to_string(texture_uv.size()) +
3716 ". Check that texture coordinate indices in face definitions reference existing texture coordinates.");
3721 face_inds[current_material].push_back(f);
3722 texture_inds[current_material].push_back(u);
3726 getline(inputOBJ, line);
3731 if (scl.
x == 0 && scl.
y == 0 && scl.
z > 0) {
3732 if (boxmax - boxmin > 1e-6f) {
3733 scl =
make_vec3(scale.
z / (boxmax - boxmin), scale.
z / (boxmax - boxmin), scale.
z / (boxmax - boxmin));
3739 if (scl.
x == 0 && (scl.
y != 0 || scl.
z != 0)) {
3740 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for x-direction is zero. Setting scaling factor to 1" << std::endl;
3742 if (scl.
y == 0 && (scl.
x != 0 || scl.
z != 0)) {
3743 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for y-direction is zero. Setting scaling factor to 1" << std::endl;
3745 if (scl.
z == 0 && (scl.
x != 0 || scl.
y != 0)) {
3746 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for z-direction is zero. Setting scaling factor to 1" << std::endl;
3761 struct TriangleData {
3762 vec3 vert0, vert1, vert2;
3763 std::string texture;
3767 bool textureColorIsOverridden;
3771 std::vector<TriangleData> triangleDataList;
3774 for (
auto iter = face_inds.begin(); iter != face_inds.end(); ++iter) {
3775 std::string materialname = iter->first;
3777 std::string texture;
3779 bool textureColorIsOverridden =
false;
3781 if (materials.find(materialname) != materials.end()) {
3782 const OBJmaterial &mat = materials.at(materialname);
3784 texture = mat.texture;
3786 textureColorIsOverridden = mat.textureColorIsOverridden;
3790 const auto &material_faces = face_inds.at(materialname);
3791 const auto &material_texture_inds = texture_inds.count(materialname) ? texture_inds.at(materialname) : std::vector<std::vector<int>>();
3794 std::string exception_message;
3795 bool exception_occurred =
false;
3798#pragma omp parallel for schedule(dynamic)
3800 for (
int i = 0; i < static_cast<int>(material_faces.size()); i++) {
3802 for (
uint t = 2; t < material_faces[i].size(); t++) {
3803 vec3 v0 = vertices.at(material_faces[i][0] - 1);
3804 vec3 v1 = vertices.at(material_faces[i][t - 1] - 1);
3805 vec3 v2 = vertices.at(material_faces[i][t] - 1);
3807 if ((v0 - v1).magnitude() == 0 || (v0 - v2).magnitude() == 0 || (v1 - v2).magnitude() == 0) {
3811 if (strcmp(upaxis,
"YUP") == 0) {
3829 if (triangle_area > MIN_TRIANGLE_AREA_THRESHOLD) {
3830 TriangleData triangleData;
3831 triangleData.vert0 = vert0;
3832 triangleData.vert1 = vert1;
3833 triangleData.vert2 = vert2;
3834 triangleData.texture = texture;
3835 triangleData.color = color;
3836 triangleData.textureColorIsOverridden = textureColorIsOverridden;
3837 triangleData.object = objects.at(material_faces[i][0] - 1);
3841 triangleData.hasTexture = !texture.empty();
3845 if (triangleData.hasTexture && i < material_texture_inds.size() && !material_texture_inds[i].empty() && t < material_texture_inds[i].size()) {
3847 int iuv0 = material_texture_inds[i][0] - 1;
3848 int iuv1 = material_texture_inds[i][t - 1] - 1;
3849 int iuv2 = material_texture_inds[i][t] - 1;
3851 if (iuv0 >= 0 && iuv0 < texture_uv.size() && iuv1 >= 0 && iuv1 < texture_uv.size() && iuv2 >= 0 && iuv2 < texture_uv.size()) {
3852 triangleData.uv0 = texture_uv.at(iuv0);
3853 triangleData.uv1 = texture_uv.at(iuv1);
3854 triangleData.uv2 = texture_uv.at(iuv2);
3856 helios_runtime_error(
"ERROR (Context::loadOBJ): Invalid texture coordinate indices in face for material '" + materialname +
"'. " +
"UV indices [" + std::to_string(iuv0 + 1) +
", " + std::to_string(iuv1 + 1) +
", " +
3857 std::to_string(iuv2 + 1) +
"] " +
"exceed available UV coordinates (1-" + std::to_string(texture_uv.size()) +
"). " +
3858 "Check that all face texture coordinate references in the OBJ file are valid.");
3860 }
else if (triangleData.hasTexture) {
3861 helios_runtime_error(
"ERROR (Context::loadOBJ): Material '" + materialname +
"' specifies texture file '" + texture +
"' " +
"but face has no texture coordinates. Either remove the texture from the material " +
3862 "or add texture coordinates (vt) and face texture indices (f v1/vt1 v2/vt2 v3/vt3) to the OBJ file.");
3869 triangleDataList.push_back(triangleData);
3873 }
catch (
const std::exception &e) {
3879 if (!exception_occurred) {
3880 exception_message = e.what();
3881 exception_occurred =
true;
3888 if (exception_occurred) {
3894 for (
const auto &triangleData: triangleDataList) {
3897 if (triangleData.hasTexture) {
3898 ID =
addTriangle(triangleData.vert0, triangleData.vert1, triangleData.vert2, triangleData.texture.c_str(), triangleData.uv0, triangleData.uv1, triangleData.uv2);
3900 if (triangleData.textureColorIsOverridden) {
3905 ID =
addTriangle(triangleData.vert0, triangleData.vert1, triangleData.vert2, triangleData.color);
3916 std::cout <<
"done." << std::endl;
3922std::map<std::string, Context::OBJmaterial> Context::loadMTL(
const std::string &filebase,
const std::string &material_file) {
3923 std::ifstream inputMTL;
3925 std::string file = material_file;
3929 std::filesystem::path resolved_path;
3931 if (std::filesystem::path(file).is_absolute()) {
3936 std::filesystem::path mtl_path = std::filesystem::path(filebase) / file;
3940 std::string resolved_file = resolved_path.string();
3941 inputMTL.open(resolved_file.c_str());
3943 if (!inputMTL.is_open()) {
3944 helios_runtime_error(
"ERROR (Context::loadMTL): Could not open material file " + resolved_file +
" after successful path resolution.");
3947 std::map<std::string, OBJmaterial> materials;
3953 while (inputMTL.good()) {
3954 if (strcmp(
"#", line.c_str()) == 0) {
3955 getline(inputMTL, line);
3957 }
else if (line ==
"newmtl") {
3958 getline(inputMTL, line);
3960 OBJmaterial mat(RGB::red,
"", 0);
3961 materials.emplace(material_name, mat);
3963 std::string map_Kd, map_d;
3965 while (line !=
"newmtl" && inputMTL.good()) {
3968 if (line ==
"newmtl") {
3970 }
else if (line ==
"map_a" || line ==
"map_Ka" || line ==
"Ks" || line ==
"Ka" || line ==
"map_Ks") {
3971 getline(inputMTL, line);
3972 }
else if (line ==
"map_Kd" || line ==
"map_d") {
3973 std::string maptype = line;
3974 getline(inputMTL, line);
3976 std::istringstream stream(line);
3978 while (stream.good()) {
3981 if (ext ==
".png" || ext ==
".PNG" || ext ==
".jpg" || ext ==
".JPG" || ext ==
".jpeg" || ext ==
".JPEG") {
3982 std::string texturefile = tmp;
3985 std::filesystem::path texture_path = texturefile;
3986 bool texture_exists =
false;
3989 if (std::filesystem::exists(texture_path)) {
3990 texture_exists =
true;
3993 texture_path = std::filesystem::path(filebase) / tmp;
3994 texturefile = texture_path.string();
3995 if (std::filesystem::exists(texture_path)) {
3996 texture_exists =
true;
4000 if (!texture_exists) {
4001 helios_runtime_error(
"ERROR (Context::loadOBJ): Texture file '" + tmp +
"' referenced in .mtl file cannot be found. " +
"Searched in current directory and OBJ file directory (" + filebase +
"). " +
4002 "Ensure texture file exists or remove texture reference from material.");
4005 if (maptype ==
"map_d") {
4006 map_d = texturefile;
4008 map_Kd = texturefile;
4012 }
else if (line ==
"Kd") {
4013 getline(inputMTL, line);
4016 materials.at(material_name).color =
make_RGBcolor(color.
r, color.
g, color.
b);
4018 getline(inputMTL, line);
4022 if (!map_Kd.empty()) {
4023 materials.at(material_name).texture = map_Kd;
4024 if (!map_d.empty() && map_d != map_Kd) {
4025 materials.at(material_name).textureHasTransparency =
true;
4027 }
else if (!map_d.empty()) {
4028 materials.at(material_name).texture = map_d;
4029 materials.at(material_name).textureColorIsOverridden =
true;
4032 getline(inputMTL, line);
4044void Context::writeOBJ(
const std::string &filename,
const std::vector<uint> &UUIDs,
bool write_normals,
bool silent)
const {
4045 writeOBJ(filename, UUIDs, {}, write_normals, silent);
4048void Context::writeOBJ(
const std::string &filename,
const std::vector<uint> &UUIDs,
const std::vector<std::string> &primitive_dat_fields,
bool write_normals,
bool silent)
const {
4050 if (UUIDs.empty()) {
4051 std::cout <<
"WARNING (Context::writeOBJ): No primitives found to write - OBJ file " << filename <<
" will not be written." << std::endl;
4054 if (filename.empty()) {
4055 std::cout <<
"WARNING (Context::writeOBJ): Filename was empty - OBJ file " << filename <<
" will not be written." << std::endl;
4059 std::string objfilename = filename;
4060 std::string mtlfilename = filename;
4066 if (file_extension !=
".obj" && file_extension !=
".OBJ") {
4067 objfilename.append(
".obj");
4068 mtlfilename.append(
".mtl");
4070 if (!file_path.empty()) {
4071 std::filesystem::path mtl_path = std::filesystem::path(file_path) / (file_stem +
".mtl");
4072 mtlfilename = mtl_path.string();
4074 mtlfilename = file_stem +
".mtl";
4078 if (!file_path.empty() && !std::filesystem::exists(file_path)) {
4079 if (!std::filesystem::create_directory(file_path)) {
4080 std::cerr <<
"failed. Directory " << file_path <<
" does not exist and it could not be created - OBJ file will not be written." << std::endl;
4086 std::cout <<
"Writing OBJ file " << objfilename <<
"..." << std::flush;
4089 std::vector<OBJmaterial> materials;
4090 std::unordered_map<std::string, uint> material_cache;
4091 const size_t primitive_count = UUIDs.size();
4092 const size_t estimated_vertices = primitive_count * 4;
4094 std::vector<vec3> verts;
4095 verts.reserve(estimated_vertices);
4096 std::vector<vec3> normals;
4097 if (write_normals) {
4098 normals.reserve(primitive_count);
4100 std::vector<vec2> uv;
4101 uv.reserve(estimated_vertices);
4103 std::map<uint, std::vector<int3>> faces;
4104 std::map<uint, std::vector<int>> normal_inds;
4105 std::map<uint, std::vector<int3>> uv_inds;
4106 size_t vertex_count = 1;
4107 size_t normal_count = 0;
4108 size_t uv_count = 1;
4109 std::map<uint, std::vector<uint>> UUIDs_write;
4111 std::map<std::string, std::map<uint, std::vector<int3>>> object_faces;
4112 std::map<std::string, std::map<uint, std::vector<int>>> object_normal_inds;
4113 std::map<std::string, std::map<uint, std::vector<int3>>> object_uv_inds;
4114 std::vector<std::string> object_order;
4115 object_order.reserve(primitive_count / 10);
4116 bool object_groups_found =
false;
4118 for (
size_t p: UUIDs) {
4120 std::ostringstream err_stream;
4121 err_stream <<
"ERROR (Context::writeOBJ): Primitive with UUID " << p <<
" does not exist. "
4122 <<
"Ensure all UUIDs in the input vector correspond to valid primitives before calling writeOBJ.";
4126 const Primitive *prim_ptr = getPrimitivePointer_private(p);
4129 std::ostringstream err_stream;
4130 err_stream <<
"ERROR (Context::writeOBJ): Voxel primitives (UUID " << p <<
") cannot be written to OBJ format. "
4131 <<
"OBJ format only supports surface primitives (triangles, patches). "
4132 <<
"Filter out voxel primitives before calling writeOBJ.";
4136 std::vector<vec3> vertices = prim_ptr->getVertices();
4139 std::string texturefile = prim_ptr->getTextureFile();
4140 bool texture_color_overridden = prim_ptr->isTextureColorOverridden();
4142 std::string obj_label =
"default";
4145 object_groups_found =
true;
4147 if (object_faces.find(obj_label) == object_faces.end()) {
4148 object_faces[obj_label] = {};
4149 object_normal_inds[obj_label] = {};
4150 object_uv_inds[obj_label] = {};
4151 object_order.push_back(obj_label);
4154 std::string material_key = texturefile +
"|" + std::to_string(C.
r) +
"," + std::to_string(C.
g) +
"," + std::to_string(C.
b) +
"|" + std::to_string(texture_color_overridden);
4157 auto material_iter = material_cache.find(material_key);
4159 if (material_iter != material_cache.end()) {
4161 material_ID = material_iter->second;
4164 OBJmaterial mat(C, texturefile, materials.size());
4165 materials.emplace_back(mat);
4166 material_ID = mat.materialID;
4169 materials.back().textureHasTransparency =
true;
4171 if (texture_color_overridden) {
4172 materials.back().textureColorIsOverridden =
true;
4175 material_cache[material_key] = material_ID;
4178 if (!primitive_dat_fields.empty()) {
4179 UUIDs_write[material_ID].push_back(p);
4182 if (write_normals) {
4184 normals.push_back(normal);
4189 int3 ftmp =
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2);
4190 faces[material_ID].push_back(ftmp);
4191 object_faces[obj_label][material_ID].push_back(ftmp);
4192 for (
int i = 0; i < 3; i++) {
4193 verts.push_back(vertices.at(i));
4197 if (write_normals) {
4198 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4199 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4202 std::vector<vec2> uv_v = getTrianglePointer_private(p)->getTextureUV();
4203 if (getTrianglePointer_private(p)->hasTexture()) {
4204 int3 tuv =
make_int3((
int) uv_count, (
int) uv_count + 1, (
int) uv_count + 2);
4205 uv_inds[material_ID].push_back(tuv);
4206 object_uv_inds[obj_label][material_ID].push_back(tuv);
4207 for (
int i = 0; i < 3; i++) {
4208 uv.push_back(uv_v.at(i));
4213 uv_inds[material_ID].push_back(tuv);
4214 object_uv_inds[obj_label][material_ID].push_back(tuv);
4217 int3 ftmp1 =
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2);
4218 int3 ftmp2 =
make_int3((
int) vertex_count, (
int) vertex_count + 2, (
int) vertex_count + 3);
4219 faces[material_ID].push_back(ftmp1);
4220 faces[material_ID].push_back(ftmp2);
4221 object_faces[obj_label][material_ID].push_back(ftmp1);
4222 object_faces[obj_label][material_ID].push_back(ftmp2);
4223 for (
int i = 0; i < 4; i++) {
4224 verts.push_back(vertices.at(i));
4227 std::vector<vec2> uv_v;
4228 uv_v = getPatchPointer_private(p)->getTextureUV();
4230 if (write_normals) {
4231 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4232 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4233 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4234 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4237 if (getPatchPointer_private(p)->hasTexture()) {
4238 int3 tuv1 =
make_int3((
int) uv_count, (
int) uv_count + 1, (
int) uv_count + 2);
4239 int3 tuv2 =
make_int3((
int) uv_count, (
int) uv_count + 2, (
int) uv_count + 3);
4240 uv_inds[material_ID].push_back(tuv1);
4241 uv_inds[material_ID].push_back(tuv2);
4242 object_uv_inds[obj_label][material_ID].push_back(tuv1);
4243 object_uv_inds[obj_label][material_ID].push_back(tuv2);
4251 for (
int i = 0; i < 4; i++) {
4252 uv.push_back(uv_v.at(i));
4258 uv_inds[material_ID].push_back(tuv);
4259 uv_inds[material_ID].push_back(tuv);
4260 object_uv_inds[obj_label][material_ID].push_back(tuv);
4261 object_uv_inds[obj_label][material_ID].push_back(tuv);
4267 assert(normal_inds.size() == faces.size());
4269 assert(uv_inds.size() == faces.size());
4270 for (
int i = 0; i < faces.size(); i++) {
4271 assert(uv_inds.at(i).size() == faces.at(i).size());
4275 std::filesystem::path output_path = std::filesystem::path(file_path);
4276 std::filesystem::path texture_dir = output_path.parent_path();
4279 if (texture_dir.empty()) {
4283 for (
auto &material: materials) {
4284 std::string texture = material.texture;
4285 if (!texture.empty()) {
4287 std::filesystem::path source_path = std::filesystem::absolute(texture, ec);
4291 source_path = std::filesystem::path(texture);
4294 if (!std::filesystem::exists(source_path)) {
4299 auto filename = source_path.filename();
4300 std::filesystem::path dest_path = texture_dir / filename;
4303 bool same_file =
false;
4305 same_file = std::filesystem::equivalent(source_path, dest_path, ec);
4313 material.texture = filename.string();
4319 std::filesystem::copy_file(source_path, dest_path, std::filesystem::copy_options::overwrite_existing, ec);
4321 material.texture = filename.string();
4330 std::ofstream objfstream;
4331 objfstream.open(objfilename);
4332 std::ofstream mtlfstream;
4333 mtlfstream.open(mtlfilename);
4335 objfstream <<
"# Helios auto-generated OBJ File" << std::endl;
4336 objfstream <<
"# baileylab.ucdavis.edu/software/helios" << std::endl;
4337 objfstream <<
"mtllib " <<
getFileName(mtlfilename) << std::endl;
4340 std::vector<std::string> vertex_chunks;
4341 const int num_threads = std::min(
static_cast<int>(verts.size() / 1000 + 1), std::max(1,
static_cast<int>(std::thread::hardware_concurrency())));
4342 vertex_chunks.resize(num_threads);
4345#pragma omp parallel num_threads(num_threads)
4350 tid = omp_get_thread_num();
4352 std::ostringstream vertex_stream;
4353 vertex_stream.precision(8);
4355 const size_t chunk_size = (verts.size() + num_threads - 1) / num_threads;
4356 const size_t start_idx = tid * chunk_size;
4357 const size_t end_idx = std::min(start_idx + chunk_size, verts.size());
4359 for (
size_t i = start_idx; i < end_idx; i++) {
4360 vertex_stream <<
"v " << verts[i].x <<
" " << verts[i].y <<
" " << verts[i].z <<
"\n";
4363 vertex_chunks[tid] = vertex_stream.str();
4366 for (
const auto &chunk: vertex_chunks) {
4367 objfstream << chunk;
4370 if (write_normals) {
4371 std::vector<std::string> normal_chunks;
4372 normal_chunks.resize(num_threads);
4375#pragma omp parallel num_threads(num_threads)
4380 tid = omp_get_thread_num();
4382 std::ostringstream normal_stream;
4383 normal_stream.precision(8);
4385 const size_t chunk_size = (normals.size() + num_threads - 1) / num_threads;
4386 const size_t start_idx = tid * chunk_size;
4387 const size_t end_idx = std::min(start_idx + chunk_size, normals.size());
4389 const float epsilon = 1e-7;
4390 for (
size_t i = start_idx; i < end_idx; i++) {
4391 vec3 n = normals[i];
4392 if (std::abs(n.
x) < epsilon)
4394 if (std::abs(n.
y) < epsilon)
4396 if (std::abs(n.
z) < epsilon)
4398 normal_stream <<
"vn " << n.
x <<
" " << n.
y <<
" " << n.
z <<
"\n";
4401 normal_chunks[tid] = normal_stream.str();
4404 for (
const auto &chunk: normal_chunks) {
4405 objfstream << chunk;
4410 std::vector<std::string> uv_chunks;
4411 uv_chunks.resize(num_threads);
4414#pragma omp parallel num_threads(num_threads)
4419 tid = omp_get_thread_num();
4421 std::ostringstream uv_stream;
4422 uv_stream.precision(8);
4424 const size_t chunk_size = (uv.size() + num_threads - 1) / num_threads;
4425 const size_t start_idx = tid * chunk_size;
4426 const size_t end_idx = std::min(start_idx + chunk_size, uv.size());
4428 for (
size_t i = start_idx; i < end_idx; i++) {
4429 uv_stream <<
"vt " << uv[i].x <<
" " << uv[i].y <<
"\n";
4432 uv_chunks[tid] = uv_stream.str();
4435 for (
const auto &chunk: uv_chunks) {
4436 objfstream << chunk;
4442 if (object_groups_found) {
4445 for (
const auto &obj_label: object_order) {
4446 objfstream <<
"o " << obj_label <<
"\n";
4448 for (
int mat = 0; mat < materials.size(); mat++) {
4449 auto fit = object_faces[obj_label].find(mat);
4450 if (fit == object_faces[obj_label].end())
4453 objfstream <<
"usemtl material" << mat <<
"\n";
4455 const auto ¤t_faces = fit->second;
4456 if (current_faces.size() > 100) {
4458 std::vector<std::string> face_chunks;
4459 face_chunks.resize(num_threads);
4462#pragma omp parallel num_threads(num_threads)
4467 tid = omp_get_thread_num();
4469 std::ostringstream face_stream;
4471 const size_t chunk_size = (current_faces.size() + num_threads - 1) / num_threads;
4472 const size_t start_idx = tid * chunk_size;
4473 const size_t end_idx = std::min(start_idx + chunk_size, current_faces.size());
4475 for (
size_t f = start_idx; f < end_idx; f++) {
4477 if (write_normals) {
4478 face_stream <<
"f " << current_faces[f].x <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"//"
4479 << object_normal_inds[obj_label][mat][f] <<
"\n";
4481 face_stream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z <<
"\n";
4483 }
else if (object_uv_inds[obj_label][mat][f].x < 0) {
4484 face_stream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1\n";
4486 if (write_normals) {
4487 face_stream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y
4488 <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"/" << object_uv_inds[obj_label][mat][f].z <<
"/" << object_normal_inds[obj_label][mat][f] <<
"\n";
4490 face_stream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
" " << current_faces[f].z <<
"/"
4491 << object_uv_inds[obj_label][mat][f].z <<
"\n";
4496 face_chunks[tid] = face_stream.str();
4500 for (
const auto &chunk: face_chunks) {
4501 objfstream << chunk;
4505 for (
size_t f = 0; f < current_faces.size(); ++f) {
4507 if (write_normals) {
4508 objfstream <<
"f " << current_faces[f].x <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"//"
4509 << object_normal_inds[obj_label][mat][f] << std::endl;
4511 objfstream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z << std::endl;
4513 }
else if (object_uv_inds[obj_label][mat][f].x < 0) {
4514 objfstream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1" << std::endl;
4516 if (write_normals) {
4517 objfstream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
"/"
4518 << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"/" << object_uv_inds[obj_label][mat][f].z <<
"/" << object_normal_inds[obj_label][mat][f] << std::endl;
4520 objfstream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
" " << current_faces[f].z <<
"/"
4521 << object_uv_inds[obj_label][mat][f].z << std::endl;
4530 for (
int mat = 0; mat < materials.size(); mat++) {
4531 assert(materials.at(mat).materialID == mat);
4532 objfstream <<
"usemtl material" << mat <<
"\n";
4534 const auto ¤t_faces = faces.at(mat);
4535 if (current_faces.size() > 100) {
4537 std::vector<std::string> face_chunks;
4538 face_chunks.resize(num_threads);
4541#pragma omp parallel num_threads(num_threads)
4546 tid = omp_get_thread_num();
4548 std::ostringstream face_stream;
4550 const size_t chunk_size = (current_faces.size() + num_threads - 1) / num_threads;
4551 const size_t start_idx = tid * chunk_size;
4552 const size_t end_idx = std::min(start_idx + chunk_size, current_faces.size());
4554 for (
size_t f = start_idx; f < end_idx; f++) {
4556 if (write_normals) {
4557 face_stream <<
"f " << current_faces[f].x <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].z <<
"//" << normal_inds.at(mat)[f] <<
"\n";
4559 face_stream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z <<
"\n";
4561 }
else if (uv_inds.at(mat)[f].x < 0) {
4562 face_stream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1\n";
4564 if (write_normals) {
4565 face_stream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
"/" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
"/" << normal_inds.at(mat)[f] <<
" "
4566 << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"/" << normal_inds.at(mat)[f] <<
"\n";
4568 face_stream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
" " << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"\n";
4573 face_chunks[tid] = face_stream.str();
4577 for (
const auto &chunk: face_chunks) {
4578 objfstream << chunk;
4582 for (
int f = 0; f < current_faces.size(); f++) {
4584 if (write_normals) {
4585 objfstream <<
"f " << current_faces[f].x <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].z <<
"//" << normal_inds.at(mat)[f] << std::endl;
4587 objfstream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z << std::endl;
4589 }
else if (uv_inds.at(mat)[f].x < 0) {
4590 objfstream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1" << std::endl;
4592 if (write_normals) {
4593 objfstream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
"/" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
"/" << normal_inds.at(mat)[f] <<
" "
4594 << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"/" << normal_inds.at(mat)[f] << std::endl;
4596 objfstream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
" " << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z << std::endl;
4604 for (
int mat = 0; mat < materials.size(); mat++) {
4605 if (materials.at(mat).texture.empty()) {
4606 RGBcolor current_color = materials.at(mat).color;
4607 mtlfstream <<
"newmtl material" << mat << std::endl;
4608 mtlfstream <<
"Ka " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
4609 mtlfstream <<
"Kd " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
4610 mtlfstream <<
"Ks 0.0 0.0 0.0" << std::endl;
4611 mtlfstream <<
"illum 2 " << std::endl;
4613 std::string current_texture = materials.at(mat).texture;
4614 mtlfstream <<
"newmtl material" << mat << std::endl;
4615 if (materials.at(mat).textureColorIsOverridden) {
4616 RGBcolor current_color = materials.at(mat).color;
4617 mtlfstream <<
"Ka " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
4618 mtlfstream <<
"Kd " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
4620 mtlfstream <<
"map_Kd " << current_texture << std::endl;
4622 if (materials.at(mat).textureHasTransparency) {
4623 mtlfstream <<
"map_d " << current_texture << std::endl;
4625 mtlfstream <<
"Ks 0.0 0.0 0.0" << std::endl;
4626 mtlfstream <<
"illum 2 " << std::endl;
4633 if (!primitive_dat_fields.empty()) {
4634 bool uuidexistswarning =
false;
4635 bool dataexistswarning =
false;
4636 bool datatypewarning =
false;
4638 for (
const std::string &label: primitive_dat_fields) {
4639 std::filesystem::path dat_path = std::filesystem::path(file_path) / (file_stem +
"_" + std::string(label) +
".dat");
4640 std::string datfilename = dat_path.string();
4641 std::ofstream datout(datfilename);
4643 for (
int mat = 0; mat < materials.size(); mat++) {
4644 for (
uint UUID: UUIDs_write.at(mat)) {
4646 uuidexistswarning =
true;
4657 dataexistswarning =
true;
4658 for (
int i = 0; i < Nprims; i++) {
4659 datout << 0 << std::endl;
4668 for (
int i = 0; i < Nprims; i++) {
4669 datout << data << std::endl;
4674 for (
int i = 0; i < Nprims; i++) {
4675 datout << data << std::endl;
4680 for (
int i = 0; i < Nprims; i++) {
4681 datout << data << std::endl;
4686 for (
int i = 0; i < Nprims; i++) {
4687 datout << data << std::endl;
4692 for (
int i = 0; i < Nprims; i++) {
4693 datout << data << std::endl;
4696 datatypewarning =
true;
4697 for (
int i = 0; i < Nprims; i++) {
4698 datout << 0 << std::endl;
4707 if (uuidexistswarning) {
4708 helios_runtime_error(
"Context::writeOBJ: One or more UUIDs do not exist in the Context. Cannot write OBJ file with invalid primitives.");
4710 if (dataexistswarning) {
4711 helios_runtime_error(
"Context::writeOBJ: Primitive data requested did not exist for one or more primitives. Cannot write incomplete data to OBJ file.");
4713 if (datatypewarning) {
4714 helios_runtime_error(
"Context::writeOBJ: Only scalar primitive data types (uint, int, float, double, and string) are supported for primitive data export.");
4723void Context::writePrimitiveData(
const std::string &filename,
const std::vector<std::string> &column_format,
const std::vector<uint> &UUIDs,
bool print_header)
const {
4724 std::ofstream file(filename);
4727 for (
const auto &label: column_format) {
4728 file << label <<
" ";
4730 file.seekp(-1, std::ios_base::end);
4734 bool uuidexistswarning =
false;
4735 bool dataexistswarning =
false;
4736 bool datatypewarning =
false;
4738 for (
uint UUID: UUIDs) {
4740 uuidexistswarning =
true;
4743 for (
const auto &label: column_format) {
4744 if (label ==
"UUID") {
4745 file << UUID <<
" ";
4749 dataexistswarning =
true;
4757 file << data <<
" ";
4761 file << data <<
" ";
4765 file << data <<
" ";
4769 file << data <<
" ";
4773 file << data <<
" ";
4775 datatypewarning =
true;
4779 file.seekp(-1, std::ios_base::end);
4783 if (uuidexistswarning) {
4784 std::cerr <<
"WARNING (Context::writePrimitiveData): Vector of UUIDs passed to writePrimitiveData() function contained UUIDs that do not exist, which were skipped." << std::endl;
4786 if (dataexistswarning) {
4787 std::cerr <<
"WARNING (Context::writePrimitiveData): Primitive data requested did not exist for one or more primitives. A default value of 0 was written in these cases." << std::endl;
4789 if (datatypewarning) {
4790 std::cerr <<
"WARNING (Context::writePrimitiveData): Only scalar primitive data types (uint, int, float, and double) are supported for this function. A column of 0's was written in these cases." << std::endl;
4796void Context::loadTabularTimeseriesData(
const std::string &data_file,
const std::vector<std::string> &col_labels,
const std::string &a_delimeter,
const std::string &a_date_string_format,
uint headerlines) {
4799 std::string resolved_filename = resolved_path.string();
4801 std::ifstream datafile(resolved_filename);
4803 if (!datafile.is_open()) {
4804 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Weather data file '" + data_file +
"' does not exist.");
4809 int datestrcol = -1;
4813 std::map<std::string, int> datacols;
4815 size_t Ncolumns = 0;
4817 size_t row = headerlines;
4819 std::vector<std::string> column_labels = col_labels;
4820 std::string delimiter = a_delimeter;
4821 std::string date_string_format = a_date_string_format;
4824 if (col_labels.size() == 1 && (col_labels.front() ==
"CIMIS" || col_labels.front() ==
"cimis")) {
4826 "",
"",
"",
"date",
"hour",
"DOY",
"ETo",
"",
"precipitation",
"",
"net_radiation",
"",
"vapor_pressure",
"",
"air_temperature",
"",
"air_humidity",
"",
"dew_point",
"",
"wind_speed",
"",
"wind_direction",
"",
"soil_temperature",
""};
4829 date_string_format =
"MMDDYYYY";
4833 if (!column_labels.empty()) {
4835 for (
auto &label: column_labels) {
4836 if (label ==
"year" || label ==
"Year") {
4838 }
else if (label ==
"DOY" || label ==
"Jul") {
4840 }
else if (label ==
"date" || label ==
"Date") {
4842 }
else if (label ==
"hour" || label ==
"Hour") {
4844 }
else if (label ==
"minute" || label ==
"Minute") {
4846 }
else if (label ==
"second" || label ==
"Second") {
4848 }
else if (!label.empty()) {
4849 if (datacols.find(label) == datacols.end()) {
4850 datacols[label] = col;
4852 datacols[label +
"_dup"] = col;
4859 Ncolumns = column_labels.size();
4863 if (headerlines == 0) {
4864 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): "
4866 " argument was specified as zero, and no column label information was given. Attempting to read the first line to see if it contains label information."
4872 if (std::getline(datafile, line)) {
4875 if (line_parsed.empty()) {
4876 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but it did not contain the specified delimiter.");
4879 Ncolumns = line_parsed.size();
4881 for (
int col = 0; col < Ncolumns; col++) {
4882 const std::string &label = line_parsed.at(col);
4884 if (label ==
"year" || label ==
"Year") {
4886 }
else if (label ==
"DOY" || label ==
"Jul") {
4888 }
else if (label ==
"date" || label ==
"Date") {
4890 }
else if (label ==
"hour" || label ==
"Hour") {
4892 }
else if (label ==
"minute" || label ==
"Minute") {
4894 }
else if (label ==
"second" || label ==
"Second") {
4896 }
else if (!label.empty()) {
4897 if (datacols.find(label) == datacols.end()) {
4898 datacols[label] = col;
4900 datacols[label +
"_dup"] = col;
4907 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but read failed.");
4910 if (yearcol == -1 && DOYcol == -1 && datestrcol == -1) {
4911 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but could not find valid label information.");
4915 if (datestrcol < 0 && (yearcol < 0 || DOYcol < 0)) {
4916 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): The date must be specified by either a column labeled "
4918 ", or by two columns labeled "
4923 }
else if (hourcol < 0) {
4924 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): At a minimum, the time must be specified by a column labeled "
4927 }
else if (datacols.empty()) {
4928 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): No columns were found containing data variables (e.g., temperature, humidity, wind speed).");
4935 for (
int i = 0; i < headerlines; i++) {
4936 std::getline(datafile, line);
4939 while (std::getline(datafile, line)) {
4949 if (line_separated.size() != Ncolumns) {
4950 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Line " + std::to_string(row) +
" had " + std::to_string(line_separated.size()) +
" columns, but was expecting " + std::to_string(Ncolumns));
4955 if (yearcol >= 0 && DOYcol >= 0) {
4957 parse_int(line_separated.at(DOYcol), DOY);
4958 if (DOY < 1 || DOY > 366) {
4959 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid date specified on line " + std::to_string(row) +
".");
4962 parse_int(line_separated.at(yearcol), year);
4964 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid year specified on line " + std::to_string(row) +
".");
4967 }
else if (datestrcol >= 0) {
4969 const std::string &datestr = line_separated.at(datestrcol);
4974 if (thisdatestr.size() != 3) {
4979 if (thisdatestr.size() != 3) {
4980 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
4981 ". It should be in the format YYYY-MM-DD, delimited by either "
4989 std::vector<int> thisdate(3);
4990 for (
int i = 0; i < 3; i++) {
4991 if (!
parse_int(thisdatestr.at(i), thisdate.at(i))) {
4992 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
4993 ". It should be in the format YYYY-MM-DD, delimited by either "
5005 if (date_string_format ==
"YYYYMMDD") {
5006 year = thisdate.at(0);
5007 month = thisdate.at(1);
5008 day = thisdate.at(2);
5009 }
else if (date_string_format ==
"YYYYDDMM") {
5010 year = thisdate.at(0);
5011 month = thisdate.at(2);
5012 day = thisdate.at(1);
5013 }
else if (date_string_format ==
"DDMMYYYY") {
5014 year = thisdate.at(2);
5015 month = thisdate.at(1);
5016 day = thisdate.at(0);
5017 }
else if (date_string_format ==
"MMDDYYYY") {
5018 year = thisdate.at(2);
5019 month = thisdate.at(0);
5020 day = thisdate.at(1);
5022 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid date string format in file " + data_file +
": " + date_string_format +
5031 ". Check that the date string does not include a delimiter (i.e., should be MMDDYYYY not MM/DD/YYYY).");
5034 if (year < 1000 || month < 1 || month > 12 || day < 1 || day > 31) {
5035 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
5036 ". It should be in the format YYYY-MM-DD, delimited by either "
5054 if (!
parse_int(line_separated.at(hourcol), hour)) {
5055 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse hour string on line " + std::to_string(row) +
" of file " + data_file +
".");
5057 if (hour > 24 && minutecol < 0 && secondcol < 0) {
5059 hour = std::floor(hr_min / 100);
5060 minute = hr_min - hour * 100;
5066 if (minutecol >= 0) {
5067 if (!
parse_int(line_separated.at(minutecol), minute)) {
5069 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Could not parse minute string on line " << row <<
" of file " << data_file <<
". Setting minute equal to 0." << std::endl;
5072 if (secondcol >= 0) {
5073 if (!
parse_int(line_separated.at(secondcol), second)) {
5075 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Could not parse second string on line " << row <<
" of file " << data_file <<
". Setting second equal to 0." << std::endl;
5081 for (
auto &dat: datacols) {
5082 std::string label = dat.first;
5083 int col = dat.second;
5086 if (!
parse_float(line_separated.at(col), dataval)) {
5087 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Failed to parse data value as "
5090 << row <<
", column " << col + 1 <<
" of file " << data_file <<
". Skipping this value..." << std::endl;
5094 if (label ==
"air_humidity" && col_labels.size() == 1 && (col_labels.front() ==
"CIMIS" || col_labels.front() ==
"cimis")) {
5095 dataval = dataval / 100.f;