static int rule_separate(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *val, ParticleData *pa) { KDTreeNearest *ptn = NULL; ParticleTarget *pt; float len = 2.0f * val->personal_space * pa->size + 1.0f; float vec[3] = {0.0f, 0.0f, 0.0f}; int neighbors = BLI_kdtree_range_search( bbd->sim->psys->tree, pa->prev_state.co, &ptn, 2.0f * val->personal_space * pa->size); int ret = 0; if (neighbors > 1 && ptn[1].dist!=0.0f) { sub_v3_v3v3(vec, pa->prev_state.co, bbd->sim->psys->particles[ptn[1].index].state.co); mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist); add_v3_v3(bbd->wanted_co, vec); bbd->wanted_speed = val->max_speed; len = ptn[1].dist; ret = 1; } if (ptn) { MEM_freeN(ptn); ptn=NULL; } /* check other boid systems */ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); if (epsys) { neighbors = BLI_kdtree_range_search( epsys->tree, pa->prev_state.co, &ptn, 2.0f * val->personal_space * pa->size); if (neighbors > 0 && ptn[0].dist < len) { sub_v3_v3v3(vec, pa->prev_state.co, ptn[0].co); mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist); add_v3_v3(bbd->wanted_co, vec); bbd->wanted_speed = val->max_speed; len = ptn[0].dist; ret = 1; } if (ptn) { MEM_freeN(ptn); ptn=NULL; } } } return ret; }
static PyObject *py_kdtree_find_range(PyKDTree *self, PyObject *args, PyObject *kwargs) { PyObject *py_list; PyObject *py_co; float co[3]; KDTreeNearest *nearest = NULL; float radius; int i, found; const char *keywords[] = {"co", "radius", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "Of:find_range", (char **)keywords, &py_co, &radius)) { return NULL; } if (mathutils_array_parse(co, 3, 3, py_co, "find_range: invalid 'co' arg") == -1) return NULL; if (radius < 0.0f) { PyErr_SetString(PyExc_RuntimeError, "negative radius given"); return NULL; } if (self->count != self->count_balance) { PyErr_SetString(PyExc_RuntimeError, "KDTree must be balanced before calling find_range()"); return NULL; } found = BLI_kdtree_range_search(self->obj, co, &nearest, radius); py_list = PyList_New(found); for (i = 0; i < found; i++) { PyList_SET_ITEM(py_list, i, kdtree_nearest_to_py(&nearest[i])); } if (nearest) { MEM_freeN(nearest); } return py_list; }
static int rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) { BoidRuleFight *fbr = (BoidRuleFight*)rule; KDTreeNearest *ptn = NULL; ParticleTarget *pt; ParticleData *epars; ParticleData *enemy_pa = NULL; BoidParticle *bpa; /* friends & enemies */ float closest_enemy[3] = {0.0f, 0.0f, 0.0f}; float closest_dist = fbr->distance + 1.0f; float f_strength = 0.0f, e_strength = 0.0f; float health = 0.0f; int n, ret = 0; /* calculate own group strength */ int neighbors = BLI_kdtree_range_search( bbd->sim->psys->tree, pa->prev_state.co, &ptn, fbr->distance); for (n=0; n<neighbors; n++) { bpa = bbd->sim->psys->particles[ptn[n].index].boid; health += bpa->data.health; } f_strength += bbd->part->boids->strength * health; if (ptn) { MEM_freeN(ptn); ptn=NULL; } /* add other friendlies and calculate enemy strength and find closest enemy */ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); if (epsys) { epars = epsys->particles; neighbors = BLI_kdtree_range_search( epsys->tree, pa->prev_state.co, &ptn, fbr->distance); health = 0.0f; for (n=0; n<neighbors; n++) { bpa = epars[ptn[n].index].boid; health += bpa->data.health; if (n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) { copy_v3_v3(closest_enemy, ptn[n].co); closest_dist = ptn[n].dist; enemy_pa = epars + ptn[n].index; } } if (pt->mode==PTARGET_MODE_ENEMY) e_strength += epsys->part->boids->strength * health; else if (pt->mode==PTARGET_MODE_FRIEND) f_strength += epsys->part->boids->strength * health; if (ptn) { MEM_freeN(ptn); ptn=NULL; } } } /* decide action if enemy presence found */ if (e_strength > 0.0f) { sub_v3_v3v3(bbd->wanted_co, closest_enemy, pa->prev_state.co); /* attack if in range */ if (closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) { float damage = BLI_rng_get_float(bbd->rng); float enemy_dir[3]; normalize_v3_v3(enemy_dir, bbd->wanted_co); /* fight mode */ bbd->wanted_speed = 0.0f; /* must face enemy to fight */ if (dot_v3v3(pa->prev_state.ave, enemy_dir)>0.5f) { bpa = enemy_pa->boid; bpa->data.health -= bbd->part->boids->strength * bbd->timestep * ((1.0f-bbd->part->boids->accuracy)*damage + bbd->part->boids->accuracy); } } else { /* approach mode */ bbd->wanted_speed = val->max_speed; } /* check if boid doesn't want to fight */ bpa = pa->boid; if (bpa->data.health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) { /* decide to flee */ if (closest_dist < fbr->flee_distance * fbr->distance) { negate_v3(bbd->wanted_co); bbd->wanted_speed = val->max_speed; } else { /* wait for better odds */ bbd->wanted_speed = 0.0f; } } ret = 1; } return ret; }
static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) { BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule; KDTreeNearest *ptn = NULL; ParticleTarget *pt; BoidParticle *bpa = pa->boid; ColliderCache *coll; float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; float co1[3], vel1[3], co2[3], vel2[3]; float len, t, inp, t_min = 2.0f; int n, neighbors = 0, nearest = 0; int ret = 0; //check deflector objects first if(acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) { ParticleCollision col; BVHTreeRayHit hit; float radius = val->personal_space * pa->size, ray_dir[3]; copy_v3_v3(col.co1, pa->prev_state.co); add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel); sub_v3_v3v3(ray_dir, col.co2, col.co1); mul_v3_fl(ray_dir, acbr->look_ahead); col.f = 0.0f; hit.index = -1; hit.dist = col.original_ray_length = len_v3(ray_dir); /* find out closest deflector object */ for(coll = bbd->sim->colliders->first; coll; coll=coll->next) { /* don't check with current ground object */ if(coll->ob == bpa->ground) continue; col.current = coll->ob; col.md = coll->collmd; if(col.md && col.md->bvhtree) BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, BKE_psys_collision_neartest_cb, &col); } /* then avoid that object */ if(hit.index>=0) { t = hit.dist/col.original_ray_length; /* avoid head-on collision */ if(dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) { /* don't know why, but uneven range [0.0,1.0] */ /* works much better than even [-1.0,1.0] */ bbd->wanted_co[0] = BLI_frand(); bbd->wanted_co[1] = BLI_frand(); bbd->wanted_co[2] = BLI_frand(); } else { copy_v3_v3(bbd->wanted_co, col.pce.nor); } mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size); bbd->wanted_speed = sqrtf(t) * len_v3(pa->prev_state.vel); bbd->wanted_speed = MAX2(bbd->wanted_speed, val->min_speed); return 1; } } //check boids in own system if(acbr->options & BRULE_ACOLL_WITH_BOIDS) { 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); if(neighbors > 1) for(n=1; n<neighbors; n++) { copy_v3_v3(co1, pa->prev_state.co); copy_v3_v3(vel1, pa->prev_state.vel); copy_v3_v3(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co); copy_v3_v3(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel); sub_v3_v3v3(loc, co1, co2); sub_v3_v3v3(vec, vel1, vel2); inp = dot_v3v3(vec,vec); /* velocities not parallel */ if(inp != 0.0f) { t = -dot_v3v3(loc, vec)/inp; /* cpa is not too far in the future so investigate further */ if(t > 0.0f && t < t_min) { madd_v3_v3fl(co1, vel1, t); madd_v3_v3fl(co2, vel2, t); sub_v3_v3v3(vec, co2, co1); len = normalize_v3(vec); /* distance of cpa is close enough */ if(len < 2.0f * val->personal_space * pa->size) { t_min = t; mul_v3_fl(vec, len_v3(vel1)); mul_v3_fl(vec, (2.0f - t)/2.0f); sub_v3_v3v3(bbd->wanted_co, vel1, vec); bbd->wanted_speed = len_v3(bbd->wanted_co); ret = 1; } } } } } if(ptn){ MEM_freeN(ptn); ptn=NULL; } /* check boids in other systems */ for(pt=bbd->sim->psys->targets.first; pt; pt=pt->next) { ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt); if(epsys) { 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); if(neighbors > 0) for(n=0; n<neighbors; n++) { copy_v3_v3(co1, pa->prev_state.co); copy_v3_v3(vel1, pa->prev_state.vel); copy_v3_v3(co2, (epsys->particles + ptn[n].index)->prev_state.co); copy_v3_v3(vel2, (epsys->particles + ptn[n].index)->prev_state.vel); sub_v3_v3v3(loc, co1, co2); sub_v3_v3v3(vec, vel1, vel2); inp = dot_v3v3(vec,vec); /* velocities not parallel */ if(inp != 0.0f) { t = -dot_v3v3(loc, vec)/inp; /* cpa is not too far in the future so investigate further */ if(t > 0.0f && t < t_min) { madd_v3_v3fl(co1, vel1, t); madd_v3_v3fl(co2, vel2, t); sub_v3_v3v3(vec, co2, co1); len = normalize_v3(vec); /* distance of cpa is close enough */ if(len < 2.0f * val->personal_space * pa->size) { t_min = t; mul_v3_fl(vec, len_v3(vel1)); mul_v3_fl(vec, (2.0f - t)/2.0f); sub_v3_v3v3(bbd->wanted_co, vel1, vec); bbd->wanted_speed = len_v3(bbd->wanted_co); ret = 1; } } } } if(ptn){ MEM_freeN(ptn); ptn=NULL; } } } if(ptn && nearest==0) MEM_freeN(ptn); return ret; }