Blender V5.0
boids.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 by Janne Karhu. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cmath>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
15#include "DNA_scene_types.h"
16
17#include "BLI_kdtree.h"
18#include "BLI_listbase.h"
19#include "BLI_math_base_safe.h"
20#include "BLI_math_rotation.h"
21#include "BLI_math_vector.h"
22#include "BLI_rand.h"
23#include "BLI_string.h"
24#include "BLI_string_utf8.h"
25#include "BLI_utildefines.h"
26
27#include "BKE_boids.h"
28#include "BKE_collision.h"
29#include "BKE_effect.h"
30#include "BKE_particle.h"
31#include "BLI_kdopbvh.hh"
32
33#include "BLT_translation.hh"
34
35#include "BKE_modifier.hh"
36
37#include "RNA_enum_types.hh"
38
39static float len_squared_v3v3_with_normal_bias(const float co_search[3],
40 const float co_test[3],
41 const void *user_data)
42{
43 const float *normal = static_cast<const float *>(user_data);
44 float d[3], dist;
45
46 sub_v3_v3v3(d, co_test, co_search);
47
48 dist = len_squared_v3(d);
49
50 /* Avoid head-on collisions. */
51 if (dot_v3v3(d, normal) < 0.0f) {
52 dist *= 10.0f;
53 }
54 return dist;
55}
56
62
63static bool apply_boid_rule(
64 BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness);
65
66static bool rule_none(BoidRule * /*rule*/,
67 BoidBrainData * /*data*/,
68 BoidValues * /*val*/,
69 ParticleData * /*pa*/)
70{
71 return false;
72}
73
75{
77 BoidSettings *boids = bbd->part->boids;
78 BoidParticle *bpa = pa->boid;
79 EffectedPoint epoint;
80 ListBase *effectors = bbd->sim->psys->effectors;
81 EffectorCache *eff = nullptr;
82 EffectorCache temp_eff;
83 EffectorData efd, cur_efd;
84 float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0);
85 float priority = 0.0f, len = 0.0f;
86 bool ret = false;
87
88 int p = 0;
89 efd.index = cur_efd.index = &p;
90
91 pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
92
93 /* first find out goal/predator with highest priority */
94 if (effectors) {
95 LISTBASE_FOREACH (EffectorCache *, cur, effectors) {
96 Object *eob = cur->ob;
97 PartDeflect *pd = cur->pd;
98
99 if (gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != bpa->ground)) {
100 if (gabr->ob == eob) {
101 /* TODO: effectors with multiple points */
102 if (get_effector_data(cur, &efd, &epoint, 0)) {
103 if (cur->pd && cur->pd->forcefield == PFIELD_BOID) {
104 priority = mul * pd->f_strength *
105 effector_falloff(cur, &efd, &epoint, bbd->part->effector_weights);
106 }
107 else {
108 priority = 1.0;
109 }
110
111 eff = cur;
112 }
113 break;
114 }
115 }
116 else if (rule->type == eBoidRuleType_Goal && eob == bpa->ground) {
117 /* skip current object */
118 }
119 else if (pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f &&
120 get_effector_data(cur, &cur_efd, &epoint, 0))
121 {
122 float temp = mul * pd->f_strength *
123 effector_falloff(cur, &cur_efd, &epoint, bbd->part->effector_weights);
124
125 if (temp == 0.0f) {
126 /* do nothing */
127 }
128 else if (temp > priority) {
129 priority = temp;
130 eff = cur;
131 efd = cur_efd;
132 len = efd.distance;
133 }
134 /* choose closest object with same priority */
135 else if (temp == priority && efd.distance < len) {
136 eff = cur;
137 efd = cur_efd;
138 len = efd.distance;
139 }
140 }
141 }
142 }
143
144 /* if the object doesn't have effector data we have to fake it */
145 if (eff == nullptr && gabr->ob) {
146 memset(&temp_eff, 0, sizeof(EffectorCache));
147 temp_eff.ob = gabr->ob;
148 temp_eff.depsgraph = bbd->sim->depsgraph;
149 temp_eff.scene = bbd->sim->scene;
150 eff = &temp_eff;
151 get_effector_data(eff, &efd, &epoint, 0);
152 priority = 1.0f;
153 }
154
155 /* Then use that effector. */
156
157 /* With avoid, factor is "fear factor". */
158 if (priority > (rule->type == eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) {
159 Object *eob = eff->ob;
160 PartDeflect *pd = eff->pd;
161 float surface = (pd && pd->shape == PFIELD_SHAPE_SURFACE) ? 1.0f : 0.0f;
162
163 if (gabr->options & BRULE_GOAL_AVOID_PREDICT) {
164 /* estimate future location of target */
165 get_effector_data(eff, &efd, &epoint, 1);
166
167 mul_v3_fl(efd.vel, efd.distance / (val->max_speed * bbd->timestep));
168 add_v3_v3(efd.loc, efd.vel);
170 efd.distance = len_v3(efd.vec_to_point);
171 }
172
173 if (rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface != 0.0f) {
174 if (!bbd->goal_ob || bbd->goal_priority < priority) {
175 bbd->goal_ob = eob;
176 copy_v3_v3(bbd->goal_co, efd.loc);
177 copy_v3_v3(bbd->goal_nor, efd.nor);
178 }
179 }
180 else if ((rule->type == eBoidRuleType_Avoid) && (bpa->data.mode == eBoidMode_Climbing) &&
181 (priority > 2.0f * gabr->fear_factor))
182 {
183 /* detach from surface and try to fly away from danger */
185 }
186
188 mul_v3_fl(bbd->wanted_co, mul);
189
190 bbd->wanted_speed = val->max_speed * priority;
191
192 /* with goals factor is approach velocity factor */
193 if (rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) {
194 float len2 = 2.0f * len_v3(pa->prev_state.vel);
195
196 surface *= pa->size * boids->height;
197
198 if (len2 > 0.0f && efd.distance - surface < len2) {
199 len2 = (efd.distance - surface) / len2;
200 bbd->wanted_speed *= powf(len2, boids->landing_smoothness);
201 }
202 }
203
204 ret = true;
205 }
206
207 return ret;
208}
209
211 BoidBrainData *bbd,
212 BoidValues *val,
213 ParticleData *pa)
214{
215 const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT;
217 KDTreeNearest_3d *ptn = nullptr;
218 BoidParticle *bpa = pa->boid;
219 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
220 float co1[3], vel1[3], co2[3], vel2[3];
221 float len, t, inp, t_min = 2.0f;
222 int n, neighbors = 0, nearest = 0;
223 bool ret = false;
224
225 /* Check deflector objects first. */
226 if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) {
228 BVHTreeRayHit hit;
229 float radius = val->personal_space * pa->size, ray_dir[3];
230
231 memset(&col, 0, sizeof(ParticleCollision));
232
233 copy_v3_v3(col.co1, pa->prev_state.co);
234 add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel);
235 sub_v3_v3v3(ray_dir, col.co2, col.co1);
236 mul_v3_fl(ray_dir, acbr->look_ahead);
237 col.f = 0.0f;
238 hit.index = -1;
239 hit.dist = col.original_ray_length = normalize_v3(ray_dir);
240
241 /* find out closest deflector object */
243 /* don't check with current ground object */
244 if (coll->ob == bpa->ground) {
245 continue;
246 }
247
248 col.current = coll->ob;
249 col.md = coll->collmd;
250
251 if (col.md && col.md->bvhtree) {
252 BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
253 col.co1,
254 ray_dir,
255 radius,
256 &hit,
258 &col,
259 raycast_flag);
260 }
261 }
262 /* then avoid that object */
263 if (hit.index >= 0) {
264 t = hit.dist / col.original_ray_length;
265
266 /* avoid head-on collision */
267 if (dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) {
268 /* don't know why, but uneven range [0.0, 1.0] */
269 /* works much better than even [-1.0, 1.0] */
270 bbd->wanted_co[0] = BLI_rng_get_float(bbd->rng);
271 bbd->wanted_co[1] = BLI_rng_get_float(bbd->rng);
272 bbd->wanted_co[2] = BLI_rng_get_float(bbd->rng);
273 }
274 else {
275 copy_v3_v3(bbd->wanted_co, col.pce.nor);
276 }
277
278 mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size);
279
280 bbd->wanted_speed = sqrtf(t) * len_v3(pa->prev_state.vel);
281 bbd->wanted_speed = std::max(bbd->wanted_speed, val->min_speed);
282
283 return true;
284 }
285 }
286
287 /* Check boids in their own system. */
288 if (acbr->options & BRULE_ACOLL_WITH_BOIDS) {
289 neighbors = BLI_kdtree_3d_range_search_with_len_squared_cb(bbd->sim->psys->tree,
290 pa->prev_state.co,
291 &ptn,
292 acbr->look_ahead *
293 len_v3(pa->prev_state.vel),
295 pa->prev_state.ave);
296 if (neighbors > 1) {
297 for (n = 1; n < neighbors; n++) {
298 copy_v3_v3(co1, pa->prev_state.co);
299 copy_v3_v3(vel1, pa->prev_state.vel);
300 copy_v3_v3(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co);
301 copy_v3_v3(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel);
302
303 sub_v3_v3v3(loc, co1, co2);
304
305 sub_v3_v3v3(vec, vel1, vel2);
306
307 inp = dot_v3v3(vec, vec);
308
309 /* velocities not parallel */
310 if (inp != 0.0f) {
311 t = -dot_v3v3(loc, vec) / inp;
312 /* cpa is not too far in the future so investigate further */
313 if (t > 0.0f && t < t_min) {
314 madd_v3_v3fl(co1, vel1, t);
315 madd_v3_v3fl(co2, vel2, t);
316
317 sub_v3_v3v3(vec, co2, co1);
318
319 len = normalize_v3(vec);
320
321 /* distance of cpa is close enough */
322 if (len < 2.0f * val->personal_space * pa->size) {
323 t_min = t;
324
325 mul_v3_fl(vec, len_v3(vel1));
326 mul_v3_fl(vec, (2.0f - t) / 2.0f);
327 sub_v3_v3v3(bbd->wanted_co, vel1, vec);
328 bbd->wanted_speed = len_v3(bbd->wanted_co);
329 ret = true;
330 }
331 }
332 }
333 }
334 }
335 }
336 MEM_SAFE_FREE(ptn);
337
338 /* check boids in other systems */
340 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
341
342 if (epsys) {
343 BLI_assert(epsys->tree != nullptr);
344 neighbors = BLI_kdtree_3d_range_search_with_len_squared_cb(epsys->tree,
345 pa->prev_state.co,
346 &ptn,
347 acbr->look_ahead *
348 len_v3(pa->prev_state.vel),
350 pa->prev_state.ave);
351
352 if (neighbors > 0) {
353 for (n = 0; n < neighbors; n++) {
354 copy_v3_v3(co1, pa->prev_state.co);
355 copy_v3_v3(vel1, pa->prev_state.vel);
356 copy_v3_v3(co2, (epsys->particles + ptn[n].index)->prev_state.co);
357 copy_v3_v3(vel2, (epsys->particles + ptn[n].index)->prev_state.vel);
358
359 sub_v3_v3v3(loc, co1, co2);
360
361 sub_v3_v3v3(vec, vel1, vel2);
362
363 inp = dot_v3v3(vec, vec);
364
365 /* velocities not parallel */
366 if (inp != 0.0f) {
367 t = -dot_v3v3(loc, vec) / inp;
368 /* cpa is not too far in the future so investigate further */
369 if (t > 0.0f && t < t_min) {
370 madd_v3_v3fl(co1, vel1, t);
371 madd_v3_v3fl(co2, vel2, t);
372
373 sub_v3_v3v3(vec, co2, co1);
374
375 len = normalize_v3(vec);
376
377 /* distance of cpa is close enough */
378 if (len < 2.0f * val->personal_space * pa->size) {
379 t_min = t;
380
381 mul_v3_fl(vec, len_v3(vel1));
382 mul_v3_fl(vec, (2.0f - t) / 2.0f);
383 sub_v3_v3v3(bbd->wanted_co, vel1, vec);
384 bbd->wanted_speed = len_v3(bbd->wanted_co);
385 ret = true;
386 }
387 }
388 }
389 }
390 }
391
392 MEM_SAFE_FREE(ptn);
393 }
394 }
395
396 if (ptn && nearest == 0) {
397 MEM_freeN(ptn);
398 }
399
400 return ret;
401}
402static bool rule_separate(BoidRule * /*rule*/,
403 BoidBrainData *bbd,
404 BoidValues *val,
405 ParticleData *pa)
406{
407 KDTreeNearest_3d *ptn = nullptr;
408 float len = 2.0f * val->personal_space * pa->size + 1.0f;
409 float vec[3] = {0.0f, 0.0f, 0.0f};
410 int neighbors = BLI_kdtree_3d_range_search(
411 bbd->sim->psys->tree, pa->prev_state.co, &ptn, 2.0f * val->personal_space * pa->size);
412 bool ret = false;
413
414 if (neighbors > 1 && ptn[1].dist != 0.0f) {
415 sub_v3_v3v3(vec, pa->prev_state.co, bbd->sim->psys->particles[ptn[1].index].state.co);
416 mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist);
417 add_v3_v3(bbd->wanted_co, vec);
418 bbd->wanted_speed = val->max_speed;
419 len = ptn[1].dist;
420 ret = true;
421 }
422 MEM_SAFE_FREE(ptn);
423
424 /* check other boid systems */
426 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
427
428 if (epsys) {
429 neighbors = BLI_kdtree_3d_range_search(
430 epsys->tree, pa->prev_state.co, &ptn, 2.0f * val->personal_space * pa->size);
431
432 if (neighbors > 0 && ptn[0].dist < len) {
433 sub_v3_v3v3(vec, pa->prev_state.co, ptn[0].co);
434 mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist);
435 add_v3_v3(bbd->wanted_co, vec);
436 bbd->wanted_speed = val->max_speed;
437 len = ptn[0].dist;
438 ret = true;
439 }
440
441 MEM_SAFE_FREE(ptn);
442 }
443 }
444 return ret;
445}
446static bool rule_flock(BoidRule * /*rule*/,
447 BoidBrainData *bbd,
448 BoidValues * /*val*/,
449 ParticleData *pa)
450{
451 KDTreeNearest_3d ptn[11];
452 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
453 int neighbors = BLI_kdtree_3d_find_nearest_n_with_len_squared_cb(
454 bbd->sim->psys->tree,
455 pa->state.co,
456 ptn,
457 ARRAY_SIZE(ptn),
459 pa->prev_state.ave);
460 int n;
461 bool ret = false;
462
463 if (neighbors > 1) {
464 for (n = 1; n < neighbors; n++) {
465 add_v3_v3(loc, bbd->sim->psys->particles[ptn[n].index].prev_state.co);
466 add_v3_v3(vec, bbd->sim->psys->particles[ptn[n].index].prev_state.vel);
467 }
468
469 mul_v3_fl(loc, 1.0f / (float(neighbors) - 1.0f));
470 mul_v3_fl(vec, 1.0f / (float(neighbors) - 1.0f));
471
472 sub_v3_v3(loc, pa->prev_state.co);
473 sub_v3_v3(vec, pa->prev_state.vel);
474
475 add_v3_v3(bbd->wanted_co, vec);
476 add_v3_v3(bbd->wanted_co, loc);
477 bbd->wanted_speed = len_v3(bbd->wanted_co);
478
479 ret = true;
480 }
481 return ret;
482}
483static bool rule_follow_leader(BoidRule *rule,
484 BoidBrainData *bbd,
485 BoidValues *val,
486 ParticleData *pa)
487{
489 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
490 float mul, len;
491 const int n = (flbr->queue_size <= 1) ? bbd->sim->psys->totpart : flbr->queue_size;
493 const int p = int(pa - bbd->sim->psys->particles);
494 int i;
495 bool ret = false;
496
497 if (flbr->ob) {
498 float vec2[3], t;
499
500 /* first check we're not blocking the leader */
501 sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
502 mul_v3_fl(vec, 1.0f / bbd->timestep);
503
504 sub_v3_v3v3(loc, pa->prev_state.co, flbr->oloc);
505
506 mul = dot_v3v3(vec, vec);
507
508 /* leader is not moving */
509 if (mul < 0.01f) {
510 len = len_v3(loc);
511 /* too close to leader */
512 if (len < 2.0f * val->personal_space * pa->size) {
513 copy_v3_v3(bbd->wanted_co, loc);
514 bbd->wanted_speed = val->max_speed;
515 return true;
516 }
517 }
518 else {
519 t = dot_v3v3(loc, vec) / mul;
520
521 /* possible blocking of leader in near future */
522 if (t > 0.0f && t < 3.0f) {
523 copy_v3_v3(vec2, vec);
524 mul_v3_fl(vec2, t);
525
526 sub_v3_v3v3(vec2, loc, vec2);
527
528 len = len_v3(vec2);
529
530 if (len < 2.0f * val->personal_space * pa->size) {
531 copy_v3_v3(bbd->wanted_co, vec2);
532 bbd->wanted_speed = val->max_speed * (3.0f - t) / 3.0f;
533 return true;
534 }
535 }
536 }
537
538 /* not blocking so try to follow leader */
539 if (p && flbr->options & BRULE_LEADER_IN_LINE) {
540 copy_v3_v3(vec, bbd->sim->psys->particles[p - 1].prev_state.vel);
541 copy_v3_v3(loc, bbd->sim->psys->particles[p - 1].prev_state.co);
542 }
543 else {
544 copy_v3_v3(loc, flbr->oloc);
545 sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
546 mul_v3_fl(vec, 1.0f / bbd->timestep);
547 }
548
549 /* fac is seconds behind leader */
550 madd_v3_v3fl(loc, vec, -flbr->distance);
551
552 sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
553 bbd->wanted_speed = len_v3(bbd->wanted_co);
554
555 ret = true;
556 }
557 else if (p % n) {
558 float vec2[3], t, t_min = 3.0f;
559
560 /* first check we're not blocking any leaders */
561 for (i = 0; i < bbd->sim->psys->totpart; i += n) {
563
565
566 mul = dot_v3v3(vec, vec);
567
568 /* leader is not moving */
569 if (mul < 0.01f) {
570 len = len_v3(loc);
571 /* too close to leader */
572 if (len < 2.0f * val->personal_space * pa->size) {
573 copy_v3_v3(bbd->wanted_co, loc);
574 bbd->wanted_speed = val->max_speed;
575 return true;
576 }
577 }
578 else {
579 t = dot_v3v3(loc, vec) / mul;
580
581 /* possible blocking of leader in near future */
582 if (t > 0.0f && t < t_min) {
583 copy_v3_v3(vec2, vec);
584 mul_v3_fl(vec2, t);
585
586 sub_v3_v3v3(vec2, loc, vec2);
587
588 len = len_v3(vec2);
589
590 if (len < 2.0f * val->personal_space * pa->size) {
591 t_min = t;
592 copy_v3_v3(bbd->wanted_co, loc);
593 bbd->wanted_speed = val->max_speed * (3.0f - t) / 3.0f;
594 ret = true;
595 }
596 }
597 }
598 }
599
600 if (ret) {
601 return true;
602 }
603
604 /* not blocking so try to follow leader */
605 if (flbr->options & BRULE_LEADER_IN_LINE) {
606 copy_v3_v3(vec, bbd->sim->psys->particles[p - 1].prev_state.vel);
607 copy_v3_v3(loc, bbd->sim->psys->particles[p - 1].prev_state.co);
608 }
609 else {
610 copy_v3_v3(vec, bbd->sim->psys->particles[p - p % n].prev_state.vel);
611 copy_v3_v3(loc, bbd->sim->psys->particles[p - p % n].prev_state.co);
612 }
613
614 /* fac is seconds behind leader */
615 madd_v3_v3fl(loc, vec, -flbr->distance);
616
617 sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
618 bbd->wanted_speed = len_v3(bbd->wanted_co);
619
620 ret = true;
621 }
622
623 return ret;
624}
625static bool rule_average_speed(BoidRule *rule,
626 BoidBrainData *bbd,
627 BoidValues *val,
628 ParticleData *pa)
629{
630 BoidParticle *bpa = pa->boid;
632 float vec[3] = {0.0f, 0.0f, 0.0f};
633
634 if (asbr->wander > 0.0f) {
635 /* abuse pa->r_ave for wandering */
636 bpa->wander[0] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
637 bpa->wander[1] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
638 bpa->wander[2] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
639
640 normalize_v3(bpa->wander);
641
642 copy_v3_v3(vec, bpa->wander);
643
644 mul_qt_v3(pa->prev_state.rot, vec);
645
647
648 mul_v3_fl(bbd->wanted_co, 1.1f);
649
650 add_v3_v3(bbd->wanted_co, vec);
651
652 /* leveling */
653 if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
655 mul_v3_fl(vec, asbr->level);
656 sub_v3_v3(bbd->wanted_co, vec);
657 }
658 }
659 else {
661
662 /* may happen at birth */
663 if (dot_v2v2(bbd->wanted_co, bbd->wanted_co) == 0.0f) {
664 bbd->wanted_co[0] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
665 bbd->wanted_co[1] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
666 bbd->wanted_co[2] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
667 }
668
669 /* leveling */
670 if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
672 mul_v3_fl(vec, asbr->level);
673 sub_v3_v3(bbd->wanted_co, vec);
674 }
675 }
676 bbd->wanted_speed = asbr->speed * val->max_speed;
677
678 return true;
679}
680static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
681{
682 BoidRuleFight *fbr = (BoidRuleFight *)rule;
683 KDTreeNearest_3d *ptn = nullptr;
684 ParticleData *epars;
685 ParticleData *enemy_pa = nullptr;
686 BoidParticle *bpa;
687 /* friends & enemies */
688 float closest_enemy[3] = {0.0f, 0.0f, 0.0f};
689 float closest_dist = fbr->distance + 1.0f;
690 float f_strength = 0.0f, e_strength = 0.0f;
691 float health = 0.0f;
692 int n;
693 bool ret = false;
694
695 /* calculate its own group strength */
696 int neighbors = BLI_kdtree_3d_range_search(
697 bbd->sim->psys->tree, pa->prev_state.co, &ptn, fbr->distance);
698 for (n = 0; n < neighbors; n++) {
699 bpa = bbd->sim->psys->particles[ptn[n].index].boid;
700 health += bpa->data.health;
701 }
702
703 f_strength += bbd->part->boids->strength * health;
704
705 MEM_SAFE_FREE(ptn);
706
707 /* add other friendlies and calculate enemy strength and find closest enemy */
709 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
710 if (epsys && epsys->part->boids) {
711 epars = epsys->particles;
712
713 neighbors = BLI_kdtree_3d_range_search(epsys->tree, pa->prev_state.co, &ptn, fbr->distance);
714
715 health = 0.0f;
716
717 for (n = 0; n < neighbors; n++) {
718 bpa = epars[ptn[n].index].boid;
719 health += bpa->data.health;
720
721 if (n == 0 && pt->mode == PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) {
722 copy_v3_v3(closest_enemy, ptn[n].co);
723 closest_dist = ptn[n].dist;
724 enemy_pa = epars + ptn[n].index;
725 }
726 }
727 if (pt->mode == PTARGET_MODE_ENEMY) {
728 e_strength += epsys->part->boids->strength * health;
729 }
730 else if (pt->mode == PTARGET_MODE_FRIEND) {
731 f_strength += epsys->part->boids->strength * health;
732 }
733
734 MEM_SAFE_FREE(ptn);
735 }
736 }
737 /* decide action if enemy presence found */
738 if (e_strength > 0.0f) {
739 sub_v3_v3v3(bbd->wanted_co, closest_enemy, pa->prev_state.co);
740
741 /* attack if in range */
742 if (closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) {
743 float damage = BLI_rng_get_float(bbd->rng);
744 float enemy_dir[3];
745
746 normalize_v3_v3(enemy_dir, bbd->wanted_co);
747
748 /* fight mode */
749 bbd->wanted_speed = 0.0f;
750
751 /* must face enemy to fight */
752 if (dot_v3v3(pa->prev_state.ave, enemy_dir) > 0.5f) {
753 bpa = enemy_pa->boid;
754 bpa->data.health -= bbd->part->boids->strength * bbd->timestep *
755 ((1.0f - bbd->part->boids->accuracy) * damage +
756 bbd->part->boids->accuracy);
757 }
758 }
759 else {
760 /* approach mode */
761 bbd->wanted_speed = val->max_speed;
762 }
763
764 /* check if boid doesn't want to fight */
765 bpa = pa->boid;
766 if (bpa->data.health / bbd->part->boids->health * bbd->part->boids->aggression <
767 e_strength / f_strength)
768 {
769 /* decide to flee */
770 if (closest_dist < fbr->flee_distance * fbr->distance) {
771 negate_v3(bbd->wanted_co);
772 bbd->wanted_speed = val->max_speed;
773 }
774 else { /* wait for better odds */
775 bbd->wanted_speed = 0.0f;
776 }
777 }
778
779 ret = true;
780 }
781
782 return ret;
783}
784
785using boid_rule_cb = bool (*)(BoidRule *rule,
787 BoidValues *val,
788 ParticleData *pa);
789
791 rule_none,
800#if 0
801 rule_help,
802 rule_protect,
803 rule_hide,
804 rule_follow_path,
805 rule_follow_wall,
806#endif
807};
808
810{
811 BoidParticle *bpa = pa->boid;
812
814 val->max_speed = boids->land_max_speed * bpa->data.health / boids->health;
815 val->max_acc = boids->land_max_acc * val->max_speed;
816 val->max_ave = boids->land_max_ave * float(M_PI) * bpa->data.health / boids->health;
817 val->min_speed = 0.0f; /* no minimum speed on land */
819 val->jump_speed = boids->land_jump_speed * bpa->data.health / boids->health;
820 }
821 else {
822 val->max_speed = boids->air_max_speed * bpa->data.health / boids->health;
823 val->max_acc = boids->air_max_acc * val->max_speed;
824 val->max_ave = boids->air_max_ave * float(M_PI) * bpa->data.health / boids->health;
825 val->min_speed = boids->air_min_speed * boids->air_max_speed;
827 val->jump_speed = 0.0f; /* no jumping in air */
828 }
829}
830
832 ParticleData *pa,
833 float ground_co[3],
834 float ground_nor[3])
835{
836 const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT;
837 BoidParticle *bpa = pa->boid;
838
839 if (bpa->data.mode == eBoidMode_Climbing) {
840 SurfaceModifierData *surmd = nullptr;
841 float x[3], v[3];
842
844
845 /* take surface velocity into account */
846 closest_point_on_surface(surmd, pa->state.co, x, nullptr, v);
847 add_v3_v3(x, v);
848
849 /* get actual position on surface */
850 closest_point_on_surface(surmd, x, ground_co, ground_nor, nullptr);
851
852 return bpa->ground;
853 }
854
855 const float zvec[3] = {0.0f, 0.0f, 2000.0f};
857 BVHTreeRayHit hit;
858 float radius = 0.0f, t, ray_dir[3];
859
860 if (!bbd->sim->colliders) {
861 return nullptr;
862 }
863
864 memset(&col, 0, sizeof(ParticleCollision));
865
866 /* first try to find below boid */
867 copy_v3_v3(col.co1, pa->state.co);
868 sub_v3_v3v3(col.co2, pa->state.co, zvec);
869 sub_v3_v3v3(ray_dir, col.co2, col.co1);
870 col.f = 0.0f;
871 hit.index = -1;
872 hit.dist = col.original_ray_length = normalize_v3(ray_dir);
873 col.pce.inside = 0;
874
876 col.current = coll->ob;
877 col.md = coll->collmd;
878 col.fac1 = col.fac2 = 0.0f;
879
880 if (col.md && col.md->bvhtree) {
881 BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
882 col.co1,
883 ray_dir,
884 radius,
885 &hit,
887 &col,
888 raycast_flag);
889 }
890 }
891 /* then use that object */
892 if (hit.index >= 0) {
893 t = hit.dist / col.original_ray_length;
894 interp_v3_v3v3(ground_co, col.co1, col.co2, t);
895 normalize_v3_v3(ground_nor, col.pce.nor);
896 return col.hit;
897 }
898
899 /* couldn't find below, so find upmost deflector object */
900 add_v3_v3v3(col.co1, pa->state.co, zvec);
901 sub_v3_v3v3(col.co2, pa->state.co, zvec);
902 sub_v3_v3(col.co2, zvec);
903 sub_v3_v3v3(ray_dir, col.co2, col.co1);
904 col.f = 0.0f;
905 hit.index = -1;
906 hit.dist = col.original_ray_length = normalize_v3(ray_dir);
907
909 col.current = coll->ob;
910 col.md = coll->collmd;
911
912 if (col.md && col.md->bvhtree) {
913 BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
914 col.co1,
915 ray_dir,
916 radius,
917 &hit,
919 &col,
920 raycast_flag);
921 }
922 }
923 /* then use that object */
924 if (hit.index >= 0) {
925 t = hit.dist / col.original_ray_length;
926 interp_v3_v3v3(ground_co, col.co1, col.co2, t);
927 normalize_v3_v3(ground_nor, col.pce.nor);
928 return col.hit;
929 }
930
931 /* default to z=0 */
932 copy_v3_v3(ground_co, pa->state.co);
933 ground_co[2] = 0;
934 ground_nor[0] = ground_nor[1] = 0.0f;
935 ground_nor[2] = 1.0f;
936 return nullptr;
937}
938static bool boid_rule_applies(ParticleData *pa, BoidSettings * /*boids*/, BoidRule *rule)
939{
940 BoidParticle *bpa = pa->boid;
941
942 if (rule == nullptr) {
943 return false;
944 }
945
947 {
948 return true;
949 }
950
951 if (bpa->data.mode == eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR) {
952 return true;
953 }
954
955 return false;
956}
958{
960 LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
961 if (rule->type == eBoidRuleType_FollowLeader) {
963
964 if (flbr->ob && flbr->cfra != cfra) {
965 /* save object locations for velocity calculations */
966 copy_v3_v3(flbr->oloc, flbr->loc);
967 copy_v3_v3(flbr->loc, flbr->ob->object_to_world().location());
968 flbr->cfra = cfra;
969 }
970 }
971 }
972 }
973}
974static void boid_climb(BoidSettings *boids,
975 ParticleData *pa,
976 float *surface_co,
977 float *surface_nor)
978{
979 BoidParticle *bpa = pa->boid;
980 float nor[3], vel[3];
981 copy_v3_v3(nor, surface_nor);
982
983 /* gather apparent gravity */
984 madd_v3_v3fl(bpa->gravity, surface_nor, -1.0f);
985 normalize_v3(bpa->gravity);
986
987 /* raise boid it's size from surface */
988 mul_v3_fl(nor, pa->size * boids->height);
989 add_v3_v3v3(pa->state.co, surface_co, nor);
990
991 /* remove normal component from velocity */
992 project_v3_v3v3(vel, pa->state.vel, surface_nor);
993 sub_v3_v3v3(pa->state.vel, pa->state.vel, vel);
994}
995static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor)
996{
997 float vec[3];
998
999 sub_v3_v3v3(vec, boid_co, goal_co);
1000
1001 return dot_v3v3(vec, goal_nor);
1002}
1003/* wanted_co is relative to boid location */
1005 BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness)
1006{
1007 if (rule == nullptr) {
1008 return false;
1009 }
1010
1011 if (!boid_rule_applies(pa, bbd->part->boids, rule)) {
1012 return false;
1013 }
1014
1015 if (!boid_rules[rule->type](rule, bbd, val, pa)) {
1016 return false;
1017 }
1018
1019 if (fuzziness < 0.0f || compare_len_v3v3(bbd->wanted_co,
1020 pa->prev_state.vel,
1021 fuzziness * len_v3(pa->prev_state.vel)) == 0)
1022 {
1023 return true;
1024 }
1025 return false;
1026}
1028{
1029 BoidState *state = static_cast<BoidState *>(boids->states.first);
1030 BoidParticle *bpa = pa->boid;
1031
1032 for (; state; state = state->next) {
1033 if (state->id == bpa->data.state_id) {
1034 return state;
1035 }
1036 }
1037
1038 /* for some reason particle isn't at a valid state */
1039 state = static_cast<BoidState *>(boids->states.first);
1040 if (state) {
1041 bpa->data.state_id = state->id;
1042 }
1043
1044 return state;
1045}
1046
1047#if 0 /* TODO */
1048static int boid_condition_is_true(BoidCondition *cond)
1049{
1050 return 0;
1051}
1052#endif
1053
1055{
1056 BoidRule *rule;
1057 BoidSettings *boids = bbd->part->boids;
1058 BoidValues val;
1059 BoidState *state = get_boid_state(boids, pa);
1060 BoidParticle *bpa = pa->boid;
1061 ParticleSystem *psys = bbd->sim->psys;
1062 int rand;
1063
1064 if (bpa->data.health <= 0.0f) {
1065 pa->alive = PARS_DYING;
1066 pa->dietime = bbd->cfra;
1067 return;
1068 }
1069
1070/* Planned for near future. */
1071#if 0
1072 BoidCondition *cond = state->conditions.first;
1073 for (; cond; cond = cond->next) {
1074 if (boid_condition_is_true(cond)) {
1075 pa->boid->state_id = cond->state_id;
1076 state = get_boid_state(boids, pa);
1077 break; /* only first true condition is used */
1078 }
1079 }
1080#endif
1081
1082 zero_v3(bbd->wanted_co);
1083 bbd->wanted_speed = 0.0f;
1084
1085 /* create random seed for every particle & frame */
1086 rand = int(psys_frand(psys, psys->seed + p) * 1000);
1087 rand = int(psys_frand(psys, int(bbd->cfra) + rand) * 1000);
1088
1089 set_boid_values(&val, bbd->part->boids, pa);
1090
1091 /* go through rules */
1092 switch (state->ruleset_type) {
1094 LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
1095 if (apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness)) {
1096 break; /* only first nonzero rule that comes through fuzzy rule is applied */
1097 }
1098 }
1099 break;
1100 }
1102 /* use random rule for each particle (always same for same particle though) */
1103 const int n = BLI_listbase_count(&state->rules);
1104 if (n) {
1105 rule = static_cast<BoidRule *>(BLI_findlink(&state->rules, rand % n));
1106 apply_boid_rule(bbd, rule, &val, pa, -1.0);
1107 }
1108 break;
1109 }
1111 float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f;
1112 int n = 0;
1113 LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
1114 if (apply_boid_rule(bbd, rule, &val, pa, -1.0f)) {
1115 add_v3_v3(wanted_co, bbd->wanted_co);
1116 wanted_speed += bbd->wanted_speed;
1117 n++;
1118 zero_v3(bbd->wanted_co);
1119 bbd->wanted_speed = 0.0f;
1120 }
1121 }
1122
1123 if (n > 1) {
1124 mul_v3_fl(wanted_co, 1.0f / float(n));
1125 wanted_speed /= float(n);
1126 }
1127
1128 copy_v3_v3(bbd->wanted_co, wanted_co);
1129 bbd->wanted_speed = wanted_speed;
1130 break;
1131 }
1132 }
1133
1134 /* decide on jumping & liftoff */
1135 if (bpa->data.mode == eBoidMode_OnLand) {
1136 /* Fuzziness makes boids capable of misjudgment. */
1137 float mul = 1.0f + state->rule_fuzziness;
1138
1139 if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) {
1140 float cvel[3], dir[3];
1141
1142 copy_v3_v3(dir, pa->prev_state.ave);
1143 normalize_v2(dir);
1144
1145 copy_v3_v3(cvel, bbd->wanted_co);
1146 normalize_v2(cvel);
1147
1148 if (dot_v2v2(cvel, dir) > 0.95f / mul) {
1150 }
1151 }
1152 else if (val.jump_speed > 0.0f) {
1153 float jump_v[3];
1154 int jump = 0;
1155
1156 /* jump to get to a location */
1157 if (bbd->wanted_co[2] > 0.0f) {
1158 float cvel[3], dir[3];
1159 float z_v, ground_v, cur_v;
1160 float len;
1161
1162 copy_v3_v3(dir, pa->prev_state.ave);
1163 normalize_v2(dir);
1164
1165 copy_v3_v3(cvel, bbd->wanted_co);
1166 normalize_v2(cvel);
1167
1168 len = len_v2(pa->prev_state.vel);
1169
1170 /* first of all, are we going in a suitable direction? */
1171 /* or at a suitably slow speed */
1172 if (dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) {
1173 /* try to reach goal at highest point of the parabolic path */
1174 cur_v = len_v2(pa->prev_state.vel);
1175 z_v = safe_sqrtf(-2.0f * bbd->sim->scene->physics_settings.gravity[2] *
1176 bbd->wanted_co[2]);
1177 ground_v = len_v2(bbd->wanted_co) *
1178 safe_sqrtf(-0.5f * bbd->sim->scene->physics_settings.gravity[2] /
1179 bbd->wanted_co[2]);
1180
1181 len = safe_sqrtf((ground_v - cur_v) * (ground_v - cur_v) + z_v * z_v);
1182
1183 if (len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) {
1184 jump = 1;
1185
1186 len = std::min(len, val.jump_speed);
1187
1188 copy_v3_v3(jump_v, dir);
1189 jump_v[2] = z_v;
1190 mul_v3_fl(jump_v, ground_v);
1191
1192 normalize_v3(jump_v);
1193 mul_v3_fl(jump_v, len);
1194 add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel);
1195 }
1196 }
1197 }
1198
1199 /* jump to go faster */
1200 if (jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) {
1201 /* pass */
1202 }
1203
1204 if (jump) {
1205 copy_v3_v3(pa->prev_state.vel, jump_v);
1207 }
1208 }
1209 }
1210}
1212{
1213 BoidSettings *boids = bbd->part->boids;
1214 BoidParticle *bpa = pa->boid;
1215 BoidValues val;
1216 EffectedPoint epoint;
1217 float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3];
1218 float dvec[3], bvec[3];
1219 float new_dir[3], new_speed;
1220 float old_dir[3], old_speed;
1221 float wanted_dir[3];
1222 float q[4], mat[3][3]; /* rotation */
1223 float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f};
1224 float force[3] = {0.0f, 0.0f, 0.0f};
1225 float pa_mass = bbd->part->mass, dtime = bbd->dfra * bbd->timestep;
1226
1227 set_boid_values(&val, boids, pa);
1228
1229 /* make sure there's something in new velocity, location & rotation */
1230 copy_particle_key(&pa->state, &pa->prev_state, 0);
1231
1232 if (bbd->part->flag & PART_SIZEMASS) {
1233 pa_mass *= pa->size;
1234 }
1235
1236 /* if boids can't fly they fall to the ground */
1237 if ((boids->options & BOID_ALLOW_FLIGHT) == 0 &&
1239 psys_uses_gravity(bbd->sim))
1240 {
1242 }
1243
1244 if (bpa->data.mode == eBoidMode_Falling) {
1245 /* Falling boids are only effected by gravity. */
1246 acc[2] = bbd->sim->scene->physics_settings.gravity[2];
1247 }
1248 else {
1249 /* figure out acceleration */
1250 float landing_level = 2.0f;
1251 float level = landing_level + 1.0f;
1252 float new_vel[3];
1253
1254 if (bpa->data.mode == eBoidMode_Liftoff) {
1255 bpa->data.mode = eBoidMode_InAir;
1256 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
1257 }
1258 else if (bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) {
1259 /* auto-leveling & landing if close to ground */
1260
1261 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
1262
1263 /* level = how many particle sizes above ground */
1264 level = (pa->prev_state.co[2] - ground_co[2]) / (2.0f * pa->size) - 0.5f;
1265
1266 landing_level = -boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass;
1267
1268 if (pa->prev_state.vel[2] < 0.0f) {
1269 if (level < 1.0f) {
1270 bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f;
1271 bbd->wanted_speed = 0.0f;
1273 }
1274 else if (level < landing_level) {
1275 bbd->wanted_speed *= (level - 1.0f) / landing_level;
1276 bbd->wanted_co[2] *= (level - 1.0f) / landing_level;
1277 }
1278 }
1279 }
1280
1281 copy_v3_v3(old_dir, pa->prev_state.ave);
1282 new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co);
1283
1284 /* first check if we have valid direction we want to go towards */
1285 if (new_speed == 0.0f) {
1286 copy_v3_v3(new_dir, old_dir);
1287 }
1288 else {
1289 float old_dir2[2], wanted_dir2[2], nor[3], angle;
1290 copy_v2_v2(old_dir2, old_dir);
1291 normalize_v2(old_dir2);
1292 copy_v2_v2(wanted_dir2, wanted_dir);
1293 normalize_v2(wanted_dir2);
1294
1295 /* choose random direction to turn if wanted velocity */
1296 /* is directly behind regardless of z-coordinate */
1297 if (dot_v2v2(old_dir2, wanted_dir2) < -0.99f) {
1298 wanted_dir[0] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
1299 wanted_dir[1] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
1300 wanted_dir[2] = 2.0f * (0.5f - BLI_rng_get_float(bbd->rng));
1301 normalize_v3(wanted_dir);
1302 }
1303
1304 /* constrain direction with maximum angular velocity */
1305 angle = safe_acosf(dot_v3v3(old_dir, wanted_dir));
1306 angle = min_ff(angle, val.max_ave);
1307
1308 cross_v3_v3v3(nor, old_dir, wanted_dir);
1310 copy_v3_v3(new_dir, old_dir);
1311 mul_qt_v3(q, new_dir);
1312 normalize_v3(new_dir);
1313
1314 /* save direction in case resulting velocity too small */
1315 axis_angle_to_quat(q, nor, angle * dtime);
1316 copy_v3_v3(pa->state.ave, old_dir);
1317 mul_qt_v3(q, pa->state.ave);
1318 normalize_v3(pa->state.ave);
1319 }
1320
1321 /* constrain speed with maximum acceleration */
1322 old_speed = len_v3(pa->prev_state.vel);
1323
1324 if (bbd->wanted_speed < old_speed) {
1325 new_speed = std::max(bbd->wanted_speed, old_speed - val.max_acc);
1326 }
1327 else {
1328 new_speed = std::min(bbd->wanted_speed, old_speed + val.max_acc);
1329 }
1330
1331 /* combine direction and speed */
1332 copy_v3_v3(new_vel, new_dir);
1333 mul_v3_fl(new_vel, new_speed);
1334
1335 /* maintain minimum flying velocity if not landing */
1336 if (level >= landing_level) {
1337 float len2 = dot_v2v2(new_vel, new_vel);
1338 float root;
1339
1340 len2 = std::max(len2, val.min_speed * val.min_speed);
1341 root = safe_sqrtf(new_speed * new_speed - len2);
1342
1343 new_vel[2] = new_vel[2] < 0.0f ? -root : root;
1344
1345 normalize_v2(new_vel);
1346 mul_v2_fl(new_vel, safe_sqrtf(len2));
1347 }
1348
1349 /* finally constrain speed to max speed */
1350 new_speed = normalize_v3(new_vel);
1351 mul_v3_fl(new_vel, std::min(new_speed, val.max_speed));
1352
1353 /* get acceleration from difference of velocities */
1354 sub_v3_v3v3(acc, new_vel, pa->prev_state.vel);
1355
1356 /* break acceleration to components */
1357 project_v3_v3v3(tan_acc, acc, pa->prev_state.ave);
1358 sub_v3_v3v3(nor_acc, acc, tan_acc);
1359 }
1360
1361 /* account for effectors */
1362 pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
1364 bbd->sim->colliders,
1365 bbd->part->effector_weights,
1366 &epoint,
1367 force,
1368 nullptr,
1369 nullptr);
1370
1372 float length = normalize_v3(force);
1373
1374 length = std::max(0.0f, length - boids->land_stick_force);
1375
1376 mul_v3_fl(force, length);
1377 }
1378
1379 add_v3_v3(acc, force);
1380
1381 /* store smoothed acceleration for nice banking etc. */
1382 madd_v3_v3fl(bpa->data.acc, acc, dtime);
1383 mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime));
1384
1385 /* integrate new location & velocity */
1386
1387 /* by regarding the acceleration as a force at this stage we
1388 * can get better control although it's a bit unphysical */
1389 mul_v3_fl(acc, 1.0f / pa_mass);
1390
1391 copy_v3_v3(dvec, acc);
1392 mul_v3_fl(dvec, dtime * dtime * 0.5f);
1393
1394 copy_v3_v3(bvec, pa->prev_state.vel);
1395 mul_v3_fl(bvec, dtime);
1396 add_v3_v3(dvec, bvec);
1397 add_v3_v3(pa->state.co, dvec);
1398
1399 madd_v3_v3fl(pa->state.vel, acc, dtime);
1400
1401 // if (bpa->data.mode != eBoidMode_InAir)
1402 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
1403
1404 /* change modes, constrain movement & keep track of down vector */
1405 switch (bpa->data.mode) {
1406 case eBoidMode_InAir: {
1407 float grav[3];
1408
1409 grav[0] = 0.0f;
1410 grav[1] = 0.0f;
1411 grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f;
1412
1413 /* don't take forward acceleration into account (better banking) */
1414 if (dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) {
1415 project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel);
1416 sub_v3_v3v3(dvec, bpa->data.acc, dvec);
1417 }
1418 else {
1419 copy_v3_v3(dvec, bpa->data.acc);
1420 }
1421
1422 /* gather apparent gravity */
1423 madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking);
1424 normalize_v3(bpa->gravity);
1425
1426 /* stick boid on goal when close enough */
1427 if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <=
1428 pa->size * boids->height)
1429 {
1431 bpa->ground = bbd->goal_ob;
1432 boid_find_ground(bbd, pa, ground_co, ground_nor);
1433 boid_climb(boids, pa, ground_co, ground_nor);
1434 }
1435 else if (pa->state.co[2] <= ground_co[2] + pa->size * boids->height) {
1436 /* land boid when below ground */
1437 if (boids->options & BOID_ALLOW_LAND) {
1438 pa->state.co[2] = ground_co[2] + pa->size * boids->height;
1439 pa->state.vel[2] = 0.0f;
1440 bpa->data.mode = eBoidMode_OnLand;
1441 }
1442 /* fly above ground */
1443 else if (bpa->ground) {
1444 pa->state.co[2] = ground_co[2] + pa->size * boids->height;
1445 pa->state.vel[2] = 0.0f;
1446 }
1447 }
1448 break;
1449 }
1450 case eBoidMode_Falling: {
1451 float grav[3];
1452
1453 grav[0] = 0.0f;
1454 grav[1] = 0.0f;
1455 grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f;
1456
1457 /* gather apparent gravity */
1458 madd_v3_v3fl(bpa->gravity, grav, dtime);
1459 normalize_v3(bpa->gravity);
1460
1461 if (boids->options & BOID_ALLOW_LAND) {
1462 /* stick boid on goal when close enough */
1463 if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <=
1464 pa->size * boids->height)
1465 {
1467 bpa->ground = bbd->goal_ob;
1468 boid_find_ground(bbd, pa, ground_co, ground_nor);
1469 boid_climb(boids, pa, ground_co, ground_nor);
1470 }
1471 /* land boid when really near ground */
1472 else if (pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height) {
1473 pa->state.co[2] = ground_co[2] + pa->size * boids->height;
1474 pa->state.vel[2] = 0.0f;
1475 bpa->data.mode = eBoidMode_OnLand;
1476 }
1477 /* if we're falling, can fly and want to go upwards lets fly */
1478 else if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) {
1479 bpa->data.mode = eBoidMode_InAir;
1480 }
1481 }
1482 else {
1483 bpa->data.mode = eBoidMode_InAir;
1484 }
1485 break;
1486 }
1487 case eBoidMode_Climbing: {
1488 boid_climb(boids, pa, ground_co, ground_nor);
1489#if 0
1490 float nor[3];
1491 copy_v3_v3(nor, ground_nor);
1492
1493 /* Gather apparent gravity to r_ve. */
1494 madd_v3_v3fl(pa->r_ve, ground_nor, -1.0);
1495 normalize_v3(pa->r_ve);
1496
1497 /* Raise boid it's size from surface. */
1498 mul_v3_fl(nor, pa->size * boids->height);
1499 add_v3_v3v3(pa->state.co, ground_co, nor);
1500
1501 /* Remove normal component from velocity. */
1502 project_v3_v3v3(v, pa->state.vel, ground_nor);
1503 sub_v3_v3v3(pa->state.vel, pa->state.vel, v);
1504#endif
1505 break;
1506 }
1507 case eBoidMode_OnLand: {
1508 /* stick boid on goal when close enough */
1509 if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <=
1510 pa->size * boids->height)
1511 {
1513 bpa->ground = bbd->goal_ob;
1514 boid_find_ground(bbd, pa, ground_co, ground_nor);
1515 boid_climb(boids, pa, ground_co, ground_nor);
1516 }
1517 /* ground is too far away so boid falls */
1518 else if (pa->state.co[2] - ground_co[2] > 1.1f * pa->size * boids->height) {
1520 }
1521 else {
1522 /* constrain to surface */
1523 pa->state.co[2] = ground_co[2] + pa->size * boids->height;
1524 pa->state.vel[2] = 0.0f;
1525 }
1526
1527 if (boids->banking > 0.0f) {
1528 float grav[3];
1529 /* Don't take gravity's strength in to account, */
1530 /* otherwise amount of banking is hard to control. */
1531 negate_v3_v3(grav, ground_nor);
1532
1533 project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel);
1534 sub_v3_v3v3(dvec, bpa->data.acc, dvec);
1535
1536 /* gather apparent gravity */
1537 madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking);
1538 normalize_v3(bpa->gravity);
1539 }
1540 else {
1541 /* gather negative surface normal */
1542 madd_v3_v3fl(bpa->gravity, ground_nor, -1.0f);
1543 normalize_v3(bpa->gravity);
1544 }
1545 break;
1546 }
1547 }
1548
1549 /* save direction to state.ave unless the boid is falling */
1550 /* (boids can't effect their direction when falling) */
1551 if (bpa->data.mode != eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f * pa->size) {
1552 copy_v3_v3(pa->state.ave, pa->state.vel);
1553 pa->state.ave[2] *= bbd->part->boids->pitch;
1554 normalize_v3(pa->state.ave);
1555 }
1556
1557 /* apply damping */
1559 mul_v3_fl(pa->state.vel, 1.0f - 0.2f * bbd->part->dampfac);
1560 }
1561
1562 /* calculate rotation matrix based on forward & down vectors */
1563 if (bpa->data.mode == eBoidMode_InAir) {
1564 copy_v3_v3(mat[0], pa->state.ave);
1565
1566 project_v3_v3v3(dvec, bpa->gravity, pa->state.ave);
1567 sub_v3_v3v3(mat[2], bpa->gravity, dvec);
1568 normalize_v3(mat[2]);
1569 }
1570 else {
1571 project_v3_v3v3(dvec, pa->state.ave, bpa->gravity);
1572 sub_v3_v3v3(mat[0], pa->state.ave, dvec);
1573 normalize_v3(mat[0]);
1574
1575 copy_v3_v3(mat[2], bpa->gravity);
1576 }
1577 negate_v3(mat[2]);
1578 cross_v3_v3v3(mat[1], mat[2], mat[0]);
1579
1580 /* apply rotation */
1581 mat3_to_quat_legacy(q, mat);
1582 copy_qt_qt(pa->state.rot, q);
1583}
1584
1586{
1587 BoidRule *rule = nullptr;
1588 if (type <= 0) {
1589 return nullptr;
1590 }
1591
1592 switch (type) {
1593 case eBoidRuleType_Goal:
1594 case eBoidRuleType_Avoid: {
1595 BoidRuleGoalAvoid *rule_goal = MEM_callocN<BoidRuleGoalAvoid>("BoidRuleGoalAvoid");
1596 rule = reinterpret_cast<BoidRule *>(rule_goal);
1597 break;
1598 }
1601 "BoidRuleAvoidCollision");
1602 rule_avoid->look_ahead = 2.0f;
1603 rule = reinterpret_cast<BoidRule *>(rule_avoid);
1604 break;
1605 }
1608 "BoidRuleFollowLeader");
1609 rule_follow->distance = 1.0f;
1610 rule = reinterpret_cast<BoidRule *>(rule_follow);
1611 break;
1612 }
1615 "BoidRuleAverageSpeed");
1616 rule_avgspeed->speed = 0.5f;
1617 rule = reinterpret_cast<BoidRule *>(rule_avgspeed);
1618 break;
1619 }
1620 case eBoidRuleType_Fight: {
1622 rule_fight->distance = 100.0f;
1623 rule_fight->flee_distance = 100.0f;
1624 rule = reinterpret_cast<BoidRule *>(rule_fight);
1625 break;
1626 }
1627 default:
1628 rule = MEM_callocN<BoidRule>("BoidRule");
1629 break;
1630 }
1631
1632 rule->type = type;
1635
1636 return rule;
1637}
1639{
1640 boids->air_max_speed = 10.0f;
1641 boids->air_max_acc = 0.5f;
1642 boids->air_max_ave = 0.5f;
1643 boids->air_personal_space = 1.0f;
1644
1645 boids->land_max_speed = 5.0f;
1646 boids->land_max_acc = 0.5f;
1647 boids->land_max_ave = 0.5f;
1648 boids->land_personal_space = 1.0f;
1649
1650 boids->options = BOID_ALLOW_FLIGHT;
1651
1652 boids->landing_smoothness = 3.0f;
1653 boids->banking = 1.0f;
1654 boids->pitch = 1.0f;
1655 boids->height = 1.0f;
1656
1657 boids->health = 1.0f;
1658 boids->accuracy = 1.0f;
1659 boids->aggression = 2.0f;
1660 boids->range = 1.0f;
1661 boids->strength = 0.1f;
1662}
1663
1665{
1666 BoidState *state = MEM_callocN<BoidState>("BoidState");
1667
1668 state->id = boids->last_state_id++;
1669 if (state->id) {
1670 SNPRINTF_UTF8(state->name, "State %i", state->id);
1671 }
1672 else {
1673 STRNCPY_UTF8(state->name, "State");
1674 }
1675
1676 state->rule_fuzziness = 0.5;
1677 state->volume = 1.0f;
1678 state->channels |= ~0;
1679
1680 return state;
1681}
1682
1684{
1685 BoidState *staten = static_cast<BoidState *>(MEM_dupallocN(state));
1686
1687 BLI_duplicatelist(&staten->rules, &state->rules);
1688 BLI_duplicatelist(&staten->conditions, &state->conditions);
1689 BLI_duplicatelist(&staten->actions, &state->actions);
1690
1691 staten->id = boids->last_state_id++;
1692
1693 return staten;
1694}
1696{
1697 if (boids) {
1698 BoidState *state = static_cast<BoidState *>(boids->states.first);
1699
1700 for (; state; state = state->next) {
1701 BLI_freelistN(&state->rules);
1702 BLI_freelistN(&state->conditions);
1703 BLI_freelistN(&state->actions);
1704 }
1705
1706 BLI_freelistN(&boids->states);
1707
1708 MEM_freeN(boids);
1709 }
1710}
1712{
1713 BoidSettings *nboids = nullptr;
1714
1715 if (boids) {
1717 BoidState *nstate;
1718
1719 nboids = static_cast<BoidSettings *>(MEM_dupallocN(boids));
1720
1721 BLI_duplicatelist(&nboids->states, &boids->states);
1722
1723 state = static_cast<BoidState *>(boids->states.first);
1724 nstate = static_cast<BoidState *>(nboids->states.first);
1725 for (; state; state = state->next, nstate = nstate->next) {
1726 BLI_duplicatelist(&nstate->rules, &state->rules);
1727 BLI_duplicatelist(&nstate->conditions, &state->conditions);
1728 BLI_duplicatelist(&nstate->actions, &state->actions);
1729 }
1730 }
1731
1732 return nboids;
1733}
1735{
1736 BoidState *state = static_cast<BoidState *>(boids->states.first);
1737
1738 for (; state; state = state->next) {
1739 if (state->flag & BOIDSTATE_CURRENT) {
1740 break;
1741 }
1742 }
1743
1744 return state;
1745}
float effector_falloff(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, struct EffectorWeights *weights)
Definition effect.cc:586
void BKE_effectors_apply(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *wind_force, float *impulse)
Definition effect.cc:1106
bool get_effector_data(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, int real_velocity)
Definition effect.cc:671
bool closest_point_on_surface(struct SurfaceModifierData *surmd, const float co[3], float surface_co[3], float surface_nor[3], float surface_vel[3])
Definition effect.cc:635
void pd_point_from_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, struct EffectedPoint *point)
Definition effect.cc:378
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time)
Definition particle.cc:3734
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit)
int psys_uses_gravity(struct ParticleSimulationData *sim)
Definition particle.cc:829
struct ParticleSystem * psys_get_target_system(struct Object *ob, struct ParticleTarget *pt)
BLI_INLINE float psys_frand(ParticleSystem *psys, unsigned int seed)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BVH_RAYCAST_DEFAULT
@ BVH_RAYCAST_WATERTIGHT
int BLI_bvhtree_ray_cast_ex(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata, int flag)
A KD-tree for nearest neighbor search.
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void void void void void void BLI_duplicatelist(ListBase *dst, const ListBase *src) ATTR_NONNULL(1
MINLINE float min_ff(float a, float b)
MINLINE float safe_sqrtf(float a)
MINLINE float safe_acosf(float a)
#define M_PI
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void mul_qt_v3(const float q[4], float r[3])
void mat3_to_quat_legacy(float q[4], const float wmat[3][3])
void copy_qt_qt(float q[4], const float a[4])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE bool compare_len_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
Random number functions.
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition rand.cc:88
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
#define ARRAY_SIZE(arr)
#define ARRAY_HAS_ITEM(arr_item, arr_start, arr_len)
#define ELEM(...)
#define DATA_(msgid)
#define BOIDSTATE_CURRENT
@ eBoidMode_OnLand
@ eBoidMode_Liftoff
@ eBoidMode_Climbing
@ eBoidMode_Falling
@ eBoidMode_InAir
#define BRULE_LEADER_IN_LINE
@ BRULE_GOAL_AVOID_PREDICT
@ BOID_ALLOW_FLIGHT
@ BOID_ALLOW_LAND
@ BOID_ALLOW_CLIMB
@ eBoidRulesetType_Average
@ eBoidRulesetType_Fuzzy
@ eBoidRulesetType_Random
@ BOIDRULE_IN_AIR
@ BOIDRULE_ON_LAND
@ eBoidRuleType_Goal
@ eBoidRuleType_Fight
@ eBoidRuleType_Avoid
@ eBoidRuleType_FollowLeader
@ eBoidRuleType_AvoidCollision
@ eBoidRuleType_AverageSpeed
@ BRULE_ACOLL_WITH_DEFLECTORS
@ BRULE_ACOLL_WITH_BOIDS
@ eModifierType_Surface
@ PFIELD_SHAPE_SURFACE
@ PART_SIZEMASS
@ PARS_DYING
@ PTARGET_MODE_ENEMY
@ PTARGET_MODE_FRIEND
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
void boid_free_settings(BoidSettings *boids)
Definition boids.cc:1695
bool(*)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa) boid_rule_cb
Definition boids.cc:785
BoidRule * boid_new_rule(int type)
Definition boids.cc:1585
BoidSettings * boid_copy_settings(const BoidSettings *boids)
Definition boids.cc:1711
static bool rule_flock(BoidRule *, BoidBrainData *bbd, BoidValues *, ParticleData *pa)
Definition boids.cc:446
static bool rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:625
BoidState * boid_get_current_state(BoidSettings *boids)
Definition boids.cc:1734
BoidState * boid_new_state(BoidSettings *boids)
Definition boids.cc:1664
static bool rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:483
static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor)
Definition boids.cc:974
static bool apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness)
Definition boids.cc:1004
void boid_body(BoidBrainData *bbd, ParticleData *pa)
Definition boids.cc:1211
static bool rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:74
void boids_precalc_rules(ParticleSettings *part, float cfra)
Definition boids.cc:957
static bool rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:210
static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:680
static BoidState * get_boid_state(BoidSettings *boids, ParticleData *pa)
Definition boids.cc:1027
static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor)
Definition boids.cc:995
static float len_squared_v3v3_with_normal_bias(const float co_search[3], const float co_test[3], const void *user_data)
Definition boids.cc:39
void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
Definition boids.cc:1054
static bool boid_rule_applies(ParticleData *pa, BoidSettings *, BoidRule *rule)
Definition boids.cc:938
BoidState * boid_duplicate_state(BoidSettings *boids, BoidState *state)
Definition boids.cc:1683
static boid_rule_cb boid_rules[]
Definition boids.cc:790
static bool rule_separate(BoidRule *, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
Definition boids.cc:402
static bool rule_none(BoidRule *, BoidBrainData *, BoidValues *, ParticleData *)
Definition boids.cc:66
static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa)
Definition boids.cc:809
void boid_default_settings(BoidSettings *boids)
Definition boids.cc:1638
static Object * boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float ground_co[3], float ground_nor[3])
Definition boids.cc:831
void jump(const btVector3 &v=btVector3(0, 0, 0))
static void mul(btAlignedObjectArray< T > &items, const Q &value)
nullptr float
#define powf(x, y)
uint nor
uint col
float length(VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
const char * name
return ret
#define sqrtf
const EnumPropertyItem rna_enum_boidrule_type_items[]
Definition rna_boid.cc:22
float wanted_co[3]
Definition BKE_boids.h:23
float goal_nor[3]
Definition BKE_boids.h:28
struct ParticleSettings * part
Definition BKE_boids.h:21
struct Object * goal_ob
Definition BKE_boids.h:26
struct RNG * rng
Definition BKE_boids.h:31
float timestep
Definition BKE_boids.h:22
float wanted_speed
Definition BKE_boids.h:23
float goal_priority
Definition BKE_boids.h:29
struct ParticleSimulationData * sim
Definition BKE_boids.h:20
float goal_co[3]
Definition BKE_boids.h:27
float acc[3]
struct Object * ground
struct BoidData data
struct Object * ob
struct Object * ob
char name[32]
struct ListBase states
float landing_smoothness
float air_personal_space
float land_personal_space
ListBase conditions
ListBase actions
ListBase rules
struct BoidState * next
float personal_space
Definition boids.cc:60
float min_speed
Definition boids.cc:59
float max_ave
Definition boids.cc:59
float max_speed
Definition boids.cc:58
float max_acc
Definition boids.cc:58
float jump_speed
Definition boids.cc:60
struct Scene * scene
Definition BKE_effect.h:69
struct PartDeflect * pd
Definition BKE_effect.h:74
struct Object * ob
Definition BKE_effect.h:70
struct Depsgraph * depsgraph
Definition BKE_effect.h:68
float loc[3]
Definition BKE_effect.h:48
float distance
Definition BKE_effect.h:53
float vec_to_point[3]
Definition BKE_effect.h:52
float nor[3]
Definition BKE_effect.h:49
float vel[3]
Definition BKE_effect.h:50
void * first
BoidParticle * boid
ParticleKey state
ParticleKey prev_state
struct BoidSettings * boids
struct EffectorWeights * effector_weights
struct Depsgraph * depsgraph
struct Scene * scene
struct ParticleSystem * psys
struct Object * ob
struct ListBase * colliders
ParticleData * particles
struct ListBase targets
ParticleSettings * part
struct ListBase * effectors
struct KDTree_3d * tree
struct PhysicsSettings physics_settings
i
Definition text_draw.cc:230
uint len