/* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */ void IMB_partial_rect_from_float(struct ImBuf *ibuf,float *buffer, int x, int y, int w, int h) { /* indices to source and destination image pixels */ float *srcFloatPxl; unsigned char *dstBytePxl; /* buffer index will fill buffer */ float *bufferIndex; /* convenience pointers to start of image buffers */ float *init_srcFloatPxl = (float *)ibuf->rect_float; unsigned char *init_dstBytePxl = (unsigned char *) ibuf->rect; /* Dithering factor */ float dither= ibuf->dither / 255.0f; /* respective attributes of image */ short profile= ibuf->profile; int channels= ibuf->channels; int i, j; /* if called -only- from GPU_paint_update_image this test will never fail but leaving it here for better or worse */ if(init_srcFloatPxl==NULL || (buffer == NULL)){ return; } if(init_dstBytePxl==NULL) { imb_addrectImBuf(ibuf); init_dstBytePxl = (unsigned char *) ibuf->rect; } if(channels==1) { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x); for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl++, bufferIndex+=4) { dstBytePxl[1]= dstBytePxl[2]= dstBytePxl[3]= dstBytePxl[0] = FTOCHAR(srcFloatPxl[0]); bufferIndex[0] = bufferIndex[1] = bufferIndex[2] = bufferIndex[3] = srcFloatPxl[0]; } } } else if (profile == IB_PROFILE_LINEAR_RGB) { if(channels == 3) { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*3; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=3, bufferIndex += 4) { linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl); F3TOCHAR4(bufferIndex, dstBytePxl); bufferIndex[3]= 1.0; } } } else if (channels == 4) { if (dither != 0.f) { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) { const float d = (BLI_frand()-0.5f)*dither; linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl); bufferIndex[3] = srcFloatPxl[3]; add_v4_fl(bufferIndex, d); F4TOCHAR4(bufferIndex, dstBytePxl); } } } else { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) { linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl); bufferIndex[3]= srcFloatPxl[3]; F4TOCHAR4(bufferIndex, dstBytePxl); } } } } } else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) { if(channels==3) { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*3; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=3, bufferIndex+=4) { copy_v3_v3(bufferIndex, srcFloatPxl); F3TOCHAR4(bufferIndex, dstBytePxl); bufferIndex[3] = 1.0; } } } else { if (dither != 0.f) { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) { const float d = (BLI_frand()-0.5f)*dither; copy_v4_v4(bufferIndex, srcFloatPxl); add_v4_fl(bufferIndex,d); F4TOCHAR4(bufferIndex, dstBytePxl); } } } else { for (j = 0; j < h; j++){ bufferIndex = buffer + w*j*4; dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4; srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4; for(i = 0; i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) { copy_v4_v4(bufferIndex, srcFloatPxl); F4TOCHAR4(bufferIndex, dstBytePxl); } } } } } /* ensure user flag is reset */ ibuf->userflags &= ~IB_RECT_INVALID; }
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *dm = derivedData, *result; ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md; ParticleSimulationData sim; ParticleSystem *psys = NULL; ParticleData *pa = NULL, *pars = NULL; MFace *mface, *orig_mface; MVert *mvert, *orig_mvert; int i, totvert, totpart = 0, totface, maxvert, maxface, first_particle = 0; short track = ob->trackflag % 3, trackneg, axis = pimd->axis; float max_co = 0.0, min_co = 0.0, temp_co[3], cross[3]; float *size = NULL; DM_ensure_tessface(dm); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */ trackneg = ((ob->trackflag > 2) ? 1 : 0); if (pimd->ob == ob) { pimd->ob = NULL; return derivedData; } if (pimd->ob) { psys = BLI_findlink(&pimd->ob->particlesystem, pimd->psys - 1); if (psys == NULL || psys->totpart == 0) return derivedData; } else return derivedData; if (pimd->flag & eParticleInstanceFlag_Parents) totpart += psys->totpart; if (pimd->flag & eParticleInstanceFlag_Children) { if (totpart == 0) first_particle = psys->totpart; totpart += psys->totchild; } if (totpart == 0) return derivedData; sim.scene = md->scene; sim.ob = pimd->ob; sim.psys = psys; sim.psmd = psys_get_modifier(pimd->ob, psys); if (pimd->flag & eParticleInstanceFlag_UseSize) { int p; float *si; si = size = MEM_callocN(totpart * sizeof(float), "particle size array"); if (pimd->flag & eParticleInstanceFlag_Parents) { for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++, si++) *si = pa->size; } if (pimd->flag & eParticleInstanceFlag_Children) { ChildParticle *cpa = psys->child; for (p = 0; p < psys->totchild; p++, cpa++, si++) { *si = psys_get_child_size(psys, cpa, 0.0f, NULL); } } } pars = psys->particles; totvert = dm->getNumVerts(dm); totface = dm->getNumTessFaces(dm); maxvert = totvert * totpart; maxface = totface * totpart; psys->lattice = psys_get_lattice(&sim); if (psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) { float min_r[3], max_r[3]; INIT_MINMAX(min_r, max_r); dm->getMinMax(dm, min_r, max_r); min_co = min_r[track]; max_co = max_r[track]; } result = CDDM_from_template(dm, maxvert, dm->getNumEdges(dm) * totpart, maxface, 0, 0); mvert = result->getVertArray(result); orig_mvert = dm->getVertArray(dm); for (i = 0; i < maxvert; i++) { MVert *inMV; MVert *mv = mvert + i; ParticleKey state; inMV = orig_mvert + i % totvert; DM_copy_vert_data(dm, result, i % totvert, i, 1); *mv = *inMV; /*change orientation based on object trackflag*/ copy_v3_v3(temp_co, mv->co); mv->co[axis] = temp_co[track]; mv->co[(axis + 1) % 3] = temp_co[(track + 1) % 3]; mv->co[(axis + 2) % 3] = temp_co[(track + 2) % 3]; if ((psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) && (pimd->flag & eParticleInstanceFlag_Path)) { float ran = 0.0f; if (pimd->random_position != 0.0f) { BLI_srandom(psys->seed + (i / totvert) % totpart); ran = pimd->random_position * BLI_frand(); } if (pimd->flag & eParticleInstanceFlag_KeepShape) { state.time = pimd->position * (1.0f - ran); } else { state.time = (mv->co[axis] - min_co) / (max_co - min_co) * pimd->position * (1.0f - ran); if (trackneg) state.time = 1.0f - state.time; mv->co[axis] = 0.0; } psys_get_particle_on_path(&sim, first_particle + i / totvert, &state, 1); normalize_v3(state.vel); /* TODO: incremental rotations somehow */ if (state.vel[axis] < -0.9999f || state.vel[axis] > 0.9999f) { state.rot[0] = 1; state.rot[1] = state.rot[2] = state.rot[3] = 0.0f; } else { float temp[3] = {0.0f, 0.0f, 0.0f}; temp[axis] = 1.0f; cross_v3_v3v3(cross, temp, state.vel); /* state.vel[axis] is the only component surviving from a dot product with the axis */ axis_angle_to_quat(state.rot, cross, saacos(state.vel[axis])); } } else { state.time = -1.0; psys_get_particle_state(&sim, first_particle + i / totvert, &state, 1); } mul_qt_v3(state.rot, mv->co); if (pimd->flag & eParticleInstanceFlag_UseSize) mul_v3_fl(mv->co, size[i / totvert]); add_v3_v3(mv->co, state.co); } mface = result->getTessFaceArray(result); orig_mface = dm->getTessFaceArray(dm); for (i = 0; i < maxface; i++) { MFace *inMF; MFace *mf = mface + i; if (pimd->flag & eParticleInstanceFlag_Parents) { if (i / totface >= psys->totpart) { if (psys->part->childtype == PART_CHILD_PARTICLES) { pa = psys->particles + (psys->child + i / totface - psys->totpart)->parent; } else { pa = NULL; } } else { pa = pars + i / totface; } } else { if (psys->part->childtype == PART_CHILD_PARTICLES) { pa = psys->particles + (psys->child + i / totface)->parent; } else { pa = NULL; } } if (pa) { if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) continue; if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) continue; if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) continue; } inMF = orig_mface + i % totface; DM_copy_poly_data(dm, result, i % totface, i, 1); *mf = *inMF; mf->v1 += (i / totface) * totvert; mf->v2 += (i / totface) * totvert; mf->v3 += (i / totface) * totvert; if (mf->v4) { mf->v4 += (i / totface) * totvert; } } CDDM_calc_edges_tessface(result); if (psys->lattice) { end_latt_deform(psys->lattice); psys->lattice = NULL; } if (size) MEM_freeN(size); CDDM_tessfaces_to_faces(result); /*builds ngon faces from tess (mface) faces*/ CDDM_calc_normals(result); return result; }
/* assume converting from linear float to sRGB byte */ void IMB_rect_from_float(struct ImBuf *ibuf) { /* quick method to convert floatbuf to byte */ float *tof = (float *)ibuf->rect_float; // int do_dither = ibuf->dither != 0.f; float dither= ibuf->dither / 255.0f; float srgb[4]; int i, channels= ibuf->channels; short profile= ibuf->profile; unsigned char *to = (unsigned char *) ibuf->rect; if(tof==NULL) return; if(to==NULL) { imb_addrectImBuf(ibuf); to = (unsigned char *) ibuf->rect; } if(channels==1) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof++) to[1]= to[2]= to[3]= to[0] = FTOCHAR(tof[0]); } else if (profile == IB_PROFILE_LINEAR_RGB) { if(channels == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) { srgb[0]= linearrgb_to_srgb(tof[0]); srgb[1]= linearrgb_to_srgb(tof[1]); srgb[2]= linearrgb_to_srgb(tof[2]); to[0] = FTOCHAR(srgb[0]); to[1] = FTOCHAR(srgb[1]); to[2] = FTOCHAR(srgb[2]); to[3] = 255; } } else if (channels == 4) { if (dither != 0.f) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { const float d = (BLI_frand()-0.5f)*dither; srgb[0]= d + linearrgb_to_srgb(tof[0]); srgb[1]= d + linearrgb_to_srgb(tof[1]); srgb[2]= d + linearrgb_to_srgb(tof[2]); srgb[3]= d + tof[3]; to[0] = FTOCHAR(srgb[0]); to[1] = FTOCHAR(srgb[1]); to[2] = FTOCHAR(srgb[2]); to[3] = FTOCHAR(srgb[3]); } } else { floatbuf_to_srgb_byte(tof, to, 0, ibuf->x, 0, ibuf->y, ibuf->x); } } } else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) { if(channels==3) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) { to[0] = FTOCHAR(tof[0]); to[1] = FTOCHAR(tof[1]); to[2] = FTOCHAR(tof[2]); to[3] = 255; } } else { if (dither != 0.f) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { const float d = (BLI_frand()-0.5f)*dither; float col[4]; col[0]= d + tof[0]; col[1]= d + tof[1]; col[2]= d + tof[2]; col[3]= d + tof[3]; to[0] = FTOCHAR(col[0]); to[1] = FTOCHAR(col[1]); to[2] = FTOCHAR(col[2]); to[3] = FTOCHAR(col[3]); } } else { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { to[0] = FTOCHAR(tof[0]); to[1] = FTOCHAR(tof[1]); to[2] = FTOCHAR(tof[2]); to[3] = FTOCHAR(tof[3]); } } } } /* ensure user flag is reset */ ibuf->userflags &= ~IB_RECT_INVALID; }
static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa, int p) { ParticleThreadContext *ctx= thread->ctx; DerivedMesh *dm= ctx->dm; float *v1, *v2, *v3, *v4, nor[3], co[3]; float cur_d, min_d, randu, randv; int distr= ctx->distr; int i, intersect, tot; int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ MFace *mface; MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); pa->num = i = ctx->index[p]; mface = dm->getTessFaceData(dm,i,CD_MFACE); switch (distr) { case PART_DISTR_JIT: if (ctx->jitlevel == 1) { if (mface->v4) psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv); else psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv); } else { float offset = fmod(ctx->jitoff[i] + (float)p, (float)ctx->jitlevel); if (!isnan(offset)) { psys_uv_to_w(ctx->jit[2*(int)offset], ctx->jit[2*(int)offset+1], mface->v4, pa->fuv); } } break; case PART_DISTR_RAND: randu= BLI_rng_get_float(thread->rng); randv= BLI_rng_get_float(thread->rng); rng_skip_tot -= 2; psys_uv_to_w(randu, randv, mface->v4, pa->fuv); break; } pa->foffset= 0.0f; /* experimental */ tot=dm->getNumTessFaces(dm); psys_interpolate_face(mvert,mface,0,0,pa->fuv,co,nor,0,0,0,0); normalize_v3(nor); negate_v3(nor); min_d=FLT_MAX; intersect=0; for (i=0,mface=dm->getTessFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++) { if (i==pa->num) continue; v1=mvert[mface->v1].co; v2=mvert[mface->v2].co; v3=mvert[mface->v3].co; if (isect_ray_tri_v3(co, nor, v2, v3, v1, &cur_d, NULL)) { if (cur_d<min_d) { min_d=cur_d; pa->foffset=cur_d*0.5f; /* to the middle of volume */ intersect=1; } } if (mface->v4) { v4=mvert[mface->v4].co; if (isect_ray_tri_v3(co, nor, v4, v1, v3, &cur_d, NULL)) { if (cur_d<min_d) { min_d=cur_d; pa->foffset=cur_d*0.5f; /* to the middle of volume */ intersect=1; } } } } if (intersect==0) pa->foffset=0.0; else { switch (distr) { case PART_DISTR_JIT: pa->foffset *= ctx->jit[p % (2 * ctx->jitlevel)]; break; case PART_DISTR_RAND: pa->foffset *= BLI_frand(); break; } } if (rng_skip_tot > 0) /* should never be below zero */ BLI_rng_skip(thread->rng, rng_skip_tot); }
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, fbr->distance, pa->prev_state.co, NULL, &ptn); 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, fbr->distance, pa->prev_state.co, NULL, &ptn); 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_frand(); 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; }
/* tries to realize the wanted velocity taking all constraints into account */ void boid_body(BoidBrainData *bbd, ParticleData *pa) { BoidSettings *boids = bbd->part->boids; BoidParticle *bpa = pa->boid; BoidValues val; EffectedPoint epoint; float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3]; float dvec[3], bvec[3]; float new_dir[3], new_speed; float old_dir[3], old_speed; float wanted_dir[3]; float q[4], mat[3][3]; /* rotation */ float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f}; float force[3] = {0.0f, 0.0f, 0.0f}; float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; set_boid_values(&val, boids, pa); /* make sure there's something in new velocity, location & rotation */ copy_particle_key(&pa->state,&pa->prev_state,0); if(bbd->part->flag & PART_SIZEMASS) pa_mass*=pa->size; /* if boids can't fly they fall to the ground */ if((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim)) bpa->data.mode = eBoidMode_Falling; if(bpa->data.mode == eBoidMode_Falling) { /* Falling boids are only effected by gravity. */ acc[2] = bbd->sim->scene->physics_settings.gravity[2]; } else { /* figure out acceleration */ float landing_level = 2.0f; float level = landing_level + 1.0f; float new_vel[3]; if(bpa->data.mode == eBoidMode_Liftoff) { bpa->data.mode = eBoidMode_InAir; bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); } else if(bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { /* auto-leveling & landing if close to ground */ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* level = how many particle sizes above ground */ level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f; landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass; if(pa->prev_state.vel[2] < 0.0f) { if(level < 1.0f) { bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f; bbd->wanted_speed = 0.0f; bpa->data.mode = eBoidMode_Falling; } else if(level < landing_level) { bbd->wanted_speed *= (level - 1.0f)/landing_level; bbd->wanted_co[2] *= (level - 1.0f)/landing_level; } } } copy_v3_v3(old_dir, pa->prev_state.ave); new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co); /* first check if we have valid direction we want to go towards */ if(new_speed == 0.0f) { copy_v3_v3(new_dir, old_dir); } else { float old_dir2[2], wanted_dir2[2], nor[3], angle; copy_v2_v2(old_dir2, old_dir); normalize_v2(old_dir2); copy_v2_v2(wanted_dir2, wanted_dir); normalize_v2(wanted_dir2); /* choose random direction to turn if wanted velocity */ /* is directly behind regardless of z-coordinate */ if(dot_v2v2(old_dir2, wanted_dir2) < -0.99f) { wanted_dir[0] = 2.0f*(0.5f - BLI_frand()); wanted_dir[1] = 2.0f*(0.5f - BLI_frand()); wanted_dir[2] = 2.0f*(0.5f - BLI_frand()); normalize_v3(wanted_dir); } /* constrain direction with maximum angular velocity */ angle = saacos(dot_v3v3(old_dir, wanted_dir)); angle = MIN2(angle, val.max_ave); cross_v3_v3v3(nor, old_dir, wanted_dir); axis_angle_to_quat( q,nor, angle); copy_v3_v3(new_dir, old_dir); mul_qt_v3(q, new_dir); normalize_v3(new_dir); /* save direction in case resulting velocity too small */ axis_angle_to_quat( q,nor, angle*dtime); copy_v3_v3(pa->state.ave, old_dir); mul_qt_v3(q, pa->state.ave); normalize_v3(pa->state.ave); } /* constrain speed with maximum acceleration */ old_speed = len_v3(pa->prev_state.vel); if(bbd->wanted_speed < old_speed) new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc); else new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc); /* combine direction and speed */ copy_v3_v3(new_vel, new_dir); mul_v3_fl(new_vel, new_speed); /* maintain minimum flying velocity if not landing */ if(level >= landing_level) { float len2 = dot_v2v2(new_vel,new_vel); float root; len2 = MAX2(len2, val.min_speed*val.min_speed); root = sasqrt(new_speed*new_speed - len2); new_vel[2] = new_vel[2] < 0.0f ? -root : root; normalize_v2(new_vel); mul_v2_fl(new_vel, sasqrt(len2)); } /* finally constrain speed to max speed */ new_speed = normalize_v3(new_vel); mul_v3_fl(new_vel, MIN2(new_speed, val.max_speed)); /* get acceleration from difference of velocities */ sub_v3_v3v3(acc, new_vel, pa->prev_state.vel); /* break acceleration to components */ project_v3_v3v3(tan_acc, acc, pa->prev_state.ave); sub_v3_v3v3(nor_acc, acc, tan_acc); } /* account for effectors */ pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint); pdDoEffectors(bbd->sim->psys->effectors, bbd->sim->colliders, bbd->part->effector_weights, &epoint, force, NULL); if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) { float length = normalize_v3(force); length = MAX2(0.0f, length - boids->land_stick_force); mul_v3_fl(force, length); } add_v3_v3(acc, force); /* store smoothed acceleration for nice banking etc. */ madd_v3_v3fl(bpa->data.acc, acc, dtime); mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime)); /* integrate new location & velocity */ /* by regarding the acceleration as a force at this stage we*/ /* can get better control allthough it's a bit unphysical */ mul_v3_fl(acc, 1.0f/pa_mass); copy_v3_v3(dvec, acc); mul_v3_fl(dvec, dtime*dtime*0.5f); copy_v3_v3(bvec, pa->prev_state.vel); mul_v3_fl(bvec, dtime); add_v3_v3(dvec, bvec); add_v3_v3(pa->state.co, dvec); madd_v3_v3fl(pa->state.vel, acc, dtime); //if(bpa->data.mode != eBoidMode_InAir) bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor); /* change modes, constrain movement & keep track of down vector */ switch(bpa->data.mode) { case eBoidMode_InAir: { float grav[3]; grav[0]= 0.0f; grav[1]= 0.0f; grav[2]= bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* don't take forward acceleration into account (better banking) */ if(dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) { project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); } else { copy_v3_v3(dvec, bpa->data.acc); } /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); /* stick boid on goal when close enough */ if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } else if(pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { /* land boid when below ground */ if(boids->options & BOID_ALLOW_LAND) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* fly above ground */ else if(bpa->ground) { pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } } break; } case eBoidMode_Falling: { float grav[3]; grav[0]= 0.0f; grav[1]= 0.0f; grav[2]= bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f; /* gather apparent gravity */ madd_v3_v3fl(bpa->gravity, grav, dtime); normalize_v3(bpa->gravity); if(boids->options & BOID_ALLOW_LAND) { /* stick boid on goal when close enough */ if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* land boid when really near ground */ else if(pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height){ pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; bpa->data.mode = eBoidMode_OnLand; } /* if we're falling, can fly and want to go upwards lets fly */ else if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) bpa->data.mode = eBoidMode_InAir; } else bpa->data.mode = eBoidMode_InAir; break; } case eBoidMode_Climbing: { boid_climb(boids, pa, ground_co, ground_nor); //float nor[3]; //copy_v3_v3(nor, ground_nor); ///* gather apparent gravity to r_ve */ //madd_v3_v3fl(pa->r_ve, ground_nor, -1.0); //normalize_v3(pa->r_ve); ///* raise boid it's size from surface */ //mul_v3_fl(nor, pa->size * boids->height); //add_v3_v3v3(pa->state.co, ground_co, nor); ///* remove normal component from velocity */ //project_v3_v3v3(v, pa->state.vel, ground_nor); //sub_v3_v3v3(pa->state.vel, pa->state.vel, v); break; } case eBoidMode_OnLand: { /* stick boid on goal when close enough */ if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { bpa->data.mode = eBoidMode_Climbing; bpa->ground = bbd->goal_ob; boid_find_ground(bbd, pa, ground_co, ground_nor); boid_climb(boids, pa, ground_co, ground_nor); } /* ground is too far away so boid falls */ else if(pa->state.co[2]-ground_co[2] > 1.1f * pa->size * boids->height) bpa->data.mode = eBoidMode_Falling; else { /* constrain to surface */ pa->state.co[2] = ground_co[2] + pa->size * boids->height; pa->state.vel[2] = 0.0f; } if(boids->banking > 0.0f) { float grav[3]; /* Don't take gravity's strength in to account, */ /* otherwise amount of banking is hard to control. */ negate_v3_v3(grav, ground_nor); project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel); sub_v3_v3v3(dvec, bpa->data.acc, dvec); /* gather apparent gravity */ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking); normalize_v3(bpa->gravity); } else { /* gather negative surface normal */ madd_v3_v3fl(bpa->gravity, ground_nor, -1.0f); normalize_v3(bpa->gravity); } break; } } /* save direction to state.ave unless the boid is falling */ /* (boids can't effect their direction when falling) */ if(bpa->data.mode!=eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f*pa->size) { copy_v3_v3(pa->state.ave, pa->state.vel); pa->state.ave[2] *= bbd->part->boids->pitch; normalize_v3(pa->state.ave); } /* apply damping */ if(ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) mul_v3_fl(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); /* calculate rotation matrix based on forward & down vectors */ if(bpa->data.mode == eBoidMode_InAir) { copy_v3_v3(mat[0], pa->state.ave); project_v3_v3v3(dvec, bpa->gravity, pa->state.ave); sub_v3_v3v3(mat[2], bpa->gravity, dvec); normalize_v3(mat[2]); } else { project_v3_v3v3(dvec, pa->state.ave, bpa->gravity); sub_v3_v3v3(mat[0], pa->state.ave, dvec); normalize_v3(mat[0]); copy_v3_v3(mat[2], bpa->gravity); } negate_v3(mat[2]); cross_v3_v3v3(mat[1], mat[2], mat[0]); /* apply rotation */ mat3_to_quat_is_ok( q,mat); copy_qt_qt(pa->state.rot, q); }