|
Blender
V2.59
|
00001 /* boids.c 00002 * 00003 * 00004 * $Id: boids.c 38285 2011-07-10 17:04:56Z jhk $ 00005 * 00006 * ***** BEGIN GPL LICENSE BLOCK ***** 00007 * 00008 * This program is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU General Public License 00010 * as published by the Free Software Foundation; either version 2 00011 * of the License, or (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software Foundation, 00020 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00021 * 00022 * The Original Code is Copyright (C) 2009 by Janne Karhu. 00023 * All rights reserved. 00024 * 00025 * The Original Code is: all of this file. 00026 * 00027 * Contributor(s): none yet. 00028 * 00029 * ***** END GPL LICENSE BLOCK ***** 00030 */ 00031 00037 #include <string.h> 00038 #include <math.h> 00039 00040 #include "MEM_guardedalloc.h" 00041 00042 #include "DNA_object_force.h" 00043 #include "DNA_scene_types.h" 00044 00045 #include "BLI_rand.h" 00046 #include "BLI_math.h" 00047 #include "BLI_blenlib.h" 00048 #include "BLI_kdtree.h" 00049 #include "BLI_utildefines.h" 00050 00051 #include "BKE_collision.h" 00052 #include "BKE_effect.h" 00053 #include "BKE_boids.h" 00054 #include "BKE_particle.h" 00055 00056 #include "BKE_modifier.h" 00057 00058 #include "RNA_enum_types.h" 00059 00060 typedef struct BoidValues { 00061 float max_speed, max_acc; 00062 float max_ave, min_speed; 00063 float personal_space, jump_speed; 00064 } BoidValues; 00065 00066 static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness); 00067 00068 static int rule_none(BoidRule *UNUSED(rule), BoidBrainData *UNUSED(data), BoidValues *UNUSED(val), ParticleData *UNUSED(pa)) 00069 { 00070 return 0; 00071 } 00072 00073 static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00074 { 00075 BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*) rule; 00076 BoidSettings *boids = bbd->part->boids; 00077 BoidParticle *bpa = pa->boid; 00078 EffectedPoint epoint; 00079 ListBase *effectors = bbd->sim->psys->effectors; 00080 EffectorCache *cur, *eff = NULL; 00081 EffectorCache temp_eff; 00082 EffectorData efd, cur_efd; 00083 float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0); 00084 float priority = 0.0f, len = 0.0f; 00085 int ret = 0; 00086 00087 pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint); 00088 00089 /* first find out goal/predator with highest priority */ 00090 if(effectors) for(cur = effectors->first; cur; cur=cur->next) { 00091 Object *eob = cur->ob; 00092 PartDeflect *pd = cur->pd; 00093 00094 if(gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != bpa->ground)) { 00095 if(gabr->ob == eob) { 00096 /* TODO: effectors with multiple points */ 00097 if(get_effector_data(cur, &efd, &epoint, 0)) { 00098 if(cur->pd && cur->pd->forcefield == PFIELD_BOID) 00099 priority = mul * pd->f_strength * effector_falloff(cur, &efd, &epoint, bbd->part->effector_weights); 00100 else 00101 priority = 1.0; 00102 00103 eff = cur; 00104 } 00105 break; 00106 } 00107 } 00108 else if(rule->type == eBoidRuleType_Goal && eob == bpa->ground) 00109 ; /* skip current object */ 00110 else if(pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f && get_effector_data(cur, &cur_efd, &epoint, 0)) { 00111 float temp = mul * pd->f_strength * effector_falloff(cur, &cur_efd, &epoint, bbd->part->effector_weights); 00112 00113 if(temp == 0.0f) 00114 ; /* do nothing */ 00115 else if(temp > priority) { 00116 priority = temp; 00117 eff = cur; 00118 efd = cur_efd; 00119 len = efd.distance; 00120 } 00121 /* choose closest object with same priority */ 00122 else if(temp == priority && efd.distance < len) { 00123 eff = cur; 00124 efd = cur_efd; 00125 len = efd.distance; 00126 } 00127 } 00128 } 00129 00130 /* if the object doesn't have effector data we have to fake it */ 00131 if(eff == NULL && gabr->ob) { 00132 memset(&temp_eff, 0, sizeof(EffectorCache)); 00133 temp_eff.ob = gabr->ob; 00134 temp_eff.scene = bbd->sim->scene; 00135 eff = &temp_eff; 00136 get_effector_data(eff, &efd, &epoint, 0); 00137 priority = 1.0f; 00138 } 00139 00140 /* then use that effector */ 00141 if(priority > (rule->type==eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) { /* with avoid, factor is "fear factor" */ 00142 Object *eob = eff->ob; 00143 PartDeflect *pd = eff->pd; 00144 float surface = (pd && pd->shape == PFIELD_SHAPE_SURFACE) ? 1.0f : 0.0f; 00145 00146 if(gabr->options & BRULE_GOAL_AVOID_PREDICT) { 00147 /* estimate future location of target */ 00148 get_effector_data(eff, &efd, &epoint, 1); 00149 00150 mul_v3_fl(efd.vel, efd.distance / (val->max_speed * bbd->timestep)); 00151 add_v3_v3(efd.loc, efd.vel); 00152 sub_v3_v3v3(efd.vec_to_point, pa->prev_state.co, efd.loc); 00153 efd.distance = len_v3(efd.vec_to_point); 00154 } 00155 00156 if(rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface!=0.0f) { 00157 if(!bbd->goal_ob || bbd->goal_priority < priority) { 00158 bbd->goal_ob = eob; 00159 VECCOPY(bbd->goal_co, efd.loc); 00160 VECCOPY(bbd->goal_nor, efd.nor); 00161 } 00162 } 00163 else if(rule->type == eBoidRuleType_Avoid && bpa->data.mode == eBoidMode_Climbing && 00164 priority > 2.0f * gabr->fear_factor) { 00165 /* detach from surface and try to fly away from danger */ 00166 negate_v3_v3(efd.vec_to_point, bpa->gravity); 00167 } 00168 00169 VECCOPY(bbd->wanted_co, efd.vec_to_point); 00170 mul_v3_fl(bbd->wanted_co, mul); 00171 00172 bbd->wanted_speed = val->max_speed * priority; 00173 00174 /* with goals factor is approach velocity factor */ 00175 if(rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) { 00176 float len2 = 2.0f*len_v3(pa->prev_state.vel); 00177 00178 surface *= pa->size * boids->height; 00179 00180 if(len2 > 0.0f && efd.distance - surface < len2) { 00181 len2 = (efd.distance - surface)/len2; 00182 bbd->wanted_speed *= pow(len2, boids->landing_smoothness); 00183 } 00184 } 00185 00186 ret = 1; 00187 } 00188 00189 return ret; 00190 } 00191 00192 static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00193 { 00194 BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule; 00195 KDTreeNearest *ptn = NULL; 00196 ParticleTarget *pt; 00197 BoidParticle *bpa = pa->boid; 00198 ColliderCache *coll; 00199 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; 00200 float co1[3], vel1[3], co2[3], vel2[3]; 00201 float len, t, inp, t_min = 2.0f; 00202 int n, neighbors = 0, nearest = 0; 00203 int ret = 0; 00204 00205 //check deflector objects first 00206 if(acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) { 00207 ParticleCollision col; 00208 BVHTreeRayHit hit; 00209 float radius = val->personal_space * pa->size, ray_dir[3]; 00210 00211 VECCOPY(col.co1, pa->prev_state.co); 00212 add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel); 00213 sub_v3_v3v3(ray_dir, col.co2, col.co1); 00214 mul_v3_fl(ray_dir, acbr->look_ahead); 00215 col.f = 0.0f; 00216 hit.index = -1; 00217 hit.dist = col.original_ray_length = len_v3(ray_dir); 00218 00219 /* find out closest deflector object */ 00220 for(coll = bbd->sim->colliders->first; coll; coll=coll->next) { 00221 /* don't check with current ground object */ 00222 if(coll->ob == bpa->ground) 00223 continue; 00224 00225 col.current = coll->ob; 00226 col.md = coll->collmd; 00227 00228 if(col.md && col.md->bvhtree) 00229 BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); 00230 } 00231 /* then avoid that object */ 00232 if(hit.index>=0) { 00233 t = hit.dist/col.original_ray_length; 00234 00235 /* avoid head-on collision */ 00236 if(dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) { 00237 /* don't know why, but uneven range [0.0,1.0] */ 00238 /* works much better than even [-1.0,1.0] */ 00239 bbd->wanted_co[0] = BLI_frand(); 00240 bbd->wanted_co[1] = BLI_frand(); 00241 bbd->wanted_co[2] = BLI_frand(); 00242 } 00243 else { 00244 copy_v3_v3(bbd->wanted_co, col.pce.nor); 00245 } 00246 00247 mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size); 00248 00249 bbd->wanted_speed = sqrt(t) * len_v3(pa->prev_state.vel); 00250 bbd->wanted_speed = MAX2(bbd->wanted_speed, val->min_speed); 00251 00252 return 1; 00253 } 00254 } 00255 00256 //check boids in own system 00257 if(acbr->options & BRULE_ACOLL_WITH_BOIDS) 00258 { 00259 neighbors = BLI_kdtree_range_search(bbd->sim->psys->tree, acbr->look_ahead * len_v3(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); 00260 if(neighbors > 1) for(n=1; n<neighbors; n++) { 00261 VECCOPY(co1, pa->prev_state.co); 00262 VECCOPY(vel1, pa->prev_state.vel); 00263 VECCOPY(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co); 00264 VECCOPY(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel); 00265 00266 sub_v3_v3v3(loc, co1, co2); 00267 00268 sub_v3_v3v3(vec, vel1, vel2); 00269 00270 inp = dot_v3v3(vec,vec); 00271 00272 /* velocities not parallel */ 00273 if(inp != 0.0f) { 00274 t = -dot_v3v3(loc, vec)/inp; 00275 /* cpa is not too far in the future so investigate further */ 00276 if(t > 0.0f && t < t_min) { 00277 VECADDFAC(co1, co1, vel1, t); 00278 VECADDFAC(co2, co2, vel2, t); 00279 00280 sub_v3_v3v3(vec, co2, co1); 00281 00282 len = normalize_v3(vec); 00283 00284 /* distance of cpa is close enough */ 00285 if(len < 2.0f * val->personal_space * pa->size) { 00286 t_min = t; 00287 00288 mul_v3_fl(vec, len_v3(vel1)); 00289 mul_v3_fl(vec, (2.0f - t)/2.0f); 00290 sub_v3_v3v3(bbd->wanted_co, vel1, vec); 00291 bbd->wanted_speed = len_v3(bbd->wanted_co); 00292 ret = 1; 00293 } 00294 } 00295 } 00296 } 00297 } 00298 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00299 00300 /* check boids in other systems */ 00301 for(pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { 00302 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); 00303 00304 if(epsys) { 00305 neighbors = BLI_kdtree_range_search(epsys->tree, acbr->look_ahead * len_v3(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); 00306 if(neighbors > 0) for(n=0; n<neighbors; n++) { 00307 VECCOPY(co1, pa->prev_state.co); 00308 VECCOPY(vel1, pa->prev_state.vel); 00309 VECCOPY(co2, (epsys->particles + ptn[n].index)->prev_state.co); 00310 VECCOPY(vel2, (epsys->particles + ptn[n].index)->prev_state.vel); 00311 00312 sub_v3_v3v3(loc, co1, co2); 00313 00314 sub_v3_v3v3(vec, vel1, vel2); 00315 00316 inp = dot_v3v3(vec,vec); 00317 00318 /* velocities not parallel */ 00319 if(inp != 0.0f) { 00320 t = -dot_v3v3(loc, vec)/inp; 00321 /* cpa is not too far in the future so investigate further */ 00322 if(t > 0.0f && t < t_min) { 00323 VECADDFAC(co1, co1, vel1, t); 00324 VECADDFAC(co2, co2, vel2, t); 00325 00326 sub_v3_v3v3(vec, co2, co1); 00327 00328 len = normalize_v3(vec); 00329 00330 /* distance of cpa is close enough */ 00331 if(len < 2.0f * val->personal_space * pa->size) { 00332 t_min = t; 00333 00334 mul_v3_fl(vec, len_v3(vel1)); 00335 mul_v3_fl(vec, (2.0f - t)/2.0f); 00336 sub_v3_v3v3(bbd->wanted_co, vel1, vec); 00337 bbd->wanted_speed = len_v3(bbd->wanted_co); 00338 ret = 1; 00339 } 00340 } 00341 } 00342 } 00343 00344 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00345 } 00346 } 00347 00348 00349 if(ptn && nearest==0) 00350 MEM_freeN(ptn); 00351 00352 return ret; 00353 } 00354 static int rule_separate(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00355 { 00356 KDTreeNearest *ptn = NULL; 00357 ParticleTarget *pt; 00358 float len = 2.0f * val->personal_space * pa->size + 1.0f; 00359 float vec[3] = {0.0f, 0.0f, 0.0f}; 00360 int neighbors = BLI_kdtree_range_search(bbd->sim->psys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); 00361 int ret = 0; 00362 00363 if(neighbors > 1 && ptn[1].dist!=0.0f) { 00364 sub_v3_v3v3(vec, pa->prev_state.co, bbd->sim->psys->particles[ptn[1].index].state.co); 00365 mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist); 00366 add_v3_v3(bbd->wanted_co, vec); 00367 bbd->wanted_speed = val->max_speed; 00368 len = ptn[1].dist; 00369 ret = 1; 00370 } 00371 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00372 00373 /* check other boid systems */ 00374 for(pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { 00375 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); 00376 00377 if(epsys) { 00378 neighbors = BLI_kdtree_range_search(epsys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); 00379 00380 if(neighbors > 0 && ptn[0].dist < len) { 00381 sub_v3_v3v3(vec, pa->prev_state.co, ptn[0].co); 00382 mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist); 00383 add_v3_v3(bbd->wanted_co, vec); 00384 bbd->wanted_speed = val->max_speed; 00385 len = ptn[0].dist; 00386 ret = 1; 00387 } 00388 00389 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00390 } 00391 } 00392 return ret; 00393 } 00394 static int rule_flock(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *UNUSED(val), ParticleData *pa) 00395 { 00396 KDTreeNearest ptn[11]; 00397 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; 00398 int neighbors = BLI_kdtree_find_n_nearest(bbd->sim->psys->tree, 11, pa->state.co, pa->prev_state.ave, ptn); 00399 int n; 00400 int ret = 0; 00401 00402 if(neighbors > 1) { 00403 for(n=1; n<neighbors; n++) { 00404 add_v3_v3(loc, bbd->sim->psys->particles[ptn[n].index].prev_state.co); 00405 add_v3_v3(vec, bbd->sim->psys->particles[ptn[n].index].prev_state.vel); 00406 } 00407 00408 mul_v3_fl(loc, 1.0f/((float)neighbors - 1.0f)); 00409 mul_v3_fl(vec, 1.0f/((float)neighbors - 1.0f)); 00410 00411 sub_v3_v3(loc, pa->prev_state.co); 00412 sub_v3_v3(vec, pa->prev_state.vel); 00413 00414 add_v3_v3(bbd->wanted_co, vec); 00415 add_v3_v3(bbd->wanted_co, loc); 00416 bbd->wanted_speed = len_v3(bbd->wanted_co); 00417 00418 ret = 1; 00419 } 00420 return ret; 00421 } 00422 static int rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00423 { 00424 BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; 00425 float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; 00426 float mul, len; 00427 int n = (flbr->queue_size <= 1) ? bbd->sim->psys->totpart : flbr->queue_size; 00428 int i, ret = 0, p = pa - bbd->sim->psys->particles; 00429 00430 if(flbr->ob) { 00431 float vec2[3], t; 00432 00433 /* first check we're not blocking the leader*/ 00434 sub_v3_v3v3(vec, flbr->loc, flbr->oloc); 00435 mul_v3_fl(vec, 1.0f/bbd->timestep); 00436 00437 sub_v3_v3v3(loc, pa->prev_state.co, flbr->oloc); 00438 00439 mul = dot_v3v3(vec, vec); 00440 00441 /* leader is not moving */ 00442 if(mul < 0.01f) { 00443 len = len_v3(loc); 00444 /* too close to leader */ 00445 if(len < 2.0f * val->personal_space * pa->size) { 00446 VECCOPY(bbd->wanted_co, loc); 00447 bbd->wanted_speed = val->max_speed; 00448 return 1; 00449 } 00450 } 00451 else { 00452 t = dot_v3v3(loc, vec)/mul; 00453 00454 /* possible blocking of leader in near future */ 00455 if(t > 0.0f && t < 3.0f) { 00456 VECCOPY(vec2, vec); 00457 mul_v3_fl(vec2, t); 00458 00459 sub_v3_v3v3(vec2, loc, vec2); 00460 00461 len = len_v3(vec2); 00462 00463 if(len < 2.0f * val->personal_space * pa->size) { 00464 VECCOPY(bbd->wanted_co, vec2); 00465 bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; 00466 return 1; 00467 } 00468 } 00469 } 00470 00471 /* not blocking so try to follow leader */ 00472 if(p && flbr->options & BRULE_LEADER_IN_LINE) { 00473 VECCOPY(vec, bbd->sim->psys->particles[p-1].prev_state.vel); 00474 VECCOPY(loc, bbd->sim->psys->particles[p-1].prev_state.co); 00475 } 00476 else { 00477 VECCOPY(loc, flbr->oloc); 00478 sub_v3_v3v3(vec, flbr->loc, flbr->oloc); 00479 mul_v3_fl(vec, 1.0f/bbd->timestep); 00480 } 00481 00482 /* fac is seconds behind leader */ 00483 VECADDFAC(loc, loc, vec, -flbr->distance); 00484 00485 sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co); 00486 bbd->wanted_speed = len_v3(bbd->wanted_co); 00487 00488 ret = 1; 00489 } 00490 else if(p % n) { 00491 float vec2[3], t, t_min = 3.0f; 00492 00493 /* first check we're not blocking any leaders */ 00494 for(i = 0; i< bbd->sim->psys->totpart; i+=n){ 00495 VECCOPY(vec, bbd->sim->psys->particles[i].prev_state.vel); 00496 00497 sub_v3_v3v3(loc, pa->prev_state.co, bbd->sim->psys->particles[i].prev_state.co); 00498 00499 mul = dot_v3v3(vec, vec); 00500 00501 /* leader is not moving */ 00502 if(mul < 0.01f) { 00503 len = len_v3(loc); 00504 /* too close to leader */ 00505 if(len < 2.0f * val->personal_space * pa->size) { 00506 VECCOPY(bbd->wanted_co, loc); 00507 bbd->wanted_speed = val->max_speed; 00508 return 1; 00509 } 00510 } 00511 else { 00512 t = dot_v3v3(loc, vec)/mul; 00513 00514 /* possible blocking of leader in near future */ 00515 if(t > 0.0f && t < t_min) { 00516 VECCOPY(vec2, vec); 00517 mul_v3_fl(vec2, t); 00518 00519 sub_v3_v3v3(vec2, loc, vec2); 00520 00521 len = len_v3(vec2); 00522 00523 if(len < 2.0f * val->personal_space * pa->size) { 00524 t_min = t; 00525 VECCOPY(bbd->wanted_co, loc); 00526 bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; 00527 ret = 1; 00528 } 00529 } 00530 } 00531 } 00532 00533 if(ret) return 1; 00534 00535 /* not blocking so try to follow leader */ 00536 if(flbr->options & BRULE_LEADER_IN_LINE) { 00537 VECCOPY(vec, bbd->sim->psys->particles[p-1].prev_state.vel); 00538 VECCOPY(loc, bbd->sim->psys->particles[p-1].prev_state.co); 00539 } 00540 else { 00541 VECCOPY(vec, bbd->sim->psys->particles[p - p%n].prev_state.vel); 00542 VECCOPY(loc, bbd->sim->psys->particles[p - p%n].prev_state.co); 00543 } 00544 00545 /* fac is seconds behind leader */ 00546 VECADDFAC(loc, loc, vec, -flbr->distance); 00547 00548 sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co); 00549 bbd->wanted_speed = len_v3(bbd->wanted_co); 00550 00551 ret = 1; 00552 } 00553 00554 return ret; 00555 } 00556 static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00557 { 00558 BoidParticle *bpa = pa->boid; 00559 BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule; 00560 float vec[3] = {0.0f, 0.0f, 0.0f}; 00561 00562 if(asbr->wander > 0.0f) { 00563 /* abuse pa->r_ave for wandering */ 00564 bpa->wander[0] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); 00565 bpa->wander[1] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); 00566 bpa->wander[2] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); 00567 00568 normalize_v3(bpa->wander); 00569 00570 VECCOPY(vec, bpa->wander); 00571 00572 mul_qt_v3(pa->prev_state.rot, vec); 00573 00574 VECCOPY(bbd->wanted_co, pa->prev_state.ave); 00575 00576 mul_v3_fl(bbd->wanted_co, 1.1f); 00577 00578 add_v3_v3(bbd->wanted_co, vec); 00579 00580 /* leveling */ 00581 if(asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) { 00582 project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity); 00583 mul_v3_fl(vec, asbr->level); 00584 sub_v3_v3(bbd->wanted_co, vec); 00585 } 00586 } 00587 else { 00588 VECCOPY(bbd->wanted_co, pa->prev_state.ave); 00589 00590 /* may happen at birth */ 00591 if(dot_v2v2(bbd->wanted_co,bbd->wanted_co)==0.0f) { 00592 bbd->wanted_co[0] = 2.0f*(0.5f - BLI_frand()); 00593 bbd->wanted_co[1] = 2.0f*(0.5f - BLI_frand()); 00594 bbd->wanted_co[2] = 2.0f*(0.5f - BLI_frand()); 00595 } 00596 00597 /* leveling */ 00598 if(asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) { 00599 project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity); 00600 mul_v3_fl(vec, asbr->level); 00601 sub_v3_v3(bbd->wanted_co, vec); 00602 } 00603 00604 } 00605 bbd->wanted_speed = asbr->speed * val->max_speed; 00606 00607 return 1; 00608 } 00609 static int rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) 00610 { 00611 BoidRuleFight *fbr = (BoidRuleFight*)rule; 00612 KDTreeNearest *ptn = NULL; 00613 ParticleTarget *pt; 00614 ParticleData *epars; 00615 ParticleData *enemy_pa = NULL; 00616 BoidParticle *bpa; 00617 /* friends & enemies */ 00618 float closest_enemy[3] = {0.0f,0.0f,0.0f}; 00619 float closest_dist = fbr->distance + 1.0f; 00620 float f_strength = 0.0f, e_strength = 0.0f; 00621 float health = 0.0f; 00622 int n, ret = 0; 00623 00624 /* calculate own group strength */ 00625 int neighbors = BLI_kdtree_range_search(bbd->sim->psys->tree, fbr->distance, pa->prev_state.co, NULL, &ptn); 00626 for(n=0; n<neighbors; n++) { 00627 bpa = bbd->sim->psys->particles[ptn[n].index].boid; 00628 health += bpa->data.health; 00629 } 00630 00631 f_strength += bbd->part->boids->strength * health; 00632 00633 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00634 00635 /* add other friendlies and calculate enemy strength and find closest enemy */ 00636 for(pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { 00637 ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); 00638 if(epsys) { 00639 epars = epsys->particles; 00640 00641 neighbors = BLI_kdtree_range_search(epsys->tree, fbr->distance, pa->prev_state.co, NULL, &ptn); 00642 00643 health = 0.0f; 00644 00645 for(n=0; n<neighbors; n++) { 00646 bpa = epars[ptn[n].index].boid; 00647 health += bpa->data.health; 00648 00649 if(n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) { 00650 VECCOPY(closest_enemy, ptn[n].co); 00651 closest_dist = ptn[n].dist; 00652 enemy_pa = epars + ptn[n].index; 00653 } 00654 } 00655 if(pt->mode==PTARGET_MODE_ENEMY) 00656 e_strength += epsys->part->boids->strength * health; 00657 else if(pt->mode==PTARGET_MODE_FRIEND) 00658 f_strength += epsys->part->boids->strength * health; 00659 00660 if(ptn){ MEM_freeN(ptn); ptn=NULL; } 00661 } 00662 } 00663 /* decide action if enemy presence found */ 00664 if(e_strength > 0.0f) { 00665 sub_v3_v3v3(bbd->wanted_co, closest_enemy, pa->prev_state.co); 00666 00667 /* attack if in range */ 00668 if(closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) { 00669 float damage = BLI_frand(); 00670 float enemy_dir[3]; 00671 00672 normalize_v3_v3(enemy_dir, bbd->wanted_co); 00673 00674 /* fight mode */ 00675 bbd->wanted_speed = 0.0f; 00676 00677 /* must face enemy to fight */ 00678 if(dot_v3v3(pa->prev_state.ave, enemy_dir)>0.5f) { 00679 bpa = enemy_pa->boid; 00680 bpa->data.health -= bbd->part->boids->strength * bbd->timestep * ((1.0f-bbd->part->boids->accuracy)*damage + bbd->part->boids->accuracy); 00681 } 00682 } 00683 else { 00684 /* approach mode */ 00685 bbd->wanted_speed = val->max_speed; 00686 } 00687 00688 /* check if boid doesn't want to fight */ 00689 bpa = pa->boid; 00690 if(bpa->data.health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) { 00691 /* decide to flee */ 00692 if(closest_dist < fbr->flee_distance * fbr->distance) { 00693 negate_v3(bbd->wanted_co); 00694 bbd->wanted_speed = val->max_speed; 00695 } 00696 else { /* wait for better odds */ 00697 bbd->wanted_speed = 0.0f; 00698 } 00699 } 00700 00701 ret = 1; 00702 } 00703 00704 return ret; 00705 } 00706 00707 typedef int (*boid_rule_cb)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa); 00708 00709 static boid_rule_cb boid_rules[] = { 00710 rule_none, 00711 rule_goal_avoid, 00712 rule_goal_avoid, 00713 rule_avoid_collision, 00714 rule_separate, 00715 rule_flock, 00716 rule_follow_leader, 00717 rule_average_speed, 00718 rule_fight, 00719 //rule_help, 00720 //rule_protect, 00721 //rule_hide, 00722 //rule_follow_path, 00723 //rule_follow_wall 00724 }; 00725 00726 static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa) 00727 { 00728 BoidParticle *bpa = pa->boid; 00729 00730 if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) { 00731 val->max_speed = boids->land_max_speed * bpa->data.health/boids->health; 00732 val->max_acc = boids->land_max_acc * val->max_speed; 00733 val->max_ave = boids->land_max_ave * (float)M_PI * bpa->data.health/boids->health; 00734 val->min_speed = 0.0f; /* no minimum speed on land */ 00735 val->personal_space = boids->land_personal_space; 00736 val->jump_speed = boids->land_jump_speed * bpa->data.health/boids->health; 00737 } 00738 else { 00739 val->max_speed = boids->air_max_speed * bpa->data.health/boids->health; 00740 val->max_acc = boids->air_max_acc * val->max_speed; 00741 val->max_ave = boids->air_max_ave * (float)M_PI * bpa->data.health/boids->health; 00742 val->min_speed = boids->air_min_speed * boids->air_max_speed; 00743 val->personal_space = boids->air_personal_space; 00744 val->jump_speed = 0.0f; /* no jumping in air */ 00745 } 00746 } 00747 00748 static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float *ground_co, float *ground_nor) 00749 { 00750 BoidParticle *bpa = pa->boid; 00751 00752 if(bpa->data.mode == eBoidMode_Climbing) { 00753 SurfaceModifierData *surmd = NULL; 00754 float x[3], v[3]; 00755 00756 surmd = (SurfaceModifierData *)modifiers_findByType ( bpa->ground, eModifierType_Surface ); 00757 00758 /* take surface velocity into account */ 00759 closest_point_on_surface(surmd, pa->state.co, x, NULL, v); 00760 add_v3_v3(x, v); 00761 00762 /* get actual position on surface */ 00763 closest_point_on_surface(surmd, x, ground_co, ground_nor, NULL); 00764 00765 return bpa->ground; 00766 } 00767 else { 00768 float zvec[3] = {0.0f, 0.0f, 2000.0f}; 00769 ParticleCollision col; 00770 ColliderCache *coll; 00771 BVHTreeRayHit hit; 00772 float radius = 0.0f, t, ray_dir[3]; 00773 00774 if(!bbd->sim->colliders) 00775 return NULL; 00776 00777 /* first try to find below boid */ 00778 copy_v3_v3(col.co1, pa->state.co); 00779 sub_v3_v3v3(col.co2, pa->state.co, zvec); 00780 sub_v3_v3v3(ray_dir, col.co2, col.co1); 00781 col.f = 0.0f; 00782 hit.index = -1; 00783 hit.dist = col.original_ray_length = len_v3(ray_dir); 00784 col.pce.inside = 0; 00785 00786 for(coll = bbd->sim->colliders->first; coll; coll = coll->next){ 00787 col.current = coll->ob; 00788 col.md = coll->collmd; 00789 col.fac1 = col.fac2 = 0.f; 00790 00791 if(col.md && col.md->bvhtree) 00792 BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); 00793 } 00794 /* then use that object */ 00795 if(hit.index>=0) { 00796 t = hit.dist/col.original_ray_length; 00797 interp_v3_v3v3(ground_co, col.co1, col.co2, t); 00798 normalize_v3_v3(ground_nor, col.pce.nor); 00799 return col.hit; 00800 } 00801 00802 /* couldn't find below, so find upmost deflector object */ 00803 add_v3_v3v3(col.co1, pa->state.co, zvec); 00804 sub_v3_v3v3(col.co2, pa->state.co, zvec); 00805 sub_v3_v3(col.co2, zvec); 00806 sub_v3_v3v3(ray_dir, col.co2, col.co1); 00807 col.f = 0.0f; 00808 hit.index = -1; 00809 hit.dist = col.original_ray_length = len_v3(ray_dir); 00810 00811 for(coll = bbd->sim->colliders->first; coll; coll = coll->next){ 00812 col.current = coll->ob; 00813 col.md = coll->collmd; 00814 00815 if(col.md && col.md->bvhtree) 00816 BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); 00817 } 00818 /* then use that object */ 00819 if(hit.index>=0) { 00820 t = hit.dist/col.original_ray_length; 00821 interp_v3_v3v3(ground_co, col.co1, col.co2, t); 00822 normalize_v3_v3(ground_nor, col.pce.nor); 00823 return col.hit; 00824 } 00825 00826 /* default to z=0 */ 00827 VECCOPY(ground_co, pa->state.co); 00828 ground_co[2] = 0; 00829 ground_nor[0] = ground_nor[1] = 0.0f; 00830 ground_nor[2] = 1.0f; 00831 return NULL; 00832 } 00833 } 00834 static int boid_rule_applies(ParticleData *pa, BoidSettings *UNUSED(boids), BoidRule *rule) 00835 { 00836 BoidParticle *bpa = pa->boid; 00837 00838 if(rule==NULL) 00839 return 0; 00840 00841 if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing) && rule->flag & BOIDRULE_ON_LAND) 00842 return 1; 00843 00844 if(bpa->data.mode==eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR) 00845 return 1; 00846 00847 return 0; 00848 } 00849 void boids_precalc_rules(ParticleSettings *part, float cfra) 00850 { 00851 BoidState *state = part->boids->states.first; 00852 BoidRule *rule; 00853 for(; state; state=state->next) { 00854 for(rule = state->rules.first; rule; rule=rule->next) { 00855 if(rule->type==eBoidRuleType_FollowLeader) { 00856 BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; 00857 00858 if(flbr->ob && flbr->cfra != cfra) { 00859 /* save object locations for velocity calculations */ 00860 VECCOPY(flbr->oloc, flbr->loc); 00861 VECCOPY(flbr->loc, flbr->ob->obmat[3]); 00862 flbr->cfra = cfra; 00863 } 00864 } 00865 } 00866 } 00867 } 00868 static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor) 00869 { 00870 BoidParticle *bpa = pa->boid; 00871 float nor[3], vel[3]; 00872 VECCOPY(nor, surface_nor); 00873 00874 /* gather apparent gravity */ 00875 VECADDFAC(bpa->gravity, bpa->gravity, surface_nor, -1.0f); 00876 normalize_v3(bpa->gravity); 00877 00878 /* raise boid it's size from surface */ 00879 mul_v3_fl(nor, pa->size * boids->height); 00880 add_v3_v3v3(pa->state.co, surface_co, nor); 00881 00882 /* remove normal component from velocity */ 00883 project_v3_v3v3(vel, pa->state.vel, surface_nor); 00884 sub_v3_v3v3(pa->state.vel, pa->state.vel, vel); 00885 } 00886 static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor) 00887 { 00888 float vec[3]; 00889 00890 sub_v3_v3v3(vec, boid_co, goal_co); 00891 00892 return dot_v3v3(vec, goal_nor); 00893 } 00894 /* wanted_co is relative to boid location */ 00895 static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness) 00896 { 00897 if(rule==NULL) 00898 return 0; 00899 00900 if(boid_rule_applies(pa, bbd->part->boids, rule)==0) 00901 return 0; 00902 00903 if(boid_rules[rule->type](rule, bbd, val, pa)==0) 00904 return 0; 00905 00906 if(fuzziness < 0.0f || compare_len_v3v3(bbd->wanted_co, pa->prev_state.vel, fuzziness * len_v3(pa->prev_state.vel))==0) 00907 return 1; 00908 else 00909 return 0; 00910 } 00911 static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa) { 00912 BoidState *state = boids->states.first; 00913 BoidParticle *bpa = pa->boid; 00914 00915 for(; state; state=state->next) { 00916 if(state->id==bpa->data.state_id) 00917 return state; 00918 } 00919 00920 /* for some reason particle isn't at a valid state */ 00921 state = boids->states.first; 00922 if(state) 00923 bpa->data.state_id = state->id; 00924 00925 return state; 00926 } 00927 //static int boid_condition_is_true(BoidCondition *cond) { 00928 // /* TODO */ 00929 // return 0; 00930 //} 00931 00932 /* determines the velocity the boid wants to have */ 00933 void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) 00934 { 00935 BoidRule *rule; 00936 BoidSettings *boids = bbd->part->boids; 00937 BoidValues val; 00938 BoidState *state = get_boid_state(boids, pa); 00939 BoidParticle *bpa = pa->boid; 00940 ParticleSystem *psys = bbd->sim->psys; 00941 int rand; 00942 //BoidCondition *cond; 00943 00944 if(bpa->data.health <= 0.0f) { 00945 pa->alive = PARS_DYING; 00946 pa->dietime = bbd->cfra; 00947 return; 00948 } 00949 00950 //planned for near future 00951 //cond = state->conditions.first; 00952 //for(; cond; cond=cond->next) { 00953 // if(boid_condition_is_true(cond)) { 00954 // pa->boid->state_id = cond->state_id; 00955 // state = get_boid_state(boids, pa); 00956 // break; /* only first true condition is used */ 00957 // } 00958 //} 00959 00960 bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; 00961 00962 /* create random seed for every particle & frame */ 00963 rand = (int)(PSYS_FRAND(psys->seed + p) * 1000); 00964 rand = (int)(PSYS_FRAND((int)bbd->cfra + rand) * 1000); 00965 00966 set_boid_values(&val, bbd->part->boids, pa); 00967 00968 /* go through rules */ 00969 switch(state->ruleset_type) { 00970 case eBoidRulesetType_Fuzzy: 00971 { 00972 for(rule = state->rules.first; rule; rule = rule->next) { 00973 if(apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness)) 00974 break; /* only first nonzero rule that comes through fuzzy rule is applied */ 00975 } 00976 break; 00977 } 00978 case eBoidRulesetType_Random: 00979 { 00980 /* use random rule for each particle (allways same for same particle though) */ 00981 rule = BLI_findlink(&state->rules, rand % BLI_countlist(&state->rules)); 00982 00983 apply_boid_rule(bbd, rule, &val, pa, -1.0); 00984 } 00985 case eBoidRulesetType_Average: 00986 { 00987 float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f; 00988 int n = 0; 00989 for(rule = state->rules.first; rule; rule=rule->next) { 00990 if(apply_boid_rule(bbd, rule, &val, pa, -1.0f)) { 00991 add_v3_v3(wanted_co, bbd->wanted_co); 00992 wanted_speed += bbd->wanted_speed; 00993 n++; 00994 bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; 00995 } 00996 } 00997 00998 if(n > 1) { 00999 mul_v3_fl(wanted_co, 1.0f/(float)n); 01000 wanted_speed /= (float)n; 01001 } 01002 01003 VECCOPY(bbd->wanted_co, wanted_co); 01004 bbd->wanted_speed = wanted_speed; 01005 break; 01006 } 01007 01008 } 01009 01010 /* decide on jumping & liftoff */ 01011 if(bpa->data.mode == eBoidMode_OnLand) { 01012 /* fuzziness makes boids capable of misjudgement */ 01013 float mul = 1.0f + state->rule_fuzziness; 01014 01015 if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) { 01016 float cvel[3], dir[3]; 01017 01018 VECCOPY(dir, pa->prev_state.ave); 01019 normalize_v2(dir); 01020 01021 VECCOPY(cvel, bbd->wanted_co); 01022 normalize_v2(cvel); 01023 01024 if(dot_v2v2(cvel, dir) > 0.95f / mul) 01025 bpa->data.mode = eBoidMode_Liftoff; 01026 } 01027 else if(val.jump_speed > 0.0f) { 01028 float jump_v[3]; 01029 int jump = 0; 01030 01031 /* jump to get to a location */ 01032 if(bbd->wanted_co[2] > 0.0f) { 01033 float cvel[3], dir[3]; 01034 float z_v, ground_v, cur_v; 01035 float len; 01036 01037 VECCOPY(dir, pa->prev_state.ave); 01038 normalize_v2(dir); 01039 01040 VECCOPY(cvel, bbd->wanted_co); 01041 normalize_v2(cvel); 01042 01043 len = len_v2(pa->prev_state.vel); 01044 01045 /* first of all, are we going in a suitable direction? */ 01046 /* or at a suitably slow speed */ 01047 if(dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) { 01048 /* try to reach goal at highest point of the parabolic path */ 01049 cur_v = len_v2(pa->prev_state.vel); 01050 z_v = sasqrt(-2.0f * bbd->sim->scene->physics_settings.gravity[2] * bbd->wanted_co[2]); 01051 ground_v = len_v2(bbd->wanted_co)*sasqrt(-0.5f * bbd->sim->scene->physics_settings.gravity[2] / bbd->wanted_co[2]); 01052 01053 len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v); 01054 01055 if(len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) { 01056 jump = 1; 01057 01058 len = MIN2(len, val.jump_speed); 01059 01060 VECCOPY(jump_v, dir); 01061 jump_v[2] = z_v; 01062 mul_v3_fl(jump_v, ground_v); 01063 01064 normalize_v3(jump_v); 01065 mul_v3_fl(jump_v, len); 01066 add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel); 01067 } 01068 } 01069 } 01070 01071 /* jump to go faster */ 01072 if(jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) { 01073 01074 } 01075 01076 if(jump) { 01077 VECCOPY(pa->prev_state.vel, jump_v); 01078 bpa->data.mode = eBoidMode_Falling; 01079 } 01080 } 01081 } 01082 } 01083 /* tries to realize the wanted velocity taking all constraints into account */ 01084 void boid_body(BoidBrainData *bbd, ParticleData *pa) 01085 { 01086 BoidSettings *boids = bbd->part->boids; 01087 BoidParticle *bpa = pa->boid; 01088 BoidValues val; 01089 EffectedPoint epoint; 01090 float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3]; 01091 float dvec[3], bvec[3]; 01092 float new_dir[3], new_speed; 01093 float old_dir[3], old_speed; 01094 float wanted_dir[3]; 01095 float q[4], mat[3][3]; /* rotation */ 01096 float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f}; 01097 float force[3] = {0.0f, 0.0f, 0.0f}; 01098 float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; 01099 01100 set_boid_values(&val, boids, pa); 01101 01102 /* make sure there's something in new velocity, location & rotation */ 01103 copy_particle_key(&pa->state,&pa->prev_state,0); 01104 01105 if(bbd->part->flag & PART_SIZEMASS) 01106 pa_mass*=pa->size; 01107 01108 /* if boids can't fly they fall to the ground */ 01109 if((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim)) 01110 bpa->data.mode = eBoidMode_Falling; 01111 01112 if(bpa->data.mode == eBoidMode_Falling) { 01113 /* Falling boids are only effected by gravity. */ 01114 acc[2] = bbd->sim->scene->physics_settings.gravity[2]; 01115 } 01116 else { 01117 /* figure out acceleration */ 01118 float landing_level = 2.0f; 01119 float level = landing_level + 1.0f; 01120 float new_vel[3]; 01121 01122 if(bpa->data.mode == eBoidMode_Liftoff) { 01123 bpa->data.mode = eBoidMode_InAir; 01124 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); 01125 } 01126 else if(bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { 01127 /* auto-leveling & landing if close to ground */ 01128 01129 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); 01130 01131 /* level = how many particle sizes above ground */ 01132 level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f; 01133 01134 landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass; 01135 01136 if(pa->prev_state.vel[2] < 0.0f) { 01137 if(level < 1.0f) { 01138 bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f; 01139 bbd->wanted_speed = 0.0f; 01140 bpa->data.mode = eBoidMode_Falling; 01141 } 01142 else if(level < landing_level) { 01143 bbd->wanted_speed *= (level - 1.0f)/landing_level; 01144 bbd->wanted_co[2] *= (level - 1.0f)/landing_level; 01145 } 01146 } 01147 } 01148 01149 VECCOPY(old_dir, pa->prev_state.ave); 01150 new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co); 01151 01152 /* first check if we have valid direction we want to go towards */ 01153 if(new_speed == 0.0f) { 01154 VECCOPY(new_dir, old_dir); 01155 } 01156 else { 01157 float old_dir2[2], wanted_dir2[2], nor[3], angle; 01158 copy_v2_v2(old_dir2, old_dir); 01159 normalize_v2(old_dir2); 01160 copy_v2_v2(wanted_dir2, wanted_dir); 01161 normalize_v2(wanted_dir2); 01162 01163 /* choose random direction to turn if wanted velocity */ 01164 /* is directly behind regardless of z-coordinate */ 01165 if(dot_v2v2(old_dir2, wanted_dir2) < -0.99f) { 01166 wanted_dir[0] = 2.0f*(0.5f - BLI_frand()); 01167 wanted_dir[1] = 2.0f*(0.5f - BLI_frand()); 01168 wanted_dir[2] = 2.0f*(0.5f - BLI_frand()); 01169 normalize_v3(wanted_dir); 01170 } 01171 01172 /* constrain direction with maximum angular velocity */ 01173 angle = saacos(dot_v3v3(old_dir, wanted_dir)); 01174 angle = MIN2(angle, val.max_ave); 01175 01176 cross_v3_v3v3(nor, old_dir, wanted_dir); 01177 axis_angle_to_quat( q,nor, angle); 01178 VECCOPY(new_dir, old_dir); 01179 mul_qt_v3(q, new_dir); 01180 normalize_v3(new_dir); 01181 01182 /* save direction in case resulting velocity too small */ 01183 axis_angle_to_quat( q,nor, angle*dtime); 01184 VECCOPY(pa->state.ave, old_dir); 01185 mul_qt_v3(q, pa->state.ave); 01186 normalize_v3(pa->state.ave); 01187 } 01188 01189 /* constrain speed with maximum acceleration */ 01190 old_speed = len_v3(pa->prev_state.vel); 01191 01192 if(bbd->wanted_speed < old_speed) 01193 new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc); 01194 else 01195 new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc); 01196 01197 /* combine direction and speed */ 01198 VECCOPY(new_vel, new_dir); 01199 mul_v3_fl(new_vel, new_speed); 01200 01201 /* maintain minimum flying velocity if not landing */ 01202 if(level >= landing_level) { 01203 float len2 = dot_v2v2(new_vel,new_vel); 01204 float root; 01205 01206 len2 = MAX2(len2, val.min_speed*val.min_speed); 01207 root = sasqrt(new_speed*new_speed - len2); 01208 01209 new_vel[2] = new_vel[2] < 0.0f ? -root : root; 01210 01211 normalize_v2(new_vel); 01212 mul_v2_fl(new_vel, sasqrt(len2)); 01213 } 01214 01215 /* finally constrain speed to max speed */ 01216 new_speed = normalize_v3(new_vel); 01217 mul_v3_fl(new_vel, MIN2(new_speed, val.max_speed)); 01218 01219 /* get acceleration from difference of velocities */ 01220 sub_v3_v3v3(acc, new_vel, pa->prev_state.vel); 01221 01222 /* break acceleration to components */ 01223 project_v3_v3v3(tan_acc, acc, pa->prev_state.ave); 01224 sub_v3_v3v3(nor_acc, acc, tan_acc); 01225 } 01226 01227 /* account for effectors */ 01228 pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint); 01229 pdDoEffectors(bbd->sim->psys->effectors, bbd->sim->colliders, bbd->part->effector_weights, &epoint, force, NULL); 01230 01231 if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) { 01232 float length = normalize_v3(force); 01233 01234 length = MAX2(0.0f, length - boids->land_stick_force); 01235 01236 mul_v3_fl(force, length); 01237 } 01238 01239 add_v3_v3(acc, force); 01240 01241 /* store smoothed acceleration for nice banking etc. */ 01242 VECADDFAC(bpa->data.acc, bpa->data.acc, acc, dtime); 01243 mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime)); 01244 01245 /* integrate new location & velocity */ 01246 01247 /* by regarding the acceleration as a force at this stage we*/ 01248 /* can get better control allthough it's a bit unphysical */ 01249 mul_v3_fl(acc, 1.0f/pa_mass); 01250 01251 VECCOPY(dvec, acc); 01252 mul_v3_fl(dvec, dtime*dtime*0.5f); 01253 01254 VECCOPY(bvec, pa->prev_state.vel); 01255 mul_v3_fl(bvec, dtime); 01256 add_v3_v3(dvec, bvec); 01257 add_v3_v3(pa->state.co, dvec); 01258 01259 VECADDFAC(pa->state.vel, pa->state.vel, acc, dtime); 01260 01261 //if(bpa->data.mode != eBoidMode_InAir) 01262 bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); 01263 01264 /* change modes, constrain movement & keep track of down vector */ 01265 switch(bpa->data.mode) { 01266 case eBoidMode_InAir: 01267 { 01268 float grav[3]; 01269 01270 grav[0]= 0.0f; 01271 grav[1]= 0.0f; 01272 grav[2]= bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; 01273 01274 /* don't take forward acceleration into account (better banking) */ 01275 if(dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) { 01276 project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); 01277 sub_v3_v3v3(dvec, bpa->data.acc, dvec); 01278 } 01279 else { 01280 VECCOPY(dvec, bpa->data.acc); 01281 } 01282 01283 /* gather apparent gravity */ 01284 VECADDFAC(bpa->gravity, grav, dvec, -boids->banking); 01285 normalize_v3(bpa->gravity); 01286 01287 /* stick boid on goal when close enough */ 01288 if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { 01289 bpa->data.mode = eBoidMode_Climbing; 01290 bpa->ground = bbd->goal_ob; 01291 boid_find_ground(bbd, pa, ground_co, ground_nor); 01292 boid_climb(boids, pa, ground_co, ground_nor); 01293 } 01294 else if(pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { 01295 /* land boid when below ground */ 01296 if(boids->options & BOID_ALLOW_LAND) { 01297 pa->state.co[2] = ground_co[2] + pa->size * boids->height; 01298 pa->state.vel[2] = 0.0f; 01299 bpa->data.mode = eBoidMode_OnLand; 01300 } 01301 /* fly above ground */ 01302 else if(bpa->ground) { 01303 pa->state.co[2] = ground_co[2] + pa->size * boids->height; 01304 pa->state.vel[2] = 0.0f; 01305 } 01306 } 01307 break; 01308 } 01309 case eBoidMode_Falling: 01310 { 01311 float grav[3]; 01312 01313 grav[0]= 0.0f; 01314 grav[1]= 0.0f; 01315 grav[2]= bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; 01316 01317 01318 /* gather apparent gravity */ 01319 VECADDFAC(bpa->gravity, bpa->gravity, grav, dtime); 01320 normalize_v3(bpa->gravity); 01321 01322 if(boids->options & BOID_ALLOW_LAND) { 01323 /* stick boid on goal when close enough */ 01324 if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { 01325 bpa->data.mode = eBoidMode_Climbing; 01326 bpa->ground = bbd->goal_ob; 01327 boid_find_ground(bbd, pa, ground_co, ground_nor); 01328 boid_climb(boids, pa, ground_co, ground_nor); 01329 } 01330 /* land boid when really near ground */ 01331 else if(pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height){ 01332 pa->state.co[2] = ground_co[2] + pa->size * boids->height; 01333 pa->state.vel[2] = 0.0f; 01334 bpa->data.mode = eBoidMode_OnLand; 01335 } 01336 /* if we're falling, can fly and want to go upwards lets fly */ 01337 else if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) 01338 bpa->data.mode = eBoidMode_InAir; 01339 } 01340 else 01341 bpa->data.mode = eBoidMode_InAir; 01342 break; 01343 } 01344 case eBoidMode_Climbing: 01345 { 01346 boid_climb(boids, pa, ground_co, ground_nor); 01347 //float nor[3]; 01348 //VECCOPY(nor, ground_nor); 01349 01351 //VECADDFAC(pa->r_ve, pa->r_ve, ground_nor, -1.0); 01352 //normalize_v3(pa->r_ve); 01353 01355 //mul_v3_fl(nor, pa->size * boids->height); 01356 //add_v3_v3v3(pa->state.co, ground_co, nor); 01357 01359 //project_v3_v3v3(v, pa->state.vel, ground_nor); 01360 //sub_v3_v3v3(pa->state.vel, pa->state.vel, v); 01361 break; 01362 } 01363 case eBoidMode_OnLand: 01364 { 01365 /* stick boid on goal when close enough */ 01366 if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { 01367 bpa->data.mode = eBoidMode_Climbing; 01368 bpa->ground = bbd->goal_ob; 01369 boid_find_ground(bbd, pa, ground_co, ground_nor); 01370 boid_climb(boids, pa, ground_co, ground_nor); 01371 } 01372 /* ground is too far away so boid falls */ 01373 else if(pa->state.co[2]-ground_co[2] > 1.1f * pa->size * boids->height) 01374 bpa->data.mode = eBoidMode_Falling; 01375 else { 01376 /* constrain to surface */ 01377 pa->state.co[2] = ground_co[2] + pa->size * boids->height; 01378 pa->state.vel[2] = 0.0f; 01379 } 01380 01381 if(boids->banking > 0.0f) { 01382 float grav[3]; 01383 /* Don't take gravity's strength in to account, */ 01384 /* otherwise amount of banking is hard to control. */ 01385 negate_v3_v3(grav, ground_nor); 01386 01387 project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); 01388 sub_v3_v3v3(dvec, bpa->data.acc, dvec); 01389 01390 /* gather apparent gravity */ 01391 VECADDFAC(bpa->gravity, grav, dvec, -boids->banking); 01392 normalize_v3(bpa->gravity); 01393 } 01394 else { 01395 /* gather negative surface normal */ 01396 VECADDFAC(bpa->gravity, bpa->gravity, ground_nor, -1.0f); 01397 normalize_v3(bpa->gravity); 01398 } 01399 break; 01400 } 01401 } 01402 01403 /* save direction to state.ave unless the boid is falling */ 01404 /* (boids can't effect their direction when falling) */ 01405 if(bpa->data.mode!=eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f*pa->size) { 01406 copy_v3_v3(pa->state.ave, pa->state.vel); 01407 pa->state.ave[2] *= bbd->part->boids->pitch; 01408 normalize_v3(pa->state.ave); 01409 } 01410 01411 /* apply damping */ 01412 if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) 01413 mul_v3_fl(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); 01414 01415 /* calculate rotation matrix based on forward & down vectors */ 01416 if(bpa->data.mode == eBoidMode_InAir) { 01417 VECCOPY(mat[0], pa->state.ave); 01418 01419 project_v3_v3v3(dvec, bpa->gravity, pa->state.ave); 01420 sub_v3_v3v3(mat[2], bpa->gravity, dvec); 01421 normalize_v3(mat[2]); 01422 } 01423 else { 01424 project_v3_v3v3(dvec, pa->state.ave, bpa->gravity); 01425 sub_v3_v3v3(mat[0], pa->state.ave, dvec); 01426 normalize_v3(mat[0]); 01427 01428 VECCOPY(mat[2], bpa->gravity); 01429 } 01430 negate_v3(mat[2]); 01431 cross_v3_v3v3(mat[1], mat[2], mat[0]); 01432 01433 /* apply rotation */ 01434 mat3_to_quat_is_ok( q,mat); 01435 copy_qt_qt(pa->state.rot, q); 01436 } 01437 01438 BoidRule *boid_new_rule(int type) 01439 { 01440 BoidRule *rule = NULL; 01441 if(type <= 0) 01442 return NULL; 01443 01444 switch(type) { 01445 case eBoidRuleType_Goal: 01446 case eBoidRuleType_Avoid: 01447 rule = MEM_callocN(sizeof(BoidRuleGoalAvoid), "BoidRuleGoalAvoid"); 01448 break; 01449 case eBoidRuleType_AvoidCollision: 01450 rule = MEM_callocN(sizeof(BoidRuleAvoidCollision), "BoidRuleAvoidCollision"); 01451 ((BoidRuleAvoidCollision*)rule)->look_ahead = 2.0f; 01452 break; 01453 case eBoidRuleType_FollowLeader: 01454 rule = MEM_callocN(sizeof(BoidRuleFollowLeader), "BoidRuleFollowLeader"); 01455 ((BoidRuleFollowLeader*)rule)->distance = 1.0f; 01456 break; 01457 case eBoidRuleType_AverageSpeed: 01458 rule = MEM_callocN(sizeof(BoidRuleAverageSpeed), "BoidRuleAverageSpeed"); 01459 ((BoidRuleAverageSpeed*)rule)->speed = 0.5f; 01460 break; 01461 case eBoidRuleType_Fight: 01462 rule = MEM_callocN(sizeof(BoidRuleFight), "BoidRuleFight"); 01463 ((BoidRuleFight*)rule)->distance = 100.0f; 01464 ((BoidRuleFight*)rule)->flee_distance = 100.0f; 01465 break; 01466 default: 01467 rule = MEM_callocN(sizeof(BoidRule), "BoidRule"); 01468 break; 01469 } 01470 01471 rule->type = type; 01472 rule->flag |= BOIDRULE_IN_AIR|BOIDRULE_ON_LAND; 01473 BLI_strncpy(rule->name, boidrule_type_items[type-1].name, sizeof(rule->name)); 01474 01475 return rule; 01476 } 01477 void boid_default_settings(BoidSettings *boids) 01478 { 01479 boids->air_max_speed = 10.0f; 01480 boids->air_max_acc = 0.5f; 01481 boids->air_max_ave = 0.5f; 01482 boids->air_personal_space = 1.0f; 01483 01484 boids->land_max_speed = 5.0f; 01485 boids->land_max_acc = 0.5f; 01486 boids->land_max_ave = 0.5f; 01487 boids->land_personal_space = 1.0f; 01488 01489 boids->options = BOID_ALLOW_FLIGHT; 01490 01491 boids->landing_smoothness = 3.0f; 01492 boids->banking = 1.0f; 01493 boids->pitch = 1.0f; 01494 boids->height = 1.0f; 01495 01496 boids->health = 1.0f; 01497 boids->accuracy = 1.0f; 01498 boids->aggression = 2.0f; 01499 boids->range = 1.0f; 01500 boids->strength = 0.1f; 01501 } 01502 01503 BoidState *boid_new_state(BoidSettings *boids) 01504 { 01505 BoidState *state = MEM_callocN(sizeof(BoidState), "BoidState"); 01506 01507 state->id = boids->last_state_id++; 01508 if(state->id) 01509 sprintf(state->name, "State %i", state->id); 01510 else 01511 strcpy(state->name, "State"); 01512 01513 state->rule_fuzziness = 0.5; 01514 state->volume = 1.0f; 01515 state->channels |= ~0; 01516 01517 return state; 01518 } 01519 01520 BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state) { 01521 BoidState *staten = MEM_dupallocN(state); 01522 01523 BLI_duplicatelist(&staten->rules, &state->rules); 01524 BLI_duplicatelist(&staten->conditions, &state->conditions); 01525 BLI_duplicatelist(&staten->actions, &state->actions); 01526 01527 staten->id = boids->last_state_id++; 01528 01529 return staten; 01530 } 01531 void boid_free_settings(BoidSettings *boids) 01532 { 01533 if(boids) { 01534 BoidState *state = boids->states.first; 01535 01536 for(; state; state=state->next) { 01537 BLI_freelistN(&state->rules); 01538 BLI_freelistN(&state->conditions); 01539 BLI_freelistN(&state->actions); 01540 } 01541 01542 BLI_freelistN(&boids->states); 01543 01544 MEM_freeN(boids); 01545 } 01546 } 01547 BoidSettings *boid_copy_settings(BoidSettings *boids) 01548 { 01549 BoidSettings *nboids = NULL; 01550 01551 if(boids) { 01552 BoidState *state; 01553 BoidState *nstate; 01554 01555 nboids = MEM_dupallocN(boids); 01556 01557 BLI_duplicatelist(&nboids->states, &boids->states); 01558 01559 state = boids->states.first; 01560 nstate = nboids->states.first; 01561 for(; state; state=state->next, nstate=nstate->next) { 01562 BLI_duplicatelist(&nstate->rules, &state->rules); 01563 BLI_duplicatelist(&nstate->conditions, &state->conditions); 01564 BLI_duplicatelist(&nstate->actions, &state->actions); 01565 } 01566 } 01567 01568 return nboids; 01569 } 01570 BoidState *boid_get_current_state(BoidSettings *boids) 01571 { 01572 BoidState *state = boids->states.first; 01573 01574 for(; state; state=state->next) { 01575 if(state->flag & BOIDSTATE_CURRENT) 01576 break; 01577 } 01578 01579 return state; 01580 } 01581