From b4353a84439085ac7d4bb7da2daed983637a75cc Mon Sep 17 00:00:00 2001 From: Janne Karhu Date: Mon, 20 Jul 2009 23:52:53 +0000 Subject: Initial code for boids v2 Too many new features to list! But here are the biggies: - Boids can move on air and/or land, or climb a goal object. - Proper interaction with collision objects. * Closest collision object in negative z direction is considered as ground. * Other collision objects are obstacles and boids collide with them. - Boid behavior rules are now added to a dynamic list. * Many new rules and many still not implemented. * Different rule evaluation modes (fuzzy, random, average). - Only particle systems defined by per system "boid relations" are considered for simulation of that system. * This is in addition to the boids own system of course. * Relations define other systems as "neutral", "friend" or "enemy". - All effectors now effect boid physics, not boid brains. * This allows forcing boids somewhere. * Exception to this is new "boid" effector, which defines boid predators (positive strength) and goals (negative strength). Known issue: - Boid health isn't yet stored in pointcache so simulations with "fight" rule are not be read from cache properly. - Object/Group visualization object's animation is not played in "particle time". This is definately the wanted behavior, but isn't possible with the current state of dupliobject code. Other new features: - Particle systems can now be named separately from particle settings. * Default name for particle settings is now "ParticleSettings" instead of "PSys" - Per particle system list of particle effector weights. * Enables different effection strengths for particles from different particle systems with without messing around with effector group setting. Other code changes: - KDTree now supports range search as it's needed for new boids. - "Keyed particle targets" renamed as general "particle targets", as they're needed for boids too. (this might break some files saved with new keyed particles) Bug fixes: - Object & group visualizations didn't work. - Interpolating pointcache didn't do rotation. --- source/blender/blenkernel/BKE_boids.h | 62 + source/blender/blenkernel/BKE_particle.h | 29 +- source/blender/blenkernel/intern/boids.c | 1526 ++++++++++++++++++++ source/blender/blenkernel/intern/depsgraph.c | 80 +- source/blender/blenkernel/intern/effect.c | 3 + source/blender/blenkernel/intern/modifier.c | 50 +- source/blender/blenkernel/intern/object.c | 46 +- source/blender/blenkernel/intern/particle.c | 55 +- source/blender/blenkernel/intern/particle_system.c | 1228 ++++++---------- source/blender/blenlib/BLI_kdtree.h | 6 +- source/blender/blenlib/intern/BLI_kdtree.c | 113 +- source/blender/blenloader/intern/readfile.c | 74 +- source/blender/blenloader/intern/writefile.c | 54 +- source/blender/editors/include/ED_physics.h | 1 + source/blender/editors/physics/physics_boids.c | 433 ++++++ source/blender/editors/space_api/spacetypes.c | 1 + .../blender/editors/space_buttons/buttons_intern.h | 8 +- source/blender/editors/space_buttons/buttons_ops.c | 112 +- .../blender/editors/space_buttons/space_buttons.c | 8 +- source/blender/editors/space_view3d/drawobject.c | 23 +- source/blender/makesdna/DNA_modifier_types.h | 5 + source/blender/makesdna/DNA_object_force.h | 1 + source/blender/makesdna/DNA_particle_types.h | 68 +- source/blender/makesdna/intern/makesdna.c | 2 + source/blender/makesrna/RNA_access.h | 2 +- source/blender/makesrna/RNA_enum_types.h | 1 + source/blender/makesrna/intern/makesrna.c | 1 + source/blender/makesrna/intern/rna_boid.c | 615 ++++++++ source/blender/makesrna/intern/rna_fluidsim.c | 2 +- source/blender/makesrna/intern/rna_internal.h | 1 + source/blender/makesrna/intern/rna_object_force.c | 3 +- source/blender/makesrna/intern/rna_particle.c | 368 +++-- 32 files changed, 3830 insertions(+), 1151 deletions(-) create mode 100644 source/blender/blenkernel/BKE_boids.h create mode 100644 source/blender/blenkernel/intern/boids.c create mode 100644 source/blender/editors/physics/physics_boids.c create mode 100644 source/blender/makesrna/intern/rna_boid.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h new file mode 100644 index 00000000000..acceff863b9 --- /dev/null +++ b/source/blender/blenkernel/BKE_boids.h @@ -0,0 +1,62 @@ +/* BKE_particle.h + * + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BKE_BOIDS_H +#define BKE_BOIDS_H + +#include "DNA_boid_types.h" + +typedef struct BoidBrainData { + Scene *scene; + struct Object *ob; + struct ParticleSystem *psys; + struct ParticleSettings *part; + float timestep, cfra, dfra; + float wanted_co[3], wanted_speed; + + /* Goal stuff */ + struct Object *goal_ob; + float goal_co[3]; + float goal_nor[3]; + float goal_priority; +} BoidBrainData; + +void boids_precalc_rules(struct ParticleSettings *part, float cfra); +void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa); +void boid_body(BoidBrainData *bbd, struct ParticleData *pa); +void boid_default_settings(BoidSettings *boids); +BoidRule *boid_new_rule(int type); +BoidState *boid_new_state(BoidSettings *boids); +BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state); +void boid_free_settings(BoidSettings *boids); +BoidSettings *boid_copy_settings(BoidSettings *boids); +BoidState *boid_get_current_state(BoidSettings *boids); +#endif diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index bb0b905b8e3..9f44d3c47cb 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -95,16 +95,6 @@ typedef struct ParticleTexture{ float rough1, rough2, roughe; /* used in path caching */ } ParticleTexture; -typedef struct BoidVecFunc{ - void (*Addf)(float *v, float *v1, float *v2); - void (*Subf)(float *v, float *v1, float *v2); - void (*Mulf)(float *v, float f); - float (*Length)(float *v); - float (*Normalize)(float *v); - float (*Inpf)(float *v1, float *v2); - void (*Copyf)(float *v1, float *v2); -} BoidVecFunc; - typedef struct ParticleSeam{ float v0[3], v1[3]; float nor[3], dir[3], tan[3]; @@ -209,6 +199,19 @@ typedef struct ParticleBillboardData } ParticleBillboardData; +/* container for moving data between deflet_particle and particle_intersect_face */ +typedef struct ParticleCollision +{ + struct Object *ob, *ob_t; // collided and current objects + struct CollisionModifierData *md; // collision modifier for ob_t; + float nor[3]; // normal at collision point + float vel[3]; // velocity of collision point + float co1[3], co2[3]; // ray start and end points + float ray_len; // original length of co2-co1, needed for collision time evaluation + float t; // time of previous collision, needed for substracting face velocity +} +ParticleCollision; + /* ----------- functions needed outside particlesystem ---------------- */ /* particle.c */ int count_particles(struct ParticleSystem *psys); @@ -231,6 +234,7 @@ int psys_ob_has_hair(struct Object *ob); int psys_in_edit_mode(struct Scene *scene, struct ParticleSystem *psys); int psys_check_enabled(struct Object *ob, struct ParticleSystem *psys); +void psys_free_boid_rules(struct ListBase *list); void psys_free_settings(struct ParticleSettings *part); void free_child_path_cache(struct ParticleSystem *psys); void psys_free_path_cache(struct ParticleSystem *psys); @@ -288,6 +292,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]); /* particle_system.c */ +struct ParticleSystem *psys_get_target_system(struct Object *ob, struct ParticleTarget *pt); void psys_count_keyed_targets(struct Object *ob, struct ParticleSystem *psys); void psys_get_reactor_target(struct Object *ob, struct ParticleSystem *psys, struct Object **target_ob, struct ParticleSystem **target_psys); @@ -298,6 +303,8 @@ void psys_make_temp_pointcache(struct Object *ob, struct ParticleSystem *psys); void psys_end_temp_pointcache(struct ParticleSystem *psys); void psys_get_pointcache_start_end(struct Scene *scene, struct ParticleSystem *psys, int *sfra, int *efra); +void psys_check_boid_data(struct ParticleSystem *psys); + void particle_system_update(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); /* ----------- functions needed only inside particlesystem ------------ */ @@ -321,11 +328,13 @@ float psys_interpolate_value_from_verts(struct DerivedMesh *dm, short from, int void psys_get_from_key(struct ParticleKey *key, float *loc, float *vel, float *rot, float *time); int psys_intersect_dm(struct Scene *scene, struct Object *ob, struct DerivedMesh *dm, float *vert_cos, float *co1, float* co2, float *min_d, int *min_face, float *min_uv, float *face_minmax, float *pa_minmax, float radius, float *ipoint); +void particle_intersect_face(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit); void psys_particle_on_dm(struct DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor); /* particle_system.c */ void initialize_particle(struct ParticleData *pa, int p, struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd); +int effector_find_co(struct Scene *scene, float *pco, struct SurfaceModifierData *sur, struct Object *ob, struct PartDeflect *pd, float *co, float *nor, float *vel, int *index); void do_effectors(int pa_no, struct ParticleData *pa, struct ParticleKey *state, struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, float *texco, float *force_field, float *vel,float framestep, float cfra); void psys_calc_dmcache(struct Object *ob, struct DerivedMesh *dm, struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c new file mode 100644 index 00000000000..931ad0b3043 --- /dev/null +++ b/source/blender/blenkernel/intern/boids.c @@ -0,0 +1,1526 @@ +/* boids.c + * + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_particle_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_boid_types.h" +#include "DNA_listBase.h" + +#include "BLI_rand.h" +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_kdtree.h" +#include "BLI_kdopbvh.h" +#include "BKE_effect.h" +#include "BKE_boids.h" +#include "BKE_particle.h" +#include "BKE_utildefines.h" +#include "BKE_modifier.h" + +#include "RNA_enum_types.h" + +typedef struct BoidValues { + float max_speed, max_acc; + float max_ave, min_speed; + float personal_space, jump_speed; +} BoidValues; + +static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness); + +static int rule_none(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa) +{ + return 0; +} + +static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*) rule; + BoidSettings *boids = bbd->part->boids; + ParticleEffectorCache *ec; + Object *priority_ob = NULL; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0); + float priority = 0.0f, len; + int ret = 0; + + /* first find out goal/predator with highest priority */ + /* if rule->ob specified use it */ + if(gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != pa->stick_ob)) { + PartDeflect *pd = gabr->ob->pd; + float vec_to_part[3]; + + if(pd && pd->forcefield == PFIELD_BOID) { + effector_find_co(bbd->scene, pa->prev_state.co, NULL, gabr->ob, pd, loc, vec, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + + priority = mul * pd->f_strength * effector_falloff(pd, vec, vec_to_part); + } + else + priority = 1.0; + + priority = 1.0; + priority_ob = gabr->ob; + } + else for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_EFFECTOR) { + Object *eob = ec->ob; + PartDeflect *pd = eob->pd; + + /* skip current object */ + if(rule->type == eBoidRuleType_Goal && eob == pa->stick_ob) + continue; + + if(pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f) { + float vec_to_part[3], temp; + + effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, vec, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + + temp = mul * pd->f_strength * effector_falloff(pd, vec, vec_to_part); + + if(temp == 0.0f) + ; /* do nothing */ + else if(temp > priority) { + priority = temp; + priority_ob = eob; + len = VecLength(vec_to_part); + } + /* choose closest object with same priority */ + else if(temp == priority) { + float len2 = VecLength(vec_to_part); + + if(len2 < len) { + priority_ob = eob; + len = len2; + } + } + } + } + } + + /* then use that effector */ + if(priority > (rule->type==eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) { /* with avoid, factor is "fear factor" */ + Object *eob = priority_ob; + PartDeflect *pd = eob->pd; + float vec_to_part[3]; + float surface = 0.0f; + float nor[3]; + + if(gabr->options & BRULE_GOAL_AVOID_PREDICT) { + /* estimate future location of target */ + surface = (float)effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, nor, vec, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + len = Normalize(vec_to_part); + + VecMulf(vec, len / (val->max_speed * bbd->timestep)); + VecAddf(loc, loc, vec); + VecSubf(vec_to_part, pa->prev_state.co, loc); + } + else { + surface = (float)effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, nor, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + len = VecLength(vec_to_part); + } + + if(rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface!=0.0f) { + if(!bbd->goal_ob || bbd->goal_priority < priority) { + bbd->goal_ob = eob; + VECCOPY(bbd->goal_co, loc); + VECCOPY(bbd->goal_nor, nor); + } + } + else if(rule->type == eBoidRuleType_Avoid && pa->boid->mode == eBoidMode_Climbing && + priority > 2.0f * gabr->fear_factor) { + /* detach from surface and try to fly away from danger */ + VECCOPY(vec_to_part, pa->r_ve); + VecMulf(vec_to_part, -1.0f); + } + + VECCOPY(bbd->wanted_co, vec_to_part); + VecMulf(bbd->wanted_co, mul); + + bbd->wanted_speed = val->max_speed * priority; + + /* with goals factor is approach velocity factor */ + if(rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) { + float len2 = 2.0f*VecLength(pa->prev_state.vel); + + surface *= pa->size * boids->height; + + if(len2 > 0.0f && len - surface < len2) { + len2 = (len - surface)/len2; + bbd->wanted_speed *= pow(len2, boids->landing_smoothness); + } + } + + ret = 1; + } + + return ret; +} + +static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule; + KDTreeNearest *ptn = NULL; + ParticleEffectorCache *ec; + ParticleTarget *pt; + 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) { + ParticleCollision col; + BVHTreeRayHit hit; + float radius = val->personal_space * pa->size, ray_dir[3]; + + VECCOPY(col.co1, pa->prev_state.co); + VecAddf(col.co2, pa->prev_state.co, pa->prev_state.vel); + VecSubf(ray_dir, col.co2, col.co1); + VecMulf(ray_dir, acbr->look_ahead); + col.t = 0.0f; + hit.index = -1; + hit.dist = col.ray_len = VecLength(ray_dir); + + /* find out closest deflector object */ + for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_DEFLECT) { + Object *eob = ec->ob; + + /* don't check with current ground object */ + if(eob == pa->stick_ob) + continue; + + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( eob, eModifierType_Collision ) ); + col.ob_t = eob; + + if(col.md && col.md->bvhtree) + BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, particle_intersect_face, &col); + } + } + /* then avoid that object */ + if(hit.index>=0) { + /* TODO: not totally happy with this part */ + t = hit.dist/col.ray_len; + + VECCOPY(bbd->wanted_co, col.nor); + + VecMulf(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size); + + bbd->wanted_speed = sqrt(t) * VecLength(pa->prev_state.vel); + + return 1; + } + } + + //check boids in own system + if(acbr->options & BRULE_ACOLL_WITH_BOIDS) + { + neighbors = BLI_kdtree_range_search(bbd->psys->tree, acbr->look_ahead * VecLength(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); + if(neighbors > 1) for(n=1; nprev_state.co); + VECCOPY(vel1, pa->prev_state.vel); + VECCOPY(co2, (bbd->psys->particles + ptn[n].index)->prev_state.co); + VECCOPY(vel2, (bbd->psys->particles + ptn[n].index)->prev_state.vel); + + VecSubf(loc, co1, co2); + + VecSubf(vec, vel1, vel2); + + inp = Inpf(vec,vec); + + /* velocities not parallel */ + if(inp != 0.0f) { + t = -Inpf(loc, vec)/inp; + /* cpa is not too far in the future so investigate further */ + if(t > 0.0f && t < t_min) { + VECADDFAC(co1, co1, vel1, t); + VECADDFAC(co2, co2, vel2, t); + + VecSubf(vec, co2, co1); + + len = Normalize(vec); + + /* distance of cpa is close enough */ + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + + VecMulf(vec, VecLength(vel1)); + VecMulf(vec, (2.0f - t)/2.0f); + VecSubf(bbd->wanted_co, vel1, vec); + bbd->wanted_speed = VecLength(bbd->wanted_co); + ret = 1; + } + } + } + } + } + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + + /* check boids in other systems */ + for(pt=bbd->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->ob, pt); + + if(epsys) { + neighbors = BLI_kdtree_range_search(epsys->tree, acbr->look_ahead * VecLength(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); + if(neighbors > 0) for(n=0; nprev_state.co); + VECCOPY(vel1, pa->prev_state.vel); + VECCOPY(co2, (epsys->particles + ptn[n].index)->prev_state.co); + VECCOPY(vel2, (epsys->particles + ptn[n].index)->prev_state.vel); + + VecSubf(loc, co1, co2); + + VecSubf(vec, vel1, vel2); + + inp = Inpf(vec,vec); + + /* velocities not parallel */ + if(inp != 0.0f) { + t = -Inpf(loc, vec)/inp; + /* cpa is not too far in the future so investigate further */ + if(t > 0.0f && t < t_min) { + VECADDFAC(co1, co1, vel1, t); + VECADDFAC(co2, co2, vel2, t); + + VecSubf(vec, co2, co1); + + len = Normalize(vec); + + /* distance of cpa is close enough */ + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + + VecMulf(vec, VecLength(vel1)); + VecMulf(vec, (2.0f - t)/2.0f); + VecSubf(bbd->wanted_co, vel1, vec); + bbd->wanted_speed = VecLength(bbd->wanted_co); + ret = 1; + } + } + } + } + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + } + } + + + if(ptn && nearest==0) + MEM_freeN(ptn); + + return ret; +} +static int rule_separate(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + KDTreeNearest *ptn = NULL; + ParticleTarget *pt; + float len = 2.0f * val->personal_space * pa->size + 1.0f; + float vec[3] = {0.0f, 0.0f, 0.0f}; + int neighbors = BLI_kdtree_range_search(bbd->psys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); + int ret = 0; + + if(neighbors > 1 && ptn[1].dist!=0.0f) { + VecSubf(vec, pa->prev_state.co, bbd->psys->particles[ptn[1].index].state.co); + VecMulf(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist); + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + bbd->wanted_speed = val->max_speed; + len = ptn[1].dist; + ret = 1; + } + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + + /* check other boid systems */ + for(pt=bbd->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->ob, pt); + + if(epsys) { + neighbors = BLI_kdtree_range_search(epsys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); + + if(neighbors > 0 && ptn[0].dist < len) { + VecSubf(vec, pa->prev_state.co, ptn[0].co); + VecMulf(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist); + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + bbd->wanted_speed = val->max_speed; + len = ptn[0].dist; + ret = 1; + } + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + } + } + return ret; +} +static int rule_flock(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + KDTreeNearest ptn[11]; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + int neighbors = BLI_kdtree_find_n_nearest(bbd->psys->tree, 11, pa->state.co, pa->prev_state.ave, ptn); + int n, nearest = 1; + int ret = 0; + + if(neighbors > 1) { + for(n=1; npsys->particles[ptn[n].index].prev_state.co); + VecAddf(vec, vec, bbd->psys->particles[ptn[n].index].prev_state.vel); + } + + VecMulf(loc, 1.0f/((float)neighbors - 1.0f)); + VecMulf(vec, 1.0f/((float)neighbors - 1.0f)); + + VecSubf(loc, loc, pa->prev_state.co); + VecSubf(vec, vec, pa->prev_state.vel); + + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + VecAddf(bbd->wanted_co, bbd->wanted_co, loc); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + return ret; +} +static int rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + float mul, len; + int n = (flbr->queue_size <= 1) ? bbd->psys->totpart : flbr->queue_size; + int i, ret = 0, p = pa - bbd->psys->particles; + + if(flbr->ob) { + float vec2[3], t; + + /* first check we're not blocking the leader*/ + VecSubf(vec, flbr->loc, flbr->oloc); + VecMulf(vec, 1.0f/bbd->timestep); + + VecSubf(loc, pa->prev_state.co, flbr->oloc); + + mul = Inpf(vec, vec); + + /* leader is not moving */ + if(mul < 0.01) { + len = VecLength(loc); + /* too close to leader */ + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed; + return 1; + } + } + else { + t = Inpf(loc, vec)/mul; + + /* possible blocking of leader in near future */ + if(t > 0.0f && t < 3.0f) { + VECCOPY(vec2, vec); + VecMulf(vec2, t); + + VecSubf(vec2, loc, vec2); + + len = VecLength(vec2); + + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, vec2); + bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; + return 1; + } + } + } + + /* not blocking so try to follow leader */ + if(p && flbr->options & BRULE_LEADER_IN_LINE) { + VECCOPY(vec, bbd->psys->particles[p-1].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p-1].prev_state.co); + } + else { + VECCOPY(loc, flbr->oloc); + VecSubf(vec, flbr->loc, flbr->oloc); + VecMulf(vec, 1.0/bbd->timestep); + } + + /* fac is seconds behind leader */ + VECADDFAC(loc, loc, vec, -flbr->distance); + + VecSubf(bbd->wanted_co, loc, pa->prev_state.co); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + else if(p % n) { + float vec2[3], t, t_min = 3.0f; + + /* first check we're not blocking any leaders */ + for(i = 0; i< bbd->psys->totpart; i+=n){ + VECCOPY(vec, bbd->psys->particles[i].prev_state.vel); + + VecSubf(loc, pa->prev_state.co, bbd->psys->particles[i].prev_state.co); + + mul = Inpf(vec, vec); + + /* leader is not moving */ + if(mul < 0.01) { + len = VecLength(loc); + /* too close to leader */ + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed; + return 1; + } + } + else { + t = Inpf(loc, vec)/mul; + + /* possible blocking of leader in near future */ + if(t > 0.0f && t < t_min) { + VECCOPY(vec2, vec); + VecMulf(vec2, t); + + VecSubf(vec2, loc, vec2); + + len = VecLength(vec2); + + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; + ret = 1; + } + } + } + } + + if(ret) return 1; + + /* not blocking so try to follow leader */ + if(flbr->options & BRULE_LEADER_IN_LINE) { + VECCOPY(vec, bbd->psys->particles[p-1].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p-1].prev_state.co); + } + else { + VECCOPY(vec, bbd->psys->particles[p - p%n].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p - p%n].prev_state.co); + } + + /* fac is seconds behind leader */ + VECADDFAC(loc, loc, vec, -flbr->distance); + + VecSubf(bbd->wanted_co, loc, pa->prev_state.co); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + + return ret; +} +static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + if(asbr->wander > 0.0f) { + /* abuse pa->r_ave for wandering */ + pa->r_ave[0] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + pa->r_ave[1] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + pa->r_ave[2] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + + Normalize(pa->r_ave); + + VECCOPY(vec, pa->r_ave); + + QuatMulVecf(pa->prev_state.rot, vec); + + VECCOPY(bbd->wanted_co, pa->prev_state.ave); + + VecMulf(bbd->wanted_co, 1.1f); + + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + + /* leveling */ + if(asbr->level > 0.0f) { + Projf(vec, bbd->wanted_co, bbd->psys->part->acc); + VecMulf(vec, asbr->level); + VecSubf(bbd->wanted_co, bbd->wanted_co, vec); + } + } + else { + VECCOPY(bbd->wanted_co, pa->prev_state.ave); + + /* may happen at birth */ + if(Inp2f(bbd->wanted_co,bbd->wanted_co)==0.0f) { + bbd->wanted_co[0] = 2.0f*(0.5f - BLI_frand()); + bbd->wanted_co[1] = 2.0f*(0.5f - BLI_frand()); + bbd->wanted_co[2] = 2.0f*(0.5f - BLI_frand()); + } + + /* leveling */ + if(asbr->level > 0.0f) { + Projf(vec, bbd->wanted_co, bbd->psys->part->acc); + VecMulf(vec, asbr->level); + VecSubf(bbd->wanted_co, bbd->wanted_co, vec); + } + + } + bbd->wanted_speed = asbr->speed * val->max_speed; + + return 1; +} +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; + /* 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->psys->tree, fbr->distance, pa->prev_state.co, NULL, &ptn); + for(n=0; npsys->particles[ptn[n].index].boid->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->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->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; nhealth; + + if(n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) { + VECCOPY(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) { + VecSubf(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] = {bbd->wanted_co[0],bbd->wanted_co[1],bbd->wanted_co[2]}; + + Normalize(enemy_dir); + + /* fight mode */ + bbd->wanted_speed = 0.0f; + + /* must face enemy to fight */ + if(Inpf(pa->prev_state.ave, enemy_dir)>0.5f) { + enemy_pa->boid->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 */ + if(pa->boid->health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) { + /* decide to flee */ + if(closest_dist < fbr->flee_distance * fbr->distance) { + VecMulf(bbd->wanted_co, -1.0f); + bbd->wanted_speed = val->max_speed; + } + else { /* wait for better odds */ + bbd->wanted_speed = 0.0f; + } + } + + ret = 1; + } + + return ret; +} + +typedef int (*boid_rule_cb)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa); + +static boid_rule_cb boid_rules[] = { + rule_none, + rule_goal_avoid, + rule_goal_avoid, + rule_avoid_collision, + rule_separate, + rule_flock, + rule_follow_leader, + rule_average_speed, + rule_fight, + //rule_help, + //rule_protect, + //rule_hide, + //rule_follow_path, + //rule_follow_wall +}; + +static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa) +{ + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) { + val->max_speed = boids->land_max_speed * pa->boid->health/boids->health; + val->max_acc = boids->land_max_acc * val->max_speed; + val->max_ave = boids->land_max_ave * M_PI * pa->boid->health/boids->health; + val->min_speed = 0.0f; /* no minimum speed on land */ + val->personal_space = boids->land_personal_space; + val->jump_speed = boids->land_jump_speed * pa->boid->health/boids->health; + } + else { + val->max_speed = boids->air_max_speed * pa->boid->health/boids->health; + val->max_acc = boids->air_max_acc * val->max_speed; + val->max_ave = boids->air_max_ave * M_PI * pa->boid->health/boids->health; + val->min_speed = boids->air_min_speed * boids->air_max_speed; + val->personal_space = boids->air_personal_space; + val->jump_speed = 0.0f; /* no jumping in air */ + } +} +static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float *ground_co, float *ground_nor) +{ + if(pa->boid->mode == eBoidMode_Climbing) { + SurfaceModifierData *surmd = NULL; + float x[3], v[3]; + + surmd = (SurfaceModifierData *)modifiers_findByType ( pa->stick_ob, eModifierType_Surface ); + + /* take surface velocity into account */ + effector_find_co(bbd->scene, pa->state.co, surmd, NULL, NULL, x, NULL, v, NULL); + VecAddf(x, x, v); + + /* get actual position on surface */ + effector_find_co(bbd->scene, x, surmd, NULL, NULL, ground_co, ground_nor, NULL, NULL); + + return pa->stick_ob; + } + else { + float zvec[3] = {0.0f, 0.0f, 2000.0f}; + ParticleCollision col; + BVHTreeRayHit hit; + ParticleEffectorCache *ec; + float radius = 0.0f, t, ray_dir[3]; + + VECCOPY(col.co1, pa->state.co); + VECCOPY(col.co2, pa->state.co); + VecAddf(col.co1, col.co1, zvec); + VecSubf(col.co2, col.co2, zvec); + VecSubf(ray_dir, col.co2, col.co1); + col.t = 0.0f; + hit.index = -1; + hit.dist = col.ray_len = VecLength(ray_dir); + + /* find out upmost deflector object */ + for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_DEFLECT) { + Object *eob = ec->ob; + + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( eob, eModifierType_Collision ) ); + col.ob_t = eob; + + if(col.md && col.md->bvhtree) + BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, particle_intersect_face, &col); + } + } + /* then use that object */ + if(hit.index>=0) { + t = hit.dist/col.ray_len; + VecLerpf(ground_co, col.co1, col.co2, t); + VECCOPY(ground_nor, col.nor); + Normalize(ground_nor); + return col.ob; + } + else { + /* default to z=0 */ + VECCOPY(ground_co, pa->state.co); + ground_co[2] = 0; + ground_nor[0] = ground_nor[1] = 0.0f; + ground_nor[2] = 1.0f; + return NULL; + } + } +} +static int boid_rule_applies(ParticleData *pa, BoidSettings *boids, BoidRule *rule) +{ + if(rule==NULL) + return 0; + + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing) && rule->flag & BOIDRULE_ON_LAND) + return 1; + + if(pa->boid->mode==eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR) + return 1; + + return 0; +} +void boids_precalc_rules(ParticleSettings *part, float cfra) +{ + BoidState *state = part->boids->states.first; + BoidRule *rule; + for(; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->type==eBoidRuleType_FollowLeader) { + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; + + if(flbr->ob && flbr->cfra != cfra) { + /* save object locations for velocity calculations */ + VECCOPY(flbr->oloc, flbr->loc); + VECCOPY(flbr->loc, flbr->ob->obmat[3]); + flbr->cfra = cfra; + } + } + } + } +} +static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor) +{ + float nor[3], vel[3]; + VECCOPY(nor, surface_nor); + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, surface_nor, -1.0); + Normalize(pa->r_ve); + + /* raise boid it's size from surface */ + VecMulf(nor, pa->size * boids->height); + VecAddf(pa->state.co, surface_co, nor); + + /* remove normal component from velocity */ + Projf(vel, pa->state.vel, surface_nor); + VecSubf(pa->state.vel, pa->state.vel, vel); +} +static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor) +{ + float vec[3]; + + VecSubf(vec, boid_co, goal_co); + + return Inpf(vec, goal_nor); +} +/* wanted_co is relative to boid location */ +static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness) +{ + if(rule==NULL) + return 0; + + if(boid_rule_applies(pa, bbd->part->boids, rule)==0) + return 0; + + if(boid_rules[rule->type](rule, bbd, val, pa)==0) + return 0; + + if(fuzziness < 0.0f || VecLenCompare(bbd->wanted_co, pa->prev_state.vel, fuzziness * VecLength(pa->prev_state.vel))==0) + return 1; + else + return 0; +} +static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa) { + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + if(state->id==pa->boid->state_id) + return state; + } + + /* for some reason particle isn't at a valid state */ + state = boids->states.first; + if(state) + pa->boid->state_id = state->id; + + return state; +} +//static int boid_condition_is_true(BoidCondition *cond) { +// /* TODO */ +// return 0; +//} + +/* determines the velocity the boid wants to have */ +void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) +{ + ParticleData *pars=bbd->psys->particles; + ParticleEffectorCache *ec=0; + BoidRule *rule; + BoidSettings *boids = bbd->part->boids; + BoidValues val; + BoidState *state = get_boid_state(boids, pa); + //BoidCondition *cond; + + if(pa->boid->health <= 0.0f) { + pa->alive = PARS_DYING; + 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 */ + // } + //} + + bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; + + /* create random seed for every particle & frame */ + BLI_srandom(bbd->psys->seed + p + (int)bbd->cfra + (int)(1000*pa->r_rot[0])); + + 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 (allways same for same particle though) */ + rule = BLI_findlink(&state->rules, (int)(1000.0f * pa->r_rot[1]) % BLI_countlist(&state->rules)); + + apply_boid_rule(bbd, rule, &val, pa, -1.0); + } + 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)) { + VecAddf(wanted_co, wanted_co, bbd->wanted_co); + wanted_speed += bbd->wanted_speed; + n++; + bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; + } + } + + if(n > 1) { + VecMulf(wanted_co, 1.0f/(float)n); + wanted_speed /= (float)n; + } + + VECCOPY(bbd->wanted_co, wanted_co); + bbd->wanted_speed = wanted_speed; + break; + } + + } + + /* decide on jumping & liftoff */ + if(pa->boid->mode == eBoidMode_OnLand) { + /* fuzziness makes boids capable of misjudgement */ + float mul = 1.0 + state->rule_fuzziness; + + if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) { + float cvel[3], dir[3]; + + VECCOPY(dir, pa->prev_state.ave); + Normalize2(dir); + + VECCOPY(cvel, bbd->wanted_co); + Normalize2(cvel); + + if(Inp2f(cvel, dir) > 0.95 / mul) + pa->boid->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; + + VECCOPY(dir, pa->prev_state.ave); + Normalize2(dir); + + VECCOPY(cvel, bbd->wanted_co); + Normalize2(cvel); + + len = Vec2Length(pa->prev_state.vel); + + /* first of all, are we going in a suitable direction? */ + /* or at a suitably slow speed */ + if(Inp2f(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) { + /* try to reach goal at highest point of the parabolic path */ + cur_v = Vec2Length(pa->prev_state.vel); + z_v = sasqrt(-2.0f * bbd->part->acc[2] * bbd->wanted_co[2]); + ground_v = Vec2Length(bbd->wanted_co)*sasqrt(-0.5f * bbd->part->acc[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); + + VECCOPY(jump_v, dir); + jump_v[2] = z_v; + VecMulf(jump_v, ground_v); + + Normalize(jump_v); + VecMulf(jump_v, len); + Vec2Addf(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) { + VECCOPY(pa->prev_state.vel, jump_v); + pa->boid->mode = eBoidMode_Falling; + } + } + } +} +/* tries to realize the wanted velocity taking all constraints into account */ +void boid_body(BoidBrainData *bbd, ParticleData *pa) +{ + BoidSettings *boids = bbd->part->boids; + BoidValues val; + 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}, tvel[3] = {0.0f, 0.0f, 1.0f}; + float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; + int p = pa - bbd->psys->particles; + + 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(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && bbd->part->acc[2] != 0.0f) + pa->boid->mode = eBoidMode_Falling; + + if(pa->boid->mode == eBoidMode_Falling) { + /* Falling boids are only effected by gravity. */ + acc[2] = bbd->part->acc[2]; + } + else { + /* figure out acceleration */ + float landing_level = 2.0f; + float level = landing_level + 1.0f; + float new_vel[3]; + + if(pa->boid->mode == eBoidMode_Liftoff) { + pa->boid->mode = eBoidMode_InAir; + pa->stick_ob = boid_find_ground(bbd, pa, ground_co, ground_nor); + } + else if(pa->boid->mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { + /* auto-leveling & landing if close to ground */ + + pa->stick_ob = 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.5; + + 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; + pa->boid->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; + } + } + } + + VECCOPY(old_dir, pa->prev_state.ave); + VECCOPY(wanted_dir, bbd->wanted_co); + new_speed = Normalize(wanted_dir); + + /* first check if we have valid direction we want to go towards */ + if(new_speed == 0.0f) { + VECCOPY(new_dir, old_dir); + } + else { + float old_dir2[2], wanted_dir2[2], nor[3], angle; + Vec2Copyf(old_dir2, old_dir); + Normalize2(old_dir2); + Vec2Copyf(wanted_dir2, wanted_dir); + Normalize2(wanted_dir2); + + /* choose random direction to turn if wanted velocity */ + /* is directly behind regardless of z-coordinate */ + if(Inp2f(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(wanted_dir); + } + + /* constrain direction with maximum angular velocity */ + angle = saacos(Inpf(old_dir, wanted_dir)); + angle = MIN2(angle, val.max_ave); + + Crossf(nor, old_dir, wanted_dir); + VecRotToQuat(nor, angle, q); + VECCOPY(new_dir, old_dir); + QuatMulVecf(q, new_dir); + Normalize(new_dir); + + /* save direction in case resulting velocity too small */ + VecRotToQuat(nor, angle*dtime, q); + VECCOPY(pa->state.ave, old_dir); + QuatMulVecf(q, pa->state.ave); + Normalize(pa->state.ave); + } + + /* constrain speed with maximum acceleration */ + old_speed = VecLength(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 */ + VECCOPY(new_vel, new_dir); + VecMulf(new_vel, new_speed); + + /* maintain minimum flying velocity if not landing */ + if(level >= landing_level) { + float len2 = Inp2f(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; + + Normalize2(new_vel); + Vec2Mulf(new_vel, sasqrt(len2)); + } + + /* finally constrain speed to max speed */ + new_speed = Normalize(new_vel); + VecMulf(new_vel, MIN2(new_speed, val.max_speed)); + + /* get acceleration from difference of velocities */ + VecSubf(acc, new_vel, pa->prev_state.vel); + + /* break acceleration to components */ + Projf(tan_acc, acc, pa->prev_state.ave); + VecSubf(nor_acc, acc, tan_acc); + } + + /* account for effectors */ + do_effectors(p, pa, &pa->state, bbd->scene, bbd->ob, bbd->psys, pa->state.co, force, tvel, bbd->dfra, bbd->cfra); + + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) { + float length = Normalize(force); + + length = MAX2(0.0f, length - boids->land_stick_force); + + VecMulf(force, length); + } + + VecAddf(acc, acc, force); + + /* store smoothed acceleration for nice banking etc. */ + VECADDFAC(pa->boid->acc, pa->boid->acc, acc, dtime); + VecMulf(pa->boid->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 */ + VecMulf(acc, 1.0f/pa_mass); + + VECCOPY(dvec, acc); + VecMulf(dvec, dtime*dtime*0.5f); + + VECCOPY(bvec, pa->prev_state.vel); + VecMulf(bvec, dtime); + VecAddf(dvec, dvec, bvec); + VecAddf(pa->state.co, pa->state.co, dvec); + + VECADDFAC(pa->state.vel, pa->state.vel, acc, dtime); + + if(pa->boid->mode != eBoidMode_InAir) + pa->stick_ob = boid_find_ground(bbd, pa, ground_co, ground_nor); + + /* change modes, constrain movement & keep track of down vector */ + switch(pa->boid->mode) { + case eBoidMode_InAir: + { + float grav[3] = {0.0f, 0.0f, bbd->part->acc[2] < 0.0f ? -1.0f : 0.0f}; + + /* don't take forward acceleration into account (better banking) */ + if(Inpf(pa->boid->acc, pa->state.vel) > 0.0f) { + Projf(dvec, pa->boid->acc, pa->state.vel); + VecSubf(dvec, pa->boid->acc, dvec); + } + else { + VECCOPY(dvec, pa->boid->acc); + } + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, grav, dvec, -boids->banking); + Normalize(pa->r_ve); + + /* 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) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = bbd->goal_ob; + boid_find_ground(bbd, pa, ground_co, ground_nor); + boid_climb(boids, pa, ground_co, ground_nor); + } + /* land boid when belowg ground */ + else if(boids->options & BOID_ALLOW_LAND && pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { + pa->state.co[2] = ground_co[2] + pa->size * boids->height; + pa->state.vel[2] = 0.0f; + pa->boid->mode = eBoidMode_OnLand; + } + break; + } + case eBoidMode_Falling: + { + float zvec[3] = {0.0f,0.0f,1.0f}; + float grav[3] = {0.0f, 0.0f, bbd->part->acc[2] < 0.0f ? -1.0f : 0.0f}; + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, grav, dtime); + Normalize(pa->r_ve); + + 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) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = 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.01 * pa->size * boids->height){ + pa->state.co[2] = ground_co[2] + pa->size * boids->height; + pa->state.vel[2] = 0.0f; + pa->boid->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) + pa->boid->mode = eBoidMode_InAir; + } + else + pa->boid->mode = eBoidMode_InAir; + break; + } + case eBoidMode_Climbing: + { + boid_climb(boids, pa, ground_co, ground_nor); + //float nor[3]; + //VECCOPY(nor, ground_nor); + + ///* gather apparent gravity to r_ve */ + //VECADDFAC(pa->r_ve, pa->r_ve, ground_nor, -1.0); + //Normalize(pa->r_ve); + + ///* raise boid it's size from surface */ + //VecMulf(nor, pa->size * boids->height); + //VecAddf(pa->state.co, ground_co, nor); + + ///* remove normal component from velocity */ + //Projf(v, pa->state.vel, ground_nor); + //VecSubf(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) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = 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.1 * pa->size * boids->height) + pa->boid->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. */ + VECCOPY(grav, ground_nor); + VecMulf(grav, -1.0f); + + Projf(dvec, pa->boid->acc, pa->state.vel); + VecSubf(dvec, pa->boid->acc, dvec); + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, grav, dvec, -boids->banking); + Normalize(pa->r_ve); + } + else { + /* gather negative surface normal to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, ground_nor, -1.0f); + Normalize(pa->r_ve); + } + break; + } + } + + /* save direction to state.ave unless the boid is falling */ + /* (boids can't effect their direction when falling) */ + if(pa->boid->mode!=eBoidMode_Falling && VecLength(pa->state.vel) > 0.1*pa->size) { + VECCOPY(pa->state.ave, pa->state.vel); + Normalize(pa->state.ave); + } + + /* apply damping */ + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) + VecMulf(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); + + /* calculate rotation matrix based on forward & down vectors */ + if(pa->boid->mode == eBoidMode_InAir) { + VECCOPY(mat[0], pa->state.ave); + + Projf(dvec, pa->r_ve, pa->state.ave); + VecSubf(mat[2], pa->r_ve, dvec); + Normalize(mat[2]); + } + else { + Projf(dvec, pa->state.ave, pa->r_ve); + VecSubf(mat[0], pa->state.ave, dvec); + Normalize(mat[0]); + + VECCOPY(mat[2], pa->r_ve); + } + VecMulf(mat[2], -1.0f); + Crossf(mat[1], mat[2], mat[0]); + + /* apply rotation */ + Mat3ToQuat_is_ok(mat, q); + QuatCopy(pa->state.rot, q); +} + +BoidRule *boid_new_rule(int type) +{ + BoidRule *rule = NULL; + if(type <= 0) + return NULL; + + switch(type) { + case eBoidRuleType_Goal: + case eBoidRuleType_Avoid: + rule = MEM_callocN(sizeof(BoidRuleGoalAvoid), "BoidRuleGoalAvoid"); + break; + case eBoidRuleType_AvoidCollision: + rule = MEM_callocN(sizeof(BoidRuleAvoidCollision), "BoidRuleAvoidCollision"); + ((BoidRuleAvoidCollision*)rule)->look_ahead = 2.0f; + break; + case eBoidRuleType_FollowLeader: + rule = MEM_callocN(sizeof(BoidRuleFollowLeader), "BoidRuleFollowLeader"); + ((BoidRuleFollowLeader*)rule)->distance = 1.0f; + break; + case eBoidRuleType_AverageSpeed: + rule = MEM_callocN(sizeof(BoidRuleAverageSpeed), "BoidRuleAverageSpeed"); + ((BoidRuleAverageSpeed*)rule)->speed = 0.5f; + break; + case eBoidRuleType_Fight: + rule = MEM_callocN(sizeof(BoidRuleFight), "BoidRuleFight"); + ((BoidRuleFight*)rule)->distance = 100.0f; + ((BoidRuleFight*)rule)->flee_distance = 100.0f; + break; + default: + rule = MEM_callocN(sizeof(BoidRule), "BoidRule"); + break; + } + + rule->type = type; + rule->flag |= BOIDRULE_IN_AIR|BOIDRULE_ON_LAND; + strcpy(rule->name, boidrule_type_items[type-1].name); + + return rule; +} +void boid_default_settings(BoidSettings *boids) +{ + boids->air_max_speed = 10.0f; + boids->air_max_acc = 0.5f; + boids->air_max_ave = 0.5f; + boids->air_personal_space = 1.0f; + + boids->land_max_speed = 5.0f; + boids->land_max_acc = 0.5f; + boids->land_max_ave = 0.5f; + boids->land_personal_space = 1.0f; + + boids->options = BOID_ALLOW_FLIGHT; + + boids->landing_smoothness = 3.0f; + boids->banking = 1.0f; + boids->height = 1.0f; + + boids->health = 1.0f; + boids->accuracy = 1.0f; + boids->aggression = 2.0f; + boids->range = 1.0f; + boids->strength = 0.1f; +} + +BoidState *boid_new_state(BoidSettings *boids) +{ + BoidState *state = MEM_callocN(sizeof(BoidState), "BoidState"); + + state->id = boids->last_state_id++; + if(state->id) + sprintf(state->name, "State %i", state->id); + else + strcpy(state->name, "State"); + + state->rule_fuzziness = 0.5; + state->volume = 1.0f; + state->channels |= ~0; + + return state; +} + +BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state) { + BoidState *staten = MEM_dupallocN(state); + + BLI_duplicatelist(&staten->rules, &state->rules); + BLI_duplicatelist(&staten->conditions, &state->conditions); + BLI_duplicatelist(&staten->actions, &state->actions); + + staten->id = boids->last_state_id++; + + return staten; +} +void boid_free_settings(BoidSettings *boids) +{ + if(boids) { + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + BLI_freelistN(&state->rules); + BLI_freelistN(&state->conditions); + BLI_freelistN(&state->actions); + } + + BLI_freelistN(&boids->states); + + MEM_freeN(boids); + } +} +BoidSettings *boid_copy_settings(BoidSettings *boids) +{ + BoidSettings *nboids = NULL; + + if(boids) { + BoidState *state; + BoidState *nstate; + + nboids = MEM_dupallocN(boids); + + BLI_duplicatelist(&nboids->states, &boids->states); + + state = boids->states.first; + nstate = nboids->states.first; + for(; state; state=state->next, nstate=nstate->next) { + BLI_duplicatelist(&nstate->rules, &state->rules); + BLI_duplicatelist(&nstate->conditions, &state->conditions); + BLI_duplicatelist(&nstate->actions, &state->actions); + } + } + + return nboids; +} +BoidState *boid_get_current_state(BoidSettings *boids) +{ + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT) + break; + } + + return state; +} \ No newline at end of file diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index f52eec34cc7..7e6a652da6b 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -39,6 +39,7 @@ #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_boid_types.h" #include "DNA_curve_types.h" #include "DNA_camera_types.h" #include "DNA_ID.h" @@ -555,6 +556,8 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O GroupObject *go; for(; psys; psys=psys->next) { + BoidRule *rule = NULL; + BoidState *state = NULL; ParticleSettings *part= psys->part; dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation"); @@ -562,16 +565,14 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O if(!psys_check_enabled(ob, psys)) continue; - if(part->phystype==PART_PHYS_KEYED) { - KeyedParticleTarget *kpt = psys->keyed_targets.first; + if(ELEM(part->phystype,PART_PHYS_KEYED,PART_PHYS_BOIDS)) { + ParticleTarget *pt = psys->targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->ob && BLI_findlink(&kpt->ob->particlesystem, kpt->psys-1)) { - node2 = dag_get_node(dag, kpt->ob); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Keyed Physics"); + for(; pt; pt=pt->next) { + if(pt->ob && BLI_findlink(&pt->ob->particlesystem, pt->psys-1)) { + node2 = dag_get_node(dag, pt->ob); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Targets"); } - else - break; } } @@ -589,33 +590,47 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O } } - if(psys->effectors.first) - psys_end_effectors(psys); + psys_end_effectors(psys); psys_init_effectors(scene, ob, psys->part->eff_group, psys); - if(psys->effectors.first) { - for(nec= psys->effectors.first; nec; nec= nec->next) { - Object *ob1= nec->ob; + for(nec= psys->effectors.first; nec; nec= nec->next) { + Object *ob1= nec->ob; - if(nec->type & PSYS_EC_EFFECTOR) { - node2 = dag_get_node(dag, ob1); - if(ob1->pd->forcefield==PFIELD_GUIDE) - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Field"); - else - dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Particle Field"); - } - else if(nec->type & PSYS_EC_DEFLECT) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Collision"); - } - else if(nec->type & PSYS_EC_PARTICLE) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Field"); - } - - if(nec->type & PSYS_EC_REACTOR) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Reactor"); + if(nec->type & PSYS_EC_EFFECTOR) { + node2 = dag_get_node(dag, ob1); + if(ob1->pd->forcefield==PFIELD_GUIDE) + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Field"); + else + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Particle Field"); + } + else if(nec->type & PSYS_EC_DEFLECT) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Collision"); + } + else if(nec->type & PSYS_EC_PARTICLE) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Field"); + } + + if(nec->type & PSYS_EC_REACTOR) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Reactor"); + } + } + + if(part->boids) { + for(state = part->boids->states.first; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + Object *ruleob = NULL; + if(rule->type==eBoidRuleType_Avoid) + ruleob = ((BoidRuleGoalAvoid*)rule)->ob; + else if(rule->type==eBoidRuleType_FollowLeader) + ruleob = ((BoidRuleFollowLeader*)rule)->ob; + + if(ruleob) { + node2 = dag_get_node(dag, ruleob); + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Boid Rule"); + } } } } @@ -2070,7 +2085,6 @@ static void dag_object_time_update_flags(Object *ob) } } } - /* flag all objects that need recalc, for changes in time for example */ void DAG_scene_update_flags(Scene *scene, unsigned int lay) { diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 7951fcf1d3e..e3c4f12184e 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -505,6 +505,9 @@ void do_physical_effector(Scene *scene, Object *ob, float *opco, short type, flo VecAddf(field,field,mag_vec); break; } + case PFIELD_BOID: + /* Boid field is handled completely in boids code. */ + break; } } diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 948d02f2a80..c129e8ed99b 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -6113,9 +6113,14 @@ static void surfaceModifier_freeData(ModifierData *md) MEM_freeN(surmd->bvhtree); } - if(surmd->dm) - surmd->dm->release(surmd->dm); + surmd->dm->release(surmd->dm); + + if(surmd->x) + MEM_freeN(surmd->x); + if(surmd->v) + MEM_freeN(surmd->v); + surmd->bvhtree = NULL; surmd->dm = NULL; } @@ -6128,7 +6133,7 @@ static int surfaceModifier_dependsOnTime(ModifierData *md) static void surfaceModifier_deformVerts( ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) + float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) { SurfaceModifierData *surmd = (SurfaceModifierData*) md; unsigned int numverts = 0, i = 0; @@ -6148,14 +6153,47 @@ static void surfaceModifier_deformVerts( if(surmd->dm) { + int init = 0; + float *vec; + MVert *x, *v; + CDDM_apply_vert_coords(surmd->dm, vertexCos); CDDM_calc_normals(surmd->dm); numverts = surmd->dm->getNumVerts ( surmd->dm ); - /* convert to global coordinates */ - for(i = 0; iobmat, CDDM_get_vert(surmd->dm, i)->co); + if(numverts != surmd->numverts || surmd->x == NULL || surmd->v == NULL || md->scene->r.cfra != surmd->cfra+1) { + if(surmd->x) { + MEM_freeN(surmd->x); + surmd->x = NULL; + } + if(surmd->v) { + MEM_freeN(surmd->v); + surmd->v = NULL; + } + + surmd->x = MEM_callocN(numverts * sizeof(MVert), "MVert"); + surmd->v = MEM_callocN(numverts * sizeof(MVert), "MVert"); + + surmd->numverts = numverts; + + init = 1; + } + + /* convert to global coordinates and calculate velocity */ + for(i = 0, x = surmd->x, v = surmd->v; idm, i)->co; + Mat4MulVecfl(ob->obmat, vec); + + if(init) + v->co[0] = v->co[1] = v->co[2] = 0.0f; + else + VecSubf(v->co, vec, x->co); + + VecCopyf(x->co, vec); + } + + surmd->cfra = md->scene->r.cfra; if(surmd->bvhtree) free_bvhtree_from_mesh(surmd->bvhtree); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 65043abe518..60b34bf0783 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -42,6 +42,7 @@ #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_boid_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -429,11 +430,13 @@ void unlink_object(Scene *scene, Object *ob) if(obt->particlesystem.first) { ParticleSystem *tpsys= obt->particlesystem.first; for(; tpsys; tpsys=tpsys->next) { - KeyedParticleTarget *kpt = tpsys->keyed_targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->ob==ob) { - BLI_remlink(&tpsys->keyed_targets, kpt); - MEM_freeN(kpt); + BoidState *state = NULL; + BoidRule *rule = NULL; + + ParticleTarget *pt = tpsys->targets.first; + for(; pt; pt=pt->next) { + if(pt->ob==ob) { + pt->ob = NULL; obt->recalc |= OB_RECALC_DATA; break; } @@ -458,6 +461,22 @@ void unlink_object(Scene *scene, Object *ob) } } } + if(tpsys->part->boids) { + for(state = tpsys->part->boids->states.first; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->type==eBoidRuleType_Avoid) { + BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*)rule; + if(gabr->ob==ob) + gabr->ob= NULL; + } + else if(rule->type==eBoidRuleType_FollowLeader) { + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*)rule; + if(flbr->ob==ob) + flbr->ob= NULL; + } + } + } + } } if(ob->pd) obt->recalc |= OB_RECALC_DATA; @@ -1063,8 +1082,14 @@ ParticleSystem *copy_particlesystem(ParticleSystem *psys) psysn->soft->particles = psysn; } - if(psys->keyed_targets.first) - BLI_duplicatelist(&psysn->keyed_targets, &psys->keyed_targets); + if(psys->particles->boid) { + psysn->particles->boid = MEM_dupallocN(psys->particles->boid); + for(a=1, pa=psysn->particles+1; atotpart; a++, pa++) + pa->boid = (pa-1)->boid + 1; + } + + if(psys->targets.first) + BLI_duplicatelist(&psysn->targets, &psys->targets); psysn->pathcache= NULL; psysn->childcache= NULL; @@ -2369,10 +2394,17 @@ void object_handle_update(Scene *scene, Object *ob) if(ob->particlesystem.first) { ParticleSystem *tpsys, *psys; DerivedMesh *dm; + ob->transflag &= ~OB_DUPLIPARTS; psys= ob->particlesystem.first; while(psys) { if(psys_check_enabled(ob, psys)) { + /* check use of dupli objects here */ + if(psys->part && psys->part->draw_as == PART_DRAW_REND && + ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) + || (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group))) + ob->transflag |= OB_DUPLIPARTS; + particle_system_update(scene, ob, psys); psys= psys->next; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index da14ac46550..6e8f9ee213c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" +#include "DNA_boid_types.h" #include "DNA_particle_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -58,6 +59,7 @@ #include "BKE_anim.h" +#include "BKE_boids.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_lattice.h" @@ -355,10 +357,13 @@ void psys_free_settings(ParticleSettings *part) MEM_freeN(part->pd); part->pd = NULL; } + if(part->pd2) { MEM_freeN(part->pd2); part->pd2 = NULL; } + + boid_free_settings(part->boids); } void free_hair(ParticleSystem *psys, int softbody) @@ -439,6 +444,9 @@ void psys_free(Object *ob, ParticleSystem * psys) psys->free_edit(psys); if(psys->particles){ + if(psys->particles->boid) + MEM_freeN(psys->particles->boid); + MEM_freeN(psys->particles); psys->particles = 0; psys->totpart = 0; @@ -479,8 +487,10 @@ void psys_free(Object *ob, ParticleSystem * psys) if(psys->pointcache) BKE_ptcache_free(psys->pointcache); - if(psys->keyed_targets.first) - BLI_freelistN(&psys->keyed_targets); + if(psys->targets.first) + BLI_freelistN(&psys->targets); + + BLI_kdtree_free(psys->tree); MEM_freeN(psys); } @@ -1043,21 +1053,21 @@ static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey-1].time - pind->kkey[0]->time); if(psys->part->phystype==PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) { - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; - kpt=kpt->next; + pt=pt->next; - while(kpt && pa->time + kpt->time < real_t) - kpt= kpt->next; + while(pt && pa->time + pt->time < real_t) + pt= pt->next; - if(kpt) { - kpt=kpt->prev; + if(pt) { + pt=pt->prev; - if(pa->time + kpt->time + kpt->duration > real_t) - real_t = pa->time + kpt->time; + if(pa->time + pt->time + pt->duration > real_t) + real_t = pa->time + pt->time; } else - real_t = pa->time + ((KeyedParticleTarget*)psys->keyed_targets.last)->time; + real_t = pa->time + ((ParticleTarget*)psys->targets.last)->time; } CLAMP(real_t, pa->time, pa->dietime); @@ -3028,7 +3038,12 @@ void object_add_particle_system(Scene *scene, Object *ob) psys->pointcache = BKE_ptcache_add(); BLI_addtail(&ob->particlesystem, psys); - psys->part = psys_new_settings("PSys", NULL); + psys->part = psys_new_settings("ParticleSettings", NULL); + + if(BLI_countlist(&ob->particlesystem)>1) + sprintf(psys->name, "ParticleSystem %i", BLI_countlist(&ob->particlesystem)); + else + strcpy(psys->name, "ParticleSystem"); md= modifier_new(eModifierType_ParticleSystem); sprintf(md->name, "ParticleSystem %i", BLI_countlist(&ob->particlesystem)); @@ -3099,14 +3114,8 @@ static void default_particle_settings(ParticleSettings *part) part->reactevent= PART_EVENT_DEATH; part->disp=100; part->from= PART_FROM_FACE; - part->nbetween= 4; - part->boidneighbours= 5; part->normfac= 1.0f; - part->max_vel = 10.0f; - part->average_vel = 0.3f; - part->max_tan_acc = 0.2f; - part->max_lat_acc = 1.0f; part->reactshape=1.0f; @@ -3136,13 +3145,9 @@ static void default_particle_settings(ParticleSettings *part) part->keyed_loops = 1; - part->banking=1.0; - part->max_bank=1.0; + for(i=0; i<10; i++) + part->effector_weight[i]=1.0f; - for(i=0; iboidrule[i]=(char)i; - part->boidfac[i]=0.5; - } #if 0 // XXX old animation system part->ipo = NULL; @@ -3176,6 +3181,8 @@ ParticleSettings *psys_copy_settings(ParticleSettings *part) partn= copy_libblock(part); if(partn->pd) partn->pd= MEM_dupallocN(part->pd); if(partn->pd2) partn->pd2= MEM_dupallocN(part->pd2); + + partn->boids = boid_copy_settings(part->boids); return partn; } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 56ca3e8e22b..b792564f50c 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_boid_types.h" #include "DNA_particle_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -49,6 +50,7 @@ #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "DNA_ipo_types.h" // XXX old animation system stuff... to be removed! +#include "DNA_listBase.h" #include "BLI_rand.h" #include "BLI_jitter.h" @@ -60,6 +62,7 @@ #include "BLI_threads.h" #include "BKE_anim.h" +#include "BKE_boids.h" #include "BKE_cdderivedmesh.h" #include "BKE_collision.h" #include "BKE_displist.h" @@ -172,6 +175,7 @@ void psys_reset(ParticleSystem *psys, int mode) static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) { ParticleData *newpars = 0, *pa; + BoidData *newboids = 0; int i, totpart, totsaved = 0; if(new_totpart<0) { @@ -185,22 +189,34 @@ static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) else totpart=new_totpart; - if(totpart) + if(totpart) { newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles"); + + if(psys->part->phystype == PART_PHYS_BOIDS) + newboids = MEM_callocN(totpart*sizeof(BoidData), "Boid Data"); + } if(psys->particles) { totsaved=MIN2(psys->totpart,totpart); /*save old pars*/ - if(totsaved) + if(totsaved) { memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData)); + if(newboids) + memcpy(newboids, psys->particles->boid, totsaved*sizeof(BoidData)); + } + if(psys->particles->keys) MEM_freeN(psys->particles->keys); - for(i=0, pa=newpars; iparticles->boid) + MEM_freeN(psys->particles->boid); + + for(i=0, pa=newpars; ikeys) { pa->keys= NULL; pa->totkey= 0; } + } for(i=totsaved, pa=psys->particles+totsaved; itotpart; i++, pa++) if(pa->hair) MEM_freeN(pa->hair); @@ -209,6 +225,13 @@ static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) } psys->particles=newpars; + if(newboids) { + pa = psys->particles; + pa->boid = newboids; + for(i=1, pa++; iboid = (pa-1)->boid + 1; + } + if(psys->child) { MEM_freeN(psys->child); psys->child=0; @@ -1607,12 +1630,15 @@ void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *ps rand= BLI_frand(); /* while loops are to have a spherical distribution (avoid cubic distribution) */ - length=2.0f; - while(length>1.0){ - pa->r_ve[0]=2.0f*(BLI_frand()-0.5f); - pa->r_ve[1]=2.0f*(BLI_frand()-0.5f); - pa->r_ve[2]=2.0f*(BLI_frand()-0.5f); - length=VecLength(pa->r_ve); + if(part->phystype != PART_PHYS_BOIDS) { + /* boids store gravity in r_ve, so skip here */ + length=2.0f; + while(length>1.0){ + pa->r_ve[0]=2.0f*(BLI_frand()-0.5f); + pa->r_ve[1]=2.0f*(BLI_frand()-0.5f); + pa->r_ve[2]=2.0f*(BLI_frand()-0.5f); + length=VecLength(pa->r_ve); + } } length=2.0f; @@ -1828,122 +1854,164 @@ void reset_particle(Scene *scene, ParticleData *pa, ParticleSystem *psys, Partic QuatMul(r_rot,r_rot,rot); } } - /* conversion done so now we apply new: */ - /* -velocity from: */ - /* *reactions */ - if(dtime>0.0f){ - VECSUB(vel,pa->state.vel,pa->prev_state.vel); - } + if(part->phystype==PART_PHYS_BOIDS) { + float dvec[3], q[4], mat[3][3]; + + VECCOPY(pa->state.co,loc); + + /* boids don't get any initial velocity */ + pa->state.vel[0]=pa->state.vel[1]=pa->state.vel[2]=0.0f; + + /* boids store direction in ave */ + if(fabs(nor[2])==1.0f) { + VecSubf(pa->state.ave, loc, ob->obmat[3]); + Normalize(pa->state.ave); + } + else { + VECCOPY(pa->state.ave, nor); + } + /* and gravity in r_ve */ + pa->r_ve[0] = pa->r_ve[1] = 0.0f; + pa->r_ve[2] = -1.0f; + if(part->acc[2]!=0.0f) + pa->r_ve[2] = part->acc[2]; + + /* calculate rotation matrix */ + Projf(dvec, pa->r_ve, pa->state.ave); + VecSubf(mat[0], pa->state.ave, dvec); + Normalize(mat[0]); + VECCOPY(mat[2], pa->r_ve); + VecMulf(mat[2], -1.0f); + Normalize(mat[2]); + Crossf(mat[1], mat[2], mat[0]); + + /* apply rotation */ + Mat3ToQuat_is_ok(mat, q); + QuatCopy(pa->state.rot, q); - /* *emitter velocity */ - if(dtime!=0.0 && part->obfac!=0.0){ - VECSUB(vel,loc,pa->state.co); - VecMulf(vel,part->obfac/dtime); + pa->boid->health = part->boids->health; + pa->boid->mode = eBoidMode_InAir; + pa->boid->state_id = ((BoidState*)part->boids->states.first)->id; + pa->boid->acc[0]=pa->boid->acc[1]=pa->boid->acc[2]=0.0f; } - - /* *emitter normal */ - if(part->normfac!=0.0) - VECADDFAC(vel,vel,nor,part->normfac); - - /* *emitter tangent */ - if(part->tanfac!=0.0) - VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_tan):1.0f)); + else { + /* conversion done so now we apply new: */ + /* -velocity from: */ - /* *texture */ - /* TODO */ + /* *reactions */ + if(dtime>0.0f){ + VECSUB(vel,pa->state.vel,pa->prev_state.vel); + } - /* *random */ - if(part->randfac!=0.0) - VECADDFAC(vel,vel,r_vel,part->randfac); + /* *emitter velocity */ + if(dtime!=0.0 && part->obfac!=0.0){ + VECSUB(vel,loc,pa->state.co); + VecMulf(vel,part->obfac/dtime); + } + + /* *emitter normal */ + if(part->normfac!=0.0) + VECADDFAC(vel,vel,nor,part->normfac); + + /* *emitter tangent */ + if(psmd && part->tanfac!=0.0) + VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_tan):1.0f)); - /* *particle */ - if(part->partfac!=0.0) - VECADDFAC(vel,vel,p_vel,part->partfac); + /* *texture */ + /* TODO */ -#if 0 // XXX old animation system - icu=find_ipocurve(psys->part->ipo,PART_EMIT_VEL); - if(icu){ - calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); - ptex.ivel*=icu->curval; - } -#endif // XXX old animation system + /* *random */ + if(part->randfac!=0.0) + VECADDFAC(vel,vel,r_vel,part->randfac); - VecMulf(vel,ptex.ivel); - - VECCOPY(pa->state.vel,vel); + /* *particle */ + if(part->partfac!=0.0) + VECADDFAC(vel,vel,p_vel,part->partfac); - /* -location from emitter */ - VECCOPY(pa->state.co,loc); + //icu=find_ipocurve(psys->part->ipo,PART_EMIT_VEL); + //if(icu){ + // calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); + // ptex.ivel*=icu->curval; + //} - /* -rotation */ - pa->state.rot[0]=1.0; - pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; + VecMulf(vel,ptex.ivel); - if(part->rotmode){ - /* create vector into which rotation is aligned */ - switch(part->rotmode){ - case PART_ROT_NOR: - VecCopyf(rot_vec, nor); - break; - case PART_ROT_VEL: - VecCopyf(rot_vec, vel); - break; - case PART_ROT_GLOB_X: - case PART_ROT_GLOB_Y: - case PART_ROT_GLOB_Z: - rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f; - break; - case PART_ROT_OB_X: - case PART_ROT_OB_Y: - case PART_ROT_OB_Z: - VecCopyf(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]); - break; - } + //if(ELEM(part->phystype, PART_PHYS_GRADU_EX, PART_PHYS_GRADU_SIM)) + // VecAddf(vel,vel,part->acc); - /* create rotation quat */ - VecNegf(rot_vec); - vectoquat(rot_vec, OB_POSX, OB_POSZ, q2); + VECCOPY(pa->state.vel,vel); - /* randomize rotation quat */ - if(part->randrotfac!=0.0f) - QuatInterpol(rot, q2, r_rot, part->randrotfac); - else - QuatCopy(rot,q2); - - /* rotation phase */ - phasefac = part->phasefac; - if(part->randphasefac != 0.0f) /* abuse r_ave[0] as a random number */ - phasefac += part->randphasefac * pa->r_ave[0]; - VecRotToQuat(x_vec, phasefac*(float)M_PI, q_phase); + /* -location from emitter */ + VECCOPY(pa->state.co,loc); - /* combine base rotation & phase */ - QuatMul(pa->state.rot, rot, q_phase); - } + /* -rotation */ + pa->state.rot[0]=1.0; + pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; + + if(part->rotmode){ + /* create vector into which rotation is aligned */ + switch(part->rotmode){ + case PART_ROT_NOR: + VecCopyf(rot_vec, nor); + break; + case PART_ROT_VEL: + VecCopyf(rot_vec, vel); + break; + case PART_ROT_GLOB_X: + case PART_ROT_GLOB_Y: + case PART_ROT_GLOB_Z: + rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f; + break; + case PART_ROT_OB_X: + case PART_ROT_OB_Y: + case PART_ROT_OB_Z: + VecCopyf(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]); + break; + } + + /* create rotation quat */ + VecNegf(rot_vec); + vectoquat(rot_vec, OB_POSX, OB_POSZ, q2); - /* -angular velocity */ + /* randomize rotation quat */ + if(part->randrotfac!=0.0f) + QuatInterpol(rot, q2, r_rot, part->randrotfac); + else + QuatCopy(rot,q2); - pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0; + /* rotation phase */ + phasefac = part->phasefac; + if(part->randphasefac != 0.0f) /* abuse r_ave[0] as a random number */ + phasefac += part->randphasefac * pa->r_ave[0]; + VecRotToQuat(x_vec, phasefac*(float)M_PI, q_phase); - if(part->avemode){ - switch(part->avemode){ - case PART_AVE_SPIN: - VECCOPY(pa->state.ave,vel); - break; - case PART_AVE_RAND: - VECCOPY(pa->state.ave,r_ave); - break; + /* combine base rotation & phase */ + QuatMul(pa->state.rot, rot, q_phase); } - Normalize(pa->state.ave); - VecMulf(pa->state.ave,part->avefac); -#if 0 // XXX old animation system - icu=find_ipocurve(psys->part->ipo,PART_EMIT_AVE); - if(icu){ - calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); - VecMulf(pa->state.ave,icu->curval); + /* -angular velocity */ + + pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0; + + if(part->avemode){ + switch(part->avemode){ + case PART_AVE_SPIN: + VECCOPY(pa->state.ave,vel); + break; + case PART_AVE_RAND: + VECCOPY(pa->state.ave,r_ave); + break; + } + Normalize(pa->state.ave); + VecMulf(pa->state.ave,part->avefac); + + //icu=find_ipocurve(psys->part->ipo,PART_EMIT_AVE); + //if(icu){ + // calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); + // VecMulf(pa->state.ave,icu->curval); + //} } -#endif // XXX old animation system } pa->dietime = pa->time + pa->lifetime; @@ -1971,48 +2039,46 @@ static void reset_all_particles(Scene *scene, Object *ob, ParticleSystem *psys, MEM_freeN(vg_vel); } /************************************************/ +/* Particle targets */ +/************************************************/ +ParticleSystem *psys_get_target_system(Object *ob, ParticleTarget *pt) +{ + ParticleSystem *psys = NULL; + + if(pt->ob == NULL || pt->ob == ob) + psys = BLI_findlink(&ob->particlesystem, pt->psys-1); + else + psys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1); + + if(psys) + pt->flag |= PTARGET_VALID; + else + pt->flag &= ~PTARGET_VALID; + + return psys; +} +/************************************************/ /* Keyed particles */ /************************************************/ /* Counts valid keyed targets */ void psys_count_keyed_targets(Object *ob, ParticleSystem *psys) { ParticleSystem *kpsys; - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; int psys_num = BLI_findindex(&ob->particlesystem, psys); int keys_valid = 1; psys->totkeyed = 0; - for(; kpt; kpt=kpt->next) { - kpsys = NULL; - if(kpt->ob==ob || kpt->ob==NULL) { - if(kpt->psys >= psys_num) - kpsys = BLI_findlink(&ob->particlesystem, kpt->psys-1); - - if(kpsys && kpsys->totpart) { - kpt->flag |= KEYED_TARGET_VALID; - psys->totkeyed += keys_valid; - if(psys->flag & PSYS_KEYED_TIMING && kpt->duration != 0.0f) - psys->totkeyed += 1; - } - else { - kpt->flag &= ~KEYED_TARGET_VALID; - keys_valid = 0; - } + for(; pt; pt=pt->next) { + kpsys = psys_get_target_system(ob, pt); + + if(kpsys && kpsys->totpart) { + psys->totkeyed += keys_valid; + if(psys->flag & PSYS_KEYED_TIMING && pt->duration != 0.0f) + psys->totkeyed += 1; } else { - if(kpt->ob) - kpsys = BLI_findlink(&kpt->ob->particlesystem, kpt->psys-1); - - if(kpsys && kpsys->totpart) { - kpt->flag |= KEYED_TARGET_VALID; - psys->totkeyed += keys_valid; - if(psys->flag & PSYS_KEYED_TIMING && kpt->duration != 0.0f) - psys->totkeyed += 1; - } - else { - kpt->flag &= ~KEYED_TARGET_VALID; - keys_valid = 0; - } + keys_valid = 0; } } @@ -2023,10 +2089,9 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) { Object *kob = ob; ParticleSystem *kpsys = psys; - KeyedParticleTarget *kpt; + ParticleTarget *pt; ParticleData *pa; int totpart = psys->totpart, i, k, totkeys = psys->totkeyed; - float prevtime, nexttime, keyedtime; /* no proper targets so let's clear and bail out */ if(psys->totkeyed==0) { @@ -2050,23 +2115,23 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) psys->flag &= ~PSYS_KEYED; - kpt = psys->keyed_targets.first; + pt = psys->targets.first; for(k=0; kob) - kpsys = BLI_findlink(&kpt->ob->particlesystem, kpt->psys - 1); + if(pt->ob) + kpsys = BLI_findlink(&pt->ob->particlesystem, pt->psys - 1); else - kpsys = BLI_findlink(&ob->particlesystem, kpt->psys - 1); + kpsys = BLI_findlink(&ob->particlesystem, pt->psys - 1); for(i=0,pa=psys->particles; ikeys + k)->time = -1.0; /* use current time */ - psys_get_particle_state(scene, kpt->ob, kpsys, i%kpsys->totpart, pa->keys + k, 1); + psys_get_particle_state(scene, pt->ob, kpsys, i%kpsys->totpart, pa->keys + k, 1); if(psys->flag & PSYS_KEYED_TIMING){ - (pa->keys+k)->time = pa->time + kpt->time; - if(kpt->duration != 0.0f && k+1 < totkeys) { + (pa->keys+k)->time = pa->time + pt->time; + if(pt->duration != 0.0f && k+1 < totkeys) { copy_particle_key(pa->keys+k+1, pa->keys+k, 1); - (pa->keys+k+1)->time = pa->time + kpt->time + kpt->duration; + (pa->keys+k+1)->time = pa->time + pt->time + pt->duration; } } else if(totkeys > 1) @@ -2075,10 +2140,10 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) pa->keys->time = pa->time; } - if(psys->flag & PSYS_KEYED_TIMING && kpt->duration!=0.0f) + if(psys->flag & PSYS_KEYED_TIMING && pt->duration!=0.0f) k++; - kpt = (kpt->next && kpt->next->flag & KEYED_TARGET_VALID) ? kpt = kpt->next : psys->keyed_targets.first; + pt = (pt->next && pt->next->flag & PTARGET_VALID) ? pt = pt->next : psys->targets.first; } psys->flag |= PSYS_KEYED; @@ -2297,6 +2362,7 @@ static void particle_cache_interpolate(int index, void *psys_ptr, float frs_sec, VecMulf(keys[2].vel, dfra / frs_sec); psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &pa->state, 1); + QuatInterpol(pa->state.rot, keys[1].rot,keys[2].rot, (cfra - cfra1) / dfra); VecMulf(pa->state.vel, frs_sec / dfra); @@ -2340,6 +2406,30 @@ static int get_particles_from_cache(Scene *scene, Object *ob, ParticleSystem *ps /************************************************/ /* Effectors */ /************************************************/ +static void update_particle_tree(ParticleSystem *psys) +{ + if(psys) { + ParticleData *pa = psys->particles; + int p, totpart = psys->totpart; + + if(!psys->tree || psys->tree_frame != psys->cfra) { + + BLI_kdtree_free(psys->tree); + + psys->tree = BLI_kdtree_new(totpart); + + for(p=0, pa=psys->particles; pflag & (PARS_NO_DISP+PARS_UNEXIST) || pa->alive != PARS_ALIVE) + continue; + + BLI_kdtree_insert(psys->tree, p, pa->state.co, NULL); + } + BLI_kdtree_balance(psys->tree); + + psys->tree_frame = psys->cfra; + } + } +} static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, short object, float *pa_co, float obmat[4][4], float force_val, float falloff, float *field) { TexResult result[4]; @@ -2541,31 +2631,29 @@ void psys_end_effectors(ParticleSystem *psys) /* NOTE: ec->ob is not valid in here anymore! - dg */ - ListBase *lb=&psys->effectors; - if(lb->first) { - ParticleEffectorCache *ec; - for(ec= lb->first; ec; ec= ec->next){ - if(ec->distances) - MEM_freeN(ec->distances); + ParticleEffectorCache *ec = psys->effectors.first; - if(ec->locations) - MEM_freeN(ec->locations); + for(; ec; ec= ec->next){ + if(ec->distances) + MEM_freeN(ec->distances); - if(ec->face_minmax) - MEM_freeN(ec->face_minmax); + if(ec->locations) + MEM_freeN(ec->locations); - if(ec->vert_cos) - MEM_freeN(ec->vert_cos); + if(ec->face_minmax) + MEM_freeN(ec->face_minmax); - if(ec->tree) - BLI_kdtree_free(ec->tree); - - if(ec->rng) - rng_free(ec->rng); - } + if(ec->vert_cos) + MEM_freeN(ec->vert_cos); - BLI_freelistN(lb); + if(ec->tree) + BLI_kdtree_free(ec->tree); + + if(ec->rng) + rng_free(ec->rng); } + + BLI_freelistN(&psys->effectors); } static void precalc_effectors(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra) @@ -2648,7 +2736,84 @@ static void precalc_effectors(Scene *scene, Object *ob, ParticleSystem *psys, Pa } } +int effector_find_co(Scene *scene, float *pco, SurfaceModifierData *sur, Object *ob, PartDeflect *pd, float *co, float *nor, float *vel, int *index) +{ + SurfaceModifierData *surmd = NULL; + int ret = 0; + + if(sur) + surmd = sur; + else if(pd && pd->flag&PFIELD_SURFACE) + { + surmd = (SurfaceModifierData *)modifiers_findByType ( ob, eModifierType_Surface ); + } + + if(surmd) { + /* closest point in the object surface is an effector */ + BVHTreeNearest nearest; + + nearest.index = -1; + nearest.dist = FLT_MAX; + + BLI_bvhtree_find_nearest(surmd->bvhtree->tree, pco, &nearest, surmd->bvhtree->nearest_callback, surmd->bvhtree); + + if(nearest.index != -1) { + VECCOPY(co, nearest.co); + + if(nor) { + VECCOPY(nor, nearest.no); + } + + if(vel) { + MFace *mface = CDDM_get_face(surmd->dm, nearest.index); + + VECCOPY(vel, surmd->v[mface->v1].co); + VecAddf(vel, vel, surmd->v[mface->v2].co); + VecAddf(vel, vel, surmd->v[mface->v3].co); + if(mface->v4) + VecAddf(vel, vel, surmd->v[mface->v4].co); + + VecMulf(vel, mface->v4 ? 0.25f : 0.333f); + } + + if(index) + *index = nearest.index; + + ret = 1; + } + else { + co[0] = co[1] = co[2] = 0.0f; + + if(nor) + nor[0] = nor[1] = nor[2] = 0.0f; + + if(vel) + vel[0] = vel[1] = vel[2] = 0.0f; + } + } + else { + /* use center of object for distance calculus */ + VECCOPY(co, ob->obmat[3]); + + if(nor) { + VECCOPY(nor, ob->obmat[2]); + } + + if(vel) { + Object obcopy = *ob; + + VECCOPY(vel, ob->obmat[3]); + + where_is_object_time(scene, ob, scene->r.cfra - 1.0); + + VecSubf(vel, vel, ob->obmat[3]); + *ob = obcopy; + } + } + + return ret; +} /* calculate forces that all effectors apply to a particle*/ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, Object *ob, ParticleSystem *psys, float *rootco, float *force_field, float *vel,float framestep, float cfra) { @@ -2658,12 +2823,11 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, ParticleData *epa; ParticleKey estate; PartDeflect *pd; - SurfaceModifierData *surmd = NULL; ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; - float distance, vec_to_part[3]; - float falloff, charge = 0.0f; - int p; + float distance, vec_to_part[3], pco[3], co[3]; + float falloff, charge = 0.0f, strength; + int p, face_index=-1; /* check all effector objects for interaction */ if(lb->first){ @@ -2687,45 +2851,33 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, where_is_object_time(scene, eob,cfra); if(pd && pd->flag&PFIELD_SURFACE) { - surmd = (SurfaceModifierData *)modifiers_findByType ( eob, eModifierType_Surface ); - } - if(surmd) { - /* closest point in the object surface is an effector */ - BVHTreeNearest nearest; float velocity[3]; - - nearest.index = -1; - nearest.dist = FLT_MAX; - /* using velocity corrected location allows for easier sliding over effector surface */ VecCopyf(velocity, state->vel); VecMulf(velocity, psys_get_timestep(psys->part)); - VecAddf(vec_to_part, state->co, velocity); - - BLI_bvhtree_find_nearest(surmd->bvhtree->tree, vec_to_part, &nearest, surmd->bvhtree->nearest_callback, surmd->bvhtree); - - if(nearest.index != -1) { - VecSubf(vec_to_part, state->co, nearest.co); - } - else - vec_to_part[0] = vec_to_part[1] = vec_to_part[2] = 0.0f; + VecAddf(pco, state->co, velocity); } else - /* use center of object for distance calculus */ - VecSubf(vec_to_part, state->co, eob->obmat[3]); + VECCOPY(pco, state->co); + + effector_find_co(scene, pco, NULL, eob, pd, co, NULL, NULL, &face_index); + + VecSubf(vec_to_part, state->co, co); distance = VecLength(vec_to_part); falloff=effector_falloff(pd,eob->obmat[2],vec_to_part); + strength = pd->f_strength * psys->part->effector_weight[0] * psys->part->effector_weight[pd->forcefield]; + if(falloff<=0.0f) ; /* don't do anything */ else if(pd->forcefield==PFIELD_TEXTURE) { do_texture_effector(pd->tex, pd->tex_mode, pd->flag&PFIELD_TEX_2D, pd->tex_nabla, pd->flag & PFIELD_TEX_OBJECT, (pd->flag & PFIELD_TEX_ROOTCO) ? rootco : state->co, eob->obmat, - pd->f_strength, falloff, force_field); + strength, falloff, force_field); } else { - do_physical_effector(scene, eob, state->co, pd->forcefield,pd->f_strength,distance, + do_physical_effector(scene, eob, state->co, pd->forcefield,strength,distance, falloff,0.0,pd->f_damp,eob->obmat[2],vec_to_part, state->vel,force_field,pd->flag&PFIELD_PLANAR,ec->rng,pd->f_noise,charge,pa->size); } @@ -2766,10 +2918,12 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, falloff=effector_falloff(pd,estate.vel,vec_to_part); + strength = pd->f_strength * psys->part->effector_weight[0] * psys->part->effector_weight[pd->forcefield]; + if(falloff<=0.0f) ; /* don't do anything */ else - do_physical_effector(scene, eob, state->co, pd->forcefield,pd->f_strength,distance, + do_physical_effector(scene, eob, state->co, pd->forcefield,strength,distance, falloff,epart->size,pd->f_damp,estate.vel,vec_to_part, state->vel,force_field,0, ec->rng, pd->f_noise,charge,pa->size); } @@ -3121,20 +3275,7 @@ int psys_intersect_dm(Scene *scene, Object *ob, DerivedMesh *dm, float *vert_cos return intersect; } -/* container for moving data between deflet_particle and particle_intersect_face */ -typedef struct ParticleCollision -{ - struct Object *ob, *ob_t; // collided and current objects - struct CollisionModifierData *md; // collision modifier for ob_t; - float nor[3]; // normal at collision point - float vel[3]; // velocity of collision point - float co1[3], co2[3]; // ray start and end points - float ray_len; // original length of co2-co1, needed for collision time evaluation - float t; // time of previous collision, needed for substracting face velocity -} -ParticleCollision; - -static void particle_intersect_face(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +void particle_intersect_face(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { ParticleCollision *col = (ParticleCollision *) userdata; MFace *face = col->md->mfaces + index; @@ -3209,20 +3350,27 @@ static void particle_intersect_face(void *userdata, int index, const BVHTreeRay /* 1. check for all possible deflectors for closest intersection on particle path */ /* 2. if deflection was found kill the particle or calculate new coordinates */ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float timestep, float dfra, float cfra){ - Object *ob = NULL; + Object *ob = NULL, *skip_ob = NULL; ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; ParticleKey reaction_state; ParticleCollision col; BVHTreeRayHit hit; float ray_dir[3], zerovec[3]={0.0,0.0,0.0}; - float radius = ((part->flag & PART_SIZE_DEFL)?pa->size:0.0f); + float radius = ((part->flag & PART_SIZE_DEFL)?pa->size:0.0f), boid_z; int deflections=0, max_deflections=10; VECCOPY(col.co1, pa->prev_state.co); VECCOPY(col.co2, pa->state.co); col.t = 0.0f; + /* override for boids */ + if(part->phystype == PART_PHYS_BOIDS) { + radius = pa->size; + boid_z = pa->state.co[2]; + skip_ob = pa->stick_ob; + } + /* 10 iterations to catch multiple deflections */ if(lb->first) while(deflections < max_deflections){ /* 1. */ @@ -3240,13 +3388,17 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa if(ec->type & PSYS_EC_DEFLECT){ ob= ec->ob; - if(part->type!=PART_HAIR) - where_is_object_time(scene, ob,cfra); + /* for boids: don't check with current ground object */ + if(ob==skip_ob) + continue; /* particles should not collide with emitter at birth */ if(ob==pob && pa->time < cfra && pa->time >= psys->cfra) continue; + if(part->type!=PART_HAIR) + where_is_object_time(scene,ob,cfra); + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( ec->ob, eModifierType_Collision ) ); col.ob_t = ob; @@ -3378,6 +3530,13 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa /* make sure we don't hit the current face again */ VECADDFAC(co, co, col.nor, (through ? -0.0001f : 0.0001f)); + if(part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) { + if(pa->boid->mode == eBoidMode_OnLand || co[2] <= boid_z) { + co[2] = boid_z; + vel[2] = 0.0f; + } + } + /* store state for reactors */ VECCOPY(reaction_state.co, co); VecLerpf(reaction_state.vel, pa->prev_state.vel, pa->state.vel, dt); @@ -3411,564 +3570,6 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa } } /************************************************/ -/* Boid physics */ -/************************************************/ -static int boid_see_mesh(ListBase *lb, Scene *scene, Object *pob, ParticleSystem *psys, float *vec1, float *vec2, float *loc, float *nor, float cfra) -{ - Object *ob, *min_ob; - DerivedMesh *dm; - MFace *mface; - MVert *mvert; - ParticleEffectorCache *ec; - ParticleSystemModifierData *psmd=psys_get_modifier(pob,psys); - float imat[4][4]; - float co1[3], co2[3], min_w[4], min_d; - int min_face=0, intersect=0; - - if(lb->first){ - intersect=0; - min_d=20000.0; - min_ob=NULL; - for(ec=lb->first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_DEFLECT){ - ob= ec->ob; - - if(psys->part->type!=PART_HAIR) - where_is_object_time(scene, ob,cfra); - - if(ob==pob) - dm=psmd->dm; - else - dm=0; - - VECCOPY(co1,vec1); - VECCOPY(co2,vec2); - - if(ec->vert_cos==0){ - /* convert particle coordinates to object coordinates */ - Mat4Invert(imat,ob->obmat); - - Mat4MulVecfl(imat,co1); - Mat4MulVecfl(imat,co2); - } - - if(psys_intersect_dm(scene,ob,dm,ec->vert_cos,co1,co2,&min_d,&min_face,min_w,ec->face_minmax,0,0,0)) - min_ob=ob; - } - } - if(min_ob){ - ob=min_ob; - - if(ob==pob){ - dm=psmd->dm; - } - else{ - psys_disable_all(ob); - - dm=mesh_get_derived_final(scene, ob, 0); - if(dm==0) - dm=mesh_get_derived_deform(scene, ob, 0); - - psys_enable_all(ob); - } - - mface=dm->getFaceDataArray(dm,CD_MFACE); - mface+=min_face; - mvert=dm->getVertDataArray(dm,CD_MVERT); - - /* get deflection point & normal */ - psys_interpolate_face(mvert,mface,0,0,min_w,loc,nor,0,0,0,0); - - VECADD(nor,nor,loc); - Mat4MulVecfl(ob->obmat,loc); - Mat4MulVecfl(ob->obmat,nor); - VECSUB(nor,nor,loc); - return 1; - } - } - return 0; -} -/* vector calculus functions in 2d vs. 3d */ -static void set_boid_vec_func(BoidVecFunc *bvf, int is_2d) -{ - if(is_2d){ - bvf->Addf = Vec2Addf; - bvf->Subf = Vec2Subf; - bvf->Mulf = Vec2Mulf; - bvf->Length = Vec2Length; - bvf->Normalize = Normalize2; - bvf->Inpf = Inp2f; - bvf->Copyf = Vec2Copyf; - } - else{ - bvf->Addf = VecAddf; - bvf->Subf = VecSubf; - bvf->Mulf = VecMulf; - bvf->Length = VecLength; - bvf->Normalize = Normalize; - bvf->Inpf = Inpf; - bvf->Copyf = VecCopyf; - } -} -/* boids have limited processing capability so once there's too much information (acceleration) no more is processed */ -static int add_boid_acc(BoidVecFunc *bvf, float lat_max, float tan_max, float *lat_accu, float *tan_accu, float *acc, float *dvec, float *vel) -{ - static float tangent[3]; - static float tan_length; - - if(vel){ - bvf->Copyf(tangent,vel); - tan_length=bvf->Normalize(tangent); - return 1; - } - else{ - float cur_tan, cur_lat; - float tan_acc[3], lat_acc[3]; - int ret=0; - - bvf->Copyf(tan_acc,tangent); - - if(tan_length>0.0){ - bvf->Mulf(tan_acc,Inpf(tangent,dvec)); - - bvf->Subf(lat_acc,dvec,tan_acc); - } - else{ - bvf->Copyf(tan_acc,dvec); - lat_acc[0]=lat_acc[1]=lat_acc[2]=0.0f; - *lat_accu=lat_max; - } - - cur_tan=bvf->Length(tan_acc); - cur_lat=bvf->Length(lat_acc); - - /* add tangential acceleration */ - if(*lat_accu+cur_lat<=lat_max){ - bvf->Addf(acc,acc,lat_acc); - *lat_accu+=cur_lat; - ret=1; - } - else{ - bvf->Mulf(lat_acc,(lat_max-*lat_accu)/cur_lat); - bvf->Addf(acc,acc,lat_acc); - *lat_accu=lat_max; - } - - /* add lateral acceleration */ - if(*tan_accu+cur_tan<=tan_max){ - bvf->Addf(acc,acc,tan_acc); - *tan_accu+=cur_tan; - ret=1; - } - else{ - bvf->Mulf(tan_acc,(tan_max-*tan_accu)/cur_tan); - bvf->Addf(acc,acc,tan_acc); - *tan_accu=tan_max; - } - - return ret; - } -} -/* determines the acceleration that the boid tries to acchieve */ -static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Scene *scene, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc) -{ - ParticleData *pars=psys->particles; - KDTreeNearest ptn[MAX_BOIDNEIGHBOURS+1]; - ParticleEffectorCache *ec=0; - float dvec[3]={0.0,0.0,0.0}, ob_co[3], ob_nor[3]; - float avoid[3]={0.0,0.0,0.0}, velocity[3]={0.0,0.0,0.0}, center[3]={0.0,0.0,0.0}; - float cubedist[MAX_BOIDNEIGHBOURS+1]; - int i, n, neighbours=0, near, not_finished=1; - - float cur_vel; - float lat_accu=0.0f, max_lat_acc=part->max_vel*part->max_lat_acc; - float tan_accu=0.0f, max_tan_acc=part->max_vel*part->max_tan_acc; - float avg_vel=part->average_vel*part->max_vel; - - acc[0]=acc[1]=acc[2]=0.0f; - /* the +1 neighbour is because boid itself is in the tree */ - neighbours=BLI_kdtree_find_n_nearest(tree,part->boidneighbours+1,pa->state.co,NULL,ptn); - - for(n=1; nsize),3.0); - cubedist[n]=1.0f/MAX2(cubedist[n],1.0f); - } - - /* initialize tangent */ - add_boid_acc(bvf,0.0,0.0,0,0,0,0,pa->state.vel); - - for(i=0; iboidrule[i]){ - case BOID_COLLIDE: - /* collision avoidance */ - bvf->Copyf(dvec,pa->prev_state.vel); - bvf->Mulf(dvec,5.0f); - bvf->Addf(dvec,dvec,pa->prev_state.co); - if(boid_see_mesh(&psys->effectors,scene, ob,psys,pa->prev_state.co,dvec,ob_co,ob_nor,cfra)){ - float probelen = bvf->Length(dvec); - float proj; - float oblen; - - Normalize(ob_nor); - proj = bvf->Inpf(ob_nor,pa->prev_state.vel); - - bvf->Subf(dvec,pa->prev_state.co,ob_co); - oblen=bvf->Length(dvec); - - bvf->Copyf(dvec,ob_nor); - bvf->Mulf(dvec,-proj); - bvf->Mulf(dvec,((probelen/oblen)-1.0f)*100.0f*part->boidfac[BOID_COLLIDE]); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_AVOID: - /* predator avoidance */ - if(psys->effectors.first){ - for(ec=psys->effectors.first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_EFFECTOR){ - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0){ - float distance; - VECSUB(dvec,eob->obmat[3],pa->prev_state.co); - - distance=Normalize(dvec); - - if(part->flag & PART_DIE_ON_COL && distance < pd->mindist){ - pa->alive = PARS_DYING; - pa->dietime=cfra; - i=BOID_TOT_RULES; - break; - } - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - else if(ec->type & PSYS_EC_PARTICLE){ - Object *eob = ec->ob; - ParticleSystem *epsys; - ParticleSettings *epart; - ParticleKey state; - PartDeflect *pd; - KDTreeNearest ptn2[MAX_BOIDNEIGHBOURS]; - int totepart, p, count; - float distance; - epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); - epart= epsys->part; - pd= epart->pd; - totepart= epsys->totpart; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0 && ec->tree){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); - for(p=0; pprev_state.co); - - distance = Normalize(dvec); - - if(part->flag & PART_DIE_ON_COL && distance < (epsys->particles+ptn2[p].index)->size){ - pa->alive = PARS_DYING; - pa->dietime=cfra; - i=BOID_TOT_RULES; - break; - } - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - } - } - } - } - break; - case BOID_CROWD: - /* crowd avoidance */ - near=0; - for(n=1; nsize){ - if(ptn[n].dist!=0.0f) { - bvf->Subf(dvec,pa->prev_state.co,pars[ptn[n].index].state.co); - bvf->Mulf(dvec,(2.0f*pa->size-ptn[n].dist)/ptn[n].dist); - bvf->Addf(avoid,avoid,dvec); - near++; - } - } - /* ptn[] is distance ordered so no need to check others */ - else break; - } - if(near){ - bvf->Mulf(avoid,part->boidfac[BOID_CROWD]*2.0f/timestep); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,avoid,0); - } - break; - case BOID_CENTER: - /* flock centering */ - if(neighbours>1){ - for(n=1; nAddf(center,center,pars[ptn[n].index].state.co); - } - bvf->Mulf(center,1.0f/((float)neighbours-1.0f)); - - bvf->Subf(dvec,center,pa->prev_state.co); - - bvf->Mulf(dvec,part->boidfac[BOID_CENTER]*2.0f); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_AV_VEL: - /* average velocity */ - cur_vel=bvf->Length(pa->prev_state.vel); - if(cur_vel>0.0){ - bvf->Copyf(dvec,pa->prev_state.vel); - bvf->Mulf(dvec,part->boidfac[BOID_AV_VEL]*(avg_vel-cur_vel)/cur_vel); - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_VEL_MATCH: - /* velocity matching */ - if(neighbours>1){ - for(n=1; nCopyf(dvec,pars[ptn[n].index].state.vel); - bvf->Mulf(dvec,cubedist[n]); - bvf->Addf(velocity,velocity,dvec); - } - bvf->Mulf(velocity,1.0f/((float)neighbours-1.0f)); - - bvf->Subf(dvec,velocity,pa->prev_state.vel); - - bvf->Mulf(dvec,part->boidfac[BOID_VEL_MATCH]); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_GOAL: - /* goal seeking */ - if(psys->effectors.first){ - for(ec=psys->effectors.first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_EFFECTOR){ - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - float temp[4]; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0){ - float distance; - VECSUB(dvec,eob->obmat[3],pa->prev_state.co); - - distance=Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - VecMulf(dvec,pd->f_strength*part->boidfac[BOID_GOAL]/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - else if(pd->forcefield==PFIELD_GUIDE){ - float distance; - - where_on_path(eob, (cfra-pa->time)/pa->lifetime, temp, dvec); - - VECSUB(dvec,temp,pa->prev_state.co); - - distance=Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - VecMulf(dvec,pd->f_strength*part->boidfac[BOID_GOAL]/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - else if(ec->type & PSYS_EC_PARTICLE){ - Object *eob = ec->ob; - ParticleSystem *epsys; - ParticleSettings *epart; - ParticleKey state; - PartDeflect *pd; - KDTreeNearest ptn2[MAX_BOIDNEIGHBOURS]; - int totepart, p, count; - float distance; - epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); - epart= epsys->part; - pd= epart->pd; - totepart= epsys->totpart; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0 && ec->tree){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); - for(p=0; pprev_state.co); - - distance = Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - } - } - } - } - break; - case BOID_LEVEL: - /* level flight */ - if((part->flag & PART_BOIDS_2D)==0){ - dvec[0]=dvec[1]=0.0; - dvec[2]=-pa->prev_state.vel[2]; - - VecMulf(dvec,part->boidfac[BOID_LEVEL]); - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - } - } -} -/* tries to realize the wanted acceleration */ -static void boid_body(Scene *scene, BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc) -{ - float dvec[3], bvec[3], length, max_vel=part->max_vel; - float q2[4], q[4]; - float g=9.81f, pa_mass=part->mass; - float yvec[3]={0.0,1.0,0.0}, zvec[3]={0.0,0.0,-1.0}, bank; - - /* apply new velocity, location & rotation */ - copy_particle_key(&pa->state,&pa->prev_state,0); - - if(part->flag & PART_SIZEMASS) - pa_mass*=pa->size; - - /* by regarding the acceleration as a force at this stage we*/ - /* can get better controll allthough it's a bit unphysical */ - bvf->Mulf(acc,1.0f/pa_mass); - - bvf->Copyf(dvec,acc); - bvf->Mulf(dvec,timestep*timestep*0.5f); - - bvf->Copyf(bvec,pa->state.vel); - bvf->Mulf(bvec,timestep); - bvf->Addf(dvec,dvec,bvec); - bvf->Addf(pa->state.co,pa->state.co,dvec); - - /* air speed from wind and vortex effectors */ - if(psys->effectors.first) { - ParticleEffectorCache *ec; - for(ec=psys->effectors.first; ec; ec=ec->next) { - if(ec->type & PSYS_EC_EFFECTOR) { - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - float direction[3], vec_to_part[3]; - float falloff; - - if(pd->f_strength != 0.0f) { - VecCopyf(direction, eob->obmat[2]); - VecSubf(vec_to_part, pa->state.co, eob->obmat[3]); - - falloff=effector_falloff(pd, direction, vec_to_part); - - switch(pd->forcefield) { - case PFIELD_WIND: - if(falloff <= 0.0f) - ; /* don't do anything */ - else { - Normalize(direction); - VecMulf(direction, pd->f_strength * falloff); - bvf->Addf(pa->state.co, pa->state.co, direction); - } - break; - case PFIELD_VORTEX: - { - float distance, mag_vec[3]; - Crossf(mag_vec, direction, vec_to_part); - Normalize(mag_vec); - - distance = VecLength(vec_to_part); - - VecMulf(mag_vec, pd->f_strength * distance * falloff); - bvf->Addf(pa->state.co, pa->state.co, mag_vec); - break; - } - } - } - } - } - } - - - if((part->flag & PART_BOIDS_2D)==0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0){ - Crossf(yvec,pa->state.vel,zvec); - - Normalize(yvec); - - bank=Inpf(yvec,acc); - - bank=-(float)atan((double)(bank/g)); - - bank*=part->banking; - - bank-=pa->bank; - if(bank>M_PI*part->max_bank){ - bank=pa->bank+(float)M_PI*part->max_bank; - } - else if(bank<-M_PI*part->max_bank){ - bank=pa->bank-(float)M_PI*part->max_bank; - } - else - bank+=pa->bank; - - pa->bank=bank; - } - else{ - bank=0.0; - } - - - VecRotToQuat(pa->state.vel,bank,q); - - VECCOPY(dvec,pa->state.vel); - VecNegf(dvec); - vectoquat(dvec, OB_POSX, OB_POSZ, q2); - - QuatMul(pa->state.rot,q,q2); - - bvf->Mulf(acc,timestep); - bvf->Addf(pa->state.vel,pa->state.vel,acc); - - if(part->flag & PART_BOIDS_2D){ - pa->state.vel[2]=0.0; - pa->state.co[2]=part->groundz; - } - - length=bvf->Length(pa->state.vel); - if(length > max_vel) - bvf->Mulf(pa->state.vel,max_vel/length); -} -/************************************************/ /* Hair */ /************************************************/ static void save_hair(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){ @@ -4025,9 +3626,9 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic ParticleData *pa; ParticleSettings *part=psys->part; KDTree *tree=0; - BoidVecFunc bvf; IpoCurve *icu_esize= NULL; //=find_ipocurve(part->ipo,PART_EMIT_SIZE); // XXX old animation system Material *ma=give_current_material(ob,part->omat); + BoidBrainData bbd; float timestep; int p, totpart; /* current time */ @@ -4107,16 +3708,23 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic precalc_effectors(scene, ob,psys,psmd,cfra); if(part->phystype==PART_PHYS_BOIDS){ - /* create particle tree for fast inter-particle comparisons */ - tree=BLI_kdtree_new(totpart); - for(p=0, pa=psys->particles; pflag & (PARS_NO_DISP+PARS_UNEXIST) || pa->alive!=PARS_ALIVE) - continue; - - BLI_kdtree_insert(tree, p, pa->state.co, NULL); + ParticleTarget *pt = psys->targets.first; + bbd.scene = scene; + bbd.ob = ob; + bbd.psys = psys; + bbd.part = part; + bbd.cfra = cfra; + bbd.dfra = dfra; + bbd.timestep = timestep; + + update_particle_tree(psys); + + boids_precalc_rules(part, cfra); + + for(; pt; pt=pt->next) { + if(pt->ob) + update_particle_tree(BLI_findlink(&pt->ob->particlesystem, pt->psys-1)); } - BLI_kdtree_balance(tree); - set_boid_vec_func(&bvf,part->flag&PART_BOIDS_2D); } /* main loop: calculate physics for all particles */ @@ -4185,10 +3793,14 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic break; case PART_PHYS_BOIDS: { - float acc[3]; - boid_brain(&bvf, pa, scene, ob, psys, part, tree, timestep,cfra,acc); - if(pa->alive != PARS_DYING) - boid_body(scene, &bvf,pa,psys,part,timestep,acc); + bbd.goal_ob = NULL; + boid_brain(&bbd, p, pa); + if(pa->alive != PARS_DYING) { + boid_body(&bbd, pa); + + /* deflection */ + deflect_particle(scene,ob,psmd,psys,part,pa,p,timestep,pa_dfra,cfra); + } break; } } @@ -4368,8 +3980,8 @@ static void cached_step(Scene *scene, Object *ob, ParticleSystemModifierData *ps dietime = birthtime + (1 + pa->loop) * (pa->dietime - pa->time); /* update alive status and push events */ - if(pa->time > cfra) { - pa->alive = PARS_UNBORN; + if(pa->time >= cfra) { + pa->alive = pa->time==cfra ? PARS_ALIVE : PARS_UNBORN; reset_particle(scene, pa, psys, psmd, ob, 0.0f, cfra, NULL, NULL, NULL); } else if(dietime <= cfra){ @@ -4454,9 +4066,33 @@ static void psys_changed_type(Object *ob, ParticleSystem *psys) psys_reset(psys, PSYS_RESET_ALL); } -void psys_changed_physics(Object *ob, ParticleSystem *psys) +void psys_check_boid_data(ParticleSystem *psys) +{ + ParticleData *pa = psys->particles; + int p = 1; + + if(!pa) + return; + + if(psys->part && psys->part->phystype==PART_PHYS_BOIDS) { + if(!pa->boid) { + pa->boid = MEM_callocN(psys->totpart * sizeof(BoidData), "Boid Data"); + + for(pa++; ptotpart; p++, pa++) + pa->boid = (pa-1)->boid + 1; + } + } + else if(pa->boid){ + MEM_freeN(pa->boid); + for(; ptotpart; p++, pa++) + pa->boid = NULL; + } +} +static void psys_changed_physics(Object *ob, ParticleSystem *psys) { - if(ELEM(psys->part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)) { + ParticleSettings *part = psys->part; + + if(ELEM(part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)) { PTCacheID pid; BKE_ptcache_id_from_particles(&pid, ob, psys); BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0); @@ -4465,6 +4101,24 @@ void psys_changed_physics(Object *ob, ParticleSystem *psys) free_keyed_keys(psys); psys->flag &= ~PSYS_KEYED; } + + if(part->phystype == PART_PHYS_BOIDS && part->boids == NULL) { + BoidState *state; + + psys_check_boid_data(psys); + + part->boids = MEM_callocN(sizeof(BoidSettings), "Boid Settings"); + boid_default_settings(part->boids); + + state = boid_new_state(part->boids); + BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Separate)); + BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Flock)); + + ((BoidRule*)state->rules.first)->flag |= BOIDRULE_CURRENT; + + state->flag |= BOIDSTATE_CURRENT; + BLI_addtail(&part->boids->states, state); + } } static void particles_fluid_step(Scene *scene, Object *ob, ParticleSystem *psys, int cfra) { diff --git a/source/blender/blenlib/BLI_kdtree.h b/source/blender/blenlib/BLI_kdtree.h index 49c21e424f7..585107b0c4a 100644 --- a/source/blender/blenlib/BLI_kdtree.h +++ b/source/blender/blenlib/BLI_kdtree.h @@ -52,9 +52,13 @@ void BLI_kdtree_balance(KDTree *tree); /* Find nearest returns index, and -1 if no node is found. * Find n nearest returns number of points found, with results in nearest. - * Normal is optional. */ +/* Normal is optional, but if given will limit results to points in normal direction from co. */ int BLI_kdtree_find_nearest(KDTree *tree, float *co, float *nor, KDTreeNearest *nearest); int BLI_kdtree_find_n_nearest(KDTree *tree, int n, float *co, float *nor, KDTreeNearest *nearest); +/* Range search returns number of points found, with results in nearest */ +/* Normal is optional, but if given will limit results to points in normal direction from co. */ +/* Remember to free nearest after use! */ +int BLI_kdtree_range_search(KDTree *tree, float range, float *co, float *nor, KDTreeNearest **nearest); #endif diff --git a/source/blender/blenlib/intern/BLI_kdtree.c b/source/blender/blenlib/intern/BLI_kdtree.c index 8e8b2e9f0e9..79a46da4c66 100644 --- a/source/blender/blenlib/intern/BLI_kdtree.c +++ b/source/blender/blenlib/intern/BLI_kdtree.c @@ -130,7 +130,7 @@ void BLI_kdtree_balance(KDTree *tree) tree->root= kdtree_balance(tree->nodes, tree->totnode, 0); } -static float squared_distance(float *v1, float *v2, float *n1, float *n2) +static float squared_distance(float *v2, float *v1, float *n1, float *n2) { float d[3], dist; @@ -140,7 +140,8 @@ static float squared_distance(float *v1, float *v2, float *n1, float *n2) dist= d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; - if(n1 && n2 && n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2] < 0.0f) + //if(n1 && n2 && n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2] < 0.0f) + if(n2 && d[0]*n2[0] + d[1]*n2[1] + d[2]*n2[2] < 0.0f) dist *= 10.0f; return dist; @@ -336,3 +337,111 @@ int BLI_kdtree_find_n_nearest(KDTree *tree, int n, float *co, float *nor, KDTree return found; } +int range_compare(const void * a, const void * b) +{ + const KDTreeNearest *kda = a; + const KDTreeNearest *kdb = b; + + if(kda->dist < kdb->dist) + return -1; + else if(kda->dist > kdb->dist) + return 1; + else + return 0; +} +static void add_in_range(KDTreeNearest **ptn, int found, int *totfoundstack, int index, float dist, float *co) +{ + KDTreeNearest *to; + + if(found+1 > *totfoundstack) { + KDTreeNearest *temp=MEM_callocN((*totfoundstack+50)*sizeof(KDTreeNode), "psys_treefoundstack"); + memcpy(temp, *ptn, *totfoundstack * sizeof(KDTreeNearest)); + if(*ptn) + MEM_freeN(*ptn); + *ptn = temp; + *totfoundstack+=50; + } + + to = (*ptn) + found; + + to->index = index; + to->dist = sqrt(dist); + VecCopyf(to->co, co); +} +int BLI_kdtree_range_search(KDTree *tree, float range, float *co, float *nor, KDTreeNearest **nearest) +{ + KDTreeNode *root, *node=0; + KDTreeNode **stack, *defaultstack[100]; + KDTreeNearest *foundstack=NULL; + float range2 = range*range, dist2; + int i, totstack, cur=0, found=0, totfoundstack=0; + + if(!tree || !tree->root) + return 0; + + stack= defaultstack; + totstack= 100; + + root= tree->root; + + if(co[root->d] + range < root->co[root->d]) { + if(root->left) + stack[cur++]=root->left; + } + else if(co[root->d] - range > root->co[root->d]) { + if(root->right) + stack[cur++]=root->right; + } + else { + dist2 = squared_distance(root->co, co, root->nor, nor); + if(dist2 <= range2) + add_in_range(&foundstack, found++, &totfoundstack, root->index, dist2, root->co); + + if(root->left) + stack[cur++]=root->left; + if(root->right) + stack[cur++]=root->right; + } + + while(cur--) { + node=stack[cur]; + + if(co[node->d] + range < node->co[node->d]) { + if(node->left) + stack[cur++]=node->left; + } + else if(co[node->d] - range > node->co[node->d]) { + if(node->right) + stack[cur++]=node->right; + } + else { + dist2 = squared_distance(node->co, co, node->nor, nor); + if(dist2 <= range2) + add_in_range(&foundstack, found++, &totfoundstack, node->index, dist2, node->co); + + if(node->left) + stack[cur++]=node->left; + if(node->right) + stack[cur++]=node->right; + } + + if(cur+3 > totstack){ + KDTreeNode **temp=MEM_callocN((totstack+100)*sizeof(KDTreeNode*), "psys_treestack"); + memcpy(temp,stack,totstack*sizeof(KDTreeNode*)); + if(stack != defaultstack) + MEM_freeN(stack); + stack=temp; + totstack+=100; + } + } + + if(stack != defaultstack) + MEM_freeN(stack); + + if(found) + qsort(foundstack, found, sizeof(KDTreeNearest), range_compare); + + *nearest = foundstack; + + return found; +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index f258746dafe..c43c720bad0 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -53,6 +53,7 @@ #include "DNA_armature_types.h" #include "DNA_ID.h" #include "DNA_actuator_types.h" +#include "DNA_boid_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" @@ -2990,6 +2991,29 @@ static void lib_link_particlesettings(FileData *fd, Main *main) part->dup_group = newlibadr(fd, part->id.lib, part->dup_group); part->eff_group = newlibadr(fd, part->id.lib, part->eff_group); part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob); + if(part->boids) { + BoidState *state = part->boids->states.first; + BoidRule *rule; + for(; state; state=state->next) { + rule = state->rules.first; + for(; rule; rule=rule->next) + switch(rule->type) { + case eBoidRuleType_Goal: + case eBoidRuleType_Avoid: + { + BoidRuleGoalAvoid *brga = (BoidRuleGoalAvoid*)rule; + brga->ob = newlibadr(fd, part->id.lib, brga->ob); + break; + } + case eBoidRuleType_FollowLeader: + { + BoidRuleFollowLeader *brfl = (BoidRuleFollowLeader*)rule; + brfl->ob = newlibadr(fd, part->id.lib, brfl->ob); + break; + } + } + } + } part->id.flag -= LIB_NEEDLINK; } part= part->id.next; @@ -3001,6 +3025,19 @@ static void direct_link_particlesettings(FileData *fd, ParticleSettings *part) part->adt= newdataadr(fd, part->adt); part->pd= newdataadr(fd, part->pd); part->pd2= newdataadr(fd, part->pd2); + + part->boids= newdataadr(fd, part->boids); + + if(part->boids) { + BoidState *state; + link_list(fd, &part->boids->states); + + for(state=part->boids->states.first; state; state=state->next) { + link_list(fd, &state->rules); + link_list(fd, &state->conditions); + link_list(fd, &state->actions); + } + } } static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase *particles) @@ -3015,10 +3052,10 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase psys->part = newlibadr_us(fd, id->lib, psys->part); if(psys->part) { - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; - for(; kpt; kpt=kpt->next) - kpt->ob=newlibadr(fd, id->lib, kpt->ob); + for(; pt; pt=pt->next) + pt->ob=newlibadr(fd, id->lib, pt->ob); psys->target_ob = newlibadr(fd, id->lib, psys->target_ob); @@ -3042,24 +3079,38 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase static void direct_link_particlesystems(FileData *fd, ListBase *particles) { ParticleSystem *psys; + ParticleData *pa; int a; for(psys=particles->first; psys; psys=psys->next) { psys->particles=newdataadr(fd,psys->particles); + if(psys->particles && psys->particles->hair){ - ParticleData *pa = psys->particles; - for(a=0; atotpart; a++, pa++) + for(a=0,pa=psys->particles; atotpart; a++, pa++) pa->hair=newdataadr(fd,pa->hair); } + if(psys->particles && psys->particles->keys){ - ParticleData *pa = psys->particles; - for(a=0; atotpart; a++, pa++) { + for(a=0,pa=psys->particles; atotpart; a++, pa++) { pa->keys= NULL; pa->totkey= 0; } psys->flag &= ~PSYS_KEYED; } + + if(psys->particles->boid) { + pa = psys->particles; + pa->boid = newdataadr(fd, pa->boid); + for(a=1,pa++; atotpart; a++, pa++) + pa->boid = (pa-1)->boid + 1; + } + else { + for(a=0,pa=psys->particles; atotpart; a++, pa++) + pa->boid = NULL; + } + + psys->child=newdataadr(fd,psys->child); psys->effectors.first=psys->effectors.last=0; @@ -3076,7 +3127,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) direct_link_pointcache(fd, sb->pointcache); } - link_list(fd, &psys->keyed_targets); + link_list(fd, &psys->targets); psys->edit = 0; psys->free_edit = NULL; @@ -3089,6 +3140,8 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) psys->pointcache= newdataadr(fd, psys->pointcache); if(psys->pointcache) direct_link_pointcache(fd, psys->pointcache); + + psys->tree = NULL; } return; } @@ -3649,6 +3702,9 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) surmd->dm = NULL; surmd->bvhtree = NULL; + surmd->x = NULL; + surmd->v = NULL; + surmd->numverts = 0; } else if (md->type==eModifierType_Hook) { HookModifierData *hmd = (HookModifierData*) md; @@ -8541,7 +8597,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) psys = MEM_callocN(sizeof(ParticleSystem), "particle_system"); psys->pointcache = BKE_ptcache_add(); - part = psys->part = psys_new_settings("PSys", main); + part = psys->part = psys_new_settings("ParticleSettings", main); /* needed for proper libdata lookup */ oldnewmap_insert(fd->libmap, psys->part, psys->part, 0); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 958e8bb874b..9e5fbfc2598 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -93,6 +93,7 @@ Any case: direct data is ALWAYS after the lib block #include "DNA_armature_types.h" #include "DNA_action_types.h" #include "DNA_actuator_types.h" +#include "DNA_boid_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" @@ -550,6 +551,39 @@ static void write_userdef(WriteData *wd) } } +static void write_boid_state(WriteData *wd, BoidState *state) +{ + BoidRule *rule = state->rules.first; + //BoidCondition *cond = state->conditions.first; + + writestruct(wd, DATA, "BoidState", 1, state); + + for(; rule; rule=rule->next) { + switch(rule->type) { + case eBoidRuleType_Goal: + case eBoidRuleType_Avoid: + writestruct(wd, DATA, "BoidRuleGoalAvoid", 1, rule); + break; + case eBoidRuleType_AvoidCollision: + writestruct(wd, DATA, "BoidRuleAvoidCollision", 1, rule); + break; + case eBoidRuleType_FollowLeader: + writestruct(wd, DATA, "BoidRuleFollowLeader", 1, rule); + break; + case eBoidRuleType_AverageSpeed: + writestruct(wd, DATA, "BoidRuleAverageSpeed", 1, rule); + break; + case eBoidRuleType_Fight: + writestruct(wd, DATA, "BoidRuleFight", 1, rule); + break; + default: + writestruct(wd, DATA, "BoidRule", 1, rule); + break; + } + } + //for(; cond; cond=cond->next) + // writestruct(wd, DATA, "BoidCondition", 1, cond); +} /* TODO: replace *cache with *cachelist once it's coded */ #define PTCACHE_WRITE_PSYS 0 #define PTCACHE_WRITE_CLOTH 1 @@ -582,6 +616,15 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase) if (part->adt) write_animdata(wd, part->adt); writestruct(wd, DATA, "PartDeflect", 1, part->pd); writestruct(wd, DATA, "PartDeflect", 1, part->pd2); + + if(part->boids && part->phystype == PART_PHYS_BOIDS) { + BoidState *state = part->boids->states.first; + + writestruct(wd, DATA, "BoidSettings", 1, part->boids); + + for(; state; state=state->next) + write_boid_state(wd, state); + } } part= part->id.next; } @@ -589,7 +632,7 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase) static void write_particlesystems(WriteData *wd, ListBase *particles) { ParticleSystem *psys= particles->first; - KeyedParticleTarget *kpt; + ParticleTarget *pt; int a; for(; psys; psys=psys->next) { @@ -604,10 +647,13 @@ static void write_particlesystems(WriteData *wd, ListBase *particles) for(a=0; atotpart; a++, pa++) writestruct(wd, DATA, "HairKey", pa->totkey, pa->hair); } + + if(psys->particles->boid && psys->part->phystype == PART_PHYS_BOIDS) + writestruct(wd, DATA, "BoidData", psys->totpart, psys->particles->boid); } - kpt = psys->keyed_targets.first; - for(; kpt; kpt=kpt->next) - writestruct(wd, DATA, "KeyedParticleTarget", 1, kpt); + pt = psys->targets.first; + for(; pt; pt=pt->next) + writestruct(wd, DATA, "ParticleTarget", 1, pt); if(psys->child) writestruct(wd, DATA, "ChildParticle", psys->totchild ,psys->child); writestruct(wd, DATA, "SoftBody", 1, psys->soft); diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h index b04bfb992dc..6ab804230d0 100644 --- a/source/blender/editors/include/ED_physics.h +++ b/source/blender/editors/include/ED_physics.h @@ -32,6 +32,7 @@ /* operators */ +void ED_operatortypes_boids(void); void ED_operatortypes_pointcache(void); void ED_operatortypes_fluid(void); //void ED_keymap_pointcache(struct wmWindowManager *wm); diff --git a/source/blender/editors/physics/physics_boids.c b/source/blender/editors/physics/physics_boids.c new file mode 100644 index 00000000000..2d3b11080e7 --- /dev/null +++ b/source/blender/editors/physics/physics_boids.c @@ -0,0 +1,433 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Janne Karhu. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +//#include +//#include +// +#include "MEM_guardedalloc.h" + +#include "DNA_boid_types.h" +#include "DNA_particle_types.h" +//#include "DNA_curve_types.h" +#include "DNA_object_types.h" +//#include "DNA_material_types.h" +//#include "DNA_texture_types.h" +#include "DNA_scene_types.h" +//#include "DNA_world_types.h" + +#include "BKE_boids.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +//#include "BKE_font.h" +//#include "BKE_library.h" +//#include "BKE_main.h" +//#include "BKE_material.h" +#include "BKE_particle.h" +//#include "BKE_texture.h" +//#include "BKE_utildefines.h" +//#include "BKE_world.h" + +//#include "BLI_editVert.h" +#include "BLI_listbase.h" +// +#include "RNA_access.h" +#include "RNA_enum_types.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +//#include "ED_curve.h" +//#include "ED_mesh.h" +// +//#include "buttons_intern.h" // own include + +/************************ add/del boid rule operators *********************/ +static int boidrule_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob= ptr.id.data; + ParticleSettings *part; + int type= RNA_enum_get(op->ptr, "type"); + + BoidRule *rule; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + part = psys->part; + + state = boid_get_current_state(part->boids); + + + for(rule=state->rules.first; rule; rule=rule->next) + rule->flag &= ~BOIDRULE_CURRENT; + + rule = boid_new_rule(type); + rule->flag |= BOIDRULE_CURRENT; + + BLI_addtail(&state->rules, rule); + + psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET); + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidrule_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Boid Rule"; + ot->description = "Add a boid rule to the current boid state."; + ot->idname= "BOID_OT_boidrule_add"; + + /* api callbacks */ + ot->invoke= WM_menu_invoke; + ot->exec= boidrule_add_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", boidrule_type_items, 0, "Type", ""); +} +static int boidrule_del_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + BoidRule *rule; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + state = boid_get_current_state(psys->part->boids); + + + for(rule=state->rules.first; rule; rule=rule->next) { + if(rule->flag & BOIDRULE_CURRENT) { + BLI_remlink(&state->rules, rule); + MEM_freeN(rule); + break; + } + + } + rule = state->rules.first; + + if(rule) + rule->flag |= BOIDRULE_CURRENT; + + DAG_scene_sort(scene); + psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidrule_del(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Boid Rule"; + ot->idname= "BOID_OT_boidrule_del"; + + /* api callbacks */ + ot->exec= boidrule_del_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/************************ move up/down boid rule operators *********************/ +static int boidrule_move_up_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + BoidRule *rule; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + state = boid_get_current_state(psys->part->boids); + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->flag & BOIDRULE_CURRENT && rule->prev) { + BLI_remlink(&state->rules, rule); + BLI_insertlink(&state->rules, rule->prev->prev, rule); + + psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET); + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + break; + } + } + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidrule_move_up(wmOperatorType *ot) +{ + ot->name= "Move Up Boid Rule"; + ot->description= "Move boid rule up in the list."; + ot->idname= "BOID_OT_boidrule_move_up"; + + ot->exec= boidrule_move_up_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int boidrule_move_down_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + BoidRule *rule; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + state = boid_get_current_state(psys->part->boids); + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->flag & BOIDRULE_CURRENT && rule->next) { + BLI_remlink(&state->rules, rule); + BLI_insertlink(&state->rules, rule->next, rule); + + psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET); + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + break; + } + } + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidrule_move_down(wmOperatorType *ot) +{ + ot->name= "Move Down Boid Rule"; + ot->description= "Move boid rule down in the list."; + ot->idname= "BOID_OT_boidrule_move_down"; + + ot->exec= boidrule_move_down_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + + +/************************ add/del boid state operators *********************/ +static int boidstate_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob= ptr.id.data; + ParticleSettings *part; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + part = psys->part; + + for(state=part->boids->states.first; state; state=state->next) + state->flag &= ~BOIDSTATE_CURRENT; + + state = boid_new_state(part->boids); + state->flag |= BOIDSTATE_CURRENT; + + BLI_addtail(&part->boids->states, state); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidstate_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Boid State"; + ot->description = "Add a boid state to the particle system."; + ot->idname= "BOID_OT_boidstate_add"; + + /* api callbacks */ + ot->exec= boidstate_add_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} +static int boidstate_del_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + ParticleSettings *part; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + part = psys->part; + + for(state=part->boids->states.first; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT) { + BLI_remlink(&part->boids->states, state); + MEM_freeN(state); + break; + } + + } + + /* there must be at least one state */ + if(!part->boids->states.first) { + state = boid_new_state(part->boids); + BLI_addtail(&part->boids->states, state); + } + else + state = part->boids->states.first; + + state->flag |= BOIDSTATE_CURRENT; + + DAG_scene_sort(scene); + psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidstate_del(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Boid State"; + ot->idname= "BOID_OT_boidstate_del"; + + /* api callbacks */ + ot->exec= boidstate_del_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/************************ move up/down boid state operators *********************/ +static int boidstate_move_up_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + BoidSettings *boids; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + boids = psys->part->boids; + + for(state = boids->states.first; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT && state->prev) { + BLI_remlink(&boids->states, state); + BLI_insertlink(&boids->states, state->prev->prev, state); + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + break; + } + } + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidstate_move_up(wmOperatorType *ot) +{ + ot->name= "Move Up Boid State"; + ot->description= "Move boid state up in the list."; + ot->idname= "BOID_OT_boidstate_move_up"; + + ot->exec= boidstate_move_up_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int boidstate_move_down_exec(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); + ParticleSystem *psys= ptr.data; + Object *ob = ptr.id.data; + BoidSettings *boids; + BoidState *state; + + if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS) + return OPERATOR_CANCELLED; + + boids = psys->part->boids; + + for(state = boids->states.first; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT && state->next) { + BLI_remlink(&boids->states, state); + BLI_insertlink(&boids->states, state->next, state); + psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET); + break; + } + } + + return OPERATOR_FINISHED; +} + +void BOID_OT_boidstate_move_down(wmOperatorType *ot) +{ + ot->name= "Move Down Boid State"; + ot->description= "Move boid state down in the list."; + ot->idname= "BOID_OT_boidstate_move_down"; + + ot->exec= boidstate_move_down_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/*******************************************************************************/ +void ED_operatortypes_boids(void) +{ + WM_operatortype_append(BOID_OT_boidrule_add); + WM_operatortype_append(BOID_OT_boidrule_del); + WM_operatortype_append(BOID_OT_boidrule_move_up); + WM_operatortype_append(BOID_OT_boidrule_move_down); + + WM_operatortype_append(BOID_OT_boidstate_add); + WM_operatortype_append(BOID_OT_boidstate_del); + WM_operatortype_append(BOID_OT_boidstate_move_up); + WM_operatortype_append(BOID_OT_boidstate_move_down); +} \ No newline at end of file diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 5a55c5fb717..b427742077a 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -93,6 +93,7 @@ void ED_spacetypes_init(void) ED_operatortypes_marker(); ED_operatortypes_pointcache(); ED_operatortypes_fluid(); + ED_operatortypes_boids(); ui_view2d_operatortypes(); diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 20db9fce8f2..adae52c1ce7 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -78,10 +78,10 @@ void OBJECT_OT_particle_system_add(struct wmOperatorType *ot); void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot); void PARTICLE_OT_new(struct wmOperatorType *ot); -void PARTICLE_OT_new_keyed_target(struct wmOperatorType *ot); -void PARTICLE_OT_remove_keyed_target(struct wmOperatorType *ot); -void PARTICLE_OT_keyed_target_move_up(struct wmOperatorType *ot); -void PARTICLE_OT_keyed_target_move_down(struct wmOperatorType *ot); +void PARTICLE_OT_new_target(struct wmOperatorType *ot); +void PARTICLE_OT_remove_target(struct wmOperatorType *ot); +void PARTICLE_OT_target_move_up(struct wmOperatorType *ot); +void PARTICLE_OT_target_move_down(struct wmOperatorType *ot); void SCENE_OT_render_layer_add(struct wmOperatorType *ot); void SCENE_OT_render_layer_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index 7dececd2679..8a9d2e9149b 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -30,6 +30,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_boid_types.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" #include "DNA_object_types.h" @@ -57,6 +58,7 @@ #include "BLI_listbase.h" #include "RNA_access.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -623,7 +625,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *op) if(psys->part) part= psys_copy_settings(psys->part); else - part= psys_new_settings("PSys", bmain); + part= psys_new_settings("ParticleSettings", bmain); ob= ptr.id.data; @@ -632,6 +634,8 @@ static int new_particle_settings_exec(bContext *C, wmOperator *op) psys->part = part; + psys_check_boid_data(psys); + DAG_scene_sort(scene); DAG_object_flush_update(scene, ob, OB_RECALC_DATA); @@ -655,28 +659,28 @@ void PARTICLE_OT_new(wmOperatorType *ot) /********************** keyed particle target operators *********************/ -static int new_keyed_particle_target_exec(bContext *C, wmOperator *op) +static int new_particle_target_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys= ptr.data; Object *ob = ptr.id.data; - KeyedParticleTarget *kpt; + ParticleTarget *pt; if(!psys) return OPERATOR_CANCELLED; - kpt = psys->keyed_targets.first; - for(; kpt; kpt=kpt->next) - kpt->flag &= ~KEYED_TARGET_CURRENT; + pt = psys->targets.first; + for(; pt; pt=pt->next) + pt->flag &= ~PTARGET_CURRENT; - kpt = MEM_callocN(sizeof(KeyedParticleTarget), "keyed particle target"); + pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target"); - kpt->flag |= KEYED_TARGET_CURRENT; - kpt->psys = 1; + pt->flag |= PTARGET_CURRENT; + pt->psys = 1; - BLI_addtail(&psys->keyed_targets, kpt); + BLI_addtail(&psys->targets, pt); DAG_scene_sort(scene); DAG_object_flush_update(scene, ob, OB_RECALC_DATA); @@ -686,44 +690,44 @@ static int new_keyed_particle_target_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void PARTICLE_OT_new_keyed_target(wmOperatorType *ot) +void PARTICLE_OT_new_target(wmOperatorType *ot) { /* identifiers */ - ot->name= "New Keyed Particle Target"; - ot->idname= "PARTICLE_OT_new_keyed_target"; + ot->name= "New Particle Target"; + ot->idname= "PARTICLE_OT_new_target"; /* api callbacks */ - ot->exec= new_keyed_particle_target_exec; + ot->exec= new_particle_target_exec; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } -static int remove_keyed_particle_target_exec(bContext *C, wmOperator *op) +static int remove_particle_target_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys= ptr.data; Object *ob = ptr.id.data; - KeyedParticleTarget *kpt; + ParticleTarget *pt; if(!psys) return OPERATOR_CANCELLED; - kpt = psys->keyed_targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->flag & KEYED_TARGET_CURRENT) { - BLI_remlink(&psys->keyed_targets, kpt); - MEM_freeN(kpt); + pt = psys->targets.first; + for(; pt; pt=pt->next) { + if(pt->flag & PTARGET_CURRENT) { + BLI_remlink(&psys->targets, pt); + MEM_freeN(pt); break; } } - kpt = psys->keyed_targets.last; + pt = psys->targets.last; - if(kpt) - kpt->flag |= KEYED_TARGET_CURRENT; + if(pt) + pt->flag |= PTARGET_CURRENT; DAG_scene_sort(scene); DAG_object_flush_update(scene, ob, OB_RECALC_DATA); @@ -733,37 +737,37 @@ static int remove_keyed_particle_target_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void PARTICLE_OT_remove_keyed_target(wmOperatorType *ot) +void PARTICLE_OT_remove_target(wmOperatorType *ot) { /* identifiers */ - ot->name= "Remove Keyed Particle Target"; - ot->idname= "PARTICLE_OT_remove_keyed_target"; + ot->name= "Remove Particle Target"; + ot->idname= "PARTICLE_OT_remove_target"; /* api callbacks */ - ot->exec= remove_keyed_particle_target_exec; + ot->exec= remove_particle_target_exec; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } -/************************ move up modifier operator *********************/ +/************************ move up particle target operator *********************/ -static int keyed_target_move_up_exec(bContext *C, wmOperator *op) +static int target_move_up_exec(bContext *C, wmOperator *op) { Scene *scene= CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys= ptr.data; Object *ob = ptr.id.data; - KeyedParticleTarget *kpt; + ParticleTarget *pt; if(!psys) return OPERATOR_CANCELLED; - kpt = psys->keyed_targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->flag & KEYED_TARGET_CURRENT && kpt->prev) { - BLI_remlink(&psys->keyed_targets, kpt); - BLI_insertlink(&psys->keyed_targets, kpt->prev->prev, kpt); + pt = psys->targets.first; + for(; pt; pt=pt->next) { + if(pt->flag & PTARGET_CURRENT && pt->prev) { + BLI_remlink(&psys->targets, pt); + BLI_insertlink(&psys->targets, pt->prev->prev, pt); DAG_object_flush_update(scene, ob, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); @@ -774,35 +778,35 @@ static int keyed_target_move_up_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void PARTICLE_OT_keyed_target_move_up(wmOperatorType *ot) +void PARTICLE_OT_target_move_up(wmOperatorType *ot) { - ot->name= "Move Up Keyed Target"; - ot->description= "Move keyed particle target up in the list."; - ot->idname= "PARTICLE_OT_keyed_target_move_up"; + ot->name= "Move Up Target"; + ot->description= "Move particle target up in the list."; + ot->idname= "PARTICLE_OT_target_move_up"; - ot->exec= keyed_target_move_up_exec; + ot->exec= target_move_up_exec; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } -/************************ move down modifier operator *********************/ +/************************ move down particle target operator *********************/ -static int keyed_target_move_down_exec(bContext *C, wmOperator *op) +static int target_move_down_exec(bContext *C, wmOperator *op) { Scene *scene= CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem); ParticleSystem *psys= ptr.data; Object *ob = ptr.id.data; - KeyedParticleTarget *kpt; + ParticleTarget *pt; if(!psys) return OPERATOR_CANCELLED; - kpt = psys->keyed_targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->flag & KEYED_TARGET_CURRENT && kpt->next) { - BLI_remlink(&psys->keyed_targets, kpt); - BLI_insertlink(&psys->keyed_targets, kpt->next, kpt); + pt = psys->targets.first; + for(; pt; pt=pt->next) { + if(pt->flag & PTARGET_CURRENT && pt->next) { + BLI_remlink(&psys->targets, pt); + BLI_insertlink(&psys->targets, pt->next, pt); DAG_object_flush_update(scene, ob, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); @@ -813,13 +817,13 @@ static int keyed_target_move_down_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void PARTICLE_OT_keyed_target_move_down(wmOperatorType *ot) +void PARTICLE_OT_target_move_down(wmOperatorType *ot) { - ot->name= "Move Down Keyed Target"; - ot->description= "Move keyed particle target down in the list."; - ot->idname= "PARTICLE_OT_keyed_target_move_down"; + ot->name= "Move Down Target"; + ot->description= "Move particle target down in the list."; + ot->idname= "PARTICLE_OT_target_move_down"; - ot->exec= keyed_target_move_down_exec; + ot->exec= target_move_down_exec; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 0c1f735451a..72c479b2877 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -198,10 +198,10 @@ void buttons_operatortypes(void) WM_operatortype_append(OBJECT_OT_particle_system_remove); WM_operatortype_append(PARTICLE_OT_new); - WM_operatortype_append(PARTICLE_OT_new_keyed_target); - WM_operatortype_append(PARTICLE_OT_remove_keyed_target); - WM_operatortype_append(PARTICLE_OT_keyed_target_move_up); - WM_operatortype_append(PARTICLE_OT_keyed_target_move_down); + WM_operatortype_append(PARTICLE_OT_new_target); + WM_operatortype_append(PARTICLE_OT_remove_target); + WM_operatortype_append(PARTICLE_OT_target_move_up); + WM_operatortype_append(PARTICLE_OT_target_move_down); WM_operatortype_append(SCENE_OT_render_layer_add); WM_operatortype_append(SCENE_OT_render_layer_remove); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 41da2c5a85b..0f498810fb5 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -40,6 +40,7 @@ #include "MTC_matrixops.h" #include "DNA_armature_types.h" +#include "DNA_boid_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_constraint_types.h" // for drawing constraint @@ -3136,7 +3137,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv Material *ma; float vel[3], imat[4][4]; float timestep, pixsize=1.0, pa_size, r_tilt, r_length; - float pa_time, pa_birthtime, pa_dietime; + float pa_time, pa_birthtime, pa_dietime, pa_health; float cfra= bsystem_time(scene, ob,(float)CFRA,0.0); float ma_r=0.0f, ma_g=0.0f, ma_b=0.0f; int a, totpart, totpoint=0, totve=0, drawn, draw_as, totchild=0; @@ -3362,6 +3363,10 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv pa_birthtime=pa->time; pa_dietime = pa->dietime; pa_size=pa->size; + if(part->phystype==PART_PHYS_BOIDS) + pa_health = pa->boid->health; + else + pa_health = -1.0; #if 0 // XXX old animation system if((part->flag&PART_ABS_TIME)==0){ @@ -3424,6 +3429,8 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv pa_size=psys_get_child_size(psys,cpa,cfra,0); + pa_health = -1.0; + r_tilt = 2.0f * cpa->rand[2]; r_length = cpa->rand[1]; } @@ -3506,9 +3513,19 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv setlinestyle(0); } - if(part->draw&PART_DRAW_NUM && !(G.f & G_RENDER_SHADOW)){ + if((part->draw&PART_DRAW_NUM || part->draw&PART_DRAW_HEALTH) && !(G.f & G_RENDER_SHADOW)){ + strcpy(val, ""); + + if(part->draw&PART_DRAW_NUM) + sprintf(val, " %i", a); + + if(part->draw&PART_DRAW_NUM && part->draw&PART_DRAW_HEALTH) + sprintf(val, "%s:", val); + + if(part->draw&PART_DRAW_HEALTH && a < totpart && part->phystype==PART_PHYS_BOIDS) + sprintf(val, "%s %.2f", val, pa_health); + /* in path drawing state.co is the end point */ - sprintf(val," %i",a); view3d_particle_text_draw_add(state.co[0], state.co[1], state.co[2], val, 0); } } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 49a6fd4daf0..ab053c136ea 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -427,9 +427,14 @@ typedef struct CollisionModifierData { typedef struct SurfaceModifierData { ModifierData modifier; + struct MVert *x; /* old position */ + struct MVert *v; /* velocity */ + struct DerivedMesh *dm; struct BVHTreeFromMesh *bvhtree; /* bounding volume hierarchy of the mesh faces */ + + int cfra, numverts; } SurfaceModifierData; typedef enum { diff --git a/source/blender/makesdna/DNA_object_force.h b/source/blender/makesdna/DNA_object_force.h index 32bfc58f56c..6cf20cc25a2 100644 --- a/source/blender/makesdna/DNA_object_force.h +++ b/source/blender/makesdna/DNA_object_force.h @@ -227,6 +227,7 @@ typedef struct SoftBody { #define PFIELD_HARMONIC 7 #define PFIELD_CHARGE 8 #define PFIELD_LENNARDJ 9 +#define PFIELD_BOID 10 /* pd->flag: various settings */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 0b3309bfc0c..2d8a186d3f4 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -61,13 +61,13 @@ typedef struct ChildParticle { float rand[3]; } ChildParticle; -typedef struct KeyedParticleTarget { - struct KeyedParticleTarget *next, *prev; +typedef struct ParticleTarget { + struct ParticleTarget *next, *prev; struct Object *ob; int psys; - short flag, rt; + short flag, mode; float time, duration; -} KeyedParticleTarget; +} ParticleTarget; /* Everything that's non dynamic for a particle: */ typedef struct ParticleData { @@ -82,7 +82,9 @@ typedef struct ParticleData { ParticleKey *keys; /* keyed states */ - float i_rot[4],r_rot[4];/* initial & random values (i_rot should be removed as it's not used anymore)*/ + struct BoidData *boid; /* boids data */ + + float r_rot[4]; /* random values */ float r_ave[3],r_ve[3]; float fuv[4], foffset; /* coordinates on face/edge number "num" and depth along*/ @@ -91,13 +93,10 @@ typedef struct ParticleData { float time, lifetime; /* dietime is not nescessarily time+lifetime as */ float dietime; /* particles can die unnaturally (collision) */ - float bank; /* banking angle for boids */ - float size, sizemul; /* size and multiplier so that we can update size when ever */ int num; /* index to vert/edge/face */ int num_dmcache; /* index to derived mesh data (face) to avoid slow lookups */ - int pad; int totkey; int bpi; /* softbody body point start index */ @@ -112,12 +111,14 @@ typedef struct ParticleSettings { ID id; struct AnimData *adt; + struct BoidSettings *boids; + int flag; short type, from, distr; /* physics modes */ short phystype, rotmode, avemode, reactevent; short draw, draw_as, draw_size, childtype; - short ren_as, rt2[3]; + short ren_as, rt2; /* number of path segments, power of 2 except */ short draw_step, ren_step; short hair_step, keys_step; @@ -126,7 +127,7 @@ typedef struct ParticleSettings { short adapt_angle, adapt_pix; short disp, omat, interpolation, rotfrom, integrator; - short kink, kink_axis, nbetween, boidneighbours; + short kink, kink_axis; /* billboards */ short bb_align, bb_uv_split, bb_anim, bb_split_offset; @@ -154,7 +155,7 @@ typedef struct ParticleSettings { /* children */ int child_nbr, ren_child_nbr; float parents, childsize, childrandsize; - float childrad, childflat, rt; + float childrad, childflat; /* clumping */ float clumpfac, clumppow; /* kink */ @@ -174,11 +175,7 @@ typedef struct ParticleSettings { /* keyed particles */ int keyed_loops; - /* boids */ - float max_vel, max_lat_acc, max_tan_acc; - float average_vel, banking, max_bank, groundz; - float boidfac[8]; - char boidrule[8]; + float effector_weight[10]; struct Group *dup_group; struct Group *eff_group; @@ -212,12 +209,14 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in struct ListBase effectors, reactevents; /* runtime */ - struct ListBase keyed_targets; + struct ListBase targets; /* used for keyed and boid physics */ + + char name[32]; /* particle system name */ float imat[4][4]; /* used for duplicators */ - float cfra; + float cfra, tree_frame; int seed; - int flag, totpart, totchild, totcached, totchildcache, rt; + int flag, totpart, totchild, totcached, totchildcache; short recalc, target_psys, totkeyed, softflag, bakespace, rt2; char bb_uvname[3][32]; /* billboard uv name */ @@ -230,6 +229,8 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in /* point cache */ struct PointCache *pointcache; + + struct KDTree *tree; /* used for interactions with self and other systems */ }ParticleSystem; /* general particle maximums */ @@ -325,7 +326,7 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in //#define PART_DRAW_PATH_LEN 2 #define PART_DRAW_SIZE 4 #define PART_DRAW_EMITTER 8 /* render emitter also */ -//#define PART_DRAW_HEALTH 16 +#define PART_DRAW_HEALTH 16 #define PART_ABS_PATH_TIME 32 //#define PART_DRAW_TRAIL 64 #define PART_DRAW_BB_LOCK 128 @@ -460,26 +461,13 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in #define PSYS_VG_ROT 10 #define PSYS_VG_EFFECTOR 11 -/* part->boidrules */ -#define BOID_TOT_RULES 8 - -#define BOID_COLLIDE 0 -#define BOID_AVOID 1 -#define BOID_CROWD 2 -#define BOID_CENTER 3 -#define BOID_AV_VEL 4 -#define BOID_VEL_MATCH 5 -#define BOID_GOAL 6 -#define BOID_LEVEL 7 - -/* psys->keyed_targets->flag */ -#define KEYED_TARGET_CURRENT 1 -#define KEYED_TARGET_VALID 2 - +/* ParticleTarget->flag */ +#define PTARGET_CURRENT 1 +#define PTARGET_VALID 2 -//#define PSYS_INTER_CUBIC 0 -//#define PSYS_INTER_LINEAR 1 -//#define PSYS_INTER_CARDINAL 2 -//#define PSYS_INTER_BSPLINE 3 +/* ParticleTarget->mode */ +#define PTARGET_MODE_NEUTRAL 0 +#define PTARGET_MODE_FRIEND 1 +#define PTARGET_MODE_ENEMY 2 #endif diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 08af6372d31..ffd164baaaf 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -132,6 +132,7 @@ char *includefiles[] = { // of makesdna.c (this file) as well "DNA_windowmanager_types.h", "DNA_anim_types.h", + "DNA_boid_types.h", // empty string to indicate end of includefiles "" @@ -1154,4 +1155,5 @@ int main(int argc, char ** argv) #include "DNA_gpencil_types.h" #include "DNA_windowmanager_types.h" #include "DNA_anim_types.h" +#include "DNA_boid_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 90270483adb..f3c2e95451d 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -221,7 +221,7 @@ extern StructRNA RNA_Key; extern StructRNA RNA_KeyboardSensor; extern StructRNA RNA_KeyingSet; extern StructRNA RNA_KeyingSetPath; -extern StructRNA RNA_KeyedParticleTarget; +extern StructRNA RNA_ParticleTarget; extern StructRNA RNA_KinematicConstraint; extern StructRNA RNA_Lamp; extern StructRNA RNA_LampSkySettings; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 276f421c586..2592a1340ec 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -34,6 +34,7 @@ extern EnumPropertyItem space_type_items[]; extern EnumPropertyItem region_type_items[]; extern EnumPropertyItem modifier_type_items[]; extern EnumPropertyItem constraint_type_items[]; +extern EnumPropertyItem boidrule_type_items[]; extern EnumPropertyItem beztriple_handle_type_items[]; extern EnumPropertyItem beztriple_interpolation_mode_items[]; diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 85c266e3f27..c8698ef57ac 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1901,6 +1901,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_animation.c", NULL, RNA_def_animation}, {"rna_actuator.c", NULL, RNA_def_actuator}, {"rna_armature.c", NULL, RNA_def_armature}, + {"rna_boid.c", NULL, RNA_def_boid}, {"rna_brush.c", NULL, RNA_def_brush}, {"rna_camera.c", NULL, RNA_def_camera}, {"rna_cloth.c", NULL, RNA_def_cloth}, diff --git a/source/blender/makesrna/intern/rna_boid.c b/source/blender/makesrna/intern/rna_boid.c new file mode 100644 index 00000000000..af274d813b5 --- /dev/null +++ b/source/blender/makesrna/intern/rna_boid.c @@ -0,0 +1,615 @@ +/** + * $Id: rna_modifier.c 21514 2009-07-11 05:41:21Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "rna_internal.h" + +#include "DNA_scene_types.h" +#include "DNA_boid_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "WM_types.h" + +EnumPropertyItem boidrule_type_items[] ={ + {eBoidRuleType_Goal, "GOAL", 0, "Goal", "Go to assigned object or loudest assigned signal source."}, + {eBoidRuleType_Avoid, "AVOID", 0, "Avoid", "Get away from assigned object or loudest assigned signal source."}, + {eBoidRuleType_AvoidCollision, "AVOID_COLLISION", 0, "Avoid Collision", "Monoeuver to avoid collisions with other boids and deflector objects in near future."}, + {eBoidRuleType_Separate, "SEPARATE", 0, "Separate", "Keep from going through other boids."}, + {eBoidRuleType_Flock, "FLOCK", 0, "Flock", "Move to center of neighbors and match their velocity."}, + {eBoidRuleType_FollowLeader, "FOLLOW_LEADER", 0, "Follow Leader", "Follow a boid or assigned object."}, + {eBoidRuleType_AverageSpeed, "AVERAGE_SPEED", 0, "Average Speed", "Maintain speed, flight level or wander."}, + {eBoidRuleType_Fight, "FIGHT", 0, "Fight", "Go to closest enemy and attack when in range."}, + //{eBoidRuleType_Protect, "PROTECT", 0, "Protect", "Go to enemy closest to target and attack when in range."}, + //{eBoidRuleType_Hide, "HIDE", 0, "Hide", "Find a deflector move to it's other side from closest enemy."}, + //{eBoidRuleType_FollowPath, "FOLLOW_PATH", 0, "Follow Path", "Move along a assigned curve or closest curve in a group."}, + //{eBoidRuleType_FollowWall, "FOLLOW_WALL", 0, "Follow Wall", "Move next to a deflector object's in direction of it's tangent."}, + {0, NULL, 0, NULL, NULL}}; + +EnumPropertyItem boidruleset_type_items[] ={ + {eBoidRulesetType_Fuzzy, "FUZZY", 0, "Fuzzy", "Rules are gone through top to bottom. Only the first rule that effect above fuzziness threshold is evaluated."}, + {eBoidRulesetType_Random, "RANDOM", 0, "Random", "A random rule is selected for each boid."}, + {eBoidRulesetType_Average, "AVERAGE", 0, "Average", "All rules are averaged."}, + {0, NULL, 0, NULL, NULL}}; + + +#ifdef RNA_RUNTIME + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_particle.h" + +static void rna_Boids_reset(bContext *C, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + ParticleSettings *part; + + if(ptr->type==&RNA_ParticleSystem) { + ParticleSystem *psys = (ParticleSystem*)ptr->data; + Object *ob = psys_find_object(scene, psys); + + psys->recalc = PSYS_RECALC_RESET; + + if(ob) { + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + } + } + else { + part = ptr->id.data; + psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET); + } +} +static void rna_Boids_reset_deps(bContext *C, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + ParticleSettings *part; + + if(ptr->type==&RNA_ParticleSystem) { + ParticleSystem *psys = (ParticleSystem*)ptr->data; + Object *ob = psys_find_object(scene, psys); + + psys->recalc = PSYS_RECALC_RESET; + + if(ob) { + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + } + } + else { + part = ptr->id.data; + psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET); + DAG_scene_sort(scene); + } +} + +static StructRNA* rna_BoidRule_refine(struct PointerRNA *ptr) +{ + BoidRule *rule= (BoidRule*)ptr->data; + + switch(rule->type) { + case eBoidRuleType_Goal: + return &RNA_BoidRuleGoal; + case eBoidRuleType_Avoid: + return &RNA_BoidRuleAvoid; + case eBoidRuleType_AvoidCollision: + return &RNA_BoidRuleAvoidCollision; + case eBoidRuleType_FollowLeader: + return &RNA_BoidRuleFollowLeader; + case eBoidRuleType_AverageSpeed: + return &RNA_BoidRuleAverageSpeed; + case eBoidRuleType_Fight: + return &RNA_BoidRuleFight; + default: + return &RNA_BoidRule; + } +} + +static char *rna_BoidRule_path(PointerRNA *ptr) +{ + return BLI_sprintfN("rules[%s]", ((BoidRule*)ptr->data)->name); // XXX not unique +} + +static PointerRNA rna_BoidState_active_boid_rule_get(PointerRNA *ptr) +{ + BoidState *state= (BoidState*)ptr->data; + BoidRule *rule = (BoidRule*)state->rules.first; + + for(; rule; rule=rule->next) { + if(rule->flag & BOIDRULE_CURRENT) + return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, rule); + } + return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, NULL); +} +static void rna_BoidState_active_boid_rule_index_range(PointerRNA *ptr, int *min, int *max) +{ + BoidState *state= (BoidState*)ptr->data; + *min= 0; + *max= BLI_countlist(&state->rules)-1; + *max= MAX2(0, *max); +} + +static int rna_BoidState_active_boid_rule_index_get(PointerRNA *ptr) +{ + BoidState *state= (BoidState*)ptr->data; + BoidRule *rule = (BoidRule*)state->rules.first; + int i=0; + + for(; rule; rule=rule->next, i++) { + if(rule->flag & BOIDRULE_CURRENT) + return i; + } + return 0; +} + +static void rna_BoidState_active_boid_rule_index_set(struct PointerRNA *ptr, int value) +{ + BoidState *state= (BoidState*)ptr->data; + BoidRule *rule = (BoidRule*)state->rules.first; + int i=0; + + for(; rule; rule=rule->next, i++) { + if(i==value) + rule->flag |= BOIDRULE_CURRENT; + else + rule->flag &= ~BOIDRULE_CURRENT; + } +} + +static PointerRNA rna_BoidSettings_active_boid_state_get(PointerRNA *ptr) +{ + BoidSettings *boids= (BoidSettings*)ptr->data; + BoidState *state = (BoidState*)boids->states.first; + + for(; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT) + return rna_pointer_inherit_refine(ptr, &RNA_BoidState, state); + } + return rna_pointer_inherit_refine(ptr, &RNA_BoidState, NULL); +} +static void rna_BoidSettings_active_boid_state_index_range(PointerRNA *ptr, int *min, int *max) +{ + BoidSettings *boids= (BoidSettings*)ptr->data; + *min= 0; + *max= BLI_countlist(&boids->states)-1; + *max= MAX2(0, *max); +} + +static int rna_BoidSettings_active_boid_state_index_get(PointerRNA *ptr) +{ + BoidSettings *boids= (BoidSettings*)ptr->data; + BoidState *state = (BoidState*)boids->states.first; + int i=0; + + for(; state; state=state->next, i++) { + if(state->flag & BOIDSTATE_CURRENT) + return i; + } + return 0; +} + +static void rna_BoidSettings_active_boid_state_index_set(struct PointerRNA *ptr, int value) +{ + BoidSettings *boids= (BoidSettings*)ptr->data; + BoidState *state = (BoidState*)boids->states.first; + int i=0; + + for(; state; state=state->next, i++) { + if(i==value) + state->flag |= BOIDSTATE_CURRENT; + else + state->flag &= ~BOIDSTATE_CURRENT; + } +} + +#else + +static void rna_def_boidrule_goal(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleGoal", "BoidRule"); + RNA_def_struct_ui_text(srna, "Goal", ""); + RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid"); + + prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Goal object."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps"); + + prop= RNA_def_property(srna, "predict", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT); + RNA_def_property_ui_text(prop, "Predict", "Predict target movement."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule_avoid(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleAvoid", "BoidRule"); + RNA_def_struct_ui_text(srna, "Avoid", ""); + RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid"); + + prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object to avoid."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps"); + + prop= RNA_def_property(srna, "predict", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT); + RNA_def_property_ui_text(prop, "Predict", "Predict target movement."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "fear_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Fear factor", "Avoid object if danger from it is above this threshol."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule_avoid_collision(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleAvoidCollision", "BoidRule"); + RNA_def_struct_ui_text(srna, "Avoid Collision", ""); + + prop= RNA_def_property(srna, "boids", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_BOIDS); + RNA_def_property_ui_text(prop, "Boids", "Avoid collision with other boids."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "deflectors", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_DEFLECTORS); + RNA_def_property_ui_text(prop, "Deflectors", "Avoid collision with deflector objects."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "look_ahead", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Look ahead", "Time to look ahead in seconds."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule_follow_leader(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleFollowLeader", "BoidRule"); + RNA_def_struct_ui_text(srna, "Follow Leader", ""); + + prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ob"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Follow this object instead of a boid."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps"); + + prop= RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Distance", "Distance behind leader to follow."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "queue_size", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Queue Size", "How many boids in a line."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "line", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_LEADER_IN_LINE); + RNA_def_property_ui_text(prop, "Line", "Follow leader in a line."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule_average_speed(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleAverageSpeed", "BoidRule"); + RNA_def_struct_ui_text(srna, "Average Speed", ""); + + prop= RNA_def_property(srna, "wander", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Wander", "How fast velocity's direction is randomized."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "level", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Level", "How much velocity's z-component is kept constant."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Speed", "Percentage of maximum speed."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule_fight(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "BoidRuleFight", "BoidRule"); + RNA_def_struct_ui_text(srna, "Fight", ""); + + prop= RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Fight Distance", "Attack boids at max this distance."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "flee_distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_text(prop, "Flee Distance", "Flee to this distance."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +static void rna_def_boidrule(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* data */ + srna= RNA_def_struct(brna, "BoidRule", NULL); + RNA_def_struct_ui_text(srna , "Boid Rule", ""); + RNA_def_struct_refine_func(srna, "rna_BoidRule_refine"); + RNA_def_struct_path_func(srna, "rna_BoidRule_path"); + + /* strings */ + prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Boid rule name."); + RNA_def_struct_name_property(srna, prop); + + /* enums */ + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, boidrule_type_items); + RNA_def_property_ui_text(prop, "Type", ""); + + /* flags */ + prop= RNA_def_property(srna, "in_air", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BOIDRULE_IN_AIR); + RNA_def_property_ui_text(prop, "In Air", "Use rule when boid is flying."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "on_land", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BOIDRULE_ON_LAND); + RNA_def_property_ui_text(prop, "On Land", "Use rule when boid is on land."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + //prop= RNA_def_property(srna, "expanded", PROP_BOOLEAN, PROP_NONE); + //RNA_def_property_boolean_sdna(prop, NULL, "mode", eModifierMode_Expanded); + //RNA_def_property_ui_text(prop, "Expanded", "Set modifier expanded in the user interface."); + + /* types */ + rna_def_boidrule_goal(brna); + rna_def_boidrule_avoid(brna); + rna_def_boidrule_avoid_collision(brna); + rna_def_boidrule_follow_leader(brna); + rna_def_boidrule_average_speed(brna); + rna_def_boidrule_fight(brna); +} + +static void rna_def_boidstate(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "BoidState", NULL); + RNA_def_struct_ui_text(srna, "Boid State", "Boid state for boid physics."); + + prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Boid state name."); + RNA_def_struct_name_property(srna, prop); + + prop= RNA_def_property(srna, "ruleset_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, boidruleset_type_items); + RNA_def_property_ui_text(prop, "Rule Evaluation", "How the rules in the list are evaluated."); + + prop= RNA_def_property(srna, "rules", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "BoidRule"); + RNA_def_property_ui_text(prop, "Boid Rules", ""); + + prop= RNA_def_property(srna, "active_boid_rule", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "BoidRule"); + RNA_def_property_pointer_funcs(prop, "rna_BoidState_active_boid_rule_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Active Boid Rule", ""); + + prop= RNA_def_property(srna, "active_boid_rule_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, "rna_BoidState_active_boid_rule_index_get", "rna_BoidState_active_boid_rule_index_set", "rna_BoidState_active_boid_rule_index_range"); + RNA_def_property_ui_text(prop, "Active Boid Rule Index", ""); + + prop= RNA_def_property(srna, "rule_fuzziness", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Rule Fuzzines", ""); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "volume", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Volume", ""); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_text(prop, "Falloff", ""); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} +static void rna_def_boid_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "BoidSettings", NULL); + RNA_def_struct_ui_text(srna, "Boid Settings", "Settings for boid physics."); + + prop= RNA_def_property(srna, "landing_smoothness", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_text(prop, "Landing Smoothness", "How smoothly the boids land."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "banking", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_text(prop, "Banking", "Amount of rotation around velocity vector on turns."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "height", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_text(prop, "Height", "Boid height relative to particle size."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + /* states */ + prop= RNA_def_property(srna, "states", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "BoidState"); + RNA_def_property_ui_text(prop, "Boid States", ""); + + prop= RNA_def_property(srna, "active_boid_state", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "BoidRule"); + RNA_def_property_pointer_funcs(prop, "rna_BoidSettings_active_boid_state_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Active Boid Rule", ""); + + prop= RNA_def_property(srna, "active_boid_state_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, "rna_BoidSettings_active_boid_state_index_get", "rna_BoidSettings_active_boid_state_index_set", "rna_BoidSettings_active_boid_state_index_range"); + RNA_def_property_ui_text(prop, "Active Boid State Index", ""); + + /* character properties */ + prop= RNA_def_property(srna, "health", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Health", "Initial boid health when born."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Strength", "Maximum caused damage on attack per second."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "aggression", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Aggression", "Boid will fight this times stronger enemy."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "accuracy", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Accuracy", "Accuracy of attack."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "range", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Range", "The maximum distance from which a boid can attack."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + /* physical properties */ + prop= RNA_def_property(srna, "air_min_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Min Air Speed", "Minimum speed in air (relative to maximum speed)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "air_max_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Max Air Speed", "Maximum speed in air."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "air_max_acc", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Max Air Acceleration", "Maximum acceleration in air (relative to maximum speed)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "air_max_ave", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Max Air Angular Velocity", "Maximum angular velocity in air (relative to 180 degrees)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "air_personal_space", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_text(prop, "Air Personal Space", "Radius of boids personal space in air (% of particle size)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_jump_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Jump Speed", "Maximum speed for jumping."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_max_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_text(prop, "Max Land Speed", "Maximum speed on land."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_max_acc", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Max Land Acceleration", "Maximum acceleration on land (relative to maximum speed)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_max_ave", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Max Land Angular Velocity", "Maximum angular velocity on land (relative to 180 degrees)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_personal_space", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_text(prop, "Land Personal Space", "Radius of boids personal space on land (% of particle size)."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "land_stick_force", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1000.0); + RNA_def_property_ui_text(prop, "Land Stick Force", "How strong a force must be to start effecting a boid on land."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + /* options */ + prop= RNA_def_property(srna, "allow_flight", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_FLIGHT); + RNA_def_property_ui_text(prop, "Allow Flight", "Allow boids to move in air."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "allow_land", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_LAND); + RNA_def_property_ui_text(prop, "Allow Land", "Allow boids to move on land."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); + + prop= RNA_def_property(srna, "allow_climb", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_CLIMB); + RNA_def_property_ui_text(prop, "Allow Climbing", "Allow boids to climb goal objects."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset"); +} + +void RNA_def_boid(BlenderRNA *brna) +{ + rna_def_boidrule(brna); + rna_def_boidstate(brna); + rna_def_boid_settings(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_fluidsim.c b/source/blender/makesrna/intern/rna_fluidsim.c index 9afe36fb142..ea4deae08e7 100644 --- a/source/blender/makesrna/intern/rna_fluidsim.c +++ b/source/blender/makesrna/intern/rna_fluidsim.c @@ -93,7 +93,7 @@ static void rna_FluidSettings_update_type(bContext *C, PointerRNA *ptr) if(ob->type == OB_MESH && !psys) { /* add particle system */ - part= psys_new_settings("PSys", bmain); + part= psys_new_settings("ParticleSettings", bmain); psys= MEM_callocN(sizeof(ParticleSystem), "particle_system"); part->type= PART_FLUID; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 3e92c606e53..5afc08439e2 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -117,6 +117,7 @@ void RNA_def_action(struct BlenderRNA *brna); void RNA_def_animation(struct BlenderRNA *brna); void RNA_def_armature(struct BlenderRNA *brna); void RNA_def_actuator(struct BlenderRNA *brna); +void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); void RNA_def_brushclone(struct BlenderRNA *brna); void RNA_def_camera(struct BlenderRNA *brna); diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 269437a8fae..2ec8d9ea41e 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -284,7 +284,7 @@ static void rna_FieldSettings_surface_update(bContext *C, PointerRNA *ptr) /* add/remove modifier as needed */ if(!md) { if(pd && (pd->flag & PFIELD_SURFACE)) - if(ELEM5(pd->forcefield,PFIELD_HARMONIC,PFIELD_FORCE,PFIELD_HARMONIC,PFIELD_CHARGE,PFIELD_LENNARDJ)) + if(ELEM6(pd->forcefield,PFIELD_HARMONIC,PFIELD_FORCE,PFIELD_HARMONIC,PFIELD_CHARGE,PFIELD_LENNARDJ,PFIELD_BOID)) if(ELEM4(ob->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) ED_object_modifier_add(NULL, scene, ob, eModifierType_Surface); } @@ -504,6 +504,7 @@ static void rna_def_field(BlenderRNA *brna) {PFIELD_HARMONIC, "HARMONIC", 0, "Harmonic", ""}, {PFIELD_CHARGE, "CHARGE", 0, "Charge", ""}, {PFIELD_LENNARDJ, "LENNARDJ", 0, "Lennard-Jones", ""}, + {PFIELD_BOID, "BOID", 0, "Boid", ""}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem falloff_items[] = { diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 3c25bd76630..f07351f0a30 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -29,6 +29,7 @@ #include "RNA_define.h" #include "RNA_types.h" +#include "RNA_access.h" #include "rna_internal.h" @@ -36,6 +37,7 @@ #include "DNA_object_force.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_boid_types.h" #include "WM_types.h" #include "WM_api.h" @@ -140,13 +142,33 @@ static void rna_Particle_reset(bContext *C, PointerRNA *ptr) } } -static void rna_Particle_keyed_reset(bContext *C, PointerRNA *ptr) +static void rna_Particle_target_reset(bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); - if(ptr->type==&RNA_KeyedParticleTarget) { + if(ptr->type==&RNA_ParticleTarget) { + ParticleTarget *pt = (ParticleTarget*)ptr->data; Object *ob = (Object*)ptr->id.data; - ParticleSystem *psys = psys_get_current(ob); + ParticleSystem *kpsys=NULL, *psys=psys_get_current(ob); + int psys_num = BLI_findindex(&ob->particlesystem, psys); + + if(pt->ob==ob || pt->ob==NULL) { + kpsys = BLI_findlink(&ob->particlesystem, pt->psys-1); + + if(kpsys) + pt->flag |= PTARGET_VALID; + else + pt->flag &= ~PTARGET_VALID; + } + else { + if(pt->ob) + kpsys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1); + + if(kpsys) + pt->flag |= PTARGET_VALID; + else + pt->flag &= ~PTARGET_VALID; + } psys->recalc = PSYS_RECALC_RESET; @@ -155,11 +177,11 @@ static void rna_Particle_keyed_reset(bContext *C, PointerRNA *ptr) } } -static void rna_Particle_keyed_redo(bContext *C, PointerRNA *ptr) +static void rna_Particle_target_redo(bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); - if(ptr->type==&RNA_KeyedParticleTarget) { + if(ptr->type==&RNA_ParticleTarget) { Object *ob = (Object*)ptr->id.data; ParticleSystem *psys = psys_get_current(ob); @@ -235,8 +257,10 @@ static void rna_particle_settings_set(PointerRNA *ptr, PointerRNA value) psys->part = (ParticleSettings *)value.data; - if(psys->part) + if(psys->part) { psys->part->id.us++; + psys_check_boid_data(psys); + } } static void rna_Particle_abspathtime_update(bContext *C, PointerRNA *ptr) { @@ -315,97 +339,100 @@ static float rna_PartSetting_linelenhead_get(struct PointerRNA *ptr) return settings->draw_line[1]; } -static int rna_ParticleSystem_name_length(PointerRNA *ptr) -{ - ParticleSystem *psys= ptr->data; - - if(psys->part) - return strlen(psys->part->id.name+2); - - return 0; -} - -static void rna_ParticleSystem_name_get(PointerRNA *ptr, char *str) -{ - ParticleSystem *psys= ptr->data; - - if(psys->part) - strcpy(str, psys->part->id.name+2); - else - strcpy(str, ""); -} - -static PointerRNA rna_ParticleSystem_active_keyed_target_get(PointerRNA *ptr) +static PointerRNA rna_ParticleSystem_active_particle_target_get(PointerRNA *ptr) { ParticleSystem *psys= (ParticleSystem*)ptr->data; - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; - for(; kpt; kpt=kpt->next) { - if(kpt->flag & KEYED_TARGET_CURRENT) - return rna_pointer_inherit_refine(ptr, &RNA_KeyedParticleTarget, kpt); + for(; pt; pt=pt->next) { + if(pt->flag & PTARGET_CURRENT) + return rna_pointer_inherit_refine(ptr, &RNA_ParticleTarget, pt); } - return rna_pointer_inherit_refine(ptr, &RNA_KeyedParticleTarget, NULL); + return rna_pointer_inherit_refine(ptr, &RNA_ParticleTarget, NULL); } -static void rna_ParticleSystem_active_keyed_target_index_range(PointerRNA *ptr, int *min, int *max) +static void rna_ParticleSystem_active_particle_target_index_range(PointerRNA *ptr, int *min, int *max) { ParticleSystem *psys= (ParticleSystem*)ptr->data; *min= 0; - *max= BLI_countlist(&psys->keyed_targets)-1; + *max= BLI_countlist(&psys->targets)-1; *max= MAX2(0, *max); } -static int rna_ParticleSystem_active_keyed_target_index_get(PointerRNA *ptr) +static int rna_ParticleSystem_active_particle_target_index_get(PointerRNA *ptr) { ParticleSystem *psys= (ParticleSystem*)ptr->data; - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; int i=0; - for(; kpt; kpt=kpt->next, i++) - if(kpt->flag & KEYED_TARGET_CURRENT) + for(; pt; pt=pt->next, i++) + if(pt->flag & PTARGET_CURRENT) return i; return 0; } -static void rna_ParticleSystem_active_keyed_target_index_set(struct PointerRNA *ptr, int value) +static void rna_ParticleSystem_active_particle_target_index_set(struct PointerRNA *ptr, int value) { ParticleSystem *psys= (ParticleSystem*)ptr->data; - KeyedParticleTarget *kpt = psys->keyed_targets.first; + ParticleTarget *pt = psys->targets.first; int i=0; - for(; kpt; kpt=kpt->next, i++) { + for(; pt; pt=pt->next, i++) { if(i==value) - kpt->flag |= KEYED_TARGET_CURRENT; + pt->flag |= PTARGET_CURRENT; else - kpt->flag &= ~KEYED_TARGET_CURRENT; + pt->flag &= ~PTARGET_CURRENT; } } -static int rna_KeyedParticleTarget_name_length(PointerRNA *ptr) +static int rna_ParticleTarget_name_length(PointerRNA *ptr) { - KeyedParticleTarget *kpt= ptr->data; + ParticleTarget *pt= ptr->data; + + if(pt->flag & PTARGET_VALID) { + ParticleSystem *psys = NULL; - if(kpt->flag & KEYED_TARGET_VALID) { - if(kpt->ob) - return strlen(kpt->ob->id.name+2) + 4; + if(pt->ob) + psys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1); + else { + Object *ob = (Object*) ptr->id.data; + psys = BLI_findlink(&ob->particlesystem, pt->psys-1); + } + + if(psys) { + if(pt->ob) + return strlen(pt->ob->id.name+2) + 2 + strlen(psys->name); + else + return strlen(psys->name); + } else - return 20; + return 15; } else return 15; - - return 0; } -static void rna_KeyedParticleTarget_name_get(PointerRNA *ptr, char *str) +static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str) { - KeyedParticleTarget *kpt= ptr->data; + ParticleTarget *pt= ptr->data; - if(kpt->flag & KEYED_TARGET_VALID) { - if(kpt->ob) - sprintf(str, "%s: %i", kpt->ob->id.name+2, kpt->psys); - else - sprintf(str, "Particle System: %i", kpt->psys); + if(pt->flag & PTARGET_VALID) { + ParticleSystem *psys = NULL; + if(pt->ob) + psys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1); + else { + Object *ob = (Object*) ptr->id.data; + psys = BLI_findlink(&ob->particlesystem, pt->psys-1); + } + + if(psys) { + if(pt->ob) + sprintf(str, "%s: %s", pt->ob->id.name+2, psys->name); + else + strcpy(str, psys->name); + } + else + strcpy(str, "Invalid target!"); } else strcpy(str, "Invalid target!"); @@ -636,10 +663,10 @@ static void rna_def_particle(BlenderRNA *brna) // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Die Time", ""); - prop= RNA_def_property(srna, "banking_angle", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "bank"); -// RNA_def_property_range(prop, lowerLimitf, upperLimitf); - RNA_def_property_ui_text(prop, "Banking Angle", ""); +// prop= RNA_def_property(srna, "banking_angle", PROP_FLOAT, PROP_NONE); +// RNA_def_property_float_sdna(prop, NULL, "bank"); +//// RNA_def_property_range(prop, lowerLimitf, upperLimitf); +// RNA_def_property_ui_text(prop, "Banking Angle", ""); prop= RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE); // RNA_def_property_range(prop, lowerLimitf, upperLimitf); @@ -1024,10 +1051,10 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Emitter", "Render emitter Object also."); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); - //prop= RNA_def_property(srna, "draw_health", PROP_BOOLEAN, PROP_NONE); - //RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_HEALTH); - //RNA_def_property_ui_text(prop, "Health", "Draw boid health"); - //RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); + prop= RNA_def_property(srna, "draw_health", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_HEALTH); + RNA_def_property_ui_text(prop, "Health", "Draw boid health"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); prop= RNA_def_property(srna, "abs_path_time", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_ABS_PATH_TIME); @@ -1149,9 +1176,6 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); - //interpolation - //TODO: can't find where interpolation is used - //TODO: is this read only/internal? prop= RNA_def_property(srna, "rotate_from", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "rotfrom"); @@ -1173,19 +1197,6 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Axis", "Which axis to use for offset"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo_child"); - /* used? - prop= RNA_def_property(srna, "inbetween", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "nbetween"); - RNA_def_property_range(prop, 0, INT_MAX); - RNA_def_property_ui_text(prop, "Inbetween", ""); - */ - - prop= RNA_def_property(srna, "boid_neighbours", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "boidneighbours"); - RNA_def_property_range(prop, 1, 10); - RNA_def_property_ui_text(prop, "Neighbours", "How many neighbours to consider for each boid"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - /* billboards */ prop= RNA_def_property(srna, "billboard_align", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "bb_align"); @@ -1421,6 +1432,12 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Acceleration", "Constant acceleration"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + prop= RNA_def_property(srna, "gravity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "acc[2]"); + RNA_def_property_range(prop, -200.0f, 200.0f); + RNA_def_property_ui_text(prop, "Gravity", "Constant acceleration in global Z axis direction"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + prop= RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "dragfac"); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -1619,46 +1636,12 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); /* boids */ - prop= RNA_def_property(srna, "max_velocity", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "max_vel"); - RNA_def_property_range(prop, 0.0f, 200.0f); - RNA_def_property_ui_text(prop, "Maximum Velocity", ""); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - - prop= RNA_def_property(srna, "lateral_acceleration_max", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "max_lat_acc"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Lateral Acceleration", "Lateral acceleration % of max velocity"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - - prop= RNA_def_property(srna, "tangential_acceleration_max", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "max_tan_acc"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Tangential acceleration", "Tangential acceleration % of max velocity"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - - prop= RNA_def_property(srna, "average_velocity", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "average_vel"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Average Velocity", "The usual speed % of max velocity"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - - prop= RNA_def_property(srna, "banking", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, -10.0f, 10.0f); - RNA_def_property_ui_text(prop, "Banking", "Banking of boids on turns (1.0==natural banking)"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - - prop= RNA_def_property(srna, "banking_max", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "max_bank"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Maximum Banking", "How much a boid can bank at a single step"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + prop= RNA_def_property(srna, "boids", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "BoidSettings"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Boid Settings", ""); - prop= RNA_def_property(srna, "ground_z", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "groundz"); - RNA_def_property_range(prop, -100.0f, 100.0f); - RNA_def_property_ui_text(prop, "Ground Z", "Default Z value"); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + /* draw objects & groups */ prop= RNA_def_property(srna, "dupli_group", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "dup_group"); @@ -1667,13 +1650,6 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Dupli Group", "Show Objects in this Group in place of particles"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); - prop= RNA_def_property(srna, "effector_group", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "eff_group"); - RNA_def_property_struct_type(prop, "Group"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Effector Group", "Limit effectors to this Group."); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - prop= RNA_def_property(srna, "dupli_object", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "dup_ob"); RNA_def_property_struct_type(prop, "Object"); @@ -1687,6 +1663,74 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Billboard Object", "Billboards face this object (default is active camera)"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); + + /* effectors */ + prop= RNA_def_property(srna, "effector_group", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "eff_group"); + RNA_def_property_struct_type(prop, "Group"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Effector Group", "Limit effectors to this Group."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_all", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[0]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "All", "All effector's weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_spherical", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[1]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Spherical", "Spherical effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_vortex", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[2]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Vortex", "Vortex effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_magnetic", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[3]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Magnetic", "Magnetic effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_wind", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[4]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Wind", "Wind effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_curveguide", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[5]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Curve Guide", "Curve guide effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_texture", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[6]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Magnetic", "Texture effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_harmonic", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[7]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Harmonic", "Harmonic effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_charge", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[8]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Charge", "Charge effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); + + prop= RNA_def_property(srna, "eweight_lennardjones", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "effector_weight[9]"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Lennard-Jones", "Lennard-Jones effector weight."); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); /* animation here? */ rna_def_animdata_common(srna); @@ -1695,17 +1739,25 @@ static void rna_def_particle_settings(BlenderRNA *brna) // struct PartDeflect *pd2; } -static void rna_def_keyed_particle_target(BlenderRNA *brna) +static void rna_def_particle_target(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "KeyedParticleTarget", NULL); - RNA_def_struct_ui_text(srna, "Keyed Particle Target", "Target particle system for keyed particles."); + static EnumPropertyItem mode_items[] = { + {PTARGET_MODE_FRIEND, "FRIEND", 0, "Friend", ""}, + {PTARGET_MODE_NEUTRAL, "NEUTRAL", 0, "Neutral", ""}, + {PTARGET_MODE_ENEMY, "ENEMY", 0, "Enemy", ""}, + {0, NULL, 0, NULL, NULL} + }; + + + srna = RNA_def_struct(brna, "ParticleTarget", NULL); + RNA_def_struct_ui_text(srna, "Particle Target", "Target particle system."); prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, "rna_KeyedParticleTarget_name_get", "rna_KeyedParticleTarget_name_length", NULL); - RNA_def_property_ui_text(prop, "Name", "Keyed particle target name."); + RNA_def_property_string_funcs(prop, "rna_ParticleTarget_name_get", "rna_ParticleTarget_name_length", NULL); + RNA_def_property_ui_text(prop, "Name", "Particle target name."); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); @@ -1713,32 +1765,36 @@ static void rna_def_keyed_particle_target(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "ob"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Target Object", "The object that has the target particle system (empty if same object)."); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_keyed_reset"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_target_reset"); prop= RNA_def_property(srna, "system", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "psys"); RNA_def_property_range(prop, 1, INT_MAX); RNA_def_property_ui_text(prop, "Target Particle System", "The index of particle system on the target object."); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_keyed_reset"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_target_reset"); prop= RNA_def_property(srna, "time", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "time"); RNA_def_property_range(prop, 0.0, 30000.0f); //TODO: replace 30000 with MAXFRAMEF when available in 2.5 RNA_def_property_ui_text(prop, "Time", ""); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_keyed_redo"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_target_redo"); prop= RNA_def_property(srna, "duration", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "duration"); RNA_def_property_range(prop, 0.0, 30000.0f); //TODO: replace 30000 with MAXFRAMEF when available in 2.5 RNA_def_property_ui_text(prop, "Duration", ""); - RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_keyed_redo"); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_target_redo"); prop= RNA_def_property(srna, "valid", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYED_TARGET_VALID); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PTARGET_VALID); RNA_def_property_clear_flag(prop, PROP_ANIMATEABLE); RNA_def_property_ui_text(prop, "Valid", "Keyed particles target is valid."); - + prop= RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATEABLE); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_target_reset"); } static void rna_def_particle_system(BlenderRNA *brna) @@ -1751,9 +1807,7 @@ static void rna_def_particle_system(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_PARTICLE_DATA); prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, "rna_ParticleSystem_name_get", "rna_ParticleSystem_name_length", NULL); RNA_def_property_ui_text(prop, "Name", "Particle system name."); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); /* access to particle settings is redirected through functions */ @@ -1807,13 +1861,6 @@ static void rna_def_particle_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Reactor Target Particle System", "For reactor systems, index of particle system on the target object."); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - /* boids */ - //prop= RNA_def_property(srna, "boids_surface_object", PROP_POINTER, PROP_NONE); - //RNA_def_property_pointer_sdna(prop, NULL, "keyed_ob"); - //RNA_def_property_flag(prop, PROP_EDITABLE); - //RNA_def_property_ui_text(prop, "Boids Surface Object", "For boids physics systems, constrain boids to this object's surface."); - //RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_reset"); - /* keyed */ prop= RNA_def_property(srna, "keyed_timing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_KEYED_TIMING); @@ -1821,19 +1868,18 @@ static void rna_def_particle_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Keyed timing", "Use key times"); RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Particle_redo"); - prop= RNA_def_property(srna, "keyed_targets", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "keyed_targets", NULL); - RNA_def_property_struct_type(prop, "KeyedParticleTarget"); - RNA_def_property_ui_text(prop, "Keyed Targets", "Target particle systems for keyed particles"); + prop= RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ParticleTarget"); + RNA_def_property_ui_text(prop, "Targets", "Target particle systems."); - prop= RNA_def_property(srna, "active_keyed_target", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "KeyedParticleTarget"); - RNA_def_property_pointer_funcs(prop, "rna_ParticleSystem_active_keyed_target_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Active Particle System", "Active particle system being displayed"); + prop= RNA_def_property(srna, "active_particle_target", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ParticleTarget"); + RNA_def_property_pointer_funcs(prop, "rna_ParticleSystem_active_particle_target_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Active Particle Target", ""); - prop= RNA_def_property(srna, "active_keyed_target_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, "rna_ParticleSystem_active_keyed_target_index_get", "rna_ParticleSystem_active_keyed_target_index_set", "rna_ParticleSystem_active_keyed_target_index_range"); - RNA_def_property_ui_text(prop, "Active Particle System Index", "Index of active particle system slot."); + prop= RNA_def_property(srna, "active_particle_target_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, "rna_ParticleSystem_active_particle_target_index_get", "rna_ParticleSystem_active_particle_target_index_set", "rna_ParticleSystem_active_particle_target_index_range"); + RNA_def_property_ui_text(prop, "Active Particle Target Index", ""); /* billboard */ @@ -1989,11 +2035,13 @@ static void rna_def_particle_system(BlenderRNA *brna) void RNA_def_particle(BlenderRNA *brna) { + rna_def_particle_target(brna); + rna_def_particle_hair_key(brna); rna_def_particle_key(brna); + rna_def_child_particle(brna); rna_def_particle(brna); - rna_def_keyed_particle_target(brna); rna_def_particle_system(brna); rna_def_particle_settings(brna); } -- cgit v1.2.3