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':
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
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")
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")
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
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"
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"
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)
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()
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()
--- /dev/null
+/* 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
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];
}
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);
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);
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);
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 ------------ */
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);
--- /dev/null
+/* 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
#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"
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");
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;
}
}
}
}
- 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");
+ }
}
}
}
}
}
}
-
/* flag all objects that need recalc, for changes in time for example */
void DAG_scene_update_flags(Scene *scene, unsigned int lay)
{
VecAddf(field,field,mag_vec);
break;
}
+ case PFIELD_BOID:
+ /* Boid field is handled completely in boids code. */
+ break;
}
}
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;
}
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;
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);
#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"
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;
}
}
}
}
+ 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;
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;
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;
}
#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"
#include "BKE_anim.h"
+#include "BKE_boids.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_lattice.h"
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)
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;
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);
}
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);
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));
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;
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;
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;
}
#include "MEM_guardedalloc.h"
+#include "DNA_boid_types.h"
#include "DNA_particle_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#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"
#include "BLI_threads.h"
#include "BKE_anim.h"
+#include "BKE_boids.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_collision.h"
#include "BKE_displist.h"
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) {
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);
}
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;
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;
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;
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;
}
}
{
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) {
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)
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;
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);
/************************************************/
/* 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];
/* 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)
}
}
+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)
{
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){
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);
}
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);
}
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;
/* 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. */
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;
/* 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);
}
}
/************************************************/
-/* 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){
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 */
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 */
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;
}
}
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){
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);
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)
{
/* 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
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;
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;
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;
+}
#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"
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;
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)
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);
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;
direct_link_pointcache(fd, sb->pointcache);
}
- link_list(fd, &psys->keyed_targets);
+ link_list(fd, &psys->targets);
psys->edit = 0;
psys->free_edit = NULL;
psys->pointcache= newdataadr(fd, psys->pointcache);
if(psys->pointcache)
direct_link_pointcache(fd, psys->pointcache);
+
+ psys->tree = NULL;
}
return;
}
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;
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);
#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"
}
}
+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
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;
}
static void write_particlesystems(WriteData *wd, ListBase *particles)
{
ParticleSystem *psys= particles->first;
- KeyedParticleTarget *kpt;
+ ParticleTarget *pt;
int a;
for(; psys; psys=psys->next) {
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);
/* operators */
+void ED_operatortypes_boids(void);
void ED_operatortypes_pointcache(void);
void ED_operatortypes_fluid(void);
//void ED_keymap_pointcache(struct wmWindowManager *wm);
--- /dev/null
+/**
+ * $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
ED_operatortypes_marker();
ED_operatortypes_pointcache();
ED_operatortypes_fluid();
+ ED_operatortypes_boids();
ui_view2d_operatortypes();
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);
#include "MEM_guardedalloc.h"
+#include "DNA_boid_types.h"
#include "DNA_curve_types.h"
#include "DNA_group_types.h"
#include "DNA_object_types.h"
#include "BLI_listbase.h"
#include "RNA_access.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
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;
psys->part = part;
+ psys_check_boid_data(psys);
+
DAG_scene_sort(scene);
DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
/********************** 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);
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);
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);
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);
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;
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);
#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
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;
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){
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];
}
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);
}
}
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 {
#define PFIELD_HARMONIC 7
#define PFIELD_CHARGE 8
#define PFIELD_LENNARDJ 9
+#define PFIELD_BOID 10
/* pd->flag: various settings */
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 {
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*/
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 */
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;
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;
/* children */
int child_nbr, ren_child_nbr;
float parents, childsize, childrandsize;
- float childrad, childflat, rt;
+ float childrad, childflat;
/* clumping */
float clumpfac, clumppow;
/* kink */
/* 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;
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 */
/* point cache */
struct PointCache *pointcache;
+
+ struct KDTree *tree; /* used for interactions with self and other systems */
}ParticleSystem;
/* general particle maximums */
//#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
#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
// 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
""
#include "DNA_gpencil_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_anim_types.h"
+#include "DNA_boid_types.h"
/* end of list */
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;
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[];
{"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},
--- /dev/null
+/**
+ * $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