/* Project v1 on v2 */ void project_v2_v2v2(float c[2], const float v1[2], const float v2[2]) { const float mul = dot_v2v2(v1, v2) / dot_v2v2(v2, v2); c[0] = mul * v2[0]; c[1] = mul * v2[1]; }
/** * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b))`` */ MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2]) { const float angle_cos = fabsf(dot_v2v2(a, b)); BLI_ASSERT_UNIT_V2(a); BLI_ASSERT_UNIT_V2(b); return (UNLIKELY(angle_cos < SMALL_NUMBER)) ? 1.0f : (1.0f / angle_cos); }
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) { float d[2]; sub_v2_v2v2(d, b, a); return dot_v2v2(d, d); }
/** * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)`` */ MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[2]) { float angle_cos; float ab[2]; BLI_ASSERT_UNIT_V2(a); BLI_ASSERT_UNIT_V2(b); add_v2_v2v2(ab, a, b); angle_cos = (normalize_v2(ab) != 0.0f) ? fabsf(dot_v2v2(a, ab)) : 0.0f; return (UNLIKELY(angle_cos < SMALL_NUMBER)) ? 1.0f : (1.0f / angle_cos); }
MINLINE float dither_random_value(float s, float t) { static float vec[2] = {12.9898f, 78.233f}; float st[2]; float value; copy_v2_fl2(st, s, t); value = sinf(dot_v2v2(st, vec)) * 43758.5453f; return value - floorf(value); }
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; }
MINLINE float normalize_v2_v2(float r[2], const float a[2]) { float d= dot_v2v2(a, a); if(d > 1.0e-35f) { d= sqrtf(d); mul_v2_v2fl(r, a, 1.0f/d); } else { zero_v2(r); d= 0.0f; } return d; }
float angle_normalized_v2v2(const float v1[2], const float v2[2]) { /* this is the same as acos(dot_v3v3(v1, v2)), but more accurate */ if (dot_v2v2(v1, v2) < 0.0f) { float vec[2]; vec[0]= -v2[0]; vec[1]= -v2[1]; return (float)M_PI - 2.0f*saasin(len_v2v2(vec, v1)/2.0f); } else return 2.0f*(float)saasin(len_v2v2(v2, v1)/2.0f); }
float angle_normalized_v2v2(const float v1[2], const float v2[2]) { /* double check they are normalized */ BLI_ASSERT_UNIT_V2(v1); BLI_ASSERT_UNIT_V2(v2); /* this is the same as acos(dot_v3v3(v1, v2)), but more accurate */ if (dot_v2v2(v1, v2) >= 0.0f) { return 2.0f * saasin(len_v2v2(v1, v2) / 2.0f); } else { float v2_n[2]; negate_v2_v2(v2_n, v2); return (float)M_PI - 2.0f * saasin(len_v2v2(v1, v2_n) / 2.0f); } }
float normalize_v2_v2(float r[2], const float a[2]) { float d = dot_v2v2(a, a); if(d > 0.0f) { d = sqrtf(d); mul_v2_v2f(r, a, 1.0f/d); } else { d = 0.0f; zero_v2(r); } return d; }
float BLI_dial_angle(Dial *dial, float current_position[2]) { float current_direction[2]; sub_v2_v2v2(current_direction, current_position, dial->center); /* only update when we have enough precision, by having the mouse adequately away from center */ if (len_squared_v2(current_direction) > dial->threshold_squared) { float angle; float cosval, sinval; normalize_v2(current_direction); if (!dial->initialized) { copy_v2_v2(dial->initial_direction, current_direction); dial->initialized = true; } /* calculate mouse angle between initial and final mouse position */ cosval = dot_v2v2(current_direction, dial->initial_direction); sinval = cross_v2v2(current_direction, dial->initial_direction); /* clamp to avoid nans in acos */ angle = atan2f(sinval, cosval); /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2 */ if ((angle * dial->last_angle < 0.0f) && (fabsf(dial->last_angle) > (float)M_PI_2)) { if (dial->last_angle < 0.0f) dial->rotations--; else dial->rotations++; } dial->last_angle = angle; return angle + 2.0f * (float)M_PI * dial->rotations; } return dial->last_angle; }
bool interp_v2_v2v2_slerp(float target[2], const float a[2], const float b[2], const float t) { float cosom, w[2]; BLI_ASSERT_UNIT_V2(a); BLI_ASSERT_UNIT_V2(b); cosom = dot_v2v2(a, b); /* direct opposites */ if (UNLIKELY(cosom < (1.0f + FLT_EPSILON))) { return false; } interp_dot_slerp(t, cosom, w); target[0] = w[0] * a[0] + w[1] * b[0]; target[1] = w[0] * a[1] + w[1] * b[1]; return true; }
static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) { SlidePointData *data = (SlidePointData *)op->customdata; BezTriple *bezt = &data->point->bezt; float co[2], dco[2]; switch (event->type) { case LEFTALTKEY: case RIGHTALTKEY: case LEFTSHIFTKEY: case RIGHTSHIFTKEY: if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) { if (data->action == SLIDE_ACTION_FEATHER) data->overall_feather = (event->val == KM_PRESS); else data->curvature_only = (event->val == KM_PRESS); } if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) data->accurate = (event->val == KM_PRESS); /* fall-through */ /* update CV position */ case MOUSEMOVE: { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); ED_mask_mouse_pos(sa, ar, event->mval, co); sub_v2_v2v2(dco, co, data->co); if (data->action == SLIDE_ACTION_HANDLE) { float delta[2], offco[2]; sub_v2_v2v2(delta, data->handle, data->co); sub_v2_v2v2(offco, co, data->co); if (data->accurate) mul_v2_fl(offco, 0.2f); add_v2_v2(offco, data->co); add_v2_v2(offco, delta); BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec); } else if (data->action == SLIDE_ACTION_POINT) { float delta[2]; copy_v2_v2(delta, dco); if (data->accurate) mul_v2_fl(delta, 0.2f); add_v2_v2v2(bezt->vec[0], data->vec[0], delta); add_v2_v2v2(bezt->vec[1], data->vec[1], delta); add_v2_v2v2(bezt->vec[2], data->vec[2], delta); } else if (data->action == SLIDE_ACTION_FEATHER) { float vec[2], no[2], p[2], c[2], w, offco[2]; float *weight = NULL; float weight_scalar = 1.0f; int overall_feather = data->overall_feather || data->initial_feather; add_v2_v2v2(offco, data->feather, dco); if (data->uw) { /* project on both sides and find the closest one, * prevents flickering when projecting onto both sides can happen */ const float u_pos = BKE_mask_spline_project_co(data->spline, data->point, data->uw->u, offco, MASK_PROJ_NEG); const float u_neg = BKE_mask_spline_project_co(data->spline, data->point, data->uw->u, offco, MASK_PROJ_POS); float dist_pos = FLT_MAX; float dist_neg = FLT_MAX; float co_pos[2]; float co_neg[2]; float u; if (u_pos > 0.0f && u_pos < 1.0f) { BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos); dist_pos = len_squared_v2v2(offco, co_pos); } if (u_neg > 0.0f && u_neg < 1.0f) { BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg); dist_neg = len_squared_v2v2(offco, co_neg); } u = dist_pos < dist_neg ? u_pos : u_neg; if (u > 0.0f && u < 1.0f) { data->uw->u = u; data->uw = BKE_mask_point_sort_uw(data->point, data->uw); weight = &data->uw->w; weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u); if (weight_scalar != 0.0f) { weight_scalar = 1.0f / weight_scalar; } BKE_mask_point_normal(data->spline, data->point, data->uw->u, no); BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p); } } else { weight = &bezt->weight; /* weight_scalar = 1.0f; keep as is */ copy_v2_v2(no, data->no); copy_v2_v2(p, bezt->vec[1]); } if (weight) { sub_v2_v2v2(c, offco, p); project_v2_v2v2(vec, c, no); w = len_v2(vec); if (overall_feather) { float delta; if (dot_v2v2(no, vec) <= 0.0f) w = -w; delta = w - data->weight * data->weight_scalar; if (data->orig_spline == NULL) { /* restore weight for currently sliding point, so orig_spline would be created * with original weights used */ *weight = data->weight; data->orig_spline = BKE_mask_spline_copy(data->spline); } slide_point_delta_all_feather(data, delta); } else { if (dot_v2v2(no, vec) <= 0.0f) w = 0.0f; if (data->orig_spline) { /* restore possible overall feather changes */ slide_point_restore_spline(data); BKE_mask_spline_free(data->orig_spline); data->orig_spline = NULL; } if (weight_scalar != 0.0f) { *weight = w * weight_scalar; } } } } WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); DAG_id_tag_update(&data->mask->id, 0); break; } case LEFTMOUSE: if (event->val == KM_RELEASE) { Scene *scene = CTX_data_scene(C); /* dont key sliding feather uw's */ if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) { if (IS_AUTOKEY_ON(scene)) { ED_mask_layer_shape_auto_key(data->masklay, CFRA); } } WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); DAG_id_tag_update(&data->mask->id, 0); free_slide_point_data(op->customdata); /* keep this last! */ return OPERATOR_FINISHED; } break; case ESCKEY: cancel_slide_point(op->customdata); WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); DAG_id_tag_update(&data->mask->id, 0); free_slide_point_data(op->customdata); /* keep this last! */ return OPERATOR_CANCELLED; } return OPERATOR_RUNNING_MODAL; }
float angle_signed_v2v2(const float v1[2], const float v2[2]) { const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]); return atan2f(perp_dot, dot_v2v2(v1, v2)); }
/* 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 bool add_vertex_extrude(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) { MaskSpline *spline; MaskSplinePoint *point; MaskSplinePoint *new_point = NULL, *ref_point = NULL; /* check on which side we want to add the point */ int point_index; float tangent_point[2]; float tangent_co[2]; bool do_cyclic_correct = false; bool do_prev; /* use prev point rather then next?? */ if (!masklay) { return false; } else { finSelectedSplinePoint(masklay, &spline, &point, true); } ED_mask_select_toggle_all(mask, SEL_DESELECT); point_index = (point - spline->points); MASKPOINT_DESEL_ALL(point); if ((spline->flag & MASK_SPLINE_CYCLIC) || (point_index > 0 && point_index != spline->tot_point - 1)) { BKE_mask_calc_tangent_polyline(spline, point, tangent_point); sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]); if (dot_v2v2(tangent_point, tangent_co) < 0.0f) { do_prev = true; } else { do_prev = false; } } else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) { do_prev = true; } else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) { do_prev = false; } else { do_prev = false; /* quiet warning */ /* should never get here */ BLI_assert(0); } /* use the point before the active one */ if (do_prev) { point_index--; if (point_index < 0) { point_index += spline->tot_point; /* wrap index */ if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) { do_cyclic_correct = true; point_index = 0; } } } // print_v2("", tangent_point); // printf("%d\n", point_index); mask_spline_add_point_at_index(spline, point_index); if (do_cyclic_correct) { ref_point = &spline->points[point_index + 1]; new_point = &spline->points[point_index]; *ref_point = *new_point; memset(new_point, 0, sizeof(*new_point)); } else { ref_point = &spline->points[point_index]; new_point = &spline->points[point_index + 1]; } masklay->act_point = new_point; setup_vertex_point(mask, spline, new_point, co, 0.5f, ref_point, false); if (masklay->splines_shapes.first) { point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, true, true); } WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); return true; }
/* determines the velocity the boid wants to have */ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) { BoidRule *rule; BoidSettings *boids = bbd->part->boids; BoidValues val; BoidState *state = get_boid_state(boids, pa); BoidParticle *bpa = pa->boid; ParticleSystem *psys = bbd->sim->psys; int rand; //BoidCondition *cond; if (bpa->data.health <= 0.0f) { pa->alive = PARS_DYING; pa->dietime = bbd->cfra; return; } //planned for near future //cond = state->conditions.first; //for (; cond; cond=cond->next) { // if (boid_condition_is_true(cond)) { // pa->boid->state_id = cond->state_id; // state = get_boid_state(boids, pa); // break; /* only first true condition is used */ // } //} zero_v3(bbd->wanted_co); bbd->wanted_speed = 0.0f; /* create random seed for every particle & frame */ rand = (int)(psys_frand(psys, psys->seed + p) * 1000); rand = (int)(psys_frand(psys, (int)bbd->cfra + rand) * 1000); set_boid_values(&val, bbd->part->boids, pa); /* go through rules */ switch (state->ruleset_type) { case eBoidRulesetType_Fuzzy: { for (rule = state->rules.first; rule; rule = rule->next) { if (apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness)) break; /* only first nonzero rule that comes through fuzzy rule is applied */ } break; } case eBoidRulesetType_Random: { /* use random rule for each particle (always same for same particle though) */ rule = BLI_findlink(&state->rules, rand % BLI_listbase_count(&state->rules)); apply_boid_rule(bbd, rule, &val, pa, -1.0); break; } case eBoidRulesetType_Average: { float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f; int n = 0; for (rule = state->rules.first; rule; rule=rule->next) { if (apply_boid_rule(bbd, rule, &val, pa, -1.0f)) { add_v3_v3(wanted_co, bbd->wanted_co); wanted_speed += bbd->wanted_speed; n++; zero_v3(bbd->wanted_co); bbd->wanted_speed = 0.0f; } } if (n > 1) { mul_v3_fl(wanted_co, 1.0f/(float)n); wanted_speed /= (float)n; } copy_v3_v3(bbd->wanted_co, wanted_co); bbd->wanted_speed = wanted_speed; break; } } /* decide on jumping & liftoff */ if (bpa->data.mode == eBoidMode_OnLand) { /* fuzziness makes boids capable of misjudgement */ float mul = 1.0f + state->rule_fuzziness; if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) { float cvel[3], dir[3]; copy_v3_v3(dir, pa->prev_state.ave); normalize_v2(dir); copy_v3_v3(cvel, bbd->wanted_co); normalize_v2(cvel); if (dot_v2v2(cvel, dir) > 0.95f / mul) bpa->data.mode = eBoidMode_Liftoff; } else if (val.jump_speed > 0.0f) { float jump_v[3]; int jump = 0; /* jump to get to a location */ if (bbd->wanted_co[2] > 0.0f) { float cvel[3], dir[3]; float z_v, ground_v, cur_v; float len; copy_v3_v3(dir, pa->prev_state.ave); normalize_v2(dir); copy_v3_v3(cvel, bbd->wanted_co); normalize_v2(cvel); len = len_v2(pa->prev_state.vel); /* first of all, are we going in a suitable direction? */ /* or at a suitably slow speed */ if (dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) { /* try to reach goal at highest point of the parabolic path */ cur_v = len_v2(pa->prev_state.vel); z_v = sasqrt(-2.0f * bbd->sim->scene->physics_settings.gravity[2] * bbd->wanted_co[2]); ground_v = len_v2(bbd->wanted_co)*sasqrt(-0.5f * bbd->sim->scene->physics_settings.gravity[2] / bbd->wanted_co[2]); len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v); if (len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) { jump = 1; len = MIN2(len, val.jump_speed); copy_v3_v3(jump_v, dir); jump_v[2] = z_v; mul_v3_fl(jump_v, ground_v); normalize_v3(jump_v); mul_v3_fl(jump_v, len); add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel); } } } /* jump to go faster */ if (jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) { } if (jump) { copy_v3_v3(pa->prev_state.vel, jump_v); bpa->data.mode = eBoidMode_Falling; } } } }