void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask) { /* we multiply with brush radius as an optimization for the brush * texture sampling functions */ if (mask) { ups->mask_tex_mouse[0] = BLI_rng_get_float(brush_rng) * ups->pixel_radius; ups->mask_tex_mouse[1] = BLI_rng_get_float(brush_rng) * ups->pixel_radius; } else { ups->tex_mouse[0] = BLI_rng_get_float(brush_rng) * ups->pixel_radius; ups->tex_mouse[1] = BLI_rng_get_float(brush_rng) * ups->pixel_radius; } }
static int lattice_select_random_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt; const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; const int seed = RNA_int_get(op->ptr, "seed"); const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); RNG *rng = BLI_rng_new_srandom(seed); int tot; BPoint *bp; tot = lt->pntsu * lt->pntsv * lt->pntsw; bp = lt->def; while (tot--) { if (!bp->hide) { if (BLI_rng_get_float(rng) < randfac) { bpoint_select_set(bp, select); } } bp++; } if (select == false) { lt->actbp = LT_ACTBP_NONE; } BLI_rng_free(rng); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); return OPERATOR_FINISHED; }
/* Random metaball selection */ static int select_random_metaelems_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); MetaBall *mb = (MetaBall *)obedit->data; MetaElem *ml; const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; const int seed = RNA_int_get(op->ptr, "seed"); RNG *rng = BLI_rng_new_srandom(seed); for (ml = mb->editelems->first; ml; ml = ml->next) { if (BLI_rng_get_float(rng) < randfac) { if (select) ml->flag |= SELECT; else ml->flag &= ~SELECT; } } BLI_rng_free(rng); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb); return OPERATOR_FINISHED; }
static void distribute_from_faces_exec(ParticleTask *thread, ParticleData *pa, int p) { ParticleThreadContext *ctx= thread->ctx; DerivedMesh *dm= ctx->dm; float randu, randv; int distr= ctx->distr; int i; int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ MFace *mface; 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; if (rng_skip_tot > 0) /* should never be below zero */ BLI_rng_skip(thread->rng, rng_skip_tot); }
// noise function for wind e.g. static float wind_func(struct RNG *rng, float strength) { int random = (BLI_rng_get_int(rng)+1) % 128; // max 2357 float force = BLI_rng_get_float(rng) + 1.0f; float ret; float sign = 0; sign = ((float)random > 64.0f) ? 1.0f: -1.0f; // dividing by 2 is not giving equal sign distribution ret = sign*((float)random / force)*strength/128.0f; return ret; }
static void curve_select_random(ListBase *editnurb, float randfac, int seed, bool select) { Nurb *nu; BezTriple *bezt; BPoint *bp; int a; RNG *rng = BLI_rng_new_srandom(seed); for (nu = editnurb->first; nu; nu = nu->next) { if (nu->type == CU_BEZIER) { bezt = nu->bezt; a = nu->pntsu; while (a--) { if (!bezt->hide) { if (BLI_rng_get_float(rng) < randfac) { select_beztriple(bezt, select, SELECT, VISIBLE); } } bezt++; } } else { bp = nu->bp; a = nu->pntsu * nu->pntsv; while (a--) { if (!bp->hide) { if (BLI_rng_get_float(rng) < randfac) { select_bpoint(bp, select, SELECT, VISIBLE); } } bp++; } } } BLI_rng_free(rng); }
/* Maps new_w weights in place, using either one of the predefined functions, or a custom curve. * Return values are in new_w. * If indices is not NULL, it must be a table of same length as org_w and new_w, mapping to the real * vertex index (in case the weight tables do not cover the whole vertices...). * cmap might be NULL, in which case curve mapping mode will return unmodified data. */ void weightvg_do_map(int num, float *new_w, short falloff_type, CurveMapping *cmap, RNG *rng) { int i; /* Return immediately, if we have nothing to do! */ /* Also security checks... */ if (((falloff_type == MOD_WVG_MAPPING_CURVE) && (cmap == NULL)) || !ELEM(falloff_type, MOD_WVG_MAPPING_CURVE, MOD_WVG_MAPPING_SHARP, MOD_WVG_MAPPING_SMOOTH, MOD_WVG_MAPPING_ROOT, MOD_WVG_MAPPING_SPHERE, MOD_WVG_MAPPING_RANDOM, MOD_WVG_MAPPING_STEP)) { return; } if (cmap && falloff_type == MOD_WVG_MAPPING_CURVE) { curvemapping_initialize(cmap); } /* Map each weight (vertex) to its new value, accordingly to the chosen mode. */ for (i = 0; i < num; ++i) { float fac = new_w[i]; /* Code borrowed from the warp modifier. */ /* Closely matches PROP_SMOOTH and similar. */ switch (falloff_type) { case MOD_WVG_MAPPING_CURVE: fac = curvemapping_evaluateF(cmap, 0, fac); break; case MOD_WVG_MAPPING_SHARP: fac = fac * fac; break; case MOD_WVG_MAPPING_SMOOTH: fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; break; case MOD_WVG_MAPPING_ROOT: fac = sqrtf(fac); break; case MOD_WVG_MAPPING_SPHERE: fac = sqrtf(2 * fac - fac * fac); break; case MOD_WVG_MAPPING_RANDOM: fac = BLI_rng_get_float(rng) * fac; break; case MOD_WVG_MAPPING_STEP: fac = (fac >= 0.5f) ? 1.0f : 0.0f; break; } new_w[i] = fac; } }
/* Loop over next points to find the end of the stroke, and compute */ static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps, int *nbr_done_gaps, const float tot_gaps_time, const float delta_time, float *next_delta_time) { int j; for (j = idx + 1; j < gtd->num_points; j++) { if (gtd->times[j] < 0) { gtd->times[j] = -gtd->times[j]; if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { /* In this mode, gap time between this stroke and the next should be 0 currently... * So we have to compute its final duration! */ if (gtd->gap_randomness > 0.0f) { /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, * and which sum to exactly tot_gaps_time... */ int rem_gaps = nbr_gaps - (*nbr_done_gaps); if (rem_gaps < 2) { /* Last gap, just give remaining time! */ *next_delta_time = tot_gaps_time; } else { float delta, min, max; /* This code ensures that if the first gaps have been shorter than average gap_duration, * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa! */ delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ min = -gtd->gap_randomness - delta; CLAMP(min, -gtd->gap_randomness, 0.0f); /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */ max = gtd->gap_randomness - delta; CLAMP(max, 0.0f, gtd->gap_randomness); *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min; } } else { *next_delta_time += gtd->gap_duration; } } (*nbr_done_gaps)++; break; } } return j - 1; }
void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], float jitterpos[2]) { float rand_pos[2]; float spread; int diameter; do { rand_pos[0] = BLI_rng_get_float(brush_rng) - 0.5f; rand_pos[1] = BLI_rng_get_float(brush_rng) - 0.5f; } while (len_squared_v2(rand_pos) > SQUARE(0.5f)); if (brush->flag & BRUSH_ABSOLUTE_JITTER) { diameter = 2 * brush->jitter_absolute; spread = 1.0; } else { diameter = 2 * BKE_brush_size_get(scene, brush); spread = brush->jitter; } /* find random position within a circle of diameter 1 */ jitterpos[0] = pos[0] + 2 * rand_pos[0] * diameter * spread; jitterpos[1] = pos[1] + 2 * rand_pos[1] * diameter * spread; }
/* almost exact copy of BLI_jitter_init */ static void init_mv_jit(float *jit, int num, int seed2, float amount) { RNG *rng; float *jit2, x, rad1, rad2, rad3; int i, num2; if (num==0) return; rad1= (float)(1.0f/sqrtf((float)num)); rad2= (float)(1.0f/((float)num)); rad3= (float)sqrtf((float)num)/((float)num); rng = BLI_rng_new(31415926 + num + seed2); x= 0; num2 = 2 * num; for (i=0; i<num2; i+=2) { jit[i] = x + amount*rad1*(0.5f - BLI_rng_get_float(rng)); jit[i+1] = i/(2.0f*num) + amount*rad1*(0.5f - BLI_rng_get_float(rng)); jit[i]-= (float)floor(jit[i]); jit[i+1]-= (float)floor(jit[i+1]); x+= rad3; x -= (float)floor(x); } jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit"); for (i=0 ; i<4 ; i++) { BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); BLI_jitterate2((float (*)[2])jit, (float (*)[2])jit2, num, rad2); } MEM_freeN(jit2); BLI_rng_free(rng); }
void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], float jitterpos[2]) { int use_jitter = (brush->flag & BRUSH_ABSOLUTE_JITTER) ? (brush->jitter_absolute != 0) : (brush->jitter != 0); /* jitter-ed brush gives weird and unpredictable result for this * kinds of stroke, so manually disable jitter usage (sergey) */ use_jitter &= (brush->flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0; if (use_jitter) { float rand_pos[2]; float spread; int diameter; do { rand_pos[0] = BLI_rng_get_float(brush_rng) - 0.5f; rand_pos[1] = BLI_rng_get_float(brush_rng) - 0.5f; } while (len_squared_v2(rand_pos) > (0.5f * 0.5f)); if (brush->flag & BRUSH_ABSOLUTE_JITTER) { diameter = 2 * brush->jitter_absolute; spread = 1.0; } else { diameter = 2 * BKE_brush_size_get(scene, brush); spread = brush->jitter; } /* find random position within a circle of diameter 1 */ jitterpos[0] = pos[0] + 2 * rand_pos[0] * diameter * spread; jitterpos[1] = pos[1] + 2 * rand_pos[1] * diameter * spread; } else { copy_v2_v2(jitterpos, pos); } }
static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) { BoidParticle *bpa = pa->boid; BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule; float vec[3] = {0.0f, 0.0f, 0.0f}; if (asbr->wander > 0.0f) { /* abuse pa->r_ave for wandering */ bpa->wander[0] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng)); bpa->wander[1] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng)); bpa->wander[2] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng)); normalize_v3(bpa->wander); copy_v3_v3(vec, bpa->wander); mul_qt_v3(pa->prev_state.rot, vec); copy_v3_v3(bbd->wanted_co, pa->prev_state.ave); mul_v3_fl(bbd->wanted_co, 1.1f); add_v3_v3(bbd->wanted_co, vec); /* leveling */ if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) { project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity); mul_v3_fl(vec, asbr->level); sub_v3_v3(bbd->wanted_co, vec); } } else { copy_v3_v3(bbd->wanted_co, pa->prev_state.ave); /* may happen at birth */ if (dot_v2v2(bbd->wanted_co, bbd->wanted_co)==0.0f) { bbd->wanted_co[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); bbd->wanted_co[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); bbd->wanted_co[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); } /* leveling */ if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) { project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity); mul_v3_fl(vec, asbr->level); sub_v3_v3(bbd->wanted_co, vec); } } bbd->wanted_speed = asbr->speed * val->max_speed; return 1; }
static bool object_rand_transverts( TransVertStore *tvs, const float offset, const float uniform, const float normal_factor, const unsigned int seed) { bool use_normal = (normal_factor != 0.0f); struct RNG *rng; TransVert *tv; int a; if (!tvs || !(tvs->transverts)) { return false; } rng = BLI_rng_new(seed); tv = tvs->transverts; for (a = 0; a < tvs->transverts_tot; a++, tv++) { const float t = max_ff(0.0f, uniform + ((1.0f - uniform) * BLI_rng_get_float(rng))); float vec[3]; BLI_rng_get_float_unit_v3(rng, vec); if (use_normal && (tv->flag & TX_VERT_USE_NORMAL)) { float no[3]; /* avoid >90d rotation to align with normal */ if (dot_v3v3(vec, tv->normal) < 0.0f) { negate_v3_v3(no, tv->normal); } else { copy_v3_v3(no, tv->normal); } interp_v3_v3v3_slerp_safe(vec, vec, no, normal_factor); } madd_v3_v3fl(tv->loc, vec, offset * t); } BLI_rng_free(rng); return true; }
/* 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_rng_get_float(bbd->rng)); wanted_dir[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); wanted_dir[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng)); normalize_v3(wanted_dir); } /* constrain direction with maximum angular velocity */ angle = saacos(dot_v3v3(old_dir, wanted_dir)); angle = min_ff(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); }
static void createFacepa(ExplodeModifierData *emd, ParticleSystemModifierData *psmd, DerivedMesh *dm) { ParticleSystem *psys = psmd->psys; MFace *fa = NULL, *mface = NULL; MVert *mvert = NULL; ParticleData *pa; KDTree *tree; RNG *rng; float center[3], co[3]; int *facepa = NULL, *vertpa = NULL, totvert = 0, totface = 0, totpart = 0; int i, p, v1, v2, v3, v4 = 0; mvert = dm->getVertArray(dm); mface = dm->getTessFaceArray(dm); totface = dm->getNumTessFaces(dm); totvert = dm->getNumVerts(dm); totpart = psmd->psys->totpart; rng = BLI_rng_new_srandom(psys->seed); if (emd->facepa) MEM_freeN(emd->facepa); facepa = emd->facepa = MEM_callocN(sizeof(int) * totface, "explode_facepa"); vertpa = MEM_callocN(sizeof(int) * totvert, "explode_vertpa"); /* initialize all faces & verts to no particle */ for (i = 0; i < totface; i++) facepa[i] = totpart; for (i = 0; i < totvert; i++) vertpa[i] = totpart; /* set protected verts */ if (emd->vgroup) { MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); if (dvert) { const int defgrp_index = emd->vgroup - 1; for (i = 0; i < totvert; i++, dvert++) { float val = BLI_rng_get_float(rng); val = (1.0f - emd->protect) * val + emd->protect * 0.5f; if (val < defvert_find_weight(dvert, defgrp_index)) vertpa[i] = -1; } } } /* make tree of emitter locations */ tree = BLI_kdtree_new(totpart); for (p = 0, pa = psys->particles; p < totpart; p++, pa++) { psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL); BLI_kdtree_insert(tree, p, co); } BLI_kdtree_balance(tree); /* set face-particle-indexes to nearest particle to face center */ for (i = 0, fa = mface; i < totface; i++, fa++) { add_v3_v3v3(center, mvert[fa->v1].co, mvert[fa->v2].co); add_v3_v3(center, mvert[fa->v3].co); if (fa->v4) { add_v3_v3(center, mvert[fa->v4].co); mul_v3_fl(center, 0.25); } else mul_v3_fl(center, 1.0f / 3.0f); p = BLI_kdtree_find_nearest(tree, center, NULL); v1 = vertpa[fa->v1]; v2 = vertpa[fa->v2]; v3 = vertpa[fa->v3]; if (fa->v4) v4 = vertpa[fa->v4]; if (v1 >= 0 && v2 >= 0 && v3 >= 0 && (fa->v4 == 0 || v4 >= 0)) facepa[i] = p; if (v1 >= 0) vertpa[fa->v1] = p; if (v2 >= 0) vertpa[fa->v2] = p; if (v3 >= 0) vertpa[fa->v3] = p; if (fa->v4 && v4 >= 0) vertpa[fa->v4] = p; } if (vertpa) MEM_freeN(vertpa); BLI_kdtree_free(tree); BLI_rng_free(rng); }
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 void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, int p) { ParticleThreadContext *ctx= thread->ctx; Object *ob= ctx->sim.ob; DerivedMesh *dm= ctx->dm; float orco1[3], co1[3], nor1[3]; float randu, randv; int cfrom= ctx->cfrom; int i; int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ MFace *mf; if (ctx->index[p] < 0) { cpa->num=0; cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; return; } mf= dm->getTessFaceData(dm, ctx->index[p], CD_MFACE); randu= BLI_rng_get_float(thread->rng); randv= BLI_rng_get_float(thread->rng); rng_skip_tot -= 2; psys_uv_to_w(randu, randv, mf->v4, cpa->fuv); cpa->num = ctx->index[p]; if (ctx->tree) { KDTreeNearest ptn[10]; int w,maxw;//, do_seams; float maxd /*, mind,dd */, totw= 0.0f; int parent[10]; float pweight[10]; psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,NULL,NULL,orco1,NULL); BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1); maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3); maxd=ptn[maxw-1].dist; /* mind=ptn[0].dist; */ /* UNUSED */ /* the weights here could be done better */ for (w=0; w<maxw; w++) { parent[w]=ptn[w].index; pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); } for (;w<10; w++) { parent[w]=-1; pweight[w]=0.0f; } for (w=0,i=0; w<maxw && i<4; w++) { if (parent[w]>=0) { cpa->pa[i]=parent[w]; cpa->w[i]=pweight[w]; totw+=pweight[w]; i++; } } for (;i<4; i++) { cpa->pa[i]=-1; cpa->w[i]=0.0f; } if (totw > 0.0f) { for (w = 0; w < 4; w++) { cpa->w[w] /= totw; } } cpa->parent=cpa->pa[0]; } if (rng_skip_tot > 0) /* should never be below zero */ BLI_rng_skip(thread->rng, rng_skip_tot); }
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_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]; memset(&col, 0, sizeof(ParticleCollision)); 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_rng_get_float(bbd->rng); bbd->wanted_co[1] = BLI_rng_get_float(bbd->rng); bbd->wanted_co[2] = BLI_rng_get_float(bbd->rng); } 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__normal( bbd->sim->psys->tree, pa->prev_state.co, pa->prev_state.ave, &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel)); 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__normal( epsys->tree, pa->prev_state.co, pa->prev_state.ave, &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel)); 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; }
static float nextfr(RNG *rng, float min, float max) { return BLI_rng_get_float(rng) * (min - max) + max; }