1.3.49
 
Loading...
Searching...
No Matches
BuildGeometry.cpp
1#include "BuildGeometry/BuildGeometry.h"
2#include "PlantArchitecture.h"
3
4using namespace helios;
5
6void BuildGeometry(const std::string &xml_input_file, PlantArchitecture *plant_architecture_ptr, Context *context_ptr, std::vector<std::vector<uint>> &canopy_UUID_vector, std::vector<std::vector<helios::vec3>> &individual_plant_locations) {
7
8 std::vector<uint> ground_UUIDs;
9 std::vector<uint> leaf_UUIDs;
10 std::vector<uint> petiolule_UUIDs;
11 std::vector<uint> petiole_UUIDs;
12 std::vector<uint> internode_UUIDs;
13 std::vector<uint> peduncle_UUIDs;
14 std::vector<uint> petal_UUIDs;
15 std::vector<uint> sepal_UUIDs;
16 std::vector<uint> fruit_UUIDs;
17
18 pugi::xml_document xmldoc;
19
20 std::string xml_error_string;
21 if (!open_xml_file(xml_input_file, xmldoc, xml_error_string)) {
22 helios_runtime_error(xml_error_string);
23 }
24
25 pugi::xml_node helios = xmldoc.child("helios");
26 pugi::xml_node node;
27
28 // ####### BUILDING THE GROUND ####### //
29
30 vec2 domain_extent(10, 10);
31 bool domain_extent_read = false;
32 node = helios.child("domain_extent");
33 if (node.empty()) {
34 std::cout << "WARNING: No value given for 'domain_extent'. Using default value of " << domain_extent << std::endl;
35 } else {
36
37 const char *domain_extent_str = node.child_value();
38 if (!parse_vec2(domain_extent_str, domain_extent)) {
39 helios_runtime_error("ERROR: Value given for 'domain_extent' could not be parsed.");
40 } else if (domain_extent.x <= 0 || domain_extent.y <= 0) {
41 helios_runtime_error("ERROR: Value given for 'domain_extent' must be greater than 0.");
42 } else {
43 domain_extent_read = true;
44 }
45 }
46
47 vec3 domain_origin(0, 0, 0);
48 bool domain_origin_read = false;
49 node = helios.child("domain_origin");
50 if (node.empty()) {
51 std::cout << "WARNING: No value given for 'domain_origin'. Using default value of " << domain_origin << std::endl;
52 } else {
53
54 const char *domain_origin_str = node.child_value();
55 if (!parse_vec3(domain_origin_str, domain_origin)) {
56 helios_runtime_error("ERROR: Value given for 'domain_origin' could not be parsed.");
57 } else {
58 domain_origin_read = true;
59 }
60 }
61
62 int2 ground_resolution(1, 1);
63 bool ground_resolution_read = false;
64 node = helios.child("ground_resolution");
65 if (node.empty()) {
66 std::cout << "WARNING: No value given for 'ground_resolution'. Using default value of " << ground_resolution << std::endl;
67 } else {
68
69 const char *ground_resolution_str = node.child_value();
70 if (!parse_int2(ground_resolution_str, ground_resolution)) {
71 helios_runtime_error("ERROR: Value given for 'ground_resolution' could not be parsed.");
72 } else if (ground_resolution.x <= 0 || ground_resolution.y <= 0) {
73 helios_runtime_error("ERROR: Value given for 'ground_resolution' must be greater than 0.");
74 } else {
75 ground_resolution_read = true;
76 }
77 }
78
79 RGBcolor ground_color;
80 bool ground_color_read = false;
81 node = helios.child("ground_color");
82 if (!node.empty()) {
83
84 const char *ground_color_str = node.child_value();
85 if (!parse_RGBcolor(ground_color_str, ground_color)) {
86 helios_runtime_error("ERROR: Value given for 'ground_color' could not be parsed.");
87 } else {
88 ground_color_read = true;
89 }
90 }
91
92 std::string ground_texture_file;
93 node = helios.child("ground_texture_file");
94 if (!node.empty()) {
95
96 const char *ground_texture_file_str = node.child_value();
97 ground_texture_file = trim_whitespace(std::string(ground_texture_file_str));
98
99 if (ground_texture_file.empty()) {
100 helios_runtime_error("ERROR: Value given for 'ground_texture_file' is empty.");
101 }
102 }
103
104 std::string ground_model_file;
105 node = helios.child("ground_model_obj_file");
106 if (!node.empty()) {
107
108 const char *ground_model_file_str = node.child_value();
109 ground_model_file = trim_whitespace(std::string(ground_model_file_str));
110
111 std::string ext = getFileExtension(ground_model_file);
112
113 if (ground_model_file.empty()) {
114 helios_runtime_error("ERROR: Value given for 'ground_model_obj_file' is empty.");
115 } else if (ext != ".obj" && ext != ".OBJ") {
116 helios_runtime_error("ERROR: File given for 'ground_model_obj_file' is not an .obj file.");
117 } else if (!std::filesystem::exists(ground_model_file)) {
118 helios_runtime_error("ERROR: File given for 'ground_model_obj_file' does not exist.");
119 }
120 }
121
122 if (!ground_model_file.empty()) {
123 if (domain_extent_read) {
124 std::cout << "WARNING: Both 'domain_extent' and 'ground_model_obj_file' were defined. The domain extent will not be used because it is determined by the geometry in the ground model file." << std::endl;
125 } else if (domain_origin_read) {
126 std::cout << "WARNING: Both 'domain_origin' and 'ground_model_obj_file' were defined. The domain origin will not be used because it is determined by the geometry in the ground model file." << std::endl;
127 } else if (ground_resolution_read) {
128 std::cout << "WARNING: Both 'ground_resolution' and 'ground_model_obj_file' were defined. The ground resolution will not be used because it is determined by the geometry in the ground model file." << std::endl;
129 } else if (!ground_texture_file.empty()) {
130 std::cout << "WARNING: Both 'ground_texture_file' and 'ground_model_obj_file' were defined. The ground texture file will not be used because all ground geometry comes from the ground model file." << std::endl;
131 } else if (ground_color_read) {
132 std::cout << "WARNING: Both 'ground_color' and 'ground_model_obj_file' were defined. The ground color will not be used because it is determined by the geometry in the ground model file." << std::endl;
133 }
134 } else if (ground_color_read && !ground_texture_file.empty()) {
135 std::cout << "WARNING: Both 'ground_color' and 'ground_texture_file' were defined. The ground color will be overridden by the texture image." << std::endl;
136 }
137
138 uint ground_objID;
139 if (!ground_model_file.empty()) {
140 ground_UUIDs = context_ptr->loadOBJ(ground_model_file.c_str());
141 ground_objID = context_ptr->addPolymeshObject(ground_UUIDs);
142 } else if (!ground_texture_file.empty()) {
143 ground_objID = context_ptr->addTileObject(domain_origin, domain_extent, nullrotation, ground_resolution, ground_texture_file.c_str());
144 ground_UUIDs = context_ptr->getObjectPrimitiveUUIDs(ground_objID);
145 } else if (ground_color_read) {
146 ground_objID = context_ptr->addTileObject(domain_origin, domain_extent, nullrotation, ground_resolution, ground_color);
147 ground_UUIDs = context_ptr->getObjectPrimitiveUUIDs(ground_objID);
148 } else {
149 ground_objID = context_ptr->addTileObject(domain_origin, domain_extent, nullrotation, ground_resolution);
150 ground_UUIDs = context_ptr->getObjectPrimitiveUUIDs(ground_objID);
151 }
152
153 context_ptr->setPrimitiveData(ground_UUIDs, "twosided_flag", uint(0));
154
155 // ####### BUILDING THE CANOPY ####### //
156
157 for (pugi::xml_node p = helios.child("canopy_block"); p; p = p.next_sibling("canopy_block")) {
158
159 std::string canopy_model_file;
160 node = p.child("canopy_model_obj_file");
161 if (!node.empty()) {
162
163 const char *canopy_model_file_str = node.child_value();
164 canopy_model_file = trim_whitespace(std::string(canopy_model_file_str));
165
166 std::string ext = getFileExtension(canopy_model_file);
167
168 if (canopy_model_file.empty()) {
169 helios_runtime_error("ERROR: Value given for 'canopy_model_obj_file' is empty.");
170 } else if (ext != ".obj" && ext != ".OBJ") {
171 helios_runtime_error("ERROR: File given for 'canopy_model_obj_file' is not an .obj file.");
172 } else if (!std::filesystem::exists(ground_model_file)) {
173 helios_runtime_error("ERROR: File given for 'canopy_model_obj_file' does not exist.");
174 }
175 }
176
177 vec3 canopy_origin(0, 0, 0);
178 bool canopy_origin_read = false;
179 node = p.child("canopy_origin");
180 if (node.empty()) {
181 std::cout << "WARNING: No value given for 'canopy_origin'. Using default value of " << canopy_origin << std::endl;
182 } else {
183
184 const char *canopy_origin_str = node.child_value();
185 if (!parse_vec3(canopy_origin_str, canopy_origin)) {
186 helios_runtime_error("ERROR: Value given for 'canopy_origin' could not be parsed.");
187 } else {
188 canopy_origin_read = true;
189 }
190 }
191
192 int2 plant_count(1, 1);
193 bool plant_count_read = false;
194 node = p.child("plant_count");
195 if (node.empty()) {
196 std::cout << "WARNING: No value given for 'plant_count'. Using default value of " << plant_count << std::endl;
197 } else {
198
199 const char *plant_count_str = node.child_value();
200 if (!parse_int2(plant_count_str, plant_count)) {
201 helios_runtime_error("ERROR: Value given for 'plant_count' could not be parsed.");
202 } else if (plant_count.x < 1 || plant_count.y < 1) {
203 helios_runtime_error("ERROR: Value given for 'plant_count' must be greater than or equal to 1.");
204 } else {
205 plant_count_read = true;
206 }
207 }
208
209 vec2 plant_spacing(0.5, 0.5);
210 bool plant_spacing_read = false;
211 node = p.child("plant_spacing");
212 if (node.empty()) {
213 std::cout << "WARNING: No value given for 'plant_spacing'. Using default value of " << plant_spacing << std::endl;
214 } else {
215
216 const char *plant_spacing_str = node.child_value();
217 if (!parse_vec2(plant_spacing_str, plant_spacing)) {
218 helios_runtime_error("ERROR: Value given for 'plant_spacing' could not be parsed.");
219 } else if (plant_spacing.x <= 0 || plant_spacing.y <= 0) {
220 helios_runtime_error("ERROR: Value given for 'plant_spacing' must be greater than 0.");
221 } else {
222 plant_spacing_read = true;
223 }
224 }
225
226 std::string plant_library_name;
227 node = p.child("plant_library_name");
228 if (!node.empty()) {
229
230 const char *plant_library_name_str = node.child_value();
231 plant_library_name = trim_whitespace(std::string(plant_library_name_str));
232 }
233
234 float plant_age = 0;
235 bool plant_age_read = false;
236 node = p.child("plant_age");
237 if (node.empty()) {
238 std::cout << "WARNING: No value given for 'plant_age'. Using default value of " << plant_age << std::endl;
239 } else {
240
241 const char *plant_age_str = node.child_value();
242 if (!parse_float(plant_age_str, plant_age)) {
243 helios_runtime_error("ERROR: Value given for 'plant_age' could not be parsed.");
244 } else if (plant_age < 0) {
245 helios_runtime_error("ERROR: Value given for 'plant_age' must be greater than or equal to 0.");
246 } else {
247 plant_age_read = true;
248 }
249 }
250
251 float ground_clipping_height = 0;
252 bool ground_clipping_height_read = false;
253 node = p.child("ground_clipping_height");
254 if (!node.empty()) {
255
256 const char *ground_clipping_height_str = node.child_value();
257 if (!parse_float(ground_clipping_height_str, ground_clipping_height)) {
258 helios_runtime_error("ERROR: Value given for 'ground_clipping_height' could not be parsed.");
259 } else {
260 ground_clipping_height_read = true;
261 }
262 }
263
264
265 if (!canopy_model_file.empty()) { // canopy geometry will be read from OBJ file specified by 'canopy_model_obj_file'
266
267 if (plant_count_read) {
268 std::cout << "WARNING: Both 'plant_count' and 'ground_model_obj_file' were defined. The plant count value will not be used because it is determined by the geometry in the canopy model file." << std::endl;
269 } else if (plant_spacing_read) {
270 std::cout << "WARNING: Both 'plant_spacing' and 'ground_model_obj_file' were defined. The plant spacing value will not be used because it is determined by the geometry in the canopy model file." << std::endl;
271 }
272
273 std::vector<uint> canopy_UUIDs = context_ptr->loadOBJ(canopy_model_file.c_str());
274
275 std::vector<helios::vec3> curr_plant_locations{};
276 individual_plant_locations.push_back(curr_plant_locations);
277 // TODO: save canopy_UUIDs for individual canopies here
278 if (canopy_origin_read) {
279 context_ptr->translatePrimitive(canopy_UUIDs, canopy_origin);
280 }
281
282 leaf_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "leaf");
283 petiolule_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "petiolule");
284 petiole_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "petiole");
285 internode_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "internode");
286 peduncle_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "peduncle");
287 petal_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "petal");
288 sepal_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "sepal");
289 fruit_UUIDs = context_ptr->filterPrimitivesByData(canopy_UUIDs, "object_label", "fruit");
290
291 canopy_UUID_vector.push_back(canopy_UUIDs);
292
293 } else { // canopy geometry will be generated using the plant architecture plug-in
294
295 if (plant_library_name.empty()) {
296 helios_runtime_error("ERROR: No value given for 'plant_library_name'.");
297 }
298
299 plant_architecture_ptr->loadPlantModelFromLibrary(plant_library_name);
300
301 if (ground_clipping_height_read) {
302 plant_architecture_ptr->enableGroundClipping(ground_clipping_height);
303 }
304
305 std::vector<uint> canopy_UUIDs = plant_architecture_ptr->buildPlantCanopyFromLibrary(canopy_origin, plant_spacing, plant_count, plant_age);
306 std::vector<helios::vec3> curr_plant_locations = plant_architecture_ptr->getPlantBasePosition(canopy_UUIDs);
307 individual_plant_locations.push_back(curr_plant_locations);
308
309 leaf_UUIDs = plant_architecture_ptr->getAllLeafUUIDs();
310 internode_UUIDs = plant_architecture_ptr->getAllInternodeUUIDs();
311 petiole_UUIDs = plant_architecture_ptr->getAllPetioleUUIDs();
312 peduncle_UUIDs = plant_architecture_ptr->getAllPeduncleUUIDs();
313 std::vector<uint> flower_UUIDs = plant_architecture_ptr->getAllFlowerUUIDs();
314 petal_UUIDs = context_ptr->filterPrimitivesByData(flower_UUIDs, "object_label", "petal");
315 sepal_UUIDs = context_ptr->filterPrimitivesByData(flower_UUIDs, "object_label", "sepal");
316 if (petal_UUIDs.empty() && sepal_UUIDs.empty()) {
317 petal_UUIDs = flower_UUIDs;
318 sepal_UUIDs.clear();
319 }
320 fruit_UUIDs = plant_architecture_ptr->getAllFruitUUIDs();
321
322 canopy_UUID_vector.push_back(canopy_UUIDs);
323 }
324 }
325
326 // ####### CROP ALL GEOMETRY TO THE GROUND ####### //
327
328 uint primitive_count = context_ptr->getPrimitiveCount();
329
330 context_ptr->cropDomainX(domain_origin.x + make_vec2(-0.5f * domain_extent.x, 0.5f * domain_extent.x));
331 context_ptr->cropDomainY(domain_origin.y + make_vec2(-0.5f * domain_extent.y, 0.5f * domain_extent.y));
332
333 uint deleted_primitives = primitive_count - context_ptr->getPrimitiveCount();
334 if (deleted_primitives > 0) {
335 std::cout << "WARNING: " << deleted_primitives << " primitives were deleted because they were overhanging the ground." << std::endl;
336
337 context_ptr->cleanDeletedUUIDs(leaf_UUIDs);
338 context_ptr->cleanDeletedUUIDs(petiolule_UUIDs);
339 context_ptr->cleanDeletedUUIDs(petiole_UUIDs);
340 context_ptr->cleanDeletedUUIDs(internode_UUIDs);
341 context_ptr->cleanDeletedUUIDs(peduncle_UUIDs);
342 context_ptr->cleanDeletedUUIDs(petal_UUIDs);
343 context_ptr->cleanDeletedUUIDs(sepal_UUIDs);
344 context_ptr->cleanDeletedUUIDs(fruit_UUIDs);
345 context_ptr->cleanDeletedUUIDs(ground_UUIDs);
346 }
347
348 // ####### SET UUID GLOBAL DATA ####### //
349
350 context_ptr->setGlobalData("ground_UUIDs", ground_UUIDs);
351 context_ptr->setGlobalData("leaf_UUIDs", leaf_UUIDs);
352 context_ptr->setGlobalData("petiolule_UUIDs", petiolule_UUIDs);
353 context_ptr->setGlobalData("petiole_UUIDs", petiole_UUIDs);
354 context_ptr->setGlobalData("internode_UUIDs", internode_UUIDs);
355 context_ptr->setGlobalData("peduncle_UUIDs", peduncle_UUIDs);
356 context_ptr->setGlobalData("petal_UUIDs", petal_UUIDs);
357 context_ptr->setGlobalData("sepal_UUIDs", sepal_UUIDs);
358 context_ptr->setGlobalData("fruit_UUIDs", fruit_UUIDs);
359}