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