1.3.64
 
Loading...
Searching...
No Matches
Assets.cpp
Go to the documentation of this file.
1
16#include "PlantArchitecture.h"
17#include "global.h"
18
19using namespace helios;
20
21uint GenericLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
22
23 // If OBJ model file is specified, load it and return the object ID
24 if (!prototype_parameters->OBJ_model_file.empty()) {
25 // Resolve OBJ file path (allows users to specify simple paths like "MyLeaf.obj")
26 std::string resolved_obj = PlantArchitecture::resolveTextureFile(prototype_parameters->OBJ_model_file);
27 return context_ptr->addPolymeshObject(context_ptr->loadOBJ(resolved_obj.c_str(), prototype_parameters->leaf_offset, 0, nullrotation, RGB::black, "ZUP", true));
28 }
29
30 std::string leaf_texture;
31 if (prototype_parameters->leaf_texture_file.empty()) {
32 helios_runtime_error("ERROR (PlantArchitecture): Leaf prototype texture file was not specified.");
33 } else if (prototype_parameters->leaf_texture_file.size() == 1) {
34 leaf_texture = prototype_parameters->leaf_texture_file.begin()->second;
35 } else if (prototype_parameters->leaf_texture_file.find(compound_leaf_index) == prototype_parameters->leaf_texture_file.end()) {
36 helios_runtime_error("ERROR (PlantArchitecture): Leaf prototype texture file for compound leaf index " + std::to_string(compound_leaf_index) + " was not found.");
37 } else {
38 leaf_texture = prototype_parameters->leaf_texture_file[compound_leaf_index];
39 }
40
41 // Resolve leaf texture path (allows users to specify simple paths like "AlmondLeaf.png")
42 leaf_texture = PlantArchitecture::resolveTextureFile(leaf_texture);
43
44 // -- main leaf generation code -- //
45
46 std::vector<uint> UUIDs;
47
48 uint Nx = prototype_parameters->subdivisions; // number of leaf subdivisions in the x-direction (longitudinal)
49 uint Ny = ceil(prototype_parameters->leaf_aspect_ratio.val() * float(Nx)); // number of leaf subdivisions in the y-direction (lateral)
50
51 if (Ny % 2 != 0) { // Ny must be even
52 Ny = Ny + 1;
53 }
54
55 const float dx = 1.f / float(Nx); // length of leaf subdivision in the x-direction
56 const float dy = prototype_parameters->leaf_aspect_ratio.val() / float(Ny); // length of leaf subdivision in the y-direction
57
58 std::vector<std::vector<vec3>> vertices;
59 resize_vector(vertices, Nx + 1, Ny + 1);
60
61 for (int j = 0; j <= Ny; j++) {
62 float dtheta = 0;
63 for (int i = 0; i <= Nx; i++) {
64
65 const float x = float(i) * dx; // x-coordinate of leaf subdivision
66 const float y = float(j) * dy - 0.5f * prototype_parameters->leaf_aspect_ratio.val(); // y-coordinate of leaf subdivision
67
68 // midrib leaf folding
69 const float y_fold = cosf(0.5f * prototype_parameters->midrib_fold_fraction.val() * M_PI) * y;
70 const float z_fold = sinf(0.5f * prototype_parameters->midrib_fold_fraction.val() * M_PI) * fabs(y);
71
72 // x-curvature
73 float z_xcurve = prototype_parameters->longitudinal_curvature.val() * powf(x, 4);
74
75 // y-curvature
76 float z_ycurve = prototype_parameters->lateral_curvature.val() * powf(y / prototype_parameters->leaf_aspect_ratio.val(), 4);
77
78 // petiole roll
79 float z_petiole = 0;
80 if (prototype_parameters->petiole_roll.val() != 0.0f) {
81 z_petiole = fmin(0.1f, prototype_parameters->petiole_roll.val() * powf(7.f * y / prototype_parameters->leaf_aspect_ratio.val(), 4) * exp(-70.f * (x))) -
82 0.01 * prototype_parameters->petiole_roll.val() / fabs(prototype_parameters->petiole_roll.val());
83 }
84
85 // vertical displacement for leaf wave at each of the four subdivision vertices
86 float z_wave = 0;
87 if (prototype_parameters->wave_period.val() > 0.0f && prototype_parameters->wave_amplitude.val() > 0.0f) {
88 z_wave = (2.f * fabs(y) * prototype_parameters->wave_amplitude.val() * sinf((x + 0.5f * float(j >= 0.5 * Ny)) * M_PI / prototype_parameters->wave_period.val()));
89 }
90
91 vertices.at(j).at(i) = make_vec3(x, y_fold, z_fold + z_ycurve + z_wave + z_petiole);
92
93 if (prototype_parameters->longitudinal_curvature.val() != 0.0f && i > 0) {
94 dtheta -= atan(4.f * prototype_parameters->longitudinal_curvature.val() * powf(x, 3) * dx);
95 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), nullorigin, make_vec3(0, 1, 0), dtheta);
96 }
97
98 if (prototype_parameters->leaf_buckle_angle.val() > 0) {
99 const float xf = prototype_parameters->leaf_buckle_length.val();
100 if (x <= prototype_parameters->leaf_buckle_length.val() && x + dx > prototype_parameters->leaf_buckle_length.val()) {
101 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), make_vec3(xf, 0, 0), make_vec3(0, 1, 0), 0.5f * deg2rad(prototype_parameters->leaf_buckle_angle.val()));
102 } else if (x + dx > prototype_parameters->leaf_buckle_length.val()) {
103 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), make_vec3(xf, 0, 0), make_vec3(0, 1, 0), deg2rad(prototype_parameters->leaf_buckle_angle.val()));
104 }
105 }
106 }
107 }
108
109 for (int j = 0; j < Ny; j++) {
110 for (int i = 0; i < Nx; i++) {
111
112 const float x = float(i) * dx;
113 const float y = float(j) * dy - 0.5f * prototype_parameters->leaf_aspect_ratio.val();
114 vec2 uv0(x, (y + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
115 vec2 uv1(x + dx, (y + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
116 vec2 uv2(x + dx, (y + dy + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
117 vec2 uv3(x, (y + dy + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
118
119 vec3 v0 = vertices.at(j).at(i);
120 vec3 v1 = vertices.at(j).at(i + 1);
121 vec3 v2 = vertices.at(j + 1).at(i + 1);
122 vec3 v3 = vertices.at(j + 1).at(i);
123
124 // Add triangle 1 and check if it has effective area (including texture transparency)
125 uint uuid1 = context_ptr->addTriangle(v0, v1, v2, leaf_texture.c_str(), uv0, uv1, uv2);
126 if (context_ptr->getPrimitiveArea(uuid1) > 0) {
127 UUIDs.push_back(uuid1);
128 } else {
129 context_ptr->deletePrimitive(uuid1);
130 }
131
132 // Add triangle 2 and check if it has effective area (including texture transparency)
133 uint uuid2 = context_ptr->addTriangle(v0, v2, v3, leaf_texture.c_str(), uv0, uv2, uv3);
134 if (context_ptr->getPrimitiveArea(uuid2) > 0) {
135 UUIDs.push_back(uuid2);
136 } else {
137 context_ptr->deletePrimitive(uuid2);
138 }
139 }
140 }
141
142 context_ptr->translatePrimitive(UUIDs, prototype_parameters->leaf_offset);
143
144 if (prototype_parameters->build_petiolule) {
145 std::vector<uint> UUIDs_petiolule = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/PetiolulePrototype.obj").string().c_str(), make_vec3(0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
146 context_ptr->translatePrimitive(UUIDs, make_vec3(0.07, 0, 0.005));
147 UUIDs.insert(UUIDs.end(), UUIDs_petiolule.begin(), UUIDs_petiolule.end());
148 }
149
150 prototype_parameters->leaf_aspect_ratio.resample();
151 prototype_parameters->midrib_fold_fraction.resample();
152 prototype_parameters->longitudinal_curvature.resample();
153 prototype_parameters->lateral_curvature.resample();
154 prototype_parameters->petiole_roll.resample();
155 prototype_parameters->wave_period.resample();
156 prototype_parameters->wave_amplitude.resample();
157 prototype_parameters->leaf_buckle_length.resample();
158 prototype_parameters->leaf_buckle_angle.resample();
159
160 return context_ptr->addPolymeshObject(UUIDs);
161}
162
163uint GeneralSphericalFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
164 return context_ptr->addSphereObject(5, make_vec3(0.5f, 0, 0), 0.5f, RGB::red);
165}
166
167uint AlmondFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
168 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/AlmondHull.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
169 uint objID = context_ptr->addPolymeshObject(UUIDs);
170 return objID;
171}
172
173uint AlmondFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
174 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/AlmondFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
175 uint objID = context_ptr->addPolymeshObject(UUIDs);
176 return objID;
177}
178
179void AlmondPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
180
181 if (phytomer->internode_length_max < 0.01) { // spurs
182 phytomer->setInternodeMaxRadius(0.005);
183 phytomer->setVegetativeBudState(BUD_DEAD);
184 phytomer->scaleLeafPrototypeScale(0.8);
185 phytomer->setFloralBudState(BUD_DEAD);
186 phytomer->parent_shoot_ptr->shoot_parameters.max_nodes_per_season = 7;
187 }
188
189 // blind nodes
190 // if( shoot_node_index<3 ){
191 // phytomer->setVegetativeBudState( BUD_DEAD );
192 // phytomer->setFloralBudState( BUD_DEAD );
193 // }
194}
195
196void AlmondPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
197}
198
199uint AppleFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
200 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/AppleFruit.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
201 uint objID = context_ptr->addPolymeshObject(UUIDs);
202 return objID;
203}
204
205uint AppleFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
206 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/AlmondFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
207 uint objID = context_ptr->addPolymeshObject(UUIDs);
208 return objID;
209}
210
211void ApplePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
212
213 if (phytomer->internode_length_max < 0.01) { // spurs
214 phytomer->setInternodeMaxRadius(0.005);
215 phytomer->setVegetativeBudState(BUD_DEAD);
216 phytomer->scaleLeafPrototypeScale(0.8);
217 phytomer->setFloralBudState(BUD_DEAD);
218 phytomer->parent_shoot_ptr->shoot_parameters.max_nodes_per_season = 6;
219 }
220}
221
222void ApplePhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
223}
224
225uint AsparagusLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
226
227 float curve_magnitude = context_ptr->randu(0.f, 0.2f);
228
229 std::vector<vec3> nodes;
230 nodes.push_back(make_vec3(0, 0, 0));
231 nodes.push_back(make_vec3(context_ptr->randu(0.4f, 0.7f), 0, -0.25f * curve_magnitude));
232 nodes.push_back(make_vec3(0.95, 0, -0.9f * curve_magnitude));
233 nodes.push_back(make_vec3(1, 0, -curve_magnitude));
234
235 std::vector<float> radius;
236 radius.push_back(0.015);
237 radius.push_back(0.015);
238 radius.push_back(0.015);
239 radius.push_back(0.0);
240
241 std::vector<RGBcolor> colors;
242 colors.push_back(RGB::forestgreen);
243 colors.push_back(RGB::forestgreen);
244 colors.push_back(RGB::forestgreen);
245 colors.push_back(RGB::forestgreen);
246
247 uint objID = context_ptr->addTubeObject(8, nodes, radius, colors);
248 context_ptr->rotateObject(objID, context_ptr->randu(0, 2.f * M_PI), "x");
249 return objID;
250}
251
252void AsparagusPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
253
254 // blind nodes
255 if (shoot_node_index <= 2) {
256 phytomer->scaleLeafPrototypeScale(0.6);
257 phytomer->setVegetativeBudState(BUD_DEAD);
258 }
259}
260
261uint BeanLeafPrototype_unifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
262 std::vector<uint> UUIDs;
263 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanLeaf_unifoliate.obj").string().c_str(), true);
264
265 uint objID = context_ptr->addPolymeshObject(UUIDs);
266 return objID;
267}
268
269uint BeanLeafPrototype_trifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
270 std::vector<uint> UUIDs;
271 if (compound_leaf_index == 0) {
272 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanLeaf_tip.obj").string().c_str(), true);
273 } else if (compound_leaf_index < 0) {
274 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanLeaf_left.obj").string().c_str(), true);
275 } else {
276 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanLeaf_right.obj").string().c_str(), true);
277 }
278 uint objID = context_ptr->addPolymeshObject(UUIDs);
279 return objID;
280}
281
282uint BeanFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
283 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanPod.obj").string().c_str(), true);
284 uint objID = context_ptr->addPolymeshObject(UUIDs);
285 return objID;
286}
287
288uint BeanFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
289 std::vector<uint> UUIDs;
290 if (flower_is_open) {
291 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanFlower_open_white.obj").string().c_str(), true);
292 } else {
293 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanFlower_closed_white.obj").string().c_str(), true);
294 }
295 uint objID = context_ptr->addPolymeshObject(UUIDs);
296 return objID;
297}
298
299void BeanPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
300
301 if (shoot_node_index > 5 || phytomer->rank > 1) {
302 phytomer->setVegetativeBudState(BUD_DEAD);
303 } else {
304 phytomer->setFloralBudState(BUD_DEAD);
305 }
306
307 // set leaf and internode scale based on position along the shoot
308 float leaf_scale = fmin(1.f, 0.6 + 0.4 * plant_age / 8.f);
309 phytomer->scaleLeafPrototypeScale(leaf_scale);
310
311 // set internode length based on position along the shoot
312 if (phytomer->rank == 0) {
313 float inode_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 10.f);
314 phytomer->scaleInternodeMaxLength(inode_scale);
315 }
316}
317
318uint BindweedFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
319 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BindweedFlower.obj").string().c_str(), true);
320 uint objID = context_ptr->addPolymeshObject(UUIDs);
321 return objID;
322}
323
324uint BougainvilleaFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
325 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BougainvilleaFlower.obj").string().c_str(), true);
326 uint objID = context_ptr->addPolymeshObject(UUIDs);
327 return objID;
328}
329
330uint CapsicumFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
331 std::string OBJ_file;
332 if (context_ptr->randn() < 0.4) {
333 OBJ_file = helios::resolvePluginAsset("plantarchitecture", "assets/obj/CapsicumFruit_green.obj").string().c_str();
334 } else {
335 OBJ_file = helios::resolvePluginAsset("plantarchitecture", "assets/obj/CapsicumFruit_red.obj").string().c_str();
336 }
337
338 std::vector<uint> UUIDs = context_ptr->loadOBJ(OBJ_file.c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
339 return context_ptr->addPolymeshObject(UUIDs);
340}
341
342void CapsicumPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
343
344 if (shoot_node_index < 6 && phytomer->rank == 0) {
345 phytomer->setVegetativeBudState(BUD_DEAD);
346 phytomer->setFloralBudState(BUD_DEAD);
347 phytomer->removeLeaf();
348 }
349
350 if (phytomer->rank >= 2) {
351 phytomer->setVegetativeBudState(BUD_DEAD);
352 phytomer->setFloralBudState(BUD_DEAD);
353 }
354
355 // set leaf and internode scale based on position along the shoot
356 float leaf_scale = std::min(1.f, 0.6f + 0.4f * shoot_node_index / 5.f);
357 phytomer->scaleLeafPrototypeScale(leaf_scale);
358
359 // set internode length based on position along the shoot
360 if (phytomer->rank == 0) {
361 float inode_scale = std::min(1.f, 0.05f + 0.95f * plant_age / 15.f);
362 phytomer->scaleInternodeMaxLength(inode_scale);
363 }
364}
365
366uint CheeseweedLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
367 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CheeseweedLeaf.obj").string().c_str(), true);
368 uint objID = context_ptr->addPolymeshObject(UUIDs);
369 return objID;
370}
371
372uint CowpeaLeafPrototype_unifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
373 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaLeaf_unifoliate.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
374
375 uint objID = context_ptr->addPolymeshObject(UUIDs);
376 return objID;
377}
378
379uint CowpeaLeafPrototype_trifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
380 std::vector<uint> UUIDs;
381 if (compound_leaf_index < 0) {
382 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaLeaf_left_highres.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
383 } else if (compound_leaf_index == 0) {
384 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaLeaf_tip_highres.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
385 } else {
386 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaLeaf_right_highres.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
387 }
388 uint objID = context_ptr->addPolymeshObject(UUIDs);
389 return objID;
390}
391
392uint CowpeaFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
393 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaPod.obj").string().c_str(), make_vec3(0., 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
394 uint objID = context_ptr->addPolymeshObject(UUIDs);
395 return objID;
396}
397
398uint CowpeaFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
399 std::vector<uint> UUIDs;
400 if (flower_is_open) {
401 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaFlower_open_yellow.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
402 } else {
403 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/CowpeaFlower_closed_yellow.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
404 }
405 uint objID = context_ptr->addPolymeshObject(UUIDs);
406 return objID;
407}
408
409void CowpeaPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
410
411 if (shoot_node_index > 5 || phytomer->rank > 1) {
412 phytomer->setVegetativeBudState(BUD_DEAD);
413 } else {
414 phytomer->setFloralBudState(BUD_DEAD);
415 }
416
417 // set leaf and internode scale based on position along the shoot
418 float leaf_scale = fmin(1.f, 0.6 + 0.4 * plant_age / 8.f);
419 phytomer->scaleLeafPrototypeScale(leaf_scale);
420
421 // set internode length based on position along the shoot
422 if (phytomer->rank == 0) {
423 float inode_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 10.f);
424 phytomer->scaleInternodeMaxLength(inode_scale);
425 }
426}
427
428// Function to generate random float between min and max
429float random_float(float min, float max) {
430 return min + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX / (max - min)));
431}
432
433// Function to check if two spheres overlap
434bool spheres_overlap(const helios::vec3 &center1, float radius1, const helios::vec3 &center2, float radius2) {
435 float distance = std::sqrt(std::pow(center1.x - center2.x, 2) + std::pow(center1.y - center2.y, 2) + std::pow(center1.z - center2.z, 2));
436 return distance < (radius1 + radius2);
437}
438
439uint GrapevineFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
440
441 int num_grapes = 60;
442 float height = 5.0f; // Height of the cluster
443 float base_radius = 2.f; // Base radius of the cluster
444 float taper_factor = 0.6f; // Taper factor (higher means more taper)
445 float grape_radius = 0.25f; // Fixed radius for each grape
446
447 std::vector<std::pair<helios::vec3, float>> grapes;
448 float z_step = height / num_grapes;
449
450 // Place the first grape at the bottom center
451 helios::vec3 first_center(0.0f, 0.0f, 0.0f);
452 grapes.push_back({first_center, grape_radius});
453
454 // Attempt to place each subsequent grape close to an existing grape
455 int max_attempts = 100; // Number of retries to find a tight fit
456
457 for (int i = 1; i < num_grapes; ++i) {
458 float z = i * z_step;
459 // Tapered radius based on height (denser at the top, sparser at the bottom)
460 float taper_radius = base_radius * (1.0f - taper_factor * (z / height));
461
462 bool placed = false;
463 int attempts = 0;
464 while (!placed && attempts < max_attempts) {
465 // Randomly select an existing grape as the reference point
466 int reference_idx = rand() % grapes.size();
467 const helios::vec3 &reference_center = grapes[reference_idx].first;
468
469 // Pick a random offset direction from the reference grape
470 float angle = random_float(0, 2 * M_PI);
471 float distance = random_float(1.2 * grape_radius, 1.3 * grape_radius); // Keep grapes close but not overlapping
472
473 // Compute the new potential center for the grape
474 helios::vec3 new_center(reference_center.x + distance * cos(angle), reference_center.y + distance * sin(angle), random_float(z - 0.5f * z_step, z + 0.5f * z_step));
475
476 // Check that the new center is within the allowable radius (for tapering)
477 float new_center_distance = std::sqrt(new_center.x * new_center.x + new_center.y * new_center.y);
478 if (new_center_distance > taper_radius) {
479 attempts++;
480 continue; // Skip if the new position exceeds the tapered radius
481 }
482
483 // Check for collisions with existing grapes
484 bool collision = false;
485 for (const auto &grape: grapes) {
486 if (spheres_overlap(new_center, grape_radius, grape.first, grape.second)) {
487 collision = true;
488 break;
489 }
490 }
491
492 // If no collision, place the grape
493 if (!collision) {
494 grapes.push_back({new_center, grape_radius});
495 placed = true;
496 }
497
498 attempts++;
499 }
500 }
501
502 std::vector<uint> UUIDs;
503 for (const auto &grape: grapes) {
504 // std::vector<uint> UUIDs_tmp = context_ptr->addSphere( 10, grape.first, grape.second, "../../../plugins/plantarchitecture/assets/textures/GrapeBerry.jpg" );
505 std::vector<uint> UUIDs_tmp = context_ptr->addSphere(10, grape.first, grape.second, make_RGBcolor(0.053, 0.076, 0.098));
506 UUIDs.insert(UUIDs.end(), UUIDs_tmp.begin(), UUIDs_tmp.end());
507 }
508
509 context_ptr->rotatePrimitive(UUIDs, 0.5 * M_PI, "y");
510
511 context_ptr->setPrimitiveData(UUIDs, "object_label", "fruit");
512
513 uint objID = context_ptr->addPolymeshObject(UUIDs);
514 return objID;
515}
516
517// uint GrapevineFlowerPrototype( helios::Context* context_ptr, uint subdivisions, bool flower_is_open ){
518// std::vector<uint> UUIDs = context_ptr->loadOBJ( helios::resolvePluginAsset("plantarchitecture", "assets/obj/OliveFlower_open.obj").string().c_str(), make_vec3(0.0,0,0), 0,nullrotation, RGB::black, "ZUP", true );
519// uint objID = context_ptr->addPolymeshObject( UUIDs );
520// return objID;
521// }
522
523void GrapevinePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
524
525 // blind nodes
526 if (shoot_node_index >= 2) {
527 phytomer->setFloralBudState(BUD_DEAD);
528 }
529 if (phytomer->rank >= 1 && shoot_node_index >= 8) {
530 phytomer->setVegetativeBudState(BUD_DEAD);
531 }
532 if (phytomer->rank >= 2) {
533 phytomer->setVegetativeBudState(BUD_DEAD);
534 phytomer->setFloralBudState(BUD_DEAD);
535 }
536}
537
538// void GrapevinePhytomerCallbackFunction( std::shared_ptr<Phytomer> phytomer ){
539//
540// if( phytomer->isdormant ){
541// if( phytomer->shoot_index.x >= phytomer->shoot_index.y-1 ){
542// phytomer->setVegetativeBudState( BUD_DORMANT ); //first vegetative buds always break
543// }
544// if( phytomer->shoot_index.x <= phytomer->shoot_index.y-4 ){
545// phytomer->setFloralBudState( BUD_DORMANT ); //first vegetative buds always break
546// }
547// }
548//
549// }
550
551uint MaizeTasselPrototype(helios::Context *context_ptr, uint subdivisions) {
552
553 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/MaizeTassel.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
554 return context_ptr->addPolymeshObject(UUIDs);
555}
556
557uint MaizeEarPrototype(helios::Context *context_ptr, uint subdivisions) {
558
559 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/MaizeEar.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
560 return context_ptr->addPolymeshObject(UUIDs);
561}
562
563void MaizePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
564
565 // set leaf scale based on position along the shoot
566 float scale = 1.f;
567 if (shoot_node_index <= 5) {
568 scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
569 phytomer->scaleInternodeMaxLength(scale);
570 } else if (shoot_node_index >= phytomer->shoot_index.z - 5) {
571 scale = fmin(1.f, 0.65 + 0.35 * float(phytomer->shoot_index.z - shoot_node_index) / 3.f);
572 }
573
574 phytomer->scaleLeafPrototypeScale(scale);
575
576 if (shoot_node_index > 8 && shoot_node_index < 12) {
577 phytomer->phytomer_parameters.inflorescence.flowers_per_peduncle = 1;
578 phytomer->phytomer_parameters.inflorescence.fruit_prototype_function = MaizeEarPrototype;
579 phytomer->phytomer_parameters.inflorescence.fruit_prototype_scale = 0.2;
580 phytomer->phytomer_parameters.peduncle.length = 0.05f;
581 phytomer->phytomer_parameters.peduncle.radius = 0.01;
582 phytomer->phytomer_parameters.peduncle.pitch = 5;
583 phytomer->setFloralBudState(BUD_ACTIVE);
584 } else {
585 phytomer->phytomer_parameters.inflorescence.fruit_prototype_function = MaizeTasselPrototype;
586 phytomer->setFloralBudState(BUD_DEAD);
587 }
588
589 // phytomer->setFloralBudState( BUD_DEAD );
590}
591
592uint OliveLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
593
594 std::vector<uint> UUIDs_upper = context_ptr->addTile(make_vec3(0.5, 0, 0), make_vec2(1, 0.2), nullrotation, make_int2(prototype_parameters->subdivisions, prototype_parameters->subdivisions),
595 helios::resolvePluginAsset("plantarchitecture", "assets/textures/OliveLeaf_upper.png").string().c_str());
596 std::vector<uint> UUIDs_lower = context_ptr->addTile(make_vec3(0.5, 0, -1e-4), make_vec2(1, 0.2), nullrotation, make_int2(prototype_parameters->subdivisions, prototype_parameters->subdivisions),
597 helios::resolvePluginAsset("plantarchitecture", "assets/textures/OliveLeaf_lower.png").string().c_str());
598 context_ptr->rotatePrimitive(UUIDs_lower, M_PI, "x");
599
600 UUIDs_upper.insert(UUIDs_upper.end(), UUIDs_lower.begin(), UUIDs_lower.end());
601 uint objID = context_ptr->addPolymeshObject(UUIDs_upper);
602 return objID;
603}
604
605uint OliveFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
606 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/OliveFruit.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
607 uint objID = context_ptr->addPolymeshObject(UUIDs);
608 std::vector<uint> UUIDs_fruit = context_ptr->filterPrimitivesByData(context_ptr->getObjectPrimitiveUUIDs(objID), "object_label", "fruit");
609 context_ptr->setPrimitiveColor(UUIDs_fruit, make_RGBcolor(0.65, 0.7, 0.4)); // green
610 return objID;
611}
612
613uint OliveFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
614 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/OliveFlower_open.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
615 uint objID = context_ptr->addPolymeshObject(UUIDs);
616 return objID;
617}
618
619void OlivePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
620}
621
622void OlivePhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
623
624 if (phytomer->isdormant) {
625 if (phytomer->shoot_index.x < phytomer->shoot_index.y - 8) {
626 phytomer->setFloralBudState(BUD_DEAD);
627 }
628 }
629}
630
631uint PistachioFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
632 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/PistachioNut.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
633 uint objID = context_ptr->addPolymeshObject(UUIDs);
634 return objID;
635}
636
637uint PistachioFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
638 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/OliveFlower_open.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
639 uint objID = context_ptr->addPolymeshObject(UUIDs);
640 return objID;
641}
642
643void PistachioPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
644
645 // blind nodes
646 if (shoot_node_index == 0) {
647 phytomer->setVegetativeBudState(BUD_DEAD);
648 phytomer->setFloralBudState(BUD_DEAD);
649 }
650}
651
652void PistachioPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
653
654 if (phytomer->isdormant) {
655 if (phytomer->shoot_index.x <= phytomer->shoot_index.y - 4) {
656 phytomer->setFloralBudState(BUD_DORMANT);
657 }
658 }
659}
660
661uint PuncturevineFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
662 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/PuncturevineFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
663 uint objID = context_ptr->addPolymeshObject(UUIDs);
664 return objID;
665}
666
667uint RedbudFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
668 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/RedbudFlower_open.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
669 return context_ptr->addPolymeshObject(UUIDs);
670}
671
672uint RedbudFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
673 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/RedbudPod.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
674 return context_ptr->addPolymeshObject(UUIDs);
675}
676
677void RedbudPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
678}
679
680void RedbudPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
681
682 // redbud has the shoot pattern that the first few nodes on the shoot are vegetative, then the rest are floral
683 if (phytomer->isdormant) {
684 int Nchild_shoots = randu(2, 4);
685 if (phytomer->shoot_index.x < phytomer->shoot_index.y - Nchild_shoots) {
686 phytomer->setVegetativeBudState(BUD_DEAD);
687 } else {
688 phytomer->setFloralBudState(BUD_DEAD);
689 }
690 }
691}
692
693uint RiceSpikePrototype(helios::Context *context_ptr, uint subdivisions) {
694 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/RiceGrain.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
695 uint objID = context_ptr->addPolymeshObject(UUIDs);
696 return objID;
697}
698
699void RicePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
700
701 // set leaf scale based on position along the shoot
702 float scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
703 phytomer->scaleLeafPrototypeScale(scale);
704
705 // set internode length based on position along the shoot
706 phytomer->scaleInternodeMaxLength(scale);
707}
708
709void ButterLettucePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
710
711 float fact = float(shoot_max_nodes - shoot_node_index) / float(shoot_max_nodes);
712
713 // set leaf scale based on position along the shoot
714 // float scale = fmin(1.f, 1 + 0.1*fact);
715 // phytomer->scaleLeafPrototypeScale(scale);
716
717 // phytomer->rotateLeaf( 0, 0, make_AxisRotation(-deg2rad(15)*fact, 0, 0));
718 phytomer->rotateLeaf(0, 0, make_AxisRotation(-deg2rad(60) * fact, 0, 0));
719}
720
721uint SorghumPaniclePrototype(helios::Context *context_ptr, uint subdivisions) {
722
723 if (subdivisions <= 1) {
724 subdivisions = 3;
725 }
726
727 float panicle_height = 1;
728 float panicle_width = 0.08;
729 float width_seed = 0.08;
730 float height_seed = 0.25;
731 float seed_tilt = 50;
732 subdivisions = 6;
733
734 std::string seed_texture_file = helios::resolvePluginAsset("plantarchitecture", "assets/textures/SorghumSeed.jpeg").string().c_str();
735 RGBcolor stem_color(0.45, 0.55, 0.42);
736
737 std::vector<uint> UUIDs;
738
739 panicle_height -= 0.8 * height_seed;
740
741 std::vector<vec3> nodes_panicle;
742 std::vector<float> radius_panicle;
743
744 for (int n = 0; n < subdivisions; n++) {
745 float x = 0;
746 float y = 0;
747 float z;
748 if (n == 0) {
749 z = 0.5f * height_seed / float(subdivisions - 1);
750 } else if (n == subdivisions - 1) {
751 z = (subdivisions - 1.5f) * height_seed / float(subdivisions - 1);
752 } else {
753 z = n * height_seed / float(subdivisions - 1);
754 }
755
756 float angle = float(n) * M_PI / float(subdivisions - 1);
757 float dr = std::fmax(0.f, 0.5f * width_seed * sin(angle));
758
759 nodes_panicle.push_back(make_vec3(x, y, z));
760 radius_panicle.push_back(dr);
761 }
762
763 std::vector<uint> UUIDs_seed_ptype = context_ptr->addTube(subdivisions, nodes_panicle, radius_panicle, seed_texture_file.c_str());
764
765 int Ntheta = ceil(6.f * panicle_height / height_seed);
766 int Nphi = ceil(2.f * M_PI * panicle_width / width_seed);
767
768 for (int j = 0; j < Nphi; j++) {
769 for (int i = 0; i < Ntheta; i++) {
770
771 if (i == 0 && j == 0) {
772 continue;
773 }
774
775 std::vector<uint> UUIDs_copy = context_ptr->copyPrimitive(UUIDs_seed_ptype);
776 context_ptr->scalePrimitive(UUIDs_copy, make_vec3(1, 1, 1) * context_ptr->randu(0.9f, 1.1f));
777
778 float phi = 2.f * M_PI * float(j + 0.5f * float(i % 2)) / float(Nphi);
779 float theta = acos(1 - 2 * float(i + float(j) / float(Nphi)) / float(Ntheta));
780 float x = sin(theta) * cos(phi);
781 float y = sin(theta) * sin(phi);
782 float z = 0.5f + 0.5f * cos(theta);
783
784 x *= 0.5f * panicle_width;
785 y *= 0.5f * panicle_width;
786 z *= panicle_height;
787
788 float tilt = -deg2rad(seed_tilt) * sqrtf(1.f - z / panicle_height);
789
790 context_ptr->rotatePrimitive(UUIDs_copy, tilt, "x");
791 context_ptr->rotatePrimitive(UUIDs_copy, phi - 0.5f * M_PI, "z");
792
793 context_ptr->translatePrimitive(UUIDs_copy, make_vec3(x, y, z));
794 UUIDs.insert(UUIDs.end(), UUIDs_copy.begin(), UUIDs_copy.end());
795 }
796 }
797
798 context_ptr->deletePrimitive(UUIDs_seed_ptype);
799
800 std::vector<uint> UUIDs_sphere = context_ptr->addSphere(10, make_vec3(0, 0, 0.5 * panicle_height), 0.5f, seed_texture_file.c_str());
801 context_ptr->scalePrimitiveAboutPoint(UUIDs_sphere, make_vec3(1.9 * panicle_width, 1.9 * panicle_width, 0.8 * panicle_height), make_vec3(0, 0, 0.5 * panicle_height));
802 UUIDs.insert(UUIDs.end(), UUIDs_sphere.begin(), UUIDs_sphere.end());
803
804 context_ptr->rotatePrimitive(UUIDs, 0.5f * M_PI, "y");
805 context_ptr->translatePrimitive(UUIDs, make_vec3(-0.2, 0, 0));
806
807 uint objID = context_ptr->addPolymeshObject(UUIDs);
808 return objID;
809}
810
811void SorghumPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
812
813 // set leaf scale based on position along the shoot
814 float scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
815 phytomer->scaleLeafPrototypeScale(scale);
816
817 // set internode length based on position along the shoot
818 phytomer->scaleInternodeMaxLength(scale);
819}
820
821uint SoybeanFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
822 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/SoybeanPod.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
823 uint objID = context_ptr->addPolymeshObject(UUIDs);
824 return objID;
825}
826
827uint SoybeanFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
828 std::vector<uint> UUIDs;
829 if (flower_is_open) {
830 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/SoybeanFlower_open_white.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
831 } else {
832 UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/BeanFlower_closed_white.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
833 }
834 uint objID = context_ptr->addPolymeshObject(UUIDs);
835 return objID;
836}
837
838void SoybeanPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
839
840 if (shoot_node_index > 5 || phytomer->rank > 1) {
841 phytomer->setVegetativeBudState(BUD_DEAD);
842 } else {
843 phytomer->setFloralBudState(BUD_DEAD);
844 }
845
846 // set leaf and internode scale based on position along the shoot
847 float leaf_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 15.f);
848 phytomer->scaleLeafPrototypeScale(leaf_scale);
849
850 // set internode length based on position along the shoot
851 float inode_scale = fmin(1.f, 0.1 + 0.9 * plant_age / 15.f);
852 phytomer->scaleInternodeMaxLength(inode_scale);
853}
854
855uint StrawberryFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
856 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/StrawberryFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
857 uint objID = context_ptr->addPolymeshObject(UUIDs);
858 return objID;
859}
860
861uint StrawberryFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
862 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/StrawberryFruit.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
863 uint objID = context_ptr->addPolymeshObject(UUIDs);
864 return objID;
865}
866
867uint TomatoFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
868 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/TomatoFruit.obj").string().c_str(), make_vec3(0., 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
869 uint objID = context_ptr->addPolymeshObject(UUIDs);
870 return objID;
871}
872
873uint TomatoFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
874 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/TomatoFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
875 uint objID = context_ptr->addPolymeshObject(UUIDs);
876 return objID;
877}
878
879void TomatoPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
880
881 if (shoot_node_index < 8 && phytomer->rank == 0) {
882 phytomer->setFloralBudState(BUD_DEAD);
883 }
884 if (phytomer->rank > 1) {
885 phytomer->setFloralBudState(BUD_DEAD);
886 phytomer->setVegetativeBudState(BUD_DEAD);
887 }
888
889 // set leaf and internode scale based on position along the shoot
890 float leaf_scale = fmin(1.f, 0.5 + 0.5 * plant_age / 10.f);
891 phytomer->scaleLeafPrototypeScale(leaf_scale);
892
893 // set internode length based on position along the shoot
894 float inode_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 10.f);
895 phytomer->scaleInternodeMaxLength(inode_scale);
896}
897
898void CherryTomatoPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
899
900 if (shoot_node_index < 8 || phytomer->rank > 1) {
901 phytomer->setFloralBudState(BUD_DEAD);
902 phytomer->setVegetativeBudState(BUD_DEAD);
903 }
904
905 // set leaf and internode scale based on position along the shoot
906 float leaf_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 15.f);
907 phytomer->scaleLeafPrototypeScale(leaf_scale);
908
909 // set internode length based on position along the shoot
910 float inode_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 10.f);
911 phytomer->scaleInternodeMaxLength(inode_scale);
912}
913
914void CherryTomatoPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
915
916 float pruning_height = 1.f;
917 float pruning_day = 101.f;
918
919 float plant_age = phytomer->parent_shoot_ptr->plantarchitecture_ptr->getPlantAge(phytomer->plantID);
920
921 if (phytomer->hasLeaf() && plant_age >= pruning_day) {
922 float height = phytomer->getInternodeNodePositions().at(0).z;
923 if (height < pruning_height) {
924 phytomer->removeLeaf();
925 }
926 }
927}
928
929uint WalnutFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
930 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/WalnutHull.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
931 uint objID = context_ptr->addPolymeshObject(UUIDs);
932 return objID;
933}
934
935uint WalnutFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
936 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/AlmondFlower.obj").string().c_str(), make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
937 uint objID = context_ptr->addPolymeshObject(UUIDs);
938 return objID;
939}
940
941void WalnutPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
942
943 // blind nodes
944 if (shoot_node_index < 4) {
945 phytomer->setVegetativeBudState(BUD_DEAD);
946 phytomer->setFloralBudState(BUD_DEAD);
947 }
948}
949
950void WalnutPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
951}
952
953uint WheatSpikePrototype(helios::Context *context_ptr, uint subdivisions) {
954 std::vector<uint> UUIDs = context_ptr->loadOBJ(helios::resolvePluginAsset("plantarchitecture", "assets/obj/WheatSpike.obj").string().c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
955 uint objID = context_ptr->addPolymeshObject(UUIDs);
956 return objID;
957}
958
959void WheatPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
960
961 // set leaf scale based on position along the shoot
962 float scale = std::fmin(1.f, 0.7f + 0.3f * float(shoot_node_index) / 5.f);
963 phytomer->scaleLeafPrototypeScale(scale);
964
965 // set internode length based on position along the shoot
966 phytomer->scaleInternodeMaxLength(scale);
967}