1.3.64
 
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, v1, v2, leaf_texture_file, uv0, uv1, uv2));
137 UUIDs.push_back(context->addTriangle(v0, v2, v3, leaf_texture_file, uv0, uv2, uv3));
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
426 context->scalePrimitive(UUID_leaf, make_vec3(lsize, lsize, lsize));
427
428 context->rotatePrimitive(UUID_leaf, -Rtheta, "y");
429 context->rotatePrimitive(UUID_leaf, Rphi, "z");
430 context->translatePrimitive(UUID_leaf, position);
431
432 UUID_leaf_plant.push_back(UUID_leaf);
433
434 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
435
436 flip++;
437 }
438 }
439 }
440
441 context->deletePrimitive(leaf_ptype);
442
443 UUID_trunk.push_back(UUID_trunk_plant);
444 UUID_branch.push_back(UUID_branch_plant);
445 UUID_leaf.push_back(UUID_leaf_plant);
446 UUID_fruit.push_back(UUID_fruit_plant);
447
448 return UUID_leaf.size() - 1;
449}
450
452
453 vector<uint> U;
454 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
455 std::vector<std::vector<uint>> UUID_leaf_plant;
456 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
457
458 std::uniform_real_distribution<float> unif_distribution;
459
460 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
461
462 bool is_dead = false;
463 if (params.dead_probability > 0) {
464 float random_draw = context->randu();
465 is_dead = random_draw <= params.dead_probability;
466 }
467
468 //------ trunks -------//
469
470 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
471 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
472
473 std::vector<float> rad_main;
474 rad_main.push_back(0.75f * trunk_radius);
475 rad_main.push_back(0.8f * trunk_radius);
476 rad_main.push_back(1.f * trunk_radius);
477 rad_main.push_back(0.7f * trunk_radius);
478 rad_main.push_back(0.95f * trunk_radius);
479 rad_main.push_back(0.1f * trunk_radius);
480 std::vector<vec3> pos_main;
481 pos_main.push_back(make_vec3(0., 0., 0.0));
482 pos_main.push_back(make_vec3(0, 0, 0.2f * trunk_height));
483 pos_main.push_back(make_vec3(0, 0, 0.22f * trunk_height));
484 pos_main.push_back(make_vec3(0, 0, 0.6f * trunk_height));
485 pos_main.push_back(make_vec3(0, 0, 0.96f * trunk_height));
486 pos_main.push_back(make_vec3(0., 0., trunk_height));
487
488 for (uint i = 0; i < rad_main.size(); i++) {
489 pos_main.at(i) = pos_main.at(i) + origin;
490 }
491
492 int wood_subdivisions = std::max(3, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
493
494 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
495 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
496
497 //------ crown -------//
498
499 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
500 float cordon_radius = std::max(1e-5f, params.cordon_radius + getVariation(params.cordon_radius_spread, generator));
501 float cordon_spacing = std::max(0.f, params.cordon_spacing + getVariation(params.cordon_spacing_spread, generator));
502
503 float diff = cordon_height - trunk_height;
504
505 float cost = cosf(canopy_rotation + 0.5f * PI_F);
506 float sint = sinf(canopy_rotation + 0.5f * PI_F);
507
508 std::vector<float> rad_crown;
509 rad_crown.push_back(0.6f * trunk_radius);
510 rad_crown.push_back(0.55f * trunk_radius);
511 rad_crown.push_back(0.5f * trunk_radius);
512 rad_crown.push_back(0.45f * trunk_radius);
513 rad_crown.push_back(0.4f * trunk_radius);
514
515 std::vector<vec3> pos_crownw;
516 pos_crownw.push_back(make_vec3(0., 0., 0.95f * trunk_height));
517 pos_crownw.push_back(make_vec3(0.05f * 0.5f * cordon_spacing * cost, 0.05f * 0.5f * cordon_spacing * sint, trunk_height));
518 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));
519 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));
520 pos_crownw.push_back(make_vec3(0.75f * 0.5f * cordon_spacing * cost, 0.75f * 0.5f * cordon_spacing * sint, cordon_height));
521
522 for (uint i = 0; i < rad_crown.size(); i++) {
523 pos_crownw.at(i) = pos_crownw.at(i) + origin;
524 }
525
526 objID = context->addTubeObject(wood_subdivisions, pos_crownw, rad_crown, params.wood_texture_file.c_str());
527 U = context->getObjectPrimitiveUUIDs(objID);
528 UUID_trunk_plant.insert(UUID_trunk_plant.end(), U.begin(), U.end());
529
530 std::vector<vec3> pos_crowne;
531 pos_crowne.push_back(make_vec3(0., 0., 0.95f * trunk_height));
532 pos_crowne.push_back(make_vec3(-0.05f * 0.5f * cordon_spacing * cost, -0.05f * 0.5f * cordon_spacing * sint, trunk_height));
533 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));
534 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));
535 pos_crowne.push_back(make_vec3(-0.75f * 0.5f * cordon_spacing * cost, -0.75f * 0.5f * cordon_spacing * sint, cordon_height));
536
537 for (uint i = 0; i < rad_crown.size(); i++) {
538 pos_crowne.at(i) = pos_crowne.at(i) + origin;
539 }
540
541 objID = context->addTubeObject(wood_subdivisions, pos_crowne, rad_crown, params.wood_texture_file.c_str());
542 U = context->getObjectPrimitiveUUIDs(objID);
543 UUID_trunk_plant.insert(UUID_trunk_plant.end(), U.begin(), U.end());
544
545 //---- Cordons -----//
546
547 float cordon_length = std::max(0.f, params.cordon_length + getVariation(params.cordon_length_spread, generator));
548
549 std::vector<float> rad_cord;
550 rad_cord.push_back(cordon_radius);
551 rad_cord.push_back(0.95f * cordon_radius);
552 rad_cord.push_back(0.9f * cordon_radius);
553 rad_cord.push_back(0.9f * cordon_radius);
554 rad_cord.push_back(0.9f * cordon_radius);
555 rad_cord.push_back(0.6f * cordon_radius);
556 rad_cord.push_back(0.2f * cordon_radius);
557
558 // West Cordon
559 std::vector<vec3> pos_cordnw;
560 pos_cordnw.push_back(make_vec3(0.7f * 0.5f * cordon_spacing * cost, 0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
561 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));
562 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));
563 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + 0.12f * sint, 0.5f * cordon_spacing * sint + 0.12f * cost, cordon_height));
564 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));
565 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));
566 pos_cordnw.push_back(make_vec3(0.5f * cordon_spacing * cost + cordon_length * sint, 0.5f * cordon_spacing * sint + cordon_length * cost, cordon_height));
567
568 std::vector<vec3> tmp;
569 tmp.resize(pos_cordnw.size());
570 for (uint i = 0; i < pos_cordnw.size(); i++) {
571 tmp.at(i) = pos_cordnw.at(i) + origin;
572 }
573
574 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
575 UUID_branch_plant = context->getObjectPrimitiveUUIDs(objID);
576
577 std::vector<vec3> pos_cordsw;
578 pos_cordsw.push_back(make_vec3(0.7f * 0.5f * cordon_spacing * cost, 0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
579 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));
580 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));
581 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - 0.12f * sint, 0.5f * cordon_spacing * sint - 0.12f * cost, cordon_height));
582 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));
583 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));
584 pos_cordsw.push_back(make_vec3(0.5f * cordon_spacing * cost - cordon_length * sint, 0.5f * cordon_spacing * sint - cordon_length * cost, cordon_height));
585
586 tmp.resize(pos_cordsw.size());
587 for (uint i = 0; i < pos_cordsw.size(); i++) {
588 tmp.at(i) = pos_cordsw.at(i) + origin;
589 }
590
591 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
592 U = context->getObjectPrimitiveUUIDs(objID);
593 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
594
595 // East Cordon
596 std::vector<vec3> pos_cordne;
597 pos_cordne.push_back(make_vec3(-0.7f * 0.5f * cordon_spacing * cost, -0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
598 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));
599 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));
600 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + 0.12f * sint, -0.5f * cordon_spacing * sint + 0.12f * cost, cordon_height));
601 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));
602 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));
603 pos_cordne.push_back(make_vec3(-0.5f * cordon_spacing * cost + cordon_length * sint, -0.5f * cordon_spacing * sint + cordon_length * cost, cordon_height));
604
605 tmp.resize(pos_cordne.size());
606 for (uint i = 0; i < pos_cordne.size(); i++) {
607 tmp.at(i) = pos_cordne.at(i) + origin;
608 }
609
610 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
611 U = context->getObjectPrimitiveUUIDs(objID);
612 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
613
614 std::vector<vec3> pos_cordse;
615 pos_cordse.push_back(make_vec3(-0.7f * 0.5f * cordon_spacing * cost, -0.7f * 0.5f * cordon_spacing * sint, 0.99f * cordon_height));
616 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));
617 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));
618 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - 0.12f * sint, -0.5f * cordon_spacing * sint - 0.12f * cost, cordon_height));
619 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));
620 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));
621 pos_cordse.push_back(make_vec3(-0.5f * cordon_spacing * cost - cordon_length * sint, -0.5f * cordon_spacing * sint - cordon_length * cost, cordon_height));
622
623 tmp.resize(pos_cordse.size());
624 for (uint i = 0; i < pos_cordse.size(); i++) {
625 tmp.at(i) = pos_cordse.at(i) + origin;
626 }
627
628 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
629 U = context->getObjectPrimitiveUUIDs(objID);
630 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
631
632 //------- primary shoots ---------//
633
634 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());
635
636 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
637 float shoot_radius = std::max(1e-5f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
638 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
639 float shoot_angle_base = params.shoot_angle_base + getVariation(params.shoot_angle_base_spread, generator);
640 float shoot_angle_tip = params.shoot_angle_tip + getVariation(params.shoot_angle_tip_spread, generator);
641
642 float height = cordon_height + shoot_length;
643
644 for (uint d = 0; d < 2; d++) { // cordons
645 for (uint c = 0; c < 2; c++) { // shoot bend direction
646
647 std::vector<vec3> pos_cord;
648 float sign;
649 if (c == 0) {
650 if (d == 0) {
651 pos_cord = pos_cordsw;
652 } else {
653 pos_cord = pos_cordnw;
654 }
655 sign = 1;
656 } else {
657 if (d == 0) {
658 pos_cord = pos_cordse;
659 } else {
660 pos_cord = pos_cordne;
661 }
662 sign = -1;
663 }
664
665 float dx = fabs(pos_cord.back().y - pos_cord.at(0).y) / (float(shoots_per_cordon));
666
667 for (int j = 1; j < shoots_per_cordon + 1; j++) {
668
669 float frac_shoot = float(j) / float(shoots_per_cordon);
670
671 vec3 cane_base = interpolateTube(pos_cord, frac_shoot);
672
673 std::vector<float> rad_pshoot;
674 std::vector<vec3> pos_pshoot;
675
676 // cane base
677 rad_pshoot.push_back(shoot_radius);
678 pos_pshoot.push_back(cane_base);
679
680 // cane nodes
681 bool inside = false;
682 float phirot = 0.5f * PI_F * (1 + (-0.5f + unif_distribution(generator)) * 1.0) + canopy_rotation;
683 if (unif_distribution(generator) < 0.5) {
684 phirot += PI_F;
685 if (c == 0) {
686 inside = true;
687 }
688 } else {
689 if (c == 1) {
690 inside = true;
691 }
692 }
693
694 float theta0;
695 if (inside) {
696 theta0 = 0.5f * PI_F * unif_distribution(generator); //*(1.f+(-0.5+unif_distribution(generator))*0.6);
697 } else {
698 theta0 = shoot_angle_base * (1.f + (-0.5 + unif_distribution(generator)) * 0.6);
699 }
700 float theta_end = shoot_angle_tip * (1.f + (-0.5 + unif_distribution(generator)) * 0.6);
701
702 uint Nz = 2 * wood_subdivisions;
703 float dz = ((1 + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
704 for (uint k = 1; k < Nz; k++) {
705
706 vec3 n = rotatePoint(make_vec3(0, 0, dz), (theta0 + (theta_end - theta0) * float(k) / float(Nz - 1)), phirot);
707
708 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0));
709
710 rad_pshoot.push_back(shoot_radius);
711 }
712
713 rad_pshoot.back() = 0.0f;
714
715 std::vector<vec3> tmp;
716 tmp.resize(pos_pshoot.size());
717 for (uint i = 0; i < pos_pshoot.size(); i++) {
718 tmp.at(i) = pos_pshoot.at(i) + origin;
719 }
720
721 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
722 U = context->getObjectPrimitiveUUIDs(objID);
723 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
724
725 if (is_dead) {
726 // Don't add grapes and leaves
727 continue;
728 }
729
730 // grape clusters
731 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
732 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
733 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
734 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
735 std::vector<std::vector<uint>> UUID_grapes;
736 if (grape_radius > 0 && cluster_radius > 0) {
737
738 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
739 vec3 p_grape = interpolateTube(tmp, fgrape);
740 int sgn = 1;
741 if (unif_distribution(generator) < 0.5) {
742 sgn = -1;
743 }
744 vec3 offset(sgn * (2.2 * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2 * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
745
746 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
747 }
748 UUID_fruit_plant.push_back(UUID_grapes);
749
750 // leaves
751 if (params.leaf_width == 0) {
752 continue;
753 }
754 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
755
756 float flip = 0;
757 if (unif_distribution(generator) < 0.5) {
758 flip = 1;
759 }
760 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
761 float lfrac = 1.f;
762 int iter = 0;
763 while (lfrac > 0.5 * leaf_width && iter < 100) {
764 iter++;
765
766 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
767
768 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
769
770 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
771 parent_normal.normalize();
772 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);
773
774 float s;
775 if (int(flip) % 2 == 0) {
776 s = 1;
777 } else {
778 s = -1;
779 }
780
781 float Rphi = -canopy_rotation - s * 0.5f * PI_F * (1.f + getVariation(0.4f, generator));
782 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
783
784 vec3 position = origin + pos_leaf + leaf_offset;
785
786 uint ID = context->copyObject(ID0);
787 context->scaleObject(ID, make_vec3(lsize, lsize, 1));
788 context->rotateObject(ID, -Rtheta, "y");
789 context->rotateObject(ID, Rphi, "z");
790 context->translateObject(ID, position);
791
792 UUID_leaf_plant.push_back(context->getObjectPrimitiveUUIDs(ID));
793
794 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
795
796 flip++;
797 }
798 }
799 }
800 }
801
802 context->deleteObject(ID0);
803
804 UUID_trunk.push_back(UUID_trunk_plant);
805 UUID_branch.push_back(UUID_branch_plant);
806 UUID_leaf.push_back(UUID_leaf_plant);
807 UUID_fruit.push_back(UUID_fruit_plant);
808
809 return UUID_leaf.size() - 1;
810}
811
813
814 float mean_shoot_angle = 0.1 * PI_F;
815
816 vector<uint> U;
817 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
818 std::vector<std::vector<uint>> UUID_leaf_plant;
819 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
820
821 std::uniform_real_distribution<float> unif_distribution;
822
823 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
824
825 float cost = cosf(canopy_rotation);
826 float sint = sinf(canopy_rotation);
827
828 bool is_dead = false;
829 if (params.dead_probability > 0) {
830 float random_draw = context->randu();
831 is_dead = random_draw <= params.dead_probability;
832 }
833
834 float cordon_length = std::max(0.f, params.cordon_length + getVariation(params.cordon_length_spread, generator));
835
836 //------ trunks -------//
837
838 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
839 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
840
841 std::vector<float> rad_main;
842 rad_main.push_back(0.75 * trunk_radius);
843 rad_main.push_back(0.8f * trunk_radius);
844 rad_main.push_back(1.f * trunk_radius);
845 rad_main.push_back(0.7f * trunk_radius);
846 rad_main.push_back(0.95f * trunk_radius);
847 rad_main.push_back(0.1 * trunk_radius);
848 std::vector<vec3> pos_main;
849 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.0f));
850 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.2f * trunk_height));
851 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.22f * trunk_height));
852 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.6f * trunk_height));
853 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, 0.96f * trunk_height));
854 pos_main.push_back(make_vec3((-cordon_length + 0.5f * trunk_radius) * cost, (-cordon_length + 0.5f * trunk_radius) * sint, trunk_height));
855
856 for (uint i = 0; i < rad_main.size(); i++) {
857 pos_main.at(i) = pos_main.at(i) + origin;
858 }
859
860 int wood_subdivisions = std::max(0, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
861
862 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
863 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
864
865 //---- Cordons -----//
866
867 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
868 float cordon_radius = std::max(1e-5f, params.cordon_radius + getVariation(params.cordon_radius_spread, generator));
869
870 float total_cordons_length = 2 * cordon_length;
871
872 float diff = cordon_height - trunk_height;
873
874 // Cordon
875 std::vector<float> rad_cord;
876 rad_cord.push_back(cordon_radius);
877 rad_cord.push_back(0.95 * cordon_radius);
878 rad_cord.push_back(0.9f * cordon_radius);
879 rad_cord.push_back(0.85f * cordon_radius);
880 rad_cord.push_back(0.8 * cordon_radius);
881 rad_cord.push_back(0.6 * cordon_radius);
882 rad_cord.push_back(0.2f * cordon_radius);
883 std::vector<vec3> pos_cord;
884 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));
885 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));
886 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));
887 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));
888 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));
889 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));
890 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));
891
892 std::vector<vec3> tmp;
893 tmp.resize(pos_cord.size());
894 for (uint i = 0; i < pos_cord.size(); i++) {
895 tmp.at(i) = pos_cord.at(i) + origin;
896 }
897
898 objID = context->addTubeObject(wood_subdivisions, tmp, rad_cord, params.wood_texture_file.c_str());
899 UUID_branch_plant = context->getObjectPrimitiveUUIDs(objID);
900
901 //------- primary shoots ---------//
902
903 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());
904
905 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
906 float shoot_radius = std::max(1e-5f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
907 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
908
909 float height = cordon_height + shoot_length;
910
911 float dx = fabs(pos_cord.back().y - pos_cord.at(0).y) / (float(shoots_per_cordon));
912
913 for (int j = 1; j < shoots_per_cordon + 1; j++) {
914
915 float frac_shoot = float(j) / float(shoots_per_cordon);
916
917 vec3 cane_base = interpolateTube(pos_cord, frac_shoot);
918
919 std::vector<float> rad_pshoot;
920 std::vector<vec3> pos_pshoot;
921
922 // cane base
923 rad_pshoot.push_back(shoot_radius);
924 pos_pshoot.push_back(cane_base);
925
926 // cane nodes
927 float phirot = (0.5f - unif_distribution(generator)) * 0.5 * PI_F;
928 phirot = 0.5 * PI_F;
929 if (unif_distribution(generator) < 0.5) {
930 phirot += PI_F;
931 }
932
933 float theta0 = (0.3f - unif_distribution(generator)) * 0.6;
934 float theta_end = (0.1f - unif_distribution(generator)) * 0.2;
935
936 uint Nz = 2 * wood_subdivisions;
937 float dz = ((1 + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
938 for (uint k = 1; k < Nz; k++) {
939
940 vec3 n = rotatePoint(make_vec3(0, 0, dz), mean_shoot_angle * PI_F / 180.f * (theta0 + 1.2 * float(k) / float(Nz - 1)), phirot);
941
942 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0));
943
944 rad_pshoot.push_back(shoot_radius);
945 }
946
947 rad_pshoot.back() = 0.0f;
948
949 std::vector<vec3> tmp;
950 tmp.resize(pos_pshoot.size());
951 for (uint i = 0; i < pos_pshoot.size(); i++) {
952 tmp.at(i) = pos_pshoot.at(i) + origin;
953 }
954
955 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
956 U = context->getObjectPrimitiveUUIDs(objID);
957 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
958
959 if (is_dead) {
960 // Don't add grapes and leaves
961 continue;
962 }
963
964 // grape clusters
965 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
966 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
967 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
968 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
969 std::vector<std::vector<uint>> UUID_grapes;
970 if (grape_radius > 0 && cluster_radius > 0) {
971
972 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
973 vec3 p_grape = interpolateTube(tmp, fgrape);
974 int sgn = 1;
975 if (unif_distribution(generator) < 0.5) {
976 sgn = -1;
977 }
978 vec3 offset(sgn * (2.2f * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2 * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
979 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
980 }
981 UUID_fruit_plant.push_back(UUID_grapes);
982
983 // leaves
984 if (params.leaf_width == 0) {
985 continue;
986 }
987 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
988
989 float flip = 0;
990 if (unif_distribution(generator) < 0.5) {
991 flip = 1;
992 }
993 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
994 float lfrac = 1.f;
995 int iter = 0;
996 while (lfrac > 0.5 * leaf_width && iter < 100) {
997 iter++;
998
999 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
1000
1001 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
1002
1003 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
1004 parent_normal.normalize();
1005 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);
1006
1007 float s;
1008 if (int(flip) % 2 == 0) {
1009 s = 1;
1010 } else {
1011 s = -1;
1012 }
1013
1014 float Rphi = -canopy_rotation - s * 0.5f * PI_F * (1.f + getVariation(0.4f, generator));
1015 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
1016
1017 vec3 position = origin + pos_leaf - leaf_offset;
1018
1019 uint ID = context->copyObject(ID0);
1020 context->scaleObject(ID, make_vec3(lsize, lsize, 1));
1021 context->rotateObject(ID, -Rtheta, "y");
1022 context->rotateObject(ID, Rphi, "z");
1023 context->translateObject(ID, position);
1024
1025 UUID_leaf_plant.push_back(context->getObjectPrimitiveUUIDs(ID));
1026
1027 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
1028
1029 flip++;
1030 }
1031 }
1032
1033 context->deleteObject(ID0);
1034
1035 UUID_trunk.push_back(UUID_trunk_plant);
1036 UUID_branch.push_back(UUID_branch_plant);
1037 UUID_leaf.push_back(UUID_leaf_plant);
1038 UUID_fruit.push_back(UUID_fruit_plant);
1039
1040 return UUID_leaf.size() - 1;
1041}
1042
1044
1045 vector<uint> U;
1046 std::vector<uint> UUID_trunk_plant, UUID_branch_plant;
1047 std::vector<std::vector<uint>> UUID_leaf_plant;
1048 std::vector<std::vector<std::vector<uint>>> UUID_fruit_plant;
1049
1050 std::uniform_real_distribution<float> unif_distribution;
1051
1052 float canopy_rotation = params.canopy_rotation + getVariation(params.canopy_rotation_spread, generator);
1053
1054 float cost = cosf(canopy_rotation);
1055 float sint = sinf(canopy_rotation);
1056
1057 bool is_dead = false;
1058 if (params.dead_probability > 0) {
1059 float random_draw = context->randu();
1060 is_dead = random_draw <= params.dead_probability;
1061 }
1062
1063 //------ trunks -------//
1064
1065 float trunk_radius = std::max(1e-5f, params.trunk_radius + getVariation(params.trunk_radius_spread, generator));
1066 float trunk_height = std::max(0.f, params.trunk_height + getVariation(params.trunk_height_spread, generator));
1067
1068 std::vector<float> rad_main;
1069 rad_main.push_back(0.75 * trunk_radius);
1070 rad_main.push_back(0.8f * trunk_radius);
1071 rad_main.push_back(1.f * trunk_radius);
1072 rad_main.push_back(0.7f * trunk_radius);
1073 rad_main.push_back(0.95f * trunk_radius);
1074 rad_main.push_back(0.1 * trunk_radius);
1075 std::vector<vec3> pos_main;
1076 pos_main.push_back(make_vec3(0., 0., 0.0));
1077 pos_main.push_back(make_vec3(0, 0, 0.2f * trunk_height));
1078 pos_main.push_back(make_vec3(0, 0, 0.22f * trunk_height));
1079 pos_main.push_back(make_vec3(0, 0, 0.6f * trunk_height));
1080 pos_main.push_back(make_vec3(0, 0, 0.96f * trunk_height));
1081 pos_main.push_back(make_vec3(0., 0., trunk_height));
1082
1083 for (uint i = 0; i < rad_main.size(); i++) {
1084 pos_main.at(i) = pos_main.at(i) + origin;
1085 }
1086
1087 int wood_subdivisions = std::max(0, params.wood_subdivisions + getVariation(params.wood_subdivisions_spread, generator));
1088
1089 uint objID = context->addTubeObject(wood_subdivisions, pos_main, rad_main, params.wood_texture_file.c_str());
1090 UUID_trunk_plant = context->getObjectPrimitiveUUIDs(objID);
1091
1092 //------- primary shoots ---------//
1093
1094 float cordon_height = std::max(0.f, params.cordon_height + getVariation(params.cordon_height_spread, generator));
1095
1096 float shoot_length = std::max(0.f, params.shoot_length + getVariation(params.shoot_length_spread, generator));
1097 float shoot_radius = std::max(0.f, params.shoot_radius + getVariation(params.shoot_radius_spread, generator));
1098 uint shoots_per_cordon = std::max(0u, params.shoots_per_cordon + getVariation(params.shoots_per_cordon_spread, generator));
1099
1100 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());
1101
1102 for (uint c = 0; c < 2; c++) {
1103
1104 for (int j = 1; j < shoots_per_cordon + 1; j++) {
1105
1106 float frac_shoot = float(j) / float(shoots_per_cordon);
1107
1108 float height = (cordon_height + shoot_length) * (1.f + 0.55 * (1 - frac_shoot));
1109
1110 vec3 cane_base = make_vec3(0., 0., trunk_height);
1111
1112 std::vector<float> rad_pshoot;
1113 std::vector<vec3> pos_pshoot;
1114
1115 // cane base
1116 rad_pshoot.push_back(shoot_radius);
1117 pos_pshoot.push_back(cane_base);
1118
1119 float theta0 = 0.5 * PI_F * (1.f - float(j - 1) / float(shoots_per_cordon));
1120 float theta_end = (0.1f - unif_distribution(generator)) * 0.2;
1121
1122 uint Nz = 2 * wood_subdivisions;
1123 float dz = ((1.f + getVariation(0.1f, generator)) * height - cordon_height) / float(Nz);
1124 for (uint k = 1; k < Nz; k++) {
1125
1126 vec3 n = rotatePoint(make_vec3(0, 0, dz), (theta0 + (theta_end - theta0) * float(k) / float(Nz - 1)), canopy_rotation + PI_F * float(c));
1127
1128 pos_pshoot.push_back(pos_pshoot.back() + n + make_vec3(getVariation(0.02f, generator), getVariation(0.01f, generator), 0.f));
1129
1130 rad_pshoot.push_back(shoot_radius);
1131 }
1132
1133 rad_pshoot.back() = 0.0f;
1134
1135 std::vector<vec3> tmp;
1136 tmp.resize(pos_pshoot.size());
1137 for (uint i = 0; i < pos_pshoot.size(); i++) {
1138 tmp.at(i) = pos_pshoot.at(i) + origin;
1139 }
1140
1141 objID = context->addTubeObject(wood_subdivisions, tmp, rad_pshoot, params.wood_texture_file.c_str());
1142 U = context->getObjectPrimitiveUUIDs(objID);
1143 UUID_branch_plant.insert(UUID_branch_plant.end(), U.begin(), U.end());
1144
1145 if (is_dead) {
1146 // Don't add grapes and leaves
1147 continue;
1148 }
1149
1150 // grape clusters
1151 float grape_radius = std::max(0.f, params.grape_radius + getVariation(params.grape_radius_spread, generator));
1152 float cluster_radius = std::max(0.f, params.cluster_radius + getVariation(params.cluster_radius_spread, generator));
1153 float cluster_height_max = std::max(0.f, params.cluster_height_max + getVariation(params.cluster_height_max_spread, generator));
1154 uint grape_subdivisions = std::max(0u, params.grape_subdivisions + getVariation(params.grape_subdivisions_spread, generator));
1155 std::vector<std::vector<uint>> UUID_grapes;
1156 if (grape_radius > 0 && cluster_radius > 0) {
1157
1158 float fgrape = 0.035 + (cluster_height_max - 0.035) * unif_distribution(generator);
1159 vec3 p_grape = interpolateTube(tmp, fgrape);
1160 int sgn = 1;
1161 if (unif_distribution(generator) < 0.5) {
1162 sgn = -1;
1163 }
1164 vec3 offset(sgn * (2.2f * cluster_radius + getVariation(0.1f, generator)) * sint, sgn * (2.f * cluster_radius + getVariation(0.1f, generator)) * cost, 0.f);
1165 UUID_grapes = addGrapeCluster(p_grape + offset, grape_radius, cluster_radius, params.grape_color, grape_subdivisions);
1166 }
1167 UUID_fruit_plant.push_back(UUID_grapes);
1168
1169 // leaves
1170 if (params.leaf_width == 0) {
1171 continue;
1172 }
1173 float leaf_width = std::max(0.f, params.leaf_width + getVariation(params.leaf_width_spread, generator));
1174
1175 float flip = 0;
1176 if (unif_distribution(generator) < 0.5) {
1177 flip = 1;
1178 }
1179 float leaf_spacing_fraction = std::max(0.f, params.leaf_spacing_fraction + getVariation(params.leaf_spacing_fraction_spread, generator));
1180 float lfrac = 1.f;
1181 int iter = 0;
1182 while (lfrac > 0.5 * leaf_width && iter < 100) {
1183 iter++;
1184
1185 float lsize = fmaxf(leaf_width * (1.f - exp(-5.f * (1 - lfrac))), 0.1 * leaf_width);
1186
1187 vec3 pos_leaf = interpolateTube(pos_pshoot, lfrac);
1188
1189 vec3 parent_normal = interpolateTube(pos_pshoot, fmax(0, lfrac - 0.001)) - pos_leaf;
1190 parent_normal.normalize();
1191 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);
1192
1193 float s;
1194 if (int(flip) % 2 == 0) {
1195 s = 1;
1196 } else {
1197 s = -1;
1198 }
1199
1200 float Rphi = -canopy_rotation - s * 0.5 * PI_F * (1.f + getVariation(0.4f, generator));
1201 float Rtheta = 0.4f * PI_F * (1.f + getVariation(0.1f, generator));
1202
1203 vec3 position = origin + pos_leaf - leaf_offset;
1204
1205 uint ID = context->copyObject(ID0);
1206 context->scaleObject(ID, make_vec3(lsize, lsize, 1));
1207 context->rotateObject(ID, -Rtheta, "y");
1208 context->rotateObject(ID, Rphi, "z");
1209 context->translateObject(ID, position);
1210
1211 UUID_leaf_plant.push_back(context->getObjectPrimitiveUUIDs(ID));
1212
1213 lfrac = lfrac - leaf_spacing_fraction * lsize * (1.f + getVariation(0.25f, generator));
1214
1215 flip++;
1216 }
1217 }
1218 }
1219
1220 context->deleteObject(ID0);
1221
1222 UUID_trunk.push_back(UUID_trunk_plant);
1223 UUID_branch.push_back(UUID_branch_plant);
1224 UUID_leaf.push_back(UUID_leaf_plant);
1225 UUID_fruit.push_back(UUID_fruit_plant);
1226
1227 return UUID_leaf.size() - 1;
1228}