Initial code for boids v2
authorJanne Karhu <jhkarh@gmail.com>
Mon, 20 Jul 2009 23:52:53 +0000 (23:52 +0000)
committerJanne Karhu <jhkarh@gmail.com>
Mon, 20 Jul 2009 23:52:53 +0000 (23:52 +0000)
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.

34 files changed:
release/ui/buttons_particle.py
release/ui/buttons_physics_field.py
source/blender/blenkernel/BKE_boids.h [new file with mode: 0644]
source/blender/blenkernel/BKE_particle.h
source/blender/blenkernel/intern/boids.c [new file with mode: 0644]
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/effect.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenlib/BLI_kdtree.h
source/blender/blenlib/intern/BLI_kdtree.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/include/ED_physics.h
source/blender/editors/physics/physics_boids.c [new file with mode: 0644]
source/blender/editors/space_api/spacetypes.c
source/blender/editors/space_buttons/buttons_intern.h
source/blender/editors/space_buttons/buttons_ops.c
source/blender/editors/space_buttons/space_buttons.c
source/blender/editors/space_view3d/drawobject.c
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_object_force.h
source/blender/makesdna/DNA_particle_types.h
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_boid.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_fluidsim.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_object_force.c
source/blender/makesrna/intern/rna_particle.c

index 56e586a7271f1b7e95ec37d46f47a9409f545440..47d325a59403bc96726b91059f7b65d3be21fced 100644 (file)
@@ -40,27 +40,33 @@ class PARTICLE_PT_particles(ParticleButtonsPanel):
                        col.itemO("object.particle_system_remove", icon="ICON_ZOOMOUT", text="")
 
                if psys:
-                       split = layout.split(percentage=0.65)
+                       part = psys.settings
+                       
+                       split = layout.split(percentage=0.32)
+                       col = split.column()
+                       col.itemL(text="Name:")
+                       if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
+                               col.itemL(text="Settings:")
+                               col.itemL(text="Type:")
                        
-                       split.template_ID(psys, "settings", new="particle.new")
+                       col = split.column()
+                       col.itemR(psys, "name", text="")
+                       if part.type in ('EMITTER', 'REACTOR', 'HAIR'):
+                               col.template_ID(psys, "settings", new="particle.new")
                        
                        #row = layout.row()
                        #row.itemL(text="Viewport")
                        #row.itemL(text="Render")
                        
-                       part = psys.settings
-                       
                        if part:
-                               ptype = psys.settings.type
-                               if ptype not in ('EMITTER', 'REACTOR', 'HAIR'):
+                               if part.type not in ('EMITTER', 'REACTOR', 'HAIR'):
                                        layout.itemL(text="No settings for fluid particles")
                                        return
-                                       
-                               split = layout.split(percentage=0.65)
                                
-                               split.enabled = particle_panel_enabled(psys)
-                               split.itemR(part, "type")
-                               split.itemR(psys, "seed")
+                               row=col.row()
+                               row.enabled = particle_panel_enabled(psys)
+                               row.itemR(part, "type", text="")
+                               row.itemR(psys, "seed")
                                
                                split = layout.split(percentage=0.65)
                                if part.type=='HAIR':
@@ -183,6 +189,13 @@ class PARTICLE_PT_cache(ParticleButtonsPanel):
 class PARTICLE_PT_initial(ParticleButtonsPanel):
        __idname__= "PARTICLE_PT_initial"
        __label__ = "Velocity"
+       
+       def poll(self, context):
+               if particle_panel_poll(context):
+                       psys = context.particle_system
+                       return psys.settings.physics_type != 'BOIDS'
+               else:
+                       return False
 
        def draw(self, context):
                layout = self.layout
@@ -250,13 +263,11 @@ class PARTICLE_PT_physics(ParticleButtonsPanel):
                psys = context.particle_system
                part = psys.settings
                
-               layout.enabled = layout.enabled = particle_panel_enabled(psys)
+               layout.enabled = particle_panel_enabled(psys)
 
                row = layout.row()
                row.itemR(part, "physics_type", expand=True)
                if part.physics_type != 'NO':
-                       layout.itemR(part, "effector_group")
-               
                        row = layout.row()
                        col = row.column(align=True)
                        col.itemR(part, "particle_size")
@@ -264,12 +275,10 @@ class PARTICLE_PT_physics(ParticleButtonsPanel):
                        col = row.column(align=True)
                        col.itemR(part, "mass")
                        col.itemR(part, "sizemass", text="Multiply mass with size")
-                                                       
-                       split = layout.split()
-                       
-                       sub = split.column()
                        
                if part.physics_type == 'NEWTON':
+                       split = layout.split()
+                       sub = split.column()
                        
                        sub.itemL(text="Forces:")
                        sub.itemR(part, "brownian_factor")
@@ -280,6 +289,9 @@ class PARTICLE_PT_physics(ParticleButtonsPanel):
                        sub.itemR(part, "acceleration")
                        
                elif part.physics_type == 'KEYED':
+                       split = layout.split()
+                       sub = split.column()
+                       
                        row = layout.row()
                        col = row.column()
                        col.active = not psys.keyed_timing
@@ -287,38 +299,189 @@ class PARTICLE_PT_physics(ParticleButtonsPanel):
                        row.itemR(psys, "keyed_timing", text="Use Timing")
                        
                        layout.itemL(text="Keys:")
+               elif part.physics_type=='BOIDS':
+                       boids = part.boids
+                       
+
                        row = layout.row()
+                       row.itemR(boids, "allow_flight")
+                       row.itemR(boids, "allow_land")
+                       row.itemR(boids, "allow_climb")
                        
-                       row.template_list(psys, "keyed_targets", psys, "active_keyed_target_index")
+                       split = layout.split()
+                       
+                       sub = split.column()
+                       col = sub.column(align=True)
+                       col.active = boids.allow_flight
+                       col.itemR(boids, "air_max_speed")
+                       col.itemR(boids, "air_min_speed", slider="True")
+                       col.itemR(boids, "air_max_acc", slider="True")
+                       col.itemR(boids, "air_max_ave", slider="True")
+                       col.itemR(boids, "air_personal_space")
+                       row = col.row()
+                       row.active = (boids.allow_land or boids.allow_climb) and boids.allow_flight
+                       row.itemR(boids, "landing_smoothness")
+                       
+                       sub = split.column()
+                       col = sub.column(align=True)
+                       col.active = boids.allow_land or boids.allow_climb
+                       col.itemR(boids, "land_max_speed")
+                       col.itemR(boids, "land_jump_speed")
+                       col.itemR(boids, "land_max_acc", slider="True")
+                       col.itemR(boids, "land_max_ave", slider="True")
+                       col.itemR(boids, "land_personal_space")
+                       col.itemR(boids, "land_stick_force")
+                       
+                       row = layout.row()
+                       
+                       col = row.column(align=True)
+                       col.itemL(text="Battle:")
+                       col.itemR(boids, "health")
+                       col.itemR(boids, "strength")
+                       col.itemR(boids, "aggression")
+                       col.itemR(boids, "accuracy")
+                       col.itemR(boids, "range")
+                       
+                       col = row.column()
+                       col.itemL(text="Misc:")
+                       col.itemR(part, "gravity")
+                       col.itemR(boids, "banking", slider=True)
+                       col.itemR(boids, "height", slider=True)
+                       
+               if part.physics_type=='NEWTON':
+                       sub.itemR(part, "size_deflect")
+                       sub.itemR(part, "die_on_collision")
+                       sub.itemR(part, "sticky")
+               elif part.physics_type=='KEYED' or part.physics_type=='BOIDS':
+                       if part.physics_type=='BOIDS':
+                               layout.itemL(text="Relations:")
+                       
+                       row = layout.row()
+                       row.template_list(psys, "targets", psys, "active_particle_target_index")
                        
                        col = row.column()
                        subrow = col.row()
                        subcol = subrow.column(align=True)
-                       subcol.itemO("particle.new_keyed_target", icon="ICON_ZOOMIN", text="")
-                       subcol.itemO("particle.remove_keyed_target", icon="ICON_ZOOMOUT", text="")
+                       subcol.itemO("particle.new_target", icon="ICON_ZOOMIN", text="")
+                       subcol.itemO("particle.remove_target", icon="ICON_ZOOMOUT", text="")
                        subrow = col.row()
                        subcol = subrow.column(align=True)
-                       subcol.itemO("particle.keyed_target_move_up", icon="VICON_MOVE_UP", text="")
-                       subcol.itemO("particle.keyed_target_move_down", icon="VICON_MOVE_DOWN", text="")
+                       subcol.itemO("particle.target_move_up", icon="VICON_MOVE_UP", text="")
+                       subcol.itemO("particle.target_move_down", icon="VICON_MOVE_DOWN", text="")
                        
-                       key = psys.active_keyed_target
+                       key = psys.active_particle_target
                        if key:
                                row = layout.row()
-                               col = row.column()
-                               #doesn't work yet
-                               #col.red_alert = key.valid
-                               col.itemR(key, "object", text="")
-                               col.itemR(key, "system", text="System")
-                               col = row.column();
-                               col.active = psys.keyed_timing
-                               col.itemR(key, "time")
-                               col.itemR(key, "duration")
+                               if part.physics_type=='KEYED':
+                                       col = row.column()
+                                       #doesn't work yet
+                                       #col.red_alert = key.valid
+                                       col.itemR(key, "object", text="")
+                                       col.itemR(key, "system", text="System")
+                                       col = row.column();
+                                       col.active = psys.keyed_timing
+                                       col.itemR(key, "time")
+                                       col.itemR(key, "duration")
+                               else:
+                                       subrow = row.row()
+                                       #doesn't work yet
+                                       #subrow.red_alert = key.valid
+                                       subrow.itemR(key, "object", text="")
+                                       subrow.itemR(key, "system", text="System")
+                                       
+                                       layout.itemR(key, "mode", expand=True)
+
+class PARTICLE_PT_boidbrain(ParticleButtonsPanel):
+       __idname__ = "PARTICLE_PT_boidbrain"
+       __label__ = "Boid Brain"
+
+       def poll(self, context):
+               psys = context.particle_system
+               if psys==None:  return False
+               if psys.settings==None:  return False
+               return psys.settings.physics_type=='BOIDS'
+       
+       def draw(self, context):
+               boids = context.particle_system.settings.boids
+               layout = self.layout
+               
+               # Currently boids can only use the first state so these are commented out for now.
+               #row = layout.row()
+               #row.template_list(boids, "states", boids, "active_boid_state_index", compact="True")
+               #col = row.row()
+               #subrow = col.row(align=True)
+               #subrow.itemO("boid.boidstate_add", icon="ICON_ZOOMIN", text="")
+               #subrow.itemO("boid.boidstate_del", icon="ICON_ZOOMOUT", text="")
+               #subrow = row.row(align=True)
+               #subrow.itemO("boid.boidstate_move_up", icon="VICON_MOVE_UP", text="")
+               #subrow.itemO("boid.boidstate_move_down", icon="VICON_MOVE_DOWN", text="")
+               
+               state = boids.active_boid_state
+               
+               #layout.itemR(state, "name", text="State name")
+               
+               row = layout.row()
+               row.itemR(state, "ruleset_type")
+               if state.ruleset_type=='FUZZY':
+                       row.itemR(state, "rule_fuzziness", slider=True)
+               else:
+                       row.itemL(text="")
+               
+               row = layout.row()
+               row.template_list(state, "rules", state, "active_boid_rule_index")
+               
+               col = row.column()
+               subrow = col.row()
+               subcol = subrow.column(align=True)
+               subcol.item_menu_enumO("boid.boidrule_add", "type", icon="ICON_ZOOMIN", text="")
+               subcol.itemO("boid.boidrule_del", icon="ICON_ZOOMOUT", text="")
+               subrow = col.row()
+               subcol = subrow.column(align=True)
+               subcol.itemO("boid.boidrule_move_up", icon="VICON_MOVE_UP", text="")
+               subcol.itemO("boid.boidrule_move_down", icon="VICON_MOVE_DOWN", text="")
+               
+               rule = state.active_boid_rule
+               
+               if rule:
+                       row = layout.row()
+                       row.itemR(rule, "name", text="")
+                       #somebody make nice icons for boids here please! -jahka
+                       row.itemR(rule, "in_air", icon="VICON_MOVE_UP", text="")
+                       row.itemR(rule, "on_land", icon="VICON_MOVE_DOWN", text="")
                        
-               if part.physics_type=='NEWTON' or part.physics_type=='BOIDS':
+                       row = layout.row()
 
-                       sub.itemR(part, "size_deflect")
-                       sub.itemR(part, "die_on_collision")
-                       sub.itemR(part, "sticky")
+                       if rule.type == 'GOAL':
+                               row.itemR(rule, "object")
+                               row = layout.row()
+                               row.itemR(rule, "predict")
+                       elif rule.type == 'AVOID':
+                               row.itemR(rule, "object")
+                               row = layout.row()
+                               row.itemR(rule, "predict")
+                               row.itemR(rule, "fear_factor")
+                       elif rule.type == 'FOLLOW_PATH':
+                               row.itemL(text="Not yet functional.")
+                       elif rule.type == 'AVOID_COLLISION':
+                               row.itemR(rule, "boids")
+                               row.itemR(rule, "deflectors")
+                               row.itemR(rule, "look_ahead")
+                       elif rule.type == 'FOLLOW_LEADER':
+                               row.itemR(rule, "object", text="")
+                               row.itemR(rule, "distance")
+                               row = layout.row()
+                               row.itemR(rule, "line")
+                               subrow = row.row()
+                               subrow.active = rule.line
+                               subrow.itemR(rule, "queue_size")
+                       elif rule.type == 'AVERAGE_SPEED':
+                               row.itemR(rule, "speed", slider=True)
+                               row.itemR(rule, "wander", slider=True)
+                               row.itemR(rule, "level", slider=True)
+                       elif rule.type == 'FIGHT':
+                               row.itemR(rule, "distance")
+                               row.itemR(rule, "flee_distance")
+               
 
 class PARTICLE_PT_render(ParticleButtonsPanel):
        __idname__= "PARTICLE_PT_render"
@@ -596,6 +759,38 @@ class PARTICLE_PT_children(ParticleButtonsPanel):
                sub = split.column()
                sub.itemR(part, "kink_shape", slider=True)
 
+class PARTICLE_PT_effectors(ParticleButtonsPanel):
+       __idname__= "PARTICLE_PT_effectors"
+       __label__ = "Effectors"
+       __default_closed__ = True
+       
+       def poll(self, context):
+               psys = context.particle_system
+               if psys==None: return False
+               if psys.settings==None: return False
+               return True;
+       
+       def draw(self, context):
+               layout = self.layout
+
+               psys = context.particle_system
+               part = psys.settings
+               
+               layout.itemR(part, "effector_group")
+               
+               layout.itemR(part, "eweight_all", slider=True)
+               
+               layout.itemS()
+               layout.itemR(part, "eweight_spherical", slider=True)
+               layout.itemR(part, "eweight_vortex", slider=True)
+               layout.itemR(part, "eweight_magnetic", slider=True)
+               layout.itemR(part, "eweight_wind", slider=True)
+               layout.itemR(part, "eweight_curveguide", slider=True)
+               layout.itemR(part, "eweight_texture", slider=True)
+               layout.itemR(part, "eweight_harmonic", slider=True)
+               layout.itemR(part, "eweight_charge", slider=True)
+               layout.itemR(part, "eweight_lennardjones", slider=True)
+               
 class PARTICLE_PT_vertexgroups(ParticleButtonsPanel):
        __idname__= "PARTICLE_PT_vertexgroups"
        __label__ = "Vertexgroups"
@@ -667,7 +862,9 @@ bpy.types.register(PARTICLE_PT_cache)
 bpy.types.register(PARTICLE_PT_emission)
 bpy.types.register(PARTICLE_PT_initial)
 bpy.types.register(PARTICLE_PT_physics)
+bpy.types.register(PARTICLE_PT_boidbrain)
 bpy.types.register(PARTICLE_PT_render)
 bpy.types.register(PARTICLE_PT_draw)
 bpy.types.register(PARTICLE_PT_children)
+bpy.types.register(PARTICLE_PT_effectors)
 bpy.types.register(PARTICLE_PT_vertexgroups)
index 3a9622b06574875b1b54feb9c6b79cfef49c36a7..cdb90c95228f7701e80aa138560518cf364d31ca 100644 (file)
@@ -53,6 +53,11 @@ class PHYSICS_PT_field(PhysicButtonsPanel):
                        sub.itemR(field, "planar")
                        sub.itemR(field, "surface")
                        
+               if field.type == "BOID":
+                       sub.itemR(field, "strength")
+                       sub = split.column()
+                       sub.itemR(field, "surface")
+                       
                if field.type == "MAGNET":
                        sub.itemR(field, "strength")
                        sub = split.column()
@@ -75,7 +80,7 @@ class PHYSICS_PT_field(PhysicButtonsPanel):
                        sub.itemR(field, "root_coordinates")
                        sub.itemR(field, "force_2d")
                        
-               if field.type in ("HARMONIC", "SPHERICAL", "CHARGE", "WIND", "VORTEX", "TEXTURE", "MAGNET"):
+               if field.type in ("HARMONIC", "SPHERICAL", "CHARGE", "WIND", "VORTEX", "TEXTURE", "MAGNET", "BOID"):
                
                        
                        layout.itemS()                  
diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h
new file mode 100644 (file)
index 0000000..acceff8
--- /dev/null
@@ -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
index bb0b905b8e3fa4b29509227593210f66779107a2..9f44d3c47cb58c796f9f0802323645c792fc3252 100644 (file)
@@ -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 (file)
index 0000000..931ad0b
--- /dev/null
@@ -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
index f52eec34cc74a5773b4308f40c74de583eef3c3f..7e6a652da6b3bb8d9824ac0c5af0d46104ba63f6 100644 (file)
@@ -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)
 {
index 7951fcf1d3e18adfd36ecec6c4c994ab5ce7fb05..e3c4f12184ed1d7bd6f46fe8daa9fbd55344f140 100644 (file)
@@ -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;
        }
 }
 
index 948d02f2a80fcd1a571a26cc68cd92319753f1f7..c129e8ed99ba1df02ccfd05f67c05d5324d08a93 100644 (file)
@@ -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);
index 65043abe5181d63977d89f107e7422e0e7721149..60b34bf0783288cad3a155cb45da595f293e2f0a 100644 (file)
@@ -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;
                                        }
index da14ac465504b76b86bfa680e5c1a61e06d9bc6c..6e8f9ee213cfa5dc771526bc08096d435305354a 100644 (file)
@@ -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;
 }
index 56ca3e8e22b21a87da86a929332c0aa38ae771bc..b792564f50cd370f31ff6fffc72a6073e029486d 100644 (file)
@@ -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)
 {      
index 49c21e424f70a741234c44c22ca6d45de1beb7ac..585107b0c4a684ba8c2dd3a900495b34ce9faac2 100644 (file)
@@ -52,9 +52,13 @@ void BLI_kdtree_balance(KDTree *tree);
 
 /* Find nearest returns index, and -1 if no node is found.
  * Find n nearest returns number of points found, with results in nearest.
- * Normal is optional. */
+/* Normal is optional, but if given will limit results to points in normal direction from co. */
 int    BLI_kdtree_find_nearest(KDTree *tree, float *co, float *nor, KDTreeNearest *nearest);
 int    BLI_kdtree_find_n_nearest(KDTree *tree, int n, float *co, float *nor, KDTreeNearest *nearest);
 
+/* Range search returns number of points found, with results in nearest */
+/* Normal is optional, but if given will limit results to points in normal direction from co. */
+/* Remember to free nearest after use! */
+int BLI_kdtree_range_search(KDTree *tree, float range, float *co, float *nor, KDTreeNearest **nearest);
 #endif
 
index 8e8b2e9f0e9e77f24cbab5fe8b0b1fd7486a8091..79a46da4c662b511944b84cdae681e15bc1c046f 100644 (file)
@@ -130,7 +130,7 @@ void BLI_kdtree_balance(KDTree *tree)
        tree->root= kdtree_balance(tree->nodes, tree->totnode, 0);
 }
 
-static float squared_distance(float *v1, float *v2, float *n1, float *n2)
+static float squared_distance(float *v2, float *v1, float *n1, float *n2)
 {
        float d[3], dist;
 
@@ -140,7 +140,8 @@ static float squared_distance(float *v1, float *v2, float *n1, float *n2)
 
        dist= d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
 
-       if(n1 && n2 && n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2] < 0.0f)
+       //if(n1 && n2 && n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2] < 0.0f)
+       if(n2 && d[0]*n2[0] + d[1]*n2[1] + d[2]*n2[2] < 0.0f)
                dist *= 10.0f;
 
        return dist;
@@ -336,3 +337,111 @@ int       BLI_kdtree_find_n_nearest(KDTree *tree, int n, float *co, float *nor, KDTree
        return found;
 }
 
+int range_compare(const void * a, const void * b)
+{
+       const KDTreeNearest *kda = a;
+       const KDTreeNearest *kdb = b;
+
+       if(kda->dist < kdb->dist)
+               return -1;
+       else if(kda->dist > kdb->dist)
+               return 1;
+       else
+               return 0;
+}
+static void add_in_range(KDTreeNearest **ptn, int found, int *totfoundstack, int index, float dist, float *co)
+{
+       KDTreeNearest *to;
+
+       if(found+1 > *totfoundstack) {
+               KDTreeNearest *temp=MEM_callocN((*totfoundstack+50)*sizeof(KDTreeNode), "psys_treefoundstack");
+               memcpy(temp, *ptn, *totfoundstack * sizeof(KDTreeNearest));
+               if(*ptn)
+                       MEM_freeN(*ptn);
+               *ptn = temp;
+               *totfoundstack+=50;
+       }
+
+       to = (*ptn) + found;
+
+       to->index = index;
+       to->dist = sqrt(dist);
+       VecCopyf(to->co, co);
+}
+int BLI_kdtree_range_search(KDTree *tree, float range, float *co, float *nor, KDTreeNearest **nearest)
+{
+       KDTreeNode *root, *node=0;
+       KDTreeNode **stack, *defaultstack[100];
+       KDTreeNearest *foundstack=NULL;
+       float range2 = range*range, dist2;
+       int i, totstack, cur=0, found=0, totfoundstack=0;
+
+       if(!tree || !tree->root)
+               return 0;
+
+       stack= defaultstack;
+       totstack= 100;
+
+       root= tree->root;
+
+       if(co[root->d] + range < root->co[root->d]) {
+               if(root->left)
+                       stack[cur++]=root->left;
+       }
+       else if(co[root->d] - range > root->co[root->d]) {
+               if(root->right)
+                       stack[cur++]=root->right;
+       }
+       else {
+               dist2 = squared_distance(root->co, co, root->nor, nor);
+               if(dist2  <= range2)
+                       add_in_range(&foundstack, found++, &totfoundstack, root->index, dist2, root->co);
+
+               if(root->left)
+                       stack[cur++]=root->left;
+               if(root->right)
+                       stack[cur++]=root->right;
+       }
+
+       while(cur--) {
+               node=stack[cur];
+
+               if(co[node->d] + range < node->co[node->d]) {
+                       if(node->left)
+                               stack[cur++]=node->left;
+               }
+               else if(co[node->d] - range > node->co[node->d]) {
+                       if(node->right)
+                               stack[cur++]=node->right;
+               }
+               else {
+                       dist2 = squared_distance(node->co, co, node->nor, nor);
+                       if(dist2 <= range2)
+                               add_in_range(&foundstack, found++, &totfoundstack, node->index, dist2, node->co);
+
+                       if(node->left)
+                               stack[cur++]=node->left;
+                       if(node->right)
+                               stack[cur++]=node->right;
+               }
+
+               if(cur+3 > totstack){
+                       KDTreeNode **temp=MEM_callocN((totstack+100)*sizeof(KDTreeNode*), "psys_treestack");
+                       memcpy(temp,stack,totstack*sizeof(KDTreeNode*));
+                       if(stack != defaultstack)
+                               MEM_freeN(stack);
+                       stack=temp;
+                       totstack+=100;
+               }
+       }
+
+       if(stack != defaultstack)
+               MEM_freeN(stack);
+
+       if(found)
+               qsort(foundstack, found, sizeof(KDTreeNearest), range_compare);
+
+       *nearest = foundstack;
+
+       return found;
+}
index f258746dafe3807cf3ff443e7955edd5dc2e40fa..c43c720bad0a45fe988dbd865aaa2e81be031c98 100644 (file)
@@ -53,6 +53,7 @@
 #include "DNA_armature_types.h"
 #include "DNA_ID.h"
 #include "DNA_actuator_types.h"
+#include "DNA_boid_types.h"
 #include "DNA_brush_types.h"
 #include "DNA_camera_types.h"
 #include "DNA_cloth_types.h"
@@ -2990,6 +2991,29 @@ static void lib_link_particlesettings(FileData *fd, Main *main)
                        part->dup_group = newlibadr(fd, part->id.lib, part->dup_group);
                        part->eff_group = newlibadr(fd, part->id.lib, part->eff_group);
                        part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob);
+                       if(part->boids) {
+                               BoidState *state = part->boids->states.first;
+                               BoidRule *rule;
+                               for(; state; state=state->next) {
+                                       rule = state->rules.first;
+                               for(; rule; rule=rule->next)
+                                       switch(rule->type) {
+                                               case eBoidRuleType_Goal:
+                                               case eBoidRuleType_Avoid:
+                                               {
+                                                       BoidRuleGoalAvoid *brga = (BoidRuleGoalAvoid*)rule;
+                                                       brga->ob = newlibadr(fd, part->id.lib, brga->ob);
+                                                       break;
+                                               }
+                                               case eBoidRuleType_FollowLeader:
+                                               {
+                                                       BoidRuleFollowLeader *brfl = (BoidRuleFollowLeader*)rule;
+                                                       brfl->ob = newlibadr(fd, part->id.lib, brfl->ob);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
                        part->id.flag -= LIB_NEEDLINK;
                }
                part= part->id.next;
@@ -3001,6 +3025,19 @@ static void direct_link_particlesettings(FileData *fd, ParticleSettings *part)
        part->adt= newdataadr(fd, part->adt);
        part->pd= newdataadr(fd, part->pd);
        part->pd2= newdataadr(fd, part->pd2);
+
+       part->boids= newdataadr(fd, part->boids);
+
+       if(part->boids) {
+               BoidState *state;
+               link_list(fd, &part->boids->states);
+               
+               for(state=part->boids->states.first; state; state=state->next) {
+                       link_list(fd, &state->rules);
+                       link_list(fd, &state->conditions);
+                       link_list(fd, &state->actions);
+               }
+       }
 }
 
 static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase *particles)
@@ -3015,10 +3052,10 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase
                
                psys->part = newlibadr_us(fd, id->lib, psys->part);
                if(psys->part) {
-                       KeyedParticleTarget *kpt = psys->keyed_targets.first;
+                       ParticleTarget *pt = psys->targets.first;
 
-                       for(; kpt; kpt=kpt->next)
-                               kpt->ob=newlibadr(fd, id->lib, kpt->ob);
+                       for(; pt; pt=pt->next)
+                               pt->ob=newlibadr(fd, id->lib, pt->ob);
 
                        psys->target_ob = newlibadr(fd, id->lib, psys->target_ob);
 
@@ -3042,24 +3079,38 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase
 static void direct_link_particlesystems(FileData *fd, ListBase *particles)
 {
        ParticleSystem *psys;
+       ParticleData *pa;
        int a;
 
        for(psys=particles->first; psys; psys=psys->next) {
                psys->particles=newdataadr(fd,psys->particles);
+               
                if(psys->particles && psys->particles->hair){
-                       ParticleData *pa = psys->particles;
-                       for(a=0; a<psys->totpart; a++, pa++)
+                       for(a=0,pa=psys->particles; a<psys->totpart; a++, pa++)
                                pa->hair=newdataadr(fd,pa->hair);
                }
+               
                if(psys->particles && psys->particles->keys){
-                       ParticleData *pa = psys->particles;
-                       for(a=0; a<psys->totpart; a++, pa++) {
+                       for(a=0,pa=psys->particles; a<psys->totpart; a++, pa++) {
                                pa->keys= NULL;
                                pa->totkey= 0;
                        }
 
                        psys->flag &= ~PSYS_KEYED;
                }
+
+               if(psys->particles->boid) {
+                       pa = psys->particles;
+                       pa->boid = newdataadr(fd, pa->boid);
+                       for(a=1,pa++; a<psys->totpart; a++, pa++)
+                               pa->boid = (pa-1)->boid + 1;
+               }
+               else {
+                       for(a=0,pa=psys->particles; a<psys->totpart; a++, pa++)
+                               pa->boid = NULL;
+               }
+
+
                psys->child=newdataadr(fd,psys->child);
                psys->effectors.first=psys->effectors.last=0;
 
@@ -3076,7 +3127,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles)
                                direct_link_pointcache(fd, sb->pointcache);
                }
 
-               link_list(fd, &psys->keyed_targets);
+               link_list(fd, &psys->targets);
 
                psys->edit = 0;
                psys->free_edit = NULL;
@@ -3089,6 +3140,8 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles)
                psys->pointcache= newdataadr(fd, psys->pointcache);
                if(psys->pointcache)
                        direct_link_pointcache(fd, psys->pointcache);
+
+               psys->tree = NULL;
        }
        return;
 }
@@ -3649,6 +3702,9 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
 
                        surmd->dm = NULL;
                        surmd->bvhtree = NULL;
+                       surmd->x = NULL;
+                       surmd->v = NULL;
+                       surmd->numverts = 0;
                }
                else if (md->type==eModifierType_Hook) {
                        HookModifierData *hmd = (HookModifierData*) md;
@@ -8541,7 +8597,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                                psys = MEM_callocN(sizeof(ParticleSystem), "particle_system");
                                psys->pointcache = BKE_ptcache_add();
 
-                               part = psys->part = psys_new_settings("PSys", main);
+                               part = psys->part = psys_new_settings("ParticleSettings", main);
                                
                                /* needed for proper libdata lookup */
                                oldnewmap_insert(fd->libmap, psys->part, psys->part, 0);
index 958e8bb874b9fefcb0d4c20c4fab81c52d5f95ee..9e5fbfc25988b98b995648f03ab7aa88cc638760 100644 (file)
@@ -93,6 +93,7 @@ Any case: direct data is ALWAYS after the lib block
 #include "DNA_armature_types.h"
 #include "DNA_action_types.h"
 #include "DNA_actuator_types.h"
+#include "DNA_boid_types.h"
 #include "DNA_brush_types.h"
 #include "DNA_camera_types.h"
 #include "DNA_cloth_types.h"
@@ -550,6 +551,39 @@ static void write_userdef(WriteData *wd)
        }
 }
 
+static void write_boid_state(WriteData *wd, BoidState *state)
+{
+       BoidRule *rule = state->rules.first;
+       //BoidCondition *cond = state->conditions.first;
+
+       writestruct(wd, DATA, "BoidState", 1, state);
+
+       for(; rule; rule=rule->next) {
+               switch(rule->type) {
+                       case eBoidRuleType_Goal:
+                       case eBoidRuleType_Avoid:
+                               writestruct(wd, DATA, "BoidRuleGoalAvoid", 1, rule);
+                               break;
+                       case eBoidRuleType_AvoidCollision:
+                               writestruct(wd, DATA, "BoidRuleAvoidCollision", 1, rule);
+                               break;
+                       case eBoidRuleType_FollowLeader:
+                               writestruct(wd, DATA, "BoidRuleFollowLeader", 1, rule);
+                               break;
+                       case eBoidRuleType_AverageSpeed:
+                               writestruct(wd, DATA, "BoidRuleAverageSpeed", 1, rule);
+                               break;
+                       case eBoidRuleType_Fight:
+                               writestruct(wd, DATA, "BoidRuleFight", 1, rule);
+                               break;
+                       default:
+                               writestruct(wd, DATA, "BoidRule", 1, rule);
+                               break;
+               }
+       }
+       //for(; cond; cond=cond->next)
+       //      writestruct(wd, DATA, "BoidCondition", 1, cond);
+}
 /* TODO: replace *cache with *cachelist once it's coded */
 #define PTCACHE_WRITE_PSYS     0
 #define PTCACHE_WRITE_CLOTH    1
@@ -582,6 +616,15 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase)
                        if (part->adt) write_animdata(wd, part->adt);
                        writestruct(wd, DATA, "PartDeflect", 1, part->pd);
                        writestruct(wd, DATA, "PartDeflect", 1, part->pd2);
+
+                       if(part->boids && part->phystype == PART_PHYS_BOIDS) {
+                               BoidState *state = part->boids->states.first;
+
+                               writestruct(wd, DATA, "BoidSettings", 1, part->boids);
+
+                               for(; state; state=state->next)
+                                       write_boid_state(wd, state);
+                       }
                }
                part= part->id.next;
        }
@@ -589,7 +632,7 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase)
 static void write_particlesystems(WriteData *wd, ListBase *particles)
 {
        ParticleSystem *psys= particles->first;
-       KeyedParticleTarget *kpt;
+       ParticleTarget *pt;
        int a;
 
        for(; psys; psys=psys->next) {
@@ -604,10 +647,13 @@ static void write_particlesystems(WriteData *wd, ListBase *particles)
                                for(a=0; a<psys->totpart; a++, pa++)
                                        writestruct(wd, DATA, "HairKey", pa->totkey, pa->hair);
                        }
+
+                       if(psys->particles->boid && psys->part->phystype == PART_PHYS_BOIDS)
+                               writestruct(wd, DATA, "BoidData", psys->totpart, psys->particles->boid);
                }
-               kpt = psys->keyed_targets.first;
-               for(; kpt; kpt=kpt->next)
-                       writestruct(wd, DATA, "KeyedParticleTarget", 1, kpt);
+               pt = psys->targets.first;
+               for(; pt; pt=pt->next)
+                       writestruct(wd, DATA, "ParticleTarget", 1, pt);
 
                if(psys->child) writestruct(wd, DATA, "ChildParticle", psys->totchild ,psys->child);
                writestruct(wd, DATA, "SoftBody", 1, psys->soft);
index b04bfb992dc4488fa3b0c9c9ce18a57a58a9ad48..6ab804230d0806337d97a3e8330f0e5bb14005c1 100644 (file)
@@ -32,6 +32,7 @@
 
 /* operators */
 
+void ED_operatortypes_boids(void);
 void ED_operatortypes_pointcache(void);
 void ED_operatortypes_fluid(void);
 //void ED_keymap_pointcache(struct wmWindowManager *wm);
diff --git a/source/blender/editors/physics/physics_boids.c b/source/blender/editors/physics/physics_boids.c
new file mode 100644 (file)
index 0000000..2d3b110
--- /dev/null
@@ -0,0 +1,433 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Janne Karhu.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+//#include <stdlib.h>
+//#include <string.h>
+//
+#include "MEM_guardedalloc.h"
+
+#include "DNA_boid_types.h"
+#include "DNA_particle_types.h"
+//#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+//#include "DNA_material_types.h"
+//#include "DNA_texture_types.h"
+#include "DNA_scene_types.h"
+//#include "DNA_world_types.h"
+
+#include "BKE_boids.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+//#include "BKE_font.h"
+//#include "BKE_library.h"
+//#include "BKE_main.h"
+//#include "BKE_material.h"
+#include "BKE_particle.h"
+//#include "BKE_texture.h"
+//#include "BKE_utildefines.h"
+//#include "BKE_world.h"
+
+//#include "BLI_editVert.h"
+#include "BLI_listbase.h"
+//
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+//#include "ED_curve.h"
+//#include "ED_mesh.h"
+//
+//#include "buttons_intern.h"  // own include
+
+/************************ add/del boid rule operators *********************/
+static int boidrule_add_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob= ptr.id.data;
+       ParticleSettings *part;
+       int type= RNA_enum_get(op->ptr, "type");
+
+       BoidRule *rule;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       part = psys->part;
+
+       state = boid_get_current_state(part->boids);
+
+
+       for(rule=state->rules.first; rule; rule=rule->next)
+               rule->flag &= ~BOIDRULE_CURRENT;
+
+       rule = boid_new_rule(type);
+       rule->flag |= BOIDRULE_CURRENT;
+
+       BLI_addtail(&state->rules, rule);
+
+       psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET);
+       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidrule_add(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Add Boid Rule";
+       ot->description = "Add a boid rule to the current boid state.";
+       ot->idname= "BOID_OT_boidrule_add";
+       
+       /* api callbacks */
+       ot->invoke= WM_menu_invoke;
+       ot->exec= boidrule_add_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_enum(ot->srna, "type", boidrule_type_items, 0, "Type", "");
+}
+static int boidrule_del_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       BoidRule *rule;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       state = boid_get_current_state(psys->part->boids);
+
+       
+       for(rule=state->rules.first; rule; rule=rule->next) {
+               if(rule->flag & BOIDRULE_CURRENT) {
+                       BLI_remlink(&state->rules, rule);
+                       MEM_freeN(rule);
+                       break;
+               }
+
+       }
+       rule = state->rules.first;
+
+       if(rule)
+               rule->flag |= BOIDRULE_CURRENT;
+
+       DAG_scene_sort(scene);
+       psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET);
+
+       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidrule_del(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Remove Boid Rule";
+       ot->idname= "BOID_OT_boidrule_del";
+       
+       /* api callbacks */
+       ot->exec= boidrule_del_exec;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up/down boid rule operators *********************/
+static int boidrule_move_up_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       BoidRule *rule;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+       
+       state = boid_get_current_state(psys->part->boids);
+       for(rule = state->rules.first; rule; rule=rule->next) {
+               if(rule->flag & BOIDRULE_CURRENT && rule->prev) {
+                       BLI_remlink(&state->rules, rule);
+                       BLI_insertlink(&state->rules, rule->prev->prev, rule);
+
+                       psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+                       break;
+               }
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidrule_move_up(wmOperatorType *ot)
+{
+       ot->name= "Move Up Boid Rule";
+       ot->description= "Move boid rule up in the list.";
+       ot->idname= "BOID_OT_boidrule_move_up";
+
+       ot->exec= boidrule_move_up_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int boidrule_move_down_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       BoidRule *rule;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+       
+       state = boid_get_current_state(psys->part->boids);
+       for(rule = state->rules.first; rule; rule=rule->next) {
+               if(rule->flag & BOIDRULE_CURRENT && rule->next) {
+                       BLI_remlink(&state->rules, rule);
+                       BLI_insertlink(&state->rules, rule->next, rule);
+
+                       psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+                       break;
+               }
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidrule_move_down(wmOperatorType *ot)
+{
+       ot->name= "Move Down Boid Rule";
+       ot->description= "Move boid rule down in the list.";
+       ot->idname= "BOID_OT_boidrule_move_down";
+
+       ot->exec= boidrule_move_down_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+
+/************************ add/del boid state operators *********************/
+static int boidstate_add_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob= ptr.id.data;
+       ParticleSettings *part;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       part = psys->part;
+
+       for(state=part->boids->states.first; state; state=state->next)
+               state->flag &= ~BOIDSTATE_CURRENT;
+
+       state = boid_new_state(part->boids);
+       state->flag |= BOIDSTATE_CURRENT;
+
+       BLI_addtail(&part->boids->states, state);
+
+       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidstate_add(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Add Boid State";
+       ot->description = "Add a boid state to the particle system.";
+       ot->idname= "BOID_OT_boidstate_add";
+       
+       /* api callbacks */
+       ot->exec= boidstate_add_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+static int boidstate_del_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       ParticleSettings *part;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       part = psys->part;
+       
+       for(state=part->boids->states.first; state; state=state->next) {
+               if(state->flag & BOIDSTATE_CURRENT) {
+                       BLI_remlink(&part->boids->states, state);
+                       MEM_freeN(state);
+                       break;
+               }
+
+       }
+
+       /* there must be at least one state */
+       if(!part->boids->states.first) {
+               state = boid_new_state(part->boids);
+               BLI_addtail(&part->boids->states, state);       
+       }
+       else
+               state = part->boids->states.first;
+
+       state->flag |= BOIDSTATE_CURRENT;
+
+       DAG_scene_sort(scene);
+       psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET);
+
+       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidstate_del(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Remove Boid State";
+       ot->idname= "BOID_OT_boidstate_del";
+       
+       /* api callbacks */
+       ot->exec= boidstate_del_exec;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up/down boid state operators *********************/
+static int boidstate_move_up_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       BoidSettings *boids;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       boids = psys->part->boids;
+       
+       for(state = boids->states.first; state; state=state->next) {
+               if(state->flag & BOIDSTATE_CURRENT && state->prev) {
+                       BLI_remlink(&boids->states, state);
+                       BLI_insertlink(&boids->states, state->prev->prev, state);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+                       break;
+               }
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidstate_move_up(wmOperatorType *ot)
+{
+       ot->name= "Move Up Boid State";
+       ot->description= "Move boid state up in the list.";
+       ot->idname= "BOID_OT_boidstate_move_up";
+
+       ot->exec= boidstate_move_up_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int boidstate_move_down_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+       ParticleSystem *psys= ptr.data;
+       Object *ob = ptr.id.data;
+       BoidSettings *boids;
+       BoidState *state;
+
+       if(!psys || !psys->part || psys->part->phystype != PART_PHYS_BOIDS)
+               return OPERATOR_CANCELLED;
+
+       boids = psys->part->boids;
+       
+       for(state = boids->states.first; state; state=state->next) {
+               if(state->flag & BOIDSTATE_CURRENT && state->next) {
+                       BLI_remlink(&boids->states, state);
+                       BLI_insertlink(&boids->states, state->next, state);
+                       psys_flush_particle_settings(scene, psys->part, PSYS_RECALC_RESET);
+                       break;
+               }
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void BOID_OT_boidstate_move_down(wmOperatorType *ot)
+{
+       ot->name= "Move Down Boid State";
+       ot->description= "Move boid state down in the list.";
+       ot->idname= "BOID_OT_boidstate_move_down";
+
+       ot->exec= boidstate_move_down_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/*******************************************************************************/
+void ED_operatortypes_boids(void)
+{
+       WM_operatortype_append(BOID_OT_boidrule_add);
+       WM_operatortype_append(BOID_OT_boidrule_del);
+       WM_operatortype_append(BOID_OT_boidrule_move_up);
+       WM_operatortype_append(BOID_OT_boidrule_move_down);
+
+       WM_operatortype_append(BOID_OT_boidstate_add);
+       WM_operatortype_append(BOID_OT_boidstate_del);
+       WM_operatortype_append(BOID_OT_boidstate_move_up);
+       WM_operatortype_append(BOID_OT_boidstate_move_down);
+}
\ No newline at end of file
index 5a55c5fb7172e2881185679095a0dea8ad986506..b427742077aff44ce201d931fea6d1a3c99915f8 100644 (file)
@@ -93,6 +93,7 @@ void ED_spacetypes_init(void)
        ED_operatortypes_marker();
        ED_operatortypes_pointcache();
        ED_operatortypes_fluid();
+       ED_operatortypes_boids();
        
        ui_view2d_operatortypes();
        
index 20db9fce8f2e15d0a408879e115d107dd14a4226..adae52c1ce746fd1b8bf707aecb548d85446c2c1 100644 (file)
@@ -78,10 +78,10 @@ void OBJECT_OT_particle_system_add(struct wmOperatorType *ot);
 void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot);
 
 void PARTICLE_OT_new(struct wmOperatorType *ot);
-void PARTICLE_OT_new_keyed_target(struct wmOperatorType *ot);
-void PARTICLE_OT_remove_keyed_target(struct wmOperatorType *ot);
-void PARTICLE_OT_keyed_target_move_up(struct wmOperatorType *ot);
-void PARTICLE_OT_keyed_target_move_down(struct wmOperatorType *ot);
+void PARTICLE_OT_new_target(struct wmOperatorType *ot);
+void PARTICLE_OT_remove_target(struct wmOperatorType *ot);
+void PARTICLE_OT_target_move_up(struct wmOperatorType *ot);
+void PARTICLE_OT_target_move_down(struct wmOperatorType *ot);
 
 void SCENE_OT_render_layer_add(struct wmOperatorType *ot);
 void SCENE_OT_render_layer_remove(struct wmOperatorType *ot);
index 7dececd2679fd08b1dcafe74711b330dcaaba31a..8a9d2e9149b81ac7d733bda954e34da03dc46592 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "DNA_boid_types.h"
 #include "DNA_curve_types.h"
 #include "DNA_group_types.h"
 #include "DNA_object_types.h"
@@ -57,6 +58,7 @@
 #include "BLI_listbase.h"
 
 #include "RNA_access.h"
+#include "RNA_enum_types.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -623,7 +625,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *op)
        if(psys->part)
                part= psys_copy_settings(psys->part);
        else
-               part= psys_new_settings("PSys", bmain);
+               part= psys_new_settings("ParticleSettings", bmain);
 
        ob= ptr.id.data;
 
@@ -632,6 +634,8 @@ static int new_particle_settings_exec(bContext *C, wmOperator *op)
 
        psys->part = part;
 
+       psys_check_boid_data(psys);
+
        DAG_scene_sort(scene);
        DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
 
@@ -655,28 +659,28 @@ void PARTICLE_OT_new(wmOperatorType *ot)
 
 /********************** keyed particle target operators *********************/
 
-static int new_keyed_particle_target_exec(bContext *C, wmOperator *op)
+static int new_particle_target_exec(bContext *C, wmOperator *op)
 {
        Scene *scene = CTX_data_scene(C);
        PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
        ParticleSystem *psys= ptr.data;
        Object *ob = ptr.id.data;
 
-       KeyedParticleTarget *kpt;
+       ParticleTarget *pt;
 
        if(!psys)
                return OPERATOR_CANCELLED;
 
-       kpt = psys->keyed_targets.first;
-       for(; kpt; kpt=kpt->next)
-               kpt->flag &= ~KEYED_TARGET_CURRENT;
+       pt = psys->targets.first;
+       for(; pt; pt=pt->next)
+               pt->flag &= ~PTARGET_CURRENT;
 
-       kpt = MEM_callocN(sizeof(KeyedParticleTarget), "keyed particle target");
+       pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target");
 
-       kpt->flag |= KEYED_TARGET_CURRENT;
-       kpt->psys = 1;
+       pt->flag |= PTARGET_CURRENT;
+       pt->psys = 1;
 
-       BLI_addtail(&psys->keyed_targets, kpt);
+       BLI_addtail(&psys->targets, pt);
 
        DAG_scene_sort(scene);
        DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
@@ -686,44 +690,44 @@ static int new_keyed_particle_target_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_new_keyed_target(wmOperatorType *ot)
+void PARTICLE_OT_new_target(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "New Keyed Particle Target";
-       ot->idname= "PARTICLE_OT_new_keyed_target";
+       ot->name= "New Particle Target";
+       ot->idname= "PARTICLE_OT_new_target";
        
        /* api callbacks */
-       ot->exec= new_keyed_particle_target_exec;
+       ot->exec= new_particle_target_exec;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-static int remove_keyed_particle_target_exec(bContext *C, wmOperator *op)
+static int remove_particle_target_exec(bContext *C, wmOperator *op)
 {
        Scene *scene = CTX_data_scene(C);
        PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
        ParticleSystem *psys= ptr.data;
        Object *ob = ptr.id.data;
 
-       KeyedParticleTarget *kpt;
+       ParticleTarget *pt;
 
        if(!psys)
                return OPERATOR_CANCELLED;
 
-       kpt = psys->keyed_targets.first;
-       for(; kpt; kpt=kpt->next) {
-               if(kpt->flag & KEYED_TARGET_CURRENT) {
-                       BLI_remlink(&psys->keyed_targets, kpt);
-                       MEM_freeN(kpt);
+       pt = psys->targets.first;
+       for(; pt; pt=pt->next) {
+               if(pt->flag & PTARGET_CURRENT) {
+                       BLI_remlink(&psys->targets, pt);
+                       MEM_freeN(pt);
                        break;
                }
 
        }
-       kpt = psys->keyed_targets.last;
+       pt = psys->targets.last;
 
-       if(kpt)
-               kpt->flag |= KEYED_TARGET_CURRENT;
+       if(pt)
+               pt->flag |= PTARGET_CURRENT;
 
        DAG_scene_sort(scene);
        DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
@@ -733,37 +737,37 @@ static int remove_keyed_particle_target_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_remove_keyed_target(wmOperatorType *ot)
+void PARTICLE_OT_remove_target(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Remove Keyed Particle Target";
-       ot->idname= "PARTICLE_OT_remove_keyed_target";
+       ot->name= "Remove Particle Target";
+       ot->idname= "PARTICLE_OT_remove_target";
        
        /* api callbacks */
-       ot->exec= remove_keyed_particle_target_exec;
+       ot->exec= remove_particle_target_exec;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-/************************ move up modifier operator *********************/
+/************************ move up particle target operator *********************/
 
-static int keyed_target_move_up_exec(bContext *C, wmOperator *op)
+static int target_move_up_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
        ParticleSystem *psys= ptr.data;
        Object *ob = ptr.id.data;
-       KeyedParticleTarget *kpt;
+       ParticleTarget *pt;
 
        if(!psys)
                return OPERATOR_CANCELLED;
        
-       kpt = psys->keyed_targets.first;
-       for(; kpt; kpt=kpt->next) {
-               if(kpt->flag & KEYED_TARGET_CURRENT && kpt->prev) {
-                       BLI_remlink(&psys->keyed_targets, kpt);
-                       BLI_insertlink(&psys->keyed_targets, kpt->prev->prev, kpt);
+       pt = psys->targets.first;
+       for(; pt; pt=pt->next) {
+               if(pt->flag & PTARGET_CURRENT && pt->prev) {
+                       BLI_remlink(&psys->targets, pt);
+                       BLI_insertlink(&psys->targets, pt->prev->prev, pt);
 
                        DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
                        WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
@@ -774,35 +778,35 @@ static int keyed_target_move_up_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_keyed_target_move_up(wmOperatorType *ot)
+void PARTICLE_OT_target_move_up(wmOperatorType *ot)
 {
-       ot->name= "Move Up Keyed Target";
-       ot->description= "Move keyed particle target up in the list.";
-       ot->idname= "PARTICLE_OT_keyed_target_move_up";
+       ot->name= "Move Up Target";
+       ot->description= "Move particle target up in the list.";
+       ot->idname= "PARTICLE_OT_target_move_up";
 
-       ot->exec= keyed_target_move_up_exec;
+       ot->exec= target_move_up_exec;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-/************************ move down modifier operator *********************/
+/************************ move down particle target operator *********************/
 
-static int keyed_target_move_down_exec(bContext *C, wmOperator *op)
+static int target_move_down_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
        ParticleSystem *psys= ptr.data;
        Object *ob = ptr.id.data;
-       KeyedParticleTarget *kpt;
+       ParticleTarget *pt;
 
        if(!psys)
                return OPERATOR_CANCELLED;
-       kpt = psys->keyed_targets.first;
-       for(; kpt; kpt=kpt->next) {
-               if(kpt->flag & KEYED_TARGET_CURRENT && kpt->next) {
-                       BLI_remlink(&psys->keyed_targets, kpt);
-                       BLI_insertlink(&psys->keyed_targets, kpt->next, kpt);
+       pt = psys->targets.first;
+       for(; pt; pt=pt->next) {
+               if(pt->flag & PTARGET_CURRENT && pt->next) {
+                       BLI_remlink(&psys->targets, pt);
+                       BLI_insertlink(&psys->targets, pt->next, pt);
 
                        DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
                        WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
@@ -813,13 +817,13 @@ static int keyed_target_move_down_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_keyed_target_move_down(wmOperatorType *ot)
+void PARTICLE_OT_target_move_down(wmOperatorType *ot)
 {
-       ot->name= "Move Down Keyed Target";
-       ot->description= "Move keyed particle target down in the list.";
-       ot->idname= "PARTICLE_OT_keyed_target_move_down";
+       ot->name= "Move Down Target";
+       ot->description= "Move particle target down in the list.";
+       ot->idname= "PARTICLE_OT_target_move_down";
 
-       ot->exec= keyed_target_move_down_exec;
+       ot->exec= target_move_down_exec;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
index 0c1f735451aeb9f50defadf21d8bf4d0036bfbf9..72c479b2877301d2b3c1c0bd476078eda4276a38 100644 (file)
@@ -198,10 +198,10 @@ void buttons_operatortypes(void)
        WM_operatortype_append(OBJECT_OT_particle_system_remove);
 
        WM_operatortype_append(PARTICLE_OT_new);
-       WM_operatortype_append(PARTICLE_OT_new_keyed_target);
-       WM_operatortype_append(PARTICLE_OT_remove_keyed_target);
-       WM_operatortype_append(PARTICLE_OT_keyed_target_move_up);
-       WM_operatortype_append(PARTICLE_OT_keyed_target_move_down);
+       WM_operatortype_append(PARTICLE_OT_new_target);
+       WM_operatortype_append(PARTICLE_OT_remove_target);
+       WM_operatortype_append(PARTICLE_OT_target_move_up);
+       WM_operatortype_append(PARTICLE_OT_target_move_down);
 
        WM_operatortype_append(SCENE_OT_render_layer_add);
        WM_operatortype_append(SCENE_OT_render_layer_remove);
index 41da2c5a85b1a319c2b2d1b8aef9279b96d5b5f7..0f498810fb5fbeddaa6d3ca26f699fe21b4fbaea 100644 (file)
@@ -40,6 +40,7 @@
 #include "MTC_matrixops.h"
 
 #include "DNA_armature_types.h"
+#include "DNA_boid_types.h"
 #include "DNA_camera_types.h"
 #include "DNA_curve_types.h"
 #include "DNA_constraint_types.h" // for drawing constraint
@@ -3136,7 +3137,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
        Material *ma;
        float vel[3], imat[4][4];
        float timestep, pixsize=1.0, pa_size, r_tilt, r_length;
-       float pa_time, pa_birthtime, pa_dietime;
+       float pa_time, pa_birthtime, pa_dietime, pa_health;
        float cfra= bsystem_time(scene, ob,(float)CFRA,0.0);
        float ma_r=0.0f, ma_g=0.0f, ma_b=0.0f;
        int a, totpart, totpoint=0, totve=0, drawn, draw_as, totchild=0;
@@ -3362,6 +3363,10 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
                                pa_birthtime=pa->time;
                                pa_dietime = pa->dietime;
                                pa_size=pa->size;
+                               if(part->phystype==PART_PHYS_BOIDS)
+                                       pa_health = pa->boid->health;
+                               else
+                                       pa_health = -1.0;
 
 #if 0 // XXX old animation system      
                                if((part->flag&PART_ABS_TIME)==0){                      
@@ -3424,6 +3429,8 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
 
                                pa_size=psys_get_child_size(psys,cpa,cfra,0);
 
+                               pa_health = -1.0;
+
                                r_tilt = 2.0f * cpa->rand[2];
                                r_length = cpa->rand[1];
                        }
@@ -3506,9 +3513,19 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
                                                setlinestyle(0);
                                        }
 
-                                       if(part->draw&PART_DRAW_NUM && !(G.f & G_RENDER_SHADOW)){
+                                       if((part->draw&PART_DRAW_NUM || part->draw&PART_DRAW_HEALTH) && !(G.f & G_RENDER_SHADOW)){
+                                               strcpy(val, "");
+                                               
+                                               if(part->draw&PART_DRAW_NUM)
+                                                       sprintf(val, " %i", a);
+
+                                               if(part->draw&PART_DRAW_NUM && part->draw&PART_DRAW_HEALTH)
+                                                       sprintf(val, "%s:", val);
+
+                                               if(part->draw&PART_DRAW_HEALTH && a < totpart && part->phystype==PART_PHYS_BOIDS)
+                                                       sprintf(val, "%s %.2f", val, pa_health);
+
                                                /* in path drawing state.co is the end point */
-                                               sprintf(val," %i",a);
                                                view3d_particle_text_draw_add(state.co[0],  state.co[1],  state.co[2], val, 0);
                                        }
                                }
index 49a6fd4daf0f5039c25efde868a0d116c9a70f40..ab053c136ea16746095059ba5bc464690be8da11 100644 (file)
@@ -427,9 +427,14 @@ typedef struct CollisionModifierData {
 typedef struct SurfaceModifierData {
        ModifierData    modifier;
 
+       struct MVert *x; /* old position */
+       struct MVert *v; /* velocity */
+
        struct DerivedMesh *dm;
 
        struct BVHTreeFromMesh *bvhtree; /* bounding volume hierarchy of the mesh faces */
+
+       int cfra, numverts;
 } SurfaceModifierData;
 
 typedef enum {
index 32bfc58f56c7c97c89419b8d92ff08cde3194a79..6cf20cc25a2c22f3d5e4b3b1392cb15b8b778df3 100644 (file)
@@ -227,6 +227,7 @@ typedef struct SoftBody {
 #define PFIELD_HARMONIC        7
 #define PFIELD_CHARGE  8
 #define PFIELD_LENNARDJ        9
+#define PFIELD_BOID            10
 
 
 /* pd->flag: various settings */
index 0b3309bfc0c5c0f0c682b65d76a7d614df880a6c..2d8a186d3f4de4264725dc852adc3bfd6e6c8441 100644 (file)
@@ -61,13 +61,13 @@ typedef struct ChildParticle {
        float rand[3];
 } ChildParticle;
 
-typedef struct KeyedParticleTarget {
-       struct KeyedParticleTarget *next, *prev;
+typedef struct ParticleTarget {
+       struct ParticleTarget *next, *prev;
        struct Object *ob;
        int psys;
-       short flag, rt;
+       short flag, mode;
        float time, duration;
-} KeyedParticleTarget;
+} ParticleTarget;
 
 /* Everything that's non dynamic for a particle:                       */
 typedef struct ParticleData {
@@ -82,7 +82,9 @@ typedef struct ParticleData {
 
        ParticleKey *keys;              /* keyed states */
 
-       float i_rot[4],r_rot[4];/* initial & random values (i_rot should be removed as it's not used anymore)*/
+       struct BoidData *boid;  /* boids data */
+
+       float r_rot[4];                 /* random values */
        float r_ave[3],r_ve[3];
 
        float fuv[4], foffset;  /* coordinates on face/edge number "num" and depth along*/
@@ -91,13 +93,10 @@ typedef struct ParticleData {
        float time, lifetime;   /* dietime is not nescessarily time+lifetime as */
        float dietime;                  /* particles can die unnaturally (collision)    */
 
-       float bank;                             /* banking angle for boids */
-
        float size, sizemul;    /* size and multiplier so that we can update size when ever */
 
        int num;                                /* index to vert/edge/face */
        int num_dmcache;                /* index to derived mesh data (face) to avoid slow lookups */
-       int pad;
 
        int totkey;
        int bpi;                                /* softbody body point start index */
@@ -112,12 +111,14 @@ typedef struct ParticleSettings {
        ID id;
        struct AnimData *adt;
 
+       struct BoidSettings *boids;
+
        int flag;
        short type, from, distr;
        /* physics modes */
        short phystype, rotmode, avemode, reactevent;
        short draw, draw_as, draw_size, childtype;
-       short ren_as, rt2[3];
+       short ren_as, rt2;
        /* number of path segments, power of 2 except */
        short draw_step, ren_step;
        short hair_step, keys_step;
@@ -126,7 +127,7 @@ typedef struct ParticleSettings {
        short adapt_angle, adapt_pix;
 
        short disp, omat, interpolation, rotfrom, integrator;
-       short kink, kink_axis, nbetween, boidneighbours;
+       short kink, kink_axis;
 
        /* billboards */
        short bb_align, bb_uv_split, bb_anim, bb_split_offset;
@@ -154,7 +155,7 @@ typedef struct ParticleSettings {
        /* children */
        int child_nbr, ren_child_nbr;
        float parents, childsize, childrandsize;
-       float childrad, childflat, rt;
+       float childrad, childflat;
        /* clumping */
        float clumpfac, clumppow;
        /* kink */
@@ -174,11 +175,7 @@ typedef struct ParticleSettings {
        /* keyed particles */
        int keyed_loops;
 
-       /* boids */
-       float max_vel, max_lat_acc, max_tan_acc;
-       float average_vel, banking, max_bank, groundz;
-       float boidfac[8];
-       char boidrule[8];
+       float effector_weight[10];
 
        struct Group *dup_group;
        struct Group *eff_group;
@@ -212,12 +209,14 @@ typedef struct ParticleSystem{                            /* note, make sure all (runtime) are NULL's in
 
        struct ListBase effectors, reactevents; /* runtime */
 
-       struct ListBase keyed_targets;
+       struct ListBase targets;                                /* used for keyed and boid physics */
+
+       char name[32];                                                  /* particle system name */
        
        float imat[4][4];       /* used for duplicators */
-       float cfra;
+       float cfra, tree_frame;
        int seed;
-       int flag, totpart, totchild, totcached, totchildcache, rt;
+       int flag, totpart, totchild, totcached, totchildcache;
        short recalc, target_psys, totkeyed, softflag, bakespace, rt2;
 
        char bb_uvname[3][32];                                  /* billboard uv name */
@@ -230,6 +229,8 @@ typedef struct ParticleSystem{                              /* note, make sure all (runtime) are NULL's in
 
        /* point cache */
        struct PointCache *pointcache;
+
+       struct KDTree *tree;                                    /* used for interactions with self and other systems */
 }ParticleSystem;
 
 /* general particle maximums */
@@ -325,7 +326,7 @@ typedef struct ParticleSystem{                              /* note, make sure all (runtime) are NULL's in
 //#define PART_DRAW_PATH_LEN   2
 #define PART_DRAW_SIZE         4
 #define PART_DRAW_EMITTER      8       /* render emitter also */
-//#define PART_DRAW_HEALTH     16
+#define PART_DRAW_HEALTH       16
 #define PART_ABS_PATH_TIME  32
 //#define PART_DRAW_TRAIL              64
 #define PART_DRAW_BB_LOCK      128
@@ -460,26 +461,13 @@ typedef struct ParticleSystem{                            /* note, make sure all (runtime) are NULL's in
 #define PSYS_VG_ROT                    10
 #define PSYS_VG_EFFECTOR       11
 
-/* part->boidrules */
-#define BOID_TOT_RULES         8
-
-#define BOID_COLLIDE           0
-#define BOID_AVOID                     1
-#define BOID_CROWD                     2
-#define BOID_CENTER                    3
-#define BOID_AV_VEL                    4
-#define BOID_VEL_MATCH         5
-#define BOID_GOAL                      6
-#define BOID_LEVEL                     7
-
-/* psys->keyed_targets->flag */
-#define KEYED_TARGET_CURRENT   1
-#define KEYED_TARGET_VALID             2
-
+/* ParticleTarget->flag */
+#define PTARGET_CURRENT                1
+#define PTARGET_VALID          2
 
-//#define PSYS_INTER_CUBIC     0
-//#define PSYS_INTER_LINEAR    1
-//#define PSYS_INTER_CARDINAL  2
-//#define PSYS_INTER_BSPLINE   3
+/* ParticleTarget->mode */
+#define PTARGET_MODE_NEUTRAL   0
+#define PTARGET_MODE_FRIEND            1
+#define PTARGET_MODE_ENEMY             2
 
 #endif
index 08af6372d315af4c3432e62c13c36966459664e6..ffd164baaaf56628508cc1787dc2aa09326b307a 100644 (file)
@@ -132,6 +132,7 @@ char *includefiles[] = {
        // of makesdna.c (this file) as well
        "DNA_windowmanager_types.h",
        "DNA_anim_types.h",
+       "DNA_boid_types.h",
 
        // empty string to indicate end of includefiles
        ""
@@ -1154,4 +1155,5 @@ int main(int argc, char ** argv)
 #include "DNA_gpencil_types.h"
 #include "DNA_windowmanager_types.h"
 #include "DNA_anim_types.h"
+#include "DNA_boid_types.h"
 /* end of list */
index 90270483adb65891e24326a86710fac77de661a2..f3c2e95451d41d88cfc0fe804ab7dba79cb6a034 100644 (file)
@@ -221,7 +221,7 @@ extern StructRNA RNA_Key;
 extern StructRNA RNA_KeyboardSensor;
 extern StructRNA RNA_KeyingSet;
 extern StructRNA RNA_KeyingSetPath;
-extern StructRNA RNA_KeyedParticleTarget;
+extern StructRNA RNA_ParticleTarget;
 extern StructRNA RNA_KinematicConstraint;
 extern StructRNA RNA_Lamp;
 extern StructRNA RNA_LampSkySettings;
index 276f421c5867c33dbf5fb32a0203dec04f309542..2592a1340ecd41c936aa7b00e26b2cccf4a88886 100644 (file)
@@ -34,6 +34,7 @@ extern EnumPropertyItem space_type_items[];
 extern EnumPropertyItem region_type_items[];
 extern EnumPropertyItem modifier_type_items[];
 extern EnumPropertyItem constraint_type_items[];
+extern EnumPropertyItem boidrule_type_items[];
 
 extern EnumPropertyItem beztriple_handle_type_items[];
 extern EnumPropertyItem beztriple_interpolation_mode_items[];
index 85c266e3f27838bc4ef70945790cec568869a4e6..c8698ef57acdc43bd3a4eee9a93fd054c48e842a 100644 (file)
@@ -1901,6 +1901,7 @@ RNAProcessItem PROCESS_ITEMS[]= {
        {"rna_animation.c", NULL, RNA_def_animation},
        {"rna_actuator.c", NULL, RNA_def_actuator},
        {"rna_armature.c", NULL, RNA_def_armature},
+       {"rna_boid.c", NULL, RNA_def_boid},
        {"rna_brush.c", NULL, RNA_def_brush},
        {"rna_camera.c", NULL, RNA_def_camera},
        {"rna_cloth.c", NULL, RNA_def_cloth},
diff --git a/source/blender/makesrna/intern/rna_boid.c b/source/blender/makesrna/intern/rna_boid.c
new file mode 100644 (file)
index 0000000..af274d8
--- /dev/null
@@ -0,0 +1,615 @@
+/**
+ * $Id: rna_modifier.c 21514 2009-07-11 05:41:21Z aligorith $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <float.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "RNA_define.h"
+#include "RNA_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_boid_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "WM_types.h"
+
+EnumPropertyItem boidrule_type_items[] ={
+       {eBoidRuleType_Goal, "GOAL", 0, "Goal", "Go to assigned object or loudest assigned signal source."},
+       {eBoidRuleType_Avoid, "AVOID", 0, "Avoid", "Get away from assigned object or loudest assigned signal source."},
+       {eBoidRuleType_AvoidCollision, "AVOID_COLLISION", 0, "Avoid Collision", "Monoeuver to avoid collisions with other boids and deflector objects in near future."},
+       {eBoidRuleType_Separate, "SEPARATE", 0, "Separate", "Keep from going through other boids."},
+       {eBoidRuleType_Flock, "FLOCK", 0, "Flock", "Move to center of neighbors and match their velocity."},
+       {eBoidRuleType_FollowLeader, "FOLLOW_LEADER", 0, "Follow Leader", "Follow a boid or assigned object."},
+       {eBoidRuleType_AverageSpeed, "AVERAGE_SPEED", 0, "Average Speed", "Maintain speed, flight level or wander."},
+       {eBoidRuleType_Fight, "FIGHT", 0, "Fight", "Go to closest enemy and attack when in range."},
+       //{eBoidRuleType_Protect, "PROTECT", 0, "Protect", "Go to enemy closest to target and attack when in range."},
+       //{eBoidRuleType_Hide, "HIDE", 0, "Hide", "Find a deflector move to it's other side from closest enemy."},
+       //{eBoidRuleType_FollowPath, "FOLLOW_PATH", 0, "Follow Path", "Move along a assigned curve or closest curve in a group."},
+       //{eBoidRuleType_FollowWall, "FOLLOW_WALL", 0, "Follow Wall", "Move next to a deflector object's in direction of it's tangent."},
+       {0, NULL, 0, NULL, NULL}};
+
+EnumPropertyItem boidruleset_type_items[] ={
+       {eBoidRulesetType_Fuzzy, "FUZZY", 0, "Fuzzy", "Rules are gone through top to bottom. Only the first rule that effect above fuzziness threshold is evaluated."},
+       {eBoidRulesetType_Random, "RANDOM", 0, "Random", "A random rule is selected for each boid."},
+       {eBoidRulesetType_Average, "AVERAGE", 0, "Average", "All rules are averaged."},
+       {0, NULL, 0, NULL, NULL}};
+
+
+#ifdef RNA_RUNTIME
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_particle.h"
+
+static void rna_Boids_reset(bContext *C, PointerRNA *ptr)
+{
+       Scene *scene = CTX_data_scene(C);
+       ParticleSettings *part;
+
+       if(ptr->type==&RNA_ParticleSystem) {
+               ParticleSystem *psys = (ParticleSystem*)ptr->data;
+               Object *ob = psys_find_object(scene, psys);
+               
+               psys->recalc = PSYS_RECALC_RESET;
+
+               if(ob) {
+                       DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+               }
+       }
+       else {
+               part = ptr->id.data;
+               psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET);
+       }
+}
+static void rna_Boids_reset_deps(bContext *C, PointerRNA *ptr)
+{
+       Scene *scene = CTX_data_scene(C);
+       ParticleSettings *part;
+
+       if(ptr->type==&RNA_ParticleSystem) {
+               ParticleSystem *psys = (ParticleSystem*)ptr->data;
+               Object *ob = psys_find_object(scene, psys);
+               
+               psys->recalc = PSYS_RECALC_RESET;
+
+               if(ob) {
+                       DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+               }
+       }
+       else {
+               part = ptr->id.data;
+               psys_flush_particle_settings(scene, part, PSYS_RECALC_RESET);
+               DAG_scene_sort(scene);
+       }
+}
+
+static StructRNA* rna_BoidRule_refine(struct PointerRNA *ptr)
+{
+       BoidRule *rule= (BoidRule*)ptr->data;
+
+       switch(rule->type) {
+               case eBoidRuleType_Goal:
+                       return &RNA_BoidRuleGoal;
+               case eBoidRuleType_Avoid:
+                       return &RNA_BoidRuleAvoid;
+               case eBoidRuleType_AvoidCollision:
+                       return &RNA_BoidRuleAvoidCollision;
+               case eBoidRuleType_FollowLeader:
+                       return &RNA_BoidRuleFollowLeader;
+               case eBoidRuleType_AverageSpeed:
+                       return &RNA_BoidRuleAverageSpeed;
+               case eBoidRuleType_Fight:
+                       return &RNA_BoidRuleFight;
+               default:
+                       return &RNA_BoidRule;
+       }
+}
+
+static char *rna_BoidRule_path(PointerRNA *ptr)
+{
+       return BLI_sprintfN("rules[%s]", ((BoidRule*)ptr->data)->name);  // XXX not unique
+}
+
+static PointerRNA rna_BoidState_active_boid_rule_get(PointerRNA *ptr)
+{
+       BoidState *state= (BoidState*)ptr->data;
+       BoidRule *rule = (BoidRule*)state->rules.first;
+
+       for(; rule; rule=rule->next) {
+               if(rule->flag & BOIDRULE_CURRENT)
+                       return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, rule);
+       }
+       return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, NULL);
+}
+static void rna_BoidState_active_boid_rule_index_range(PointerRNA *ptr, int *min, int *max)
+{
+       BoidState *state= (BoidState*)ptr->data;
+       *min= 0;
+       *max= BLI_countlist(&state->rules)-1;
+       *max= MAX2(0, *max);
+}
+
+static int rna_BoidState_active_boid_rule_index_get(PointerRNA *ptr)
+{
+       BoidState *state= (BoidState*)ptr->data;
+       BoidRule *rule = (BoidRule*)state->rules.first;
+       int i=0;
+
+       for(; rule; rule=rule->next, i++) {
+               if(rule->flag & BOIDRULE_CURRENT)
+                       return i;
+       }
+       return 0;
+}
+
+static void rna_BoidState_active_boid_rule_index_set(struct PointerRNA *ptr, int value)
+{
+       BoidState *state= (BoidState*)ptr->data;
+       BoidRule *rule = (BoidRule*)state->rules.first;
+       int i=0;
+
+       for(; rule; rule=rule->next, i++) {
+               if(i==value)
+                       rule->flag |= BOIDRULE_CURRENT;
+               else
+                       rule->flag &= ~BOIDRULE_CURRENT;
+       }
+}
+
+static PointerRNA rna_BoidSettings_active_boid_state_get(PointerRNA *ptr)
+{
+       BoidSettings *boids= (BoidSettings*)ptr->data;
+       BoidState *state = (BoidState*)boids->states.first;
+
+       for(; state; state=state->next) {
+               if(state->flag & BOIDSTATE_CURRENT)
+                       return rna_pointer_inherit_refine(ptr, &RNA_BoidState, state);
+       }
+       return rna_pointer_inherit_refine(ptr, &RNA_BoidState, NULL);
+}
+static void rna_BoidSettings_active_boid_state_index_range(PointerRNA *ptr, int *min, int *max)
+{
+       BoidSettings *boids= (BoidSettings*)ptr->data;
+       *min= 0;
+       *max= BLI_countlist(&boids->states)-1;
+       *max= MAX2(0, *max);
+}
+
+static int rna_BoidSettings_active_boid_state_index_get(PointerRNA *ptr)
+{
+       BoidSettings *boids= (BoidSettings*)ptr->data;
+       BoidState *state = (BoidState*)boids->states.first;
+       int i=0;
+
+       for(; state; state=state->next, i++) {
+               if(state->flag & BOIDSTATE_CURRENT)
+                       return i;
+       }
+       return 0;
+}
+
+static void rna_BoidSettings_active_boid_state_index_set(struct PointerRNA *ptr, int value)
+{
+       BoidSettings *boids= (BoidSettings*)ptr->data;
+       BoidState *state = (BoidState*)boids->states.first;
+       int i=0;
+
+       for(; state; state=state->next, i++) {
+               if(i==value)
+                       state->flag |= BOIDSTATE_CURRENT;
+               else
+                       state->flag &= ~BOIDSTATE_CURRENT;
+       }
+}
+
+#else
+
+static void rna_def_boidrule_goal(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "BoidRuleGoal", "BoidRule");
+       RNA_def_struct_ui_text(srna, "Goal", "");
+       RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid");
+
+       prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "ob");
+       RNA_def_property_flag(prop, PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Object", "Goal object.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps");
+
+       prop= RNA_def_property(srna, "predict", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT);
+       RNA_def_property_ui_text(prop, "Predict", "Predict target movement.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_avoid(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "BoidRuleAvoid", "BoidRule");
+       RNA_def_struct_ui_text(srna, "Avoid", "");
+       RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid");
+
+       prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "ob");
+       RNA_def_property_flag(prop, PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Object", "Object to avoid.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps");
+
+       prop= RNA_def_property(srna, "predict", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT);
+       RNA_def_property_ui_text(prop, "Predict", "Predict target movement.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+
+       prop= RNA_def_property(srna, "fear_factor", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_text(prop, "Fear factor", "Avoid object if danger from it is above this threshol.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_avoid_collision(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "BoidRuleAvoidCollision", "BoidRule");
+       RNA_def_struct_ui_text(srna, "Avoid Collision", "");
+
+       prop= RNA_def_property(srna, "boids", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_BOIDS);
+       RNA_def_property_ui_text(prop, "Boids", "Avoid collision with other boids.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+
+       prop= RNA_def_property(srna, "deflectors", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_DEFLECTORS);
+       RNA_def_property_ui_text(prop, "Deflectors", "Avoid collision with deflector objects.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+
+       prop= RNA_def_property(srna, "look_ahead", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_text(prop, "Look ahead", "Time to look ahead in seconds.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_follow_leader(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "BoidRuleFollowLeader", "BoidRule");
+       RNA_def_struct_ui_text(srna, "Follow Leader", "");
+
+       prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "ob");
+       RNA_def_property_flag(prop, PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Object", "Follow this object instead of a boid.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset_deps");
+
+       prop= RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_text(prop, "Distance", "Distance behind leader to follow.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+
+       prop= RNA_def_property(srna, "queue_size", PROP_INT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0f, 100.0f);
+       RNA_def_property_ui_text(prop, "Queue Size", "How many boids in a line.");
+       RNA_def_property_update(prop, NC_OBJECT|ND_PARTICLE, "rna_Boids_reset");
+
+       prop= RNA_d