Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJanne Karhu <jhkarh@gmail.com>2009-07-21 03:52:53 +0400
committerJanne Karhu <jhkarh@gmail.com>2009-07-21 03:52:53 +0400
commitb4353a84439085ac7d4bb7da2daed983637a75cc (patch)
tree01e560f80b4c190cddae071c7ca41cd5840fe1ec /source/blender/blenkernel
parent01b787636b468a1d37a27bb7601de5b0dea9c4b0 (diff)
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.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_boids.h62
-rw-r--r--source/blender/blenkernel/BKE_particle.h29
-rw-r--r--source/blender/blenkernel/intern/boids.c1526
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c80
-rw-r--r--source/blender/blenkernel/intern/effect.c3
-rw-r--r--source/blender/blenkernel/intern/modifier.c50
-rw-r--r--source/blender/blenkernel/intern/object.c46
-rw-r--r--source/blender/blenkernel/intern/particle.c55
-rw-r--r--source/blender/blenkernel/intern/particle_system.c1228
9 files changed, 2212 insertions, 867 deletions
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 <string.h>
+#include <math.h>
+
+#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; n<neighbors; n++) {
+ VECCOPY(co1, pa->prev_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; n<neighbors; n++) {
+ VECCOPY(co1, pa->prev_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; n<neighbors; n++) {
+ VecAddf(loc, loc, bbd->psys->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; n<neighbors; n++)
+ health += bbd->psys->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; n<neighbors; n++) {
+ health += epars[ptn[n].index].boid->health;
+
+ 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; i<numverts; i++)
- Mat4MulVecfl(ob->obmat, 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; i<numverts; i++, x++, v++) {
+ vec = CDDM_get_vert(surmd->dm, 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; a<psysn->totpart; 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; i<BOID_TOT_RULES; i++){
- part->boidrule[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; i<totsaved; i++, pa++)
+ if(psys->particles->boid)
+ MEM_freeN(psys->particles->boid);
+
+ for(i=0, pa=newpars; i<totsaved; i++, pa++) {
if(pa->keys) {
pa->keys= NULL;
pa->totkey= 0;
}
+ }
for(i=totsaved, pa=psys->particles+totsaved; i<psys->totpart; 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++; i<totpart; i++,pa++)
+ pa->boid = (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; k<totkeys; k++) {
- if(kpt->ob)
- 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; i<totpart; i++, pa++) {
(pa->keys + 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; p<totpart; p++,pa++){
+ if(pa->flag & (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; n<neighbours; n++){
- cubedist[n]=(float)pow((double)(ptn[n].dist/pa->size),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; i<BOID_TOT_RULES && not_finished; i++){
- switch(part->boidrule[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; p<count; p++){
- state.time=-1.0;
- if(psys_get_particle_state(scene, eob,epsys,ptn2[p].index,&state,0)){
- VECSUB(dvec, state.co, pa->prev_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; n<neighbours; n++){
- if(ptn[n].dist<2.0f*pa->size){
- 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; n<neighbours; n++){
- bvf->Addf(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; n<neighbours; n++){
- bvf->Copyf(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; p<count; p++){
- state.time=-1.0;
- if(psys_get_particle_state(scene, eob,epsys,ptn2[p].index,&state,0)){
- VECSUB(dvec, state.co, pa->prev_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; p<totpart; p++,pa++){
- if(pa->flag & (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++; p<psys->totpart; p++, pa++)
+ pa->boid = (pa-1)->boid + 1;
+ }
+ }
+ else if(pa->boid){
+ MEM_freeN(pa->boid);
+ for(; p<psys->totpart; 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)
{