1.3.49
 
Loading...
Searching...
No Matches
grapevine.cpp
Go to the documentation of this file.
1
16#include "CanopyGenerator.h"
17
18using namespace helios;
19using namespace std;
20
21std::vector<std::vector<uint>> CanopyGenerator::addGrapeCluster(vec3 position, float grape_rad, float cluster_rad, helios::RGBcolor grape_color, uint grape_subdiv) {
22
23 std::uniform_real_distribution<float> unif_distribution;
24
25 float z = position.z;
26
27 float theta = 0;
28 float RB = cluster_rad;
29 int N = 100;
30 int count = 0;
31 int max_grape_levels = 13.f - 4.f * unif_distribution(generator);
32
33 vec3 xgrape;
34
35 std::vector<std::vector<uint>> UUID;
36
37 while ((N > 3 || count < 3) && count < max_grape_levels) {
38
39 if (count < 3) {
40 RB = cluster_rad * powf(0.5f, float(2 - count));
41 } else if (count == 3) {
42 RB = cluster_rad;
43 }
44
45 N = floor(PI_F * RB / grape_rad) + 1;
46
47 float dtheta = 2.f * PI_F / float(N);
48 for (int j = 0; j < N; j++) {
49
50 xgrape.x = position.x + RB * sin(theta) + unif_distribution(generator) * 0.25f * grape_rad;
51 xgrape.y = position.y + RB * cos(theta) + unif_distribution(generator) * 0.25f * grape_rad;
52 xgrape.z = z + unif_distribution(generator) * 0.25f * grape_rad;
53
54 uint objID = context->addSphereObject(fmax(2.f, float(grape_subdiv)), xgrape, grape_rad, grape_color);
55 std::vector<uint> U = context->getObjectPrimitiveUUIDs(objID);
56 UUID.push_back(U);
57
58 theta = theta + dtheta;
59 }
60 z = z - 1.8f * grape_rad;
61 theta = theta + 0.5f * dtheta;
62 RB = RB * 0.85f + unif_distribution(generator) * RB * 0.15f;
63 count = count + 1;
64 }
65
66 if (enable_element_labels) {
67 context->setPrimitiveData(flatten(UUID), "element_label", "berry");
68 }
69
70 return UUID;
71}
72
73std::vector<uint> leafPrototype(const int2 leaf_subdivisions, const char *leaf_texture_file, Context *context) {
74
75 int Nx = leaf_subdivisions.x;
76 int Ny = ceil(leaf_subdivisions.y * 0.5);
77
78 float dx = 1.f / float(Nx);
79 float dy = 0.5f / float(Ny);
80
81 std::vector<uint> UUIDs;
82
83 float a0 = 0.15 * (1 + context->randu());
84 float e0 = 0.1f * (1 + context->randu());
85
86 for (int i = 0; i < Nx; i++) {
87 for (int j = 0; j < Ny; j++) {
88
89 float x = i * dx;
90 float y = j * dy;
91
92 float mag;
93 float z;
94
95 mag = sqrt(x * x + 2 * y * y);
96 // z = a0*mag/(e0+mag);
97 z = a0 * y / (e0 + y) + a0 * x / (e0 + x);
98 vec3 v0(x, y, z);
99
100 mag = sqrt((x + dx) * (x + dx) + 2 * y * y);
101 // z = a0*mag/(e0+mag);
102 z = a0 * y / (e0 + y) + a0 * (x + dx) / (e0 + x + dx);
103 vec3 v1(x + dx, y, z);
104
105 mag = sqrt((x + dx) * (x + dx) + 2 * (y + dy) * (y + dy));
106 // z = a0*mag/(e0+mag);
107 z = a0 * (y + dy) / (e0 + y + dy) + a0 * (x + dx) / (e0 + x + dx);
108 vec3 v2(x + dx, y + dy, z);
109
110 mag = sqrt(x * x + 2 * (y + dy) * (y + dy));
111 // z = a0*mag/(e0+mag);
112 z = a0 * (y + dy) / (e0 + y + dy) + a0 * x / (e0 + x);
113 vec3 v3(x, y + dy, z);
114
115 float dj = float(j) * dy;
116 float dj_plus = float(j + 1) * dy;
117
118 vec2 uv0(1.f - x, 0.5f + dj);
119 vec2 uv1(1.f - x - dx, 0.5f + dj);
120 vec2 uv2(1.f - x - dx, 0.5f + dj_plus);
121 vec2 uv3(1.f - x, 0.5f + dj_plus);
122
123 UUIDs.push_back(context->addTriangle(v0, v1, v2, leaf_texture_file, uv0, uv1, uv2));
124 UUIDs.push_back(context->addTriangle(v0, v2, v3, leaf_texture_file, uv0, uv2, uv3));
125
126 v0.y = -v0.y;
127 v1.y = -v1.y;
128 v2.y = -v2.y;
129 v3.y = -v3.y;
130
131 uv0 = make_vec2(1.f - x, 0.5f - dj);
132 uv1 = make_vec2(1.f - x - dx, 0.5f - dj);
133 uv2 = make_vec2(1.f - x - dx, 0.5f - dj_plus);
134 uv3 = make_vec2(1.f - x, 0.5f - dj_plus);
135
136 UUIDs.push_back(context->addTriangle(v0, v2, v1, leaf_texture_file, uv0, uv2, uv1));
137 UUIDs.push_back(context->addTriangle(v0, v3, v2, leaf_texture_file, uv0, uv3, uv2));
138 }
139 }
140
141 context->translatePrimitive(UUIDs, make_vec3(-1, 0, 0));
142
143 return UUIDs;
144}
145
147
148 vector<uint> U;
149 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
150 std::vector<std::vector<uint>> UUID_leaf_plant;
151 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
152
153 std::uniform_real_distribution<float> unif_distribution;
154
155 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
156
157 bool is_dead = false;
158 if (params.dead_probability > 0) {
159 float random_draw = context->randu();
160 is_dead = random_draw <= params.dead_probability;
161 }
162
163 //------ trunks -------//
164
165 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
166 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
167
168 std::vector<float> rad_main;
169 rad_main.push_back(0.75f * trunk_radius);
170 rad_main.push_back(0.8f * trunk_radius);
171 rad_main.push_back(1.f * trunk_radius);
172 rad_main.push_back(0.7f * trunk_radius);
173 rad_main.push_back(0.95f * trunk_radius);
174 rad_main.push_back(0.1f * trunk_radius);
175 std::vector<vec3> pos_main;
176 pos_main.push_back(make_vec3(0., 0., 0.0));
177 pos_main.push_back(make_vec3(0, 0, 0.2f * trunk_height));
178 pos_main.push_back(make_vec3(0, 0, 0.22f * trunk_height));
179 pos_main.push_back(make_vec3(0, 0, 0.6f * trunk_height));
180 pos_main.push_back(make_vec3(0, 0, 0.96f * trunk_height));
181 pos_main.push_back(make_vec3(0., 0., trunk_height));
182
183 for (uint i = 0; i < rad_main.size(); i++) {
184 pos_main.at(i) = pos_main.at(i) + origin;
185 }
186
187 int wood_subdivisions = std::max(3, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
188
189 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
190 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
191 if (enable_element_labels) {
192 context->setPrimitiveData(UUID_trunk_plant, "element_label", "trunk");
193 }
194
195 //---- Cordons -----//
196
197 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
198 float cordon_radius = std::max(1e-5f, params.cordon_radius + getVariation(params.cordon_radius_spread, generator));
199 float cordon_length = std::max(0.f, params.cordon_length + getVariation(params.cordon_length_spread, generator));
200
201 float diff = cordon_height - trunk_height;
202
203 float cost = cosf(canopy_rotation);
204 float sint = sinf(canopy_rotation);
205
206 int Ncord = 2 * wood_subdivisions;
207
208 // West Cordon
209
210 std::vector<float> rad_cordw;
211 rad_cordw.push_back(cordon_radius);
212 std::vector<vec3> pos_cordw;
213 pos_cordw.push_back(make_vec3(0.01f * cordon_length * cost, 0.01f * cordon_length * sint, 0.95 * trunk_height));
214 vec3 n_start = sphere2cart(make_SphericalCoord(cordon_height, 0.4f * PI_F * (1 + getVariation(0.2f, generator)), getVariation(0.2f * PI_F, generator)));
215 vec3 n_end = make_vec3(0.5f * cordon_height, 0, 0);
216
217 for (int i = 1; i < Ncord; i++) {
218 float frac = float(i) / float(Ncord - 1);
219 vec3 n = spline_interp3(frac, pos_cordw.front(), n_start, make_vec3(cordon_length * cost, cordon_length * sint, trunk_height + diff), n_end);
220 pos_cordw.push_back(n);
221 rad_cordw.push_back(cordon_radius * (1.f - 0.6f * frac));
222 }
223
224 std::vector<vec3> tmp;
225 tmp.resize(pos_cordw.size());
226 for (uint i = 0; i < pos_cordw.size(); i++) {
227 tmp.at(i) = pos_cordw.at(i) + origin;
228 }
229
230 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cordw, params.wood_texture_file.c_str());
231 UUID_branch_plant = context->getObjectPrimitiveUUIDs(objID);
232
233 // East Cordon
234 std::vector<float> rad_corde;
235 rad_corde.push_back(cordon_radius);
236 std::vector<vec3> pos_corde;
237 pos_corde.push_back(make_vec3(-0.01f * cordon_length * cost, -0.01f * cordon_length * sint, 0.95f * trunk_height));
238 n_start = sphere2cart(make_SphericalCoord(cordon_height, 0.4f * PI_F * (1 + getVariation(0.2f, generator)), PI_F + getVariation(0.2f * PI_F, generator)));
239 n_end = make_vec3(-0.5f * cordon_height, 0, 0);
240
241 for (int i = 1; i < Ncord; i++) {
242 float frac = float(i) / float(Ncord - 1);
243 vec3 n = spline_interp3(frac, pos_corde.front(), n_start, make_vec3(-cordon_length * cost, -cordon_length * sint, trunk_height + diff), n_end);
244 pos_corde.push_back(n);
245 rad_corde.push_back(cordon_radius * (1.f - 0.6f * frac));
246 }
247
248 tmp.resize(pos_corde.size());
249 for (uint i = 0; i < pos_corde.size(); i++) {
250 tmp.at(i) = pos_corde.at(i) + origin;
251 }
252
253 objID = context->addTubeObject(wood_subdivisions, tmp, rad_corde, params.wood_texture_file.c_str());
254 U = context->getObjectPrimitiveUUIDs(objID);
255 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
256
257 if (enable_element_labels) { // note: only need to set label once because the UUID vector contains both cordons
258 context->setPrimitiveData(UUID_branch_plant, "element_label", "cordon");
259 }
260
261 //------- primary shoots ---------//
262
263 // uint ID0 = context->addTileObject( make_vec3(0,0,0), make_vec2(1,1), make_SphericalCoord(0,0), params.leaf_subdivisions, params.leaf_texture_file.c_str() );
264
265 std::vector<uint> leaf_ptype = leafPrototype(params.leaf_subdivisions, params.leaf_texture_file.c_str(), context);
266
267 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
268 float shoot_radius = std::max(1e-5f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
269 uint shoots_per_cordon = std::max(uint(0), params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
270
271 float height = cordon_height + shoot_length;
272
273 // Looping over each cordon
274 for (uint c = 0; c < 2; c++) {
275
276 std::vector<float> rad_cord;
277 std::vector<vec3> pos_cord;
278 float sign;
279 if (c == 0) {
280 pos_cord = pos_cordw;
281 rad_cord = rad_cordw;
282 sign = 1;
283 } else {
284 pos_cord = pos_corde;
285 rad_cord = rad_corde;
286 sign = -1;
287 }
288
289 float dx = fabs(pos_cord.back().y - pos_cord.at(0).y) / (float(shoots_per_cordon));
290
291 // looping over each shoot in the cordon
292 for (int j = 1; j < shoots_per_cordon + 1; j++) {
293
294 // fraction of distance along cordon
295 float frac_shoot = float(j) / float(shoots_per_cordon);
296
297 vec3 cane_base = interpolateTube(pos_cord, frac_shoot);
298
299 std::vector<float> rad_pshoot;
300 std::vector<vec3> pos_pshoot;
301
302 // shoot base
303 rad_pshoot.push_back(shoot_radius);
304 pos_pshoot.push_back(cane_base);
305
306 // shoot nodes
307 float phirot = (0.5f - unif_distribution(generator)) * 0.5f * PI_F;
308 phirot = 0.5 * PI_F;
309 if (unif_distribution(generator) < 0.5) {
310 phirot += PI_F;
311 }
312
313 // tangent vector for start of shoot
314 vec3 n_start = sphere2cart(make_SphericalCoord(0.3f * height, 0.5f * PI_F * (1 - unif_distribution(generator)), 2.f * PI_F * unif_distribution(generator)));
315 // tangent vector for middle of shoot
316 vec3 n_mid = sphere2cart(make_SphericalCoord(0.3f * height, 0.5f * PI_F * (1 - unif_distribution(generator) * 0.15), 2.f * PI_F * unif_distribution(generator)));
317 // tangent vector for end of shoot
318 vec3 n_end = sphere2cart(make_SphericalCoord(0.3f * height, 0.5f * PI_F * (1 - unif_distribution(generator) * 0.5), 2.f * PI_F * unif_distribution(generator)));
319
320 uint Nz = 2 * wood_subdivisions;
321 float dz = ((1 + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
322
323 float total_cordons_length = 2 * cordon_length;
324
325 // position of middle of shoot
326 vec3 cane_mid = cane_base + make_vec3(0.1f * getVariation(total_cordons_length, generator), 0.1f * getVariation(total_cordons_length, generator), 0.75f * Nz * dz);
327 // position of end of shoot
328 vec3 cane_end = cane_base + make_vec3(0.15f * getVariation(total_cordons_length, generator), 0.15f * getVariation(total_cordons_length, generator), Nz * dz);
329
330 float zfrac_mid = (cane_mid.z - cane_base.z) / (cane_end.z - cane_base.z);
331
332 // build nodes for shoot tube
333 for (uint k = 1; k < Nz; k++) {
334
335 float zfrac = float(k) / float(Nz - 1);
336
337 vec3 n;
338 if (zfrac < zfrac_mid) {
339 n = spline_interp3(zfrac / zfrac_mid, cane_base, n_start, cane_mid, n_mid);
340 } else {
341 n = spline_interp3((zfrac - zfrac_mid) / (1 - zfrac_mid), cane_mid, n_mid, cane_end, n_end);
342 }
343
344 pos_pshoot.push_back(n);
345
346 rad_pshoot.push_back(shoot_radius * (1.f - 0.5f * float(k) / float(Nz)));
347 }
348
349 rad_pshoot.back() = 0.0f;
350
351 std::vector<vec3> tmp;
352 tmp.resize(pos_pshoot.size());
353 for (uint i = 0; i < pos_pshoot.size(); i++) {
354 tmp.at(i) = pos_pshoot.at(i) + origin;
355 }
356
357 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
358 U = context->getObjectPrimitiveUUIDs(objID);
359 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
360 if (enable_element_labels) {
361 context->setPrimitiveData(U, "element_label", "cane");
362 }
363
364 if (is_dead) {
365 // Don't add grapes and leaves
366 continue;
367 }
368
369 // grape clusters
370 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
371 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
372 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
373 uint grape_subdivisions = std::max(3u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
374 std::vector<std::vector<uint>> UUID_grapes;
375 if (grape_radius > 0 && cluster_radius > 0) {
376
377 float fgrape = 0.035f + (cluster_height_max - 0.035) * unif_distribution(generator);
378 vec3 p_grape = interpolateTube(tmp, fgrape);
379 int sgn = 1;
380 if (unif_distribution(generator) < 0.5) {
381 sgn = -1;
382 }
383 vec3 offset(sgn * (0.25f * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (0.1f * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
384 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
385 }
386 UUID_fruit_plant.push_back(UUID_grapes);
387
388 // leaves
389 if (params.leaf_width == 0) {
390 continue;
391 }
392 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
393
394 float flip = 0;
395 if (unif_distribution(generator) < 0.5) {
396 flip = 1;
397 }
398 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction, generator));
399 float lfrac = 1.f;
400 int iter = 0;
401 while (lfrac > 0.5 * leaf_width && iter < 100) {
402 iter++;
403
404 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1f * leaf_width);
405
406 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
407
408 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
409 parent_normal.normalize();
410 vec3 leaf_offset = rotatePointAboutLine(make_vec3(getVariation(0.1f * lsize, generator), getVariation(0.75f * lsize, generator), 0), make_vec3(0, 0, 0), parent_normal, flip * PI_F + getVariation(0.25f * PI_F, generator));
411
412 float s;
413 if (int(flip) % 2 == 0) {
414 s = 1;
415 } else {
416 s = -1;
417 }
418
419 float Rphi = -canopy_rotation - s * 0.5 * PI_F * (1.f + getVariation(0.4f, generator));
420 float Rtheta = 0.25f * PI_F * (1.f + getVariation(0.2f, generator));
421
422 vec3 position = origin + pos_leaf + leaf_offset;
423
424 std::vector<uint> UUID_leaf = context->copyPrimitive(leaf_ptype);
425 // context->addPolymeshObject( UUID_leaf );
426 context->scalePrimitive(UUID_leaf, make_vec3(lsize, lsize, lsize));
427 context->rotatePrimitive(UUID_leaf, -Rtheta, "y");
428 context->rotatePrimitive(UUID_leaf, Rphi, "z");
429 context->translatePrimitive(UUID_leaf, position);
430
431 UUID_leaf_plant.push_back(UUID_leaf);
432
433 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
434
435 flip++;
436
437 if (enable_element_labels) {
438 context->setPrimitiveData(UUID_leaf, "element_label", "leaf");
439 }
440 }
441 }
442 }
443
444 // context->deleteObject(ID0);
445 context->deletePrimitive(leaf_ptype);
446
447 UUID_trunk.push_back(UUID_trunk_plant);
448 UUID_branch.push_back(UUID_branch_plant);
449 UUID_leaf.push_back(UUID_leaf_plant);
450 UUID_fruit.push_back(UUID_fruit_plant);
451
452 return UUID_leaf.size() - 1;
453}
454
456
457 vector<uint> U;
458 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
459 std::vector<std::vector<uint>> UUID_leaf_plant;
460 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
461
462 std::uniform_real_distribution<float> unif_distribution;
463
464 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
465
466 bool is_dead = false;
467 if (params.dead_probability > 0) {
468 float random_draw = context->randu();
469 is_dead = random_draw <= params.dead_probability;
470 }
471
472 //------ trunks -------//
473
474 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
475 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
476
477 std::vector<float> rad_main;
478 rad_main.push_back(0.75f * trunk_radius);
479 rad_main.push_back(0.8f * trunk_radius);
480 rad_main.push_back(1.f * trunk_radius);
481 rad_main.push_back(0.7f * trunk_radius);
482 rad_main.push_back(0.95f * trunk_radius);
483 rad_main.push_back(0.1f * trunk_radius);
484 std::vector<vec3> pos_main;
485 pos_main.push_back(make_vec3(0., 0., 0.0));
486 pos_main.push_back(make_vec3(0, 0, 0.2f * trunk_height));
487 pos_main.push_back(make_vec3(0, 0, 0.22f * trunk_height));
488 pos_main.push_back(make_vec3(0, 0, 0.6f * trunk_height));
489 pos_main.push_back(make_vec3(0, 0, 0.96f * trunk_height));
490 pos_main.push_back(make_vec3(0., 0., trunk_height));
491
492 for (uint i = 0; i < rad_main.size(); i++) {
493 pos_main.at(i) = pos_main.at(i) + origin;
494 }
495
496 int wood_subdivisions = std::max(3, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
497
498 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
499 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
500
501 //------ crown -------//
502
503 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
504 float cordon_radius = std::max(1e-5f, params.cordon_radius + getVariation(params.cordon_radius_spread, generator));
505 float cordon_spacing = std::max(0.f, params.cordon_spacing + getVariation(params.cordon_spacing_spread, generator));
506
507 float diff = cordon_height - trunk_height;
508
509 float cost = cosf(canopy_rotation + 0.5f * PI_F);
510 float sint = sinf(canopy_rotation + 0.5f * PI_F);
511
512 std::vector<float> rad_crown;
513 rad_crown.push_back(0.6f * trunk_radius);
514 rad_crown.push_back(0.55f * trunk_radius);
515 rad_crown.push_back(0.5f * trunk_radius);
516 rad_crown.push_back(0.45f * trunk_radius);
517 rad_crown.push_back(0.4f * trunk_radius);
518
519 std::vector<vec3> pos_crownw;
520 pos_crownw.push_back(make_vec3(0., 0., 0.95f * trunk_height));
521 pos_crownw.push_back(make_vec3(0.05f * 0.5f * cordon_spacing * cost, 0.05f * 0.5f * cordon_spacing * sint, trunk_height));
522 pos_crownw.push_back(make_vec3(0.25f * 0.5f * cordon_spacing * cost, 0.25f * 0.5f * cordon_spacing * sint, trunk_height + 0.1f * diff));
523 pos_crownw.push_back(make_vec3(0.45f * 0.5f * cordon_spacing * cost, 0.45f * 0.5f * cordon_spacing * sint, trunk_height + 0.65f * diff));
524 pos_crownw.push_back(make_vec3(0.75f * 0.5f * cordon_spacing * cost, 0.75f * 0.5f * cordon_spacing * sint, cordon_height));
525
526 for (uint i = 0; i < rad_crown.size(); i++) {
527 pos_crownw.at(i) = pos_crownw.at(i) + origin;
528 }
529
530 objID = context->addTubeObject(wood_subdivisions, pos_crownw, rad_crown, params.wood_texture_file.c_str());
531 U = context->getObjectPrimitiveUUIDs(objID);
532 UUID_trunk_plant.insert(UUID_trunk_plant.end(), U.begin(), U.end());
533
534 std::vector<vec3> pos_crowne;
535 pos_crowne.push_back(make_vec3(0., 0., 0.95f * trunk_height));
536 pos_crowne.push_back(make_vec3(-0.05f * 0.5f * cordon_spacing * cost, -0.05f * 0.5f * cordon_spacing * sint, trunk_height));
537 pos_crowne.push_back(make_vec3(-0.25f * 0.5f * cordon_spacing * cost, -0.25f * 0.5f * cordon_spacing * sint, trunk_height + 0.1f * diff));
538 pos_crowne.push_back(make_vec3(-0.45f * 0.5f * cordon_spacing * cost, -0.45f * 0.5f * cordon_spacing * sint, trunk_height + 0.65f * diff));
539 pos_crowne.push_back(make_vec3(-0.75f * 0.5f * cordon_spacing * cost, -0.75f * 0.5f * cordon_spacing * sint, cordon_height));
540
541 for (uint i = 0; i < rad_crown.size(); i++) {
542 pos_crowne.at(i) = pos_crowne.at(i) + origin;
543 }
544
545 objID = context->addTubeObject(wood_subdivisions, pos_crowne, rad_crown, params.wood_texture_file.c_str());
546 U = context->getObjectPrimitiveUUIDs(objID);
547 UUID_trunk_plant.insert(UUID_trunk_plant.end(), U.begin(), U.end());
548
549 //---- Cordons -----//
550
551 float cordon_length = std::max(0.f, params.cordon_length + getVariation(params.cordon_length_spread, generator));
552
553 std::vector<float> rad_cord;
554 rad_cord.push_back(cordon_radius);
555 rad_cord.push_back(0.95f * cordon_radius);
556 rad_cord.push_back(0.9f * cordon_radius);
557 rad_cord.push_back(0.9f * cordon_radius);
558 rad_cord.push_back(0.9f * cordon_radius);
559 rad_cord.push_back(0.6f * cordon_radius);
560 rad_cord.push_back(0.2f * cordon_radius);
561
562 // West Cordon
563 std::vector<vec3> pos_cordnw;
564 pos_cordnw.push_back(make_vec3(0.7f * 0.5f * cordon_spacing * cost, 0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
565 pos_cordnw.push_back(make_vec3(0.85f * 0.5f * cordon_spacing * cost + 0.025f * sint, 0.85f * 0.5f * cordon_spacing * sint + 0.025f * cost, cordon_height));
566 pos_cordnw.push_back(make_vec3(0.95f * 0.5f * cordon_spacing * cost + 0.075f * sint, 0.95f * 0.5f * cordon_spacing * sint + 0.075f * cost, cordon_height));
567 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + 0.12f * sint, 0.5f * cordon_spacing * sint + 0.12f * cost, cordon_height));
568 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + 0.4f * cordon_length * sint, 0.5f * cordon_spacing * sint + 0.4f * cordon_length * cost, 0.94f * cordon_height));
569 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + 0.8f * cordon_length * sint, 0.5f * cordon_spacing * sint + 0.8f * cordon_length * cost, 0.97f * cordon_height));
570 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + cordon_length * sint, 0.5f * cordon_spacing * sint + cordon_length * cost, cordon_height));
571
572 std::vector<vec3> tmp;
573 tmp.resize(pos_cordnw.size());
574 for (uint i = 0; i < pos_cordnw.size(); i++) {
575 tmp.at(i) = pos_cordnw.at(i) + origin;
576 }
577
578 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
579 UUID_branch_plant = context->getObjectPrimitiveUUIDs(objID);
580
581 std::vector<vec3> pos_cordsw;
582 pos_cordsw.push_back(make_vec3(0.7f * 0.5f * cordon_spacing * cost, 0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
583 pos_cordsw.push_back(make_vec3(0.85f * 0.5f * cordon_spacing * cost - 0.025f * sint, 0.85f * 0.5f * cordon_spacing * sint - 0.025f * cost, cordon_height));
584 pos_cordsw.push_back(make_vec3(0.95f * 0.5f * cordon_spacing * cost - 0.075f * sint, 0.95f * 0.5f * cordon_spacing * sint - 0.075f * cost, cordon_height));
585 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - 0.12f * sint, 0.5f * cordon_spacing * sint - 0.12f * cost, cordon_height));
586 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - 0.4f * cordon_length * sint, 0.5f * cordon_spacing * sint - 0.4f * cordon_length * cost, 0.94f * cordon_height));
587 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - 0.8f * cordon_length * sint, 0.5f * cordon_spacing * sint - 0.8f * cordon_length * cost, 0.97f * cordon_height));
588 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - cordon_length * sint, 0.5f * cordon_spacing * sint - cordon_length * cost, cordon_height));
589
590 tmp.resize(pos_cordsw.size());
591 for (uint i = 0; i < pos_cordsw.size(); i++) {
592 tmp.at(i) = pos_cordsw.at(i) + origin;
593 }
594
595 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
596 U = context->getObjectPrimitiveUUIDs(objID);
597 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
598
599 // East Cordon
600 std::vector<vec3> pos_cordne;
601 pos_cordne.push_back(make_vec3(-0.7f * 0.5f * cordon_spacing * cost, -0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
602 pos_cordne.push_back(make_vec3(-0.85f * 0.5f * cordon_spacing * cost + 0.025f * sint, -0.85f * 0.5f * cordon_spacing * sint + 0.025f * cost, cordon_height));
603 pos_cordne.push_back(make_vec3(-0.95f * 0.5f * cordon_spacing * cost + 0.075f * sint, -0.95f * 0.5f * cordon_spacing * sint + 0.075f * cost, cordon_height));
604 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + 0.12f * sint, -0.5f * cordon_spacing * sint + 0.12f * cost, cordon_height));
605 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + 0.4f * cordon_length * sint, -0.5f * cordon_spacing * sint + 0.4f * cordon_length * cost, 0.94f * cordon_height));
606 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + 0.8f * cordon_length * sint, -0.5f * cordon_spacing * sint + 0.8f * cordon_length * cost, 0.97f * cordon_height));
607 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + cordon_length * sint, -0.5f * cordon_spacing * sint + cordon_length * cost, cordon_height));
608
609 tmp.resize(pos_cordne.size());
610 for (uint i = 0; i < pos_cordne.size(); i++) {
611 tmp.at(i) = pos_cordne.at(i) + origin;
612 }
613
614 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
615 U = context->getObjectPrimitiveUUIDs(objID);
616 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
617
618 std::vector<vec3> pos_cordse;
619 pos_cordse.push_back(make_vec3(-0.7f * 0.5f * cordon_spacing * cost, -0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
620 pos_cordse.push_back(make_vec3(-0.85f * 0.5f * cordon_spacing * cost - 0.025f * sint, -0.85f * 0.5f * cordon_spacing * sint - 0.025f * cost, cordon_height));
621 pos_cordse.push_back(make_vec3(-0.95f * 0.5f * cordon_spacing * cost - 0.075f * sint, -0.95f * 0.5f * cordon_spacing * sint - 0.075f * cost, cordon_height));
622 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - 0.12f * sint, -0.5f * cordon_spacing * sint - 0.12f * cost, cordon_height));
623 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - 0.4f * cordon_length * sint, -0.5f * cordon_spacing * sint - 0.4f * cordon_length * cost, 0.94f * cordon_height));
624 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - 0.8f * cordon_length * sint, -0.5f * cordon_spacing * sint - 0.8f * cordon_length * cost, 0.97f * cordon_height));
625 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - cordon_length * sint, -0.5f * cordon_spacing * sint - cordon_length * cost, cordon_height));
626
627 tmp.resize(pos_cordse.size());
628 for (uint i = 0; i < pos_cordse.size(); i++) {
629 tmp.at(i) = pos_cordse.at(i) + origin;
630 }
631
632 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
633 U = context->getObjectPrimitiveUUIDs(objID);
634 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
635
636 //------- primary shoots ---------//
637
638 uint ID0 = context->addTileObject(make_vec3(0, 0, 0), make_vec2(1, 1), make_SphericalCoord(0, PI_F), params.leaf_subdivisions, params.leaf_texture_file.c_str());
639
640 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
641 float shoot_radius = std::max(1e-5f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
642 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
643 float shoot_angle_base = params.shoot_angle_base + getVariation(params.shoot_angle_base_spread, generator);
644 float shoot_angle_tip = params.shoot_angle_tip + getVariation(params.shoot_angle_tip_spread, generator);
645
646 float height = cordon_height + shoot_length;
647
648 for (uint d = 0; d < 2; d++) { // cordons
649 for (uint c = 0; c < 2; c++) { // shoot bend direction
650
651 std::vector<vec3> pos_cord;
652 float sign;
653 if (c == 0) {
654 if (d == 0) {
655 pos_cord = pos_cordsw;
656 } else {
657 pos_cord = pos_cordnw;
658 }
659 sign = 1;
660 } else {
661 if (d == 0) {
662 pos_cord = pos_cordse;
663 } else {
664 pos_cord = pos_cordne;
665 }
666 sign = -1;
667 }
668
669 float dx = fabs(pos_cord.back().y - pos_cord.at(0).y) / (float(shoots_per_cordon));
670
671 for (int j = 1; j < shoots_per_cordon + 1; j++) {
672
673 float frac_shoot = float(j) / float(shoots_per_cordon);
674
675 vec3 cane_base = interpolateTube(pos_cord, frac_shoot);
676
677 std::vector<float> rad_pshoot;
678 std::vector<vec3> pos_pshoot;
679
680 // cane base
681 rad_pshoot.push_back(shoot_radius);
682 pos_pshoot.push_back(cane_base);
683
684 // cane nodes
685 bool inside = false;
686 float phirot = 0.5f * PI_F * (1 + (-0.5f + unif_distribution(generator)) * 1.0) + canopy_rotation;
687 if (unif_distribution(generator) < 0.5) {
688 phirot += PI_F;
689 if (c == 0) {
690 inside = true;
691 }
692 } else {
693 if (c == 1) {
694 inside = true;
695 }
696 }
697
698 float theta0;
699 if (inside) {
700 theta0 = 0.5f * PI_F * unif_distribution(generator); //*(1.f+(-0.5+unif_distribution(generator))*0.6);
701 } else {
702 theta0 = shoot_angle_base * (1.f + (-0.5 + unif_distribution(generator)) * 0.6);
703 }
704 float theta_end = shoot_angle_tip * (1.f + (-0.5 + unif_distribution(generator)) * 0.6);
705
706 uint Nz = 2 * wood_subdivisions;
707 float dz = ((1 + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
708 for (uint k = 1; k < Nz; k++) {
709
710 vec3 n = rotatePoint(make_vec3(0, 0, dz), (theta0 + (theta_end - theta0) * float(k) / float(Nz - 1)), phirot);
711
712 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0));
713
714 rad_pshoot.push_back(shoot_radius);
715 }
716
717 rad_pshoot.back() = 0.0f;
718
719 std::vector<vec3> tmp;
720 tmp.resize(pos_pshoot.size());
721 for (uint i = 0; i < pos_pshoot.size(); i++) {
722 tmp.at(i) = pos_pshoot.at(i) + origin;
723 }
724
725 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
726 U = context->getObjectPrimitiveUUIDs(objID);
727 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
728
729 if (is_dead) {
730 // Don't add grapes and leaves
731 continue;
732 }
733
734 // grape clusters
735 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
736 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
737 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
738 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
739 std::vector<std::vector<uint>> UUID_grapes;
740 if (grape_radius > 0 && cluster_radius > 0) {
741
742 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
743 vec3 p_grape = interpolateTube(tmp, fgrape);
744 int sgn = 1;
745 if (unif_distribution(generator) < 0.5) {
746 sgn = -1;
747 }
748 vec3 offset(sgn * (2.2 * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2 * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
749
750 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
751 }
752 UUID_fruit_plant.push_back(UUID_grapes);
753
754 // leaves
755 if (params.leaf_width == 0) {
756 continue;
757 }
758 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
759
760 float flip = 0;
761 if (unif_distribution(generator) < 0.5) {
762 flip = 1;
763 }
764 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
765 float lfrac = 1.f;
766 int iter = 0;
767 while (lfrac > 0.5 * leaf_width && iter < 100) {
768 iter++;
769
770 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
771
772 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
773
774 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
775 parent_normal.normalize();
776 vec3 leaf_offset = rotatePointAboutLine(make_vec3(0, lsize * (0.3 + getVariation(0.25f, generator)), 0), make_vec3(0, 0, 0), parent_normal, flip * PI_F + unif_distribution(generator) * 0.25 * PI_F);
777
778 float s;
779 if (int(flip) % 2 == 0) {
780 s = 1;
781 } else {
782 s = -1;
783 }
784
785 float Rphi = -canopy_rotation - s * 0.5f * PI_F * (1.f + getVariation(0.4f, generator));
786 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
787
788 vec3 position = origin + pos_leaf + leaf_offset;
789
790 uint ID = context->copyObject(ID0);
791 context->getTileObjectPointer(ID)->scale(make_vec3(lsize, lsize, 1));
792 context->getObjectPointer(ID)->rotate(-Rtheta, "y");
793 context->getObjectPointer(ID)->rotate(Rphi, "z");
794 context->getObjectPointer(ID)->translate(position);
795
796 UUID_leaf_plant.push_back(context->getObjectPointer(ID)->getPrimitiveUUIDs());
797
798 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
799
800 flip++;
801 }
802 }
803 }
804 }
805
806 context->deleteObject(ID0);
807
808 UUID_trunk.push_back(UUID_trunk_plant);
809 UUID_branch.push_back(UUID_branch_plant);
810 UUID_leaf.push_back(UUID_leaf_plant);
811 UUID_fruit.push_back(UUID_fruit_plant);
812
813 return UUID_leaf.size() - 1;
814}
815
817
818 float mean_shoot_angle = 0.1 * PI_F;
819
820 vector<uint> U;
821 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
822 std::vector<std::vector<uint>> UUID_leaf_plant;
823 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
824
825 std::uniform_real_distribution<float> unif_distribution;
826
827 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
828
829 float cost = cosf(canopy_rotation);
830 float sint = sinf(canopy_rotation);
831
832 bool is_dead = false;
833 if (params.dead_probability > 0) {
834 float random_draw = context->randu();
835 is_dead = random_draw <= params.dead_probability;
836 }
837
838 float cordon_length = std::max(0.f, params.cordon_length + getVariation(params.cordon_length_spread, generator));
839
840 //------ trunks -------//
841
842 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
843 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
844
845 std::vector<float> rad_main;
846 rad_main.push_back(0.75 * trunk_radius);
847 rad_main.push_back(0.8f * trunk_radius);
848 rad_main.push_back(1.f * trunk_radius);
849 rad_main.push_back(0.7f * trunk_radius);
850 rad_main.push_back(0.95f * trunk_radius);
851 rad_main.push_back(0.1 * trunk_radius);
852 std::vector<vec3> pos_main;
853 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.0f));
854 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.2f * trunk_height));
855 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.22f * trunk_height));
856 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.6f * trunk_height));
857 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.96f * trunk_height));
858 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, trunk_height));
859
860 for (uint i = 0; i < rad_main.size(); i++) {
861 pos_main.at(i) = pos_main.at(i) + origin;
862 }
863
864 int wood_subdivisions = std::max(0, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
865
866 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
867 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
868
869 //---- Cordons -----//
870
871 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
872 float cordon_radius = std::max(1e-5f, params.cordon_radius + getVariation(params.cordon_radius_spread, generator));
873
874 float total_cordons_length = 2 * cordon_length;
875
876 float diff = cordon_height - trunk_height;
877
878 // Cordon
879 std::vector<float> rad_cord;
880 rad_cord.push_back(cordon_radius);
881 rad_cord.push_back(0.95 * cordon_radius);
882 rad_cord.push_back(0.9f * cordon_radius);
883 rad_cord.push_back(0.85f * cordon_radius);
884 rad_cord.push_back(0.8 * cordon_radius);
885 rad_cord.push_back(0.6 * cordon_radius);
886 rad_cord.push_back(0.2f * cordon_radius);
887 std::vector<vec3> pos_cord;
888 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.01f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.01f * total_cordons_length) * sint, 0.95f * trunk_height));
889 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.05f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.05f * total_cordons_length) * sint, trunk_height + 0.1f * diff));
890 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.15f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.15f * total_cordons_length) * sint, trunk_height + 0.65f * diff));
891 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.45f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.45f * total_cordons_length) * sint, trunk_height + 0.95f * diff));
892 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.6f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.6f * total_cordons_length) * sint, trunk_height + 1.05f * diff));
893 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 0.85f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 0.85f * total_cordons_length) * sint, trunk_height + diff));
894 pos_cord.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius + 1.0f * total_cordons_length) * cost, (-cordon_length + 0.5f * trunk_radius + 1.0f * total_cordons_length) * sint, trunk_height + diff));
895
896 std::vector<vec3> tmp;
897 tmp.resize(pos_cord.size());
898 for (uint i = 0; i < pos_cord.size(); i++) {
899 tmp.at(i) = pos_cord.at(i) + origin;
900 }
901
902 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
903 UUID_branch_plant = context->getObjectPrimitiveUUIDs(objID);
904
905 //------- primary shoots ---------//
906
907 uint ID0 = context->addTileObject(make_vec3(0, 0, 0), make_vec2(1, 1), make_SphericalCoord(0, PI_F), params.leaf_subdivisions, params.leaf_texture_file.c_str());
908
909 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
910 float shoot_radius = std::max(1e-5f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
911 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
912
913 float height = cordon_height + shoot_length;
914
915 float dx = fabs(pos_cord.back().y - pos_cord.at(0).y) / (float(shoots_per_cordon));
916
917 for (int j = 1; j < shoots_per_cordon + 1; j++) {
918
919 float frac_shoot = float(j) / float(shoots_per_cordon);
920
921 vec3 cane_base = interpolateTube(pos_cord, frac_shoot);
922
923 std::vector<float> rad_pshoot;
924 std::vector<vec3> pos_pshoot;
925
926 // cane base
927 rad_pshoot.push_back(shoot_radius);
928 pos_pshoot.push_back(cane_base);
929
930 // cane nodes
931 float phirot = (0.5f - unif_distribution(generator)) * 0.5 * PI_F;
932 phirot = 0.5 * PI_F;
933 if (unif_distribution(generator) < 0.5) {
934 phirot += PI_F;
935 }
936
937 float theta0 = (0.3f - unif_distribution(generator)) * 0.6;
938 float theta_end = (0.1f - unif_distribution(generator)) * 0.2;
939
940 uint Nz = 2 * wood_subdivisions;
941 float dz = ((1 + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
942 for (uint k = 1; k < Nz; k++) {
943
944 vec3 n = rotatePoint(make_vec3(0, 0, dz), mean_shoot_angle * PI_F / 180.f * (theta0 + 1.2 * float(k) / float(Nz - 1)), phirot);
945
946 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0));
947
948 rad_pshoot.push_back(shoot_radius);
949 }
950
951 rad_pshoot.back() = 0.0f;
952
953 std::vector<vec3> tmp;
954 tmp.resize(pos_pshoot.size());
955 for (uint i = 0; i < pos_pshoot.size(); i++) {
956 tmp.at(i) = pos_pshoot.at(i) + origin;
957 }
958
959 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
960 U = context->getObjectPrimitiveUUIDs(objID);
961 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
962
963 if (is_dead) {
964 // Don't add grapes and leaves
965 continue;
966 }
967
968 // grape clusters
969 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
970 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
971 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
972 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
973 std::vector<std::vector<uint>> UUID_grapes;
974 if (grape_radius > 0 && cluster_radius > 0) {
975
976 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
977 vec3 p_grape = interpolateTube(tmp, fgrape);
978 int sgn = 1;
979 if (unif_distribution(generator) < 0.5) {
980 sgn = -1;
981 }
982 vec3 offset(sgn * (2.2f * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2 * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
983 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
984 }
985 UUID_fruit_plant.push_back(UUID_grapes);
986
987 // leaves
988 if (params.leaf_width == 0) {
989 continue;
990 }
991 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
992
993 float flip = 0;
994 if (unif_distribution(generator) < 0.5) {
995 flip = 1;
996 }
997 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
998 float lfrac = 1.f;
999 int iter = 0;
1000 while (lfrac > 0.5 * leaf_width && iter < 100) {
1001 iter++;
1002
1003 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
1004
1005 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
1006
1007 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
1008 parent_normal.normalize();
1009 vec3 leaf_offset = rotatePointAboutLine(make_vec3(0, lsize * (0.3 + getVariation(0.25f, generator)), 0), make_vec3(0, 0, 0), parent_normal, flip * PI_F + unif_distribution(generator) * 0.25f * PI_F);
1010
1011 float s;
1012 if (int(flip) % 2 == 0) {
1013 s = 1;
1014 } else {
1015 s = -1;
1016 }
1017
1018 float Rphi = -canopy_rotation - s * 0.5f * PI_F * (1.f + getVariation(0.4f, generator));
1019 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
1020
1021 vec3 position = origin + pos_leaf - leaf_offset;
1022
1023 uint ID = context->copyObject(ID0);
1024 context->getTileObjectPointer(ID)->scale(make_vec3(lsize, lsize, 1));
1025 context->getObjectPointer(ID)->rotate(-Rtheta, "y");
1026 context->getObjectPointer(ID)->rotate(Rphi, "z");
1027 context->getObjectPointer(ID)->translate(position);
1028
1029 UUID_leaf_plant.push_back(context->getObjectPointer(ID)->getPrimitiveUUIDs());
1030
1031 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
1032
1033 flip++;
1034 }
1035 }
1036
1037 context->deleteObject(ID0);
1038
1039 UUID_trunk.push_back(UUID_trunk_plant);
1040 UUID_branch.push_back(UUID_branch_plant);
1041 UUID_leaf.push_back(UUID_leaf_plant);
1042 UUID_fruit.push_back(UUID_fruit_plant);
1043
1044 return UUID_leaf.size() - 1;
1045}
1046
1048
1049 vector<uint> U;
1050 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
1051 std::vector<std::vector<uint>> UUID_leaf_plant;
1052 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
1053
1054 std::uniform_real_distribution<float> unif_distribution;
1055
1056 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
1057
1058 float cost = cosf(canopy_rotation);
1059 float sint = sinf(canopy_rotation);
1060
1061 bool is_dead = false;
1062 if (params.dead_probability > 0) {
1063 float random_draw = context->randu();
1064 is_dead = random_draw <= params.dead_probability;
1065 }
1066
1067 //------ trunks -------//
1068
1069 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
1070 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
1071
1072 std::vector<float> rad_main;
1073 rad_main.push_back(0.75 * trunk_radius);
1074 rad_main.push_back(0.8f * trunk_radius);
1075 rad_main.push_back(1.f * trunk_radius);
1076 rad_main.push_back(0.7f * trunk_radius);
1077 rad_main.push_back(0.95f * trunk_radius);
1078 rad_main.push_back(0.1 * trunk_radius);
1079 std::vector<vec3> pos_main;
1080 pos_main.push_back(make_vec3(0., 0., 0.0));
1081 pos_main.push_back(make_vec3(0, 0, 0.2f * trunk_height));
1082 pos_main.push_back(make_vec3(0, 0, 0.22f * trunk_height));
1083 pos_main.push_back(make_vec3(0, 0, 0.6f * trunk_height));
1084 pos_main.push_back(make_vec3(0, 0, 0.96f * trunk_height));
1085 pos_main.push_back(make_vec3(0., 0., trunk_height));
1086
1087 for (uint i = 0; i < rad_main.size(); i++) {
1088 pos_main.at(i) = pos_main.at(i) + origin;
1089 }
1090
1091 int wood_subdivisions = std::max(0, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
1092
1093 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
1094 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
1095
1096 //------- primary shoots ---------//
1097
1098 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
1099
1100 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
1101 float shoot_radius = std::max(0.f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
1102 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
1103
1104 uint ID0 = context->addTileObject(make_vec3(0, 0, 0), make_vec2(1, 1), make_SphericalCoord(0, PI_F), params.leaf_subdivisions, params.leaf_texture_file.c_str());
1105
1106 for (uint c = 0; c < 2; c++) {
1107
1108 for (int j = 1; j < shoots_per_cordon + 1; j++) {
1109
1110 float frac_shoot = float(j) / float(shoots_per_cordon);
1111
1112 float height = (cordon_height + shoot_length) * (1.f + 0.55 * (1 - frac_shoot));
1113
1114 vec3 cane_base = make_vec3(0., 0., trunk_height);
1115
1116 std::vector<float> rad_pshoot;
1117 std::vector<vec3> pos_pshoot;
1118
1119 // cane base
1120 rad_pshoot.push_back(shoot_radius);
1121 pos_pshoot.push_back(cane_base);
1122
1123 float theta0 = 0.5 * PI_F * (1.f - float(j - 1) / float(shoots_per_cordon));
1124 float theta_end = (0.1f - unif_distribution(generator)) * 0.2;
1125
1126 uint Nz = 2 * wood_subdivisions;
1127 float dz = ((1.f + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
1128 for (uint k = 1; k < Nz; k++) {
1129
1130 vec3 n = rotatePoint(make_vec3(0, 0, dz), (theta0 + (theta_end - theta0) * float(k) / float(Nz - 1)), canopy_rotation + PI_F * float(c));
1131
1132 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0.f));
1133
1134 rad_pshoot.push_back(shoot_radius);
1135 }
1136
1137 rad_pshoot.back() = 0.0f;
1138
1139 std::vector<vec3> tmp;
1140 tmp.resize(pos_pshoot.size());
1141 for (uint i = 0; i < pos_pshoot.size(); i++) {
1142 tmp.at(i) = pos_pshoot.at(i) + origin;
1143 }
1144
1145 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
1146 U = context->getObjectPrimitiveUUIDs(objID);
1147 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
1148
1149 if (is_dead) {
1150 // Don't add grapes and leaves
1151 continue;
1152 }
1153
1154 // grape clusters
1155 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
1156 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
1157 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
1158 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
1159 std::vector<std::vector<uint>> UUID_grapes;
1160 if (grape_radius > 0 && cluster_radius > 0) {
1161
1162 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
1163 vec3 p_grape = interpolateTube(tmp, fgrape);
1164 int sgn = 1;
1165 if (unif_distribution(generator) < 0.5) {
1166 sgn = -1;
1167 }
1168 vec3 offset(sgn * (2.2f * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2.f * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
1169 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
1170 }
1171 UUID_fruit_plant.push_back(UUID_grapes);
1172
1173 // leaves
1174 if (params.leaf_width == 0) {
1175 continue;
1176 }
1177 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
1178
1179 float flip = 0;
1180 if (unif_distribution(generator) < 0.5) {
1181 flip = 1;
1182 }
1183 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
1184 float lfrac = 1.f;
1185 int iter = 0;
1186 while (lfrac > 0.5 * leaf_width && iter < 100) {
1187 iter++;
1188
1189 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
1190
1191 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
1192
1193 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
1194 parent_normal.normalize();
1195 vec3 leaf_offset = rotatePointAboutLine(make_vec3(0, lsize * (0.3f + getVariation(0.25f, generator)), 0), make_vec3(0, 0, 0), parent_normal, flip * PI_F + unif_distribution(generator) * 0.25f * PI_F);
1196
1197 float s;
1198 if (int(flip) % 2 == 0) {
1199 s = 1;
1200 } else {
1201 s = -1;
1202 }
1203
1204 float Rphi = -canopy_rotation - s * 0.5 * PI_F * (1.f + getVariation(0.4f, generator));
1205 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
1206
1207 vec3 position = origin + pos_leaf - leaf_offset;
1208
1209 uint ID = context->copyObject(ID0);
1210 context->getTileObjectPointer(ID)->scale(make_vec3(lsize, lsize, 1));
1211 context->getObjectPointer(ID)->rotate(-Rtheta, "y");
1212 context->getObjectPointer(ID)->rotate(Rphi, "z");
1213 context->getObjectPointer(ID)->translate(position);
1214
1215 UUID_leaf_plant.push_back(context->getObjectPointer(ID)->getPrimitiveUUIDs());
1216
1217 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
1218
1219 flip++;
1220 }
1221 }
1222 }
1223
1224 context->deleteObject(ID0);
1225
1226 UUID_trunk.push_back(UUID_trunk_plant);
1227 UUID_branch.push_back(UUID_branch_plant);
1228 UUID_leaf.push_back(UUID_leaf_plant);
1229 UUID_fruit.push_back(UUID_fruit_plant);
1230
1231 return UUID_leaf.size() - 1;
1232}