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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h1
-rw-r--r--source/blender/blenkernel/BKE_bad_level_calls.h7
-rw-r--r--source/blender/blenkernel/BKE_blender.h2
-rw-r--r--source/blender/blenkernel/BKE_displist.h2
-rw-r--r--source/blender/blenkernel/BKE_global.h8
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_modifier.h13
-rw-r--r--source/blender/blenkernel/BKE_particle.h254
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h48
-rw-r--r--source/blender/blenkernel/BKE_softbody.h13
-rw-r--r--source/blender/blenkernel/BKE_utildefines.h4
-rw-r--r--source/blender/blenkernel/bad_level_call_stubs/stubs.c7
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c18
-rw-r--r--source/blender/blenkernel/intern/anim.c268
-rw-r--r--source/blender/blenkernel/intern/blender.c2
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c4
-rw-r--r--source/blender/blenkernel/intern/customdata.c87
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c66
-rw-r--r--source/blender/blenkernel/intern/displist.c4
-rw-r--r--source/blender/blenkernel/intern/effect.c4
-rw-r--r--source/blender/blenkernel/intern/ipo.c80
-rw-r--r--source/blender/blenkernel/intern/library.c25
-rw-r--r--source/blender/blenkernel/intern/modifier.c1281
-rw-r--r--source/blender/blenkernel/intern/object.c120
-rw-r--r--source/blender/blenkernel/intern/particle.c2978
-rw-r--r--source/blender/blenkernel/intern/particle_system.c4371
-rw-r--r--source/blender/blenkernel/intern/pointcache.c185
-rw-r--r--source/blender/blenkernel/intern/scene.c15
-rw-r--r--source/blender/blenkernel/intern/softbody.c453
29 files changed, 10000 insertions, 321 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 1852dc72a40..29fc1438c47 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -70,6 +70,7 @@ struct DerivedMesh {
CustomData vertData, edgeData, faceData;
int numVertData, numEdgeData, numFaceData;
int needsFree; /* checked on ->release, is set to 0 for cached results */
+ int deformedOnly; /* set by modifier stack if only deformed from original */
/* Misc. Queries */
diff --git a/source/blender/blenkernel/BKE_bad_level_calls.h b/source/blender/blenkernel/BKE_bad_level_calls.h
index b942add80e2..7a838ff7614 100644
--- a/source/blender/blenkernel/BKE_bad_level_calls.h
+++ b/source/blender/blenkernel/BKE_bad_level_calls.h
@@ -231,5 +231,12 @@ struct MeshDeformModifierData;
void harmonic_coordinates_bind(struct MeshDeformModifierData *mmd,
float (*vertexcos)[3], int totvert, float cagemat[][4]);
+/* particle.c */
+struct ParticleSystem;
+
+void PE_free_particle_edit(struct ParticleSystem *psys);
+void PE_get_colors(char sel[4], char nosel[4]);
+void PE_recalc_world_cos(struct Object *ob, struct ParticleSystem *psys);
+
#endif
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index 55a542f51d5..5cd905d07ac 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -44,7 +44,7 @@ struct ListBase;
struct MemFile;
#define BLENDER_VERSION 245
-#define BLENDER_SUBVERSION 7
+#define BLENDER_SUBVERSION 8
#define BLENDER_MINVERSION 240
#define BLENDER_MINSUBVERSION 0
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index 979ed31fb20..2dc4de32132 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -123,5 +123,7 @@ void filldisplist(struct ListBase *dispbase, struct ListBase *to);
void fastshade_free_render(void);
+float calc_taper(struct Object *taperobj, int cur, int tot);
+
#endif
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 949b6c959b9..e71145e8d79 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -187,6 +187,7 @@ typedef struct Global {
/*#endif*/
#define G_DRAWSHARP (1 << 28) /* draw edges with the sharp flag */
#define G_SCULPTMODE (1 << 29)
+#define G_PARTICLEEDIT (1 << 30)
#define G_AUTOMATKEYS (1 << 30)
#define G_HIDDENHANDLES (1 << 31) /* used for curves only */
@@ -243,9 +244,10 @@ typedef struct Global {
#define B_ENDIAN 0
/* G.moving, signals drawing in (3d) window to denote transform */
-#define G_TRANSFORM_OBJ 1
-#define G_TRANSFORM_EDIT 2
-#define G_TRANSFORM_MANIP 4
+#define G_TRANSFORM_OBJ 1
+#define G_TRANSFORM_EDIT 2
+#define G_TRANSFORM_MANIP 4
+#define G_TRANSFORM_PARTICLE 8
/* G.special1 */
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 6e0f2fdb284..2cfa1dc5cc4 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -78,6 +78,7 @@ typedef struct Main {
ListBase action;
ListBase nodetree;
ListBase brush;
+ ListBase particle;
} Main;
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 297443b883d..24da941db90 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -59,6 +59,12 @@ typedef enum {
eModifierTypeType_Constructive,
eModifierTypeType_Nonconstructive,
+
+ /* both deformVerts & applyModifier are valid calls
+ * used for particles modifier that doesn't actually modify the object
+ * unless it's a mesh and can be exploded -> curve can also emit particles
+ */
+ eModifierTypeType_DeformOrConstruct
} ModifierTypeType;
typedef enum {
@@ -81,8 +87,8 @@ typedef enum {
eModifierTypeFlag_RequiresOriginalData = (1<<5),
} ModifierTypeFlag;
-typedef void (*ObjectWalkFunc)(void *userData, Object *ob, Object **obpoin);
-typedef void (*IDWalkFunc)(void *userData, Object *ob, ID **idpoin);
+typedef void (*ObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin);
+typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin);
typedef struct ModifierTypeInfo {
/* The user visible name for this modifier */
@@ -277,11 +283,14 @@ int modifiers_getCageIndex(struct Object *ob,
int *lastPossibleCageIndex_r);
int modifiers_isSoftbodyEnabled(struct Object *ob);
+int modifiers_isParticleEnabled(struct Object *ob);
struct Object *modifiers_isDeformedByArmature(struct Object *ob);
struct Object *modifiers_isDeformedByLattice(struct Object *ob);
int modifiers_usesArmature(struct Object *ob, struct bArmature *arm);
int modifiers_isDeformed(struct Object *ob);
+int modifiers_indexInObject(struct Object *ob, struct ModifierData *md);
+
/* Calculates and returns a linked list of CustomDataMasks indicating the
* data required by each modifier in the stack pointed to by md for correct
* evaluation, assuming the data indicated by dataMask is required at the
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
new file mode 100644
index 00000000000..afed219dbc6
--- /dev/null
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -0,0 +1,254 @@
+/* BKE_particle.h
+ *
+ *
+ * $Id: BKE_particle.h $
+ *
+ * ***** BEGIN GPL/BL DUAL 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. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License. See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * 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) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+#ifndef BKE_PARTICLE_H
+#define BKE_PARTICLE_H
+
+#include "DNA_particle_types.h"
+#include "DNA_object_types.h"
+
+struct ParticleSystemModifierData;
+struct ParticleSystem;
+struct ParticleKey;
+struct HairKey;
+
+struct Main;
+struct Group;
+struct Object;
+struct DerivedMesh;
+struct ModifierData;
+struct MTFace;
+struct MFace;
+struct MVert;
+struct IpoCurve;
+struct LinkNode;
+struct KDTree;
+
+typedef struct ParticleEffectorCache {
+ struct ParticleEffectorCache *next, *prev;
+ struct Object *ob;
+
+ /* precalculated variables for guides */
+ float firstloc[4], firstdir[3];
+ float *distances;
+ float *locations;
+ /* precalculated variables for deflection */
+ float ob_minmax[6];
+ float *face_minmax;
+ float *vert_cos;
+ /* precalculated variables for boids */
+ struct KDTree *tree;
+
+ short type, psys_nbr;
+
+ struct Object obcopy; /* for restoring transformation data */
+} ParticleEffectorCache;
+
+typedef struct ParticleReactEvent {
+ struct ParticleReactEvent *next, *prev;
+ int event, pa_num;
+ Object *ob;
+ struct ParticleSystem *psys;
+ struct ParticleKey state;
+
+ float time, size;
+}ParticleReactEvent;
+
+typedef struct ParticleTexture{
+ float ivel; /* used in reset */
+ float time, life, exist, size; /* used in init */
+ float pvel[3]; /* used in physics */
+ float length, clump, kink; /* 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];
+ float length2;
+} ParticleSeam;
+
+typedef struct ParticleCacheKey{
+ float co[3];
+ float vel[3];
+ float rot[4];
+ float col[3];
+ int steps;
+} ParticleCacheKey;
+
+typedef struct ParticleEditKey{
+ float *co;
+ float *vel;
+ float *rot;
+ float *time;
+
+ float world_co[3];
+ float length;
+ short flag;
+} ParticleEditKey;
+
+typedef struct ParticleUndo {
+ struct ParticleUndo *next, *prev;
+ struct ParticleEditKey **keys;
+ struct KDTree *emitter_field;
+ struct ParticleData *particles;
+ float *emitter_cosnos;
+ int totpart, totkeys;
+ char name[64];
+} ParticleUndo;
+
+typedef struct ParticleEdit{
+ ListBase undo;
+ struct ParticleUndo *curundo;
+ struct KDTree *emitter_field;
+ ParticleEditKey **keys;
+ int *mirror_cache;
+ float *emitter_cosnos;
+
+ int totkeys;
+} ParticleEdit;
+
+/* ----------- functions needed outside particlesystem ---------------- */
+/* particle.c */
+int count_particles(struct ParticleSystem *psys);
+int count_particles_mod(struct ParticleSystem *psys, int totgr, int cur);
+int psys_count_keys(struct ParticleSystem *psys);
+char *psys_menu_string(struct Object *ob, int for_sb);
+
+struct ParticleSystem *psys_get_current(struct Object *ob);
+short psys_get_current_num(struct Object *ob);
+//struct ParticleSystem *psys_get(struct Object *ob, int index);
+struct ParticleData *psys_get_selected_particle(struct ParticleSystem *psys, int *index);
+struct ParticleKey *psys_get_selected_key(struct ParticleSystem *psys, int pa_index, int *key_index);
+void psys_change_act(void *ob_v, void *act_v);
+struct Object *psys_get_lattice(struct Object *ob, struct ParticleSystem *psys);
+void psys_disable_all(struct Object *ob);
+void psys_enable_all(struct Object *ob);
+int psys_ob_has_hair(struct Object *ob);
+int psys_in_edit_mode(struct ParticleSystem *psys);
+
+void psys_free_settings(struct ParticleSettings *part);
+void free_child_path_cache(struct ParticleSystem *psys);
+void psys_free_path_cache(struct ParticleSystem *psys);
+void free_hair(struct ParticleSystem *psys);
+void free_keyed_keys(struct ParticleSystem *psys);
+void psys_free(struct Object * ob, struct ParticleSystem * psys);
+
+void clear_particles_from_cache(struct Object *ob, struct ParticleSystem *psys, int cfra);
+//void psys_remove_from_particle_list(struct Object *ob, short nbr, struct ParticleSystem *psys);
+
+void psys_interpolate_uvs(struct MTFace *tface, int quad, float *uv, float *uvco);
+
+void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time);
+
+void psys_particle_on_emitter(struct Object *ob, struct ParticleSystemModifierData *psmd, int distr, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan);
+struct ParticleSystemModifierData *psys_get_modifier(struct Object *ob, struct ParticleSystem *psys);
+
+struct ParticleSettings *psys_new_settings(char *name, struct Main *main);
+struct ParticleSettings *psys_copy_settings(struct ParticleSettings *part);
+void psys_flush_settings(struct ParticleSettings *part, int event, int hair_recalc);
+
+void psys_find_parents(struct Object *ob, struct ParticleSystemModifierData *psmd, struct ParticleSystem *psys);
+
+void psys_cache_paths(struct Object *ob, struct ParticleSystem *psys, float cfra, int editupdate);
+void psys_cache_child_paths(struct Object *ob, struct ParticleSystem *psys, float cfra, int editupdate);
+int do_guide(struct ParticleKey *state, int pa_num, float time, struct ListBase *lb);
+float psys_get_size(struct Object *ob, struct Material *ma, struct ParticleSystemModifierData *psmd, struct IpoCurve *icu_size, struct ParticleSystem *psys, struct ParticleSettings *part, struct ParticleData *pa, float *vg_size);
+float psys_get_timestep(struct ParticleSettings *part);
+float psys_get_child_time(struct ParticleSystem *psys, int child_nbr, float cfra);
+float psys_get_child_size(struct ParticleSystem *psys, int child_nbr, float cfra, float *pa_time);
+void psys_get_particle_on_path(struct Object *ob, struct ParticleSystem *psys, int pa_num, struct ParticleKey *state, int vel);
+int psys_get_particle_state(struct Object *ob, struct ParticleSystem *psys, int p, struct ParticleKey *state, int always);
+
+/* particle_system.c */
+int 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_init_effectors(struct Object *obsrc, struct Group *group, struct ParticleSystem *psys);
+void psys_end_effectors(struct ParticleSystem *psys);
+
+void particle_system_update(struct Object *ob, struct ParticleSystem *psys);
+
+/* ----------- functions needed only inside particlesystem ------------ */
+/* particle.c */
+void psys_key_to_object(struct Object *ob, struct ParticleKey *key, float imat[][4]);
+//void psys_key_to_geometry(struct DerivedMesh *dm, struct ParticleData *pa, struct ParticleKey *key);
+//void psys_key_from_geometry(struct DerivedMesh *dm, struct ParticleData *pa, struct ParticleKey *key);
+//void psys_face_mat(struct DerivedMesh *dm, struct ParticleData *pa, float mat[][4]);
+void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float *vec);
+//void psys_vec_rot_from_face(struct DerivedMesh *dm, struct ParticleData *pa, float *vec);
+void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[][4]);
+void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[][4]);
+
+float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup);
+void psys_get_texture(struct Object *ob, struct Material *ma, struct ParticleSystemModifierData *psmd, struct ParticleSystem *psys, struct ParticleData *pa, struct ParticleTexture *ptex, int event);
+void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface, float *uv, float *vec, float *nor, float *utan, float *vtan);
+float psys_interpolate_value_from_verts(struct DerivedMesh *dm, short from, int index, float *fw, float *values);
+void psys_get_from_key(struct ParticleKey *key, float *loc, float *vel, float *rot, float *time);
+
+int psys_intersect_dm(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 psys_particle_on_dm(struct Object *ob, struct DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan);
+
+/* particle_system.c */
+void initialize_particle(struct ParticleData *pa, int p, struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd);
+void reset_particle(struct ParticleData *pa, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd, struct Object *ob, float dtime, float cfra, float *vg_vel, float *vg_tan, float *vg_rot);
+
+int psys_particle_dm_face_lookup(struct Object *ob, struct DerivedMesh *dm, int index, float *fw, struct LinkNode *node);
+
+/* ParticleEffectorCache->type */
+#define PSYS_EC_EFFECTOR 1
+#define PSYS_EC_DEFLECT 2
+#define PSYS_EC_PARTICLE 4
+#define PSYS_EC_REACTOR 8
+
+/* ParticleEditKey->flag */
+#define PEK_SELECT 1
+#define PEK_TO_SELECT 2
+#define PEK_TAG 4
+#define PEK_HIDE 8
+
+/* index_dmcache */
+#define DMCACHE_NOTFOUND -1
+#define DMCACHE_ISCHILD -2
+
+#endif
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
new file mode 100644
index 00000000000..46d240a4183
--- /dev/null
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -0,0 +1,48 @@
+/*
+*
+* ***** 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) 2006 Blender Foundation.
+* All rights reserved.
+*
+* The Original Code is: all of this file.
+*
+* Contributor(s): Campbell Barton <ideasman42@gmail.com>
+*
+* ***** END GPL LICENSE BLOCK *****
+*/
+
+#ifndef BKE_POINTCACHE_H
+#define BKE_POINTCACHE_H
+
+#include "DNA_ID.h"
+
+/* options for clearing pointcache - used for BKE_ptcache_id_clear
+ Before and after are non inclusive (they wont remove the cfra) */
+#define PTCACHE_CLEAR_ALL 0
+#define PTCACHE_CLEAR_FRAME 1
+#define PTCACHE_CLEAR_BEFORE 2
+#define PTCACHE_CLEAR_AFTER 3
+
+#define PTCACHE_EXT ".bphys"
+#define PTCACHE_PATH "//pointcache/"
+
+int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_index, short do_path, short do_ext);
+FILE * BKE_ptcache_id_fopen(struct ID *id, char mode, int cfra, int stack_index);
+void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 15200bf46f8..0f58b93730a 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -37,12 +37,25 @@
struct Object;
struct SoftBody;
+typedef struct BodyPoint {
+ float origS[3], origE[3], origT[3], pos[3], vec[3], force[3];
+ float goal;
+ float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */
+ int nofsprings; int *springs;
+ float choke;
+ float colball;
+ short flag;
+ char octantflag;
+} BodyPoint;
+
/* allocates and initializes general main data */
extern struct SoftBody *sbNew(void);
/* frees internal data and softbody itself */
extern void sbFree(struct SoftBody *sb);
+extern void softbody_clear_cache(struct Object *ob, float framenr);
+
/* do one simul step, reading and writing vertex locs from given array */
extern void sbObjectStep(struct Object *ob, float framnr, float (*vertexCos)[3], int numVerts);
diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h
index a30617a2f15..f3f66190c31 100644
--- a/source/blender/blenkernel/BKE_utildefines.h
+++ b/source/blender/blenkernel/BKE_utildefines.h
@@ -59,6 +59,10 @@
#define ELEM8(a, b, c, d, e, f, g, h, i) ( ELEM4(a, b, c, d, e) || ELEM4(a, f, g, h, i) )
#define ELEM9(a, b, c, d, e, f, g, h, i, j) ( ELEM4(a, b, c, d, e) || ELEM5(a, f, g, h, i, j) )
+/* shift around elements */
+#define SHIFT3(type, a, b, c) { type tmp; tmp = a; a = c; c = b; b = tmp; }
+#define SHIFT4(type, a, b, c, d) { type tmp; tmp = a; a = d; d = c; c = b; b = tmp; }
+
/* string compare */
#define STREQ(str, a) ( strcmp((str), (a))==0 )
#define STREQ2(str, a, b) ( STREQ(str, a) || STREQ(str, b) )
diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
index a3925dfaa9a..7e1aca3bb20 100644
--- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c
+++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c
@@ -246,6 +246,8 @@ void fluidsimSettingsCopy(struct FluidsimSettings* sb) {}
/*new render funcs */
int externtex(struct MTex *mtex, float *vec, float *tin, float *tr, float *tg, float *tb, float *ta) { return 0; }
+void texture_rgb_blend(float *in, float *tex, float *out, float fact, float facg, int blendtype) {}
+float texture_value_blend(float tex, float out, float fact, float facg, int blendtype, int flip) { return 0; }
void RE_FreeRenderResult(struct RenderResult *rr) {}
void RE_GetResultImage(struct Render *re, struct RenderResult *rr) {}
@@ -339,3 +341,8 @@ Sequence *get_forground_frame_seq(int frame){return 0;};
void harmonic_coordinates_bind(struct MeshDeformModifierData *mmd,
float (*vertexcos)[3], int totvert, float cagemat[][4]) {}
+/* particle.c */
+void PE_free_particle_edit(struct ParticleSystem *psys) {}
+void PE_get_colors(char sel[4], char nosel[4]) {}
+void PE_recalc_world_cos(struct Object *ob, struct ParticleSystem *psys) {}
+
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 4e6d4a31173..5e688af14a4 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -55,6 +55,7 @@
#include "DNA_view3d_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "DNA_particle_types.h"
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
@@ -77,6 +78,7 @@
#include "BKE_subsurf.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
+#include "BKE_particle.h"
#ifdef WITH_VERSE
#include "BKE_verse.h"
@@ -1961,6 +1963,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
*/
if(mti->type == eModifierTypeType_OnlyDeform) {
+
/* No existing verts to deform, need to build them. */
if(!deformedVerts) {
if(dm) {
@@ -2002,9 +2005,12 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3],
/* set the DerivedMesh to only copy needed data */
DM_set_only_copy(dm, (CustomDataMask)curr->link);
-
- ndm = mti->applyModifier(md, ob, dm, useRenderParams,
- !inputVertexCos);
+
+ if(((CustomDataMask)curr->link) & CD_MASK_ORIGSPACE)
+ if(!CustomData_has_layer(&dm->faceData, CD_ORIGSPACE))
+ CustomData_add_layer(&dm->faceData, CD_ORIGSPACE, CD_DEFAULT, NULL, dm->getNumFaces(dm));
+
+ ndm = mti->applyModifier(md, ob, dm, useRenderParams, !inputVertexCos);
if(ndm) {
/* if the modifier returned a new dm, release the old one */
@@ -2182,6 +2188,10 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r,
/* set the DerivedMesh to only copy needed data */
DM_set_only_copy(dm, (CustomDataMask)curr->link);
+ if(((CustomDataMask)curr->link) & CD_MASK_ORIGSPACE)
+ if(!CustomData_has_layer(&dm->faceData, CD_ORIGSPACE))
+ CustomData_add_layer(&dm->faceData, CD_ORIGSPACE, CD_DEFAULT, NULL, dm->getNumFaces(dm));
+
ndm = mti->applyModifierEM(md, ob, em, dm);
if (ndm) {
@@ -2360,7 +2370,7 @@ static void mesh_build_data(Object *ob, CustomDataMask dataMask)
if(ob!=G.obedit) {
Object *obact = G.scene->basact?G.scene->basact->object:NULL;
- int editing = (FACESEL_PAINT_TEST);
+ int editing = (FACESEL_PAINT_TEST)|(G.f & G_PARTICLEEDIT);
int needMapping = editing && (ob==obact);
if( (G.f & G_WEIGHTPAINT) && ob==obact ) {
diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c
index 32ea2b3ed2c..43168733b3f 100644
--- a/source/blender/blenkernel/intern/anim.c
+++ b/source/blender/blenkernel/intern/anim.c
@@ -38,6 +38,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.h"
+#include "BLI_rand.h"
#include "DNA_listBase.h"
#include "DNA_curve_types.h"
@@ -47,6 +48,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
#include "DNA_vfont_types.h"
@@ -60,8 +62,10 @@
#include "BKE_global.h"
#include "BKE_ipo.h"
#include "BKE_key.h"
+#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_utildefines.h"
#include "BKE_bad_level_calls.h"
@@ -563,134 +567,143 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par)
dm->release(dm);
}
-
-
-static void particle_duplilist(ListBase *lb, Scene *sce, Object *par, PartEff *paf)
+static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, ParticleSystem *psys)
{
- Object *ob, copyob;
- Base *base;
- Particle *pa;
- float ctime, vec1[3];
- float vec[3], tmat[4][4], mat[3][3];
- float *q2;
- int lay, a, counter; /* counter is used to find in render the indexed object */
-
- pa= paf->keys;
- if(pa==NULL || (G.rendering && paf->disp!=100)) {
- build_particle_system(par);
- pa= paf->keys;
- if(pa==NULL) return;
- }
-
- ctime= bsystem_time(par, (float)G.scene->r.cfra, 0.0);
+ GroupObject *go;
+ Object *ob, **oblist=0;
+ ParticleSettings *part;
+ ParticleData *pa;
+ ParticleKey state;
+ float ctime, pa_time;
+ float tmat[4][4], mat[3][3], obrotmat[3][3], parotmat[3][3], size=0.0;
+ float xvec[3] = {-1.0, 0.0, 0.0}, *q;
+ int lay, a, k, step_nbr = 0, counter;
+ int totpart, totchild, totgroup=0, pa_num;
+
+ if(psys==0) return;
+ part=psys->part;
+
+ if(part==0) return;
+
+ ctime = bsystem_time(par, (float)G.scene->r.cfra, 0.0);
+
+ totpart = psys->totpart;
+ totchild = psys->totchild;
+
+ BLI_srandom(31415926 + psys->seed);
+
lay= G.scene->lay;
+ if((part->draw_as == PART_DRAW_OB && part->dup_ob) ||
+ (part->draw_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first)) {
- for(base= sce->base.first; base; base= base->next) {
- if(base->object->type>0 && (base->lay & lay) && G.obedit!=base->object) {
- ob= base->object->parent;
- while(ob) {
- if(ob==par) {
-
- ob= base->object;
- /* temp copy, to have ipos etc to work OK */
- copyob= *ob;
-
- /* don't want parent animation to apply on past object positions */
- if(!(paf->flag & PAF_STATIC))
- ob->parent= NULL;
-
- for(a=0, pa= paf->keys, counter=0; a<paf->totpart; a++, pa+=paf->totkey, counter++) {
-
- if(paf->flag & PAF_STATIC) {
- float mtime;
-
- where_is_particle(paf, pa, pa->time, vec1);
- mtime= pa->time+pa->lifetime;
-
- for(ctime= pa->time; ctime<mtime; ctime+=paf->staticstep, counter++) {
-
- /* make sure hair grows until the end.. */
- if(ctime>pa->time+pa->lifetime) ctime= pa->time+pa->lifetime;
-
- /* to give ipos in object correct offset */
- where_is_object_time(ob, ctime-pa->time);
-
- where_is_particle(paf, pa, ctime, vec); // makes sure there's always a vec
- Mat4MulVecfl(par->obmat, vec);
-
- if(paf->stype==PAF_VECT) {
- where_is_particle(paf, pa, ctime+1.0, vec1); // makes sure there's always a vec
- Mat4MulVecfl(par->obmat, vec1);
-
- VecSubf(vec1, vec1, vec);
- q2= vectoquat(vec1, ob->trackflag, ob->upflag);
-
- QuatToMat3(q2, mat);
- Mat4CpyMat4(tmat, ob->obmat);
- Mat4MulMat43(ob->obmat, tmat, mat);
- }
-
- VECCOPY(ob->obmat[3], vec);
- /* put object back in original state, so it cam be restored OK */
- Mat4CpyMat4(tmat, ob->obmat);
- Mat4CpyMat4(ob->obmat, copyob.obmat);
- new_dupli_object(lb, ob, tmat, par->lay, counter);
- }
- }
- else { // non static particles
-
- if((paf->flag & PAF_UNBORN)==0 && ctime < pa->time) continue;
- if((paf->flag & PAF_DIED)==0 && ctime > pa->time+pa->lifetime) continue;
+ if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && part->draw & PART_DRAW_KEYS)
+ step_nbr = part->keys_step;
+ else
+ step_nbr = 0;
- //if(ctime < pa->time+pa->lifetime) {
+ psys->lattice = psys_get_lattice(par, psys);
- /* to give ipos in object correct offset, ob->parent is NULLed */
- where_is_object_time(ob, ctime-pa->time);
-
- where_is_particle(paf, pa, ctime, vec);
- if(paf->stype==PAF_VECT) {
-
- /* if particle died, we use previous position */
- if(ctime > pa->time+pa->lifetime) {
- where_is_particle(paf, pa, pa->time+pa->lifetime-1.0f, vec1);
- VecSubf(vec1, vec, vec1);
- }
- else {
- where_is_particle(paf, pa, ctime+1.0f, vec1);
- VecSubf(vec1, vec1, vec);
- }
- q2= vectoquat(vec1, ob->trackflag, ob->upflag);
-
- QuatToMat3(q2, mat);
- Mat4CpyMat4(tmat, ob->obmat);
- Mat4MulMat43(ob->obmat, tmat, mat);
- }
+ if(part->draw_as==PART_DRAW_GR) {
+ group_handle_recalc_and_update(par, part->dup_group);
- VECCOPY(ob->obmat[3], vec);
-
- /* put object back in original state, so it can be restored OK */
- Mat4CpyMat4(tmat, ob->obmat);
- Mat4CpyMat4(ob->obmat, copyob.obmat);
- new_dupli_object(lb, ob, tmat, par->lay, counter);
- }
+ go= part->dup_group->gobject.first;
+ while(go) {
+ go=go->next;
+ totgroup++;
+ }
+
+ oblist= MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list");
+ go= part->dup_group->gobject.first;
+ for(a=0; a<totgroup; a++, go=go->next)
+ oblist[a]=go->ob;
+ }
+
+ if(totchild==0 || part->draw & PART_DRAW_PARENT)
+ a=0;
+ else
+ a=totpart;
+
+ for(pa=psys->particles,counter=0; a<totpart+totchild; a++,pa++,counter++) {
+ if(a<totpart) {
+ if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue;
+
+ pa_num=pa->num;
+
+ pa_time=pa->time;
+
+ size=pa->size;
+ }
+ else {
+ /* TODO: figure these two out */
+ pa_num = a;
+ pa_time = psys->particles[psys->child[a - totpart].parent].time;
+
+ size=psys_get_child_size(psys, a - totpart, ctime, 0);
+ }
+
+ if(part->draw_as==PART_DRAW_GR) {
+ if(part->draw&PART_DRAW_RAND_GR)
+ ob = oblist[BLI_rand() % totgroup];
+ else if(part->from==PART_FROM_PARTICLE)
+ ob = oblist[pa_num % totgroup];
+ else
+ ob = oblist[a % totgroup];
+ }
+ else
+ ob = part->dup_ob;
+
+ for(k=0; k<=step_nbr; k++, counter++) {
+ if(step_nbr) {
+ state.time = (float)k / (float)step_nbr;
+ psys_get_particle_on_path(par, psys, a, &state, 0);
+ }
+ else {
+ state.time = -1.0;
+ if(psys_get_particle_state(par, psys, a, &state, 0) == 0)
+ continue;
+ }
+
+ QuatToMat3(state.rot, parotmat);
+
+ if(part->draw_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {
+ for(go= part->dup_group->gobject.first; go; go= go->next) {
+
+ Mat4CpyMat4(tmat, go->ob->obmat);
+ Mat4MulMat43(tmat, go->ob->obmat, parotmat);
+ Mat4MulFloat3((float *)tmat, size);
+
+ VECADD(tmat[3], go->ob->obmat[3], state.co);
+
+ new_dupli_object(lb, go->ob, tmat, par->lay, counter);
}
- /* temp copy, to have ipos etc to work OK */
- *ob= copyob;
+ }
+ else {
+ /* to give ipos in object correct offset */
+ where_is_object_time(ob, ctime-pa_time);
- break;
+ q = vectoquat(xvec, ob->trackflag, ob->upflag);
+ QuatToMat3(q, obrotmat);
+
+ Mat3MulMat3(mat, parotmat, obrotmat);
+ Mat4CpyMat4(tmat, ob->obmat);
+ Mat4MulMat43(tmat, ob->obmat, mat);
+ Mat4MulFloat3((float *)tmat, size);
+
+ VECCOPY(tmat[3], state.co);
+
+ new_dupli_object(lb, ob, tmat, par->lay, counter);
}
- ob= ob->parent;
}
}
}
-
- if(G.rendering && paf->disp!=100) {
- MEM_freeN(paf->keys);
- paf->keys= NULL;
+ if(oblist)
+ MEM_freeN(oblist);
+
+ if(psys->lattice) {
+ end_latt_deform();
+ psys->lattice = 0;
}
-
-
}
static Object *find_family_object(Object **obar, char *family, char ch)
@@ -771,13 +784,14 @@ ListBase *object_duplilist(Scene *sce, Object *ob)
duplilist->first= duplilist->last= NULL;
if(ob->transflag & OB_DUPLI) {
- if(ob->transflag & OB_DUPLIVERTS) {
+ if(ob->transflag & OB_DUPLIPARTS) {
+ ParticleSystem *psys = ob->particlesystem.first;
+ for(; psys; psys=psys->next)
+ new_particle_duplilist(duplilist, sce, ob, psys);
+ }
+ else if(ob->transflag & OB_DUPLIVERTS) {
if(ob->type==OB_MESH) {
- PartEff *paf;
- if( (paf=give_parteff(ob)) )
- particle_duplilist(duplilist, sce, ob, paf);
- else
- vertex_duplilist(duplilist, sce, ob);
+ vertex_duplilist(duplilist, sce, ob);
}
else if(ob->type==OB_FONT) {
font_duplilist(duplilist, ob);
@@ -823,14 +837,18 @@ int count_duplilist(Object *ob)
if(ob->transflag & OB_DUPLIVERTS) {
if(ob->type==OB_MESH) {
if(ob->transflag & OB_DUPLIVERTS) {
- PartEff *paf;
- if( (paf=give_parteff(ob)) ) {
- return paf->totpart;
- }
- else {
+ ParticleSystem *psys = ob->particlesystem.first;
+ int pdup=0;
+
+ for(; psys; psys=psys->next)
+ pdup += psys->totpart;
+
+ if(pdup==0){
Mesh *me= ob->data;
return me->totvert;
}
+ else
+ return pdup;
}
}
}
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index 9d86b86a47f..9845f571126 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -266,7 +266,7 @@ static void clear_global(void)
free_vertexpaint();
- G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT);
+ G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT + G_PARTICLEEDIT);
}
/* make sure path names are correct for OS */
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 2d5f5f091c3..6a856307916 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -751,6 +751,7 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *ob)
/* this does a referenced copy, the only new layers being ORIGINDEX */
DM_init(dm, mesh->totvert, mesh->totedge, mesh->totface);
+ dm->deformedOnly = 1;
CustomData_merge(&mesh->vdata, &dm->vertData, CD_MASK_MESH, CD_REFERENCE,
mesh->totvert);
@@ -798,6 +799,8 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me)
MFace *mface = cddm->mface;
int i, *index;
+ dm->deformedOnly = 1;
+
CustomData_merge(&em->vdata, &dm->vertData, CD_MASK_DERIVEDMESH,
CD_CALLOC, dm->numVertData);
/* CustomData_merge(&em->edata, &dm->edgeData, CD_MASK_DERIVEDMESH,
@@ -889,6 +892,7 @@ DerivedMesh *CDDM_copy(DerivedMesh *source)
/* this initializes dm, and copies all non mvert/medge/mface layers */
DM_from_template(dm, source, numVerts, numEdges, numFaces);
+ dm->deformedOnly = source->deformedOnly;
CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts);
CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges);
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 466ba9f4201..aa0dc10f4e0 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -271,7 +271,7 @@ static void layerSwap_tface(void *data, int *corner_indices)
static void layerDefault_tface(void *data, int count)
{
- static MTFace default_tf = {{{0, 1}, {0, 0}, {1, 0}, {1, 1}}, NULL,
+ static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}, NULL,
0, 0, TF_DYNAMIC, 0, 0};
MTFace *tf = (MTFace*)data;
int i;
@@ -280,6 +280,83 @@ static void layerDefault_tface(void *data, int count)
tf[i] = default_tf;
}
+static void layerCopy_origspace_face(const void *source, void *dest, int count)
+{
+ const OrigSpaceFace *source_tf = (const OrigSpaceFace*)source;
+ OrigSpaceFace *dest_tf = (OrigSpaceFace*)dest;
+ int i;
+
+ for(i = 0; i < count; ++i)
+ dest_tf[i] = source_tf[i];
+}
+
+static void layerInterp_origspace_face(void **sources, float *weights,
+ float *sub_weights, int count, void *dest)
+{
+ OrigSpaceFace *osf = dest;
+ int i, j, k;
+ float uv[4][2];
+ float *sub_weight;
+
+ if(count <= 0) return;
+
+ memset(uv, 0, sizeof(uv));
+
+ sub_weight = sub_weights;
+ for(i = 0; i < count; ++i) {
+ float weight = weights ? weights[i] : 1;
+ OrigSpaceFace *src = sources[i];
+
+ for(j = 0; j < 4; ++j) {
+ if(sub_weights) {
+ for(k = 0; k < 4; ++k, ++sub_weight) {
+ float w = (*sub_weight) * weight;
+ float *tmp_uv = src->uv[k];
+
+ uv[j][0] += tmp_uv[0] * w;
+ uv[j][1] += tmp_uv[1] * w;
+ }
+ } else {
+ uv[j][0] += src->uv[j][0] * weight;
+ uv[j][1] += src->uv[j][1] * weight;
+ }
+ }
+ }
+
+ *osf = *(OrigSpaceFace *)sources[0];
+ for(j = 0; j < 4; ++j) {
+ osf->uv[j][0] = uv[j][0];
+ osf->uv[j][1] = uv[j][1];
+ }
+}
+
+static void layerSwap_origspace_face(void *data, int *corner_indices)
+{
+ OrigSpaceFace *osf = data;
+ float uv[4][2];
+ int j;
+
+ for(j = 0; j < 4; ++j) {
+ uv[j][0] = osf->uv[corner_indices[j]][0];
+ uv[j][1] = osf->uv[corner_indices[j]][1];
+ }
+ memcpy(osf->uv, uv, sizeof(osf->uv));
+}
+
+static void layerDefault_origspace_face(void *data, int count)
+{
+ static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
+ OrigSpaceFace *osf = (OrigSpaceFace*)data;
+ int i;
+
+ for(i = 0; i < count; i++)
+ osf[i] = default_osf;
+}
+/* --------- */
+
+
+
+
static void layerInterp_mcol(void **sources, float *weights,
float *sub_weights, int count, void *dest)
{
@@ -370,11 +447,13 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(MFloatProperty), "MFloatProperty",1,"Float",NULL,NULL,NULL,NULL},
{sizeof(MIntProperty), "MIntProperty",1,"Int",NULL,NULL,NULL,NULL},
{sizeof(MStringProperty), "MStringProperty",1,"String",NULL,NULL,NULL,NULL},
+ {sizeof(OrigSpaceFace), "OrigSpaceFace", 1, "UVTex", layerCopy_origspace_face, NULL,
+ layerInterp_origspace_face, layerSwap_origspace_face, layerDefault_origspace_face},
};
const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDMVert", "CDMSticky", "CDMDeformVert", "CDMEdge", "CDMFace", "CDMTFace",
- "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty","CDMIntProperty","CDMStringProperty"};
+ "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty","CDMIntProperty","CDMStringProperty", "CDOrigSpace"};
const CustomDataMask CD_MASK_BAREMESH =
CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE;
@@ -387,8 +466,8 @@ const CustomDataMask CD_MASK_EDITMESH =
CD_MASK_MCOL|CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR;
const CustomDataMask CD_MASK_DERIVEDMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE |
- CD_MASK_MCOL | CD_MASK_ORIGINDEX|
- CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR;
+ CD_MASK_MCOL | CD_MASK_ORIGINDEX |
+ CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_ORIGSPACE;
static const LayerTypeInfo *layerType_getInfo(int type)
{
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 11ec8670ed1..924f544285e 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -57,6 +57,7 @@
#include "DNA_object_force.h"
#include "DNA_object_fluidsim.h"
#include "DNA_oops_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -72,8 +73,9 @@
#include "BKE_mball.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
-#include "BKE_scene.h"
+#include "BKE_particle.h"
#include "BKE_utildefines.h"
+#include "BKE_scene.h"
#include "MEM_guardedalloc.h"
#include "blendef.h"
@@ -353,6 +355,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int
DagNode * node2;
DagNode * node3;
Key *key;
+ ParticleSystem *psys;
int addtoroot= 1;
node = dag_get_node(dag, ob);
@@ -586,6 +589,54 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int
}
}
}
+
+ psys= ob->particlesystem.first;
+ if(psys) {
+ ParticleEffectorCache *nec;
+
+ for(; psys; psys=psys->next) {
+ ParticleSettings *part= psys->part;
+
+ dag_add_relation(dag, node, node, DAG_RL_OB_DATA);
+
+ if(part->phystype==PART_PHYS_KEYED && psys->keyed_ob &&
+ BLI_findlink(&psys->keyed_ob->particlesystem,psys->keyed_psys-1)) {
+ node2 = dag_get_node(dag, psys->keyed_ob);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA);
+ }
+
+ if(psys->effectors.first)
+ psys_end_effectors(psys);
+ psys_init_effectors(ob,psys->part->eff_group,psys);
+
+ if(psys->effectors.first) {
+ 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);
+ else
+ dag_add_relation(dag, node2, node, DAG_RL_OB_DATA);
+ }
+ 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);
+ }
+ else if(nec->type & PSYS_EC_PARTICLE) {
+ node2 = dag_get_node(dag, ob1);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA);
+ }
+
+ if(nec->type & PSYS_EC_REACTOR) {
+ node2 = dag_get_node(dag, ob1);
+ dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA);
+ }
+ }
+ }
+ }
+ }
for (con = ob->constraints.first; con; con=con->next) {
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
@@ -1813,6 +1864,8 @@ static void dag_object_time_update_flags(Object *ob)
ob->recalc |= OB_RECALC_DATA; // NT FSPARTICLE
}
}
+ if(ob->particlesystem.first)
+ ob->recalc |= OB_RECALC_DATA;
break;
case OB_CURVE:
case OB_SURF:
@@ -1842,6 +1895,17 @@ static void dag_object_time_update_flags(Object *ob)
if(ob->transflag & OB_DUPLI) ob->recalc |= OB_RECALC_DATA;
break;
}
+
+ if(ob->particlesystem.first) {
+ ParticleSystem *psys= ob->particlesystem.first;
+
+ for(; psys; psys=psys->next) {
+ if(psys->flag & PSYS_ENABLED) {
+ ob->recalc |= OB_RECALC_DATA;
+ break;
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c
index 30a4c8c3433..f410167c2ac 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.c
@@ -1098,7 +1098,7 @@ void curve_to_filledpoly(Curve *cu, ListBase *nurb, ListBase *dispbase)
- first point left, last point right
- based on subdivided points in original curve, not on points in taper curve (still)
*/
-static float calc_taper(Object *taperobj, int cur, int tot)
+float calc_taper(Object *taperobj, int cur, int tot)
{
Curve *cu;
DispList *dl;
@@ -1256,7 +1256,7 @@ void curve_calc_modifiers_post(Object *ob, ListBase *nurb, ListBase *dispbase, i
if ((md->mode & required_mode) != required_mode) continue;
if (mti->isDisabled && mti->isDisabled(md)) continue;
- if (mti->type!=eModifierTypeType_OnlyDeform) continue;
+ if (mti->type!=eModifierTypeType_OnlyDeform && mti->type!=eModifierTypeType_DeformOrConstruct) continue;
for (dl=dispbase->first; dl; dl=dl->next) {
mti->deformVerts(md, ob, NULL, (float(*)[3]) dl->verts, (dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr);
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 07fe8bbee31..1b345616888 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -799,7 +799,7 @@ static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3],
// t= 0.5; // this is labda of line, can use it optimize quad intersection
// sorry but no .. see below (BM)
- if( LineIntersectsTriangle(opco, npco, nv1, nv2, nv3, &t) ) {
+ if( LineIntersectsTriangle(opco, npco, nv1, nv2, nv3, &t, NULL) ) {
if (t < min_t) {
deflected = 1;
deflected_now = 1;
@@ -810,7 +810,7 @@ static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3],
// it might give a smaller t on (close to) the edge .. this is numerics not esoteric maths :)
// note: the 2 triangles don't need to share a plane ! (BM)
if (mface->v4) {
- if( LineIntersectsTriangle(opco, npco, nv1, nv3, nv4, &t2) ) {
+ if( LineIntersectsTriangle(opco, npco, nv1, nv3, nv4, &t2, NULL) ) {
if (t2 < min_t) {
deflected = 1;
deflected_now = 2;
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index 83b845a0064..03a34df090a 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -52,6 +52,7 @@
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
#include "DNA_sound_types.h"
@@ -93,7 +94,7 @@ int ob_ar[OB_TOTIPO]= {
OB_ROT_X, OB_ROT_Y, OB_ROT_Z, OB_DROT_X, OB_DROT_Y, OB_DROT_Z,
OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z, OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z,
OB_LAY, OB_TIME, OB_COL_R, OB_COL_G, OB_COL_B, OB_COL_A,
- OB_PD_FSTR, OB_PD_FFALL, OB_PD_SDAMP, OB_PD_RDAMP, OB_PD_PERM
+ OB_PD_FSTR, OB_PD_FFALL, OB_PD_SDAMP, OB_PD_RDAMP, OB_PD_PERM, OB_PD_FMAXD
};
int ac_ar[AC_TOTIPO]= {
@@ -180,6 +181,12 @@ int fluidsim_ar[FLUIDSIM_TOTIPO]= {
FLUIDSIM_ACTIVE
};
+int part_ar[PART_TOTIPO]= {
+ PART_EMIT_FREQ, PART_EMIT_LIFE, PART_EMIT_VEL, PART_EMIT_AVE, PART_EMIT_SIZE,
+ PART_AVE, PART_SIZE, PART_DRAG, PART_BROWN, PART_DAMP, PART_LENGTH, PART_CLUMP,
+ PART_GRAV_X, PART_GRAV_Y, PART_GRAV_Z, PART_KINK_AMP, PART_KINK_FREQ, PART_KINK_SHAPE,
+ PART_BB_TILT
+};
float frame_to_float(int cfra) /* see also bsystem_time in object.c */
@@ -1231,6 +1238,7 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type)
Lamp *la;
Sequence *seq;
World *wo;
+ ParticleSettings *part;
*type= IPO_FLOAT;
@@ -1308,6 +1316,9 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type)
case OB_PD_PERM:
if(ob->pd) poin= &(ob->pd->pdef_perm);
break;
+ case OB_PD_FMAXD:
+ if(ob->pd) poin= &(ob->pd->maxdist);
+ break;
}
}
else if( GS(id->name)==ID_MA) {
@@ -1560,7 +1571,48 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type)
poin= &(snd->attenuation); break;
}
}
-
+ else if( GS(id->name)==ID_PA) {
+
+ part= (ParticleSettings *)id;
+
+ switch(icu->adrcode) {
+ case PART_EMIT_FREQ:
+ case PART_EMIT_LIFE:
+ case PART_EMIT_VEL:
+ case PART_EMIT_AVE:
+ case PART_EMIT_SIZE:
+ poin= NULL; break;
+ case PART_CLUMP:
+ poin= &(part->clumpfac); break;
+ case PART_AVE:
+ poin= &(part->avefac); break;
+ case PART_SIZE:
+ poin= &(part->size); break;
+ case PART_DRAG:
+ poin= &(part->dragfac); break;
+ case PART_BROWN:
+ poin= &(part->brownfac); break;
+ case PART_DAMP:
+ poin= &(part->dampfac); break;
+ case PART_LENGTH:
+ poin= &(part->length); break;
+ case PART_GRAV_X:
+ poin= &(part->acc[0]); break;
+ case PART_GRAV_Y:
+ poin= &(part->acc[1]); break;
+ case PART_GRAV_Z:
+ poin= &(part->acc[2]); break;
+ case PART_KINK_AMP:
+ poin= &(part->kink_amp); break;
+ case PART_KINK_FREQ:
+ poin= &(part->kink_freq); break;
+ case PART_KINK_SHAPE:
+ poin= &(part->kink_shape); break;
+ case PART_BB_TILT:
+ poin= &(part->bb_tilt); break;
+ }
+ }
+
return poin;
}
@@ -1855,6 +1907,29 @@ void set_icu_vars(IpoCurve *icu)
break;
}
}
+ else if(icu->blocktype==ID_PA){
+
+ switch(icu->adrcode) {
+ case PART_EMIT_LIFE:
+ case PART_SIZE:
+ case PART_KINK_FREQ:
+ case PART_EMIT_VEL:
+ case PART_EMIT_AVE:
+ case PART_EMIT_SIZE:
+ icu->ymin= 0.0;
+ break;
+ case PART_CLUMP:
+ case PART_DRAG:
+ case PART_DAMP:
+ case PART_LENGTH:
+ icu->ymin= 0.0;
+ icu->ymax= 1.0;
+ break;
+ case PART_KINK_SHAPE:
+ icu->ymin= -0.999;
+ icu->ymax= 0.999;
+ }
+ }
else if(icu->blocktype==ID_CO) {
icu->ymin= 0.0;
icu->ymax= 1.0f;
@@ -2325,6 +2400,7 @@ void make_cfra_list(Ipo *ipo, ListBase *elems)
case OB_PD_SDAMP:
case OB_PD_RDAMP:
case OB_PD_PERM:
+ case OB_PD_FMAXD:
bezt= icu->bezt;
if(bezt) {
a= icu->totvert;
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 28a6aad7b4d..e81d3bac655 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -78,6 +78,8 @@
#include "DNA_nla_types.h"
#include "DNA_effect_types.h"
#include "DNA_brush_types.h"
+#include "DNA_particle_types.h"
+#include "BKE_particle.h"
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
@@ -194,6 +196,8 @@ ListBase *wich_libbase(Main *mainlib, short type)
return &(mainlib->nodetree);
case ID_BR:
return &(mainlib->brush);
+ case ID_PA:
+ return &(mainlib->particle);
}
return 0;
}
@@ -254,16 +258,17 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[18]= &(main->nodetree);
lb[19]= &(main->brush);
lb[20]= &(main->script);
+ lb[21]= &(main->particle);
- lb[21]= &(main->world);
- lb[22]= &(main->screen);
- lb[23]= &(main->object);
- lb[24]= &(main->scene);
- lb[25]= &(main->library);
+ lb[22]= &(main->world);
+ lb[23]= &(main->screen);
+ lb[24]= &(main->object);
+ lb[25]= &(main->scene);
+ lb[26]= &(main->library);
- lb[26]= NULL;
+ lb[27]= NULL;
- return 26;
+ return 27;
}
/* *********** ALLOC AND FREE *****************
@@ -359,6 +364,9 @@ static ID *alloc_libblock_notest(short type)
case ID_BR:
id = MEM_callocN(sizeof(Brush), "brush");
break;
+ case ID_PA:
+ id = MEM_callocN(sizeof(ParticleSettings), "ParticleSettings");
+ break;
}
return id;
}
@@ -503,6 +511,9 @@ void free_libblock(ListBase *lb, void *idv)
case ID_BR:
free_brush((Brush *)id);
break;
+ case ID_PA:
+ psys_free_settings((ParticleSettings *)id);
+ break;
}
if (id->properties) {
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 3a6611a2be7..9336dde0151 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -41,10 +41,11 @@
#include "math.h"
#include "float.h"
-#include "BLI_blenlib.h"
-#include "BLI_rand.h"
#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_kdtree.h"
#include "BLI_linklist.h"
+#include "BLI_rand.h"
#include "BLI_edgehash.h"
#include "BLI_ghash.h"
#include "BLI_memarena.h"
@@ -59,6 +60,7 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "DNA_curve_types.h"
@@ -72,20 +74,24 @@
#include "BKE_main.h"
#include "BKE_anim.h"
#include "BKE_bad_level_calls.h"
+#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
-#include "BKE_utildefines.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_DerivedMesh.h"
#include "BKE_booleanops.h"
#include "BKE_displist.h"
#include "BKE_modifier.h"
#include "BKE_lattice.h"
+#include "BKE_library.h"
#include "BKE_subsurf.h"
#include "BKE_object.h"
#include "BKE_mesh.h"
#include "BKE_softbody.h"
#include "BKE_material.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_utildefines.h"
#include "depsgraph_private.h"
#include "LOD_DependKludge.h"
@@ -4685,7 +4691,7 @@ static void armatureModifier_deformVerts(
armature_deform_verts(amd->object, ob, derivedData, vertexCos, NULL,
numVerts, amd->deformflag,
- amd->prevCos, amd->defgrp_name);
+ (float(*)[3])amd->prevCos, amd->defgrp_name);
/* free cache */
if(amd->prevCos) {
MEM_freeN(amd->prevCos);
@@ -4998,6 +5004,1220 @@ static DerivedMesh *booleanModifier_applyModifier(
return derivedData;
}
+/* Particles */
+static void particleSystemModifier_initData(ModifierData *md)
+{
+ ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md;
+ psmd->psys= 0;
+ psmd->dm=0;
+
+}
+static void particleSystemModifier_freeData(ModifierData *md)
+{
+ ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md;
+
+ if(psmd->dm){
+ psmd->dm->needsFree = 1;
+ psmd->dm->release(psmd->dm);
+ psmd->dm=0;
+ }
+
+ psmd->psys->flag &= ~PSYS_ENABLED;
+ psmd->psys->flag |= PSYS_DELETE;
+}
+static void particleSystemModifier_copyData(ModifierData *md, ModifierData *target)
+{
+ ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md;
+ ParticleSystemModifierData *tpsmd= (ParticleSystemModifierData*) target;
+
+ tpsmd->dm = 0;
+ //tpsmd->facepa = 0;
+ tpsmd->flag = psmd->flag;
+ /* need to keep this to recognise a bit later in copy_object */
+ tpsmd->psys = psmd->psys;
+}
+
+CustomDataMask particleSystemModifier_requiredDataMask(ModifierData *md)
+{
+ ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md;
+ CustomDataMask dataMask = (1 << CD_MTFACE) + (1 << CD_MEDGE);
+ int i;
+
+ /* ask for vertexgroups if we need them */
+ for(i=0; i<PSYS_TOT_VG; i++){
+ if(psmd->psys->vgroup[i]){
+ dataMask |= (1 << CD_MDEFORMVERT);
+ break;
+ }
+ }
+
+ /* particles only need this if they are after a non deform modifier, and
+ * the modifier stack will only create them in that case. */
+ dataMask |= CD_MASK_ORIGSPACE;
+
+ return dataMask;
+}
+static int is_last_displist(Object *ob)
+{
+ Curve *cu = ob->data;
+ static int curvecount=0, totcurve=0;
+
+ if(curvecount==0){
+ DispList *dl;
+
+ totcurve=0;
+ for(dl=cu->disp.first; dl; dl=dl->next){
+ totcurve++;
+ }
+ }
+
+ curvecount++;
+
+ if(curvecount==totcurve){
+ curvecount=0;
+ return 1;
+ }
+
+ return 0;
+}
+/* saves the current emitter state for a particle system and calculates particles */
+static void particleSystemModifier_deformVerts(
+ ModifierData *md, Object *ob, DerivedMesh *derivedData,
+ float (*vertexCos)[3], int numVerts)
+{
+ DerivedMesh *dm = derivedData;
+ ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md;
+ ParticleSystem * psys=0;
+ int totvert=0,totedge=0,totface=0,needsFree=0;
+
+ if(ob->particlesystem.first)
+ psys=psmd->psys;
+ else
+ return;
+
+ if((psys->flag&PSYS_ENABLED)==0)
+ return;
+
+ if(dm==0){
+ if(ob->type==OB_MESH){
+ dm = CDDM_from_mesh((Mesh*)(ob->data), ob);
+
+ CDDM_apply_vert_coords(dm, vertexCos);
+ //CDDM_calc_normals(dm);
+
+ needsFree=1;
+ }
+ else if(ELEM3(ob->type,OB_FONT,OB_CURVE,OB_SURF)){
+ Object *tmpobj;
+ Curve *tmpcu;
+
+ if(is_last_displist(ob)){
+ /* copies object and modifiers (but not the data) */
+ tmpobj= copy_object( ob );
+ tmpcu = (Curve *)tmpobj->data;
+ tmpcu->id.us--;
+
+ /* copies the data */
+ tmpobj->data = copy_curve( (Curve *) ob->data );
+
+ makeDispListCurveTypes( tmpobj, 1 );
+ nurbs_to_mesh( tmpobj );
+
+ dm = CDDM_from_mesh((Mesh*)(tmpobj->data), tmpobj);
+ //CDDM_calc_normals(dm);
+
+ free_libblock_us( &G.main->object, tmpobj );
+
+ needsFree=1;
+ }
+ else return;
+ }
+ else return;
+ }
+
+ /* clear old dm */
+ if(psmd->dm){
+ totvert=psmd->dm->getNumVerts(psmd->dm);
+ totedge=psmd->dm->getNumEdges(psmd->dm);
+ totface=psmd->dm->getNumFaces(psmd->dm);
+ psmd->dm->needsFree = 1;
+ psmd->dm->release(psmd->dm);
+ }
+
+ /* make new dm */
+ psmd->dm=CDDM_copy(dm);
+ CDDM_calc_normals(psmd->dm);
+
+ if(needsFree){
+ dm->needsFree = 1;
+ dm->release(dm);
+ }
+
+ /* protect dm */
+ psmd->dm->needsFree = 0;
+
+ /* report change in mesh structure */
+ if(psmd->dm->getNumVerts(psmd->dm)!=totvert ||
+ psmd->dm->getNumEdges(psmd->dm)!=totedge ||
+ psmd->dm->getNumFaces(psmd->dm)!=totface){
+ /* in file read dm hasn't really changed but just wasn't saved in file */
+ if(psmd->flag & eParticleSystemFlag_Loaded)
+ psmd->flag &= ~eParticleSystemFlag_Loaded;
+ else{
+ /* TODO PARTICLE - Added this so changing subsurf under hair updates it
+ should it be done elsewhere? - Campbell */
+ psys->recalc |= PSYS_RECALC_HAIR;
+ psys->recalc |= PSYS_DISTR;
+ psmd->flag |= eParticleSystemFlag_DM_changed;
+ }
+ }
+
+ if(psys){
+ particle_system_update(ob,psys);
+ psmd->flag |= eParticleSystemFlag_psys_updated;
+ psmd->flag &= ~eParticleSystemFlag_DM_changed;
+ }
+}
+
+static void particleSystemModifier_deformVertsEM(
+ ModifierData *md, Object *ob, EditMesh *editData,
+ DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
+{
+ DerivedMesh *dm = derivedData;
+
+ if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data);
+
+ particleSystemModifier_deformVerts(md, ob, dm, vertexCos, numVerts);
+
+ if(!derivedData) dm->release(dm);
+}
+
+/* Particle Instance */
+static void particleInstanceModifier_initData(ModifierData *md)
+{
+ ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md;
+
+ pimd->flag = eParticleInstanceFlag_Parents|eParticleInstanceFlag_Unborn|
+ eParticleInstanceFlag_Alive|eParticleInstanceFlag_Dead;
+ pimd->psys = 1;
+
+}
+static void particleInstanceModifier_copyData(ModifierData *md, ModifierData *target)
+{
+ ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md;
+ ParticleInstanceModifierData *tpimd= (ParticleInstanceModifierData*) target;
+
+ tpimd->ob = pimd->ob;
+ tpimd->psys = pimd->psys;
+ tpimd->flag = pimd->flag;
+}
+
+static int particleInstanceModifier_dependsOnTime(ModifierData *md)
+{
+ return 0;
+}
+static void particleInstanceModifier_updateDepgraph(ModifierData *md, DagForest *forest,
+ Object *ob, DagNode *obNode)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md;
+
+ if (pimd->ob) {
+ DagNode *curNode = dag_get_node(forest, pimd->ob);
+
+ dag_add_relation(forest, curNode, obNode,
+ DAG_RL_DATA_DATA | DAG_RL_OB_DATA);
+ }
+}
+
+static void particleInstanceModifier_foreachObjectLink(ModifierData *md, Object *ob,
+ ObjectWalkFunc walk, void *userData)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md;
+
+ walk(userData, ob, &pimd->ob);
+}
+
+static DerivedMesh * particleInstanceModifier_applyModifier(
+ ModifierData *md, Object *ob, DerivedMesh *derivedData,
+ int useRenderParams, int isFinalCalc)
+{
+ DerivedMesh *dm = derivedData, *result;
+ ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md;
+ ParticleSystem * psys=0;
+ ParticleData *pa=0, *pars=0;
+ MFace *mface, *orig_mface;
+ MVert *mvert, *orig_mvert;
+ int i,totvert, totpart=0, totface, maxvert, maxface, first_particle=0;
+ short track=ob->trackflag%3, trackneg;
+ float max_co=0.0, min_co=0.0, temp_co[3];
+
+ trackneg=((ob->trackflag>2)?1:0);
+
+ if(pimd->ob==ob){
+ pimd->ob=0;
+ return derivedData;
+ }
+
+ if(pimd->ob){
+ psys = BLI_findlink(&pimd->ob->particlesystem,pimd->psys-1);
+ if(psys==0 || psys->totpart==0)
+ return derivedData;
+ }
+ else return derivedData;
+
+ if(pimd->flag & eParticleInstanceFlag_Parents)
+ totpart+=psys->totpart;
+ if(pimd->flag & eParticleInstanceFlag_Children){
+ if(totpart==0)
+ first_particle=psys->totpart;
+ totpart+=psys->totchild;
+ }
+
+ if(totpart==0)
+ return derivedData;
+
+ pars=psys->particles;
+
+ totvert=dm->getNumVerts(dm);
+ totface=dm->getNumFaces(dm);
+
+ maxvert=totvert*totpart;
+ maxface=totface*totpart;
+
+ psys->lattice=psys_get_lattice(ob, psys);
+
+ if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED)){
+ float co[3];
+ for(i=0; i< totvert; i++){
+ dm->getVertCo(dm,i,co);
+ if(i==0){
+ min_co=max_co=co[track];
+ }
+ else{
+ if(co[track]<min_co)
+ min_co=co[track];
+
+ if(co[track]>max_co)
+ max_co=co[track];
+ }
+ }
+ }
+
+ result = CDDM_from_template(dm, maxvert,dm->getNumEdges(dm)*totpart,maxface);
+
+ mvert=result->getVertArray(result);
+ orig_mvert=dm->getVertArray(dm);
+
+ for(i=0; i<maxvert; i++){
+ MVert *inMV;
+ MVert *mv = mvert + i;
+ ParticleKey state;
+
+ inMV = orig_mvert + i%totvert;
+ DM_copy_vert_data(dm, result, i%totvert, i, 1);
+ *mv = *inMV;
+
+ /*change orientation based on object trackflag*/
+ VECCOPY(temp_co,mv->co);
+ mv->co[0]=temp_co[track];
+ mv->co[1]=temp_co[(track+1)%3];
+ mv->co[2]=temp_co[(track+2)%3];
+
+ if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && pimd->flag & eParticleInstanceFlag_Path){
+ state.time=(mv->co[0]-min_co)/(max_co-min_co);
+ if(trackneg)
+ state.time=1.0f-state.time;
+ psys_get_particle_on_path(pimd->ob,psys,first_particle + i/totvert,&state,1);
+ }
+ else{
+ state.time=-1.0;
+ psys_get_particle_state(pimd->ob,psys,i/totvert,&state,1);
+ }
+
+ /*displace vertice to path location*/
+ if(pimd->flag & eParticleInstanceFlag_Path)
+ mv->co[0]=0.0;
+
+ QuatMulVecf(state.rot,mv->co);
+ VECADD(mv->co,mv->co,state.co);
+ }
+
+ mface=result->getFaceArray(result);
+ orig_mface=dm->getFaceArray(dm);
+
+ for(i=0; i<maxface; i++){
+ MFace *inMF;
+ MFace *mf = mface + i;
+
+ if(pimd->flag & eParticleInstanceFlag_Parents){
+ if(i/totface>=psys->totpart){
+ if(psys->part->childtype==PART_CHILD_PARTICLES)
+ pa=psys->particles+(psys->child+i/totface-psys->totpart)->parent;
+ else
+ pa=0;
+ }
+ else
+ pa=pars+i/totface;
+ }
+ else{
+ if(psys->part->childtype==PART_CHILD_PARTICLES)
+ pa=psys->particles+(psys->child+i/totface)->parent;
+ else
+ pa=0;
+ }
+
+ if(pa){
+ if(pa->alive==PARS_UNBORN && (pimd->flag&eParticleInstanceFlag_Unborn)==0) continue;
+ if(pa->alive==PARS_ALIVE && (pimd->flag&eParticleInstanceFlag_Alive)==0) continue;
+ if(pa->alive==PARS_DEAD && (pimd->flag&eParticleInstanceFlag_Dead)==0) continue;
+ }
+
+ inMF = orig_mface + i%totface;
+ DM_copy_face_data(dm, result, i%totface, i, 1);
+ *mf = *inMF;
+
+ mf->v1+=(i/totface)*totvert;
+ mf->v2+=(i/totface)*totvert;
+ mf->v3+=(i/totface)*totvert;
+ if(mf->v4)
+ mf->v4+=(i/totface)*totvert;
+ }
+
+ CDDM_calc_edges(result);
+ CDDM_calc_normals(result);
+
+ if(psys->lattice){
+ end_latt_deform();
+ psys->lattice=0;
+ }
+
+ return result;
+}
+static DerivedMesh *particleInstanceModifier_applyModifierEM(
+ ModifierData *md, Object *ob, EditMesh *editData,
+ DerivedMesh *derivedData)
+{
+ return particleInstanceModifier_applyModifier(md, ob, derivedData, 0, 1);
+}
+
+/* Explode */
+static void explodeModifier_initData(ModifierData *md)
+{
+ ExplodeModifierData *emd= (ExplodeModifierData*) md;
+
+ emd->facepa=0;
+ emd->flag |= eExplodeFlag_Unborn+eExplodeFlag_Alive+eExplodeFlag_Dead;
+}
+static void explodeModifier_freeData(ModifierData *md)
+{
+ ExplodeModifierData *emd= (ExplodeModifierData*) md;
+
+ if(emd->facepa) MEM_freeN(emd->facepa);
+}
+static void explodeModifier_copyData(ModifierData *md, ModifierData *target)
+{
+ ExplodeModifierData *emd= (ExplodeModifierData*) md;
+ ExplodeModifierData *temd= (ExplodeModifierData*) target;
+
+ temd->facepa = 0;
+ temd->flag = emd->flag;
+}
+static int explodeModifier_dependsOnTime(ModifierData *md)
+{
+ return 1;
+}
+CustomDataMask explodeModifier_requiredDataMask(ModifierData *md)
+{
+ ExplodeModifierData *emd= (ExplodeModifierData*) md;
+ CustomDataMask dataMask = 0;
+
+ if(emd->vgroup)
+ dataMask |= (1 << CD_MDEFORMVERT);
+
+ return dataMask;
+}
+
+/* this should really be put somewhere permanently */
+static float vert_weight(MDeformVert *dvert, int group)
+{
+ MDeformWeight *dw;
+ int i;
+
+ if(dvert) {
+ dw= dvert->dw;
+ for(i= dvert->totweight; i>0; i--, dw++) {
+ if(dw->def_nr == group) return dw->weight;
+ if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/
+ }
+ }
+ return 0.0;
+}
+
+static void explodeModifier_createFacepa(ExplodeModifierData *emd,
+ ParticleSystemModifierData *psmd,
+ Object *ob, DerivedMesh *dm)
+{
+ ParticleSystem *psys=psmd->psys;
+ MFace *fa=0, *mface=0;
+ MVert *mvert = 0;
+ ParticleData *pa;
+ KDTree *tree;
+ float center[3], co[3];
+ int *facepa=0,*vertpa=0,totvert=0,totface=0,totpart=0;
+ int i,p,v1,v2,v3,v4=0;
+
+ mvert = dm->getVertArray(dm);
+ mface = dm->getFaceArray(dm);
+ totface= dm->getNumFaces(dm);
+ totvert= dm->getNumVerts(dm);
+ totpart= psmd->psys->totpart;
+
+ BLI_srandom(psys->seed);
+
+ if(emd->facepa)
+ MEM_freeN(emd->facepa);
+
+ facepa = emd->facepa = MEM_callocN(sizeof(int)*totface, "explode_facepa");
+
+ vertpa = MEM_callocN(sizeof(int)*totvert, "explode_vertpa");
+
+ /* initialize all faces & verts to no particle */
+ for(i=0; i<totface; i++)
+ facepa[i]=totpart;
+
+ for (i=0; i<totvert; i++)
+ vertpa[i]=totpart;
+
+ /* set protected verts */
+ if(emd->vgroup){
+ MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
+ float val;
+ if(dvert){
+ for(i=0; i<totvert; i++){
+ val = BLI_frand();
+ val = (1.0f-emd->protect)*val + emd->protect*0.5f;
+ if(val < vert_weight(dvert+i,emd->vgroup-1))
+ vertpa[i] = -1;
+ }
+ }
+ }
+
+ /* make tree of emitter locations */
+ tree=BLI_kdtree_new(totpart);
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++){
+ psys_particle_on_dm(ob,dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0);
+ BLI_kdtree_insert(tree, p, co, NULL);
+ }
+ BLI_kdtree_balance(tree);
+
+ /* set face-particle-indexes to nearest particle to face center */
+ for(i=0,fa=mface; i<totface; i++,fa++){
+ VecAddf(center,mvert[fa->v1].co,mvert[fa->v2].co);
+ VecAddf(center,center,mvert[fa->v3].co);
+ if(fa->v4){
+ VecAddf(center,center,mvert[fa->v4].co);
+ VecMulf(center,0.25);
+ }
+ else
+ VecMulf(center,0.3333f);
+
+ p= BLI_kdtree_find_nearest(tree,center,NULL,NULL);
+
+ v1=vertpa[fa->v1];
+ v2=vertpa[fa->v2];
+ v3=vertpa[fa->v3];
+ if(fa->v4)
+ v4=vertpa[fa->v4];
+
+ if(v1>=0 && v2>=0 && v3>=0 && (fa->v4==0 || v4>=0))
+ facepa[i]=p;
+
+ if(v1>=0) vertpa[fa->v1]=p;
+ if(v2>=0) vertpa[fa->v2]=p;
+ if(v3>=0) vertpa[fa->v3]=p;
+ if(fa->v4 && v4>=0) vertpa[fa->v4]=p;
+ }
+
+ if(vertpa) MEM_freeN(vertpa);
+ BLI_kdtree_free(tree);
+}
+static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, DerivedMesh *dm){
+ DerivedMesh *splitdm;
+ MFace *mf=0,*df1=0,*df2=0,*df3=0;
+ MFace *mface=CDDM_get_faces(dm);
+ MVert *dupve, *mv;
+ int totvert=dm->getNumVerts(dm);
+ int totface=dm->getNumFaces(dm);
+
+ int *edgesplit = MEM_callocN(sizeof(int)*totvert*totvert,"explode_edgesplit");
+ int *facesplit = MEM_callocN(sizeof(int)*totface,"explode_edgesplit");
+ int *vertpa = MEM_callocN(sizeof(int)*totvert,"explode_vertpa2");
+ int *facepa = emd->facepa;
+ int *fs, totesplit=0,totfsplit=0,totin=0,curdupvert=0,curdupface=0,curdupin=0;
+ int i,j,v1,v2,v3,v4;
+
+ /* recreate vertpa from facepa calculation */
+ for (i=0,mf=mface; i<totface; i++,mf++) {
+ vertpa[mf->v1]=facepa[i];
+ vertpa[mf->v2]=facepa[i];
+ vertpa[mf->v3]=facepa[i];
+ if(mf->v4)
+ vertpa[mf->v4]=facepa[i];
+ }
+
+ /* mark edges for splitting and how to split faces */
+ for (i=0,mf=mface,fs=facesplit; i<totface; i++,mf++,fs++) {
+ if(mf->v4){
+ v1=vertpa[mf->v1];
+ v2=vertpa[mf->v2];
+ v3=vertpa[mf->v3];
+ v4=vertpa[mf->v4];
+
+ if(v1!=v2){
+ edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1;
+ (*fs)++;
+ }
+
+ if(v2!=v3){
+ edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1;
+ (*fs)++;
+ }
+
+ if(v3!=v4){
+ edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1;
+ (*fs)++;
+ }
+
+ if(v1!=v4){
+ edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1;
+ (*fs)++;
+ }
+
+ if(*fs==2){
+ if((v1==v2 && v3==v4) || (v1==v4 && v2==v3))
+ *fs=1;
+ else if(v1!=v2){
+ if(v1!=v4)
+ edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1;
+ else
+ edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1;
+ }
+ else{
+ if(v1!=v4)
+ edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1;
+ else
+ edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1;
+ }
+ }
+ }
+ }
+
+ /* count splits & reindex */
+ totesplit=totvert;
+ for(j=0; j<totvert; j++){
+ for(i=j+1; i<totvert; i++){
+ if(edgesplit[j*totvert+i])
+ edgesplit[j*totvert+i]=edgesplit[i*totvert+j]=totesplit++;
+ }
+ }
+ /* count new faces due to splitting */
+ for(i=0,fs=facesplit; i<totface; i++,fs++){
+ if(*fs==1)
+ totfsplit+=1;
+ else if(*fs==2)
+ totfsplit+=2;
+ else if(*fs==3)
+ totfsplit+=3;
+ else if(*fs==4){
+ totfsplit+=3;
+
+ mf=dm->getFaceData(dm,i,CD_MFACE);//CDDM_get_face(dm,i);
+
+ if(vertpa[mf->v1]!=vertpa[mf->v2] && vertpa[mf->v2]!=vertpa[mf->v3])
+ totin++;
+ }
+ }
+
+ splitdm= CDDM_from_template(dm, totesplit+totin, dm->getNumEdges(dm),totface+totfsplit);
+
+ /* copy new faces & verts (is it really this painful with custom data??) */
+ for(i=0; i<totvert; i++){
+ MVert source;
+ MVert *dest;
+ dm->getVert(dm, i, &source);
+ dest = CDDM_get_vert(splitdm, i);
+
+ DM_copy_vert_data(dm, splitdm, i, i, 1);
+ *dest = source;
+ }
+ for(i=0; i<totface; i++){
+ MFace source;
+ MFace *dest;
+ dm->getFace(dm, i, &source);
+ dest = CDDM_get_face(splitdm, i);
+
+ DM_copy_face_data(dm, splitdm, i, i, 1);
+ *dest = source;
+ }
+
+ /* override original facepa (original pointer is saved in caller function) */
+ facepa= MEM_callocN(sizeof(int)*(totface+totfsplit),"explode_facepa");
+ memcpy(facepa,emd->facepa,totface*sizeof(int));
+ emd->facepa=facepa;
+
+ /* create new verts */
+ curdupvert=totvert;
+ for(j=0; j<totvert; j++){
+ for(i=j+1; i<totvert; i++){
+ if(edgesplit[j*totvert+i]){
+ mv=CDDM_get_vert(splitdm,j);
+ dupve=CDDM_get_vert(splitdm,edgesplit[j*totvert+i]);
+
+ DM_copy_vert_data(splitdm,splitdm,j,edgesplit[j*totvert+i],1);
+
+ *dupve=*mv;
+
+ mv=CDDM_get_vert(splitdm,i);
+
+ VECADD(dupve->co,dupve->co,mv->co);
+ VecMulf(dupve->co,0.5);
+ }
+ }
+ }
+
+ /* create new faces */
+ curdupface=totface;
+ curdupin=totesplit;
+ for(i=0,fs=facesplit; i<totface; i++,fs++){
+ if(*fs){
+ mf=CDDM_get_face(splitdm,i);
+
+ v1=vertpa[mf->v1];
+ v2=vertpa[mf->v2];
+ v3=vertpa[mf->v3];
+ v4=vertpa[mf->v4];
+ /* ouch! creating new faces & remapping them to new verts is no fun */
+ if(*fs==1){
+ df1=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df1=*mf;
+ curdupface++;
+
+ if(v1==v2){
+ df1->v1=edgesplit[mf->v1*totvert+mf->v4];
+ df1->v2=edgesplit[mf->v2*totvert+mf->v3];
+ mf->v3=df1->v2;
+ mf->v4=df1->v1;
+ }
+ else{
+ df1->v1=edgesplit[mf->v1*totvert+mf->v2];
+ df1->v4=edgesplit[mf->v3*totvert+mf->v4];
+ mf->v2=df1->v1;
+ mf->v3=df1->v4;
+ }
+
+ facepa[i]=v1;
+ facepa[curdupface-1]=v3;
+
+ test_index_face(df1, &splitdm->faceData, curdupface, (df1->v4 ? 4 : 3));
+ }
+ if(*fs==2){
+ df1=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df1=*mf;
+ curdupface++;
+
+ df2=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df2=*mf;
+ curdupface++;
+
+ if(v1!=v2){
+ if(v1!=v4){
+ df1->v1=edgesplit[mf->v1*totvert+mf->v4];
+ df1->v2=edgesplit[mf->v1*totvert+mf->v2];
+ df2->v1=df1->v3=mf->v2;
+ df2->v3=df1->v4=mf->v4;
+ df2->v2=mf->v3;
+
+ mf->v2=df1->v2;
+ mf->v3=df1->v1;
+
+ df2->v4=mf->v4=0;
+
+ facepa[i]=v1;
+ }
+ else{
+ df1->v2=edgesplit[mf->v1*totvert+mf->v2];
+ df1->v3=edgesplit[mf->v2*totvert+mf->v3];
+ df1->v4=mf->v3;
+ df2->v2=mf->v3;
+ df2->v3=mf->v4;
+
+ mf->v1=df1->v2;
+ mf->v3=df1->v3;
+
+ df2->v4=mf->v4=0;
+
+ facepa[i]=v2;
+ }
+ facepa[curdupface-1]=facepa[curdupface-2]=v3;
+ }
+ else{
+ if(v1!=v4){
+ df1->v3=edgesplit[mf->v3*totvert+mf->v4];
+ df1->v4=edgesplit[mf->v1*totvert+mf->v4];
+ df1->v2=mf->v3;
+
+ mf->v1=df1->v4;
+ mf->v2=df1->v3;
+ mf->v3=mf->v4;
+
+ df2->v4=mf->v4=0;
+
+ facepa[i]=v4;
+ }
+ else{
+ df1->v3=edgesplit[mf->v2*totvert+mf->v3];
+ df1->v4=edgesplit[mf->v3*totvert+mf->v4];
+ df1->v1=mf->v4;
+ df1->v2=mf->v2;
+ df2->v3=mf->v4;
+
+ mf->v1=df1->v4;
+ mf->v2=df1->v3;
+
+ df2->v4=mf->v4=0;
+
+ facepa[i]=v3;
+ }
+
+ facepa[curdupface-1]=facepa[curdupface-2]=v1;
+ }
+
+ test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3));
+ }
+ else if(*fs==3){
+ df1=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df1=*mf;
+ curdupface++;
+
+ df2=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df2=*mf;
+ curdupface++;
+
+ df3=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df3=*mf;
+ curdupface++;
+
+ if(v1==v2){
+ df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v4];
+ df3->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3];
+ df3->v3=df2->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4];
+ df3->v2=mf->v3;
+ df2->v3=mf->v4;
+ df1->v4=df2->v4=df3->v4=0;
+
+ mf->v3=df1->v2;
+ mf->v4=df1->v1;
+
+ facepa[i]=facepa[curdupface-3]=v1;
+ facepa[curdupface-1]=v3;
+ facepa[curdupface-2]=v4;
+ }
+ else if(v2==v3){
+ df3->v1=df2->v3=df1->v1=edgesplit[mf->v1*totvert+mf->v4];
+ df2->v2=df1->v2=edgesplit[mf->v1*totvert+mf->v2];
+ df3->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4];
+
+ df3->v3=mf->v4;
+ df2->v1=mf->v1;
+ df1->v4=df2->v4=df3->v4=0;
+
+ mf->v1=df1->v2;
+ mf->v4=df1->v3;
+
+ facepa[i]=facepa[curdupface-3]=v2;
+ facepa[curdupface-1]=v4;
+ facepa[curdupface-2]=v1;
+ }
+ else if(v3==v4){
+ df3->v2=df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2];
+ df2->v3=df1->v2=edgesplit[mf->v2*totvert+mf->v3];
+ df3->v3=df1->v3=edgesplit[mf->v1*totvert+mf->v4];
+
+ df3->v1=mf->v1;
+ df2->v2=mf->v2;
+ df1->v4=df2->v4=df3->v4=0;
+
+ mf->v1=df1->v3;
+ mf->v2=df1->v2;
+
+ facepa[i]=facepa[curdupface-3]=v3;
+ facepa[curdupface-1]=v1;
+ facepa[curdupface-2]=v2;
+ }
+ else{
+ df3->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2];
+ df3->v3=df2->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3];
+ df2->v3=df1->v3=edgesplit[mf->v3*totvert+mf->v4];
+
+ df3->v2=mf->v2;
+ df2->v2=mf->v3;
+ df1->v4=df2->v4=df3->v4=0;
+
+ mf->v2=df1->v1;
+ mf->v3=df1->v3;
+
+ facepa[i]=facepa[curdupface-3]=v1;
+ facepa[curdupface-1]=v2;
+ facepa[curdupface-2]=v3;
+ }
+
+ test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3));
+ }
+ else if(*fs==4){
+ if(v1!=v2 && v2!=v3){
+
+ /* set new vert to face center */
+ mv=CDDM_get_vert(splitdm,mf->v1);
+ dupve=CDDM_get_vert(splitdm,curdupin);
+ DM_copy_vert_data(splitdm,splitdm,mf->v1,curdupin,1);
+ *dupve=*mv;
+
+ mv=CDDM_get_vert(splitdm,mf->v2);
+ VECADD(dupve->co,dupve->co,mv->co);
+ mv=CDDM_get_vert(splitdm,mf->v3);
+ VECADD(dupve->co,dupve->co,mv->co);
+ mv=CDDM_get_vert(splitdm,mf->v4);
+ VECADD(dupve->co,dupve->co,mv->co);
+ VecMulf(dupve->co,0.25);
+
+
+ df1=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df1=*mf;
+ curdupface++;
+
+ df2=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df2=*mf;
+ curdupface++;
+
+ df3=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df3=*mf;
+ curdupface++;
+
+ df1->v1=edgesplit[mf->v1*totvert+mf->v2];
+ df3->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3];
+
+ df2->v1=edgesplit[mf->v1*totvert+mf->v4];
+ df3->v4=df2->v3=edgesplit[mf->v3*totvert+mf->v4];
+
+ df3->v1=df2->v2=df1->v4=curdupin;
+
+ mf->v2=df1->v1;
+ mf->v3=curdupin;
+ mf->v4=df2->v1;
+
+ curdupin++;
+
+ facepa[i]=v1;
+ facepa[curdupface-3]=v2;
+ facepa[curdupface-2]=v3;
+ facepa[curdupface-1]=v4;
+
+ test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3));
+
+ test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3));
+ }
+ else{
+ df1=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df1=*mf;
+ curdupface++;
+
+ df2=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df2=*mf;
+ curdupface++;
+
+ df3=CDDM_get_face(splitdm,curdupface);
+ DM_copy_face_data(splitdm,splitdm,i,curdupface,1);
+ *df3=*mf;
+ curdupface++;
+
+ if(v2==v3){
+ df1->v1=edgesplit[mf->v1*totvert+mf->v2];
+ df3->v1=df1->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3];
+ df2->v1=df1->v4=edgesplit[mf->v1*totvert+mf->v4];
+
+ df3->v3=df2->v3=edgesplit[mf->v3*totvert+mf->v4];
+
+ df3->v2=mf->v3;
+ df3->v4=0;
+
+ mf->v2=df1->v1;
+ mf->v3=df1->v4;
+ mf->v4=0;
+
+ facepa[i]=v1;
+ facepa[curdupface-3]=facepa[curdupface-2]=v2;
+ facepa[curdupface-1]=v3;
+ }
+ else{
+ df3->v1=df2->v1=df1->v2=edgesplit[mf->v1*totvert+mf->v2];
+ df2->v4=df1->v3=edgesplit[mf->v3*totvert+mf->v4];
+ df1->v4=edgesplit[mf->v1*totvert+mf->v4];
+
+ df3->v3=df2->v2=edgesplit[mf->v2*totvert+mf->v3];
+
+ df3->v4=0;
+
+ mf->v1=df1->v4;
+ mf->v2=df1->v3;
+ mf->v3=mf->v4;
+ mf->v4=0;
+
+ facepa[i]=v4;
+ facepa[curdupface-3]=facepa[curdupface-2]=v1;
+ facepa[curdupface-1]=v2;
+ }
+
+ test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3));
+ test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3));
+ }
+ }
+
+ test_index_face(df1, &splitdm->faceData, i, (df1->v4 ? 4 : 3));
+ }
+ }
+
+ MEM_freeN(edgesplit);
+ MEM_freeN(facesplit);
+ MEM_freeN(vertpa);
+
+ return splitdm;
+
+}
+static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd,
+ ParticleSystemModifierData *psmd, Object *ob,
+ DerivedMesh *to_explode)
+{
+ DerivedMesh *explode, *dm=to_explode;
+ MFace *mf=0;
+ MVert *dupvert=0;
+ ParticleSettings *part=psmd->psys->part;
+ ParticleData *pa, *pars=psmd->psys->particles;
+ ParticleKey state;
+ float *vertco=0, imat[4][4];
+ float loc0[3], nor[3];
+ float timestep, cfra;
+ int *facepa=emd->facepa, *vertpa=0;
+ int totdup=0,totvert=0,totface=0,totpart=0;
+ int i, j, v, mindex=0;
+
+ totface= dm->getNumFaces(dm);
+ totvert= dm->getNumVerts(dm);
+ totpart= psmd->psys->totpart;
+
+ timestep= psys_get_timestep(part);
+
+ if(part->flag & PART_GLOB_TIME)
+ cfra=bsystem_time(0,(float)G.scene->r.cfra,0.0);
+ else
+ cfra=bsystem_time(ob,(float)G.scene->r.cfra,0.0);
+
+ /* table for vertice <-> particle relations (row totpart+1 is for yet unexploded verts) */
+ vertpa = MEM_callocN(sizeof(int)*(totpart+1)*totvert, "explode_vertpatab");
+ for(i=0; i<(totpart+1)*totvert; i++)
+ vertpa[i] = -1;
+
+ for (i=0; i<totface; i++) {
+ if(facepa[i]==totpart || cfra <= (pars+facepa[i])->time)
+ mindex = totpart*totvert;
+ else
+ mindex = facepa[i]*totvert;
+
+ mf=CDDM_get_face(dm,i);
+
+ /*set face vertices to exist in particle group*/
+ vertpa[mindex+mf->v1] = 1;
+ vertpa[mindex+mf->v2] = 1;
+ vertpa[mindex+mf->v3] = 1;
+ if(mf->v4)
+ vertpa[mindex+mf->v4] = 1;
+ }
+
+ /*make new vertice indexes & count total vertices after duplication*/
+ for(i=0; i<(totpart+1)*totvert; i++){
+ if(vertpa[i] != -1)
+ vertpa[i] = totdup++;
+ }
+
+ /*the final duplicated vertices*/
+ explode= CDDM_from_template(dm, totdup, 0,totface);
+ dupvert= CDDM_get_verts(explode);
+
+ /* getting back to object space */
+ Mat4Invert(imat,ob->obmat);
+
+ psmd->psys->lattice = psys_get_lattice(ob, psmd->psys);
+
+ /*duplicate & displace vertices*/
+ for(i=0, pa=pars; i<=totpart; i++, pa++){
+ if(i!=totpart){
+ psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,loc0,nor,0,0);
+ Mat4MulVecfl(ob->obmat,loc0);
+
+ state.time=cfra;
+ psys_get_particle_state(ob,psmd->psys,i,&state,1);
+ }
+
+ for(j=0; j<totvert; j++){
+ v=vertpa[i*totvert+j];
+ if(v != -1) {
+ MVert source;
+ MVert *dest;
+
+ dm->getVert(dm, j, &source);
+ dest = CDDM_get_vert(explode,v);
+
+ DM_copy_vert_data(dm,explode,j,v,1);
+ *dest = source;
+
+ if(i!=totpart){
+ vertco=CDDM_get_vert(explode,v)->co;
+
+ Mat4MulVecfl(ob->obmat,vertco);
+
+ VECSUB(vertco,vertco,loc0);
+
+ /* apply rotation, size & location */
+ QuatMulVecf(state.rot,vertco);
+ VecMulf(vertco,pa->size);
+ VECADD(vertco,vertco,state.co);
+
+ Mat4MulVecfl(imat,vertco);
+ }
+ }
+ }
+ }
+
+ /*map new vertices to faces*/
+ for (i=0; i<totface; i++) {
+ MFace source;
+ int orig_v4;
+
+ if(facepa[i]!=totpart)
+ {
+ pa=pars+facepa[i];
+
+ if(pa->alive==PARS_UNBORN && (emd->flag&eExplodeFlag_Unborn)==0) continue;
+ if(pa->alive==PARS_ALIVE && (emd->flag&eExplodeFlag_Alive)==0) continue;
+ if(pa->alive==PARS_DEAD && (emd->flag&eExplodeFlag_Dead)==0) continue;
+ }
+
+ dm->getFace(dm,i,&source);
+ mf=CDDM_get_face(explode,i);
+
+ orig_v4 = source.v4;
+
+ if(facepa[i]!=totpart && cfra <= pa->time)
+ mindex = totpart*totvert;
+ else
+ mindex = facepa[i]*totvert;
+
+ source.v1 = vertpa[mindex+source.v1];
+ source.v2 = vertpa[mindex+source.v2];
+ source.v3 = vertpa[mindex+source.v3];
+ if(source.v4)
+ source.v4 = vertpa[mindex+source.v4];
+
+ DM_copy_face_data(dm,explode,i,i,1);
+
+ *mf = source;
+
+ test_index_face(mf, &explode->faceData, i, (mf->v4 ? 4 : 3));
+ }
+
+
+ /* cleanup */
+ if(vertpa) MEM_freeN(vertpa);
+
+ /* finalization */
+ CDDM_calc_edges(explode);
+ CDDM_calc_normals(explode);
+
+ if(psmd->psys->lattice){
+ end_latt_deform();
+ psmd->psys->lattice=0;
+ }
+
+ return explode;
+}
+
+static ParticleSystemModifierData * explodeModifier_findPrecedingParticlesystem(Object *ob, ModifierData *emd)
+{
+ ModifierData *md;
+ ParticleSystemModifierData *psmd=0;
+
+ for (md=ob->modifiers.first; emd!=md; md=md->next){
+ if(md->type==eModifierType_ParticleSystem)
+ psmd= (ParticleSystemModifierData*) md;
+ }
+ return psmd;
+}
+static DerivedMesh * explodeModifier_applyModifier(
+ ModifierData *md, Object *ob, DerivedMesh *derivedData,
+ int useRenderParams, int isFinalCalc)
+{
+ DerivedMesh *dm = derivedData;
+ ExplodeModifierData *emd= (ExplodeModifierData*) md;
+ ParticleSystemModifierData *psmd=explodeModifier_findPrecedingParticlesystem(ob,md);;
+
+ if(psmd){
+ ParticleSystem * psys=psmd->psys;
+
+ if(psys==0 || psys->totpart==0) return derivedData;
+ if(psys->part==0 || psys->particles==0) return derivedData;
+
+ /* 1. find faces to be exploded if needed */
+ if(emd->facepa==0 || psmd->flag&eParticleSystemFlag_Pars || emd->flag&eExplodeFlag_CalcFaces){
+ if(psmd->flag & eParticleSystemFlag_Pars)
+ psmd->flag &= ~eParticleSystemFlag_Pars;
+
+ if(emd->flag & eExplodeFlag_CalcFaces)
+ emd->flag &= ~eExplodeFlag_CalcFaces;
+
+ explodeModifier_createFacepa(emd,psmd,ob,derivedData);
+ }
+
+ /* 2. create new mesh */
+ if(emd->flag & eExplodeFlag_EdgeSplit){
+ int *facepa = emd->facepa;
+ DerivedMesh *splitdm=explodeModifier_splitEdges(emd,dm);
+ DerivedMesh *explode=explodeModifier_explodeMesh(emd,psmd,ob,splitdm);
+
+ MEM_freeN(emd->facepa);
+ emd->facepa=facepa;
+ splitdm->release(splitdm);
+ return explode;
+ }
+ else
+ return explodeModifier_explodeMesh(emd,psmd,ob,derivedData);
+ }
+ return derivedData;
+}
/* MeshDeform */
static void meshdeformModifier_initData(ModifierData *md)
@@ -5535,6 +6755,42 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type)
mti->deformVerts = meshdeformModifier_deformVerts;
mti->deformVertsEM = meshdeformModifier_deformVertsEM;
+ mti = INIT_TYPE(ParticleSystem);
+ mti->type = eModifierTypeType_OnlyDeform;
+ mti->flags = eModifierTypeFlag_AcceptsMesh
+ |eModifierTypeFlag_SupportsEditmode
+ |eModifierTypeFlag_EnableInEditmode;
+ mti->initData = particleSystemModifier_initData;
+ mti->freeData = particleSystemModifier_freeData;
+ mti->copyData = particleSystemModifier_copyData;
+ mti->deformVerts = particleSystemModifier_deformVerts;
+ mti->deformVertsEM = particleSystemModifier_deformVertsEM;
+ mti->requiredDataMask = particleSystemModifier_requiredDataMask;
+
+ mti = INIT_TYPE(ParticleInstance);
+ mti->type = eModifierTypeType_Constructive;
+ mti->flags = eModifierTypeFlag_AcceptsMesh
+ | eModifierTypeFlag_SupportsMapping
+ | eModifierTypeFlag_SupportsEditmode
+ | eModifierTypeFlag_EnableInEditmode;
+ mti->initData = particleInstanceModifier_initData;
+ mti->copyData = particleInstanceModifier_copyData;
+ mti->dependsOnTime = particleInstanceModifier_dependsOnTime;
+ mti->foreachObjectLink = particleInstanceModifier_foreachObjectLink;
+ mti->applyModifier = particleInstanceModifier_applyModifier;
+ mti->applyModifierEM = particleInstanceModifier_applyModifierEM;
+ mti->updateDepgraph = particleInstanceModifier_updateDepgraph;
+
+ mti = INIT_TYPE(Explode);
+ mti->type = eModifierTypeType_Nonconstructive;
+ mti->flags = eModifierTypeFlag_AcceptsMesh;
+ mti->initData = explodeModifier_initData;
+ mti->freeData = explodeModifier_freeData;
+ mti->copyData = explodeModifier_copyData;
+ mti->dependsOnTime = explodeModifier_dependsOnTime;
+ mti->requiredDataMask = explodeModifier_requiredDataMask;
+ mti->applyModifier = explodeModifier_applyModifier;
+
typeArrInit = 0;
#undef INIT_TYPE
}
@@ -5727,6 +6983,13 @@ int modifiers_isSoftbodyEnabled(Object *ob)
return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render));
}
+int modifiers_isParticleEnabled(Object *ob)
+{
+ ModifierData *md = modifiers_findByType(ob, eModifierType_ParticleSystem);
+
+ return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render));
+}
+
LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask)
{
LinkNode *dataMasks = NULL;
@@ -5910,3 +7173,13 @@ int modifiers_isDeformed(Object *ob)
return 0;
}
+int modifiers_indexInObject(Object *ob, ModifierData *md_seek)
+{
+ int i= 0;
+ ModifierData *md;
+
+ for (md=ob->modifiers.first; (md && md_seek!=md); md=md->next, i++);
+ if (!md) return -1; /* modifier isnt in the object */
+ return i;
+}
+
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 095d6f525a9..d9b2ce84fa5 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -60,6 +60,7 @@
#include "DNA_object_force.h"
#include "DNA_object_fluidsim.h"
#include "DNA_oops_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -101,6 +102,7 @@
#include "BKE_mball.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_property.h"
#include "BKE_sca.h"
#include "BKE_scene.h"
@@ -164,6 +166,15 @@ void object_free_modifiers(Object *ob)
modifier_free(md);
}
+
+ /* particle modifiers were freed, so free the particlesystems as well */
+ while(ob->particlesystem.first){
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ BLI_remlink(&ob->particlesystem,psys);
+
+ psys_free(ob,psys);
+ }
}
/* here we will collect all local displist stuff */
@@ -235,7 +246,11 @@ void free_object(Object *ob)
BPY_free_scriptlink(&ob->scriptlink);
- if(ob->pd) MEM_freeN(ob->pd);
+ if(ob->pd){
+ if(ob->pd->tex)
+ ob->pd->tex->id.us--;
+ MEM_freeN(ob->pd);
+ }
if(ob->soft) sbFree(ob->soft);
if(ob->fluidsimSettings) fluidsimSettingsFree(ob->fluidsimSettings);
}
@@ -380,6 +395,47 @@ void unlink_object(Object *ob)
}
}
+ /* particle systems */
+ if(obt->particlesystem.first) {
+ ParticleSystem *tpsys= obt->particlesystem.first;
+ for(; tpsys; tpsys=tpsys->next) {
+ if(tpsys->keyed_ob==ob) {
+ ParticleSystem *psys= BLI_findlink(&ob->particlesystem,tpsys->keyed_psys-1);
+
+ if(psys && psys->keyed_ob) {
+ tpsys->keyed_ob= psys->keyed_ob;
+ tpsys->keyed_psys= psys->keyed_psys;
+ }
+ else
+ tpsys->keyed_ob= NULL;
+
+ obt->recalc |= OB_RECALC_DATA;
+ }
+
+ if(tpsys->target_ob==ob) {
+ tpsys->target_ob= NULL;
+ obt->recalc |= OB_RECALC_DATA;
+ }
+
+ if(tpsys->part->dup_ob==ob)
+ tpsys->part->dup_ob= NULL;
+
+ if(tpsys->part->flag&PART_STICKY) {
+ ParticleData *pa;
+ int p;
+
+ for(p=0,pa=tpsys->particles; p<tpsys->totpart; p++,pa++) {
+ if(pa->stick_ob==ob) {
+ pa->stick_ob= 0;
+ pa->flag &= ~PARS_STICKY;
+ }
+ }
+ }
+ }
+ if(ob->pd)
+ obt->recalc |= OB_RECALC_DATA;
+ }
+
obt= obt->id.next;
}
@@ -946,6 +1002,22 @@ SoftBody *copy_softbody(SoftBody *sb)
return sbn;
}
+ParticleSystem *copy_particlesystem(ParticleSystem *psys)
+{
+ ParticleSystem *psysn;
+
+ psysn= MEM_dupallocN(psys);
+ psysn->particles= MEM_dupallocN(psys->particles);
+
+ psysn->child= MEM_dupallocN(psys->child);
+
+ psysn->effectors.first= psysn->effectors.last= 0;
+
+ id_us_plus((ID *)psysn->part);
+
+ return psysn;
+}
+
static void copy_object_pose(Object *obn, Object *ob)
{
bPoseChannel *chan;
@@ -981,6 +1053,7 @@ Object *copy_object(Object *ob)
{
Object *obn;
ModifierData *md;
+ ParticleSystem *psys;
int a;
obn= copy_libblock(ob);
@@ -1031,7 +1104,11 @@ Object *copy_object(Object *ob)
obn->disp.first= obn->disp.last= NULL;
- if(ob->pd) obn->pd= MEM_dupallocN(ob->pd);
+ if(ob->pd){
+ obn->pd= MEM_dupallocN(ob->pd);
+ if(obn->pd->tex)
+ id_us_plus(&(obn->pd->tex->id));
+ }
obn->soft= copy_softbody(ob->soft);
/* NT copy fluid sim setting memory */
@@ -1042,6 +1119,23 @@ Object *copy_object(Object *ob)
obn->fluidsimSettings->orgMesh = (Mesh *)obn->data;
}
}
+
+ obn->particlesystem.first= obn->particlesystem.last= NULL;
+ for(psys=ob->particlesystem.first; psys; psys=psys->next) {
+ ParticleSystemModifierData *psmd;
+ ParticleSystem *npsys= copy_particlesystem(psys);
+
+ BLI_addtail(&obn->particlesystem, npsys);
+
+ /* need to update particle modifiers too */
+ for(md=obn->modifiers.first; md; md=md->next) {
+ if(md->type==eModifierType_ParticleSystem) {
+ psmd= (ParticleSystemModifierData*)md;
+ if(psmd->psys==psys)
+ psmd->psys= npsys;
+ }
+ }
+ }
obn->derivedDeform = NULL;
obn->derivedFinal = NULL;
@@ -2077,8 +2171,28 @@ void object_handle_update(Object *ob)
where_is_pose(ob);
}
}
+
+ if(ob->particlesystem.first) {
+ ParticleSystem *tpsys, *psys;
+
+ psys= ob->particlesystem.first;
+ while(psys) {
+ if(psys->flag & PSYS_ENABLED) {
+ particle_system_update(ob, psys);
+ psys= psys->next;
+ }
+ else if(psys->flag & PSYS_DELETE) {
+ tpsys=psys->next;
+ BLI_remlink(&ob->particlesystem, psys);
+ psys_free(ob,psys);
+ psys= tpsys;
+ }
+ else
+ psys= psys->next;
+ }
+ }
}
-
+
/* the no-group proxy case, we call update */
if(ob->proxy && ob->proxy_group==NULL) {
/* set pointer in library proxy target, for copying, but restore it */
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
new file mode 100644
index 00000000000..c0de1901f69
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle.c
@@ -0,0 +1,2978 @@
+/* particle.c
+ *
+ *
+ * $Id: particle.c $
+ *
+ * ***** BEGIN GPL/BL DUAL 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. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License. See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * 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) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_texture_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_key_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_kdtree.h"
+#include "BLI_linklist.h"
+#include "BLI_rand.h"
+
+#include "BKE_anim.h"
+
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_lattice.h"
+#include "BKE_utildefines.h"
+#include "BKE_displist.h"
+#include "BKE_particle.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_ipo.h"
+#include "BKE_object.h"
+#include "BKE_softbody.h"
+#include "BKE_material.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_depsgraph.h"
+#include "BKE_bad_level_calls.h"
+#include "BKE_modifier.h"
+
+#include "blendef.h"
+#include "RE_render_ext.h"
+
+static void key_from_object(Object *ob, ParticleKey *key);
+static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index,
+ float *fuv, float *orco, ParticleTexture *ptex, int event);
+
+/* few helpers for countall etc. */
+int count_particles(ParticleSystem *psys){
+ ParticleSettings *part=psys->part;
+ ParticleData *pa;
+ int tot=0,p;
+
+ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++){
+ if(pa->alive == PARS_KILLED);
+ else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0);
+ else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0);
+ else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP));
+ else tot++;
+ }
+ return tot;
+}
+int count_particles_mod(ParticleSystem *psys, int totgr, int cur){
+ ParticleSettings *part=psys->part;
+ ParticleData *pa;
+ int tot=0,p;
+
+ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++){
+ if(pa->alive == PARS_KILLED);
+ else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0);
+ else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0);
+ else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP));
+ else if(p%totgr==cur) tot++;
+ }
+ return tot;
+}
+int psys_count_keys(ParticleSystem *psys)
+{
+ ParticleData *pa;
+ int i, totpart=psys->totpart, totkey=0;
+
+ for(i=0, pa=psys->particles; i<totpart; i++, pa++)
+ totkey += pa->totkey;
+
+ return totkey;
+}
+/* remember to free the pointer returned from this! */
+char *psys_menu_string(Object *ob, int for_sb)
+{
+ ParticleSystem *psys;
+ DynStr *ds;
+ char *str, num[6];
+ int i;
+
+ ds = BLI_dynstr_new();
+
+ if(for_sb)
+ BLI_dynstr_append(ds, "|Object%x-1");
+
+ for(i=0,psys=ob->particlesystem.first; psys; i++,psys=psys->next){
+
+ BLI_dynstr_append(ds, "|");
+ sprintf(num,"%i. ",i+1);
+ BLI_dynstr_append(ds, num);
+ BLI_dynstr_append(ds, psys->part->id.name+2);
+ sprintf(num,"%%x%i",i+1);
+ BLI_dynstr_append(ds, num);
+ }
+
+ str = BLI_dynstr_get_cstring(ds);
+
+ BLI_dynstr_free(ds);
+
+ return str;
+}
+/************************************************/
+/* Getting stuff */
+/************************************************/
+/* get object's active particle system safely */
+ParticleSystem *psys_get_current(Object *ob)
+{
+ ParticleSystem *psys;
+ if(ob==0) return 0;
+
+ for(psys=ob->particlesystem.first; psys; psys=psys->next){
+ if(psys->flag & PSYS_CURRENT)
+ return psys;
+ }
+
+ return 0;
+}
+short psys_get_current_num(Object *ob)
+{
+ ParticleSystem *psys;
+ short i;
+
+ if(ob==0) return 0;
+
+ for(psys=ob->particlesystem.first, i=0; psys; psys=psys->next, i++)
+ if(psys->flag & PSYS_CURRENT)
+ return i;
+
+ return i;
+}
+/* change object's active particle system */
+void psys_change_act(void *ob_v, void *act_v)
+{
+ Object *ob = ob_v;
+ ParticleSystem *npsys, *psys;
+ short act = *((short*)act_v)-1;
+
+ if(act>=0){
+ npsys=BLI_findlink(&ob->particlesystem,act);
+ psys=psys_get_current(ob);
+
+ if(psys)
+ psys->flag &= ~PSYS_CURRENT;
+ if(npsys)
+ npsys->flag |= PSYS_CURRENT;
+ }
+}
+Object *psys_get_lattice(Object *ob, ParticleSystem *psys)
+{
+ Object *lattice=0;
+
+ if(!psys_in_edit_mode(psys)==0){
+
+ ModifierData *md = (ModifierData*)psys_get_modifier(ob,psys);
+
+ for(; md; md=md->next){
+ if(md->type==eModifierType_Lattice){
+ LatticeModifierData *lmd = (LatticeModifierData *)md;
+ lattice=lmd->object;
+ break;
+ }
+ }
+ if(lattice)
+ init_latt_deform(lattice,0);
+ }
+
+ return lattice;
+}
+void psys_disable_all(Object *ob)
+{
+ ParticleSystem *psys=ob->particlesystem.first;
+
+ for(; psys; psys=psys->next)
+ psys->flag &= ~PSYS_ENABLED;
+}
+void psys_enable_all(Object *ob)
+{
+ ParticleSystem *psys=ob->particlesystem.first;
+
+ for(; psys; psys=psys->next)
+ psys->flag |= PSYS_ENABLED;
+}
+int psys_ob_has_hair(Object *ob)
+{
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ for(; psys; psys=psys->next)
+ if(psys->part->type == PART_HAIR)
+ return 1;
+
+ return 0;
+}
+int psys_in_edit_mode(ParticleSystem *psys)
+{
+ return ((G.f & G_PARTICLEEDIT) && psys==psys_get_current(OBACT) && psys->edit);
+}
+
+/************************************************/
+/* Freeing stuff */
+/************************************************/
+void psys_free_settings(ParticleSettings *part)
+{
+ if(part->pd)
+ MEM_freeN(part->pd);
+}
+void free_hair(ParticleSystem *psys)
+{
+ ParticleData *pa;
+ int i, totpart=psys->totpart;
+
+ for(i=0, pa=psys->particles; i<totpart; i++, pa++) {
+ if(pa->hair)
+ MEM_freeN(pa->hair);
+ pa->hair = NULL;
+ }
+
+ psys->flag &= ~PSYS_HAIR_DONE;
+}
+void free_keyed_keys(ParticleSystem *psys)
+{
+ if(psys->particles && psys->particles->keys)
+ MEM_freeN(psys->particles->keys);
+}
+void free_child_path_cache(ParticleSystem *psys)
+{
+
+ if(psys->childcache){
+ if(psys->childcache[0])
+ MEM_freeN(psys->childcache[0]);
+
+ MEM_freeN(psys->childcache);
+
+ psys->childcache = NULL;
+ psys->totchildcache = 0;
+ }
+}
+void psys_free_path_cache(ParticleSystem *psys)
+{
+ if(psys->pathcache){
+ if(psys->pathcache[0])
+ MEM_freeN(psys->pathcache[0]);
+
+ MEM_freeN(psys->pathcache);
+
+ psys->pathcache = NULL;
+ psys->totcached = 0;
+ }
+ free_child_path_cache(psys);
+}
+/* free everything */
+void psys_free(Object *ob, ParticleSystem * psys)
+{
+ if(psys){
+ if(ob->particlesystem.first == NULL && G.f & G_PARTICLEEDIT)
+ G.f &= ~G_PARTICLEEDIT;
+
+ psys_free_path_cache(psys);
+
+ free_hair(psys);
+
+ free_keyed_keys(psys);
+
+ PE_free_particle_edit(psys);
+
+ if(psys->particles){
+ MEM_freeN(psys->particles);
+ psys->particles = 0;
+ psys->totpart = 0;
+ }
+
+ if(psys->child){
+ MEM_freeN(psys->child);
+ psys->child = 0;
+ psys->totchild = 0;
+ }
+
+ if(psys->effectors.first)
+ psys_end_effectors(psys);
+
+ if(psys->part){
+ psys->part->id.us--;
+ psys->part=0;
+ }
+
+ if(psys->soft){
+ sbFree(psys->soft);
+ psys->soft = 0;
+ }
+
+ MEM_freeN(psys);
+ }
+}
+
+/************************************************/
+/* Interpolated Particles */
+/************************************************/
+static float interpolate_particle_value(float v1, float v2, float v3, float v4, float *w, int four)
+{
+ float value;
+
+ value= w[0]*v1 + w[1]*v2 + w[2]*v3;
+ if(four)
+ value += w[3]*v4;
+
+ return value;
+}
+static void weighted_particle_vector(float *v1, float *v2, float *v3, float *v4, float *weights, float *vec)
+{
+ vec[0]= weights[0]*v1[0] + weights[1]*v2[0] + weights[2]*v3[0] + weights[3]*v4[0];
+ vec[1]= weights[0]*v1[1] + weights[1]*v2[1] + weights[2]*v3[1] + weights[3]*v4[1];
+ vec[2]= weights[0]*v1[2] + weights[1]*v2[2] + weights[2]*v3[2] + weights[3]*v4[2];
+}
+static void interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result)
+{
+ float t[4];
+
+ if(type<0) {
+ VecfCubicInterpol(keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt, result->co, result->vel);
+ }
+ else {
+ set_four_ipo(dt, t, type);
+
+ weighted_particle_vector(keys[0].co, keys[1].co, keys[2].co, keys[3].co, t, result->co);
+
+ //if(ve){
+ // if(dt>0.999f){
+ // set_four_ipo(dt+0.001f,t,ipo_type);
+ // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp);
+ // VECSUB(ve,temp,co);
+ // }
+ // else{
+ // set_four_ipo(dt-0.001f,t,ipo_type);
+ // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp);
+ // VECSUB(ve,co,temp);
+ // }
+ //}
+ }
+}
+
+
+
+/************************************************/
+/* Particles on a dm */
+/************************************************/
+/* interpolate a location on a face based on face coordinates */
+void psys_interpolate_face(MVert *mvert, MFace *mface, MTFace *tface, float *w, float *vec, float *nor, float *utan, float *vtan){
+ float *v1=0, *v2=0, *v3=0, *v4=0;
+ float e1[3],e2[3],s1,s2,t1,t2;
+ float *uv1, *uv2, *uv3, *uv4;
+ float n1[3], n2[3], n3[3], n4[3];
+ float tuv[4][2];
+
+ v1= (mvert+mface->v1)->co;
+ v2= (mvert+mface->v2)->co;
+ v3= (mvert+mface->v3)->co;
+ VECCOPY(n1,(mvert+mface->v1)->no);
+ VECCOPY(n2,(mvert+mface->v2)->no);
+ VECCOPY(n3,(mvert+mface->v3)->no);
+ Normalize(n1);
+ Normalize(n2);
+ Normalize(n3);
+
+ if(mface->v4) {
+ v4= (mvert+mface->v4)->co;
+ VECCOPY(n4,(mvert+mface->v4)->no);
+ Normalize(n4);
+
+ vec[0]= w[0]*v1[0] + w[1]*v2[0] + w[2]*v3[0] + w[3]*v4[0];
+ vec[1]= w[0]*v1[1] + w[1]*v2[1] + w[2]*v3[1] + w[3]*v4[1];
+ vec[2]= w[0]*v1[2] + w[1]*v2[2] + w[2]*v3[2] + w[3]*v4[2];
+
+ if(nor){
+ if(mface->flag & ME_SMOOTH){
+ nor[0]= w[0]*n1[0] + w[1]*n2[0] + w[2]*n3[0] + w[3]*n4[0];
+ nor[1]= w[0]*n1[1] + w[1]*n2[1] + w[2]*n3[1] + w[3]*n4[1];
+ nor[2]= w[0]*n1[2] + w[1]*n2[2] + w[2]*n3[2] + w[3]*n4[2];
+ }
+ else
+ CalcNormFloat4(v1,v2,v3,v4,nor);
+ }
+ }
+ else {
+ vec[0]= w[0]*v1[0] + w[1]*v2[0] + w[2]*v3[0];
+ vec[1]= w[0]*v1[1] + w[1]*v2[1] + w[2]*v3[1];
+ vec[2]= w[0]*v1[2] + w[1]*v2[2] + w[2]*v3[2];
+
+ if(nor){
+ if(mface->flag & ME_SMOOTH){
+ nor[0]= w[0]*n1[0] + w[1]*n2[0] + w[2]*n3[0];
+ nor[1]= w[0]*n1[1] + w[1]*n2[1] + w[2]*n3[1];
+ nor[2]= w[0]*n1[2] + w[1]*n2[2] + w[2]*n3[2];
+ }
+ else
+ CalcNormFloat(v1,v2,v3,nor);
+ }
+ }
+
+ /* calculate tangent vectors */
+ if(utan && vtan){
+ if(tface){
+ uv1= tface->uv[0];
+ uv2= tface->uv[1];
+ uv3= tface->uv[2];
+ uv4= tface->uv[3];
+ }
+ else{
+ uv1= tuv[0]; uv2= tuv[1]; uv3= tuv[2]; uv4= tuv[3];
+ spheremap(v1[0], v1[1], v1[2], uv1, uv1+1);
+ spheremap(v2[0], v2[1], v2[2], uv2, uv2+1);
+ spheremap(v3[0], v3[1], v3[2], uv3, uv3+1);
+ if(v4)
+ spheremap(v4[0], v4[1], v4[2], uv4, uv4+1);
+ }
+
+ if(v4){
+ s1= uv3[0] - uv1[0];
+ s2= uv4[0] - uv1[0];
+
+ t1= uv3[1] - uv1[1];
+ t2= uv4[1] - uv1[1];
+
+ VecSubf(e1, v3, v1);
+ VecSubf(e2, v4, v1);
+ }
+ else{
+ s1= uv2[0] - uv1[0];
+ s2= uv3[0] - uv1[0];
+
+ t1= uv2[1] - uv1[1];
+ t2= uv3[1] - uv1[1];
+
+ VecSubf(e1, v2, v1);
+ VecSubf(e2, v3, v1);
+ }
+
+ vtan[0] = (s1*e2[0] - s2*e1[0]);
+ vtan[1] = (s1*e2[1] - s2*e1[1]);
+ vtan[2] = (s1*e2[2] - s2*e1[2]);
+
+ utan[0] = (t1*e2[0] - t2*e1[0]);
+ utan[1] = (t1*e2[1] - t2*e1[1]);
+ utan[2] = (t1*e2[2] - t2*e1[2]);
+ }
+}
+void psys_interpolate_uvs(MTFace *tface, int quad, float *w, float *uvco){
+ float v10= tface->uv[0][0];
+ float v11= tface->uv[0][1];
+ float v20= tface->uv[1][0];
+ float v21= tface->uv[1][1];
+ float v30= tface->uv[2][0];
+ float v31= tface->uv[2][1];
+ float v40,v41;
+
+ if(quad) {
+ v40= tface->uv[3][0];
+ v41= tface->uv[3][1];
+
+ uvco[0]= w[0]*v10 + w[1]*v20 + w[2]*v30 + w[3]*v40;
+ uvco[1]= w[0]*v11 + w[1]*v21 + w[2]*v31 + w[3]*v41;
+ }
+ else {
+ uvco[0]= w[0]*v10 + w[1]*v20 + w[2]*v30;
+ uvco[1]= w[0]*v11 + w[1]*v21 + w[2]*v31;
+ }
+}
+float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, float *fw, float *values)
+{
+ if(values==0)
+ return 0.0;
+
+ switch(from){
+ case PART_FROM_VERT:
+ return values[index];
+ case PART_FROM_FACE:
+ case PART_FROM_VOLUME:
+ {
+ MFace *mf=dm->getFaceData(dm,index,CD_MFACE);
+ return interpolate_particle_value(values[mf->v1],values[mf->v2],values[mf->v3],values[mf->v4],fw,mf->v4);
+ }
+
+ }
+ return 0.0;
+}
+
+/* conversion of pa->fw to origspace layer coordinates */
+static void psys_w_to_origspace(float *w, float *uv)
+{
+ uv[0]= w[1] + w[2];
+ uv[1]= w[2] + w[3];
+}
+
+/* conversion of pa->fw to weights in face from origspace */
+static void psys_origspace_to_w(OrigSpaceFace *osface, int quad, float *w, float *neww)
+{
+ float v[4][3], co[3];
+
+ v[0][0]= osface->uv[0][0]; v[0][1]= osface->uv[0][1]; v[0][2]= 0.0f;
+ v[1][0]= osface->uv[1][0]; v[1][1]= osface->uv[1][1]; v[1][2]= 0.0f;
+ v[2][0]= osface->uv[2][0]; v[2][1]= osface->uv[2][1]; v[2][2]= 0.0f;
+
+ psys_w_to_origspace(w, co);
+ co[2]= 0.0f;
+
+ if(quad) {
+ v[3][0]= osface->uv[3][0]; v[3][1]= osface->uv[3][1]; v[3][2]= 0.0f;
+ MeanValueWeights(v, 4, co, neww);
+ }
+ else {
+ MeanValueWeights(v, 3, co, neww);
+ neww[3]= 0.0f;
+ }
+}
+
+/* find the derived mesh face for a particle, set the mf passed.
+This is slow, can be optimized but only for many lookups, return the face lookup index*/
+int psys_particle_dm_face_lookup(Object *ob, DerivedMesh *dm, int index, float *fw, struct LinkNode *node)
+{
+ Mesh *me= (Mesh*)ob->data;
+ MFace *mface;
+ OrigSpaceFace *osface;
+ int *origindex;
+ int quad, findex, totface;
+ float uv[2], (*faceuv)[2];
+
+ mface = dm->getFaceDataArray(dm, CD_MFACE);
+ origindex = dm->getFaceDataArray(dm, CD_ORIGINDEX);
+ osface = dm->getFaceDataArray(dm, CD_ORIGSPACE);
+
+ totface = dm->getNumFaces(dm);
+
+ if(osface==NULL || origindex==NULL) {
+ /* Assume we dont need osface data */
+ if (index <totface) {
+ printf("\tNO CD_ORIGSPACE, assuming not needed\n");
+ return index;
+ } else {
+ printf("\tNO CD_ORIGSPACE, error out of range\n");
+ return DMCACHE_NOTFOUND;
+ }
+ }
+ else if(index >= me->totface)
+ return DMCACHE_NOTFOUND; /* index not in the original mesh */
+
+ psys_w_to_origspace(fw, uv);
+
+ if(node) { /* we have a linked list of faces that we use, faster! */
+ for(;node; node=node->next) {
+ findex= (int)node->link;
+ faceuv= osface[findex].uv;
+ quad= mface[findex].v4;
+
+ /* check that this intersects - Its possible this misses :/ -
+ * could also check its not between */
+ if(quad) {
+ if(IsectPQ2Df(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3]))
+ return findex;
+ }
+ else if(IsectPT2Df(uv, faceuv[0], faceuv[1], faceuv[2]))
+ return findex;
+ }
+ }
+ else { /* if we have no node, try every face */
+ for(findex=0; findex<totface; findex++) {
+ if(origindex[findex] == index) {
+ faceuv= osface[findex].uv;
+ quad= mface[findex].v4;
+
+ /* check that this intersects - Its possible this misses :/ -
+ * could also check its not between */
+ if(quad) {
+ if(IsectPQ2Df(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3]))
+ return findex;
+ }
+ else if(IsectPT2Df(uv, faceuv[0], faceuv[1], faceuv[2]))
+ return findex;
+ }
+ }
+ }
+
+ return DMCACHE_NOTFOUND;
+}
+
+/* interprets particle data to get a point on a mesh in object space */
+#define PARTICLE_ERROR(_nor, _vec) _vec[0]=_vec[1]=_vec[2]=0.0; if(_nor){ _nor[0]=_nor[1]=0.0; _nor[2]=1.0; }
+void psys_particle_on_dm(Object *ob, DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan)
+{
+ if(index < 0){ /* 'no dm' error has happened! */
+ PARTICLE_ERROR(nor, vec);
+ return;
+ }
+
+ if (dm->deformedOnly || index_dmcache == DMCACHE_ISCHILD) {
+ /* this works for meshes with deform verts only - constructive modifiers wont work properly*/
+ float temp1[3];
+
+ if(index_dmcache == DMCACHE_ISCHILD && index >= dm->getNumFaces(dm)) {
+ PARTICLE_ERROR(nor, vec);
+ return;
+ }
+
+ if(from == PART_FROM_VERT) {
+ dm->getVertCo(dm,index,vec);
+ if(nor){
+ dm->getVertNo(dm,index,nor);
+ Normalize(nor);
+ }
+ }
+ else { /* PART_FROM_FACE / PART_FROM_VOLUME */
+ MFace *mface=dm->getFaceData(dm,index,CD_MFACE);
+ MTFace *mtface=0;
+ MVert *mvert=dm->getVertDataArray(dm,CD_MVERT);
+ int uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE);
+
+ if(uv_index>=0){
+ CustomDataLayer *layer=&dm->faceData.layers[uv_index];
+ mtface= &((MTFace*)layer->data)[index];
+ }
+
+ if(from==PART_FROM_VOLUME){
+ psys_interpolate_face(mvert,mface,mtface,fw,vec,temp1,utan,vtan);
+ if(nor)
+ VECCOPY(nor,temp1);
+ Normalize(temp1);
+ VecMulf(temp1,-foffset);
+ VECADD(vec,vec,temp1);
+ }
+ else
+ psys_interpolate_face(mvert,mface,mtface,fw,vec,nor,utan,vtan);
+ }
+ } else {
+ /* Need to support constructive modifiers, this is a bit more tricky
+ we need a customdata layer like UV's so we can position the particle */
+
+ /* Only face supported at the moment */
+ if (from==PART_FROM_FACE) {
+ /* find a face on the derived mesh that uses this face */
+ Mesh *me= (Mesh*)ob->data;
+ MVert *mvert;
+ MFace *mface;
+ MTFace *mtface;
+ OrigSpaceFace *osface;
+ int *origindex;
+ float fw_mod[4];
+ int i, totface;
+
+ mvert= dm->getVertDataArray(dm,CD_MVERT);
+
+ osface= dm->getFaceDataArray(dm, CD_ORIGSPACE);
+ origindex= dm->getFaceDataArray(dm, CD_ORIGINDEX);
+
+ /* For this to work we need origindex and OrigSpace coords */
+ if(origindex==NULL || osface==NULL || index>=me->totface) {
+ PARTICLE_ERROR(nor, vec);
+ return;
+ }
+
+ if (index_dmcache == DMCACHE_NOTFOUND)
+ i = psys_particle_dm_face_lookup(ob, dm, index, fw, (LinkNode*)NULL);
+ else
+ i = index_dmcache;
+
+ totface = dm->getNumFaces(dm);
+
+ /* Any time this happens, and the face has not been removed,
+ * its a BUG watch out for this error! */
+ if (i==-1) {
+ printf("Cannot find original face %i\n", index);
+ PARTICLE_ERROR(nor, vec);
+ return;
+ }
+ else if(i >= totface)
+ return;
+
+ mface= dm->getFaceData(dm, i, CD_MFACE);
+ mtface= dm->getFaceData(dm, i, CD_MTFACE);
+ osface += i;
+
+ /* we need to modify the original weights to become weights for
+ * the derived mesh face */
+ psys_origspace_to_w(osface, mface->v4, fw, fw_mod);
+ psys_interpolate_face(mvert,mface,mtface,fw_mod,vec,nor,utan,vtan);
+ }
+ else {
+ /* TODO PARTICLE - support verts and volume */
+ PARTICLE_ERROR(nor, vec);
+ }
+ }
+}
+#undef PARTICLE_ERROR
+
+ParticleSystemModifierData *psys_get_modifier(Object *ob, ParticleSystem *psys)
+{
+ ModifierData *md;
+ ParticleSystemModifierData *psmd;
+
+ for(md=ob->modifiers.first; md; md=md->next){
+ if(md->type==eModifierType_ParticleSystem){
+ psmd= (ParticleSystemModifierData*) md;
+ if(psmd->psys==psys){
+ return psmd;
+ }
+ }
+ }
+ return 0;
+}
+/************************************************/
+/* Particles on a shape */
+/************************************************/
+/* ready for future use */
+void psys_particle_on_shape(int distr, int index, float *fuv, float *vec, float *nor, float *utan, float *vtan)
+{
+ /* TODO */
+ float zerovec[3]={0.0f,0.0f,0.0f};
+ if(vec){
+ VECCOPY(vec,zerovec);
+ }
+ if(nor){
+ VECCOPY(nor,zerovec);
+ }
+ if(utan){
+ VECCOPY(utan,zerovec);
+ }
+ if(vtan){
+ VECCOPY(vtan,zerovec);
+ }
+}
+/************************************************/
+/* Particles on emitter */
+/************************************************/
+void psys_particle_on_emitter(Object *ob, ParticleSystemModifierData *psmd, int from, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan){
+ if(psmd){
+ if(psmd->psys->part->distr==PART_DISTR_GRID){
+ if(vec){
+ VECCOPY(vec,fuv);
+ }
+ return;
+ }
+ /* we cant use the num_dmcache */
+ psys_particle_on_dm(ob, psmd->dm,from,index,index_dmcache,fuv,foffset,vec,nor,utan,vtan);
+ }
+ else
+ psys_particle_on_shape(from,index,fuv,vec,nor,utan,vtan);
+
+}
+/************************************************/
+/* Path Cache */
+/************************************************/
+static void hair_to_particle(ParticleKey *key, HairKey *hkey)
+{
+ VECCOPY(key->co, hkey->co);
+ key->time = hkey->time;
+}
+static void bp_to_particle(ParticleKey *key, BodyPoint *bp, HairKey *hkey)
+{
+ VECCOPY(key->co, bp->pos);
+ key->time = hkey->time;
+}
+static float vert_weight(MDeformVert *dvert, int group)
+{
+ MDeformWeight *dw;
+ int i;
+
+ if(dvert) {
+ dw= dvert->dw;
+ for(i= dvert->totweight; i>0; i--, dw++) {
+ if(dw->def_nr == group) return dw->weight;
+ if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/
+ }
+ }
+ return 0.0;
+}
+static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4])
+{
+ float vec[3]={0.0,0.0,0.0}, q1[4]={1,0,0,0},*q2;
+ float t;
+
+ CLAMP(time,0.0,1.0);
+
+ if(shape!=0.0f && type!=PART_KINK_BRAID) {
+ if(shape<0.0f)
+ time= (float)pow(time, 1.0+shape);
+ else
+ time= (float)pow(time, 1.0/(1.0-shape));
+ }
+
+ t=time;
+
+ t*=(float)M_PI*freq;
+
+ if(par==0) return;
+
+ switch(type){
+ case PART_KINK_CURL:
+ vec[axis]=1.0;
+ if(par_rot)
+ q2=par_rot;
+ else{
+ q2=vectoquat(par->vel,axis,(axis+1)%3);
+ }
+ QuatMulVecf(q2,vec);
+ VecMulf(vec,amplitude);
+ VECADD(state->co,state->co,vec);
+
+ VECSUB(vec,state->co,par->co);
+
+ if(t!=0.0)
+ VecRotToQuat(par->vel,t,q1);
+
+ QuatMulVecf(q1,vec);
+
+ VECADD(state->co,par->co,vec);
+ break;
+ case PART_KINK_RADIAL:
+ VECSUB(vec,state->co,par->co);
+
+ Normalize(vec);
+ VecMulf(vec,amplitude*(float)sin(t));
+
+ VECADD(state->co,state->co,vec);
+ break;
+ case PART_KINK_WAVE:
+ vec[axis]=1.0;
+ if(obmat)
+ Mat4MulVecfl(obmat,vec);
+
+ if(par_rot)
+ QuatMulVecf(par_rot,vec);
+
+ Projf(q1,vec,par->vel);
+
+ VECSUB(vec,vec,q1);
+ Normalize(vec);
+
+ VecMulf(vec,amplitude*(float)sin(t));
+
+ VECADD(state->co,state->co,vec);
+ break;
+ case PART_KINK_BRAID:
+ if(par){
+ float y_vec[3]={0.0,1.0,0.0};
+ float z_vec[3]={0.0,0.0,1.0};
+ float vec_from_par[3], vec_one[3], radius, state_co[3];
+ float inp_y,inp_z,length;
+
+ if(par_rot)
+ q2=par_rot;
+ else
+ q2=vectoquat(par->vel,axis,(axis+1)%3);
+ QuatMulVecf(q2,y_vec);
+ QuatMulVecf(q2,z_vec);
+
+ VECSUB(vec_from_par,state->co,par->co);
+ VECCOPY(vec_one,vec_from_par);
+ radius=Normalize(vec_one);
+
+ inp_y=Inpf(y_vec,vec_one);
+ inp_z=Inpf(z_vec,vec_one);
+
+ if(inp_y>0.5){
+ VECCOPY(state_co,y_vec);
+
+ VecMulf(y_vec,amplitude*(float)cos(t));
+ VecMulf(z_vec,amplitude/2.0f*(float)sin(2.0f*t));
+ }
+ else if(inp_z>0.0){
+ VECCOPY(state_co,z_vec);
+ VecMulf(state_co,(float)sin(M_PI/3.0f));
+ VECADDFAC(state_co,state_co,y_vec,-0.5f);
+
+ VecMulf(y_vec,-amplitude*(float)cos(t + M_PI/3.0f));
+ VecMulf(z_vec,amplitude/2.0f*(float)cos(2.0f*t + M_PI/6.0f));
+ }
+ else{
+ VECCOPY(state_co,z_vec);
+ VecMulf(state_co,-(float)sin(M_PI/3.0f));
+ VECADDFAC(state_co,state_co,y_vec,-0.5f);
+
+ VecMulf(y_vec,amplitude*(float)-sin(t+M_PI/6.0f));
+ VecMulf(z_vec,amplitude/2.0f*(float)-sin(2.0f*t+M_PI/3.0f));
+ }
+
+ VecMulf(state_co,amplitude);
+ VECADD(state_co,state_co,par->co);
+ VECSUB(vec_from_par,state->co,state_co);
+
+ length=Normalize(vec_from_par);
+ VecMulf(vec_from_par,MIN2(length,amplitude/2.0f));
+
+ VECADD(state_co,par->co,y_vec);
+ VECADD(state_co,state_co,z_vec);
+ VECADD(state_co,state_co,vec_from_par);
+
+ shape=(2.0f*(float)M_PI)*(1.0f+shape);
+
+ if(t<shape){
+ shape=t/shape;
+ shape=(float)sqrt((double)shape);
+ VecLerpf(state->co,state->co,state_co,shape);
+ }
+ else{
+ VECCOPY(state->co,state_co);
+ }
+ }
+ break;
+ //case PART_KINK_ROT:
+ // vec[axis]=1.0;
+
+ // QuatMulVecf(par->rot,vec);
+
+ // VecMulf(vec,amplitude*(float)sin(t));
+
+ // VECADD(state->co,state->co,vec);
+ // break;
+ }
+}
+static void do_postkink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4])
+{
+ static ParticleKey first;
+ static float q[4];
+ float vec[3]={0.0,0.0,0.0};
+ float t;
+
+ CLAMP(time,0.0,1.0);
+
+ t=time;
+
+ t*=(float)M_PI*freq;
+
+ if(par==0) return;
+
+ switch(type){
+ case PART_KINK_ROLL:
+ if(time<(0.5+shape/2.0f)){
+ float *q2;
+ memcpy(&first,state,sizeof(ParticleKey));
+ Normalize(first.vel);
+ if(par_rot)
+ q2=par_rot;
+ else
+ q2=vectoquat(par->vel,axis,(axis+1)%3);
+ QUATCOPY(q,q2);
+ }
+ else{
+ float fac;
+ shape=0.5f+shape/2.0f;
+ t-=(float)M_PI*(shape*freq + 0.5f);
+
+ vec[axis]=1.0;
+
+ QuatMulVecf(q,vec);
+
+ fac=amplitude*(1.0f+((1.0f-time)/(1.0f-shape)*(float)sin(t)));
+ VECADDFAC(state->co,first.co,vec,fac);
+ fac=amplitude*((1.0f-time)/(1.0f-shape)*(float)cos(t));
+ VECADDFAC(state->co,state->co,first.vel,fac);
+ }
+ break;
+ }
+}
+static void do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump)
+{
+ if(par && clumpfac!=0.0){
+ float clump, cpow;
+
+ if(clumppow<0.0)
+ cpow=1.0f+clumppow;
+ else
+ cpow=1.0f+9.0f*clumppow;
+
+ if(clumpfac<0.0) /* clump roots instead of tips */
+ clump = -clumpfac*pa_clump*(float)pow(1.0-(double)time,(double)cpow);
+ else
+ clump = clumpfac*pa_clump*(float)pow((double)time,(double)cpow);
+ VecLerpf(state->co,state->co,par->co,clump);
+ }
+}
+int do_guide(ParticleKey *state, int pa_num, float time, ListBase *lb)
+{
+ PartDeflect *pd;
+ ParticleEffectorCache *ec;
+ Object *eob;
+ Curve *cu;
+ ParticleKey key, par;
+
+ float effect[3]={0.0,0.0,0.0}, distance, f_force, mindist, totforce=0.0;
+ float guidevec[4], guidedir[3], rot2[4], temp[3], angle, pa_loc[3], pa_zero[3]={0.0f,0.0f,0.0f};
+ float veffect[3]={0.0,0.0,0.0}, guidetime;
+
+ effect[0]=effect[1]=effect[2]=0.0;
+
+ if(lb->first){
+ for(ec = lb->first; ec; ec= ec->next){
+ eob= ec->ob;
+ if(ec->type & PSYS_EC_EFFECTOR){
+ pd=eob->pd;
+ if(pd->forcefield==PFIELD_GUIDE){
+ cu = (Curve*)eob->data;
+
+ distance=ec->distances[pa_num];
+ mindist=pd->f_strength;
+
+ VECCOPY(pa_loc, ec->locations+3*pa_num);
+ VECCOPY(pa_zero,pa_loc);
+ VECADD(pa_zero,pa_zero,ec->firstloc);
+
+ guidetime=time/(1.0-pd->free_end);
+
+ /* WARNING: bails out with continue here */
+ if(((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist) || guidetime>1.0f) continue;
+
+ if(guidetime>1.0f) continue;
+
+ /* calculate contribution factor for this guide */
+ f_force=1.0f;
+ if(distance<=mindist);
+ else if(pd->flag & PFIELD_USEMAX) {
+ if(mindist>=pd->maxdist) f_force= 0.0f;
+ else if(pd->f_power!=0.0f){
+ f_force= 1.0f - (distance-mindist)/(pd->maxdist - mindist);
+ f_force = (float)pow(f_force, pd->f_power);
+ }
+ }
+ else if(pd->f_power!=0.0f){
+ f_force= 1.0f/(1.0f + distance-mindist);
+ f_force = (float)pow(f_force, pd->f_power);
+ }
+
+ if(pd->flag & PFIELD_GUIDE_PATH_ADD)
+ where_on_path(eob, f_force*guidetime, guidevec, guidedir);
+ else
+ where_on_path(eob, guidetime, guidevec, guidedir);
+
+ Mat4MulVecfl(ec->ob->obmat,guidevec);
+ Mat4Mul3Vecfl(ec->ob->obmat,guidedir);
+
+ Normalize(guidedir);
+
+ if(guidetime!=0.0){
+ /* curve direction */
+ Crossf(temp, ec->firstdir, guidedir);
+ angle=Inpf(ec->firstdir,guidedir)/(VecLength(ec->firstdir));
+ angle=saacos(angle);
+ VecRotToQuat(temp,angle,rot2);
+ QuatMulVecf(rot2,pa_loc);
+
+ /* curve tilt */
+ VecRotToQuat(guidedir,guidevec[3]-ec->firstloc[3],rot2);
+ QuatMulVecf(rot2,pa_loc);
+
+ //q=vectoquat(guidedir, pd->kink_axis, (pd->kink_axis+1)%3);
+ //QuatMul(par.rot,rot2,q);
+ }
+ //else{
+ // par.rot[0]=1.0f;
+ // par.rot[1]=par.rot[2]=par.rot[3]=0.0f;
+ //}
+
+ /* curve taper */
+ if(cu->taperobj)
+ VecMulf(pa_loc,calc_taper(cu->taperobj,(int)(f_force*guidetime*100.0),100));
+ /* TODO */
+ //else{
+ ///* curve size*/
+ // calc_curve_subdiv_radius(cu,cu->nurb.first,((Nurb*)cu->nurb.first)->
+ //}
+ par.co[0]=par.co[1]=par.co[2]=0.0f;
+ VECCOPY(key.co,pa_loc);
+ do_prekink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0);
+ do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f);
+ do_postkink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0);
+ VECCOPY(pa_loc,key.co);
+
+ VECADD(pa_loc,pa_loc,guidevec);
+ VECSUB(pa_loc,pa_loc,pa_zero);
+ VECADDFAC(effect,effect,pa_loc,f_force);
+ VECADDFAC(veffect,veffect,guidedir,f_force);
+ totforce+=f_force;
+ }
+ }
+ }
+
+ if(totforce!=0.0){
+ if(totforce>1.0)
+ VecMulf(effect,1.0f/totforce);
+ CLAMP(totforce,0.0,1.0);
+ VECADD(effect,effect,pa_zero);
+ VecLerpf(state->co,state->co,effect,totforce);
+
+ Normalize(veffect);
+ VecMulf(veffect,VecLength(state->vel));
+ VECCOPY(state->vel,veffect);
+ return 1;
+ }
+ }
+ return 0;
+}
+static void do_rough(float *loc, float t, float fac, float size, float thres, ParticleKey *state)
+{
+ float rough[3];
+ float rco[3];
+
+ if(thres!=0.0)
+ if((float)fabs((float)(-1.5+loc[0]+loc[1]+loc[2]))<1.5f*thres) return;
+
+ VECCOPY(rco,loc);
+ VecMulf(rco,t);
+ rough[0]=-1.0f+2.0f*BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2,0,2);
+ rough[1]=-1.0f+2.0f*BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2,0,2);
+ rough[2]=-1.0f+2.0f*BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2,0,2);
+ VECADDFAC(state->co,state->co,rough,fac);
+}
+static void do_rough_end(float *loc, float t, float fac, float shape, ParticleKey *state, ParticleKey *par)
+{
+ float rough[3], rnor[3];
+ float roughfac;
+
+ roughfac=fac*(float)pow((double)t,shape);
+ VECCOPY(rough,loc);
+ rough[0]=-1.0f+2.0f*rough[0];
+ rough[1]=-1.0f+2.0f*rough[1];
+ rough[2]=-1.0f+2.0f*rough[2];
+ VecMulf(rough,roughfac);
+
+
+ if(par){
+ VECCOPY(rnor,par->vel);
+ }
+ else{
+ VECCOPY(rnor,state->vel);
+ }
+ Normalize(rnor);
+ Projf(rnor,rough,rnor);
+ VECSUB(rough,rough,rnor);
+
+ VECADD(state->co,state->co,rough);
+}
+static int check_path_length(int k, int p, ParticleCacheKey **cache, ParticleCacheKey *state, float length, float *dvec)
+{
+ static float max_length = 1.0, cur_length = 0.0;
+
+ if(k) {
+ if(cur_length + length > max_length){
+ //if(p<totparent){
+ // if(k<=(int)cache[totpart+p]->time){
+ // /* parents need to be calculated fully first so that they don't mess up their children */
+ // /* we'll make a note of where we got to though so that they're easy to finish later */
+ // state->time=(max_length-cur_length)/length;
+ // cache[totpart+p]->time=(float)k;
+ // }
+ //}
+ //else{
+ VecMulf(dvec, (max_length - cur_length) / length);
+ VECADD(state->co, (state - 1)->co, dvec);
+ cache[p]->steps = k;
+ /* something over the maximum step value */
+ return k=100000;
+ //}
+ }
+ else {
+ cur_length+=length;
+ }
+ }
+ else {/* reset signal */
+ max_length=length;
+ cur_length=0.0;
+ }
+ return k;
+}
+static void finalize_path_length(int p, ParticleCacheKey **cache)
+{
+ ParticleCacheKey *state = cache[p];
+ float dvec[3];
+ state += state->steps;
+
+ VECSUB(dvec, state->co, (state - 1)->co);
+ VecMulf(dvec, state->steps);
+ VECADD(state->co, (state - 1)->co, dvec);
+}
+static void offset_child(ChildParticle *cpa, ParticleKey *par, ParticleKey *child, float flat, float radius)
+{
+ VECCOPY(child->co,cpa->fuv);
+ VecMulf(child->co,radius);
+
+ child->co[0]*=flat;
+
+ VECCOPY(child->vel,par->vel);
+
+ QuatMulVecf(par->rot,child->co);
+
+ QUATCOPY(child->rot,par->rot);
+
+ VECADD(child->co,child->co,par->co);
+}
+float *psys_cache_vgroup(DerivedMesh *dm, ParticleSystem *psys, int vgroup)
+{
+ float *vg=0;
+
+ if(psys->vgroup[vgroup]){
+ MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
+ if(dvert){
+ int totvert=dm->getNumVerts(dm), i;
+ vg=MEM_callocN(sizeof(float)*totvert, "vg_cache");
+ if(psys->vg_neg&(1<<vgroup)){
+ for(i=0; i<totvert; i++)
+ vg[i]=1.0f-vert_weight(dvert+i,psys->vgroup[vgroup]-1);
+ }
+ else{
+ for(i=0; i<totvert; i++)
+ vg[i]=vert_weight(dvert+i,psys->vgroup[vgroup]-1);
+ }
+ }
+ }
+ return vg;
+}
+void psys_find_parents(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys)
+{
+ ParticleSettings *part=psys->part;
+ KDTree *tree;
+ ChildParticle *cpa;
+ int p, totparent,totchild=psys->totchild;
+ float co[3], *orcos=0;
+ int from=PART_FROM_FACE;
+ totparent=(int)(totchild*part->parents*0.3);
+
+ tree=BLI_kdtree_new(totparent);
+
+ for(p=0,cpa=psys->child; p<totparent; p++,cpa++){
+ psys_particle_on_emitter(ob,psmd,from,cpa->num,-1,cpa->fuv,cpa->foffset,co,0,0,0);
+ BLI_kdtree_insert(tree, p, co, NULL);
+ }
+
+ BLI_kdtree_balance(tree);
+
+ for(; p<totchild; p++,cpa++){
+ psys_particle_on_emitter(ob,psmd,from,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co,0,0,0);
+ cpa->parent=BLI_kdtree_find_nearest(tree, co, NULL, NULL);
+ }
+
+ BLI_kdtree_free(tree);
+ if(orcos)
+ MEM_freeN(orcos);
+}
+void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate)
+{
+ ParticleSettings *part = psys->part;
+ ParticleEditSettings *pset = &G.scene->toolsettings->particle;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
+ ParticleData *pa;
+ ChildParticle *cpa;
+ ParticleCacheKey **cache = psys->childcache, **pcache = psys->pathcache;
+ ParticleCacheKey *tcache, *state, *par=0, *key[4];
+ ParticleTexture ptex;
+ Material *ma = give_current_material(ob, part->omat);
+
+ float length, pa_length = 1.0, pa_clump = 1.0, pa_kink = 1.0;
+ float pa_rough1 = 1.0, pa_rough2 = 1.0, pa_roughe = 1.0;
+ float t, rough_t;
+ float dvec[3], orco[3], ornor[3], imat[4][4];
+ float *vg_length = 0, *vg_clump = 0, *vg_kink = 0;
+ float *vg_rough1 = 0, *vg_rough2 = 0, *vg_roughe = 0;
+ float cpa_1st[3];
+
+ int k, i, totparent=0, between=0, edit=0;
+ int steps = (int)pow(2.0,(double)part->draw_step);
+ int totchild = psys->totchild;
+ int cpa_num; short cpa_from;
+
+ if(part->flag & PART_ANIM_BRANCHING)
+ BLI_srandom(31415926 + psys->seed + (int)cfra);
+ else
+ BLI_srandom(31415926 + psys->seed);
+
+ /*---start figuring out what is actually wanted---*/
+ if(psys_in_edit_mode(psys)){
+ if(G.rendering==0 && (psys->edit==NULL || pset->flag & PE_SHOW_CHILD)==0)
+ totchild=0;
+ edit=1;
+ }
+
+ if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
+ totparent=(int)(totchild*part->parents*0.3);
+ /* part->parents could still be 0 so we can't test with totparent */
+ between=1;
+ }
+
+ if(G.rendering)
+ steps=(int)pow(2.0,(double)part->ren_step);
+ else if(part->flag & PART_CHILD_RENDER){
+ totchild=0;
+ }
+ else{
+ totchild=(int)((float)totchild*(float)part->disp/100.0f);
+ totparent=MIN2(totparent,totchild);
+ }
+
+ if(totchild==0) return;
+
+ if(editupdate && psys->childcache && !(part->flag & PART_BRANCHING) && totchild == psys->totchildcache) {
+ cache = psys->childcache;
+ }
+ else {
+ /* clear out old and create new empty path cache */
+ free_child_path_cache(psys);
+
+ cache = psys->childcache = MEM_callocN(totchild*sizeof(void *), "Child path cache array");
+ tcache = MEM_callocN(totchild * (steps + 1) * sizeof(ParticleCacheKey), "Child path cache");
+ for(i=0; i<totchild; i++)
+ cache[i] = tcache + i * (steps + 1);
+ }
+
+ psys->lattice = psys_get_lattice(ob,psys);
+
+ /* cache all relevant vertex groups if they exist */
+ if(part->from!=PART_FROM_PARTICLE){
+ vg_length = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_LENGTH);
+ vg_clump = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_CLUMP);
+ vg_kink = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_KINK);
+ vg_rough1 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH1);
+ vg_rough2 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH2);
+ vg_roughe = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGHE);
+ }
+
+ /* set correct ipo timing */
+ if(part->flag&PART_ABS_TIME && part->ipo){
+ calc_ipo(part->ipo, cfra);
+ execute_ipo((ID *)part, part->ipo);
+ }
+
+ Mat4Invert(imat,ob->obmat);
+
+ for(i=0,cpa=psys->child; i<totchild; i++, cpa++){
+ int guided=0;
+ float *cpa_fuv=0;
+ float branch_begin=0.0f, branch_end=0.0f, branch_prob=0.0f;
+ float branchfac, rough_rand=0.0f;
+
+ if(part->flag & PART_BRANCHING) {
+ branch_begin=BLI_frand();
+ branch_end=branch_begin+(1.0f-branch_begin)*BLI_frand();
+ branch_prob=BLI_frand();
+ rough_rand=BLI_frand();
+ }
+
+ if(i<psys->totpart){
+ branch_begin=0.0f;
+ branch_end=1.0f;
+ branch_prob=0.0f;
+ }
+
+ if(between){
+ int w, needupdate;
+ float foffset;
+
+ if(editupdate && !(part->flag & PART_BRANCHING)) {
+ needupdate= 0;
+ w= 0;
+ while(w<4 && cpa->pa[w]>=0) {
+ if(psys->particles[cpa->pa[w]].flag & PARS_EDIT_RECALC) {
+ needupdate= 1;
+ break;
+ }
+ w++;
+ }
+
+ if(!needupdate)
+ continue;
+ else
+ memset(cache[i], 0, sizeof(*cache[i])*(steps+1));
+ }
+
+ /* get parent paths */
+ w= 0;
+ while(w<4 && cpa->pa[w]>=0){
+ key[w] = pcache[cpa->pa[w]];
+ w++;
+ }
+
+ /* get the original coordinates (orco) for texture usage */
+ cpa_num = cpa->num;
+
+ foffset= cpa->foffset;
+ if(part->childtype == PART_CHILD_FACES)
+ foffset = -(2.0f + part->childspread);
+ cpa_fuv = cpa->fuv;
+ cpa_from = PART_FROM_FACE;
+
+ psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,ornor,0,0);
+
+ /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */
+ VECCOPY(cpa_1st,orco);
+ Mat4MulVecfl(ob->obmat,cpa_1st);
+
+ pa=0;
+ }
+ else{
+ if(editupdate && !(part->flag & PART_BRANCHING)) {
+ if(!(psys->particles[cpa->parent].flag & PARS_EDIT_RECALC))
+ continue;
+
+ memset(cache[i], 0, sizeof(*cache[i])*(steps+1));
+ }
+
+ /* get the parent path */
+ key[0]=pcache[cpa->parent];
+
+ /* get the original coordinates (orco) for texture usage */
+ pa=psys->particles+cpa->parent;
+
+ cpa_from=part->from;
+ cpa_num=pa->num;
+ cpa_fuv=pa->fuv;
+
+ psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,ornor,0,0);
+ }
+
+ cache[i]->steps = steps;
+
+ /* correct child ipo timing */
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ float dsta=part->end-part->sta;
+ calc_ipo(part->ipo, 100.0f*(cfra-(part->sta+dsta*cpa->rand[1]))/(part->lifetime*(1.0f - part->randlife*cpa->rand[0])));
+ execute_ipo((ID *)part, part->ipo);
+ }
+
+ /* get different child parameters from textures & vgroups */
+ ptex.length=part->length*(1.0f - part->randlength*cpa->rand[0]);
+ ptex.clump=1.0;
+ ptex.kink=1.0;
+
+ get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE);
+
+ pa_length=ptex.length;
+ pa_clump=ptex.clump;
+ pa_kink=ptex.kink;
+ pa_rough1=1.0;
+ pa_rough2=1.0;
+ pa_roughe=1.0;
+
+ if(vg_length)
+ pa_length*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_length);
+ if(vg_clump)
+ pa_clump*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_clump);
+ if(vg_kink)
+ pa_kink*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_kink);
+ if(vg_rough1)
+ pa_rough1*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough1);
+ if(vg_rough2)
+ pa_rough2*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough2);
+ if(vg_roughe)
+ pa_roughe*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_roughe);
+
+ /* create the child path */
+ for(k=0,state=cache[i]; k<=steps; k++,state++){
+ t=(float)k/(float)steps;
+
+ if(between){
+ int w=0;
+
+ state->co[0] = state->co[1] = state->co[2] = 0.0f;
+ state->vel[0] = state->vel[1] = state->vel[2] = 0.0f;
+
+ //QUATCOPY(state->rot,key[0]->rot);
+
+ /* child position is the weighted sum of parent positions */
+ while(w<4 && cpa->pa[w]>=0){
+ state->co[0] += cpa->w[w] * key[w]->co[0];
+ state->co[1] += cpa->w[w] * key[w]->co[1];
+ state->co[2] += cpa->w[w] * key[w]->co[2];
+
+ state->vel[0] += cpa->w[w] * key[w]->vel[0];
+ state->vel[1] += cpa->w[w] * key[w]->vel[1];
+ state->vel[2] += cpa->w[w] * key[w]->vel[2];
+ key[w]++;
+ w++;
+ }
+ if(k==0){
+ /* calculate the offset between actual child root position and first position interpolated from parents */
+ VECSUB(cpa_1st,cpa_1st,state->co);
+ }
+ /* apply offset for correct positioning */
+ VECADD(state->co,state->co,cpa_1st);
+ }
+ else{
+ /* offset the child from the parent position */
+ offset_child(cpa, (ParticleKey*)key[0], (ParticleKey*)state, part->childflat, part->childrad);
+
+ key[0]++;
+ }
+
+ if(totparent){
+ if(i>=totparent)
+ par = cache[cpa->parent] + k;
+ else
+ par=0;
+ }
+ else if(cpa->parent>=0){
+ par=pcache[cpa->parent]+k;
+ }
+
+ /* apply different deformations to the child path */
+ if(part->flag & PART_CHILD_GUIDE)
+ guided = do_guide((ParticleKey*)state, i, t, &(psys->effectors)); //safe to cast, since only co and vel are used
+
+ if(guided==0){
+ if(part->kink)
+ do_prekink((ParticleKey*)state, (ParticleKey*)par, par->rot, t,
+ part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat);
+
+ do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump);
+
+ if(part->kink)
+ do_postkink((ParticleKey*)state, (ParticleKey*)par, par->rot, t,
+ part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat);
+ }
+
+ if(part->flag & PART_BRANCHING && between == 0 && part->flag & PART_ANIM_BRANCHING)
+ rough_t = t * rough_rand;
+ else
+ rough_t = t;
+
+ if(part->rough1 != 0.0 && pa_rough1 != 0.0)
+ do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state);
+
+ if(part->rough2 != 0.0 && pa_rough2 != 0.0)
+ do_rough(cpa->rand, rough_t, pa_rough2*part->rough2, part->rough2_size, part->rough2_thres, (ParticleKey*)state);
+
+ if(part->rough_end != 0.0 && pa_roughe != 0.0)
+ do_rough_end(cpa->rand, rough_t, pa_roughe*part->rough_end, part->rough_end_shape, (ParticleKey*)state, (ParticleKey*)par);
+
+ if(part->flag & PART_BRANCHING && between==0){
+ if(branch_prob > part->branch_thres){
+ branchfac=0.0f;
+ }
+ else{
+ if(part->flag & PART_SYMM_BRANCHING){
+ if(t < branch_begin || t > branch_end)
+ branchfac=0.0f;
+ else{
+ if((t-branch_begin)/(branch_end-branch_begin)<0.5)
+ branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin);
+ else
+ branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin);
+
+ CLAMP(branchfac,0.0f,1.0f);
+ }
+ }
+ else{
+ if(t < branch_begin){
+ branchfac=0.0f;
+ }
+ else{
+ branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f);
+ CLAMP(branchfac,0.0f,1.0f);
+ }
+ }
+ }
+
+ if(i<psys->totpart){
+ VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac);
+ }
+ else
+ VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac);
+ }
+
+ /* we have to correct velocity because of kink & clump */
+ if(k>1){
+ VECSUB((state-1)->vel,state->co,(state-2)->co);
+ VecMulf((state-1)->vel,0.5);
+ }
+
+ /* check if path needs to be cut before actual end of data points */
+ if(k){
+ VECSUB(dvec,state->co,(state-1)->co);
+ if(part->flag&PART_ABS_LENGTH)
+ length=VecLength(dvec);
+ else
+ length=1.0f/(float)steps;
+
+ k=check_path_length(k,i,cache,state,length,dvec);
+ }
+ else{
+ /* initialize length calculation */
+ if(part->flag&PART_ABS_LENGTH)
+ check_path_length(0,0,0,0,part->abslength*pa_length,0);
+ else
+ check_path_length(0,0,0,0,pa_length,0);
+ }
+ }
+ }
+ /* now let's finalise the interpolated parents that we might have left half done before */
+ if(totchild) for(i=0,cpa=psys->child; i<totparent; i++, cpa++)
+ finalize_path_length(i,cache);
+
+ if(vg_length)
+ MEM_freeN(vg_length);
+ if(vg_clump)
+ MEM_freeN(vg_clump);
+ if(vg_kink)
+ MEM_freeN(vg_kink);
+ if(vg_rough1)
+ MEM_freeN(vg_rough1);
+ if(vg_rough2)
+ MEM_freeN(vg_roughe);
+ if(vg_roughe)
+ MEM_freeN(vg_roughe);
+
+ psys->totchildcache = totchild;
+
+ if(psys->lattice){
+ end_latt_deform();
+ psys->lattice=0;
+ }
+}
+/* Calculates paths ready for drawing/rendering. */
+/* -Usefull for making use of opengl vertex arrays for super fast strand drawing. */
+/* -Makes child strands possible and creates them too into the cache. */
+/* -Cached path data is also used to determine cut position for the editmode tool. */
+void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate)
+{
+ ParticleCacheKey *ca, **cache=psys->pathcache;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ ParticleEditSettings *pset = &G.scene->toolsettings->particle;
+
+ ParticleData *pa;
+ ParticleKey keys[4], result, *kkey[2] = {NULL, NULL};
+ HairKey *hkey[2] = {NULL, NULL};
+
+ ParticleEdit *edit = 0;
+ ParticleEditKey *ekey = 0;
+
+ SoftBody *soft = 0;
+ BodyPoint *bp[2] = {NULL, NULL};
+
+ float birthtime = 0.0, dietime = 0.0;
+ float t, time, keytime, dfra = 1.0, frs_sec = G.scene->r.frs_sec;
+ float col[3] = {0.5f, 0.5f, 0.5f};
+ float prev_tangent[3], hairmat[4][4];
+ int k,i;
+ int steps = (int)pow(2.0, (double)psys->part->draw_step);
+ int totpart = psys->totpart;
+ char nosel[4], sel[4];
+ float sel_col[3];
+ float nosel_col[3];
+
+ /* we don't have anything valid to create paths from so let's quit here */
+ if((psys->flag & PSYS_HAIR_DONE)==0 && (psys->flag & PSYS_KEYED)==0)
+ return;
+
+ if(G.rendering)
+ steps = (int)pow(2.0, (double)psys->part->ren_step);
+ else if(psys_in_edit_mode(psys)){
+ edit=psys->edit;
+
+ //timed = edit->draw_timed;
+
+ PE_get_colors(sel,nosel);
+ if(pset->brushtype == PE_BRUSH_WEIGHT){
+ sel_col[0] = sel_col[1] = sel_col[2] = 1.0f;
+ nosel_col[0] = nosel_col[1] = nosel_col[2] = 0.0f;
+ }
+ else{
+ sel_col[0] = (float)sel[0] / 255.0f;
+ sel_col[1] = (float)sel[1] / 255.0f;
+ sel_col[2] = (float)sel[2] / 255.0f;
+ nosel_col[0] = (float)nosel[0] / 255.0f;
+ nosel_col[1] = (float)nosel[1] / 255.0f;
+ nosel_col[2] = (float)nosel[2] / 255.0f;
+ }
+ }
+
+ if(editupdate && psys->pathcache && totpart == psys->totcached) {
+ cache = psys->pathcache;
+ }
+ else {
+ /* clear out old and create new empty path cache */
+ psys_free_path_cache(psys);
+
+ /* allocate cache array for fast access and set pointers to contiguous mem block */
+ cache = psys->pathcache = MEM_callocN(MAX2(1, totpart) * sizeof(void *), "Path cache array");
+ cache[0] = MEM_callocN(totpart * (steps + 1) * sizeof(ParticleCacheKey), "Path cache");
+ for(i=1; i<totpart; i++)
+ cache[i] = cache[0] + i * (steps + 1);
+ }
+
+ if(edit==NULL && psys->soft && psys->softflag & OB_SB_ENABLE)
+ soft = psys->soft;
+
+ psys->lattice = psys_get_lattice(ob, psys);
+
+ /*---first main loop: create all actual particles' paths---*/
+ for(i=0,pa=psys->particles; i<totpart; i++, pa++){
+ if(psys && edit==NULL && (pa->flag & PARS_NO_DISP || pa->flag & PARS_UNEXIST)) {
+ if(soft)
+ bp[0] += pa->totkey; /* TODO use of initialized value? */
+ continue;
+ }
+
+ if(editupdate && !(pa->flag & PARS_EDIT_RECALC)) continue;
+ else memset(cache[i], 0, sizeof(*cache[i])*(steps+1));
+
+ cache[i]->steps = steps;
+
+ if(edit)
+ ekey = edit->keys[i];
+
+ /*--get the first data points--*/
+ if(psys->flag & PSYS_KEYED) {
+ kkey[0] = pa->keys;
+ kkey[1] = kkey[0] + 1;
+
+ birthtime = kkey[0]->time;
+ dietime = kkey[0][pa->totkey-1].time;
+ }
+ else {
+ hkey[0] = pa->hair;
+ hkey[1] = hkey[0] + 1;
+
+ birthtime = hkey[0]->time;
+ dietime = hkey[0][pa->totkey-1].time;
+
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
+ }
+
+ if(soft){
+ bp[0] = soft->bpoint + pa->bpi;
+ bp[1] = bp[0] + 1;
+ }
+
+ /*--interpolate actual path from data points--*/
+ for(k=0, ca=cache[i]; k<=steps; k++, ca++){
+ time = (float)k / (float)steps;
+
+ t = birthtime + time * (dietime - birthtime);
+
+ if(psys->flag & PSYS_KEYED) {
+ while(kkey[1]->time < t) {
+ kkey[1]++;
+ }
+
+ kkey[0] = kkey[1] - 1;
+ }
+ else {
+ while(hkey[1]->time < t) {
+ hkey[1]++;
+ bp[1]++;
+ }
+
+ hkey[0] = hkey[1] - 1;
+ }
+
+ if(soft) {
+ bp[0] = bp[1] - 1;
+ bp_to_particle(keys + 1, bp[0], hkey[0]);
+ bp_to_particle(keys + 2, bp[1], hkey[1]);
+ }
+ else if(psys->flag & PSYS_KEYED) {
+ memcpy(keys + 1, kkey[0], sizeof(ParticleKey));
+ memcpy(keys + 2, kkey[1], sizeof(ParticleKey));
+ }
+ else {
+ hair_to_particle(keys + 1, hkey[0]);
+ hair_to_particle(keys + 2, hkey[1]);
+ }
+
+
+ if((psys->flag & PSYS_KEYED)==0) {
+ if(soft) {
+ if(hkey[0] != pa->hair)
+ bp_to_particle(keys, bp[0] - 1, hkey[0] - 1);
+ else
+ bp_to_particle(keys, bp[0], hkey[0]);
+ }
+ else {
+ if(hkey[0] != pa->hair)
+ hair_to_particle(keys, hkey[0] - 1);
+ else
+ hair_to_particle(keys, hkey[0]);
+ }
+
+ if(soft) {
+ if(hkey[1] != pa->hair + pa->totkey - 1)
+ bp_to_particle(keys + 3, bp[1], hkey[1] + 1);
+ else
+ bp_to_particle(keys + 3, bp[1], hkey[1]);
+ }
+ else {
+ if(hkey[1] != pa->hair + pa->totkey - 1)
+ hair_to_particle(keys + 3, hkey[1] + 1);
+ else
+ hair_to_particle(keys + 3, hkey[1]);
+ }
+ }
+
+ dfra = keys[2].time - keys[1].time;
+
+ keytime = (t - keys[1].time) / dfra;
+
+ /* convert velocity to timestep size */
+ if(psys->flag & PSYS_KEYED){
+ VecMulf(keys[1].vel, dfra / frs_sec);
+ VecMulf(keys[2].vel, dfra / frs_sec);
+ }
+
+ /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0,1]->[k2,k3] (k1 & k4 used for cardinal & bspline interpolation)*/
+ interpolate_particle((psys->flag & PSYS_KEYED) ? -1 /* signal for cubic interpolation */
+ : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL)
+ ,keys, keytime, &result);
+
+
+ /* the velocity needs to be converted back from cubic interpolation */
+ if(psys->flag & PSYS_KEYED){
+ VecMulf(result.vel, frs_sec / dfra);
+ }
+ else if(soft==NULL) { /* softbody and keyed are allready in global space */
+ Mat4MulVecfl(hairmat, result.co);
+ }
+
+
+ /* apply guide curves to path data */
+ if(edit==0 && psys->effectors.first && (psys->part->flag & PART_CHILD_GUIDE)==0)
+ do_guide(&result, i, time, &psys->effectors);
+
+ /* figure out rotation */
+
+ if(k) {
+ float angle, tangent[3], normal[3], q[4];
+
+ if(k == 1) {
+ float *q2;
+
+ VECSUB(tangent, result.co, (ca - 1)->co);
+
+ q2 = vectoquat(tangent, OB_POSX, OB_POSZ);
+
+ QUATCOPY((ca - 1)->rot, q2);
+
+ VECCOPY(prev_tangent, tangent);
+ Normalize(prev_tangent);
+ }
+ else {
+ VECSUB(tangent, result.co, (ca - 1)->co);
+ Normalize(tangent);
+ angle = saacos(Inpf(tangent, prev_tangent));
+
+ if((angle > -0.000001) && (angle < 0.000001)){
+ QUATCOPY((ca - 1)->rot, (ca - 2)->rot);
+ }
+ else{
+ Crossf(normal, prev_tangent, tangent);
+ VecRotToQuat(normal, angle, q);
+ QuatMul((ca - 1)->rot, q, (ca - 2)->rot);
+ }
+
+ VECCOPY(prev_tangent, tangent);
+ }
+
+ if(k == steps) {
+ QUATCOPY(ca->rot, (ca - 1)->rot);
+ }
+ }
+
+ VECCOPY(ca->co, result.co);
+
+ if(k){
+ VECSUB(ca->vel, ca->co, (ca-1)->co);
+
+ if(k==1) {
+ VECCOPY((ca-1)->vel, ca->vel);
+ }
+
+ }
+
+
+ /* selection coloring in edit mode */
+ if(edit){
+ if(pset->brushtype==PE_BRUSH_WEIGHT){
+ if(k==steps)
+ VecLerpf(ca->col, nosel_col, sel_col, hkey[0]->weight);
+ else
+ VecLerpf(ca->col,nosel_col,sel_col,
+ (1.0f - keytime) * hkey[0]->weight + keytime * hkey[1]->weight);
+ }
+ else{
+ if((ekey + (hkey[0] - pa->hair))->flag & PEK_SELECT){
+ if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){
+ VECCOPY(ca->col, sel_col);
+ }
+ else{
+ VecLerpf(ca->col, sel_col, nosel_col, keytime);
+ }
+ }
+ else{
+ if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){
+ VecLerpf(ca->col, nosel_col, sel_col, keytime);
+ }
+ else{
+ VECCOPY(ca->col, nosel_col);
+ }
+ }
+ }
+ }
+ else{
+ VECCOPY(ca->col, col);
+ }
+
+ if(psys->lattice && edit==0)
+ calc_latt_deform(ca->co, 1.0f);
+ }
+ }
+
+ psys->totcached = totpart;
+
+ if(psys && psys->lattice){
+ end_latt_deform();
+ psys->lattice=0;
+ }
+}
+/************************************************/
+/* Particle Key handling */
+/************************************************/
+void copy_particle_key(ParticleKey *to, ParticleKey *from, int time){
+ if(time){
+ memcpy(to,from,sizeof(ParticleKey));
+ }
+ else{
+ float to_time=to->time;
+ memcpy(to,from,sizeof(ParticleKey));
+ to->time=to_time;
+ }
+ /*
+ VECCOPY(to->co,from->co);
+ VECCOPY(to->vel,from->vel);
+ QUATCOPY(to->rot,from->rot);
+ if(time)
+ to->time=from->time;
+ to->flag=from->flag;
+ to->sbw=from->sbw;
+ */
+}
+void psys_get_from_key(ParticleKey *key, float *loc, float *vel, float *rot, float *time){
+ if(loc) VECCOPY(loc,key->co);
+ if(vel) VECCOPY(vel,key->vel);
+ if(rot) QUATCOPY(rot,key->rot);
+ if(time) *time=key->time;
+}
+/*-------changing particle keys from space to another-------*/
+void psys_key_to_object(Object *ob, ParticleKey *key, float imat[][4]){
+ float q[4], imat2[4][4];
+
+ if(imat==0){
+ Mat4Invert(imat2,ob->obmat);
+ imat=imat2;
+ }
+
+ VECADD(key->vel,key->vel,key->co);
+
+ Mat4MulVecfl(imat,key->co);
+ Mat4MulVecfl(imat,key->vel);
+ Mat4ToQuat(imat,q);
+
+ VECSUB(key->vel,key->vel,key->co);
+ QuatMul(key->rot,q,key->rot);
+}
+static void key_from_object(Object *ob, ParticleKey *key){
+ float q[4];
+
+ VECADD(key->vel,key->vel,key->co);
+
+ Mat4MulVecfl(ob->obmat,key->co);
+ Mat4MulVecfl(ob->obmat,key->vel);
+ Mat4ToQuat(ob->obmat,q);
+
+ VECSUB(key->vel,key->vel,key->co);
+ QuatMul(key->rot,q,key->rot);
+}
+
+static void triatomat(float *v1, float *v2, float *v3, float (*uv)[2], float mat[][4])
+{
+ float det, w1, w2, d1[2], d2[2];
+
+ memset(mat, 0, sizeof(float)*4*4);
+ mat[3][3]= 1.0f;
+
+ /* first axis is the normal */
+ CalcNormFloat(v1, v2, v3, mat[2]);
+
+ /* second axis along (1, 0) in uv space */
+ if(uv) {
+ d1[0]= uv[1][0] - uv[0][0];
+ d1[1]= uv[1][1] - uv[0][1];
+ d2[0]= uv[2][0] - uv[0][0];
+ d2[1]= uv[2][1] - uv[0][1];
+
+ det = d2[0]*d1[1] - d2[1]*d1[0];
+
+ if(det != 0.0f) {
+ det= 1.0f/det;
+ w1= -d2[1]*det;
+ w2= d1[1]*det;
+
+ mat[1][0]= w1*(v2[0] - v1[0]) + w2*(v3[0] - v1[0]);
+ mat[1][1]= w1*(v2[1] - v1[1]) + w2*(v3[1] - v1[1]);
+ mat[1][2]= w1*(v2[2] - v1[2]) + w2*(v3[2] - v1[2]);
+ Normalize(mat[1]);
+ }
+ else
+ mat[1][0]= mat[1][1]= mat[1][2]= 0.0f;
+ }
+ else {
+ VecSubf(mat[1], v2, v1);
+ Normalize(mat[1]);
+ }
+
+ /* third as a cross product */
+ Crossf(mat[0], mat[1], mat[2]);
+}
+
+static void psys_face_mat(DerivedMesh *dm, ParticleData *pa, float mat[][4])
+{
+ float v1[3], v2[3], v3[3];
+ MFace *mface;
+ OrigSpaceFace *osface;
+
+ int i = pa->num_dmcache==DMCACHE_NOTFOUND ? pa->num : pa->num_dmcache;
+
+ if (i==-1 || i >= dm->getNumFaces(dm)) { Mat4One(mat); return; }
+ mface=dm->getFaceData(dm,i,CD_MFACE);
+ osface=dm->getFaceData(dm,i,CD_ORIGSPACE);
+
+ dm->getVertCo(dm,mface->v1,v1);
+ dm->getVertCo(dm,mface->v2,v2);
+ dm->getVertCo(dm,mface->v3,v3);
+
+ triatomat(v1, v2, v3, (osface)? osface->uv: NULL, mat);
+}
+void psys_mat_hair_to_object(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[][4])
+{
+ float vec[3];
+
+ psys_face_mat(dm, pa, hairmat);
+ psys_particle_on_dm(ob, dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0);
+ VECCOPY(hairmat[3],vec);
+}
+
+/*
+void psys_key_to_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key)
+{
+ float q[4], v1[3], v2[3], v3[3];
+
+ dm->getVertCo(dm,pa->verts[0],v1);
+ dm->getVertCo(dm,pa->verts[1],v2);
+ dm->getVertCo(dm,pa->verts[2],v3);
+
+ triatoquat(v1, v2, v3, q);
+
+ QuatInv(q);
+
+ VECSUB(key->co,key->co,v1);
+
+ VECADD(key->vel,key->vel,key->co);
+
+ QuatMulVecf(q, key->co);
+ QuatMulVecf(q, key->vel);
+
+ VECSUB(key->vel,key->vel,key->co);
+
+ QuatMul(key->rot,q,key->rot);
+}
+
+void psys_key_from_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key)
+{
+ float q[4], v1[3], v2[3], v3[3];
+
+ dm->getVertCo(dm,pa->verts[0],v1);
+ dm->getVertCo(dm,pa->verts[1],v2);
+ dm->getVertCo(dm,pa->verts[2],v3);
+
+ triatoquat(v1, v2, v3, q);
+
+ VECADD(key->vel,key->vel,key->co);
+
+ QuatMulVecf(q, key->co);
+ QuatMulVecf(q, key->vel);
+
+ VECSUB(key->vel,key->vel,key->co);
+
+ VECADD(key->co,key->co,v1);
+
+ QuatMul(key->rot,q,key->rot);
+}
+*/
+
+void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float *vec)//to_geometry(DerivedMesh *dm, ParticleData *pa, float *vec)
+{
+ float mat[4][4];
+
+ psys_face_mat(dm, pa, mat);
+ Mat4Transp(mat); /* cheap inverse for rotation matrix */
+ Mat4Mul3Vecfl(mat, vec);
+}
+
+/* unused */
+#if 0
+static void psys_vec_rot_from_face(DerivedMesh *dm, ParticleData *pa, float *vec)//from_geometry(DerivedMesh *dm, ParticleData *pa, float *vec)
+{
+ float q[4], v1[3], v2[3], v3[3];
+ /*
+ dm->getVertCo(dm,pa->verts[0],v1);
+ dm->getVertCo(dm,pa->verts[1],v2);
+ dm->getVertCo(dm,pa->verts[2],v3);
+ */
+ /* replace with this */
+ MFace *mface;
+ int i; // = psys_particle_dm_face_lookup(dm, pa->num, pa->fuv, pa->foffset, (LinkNode*)NULL);
+ i = pa->num_dmcache==DMCACHE_NOTFOUND ? pa->num : pa->num_dmcache;
+ if (i==-1 || i >= dm->getNumFaces(dm)) { vec[0] = vec[1] = 0; vec[2] = 1; return; }
+ mface=dm->getFaceData(dm,i,CD_MFACE);
+
+ dm->getVertCo(dm,mface->v1,v1);
+ dm->getVertCo(dm,mface->v2,v2);
+ dm->getVertCo(dm,mface->v3,v3);
+ /* done */
+
+ triatoquat(v1, v2, v3, q);
+
+ QuatMulVecf(q, vec);
+
+ //VECADD(vec,vec,v1);
+}
+#endif
+
+void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[][4])
+{
+ float facemat[4][4];
+
+ psys_mat_hair_to_object(ob, dm, from, pa, facemat);
+
+ Mat4MulMat4(hairmat, facemat, ob->obmat);
+}
+
+/************************************************/
+/* ParticleSettings handling */
+/************************************************/
+static void default_particle_settings(ParticleSettings *part)
+{
+ int i;
+
+ part->type= PART_EMITTER;
+ part->distr= PART_DISTR_JIT;
+ part->draw_as=PART_DRAW_DOT;
+ part->bb_uv_split=1;
+ part->bb_align=PART_BB_VIEW;
+ part->bb_split_offset=PART_BB_OFF_LINEAR;
+ part->flag=PART_REACT_MULTIPLE|PART_HAIR_GEOMETRY;
+
+ part->sta= 1.0;
+ part->end= 100.0;
+ part->lifetime= 50.0;
+ part->jitfac= 1.0;
+ part->totpart= 1000;
+ part->grid_res= 10;
+ part->timetweak= 1.0;
+ part->keyed_time= 0.5;
+ //part->userjit;
+
+ part->integrator= PART_INT_MIDPOINT;
+ part->phystype= PART_PHYS_NEWTON;
+ part->hair_step= 10;
+ part->keys_step= 5;
+ part->draw_step= 4;
+ part->ren_step= 6;
+ part->adapt_angle= 5;
+ part->adapt_pix= 3;
+ part->kink_axis= 2;
+ part->reactevent= PART_EVENT_DEATH;
+ part->disp=100;
+ part->from= PART_FROM_FACE;
+ part->length= 1.0;
+ part->rotfac= 1.0;
+ part->nbetween= 4;
+ part->boidneighbours= 5;
+
+ 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->mass=1.0;
+ part->size=1.0;
+ part->childsize=1.0;
+
+ part->child_nbr=10;
+ part->childrad=0.2f;
+ part->childflat=0.0f;
+ part->clumppow=0.0f;
+ part->kink_amp=0.2f;
+ part->kink_freq=2.0;
+
+ part->rough1_size=1.0;
+ part->rough2_size=1.0;
+ part->rough_end_shape=1.0;
+
+ part->draw_line[0]=0.5;
+
+ part->banking=1.0;
+ part->max_bank=1.0;
+
+ for(i=0; i<BOID_TOT_RULES; i++){
+ part->boidrule[i]=(char)i;
+ part->boidfac[i]=0.5;
+ }
+
+ part->ipo = NULL;
+}
+
+
+ParticleSettings *psys_new_settings(char *name, Main *main)
+{
+ ParticleSettings *part;
+
+ part= alloc_libblock(&main->particle, ID_PA, name);
+
+ default_particle_settings(part);
+
+ return part;
+}
+
+ParticleSettings *psys_copy_settings(ParticleSettings *part)
+{
+ ParticleSettings *partn;
+
+ partn= copy_libblock(part);
+ if(partn->pd) partn->pd= MEM_dupallocN(part->pd);
+
+ return partn;
+}
+
+void psys_make_local_settings(ParticleSettings *part)
+{
+ Object *ob;
+ ParticleSettings *par;
+ int local=0, lib=0;
+
+ /* - only lib users: do nothing
+ * - only local users: set flag
+ * - mixed: make copy
+ */
+
+ if(part->id.lib==0) return;
+ if(part->id.us==1) {
+ part->id.lib= 0;
+ part->id.flag= LIB_LOCAL;
+ new_id(0, (ID *)part, 0);
+ return;
+ }
+
+ /* test objects */
+ ob= G.main->object.first;
+ while(ob) {
+ ParticleSystem *psys=ob->particlesystem.first;
+ for(; psys; psys=psys->next){
+ if(psys->part==part) {
+ if(ob->id.lib) lib= 1;
+ else local= 1;
+ }
+ }
+ ob= ob->id.next;
+ }
+
+ if(local && lib==0) {
+ part->id.lib= 0;
+ part->id.flag= LIB_LOCAL;
+ new_id(0, (ID *)part, 0);
+ }
+ else if(local && lib) {
+
+ par= psys_copy_settings(part);
+ par->id.us= 0;
+
+ /* do objects */
+ ob= G.main->object.first;
+ while(ob) {
+ ParticleSystem *psys=ob->particlesystem.first;
+ for(; psys; psys=psys->next){
+ if(psys->part==part && ob->id.lib==0) {
+ psys->part= par;
+ par->id.us++;
+ part->id.us--;
+ }
+ }
+ ob= ob->id.next;
+ }
+ }
+}
+
+/* should be integrated to depgraph signals */
+void psys_flush_settings(ParticleSettings *part, int event, int hair_recalc)
+{
+ Base *base;
+ Object *ob, *tob;
+ ParticleSystem *psys;
+ int flush;
+
+ /* update all that have same particle settings */
+ for(base = G.scene->base.first; base; base= base->next) {
+ if(base->object->particlesystem.first) {
+ ob=base->object;
+ flush=0;
+ for(psys=ob->particlesystem.first; psys; psys=psys->next){
+ if(psys->part==part){
+ psys->recalc |= event;
+ if(hair_recalc)
+ psys->recalc |= PSYS_RECALC_HAIR;
+ flush++;
+ }
+ else if(psys->part->type==PART_REACTOR){
+ ParticleSystem *tpsys;
+ tob=psys->target_ob;
+ if(tob==0)
+ tob=ob;
+ tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1);
+
+ if(tpsys && tpsys->part==part){
+ psys->flag |= event;
+ flush++;
+ }
+ }
+ }
+ if(flush)
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ }
+ }
+}
+/************************************************/
+/* Textures */
+/************************************************/
+static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float *fw, float *orco, ParticleTexture *ptex, int event)
+{
+ MTex *mtex;
+ int m,setvars=0;
+ float value, rgba[4], texco[3];
+
+ if(ma) for(m=0; m<MAX_MTEX; m++){
+ mtex=ma->mtex[m];
+ if(mtex && (ma->septex & (1<<m))==0){
+ float var=mtex->varfac;
+ short blend=mtex->blendtype;
+ short neg=mtex->pmaptoneg;
+
+ if(mtex->texco & TEXCO_UV && fw){
+ int uv_index=CustomData_get_named_layer_index(&dm->faceData,CD_MTFACE,mtex->uvname);
+ if(uv_index<0){
+ uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE);
+ }
+ if(uv_index>=0){
+ CustomDataLayer *layer=&dm->faceData.layers[uv_index];
+ MTFace *mtface= &((MTFace*)layer->data)[face_index];
+ MFace *mf=dm->getFaceData(dm,face_index,CD_MFACE);
+ psys_interpolate_uvs(mtface,mf->v4,fw,texco);
+ texco[0]*=2.0;
+ texco[1]*=2.0;
+ texco[0]-=1.0;
+ texco[1]-=1.0;
+ }
+ else
+ VECCOPY(texco,orco);
+ }
+ else{
+ VECCOPY(texco,orco);
+ }
+ externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3);
+ if((event & mtex->pmapto) & MAP_PA_TIME){
+ if((setvars&MAP_PA_TIME)==0){
+ ptex->time=0.0;
+ setvars|=MAP_PA_TIME;
+ }
+ ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME);
+ }
+ if((event & mtex->pmapto) & MAP_PA_LENGTH)
+ ptex->length= texture_value_blend(value,ptex->length,value,var,blend,neg & MAP_PA_LENGTH);
+ if((event & mtex->pmapto) & MAP_PA_CLUMP)
+ ptex->clump= texture_value_blend(value,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP);
+ if((event & mtex->pmapto) & MAP_PA_KINK)
+ ptex->kink= texture_value_blend(value,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP);
+ }
+ }
+ CLAMP(ptex->time,0.0,1.0);
+ CLAMP(ptex->length,0.0,1.0);
+ CLAMP(ptex->clump,0.0,1.0);
+ CLAMP(ptex->kink,0.0,1.0);
+}
+void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleData *pa, ParticleTexture *ptex, int event)
+{
+ MTex *mtex;
+ int m;
+ float value, rgba[4], texco[3];
+ int setvars=0;
+
+ if(ma) for(m=0; m<MAX_MTEX; m++){
+ mtex=ma->mtex[m];
+ if(mtex && (ma->septex & (1<<m))==0){
+ float var=mtex->varfac;
+ short blend=mtex->blendtype;
+ short neg=mtex->pmaptoneg;
+
+ if(mtex->texco & TEXCO_UV){
+ int uv_index=CustomData_get_named_layer_index(&psmd->dm->faceData,CD_MTFACE,mtex->uvname);
+ if(uv_index<0){
+ uv_index=CustomData_get_active_layer_index(&psmd->dm->faceData,CD_MTFACE);
+ }
+ if(uv_index>=0){
+ CustomDataLayer *layer=&psmd->dm->faceData.layers[uv_index];
+ MTFace *mtface= &((MTFace*)layer->data)[pa->num];
+ MFace *mf=psmd->dm->getFaceData(psmd->dm,pa->num,CD_MFACE);
+ psys_interpolate_uvs(mtface,mf->v4,pa->fuv,texco);
+ texco[0]*=2.0;
+ texco[1]*=2.0;
+ texco[0]-=1.0;
+ texco[1]-=1.0;
+ }
+ else
+ //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->foffset,texco,0,0,0);
+ /* <jahka> anyways I think it will be too small a difference to notice, so psys_get_texture should only know about the original mesh structure.. no dm needed anywhere */
+ psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0);
+ }
+ else{
+ //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->offset,texco,0,0,0);
+ /* ditto above */
+ psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0);
+ }
+ externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3);
+
+ if((event & mtex->pmapto) & MAP_PA_TIME){
+ if((setvars&MAP_PA_TIME)==0){
+ ptex->time=0.0;
+ setvars|=MAP_PA_TIME;
+ }
+ ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME);
+ }
+ if((event & mtex->pmapto) & MAP_PA_LIFE)
+ ptex->life= texture_value_blend(mtex->def_var,ptex->life,value,var,blend,neg & MAP_PA_LIFE);
+ if((event & mtex->pmapto) & MAP_PA_DENS)
+ ptex->exist= texture_value_blend(mtex->def_var,ptex->exist,value,var,blend,neg & MAP_PA_DENS);
+ if((event & mtex->pmapto) & MAP_PA_SIZE)
+ ptex->size= texture_value_blend(mtex->def_var,ptex->size,value,var,blend,neg & MAP_PA_SIZE);
+ if((event & mtex->pmapto) & MAP_PA_IVEL)
+ ptex->ivel= texture_value_blend(mtex->def_var,ptex->ivel,value,var,blend,neg & MAP_PA_IVEL);
+ if((event & mtex->pmapto) & MAP_PA_PVEL)
+ texture_rgb_blend(ptex->pvel,rgba,ptex->pvel,value,var,blend);
+ if((event & mtex->pmapto) & MAP_PA_LENGTH)
+ ptex->length= texture_value_blend(mtex->def_var,ptex->length,value,var,blend,neg & MAP_PA_LENGTH);
+ if((event & mtex->pmapto) & MAP_PA_CLUMP)
+ ptex->clump= texture_value_blend(mtex->def_var,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP);
+ if((event & mtex->pmapto) & MAP_PA_KINK)
+ ptex->kink= texture_value_blend(mtex->def_var,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP);
+ }
+ }
+ CLAMP(ptex->time,0.0,1.0);
+ CLAMP(ptex->life,0.0,1.0);
+ CLAMP(ptex->exist,0.0,1.0);
+ CLAMP(ptex->size,0.0,1.0);
+ CLAMP(ptex->ivel,0.0,1.0);
+ CLAMP(ptex->length,0.0,1.0);
+ CLAMP(ptex->clump,0.0,1.0);
+ CLAMP(ptex->kink,0.0,1.0);
+}
+/************************************************/
+/* Particle State */
+/************************************************/
+float psys_get_timestep(ParticleSettings *part)
+{
+ return 0.04f*part->timetweak;
+}
+/* part->size should be updated with possible ipo effection before this is called */
+float psys_get_size(Object *ob, Material *ma, ParticleSystemModifierData *psmd, IpoCurve *icu_size, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, float *vg_size)
+{
+ ParticleTexture ptex;
+ float size=1.0f;
+
+ if(ma && part->from!=PART_FROM_PARTICLE){
+ ptex.size=size;
+ psys_get_texture(ob,ma,psmd,psys,pa,&ptex,MAP_PA_SIZE);
+ size=ptex.size;
+ }
+
+ if(icu_size){
+ calc_icu(icu_size,pa->time);
+ size*=icu_size->curval;
+ }
+
+ if(vg_size)
+ size*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_size);
+
+ if(part->randsize!=0.0)
+ size*= 1.0f - part->randsize*pa->sizemul;
+
+ return size*part->size;
+}
+float psys_get_child_time(ParticleSystem *psys, int child_nbr, float cfra)
+{
+ ParticleSettings *part = psys->part;
+ ChildParticle *cpa=psys->child+child_nbr;
+
+ if(part->childtype==PART_CHILD_FACES){
+ float time;
+ int w=0;
+ time=0.0;
+ while(w<4 && cpa->pa[w]>=0){
+ time+=cpa->w[w]*(psys->particles+cpa->pa[w])->time;
+ w++;
+ }
+
+ return (cfra-time)/(part->lifetime*(1.0f-part->randlife*cpa->rand[1]));
+ }
+ else{
+ ParticleData *pa = psys->particles + cpa->parent;
+ return (cfra-pa->time)/pa->lifetime;
+ }
+}
+float psys_get_child_size(ParticleSystem *psys, int child_nbr, float cfra, float *pa_time)
+{
+ ParticleSettings *part = psys->part;
+ ChildParticle *cpa = psys->child + child_nbr;
+ float size, time;
+
+ if(part->childtype==PART_CHILD_FACES){
+ if(pa_time)
+ time=*pa_time;
+ else
+ time=psys_get_child_time(psys,child_nbr,cfra);
+
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ calc_ipo(part->ipo, 100*time);
+ execute_ipo((ID *)part, part->ipo);
+ }
+ size=part->size;
+ }
+ else
+ size=psys->particles[cpa->parent].size;
+
+ size*=part->childsize;
+
+ if(part->childrandsize!=0.0)
+ size *= 1.0f - part->childrandsize*cpa->rand[2];
+
+ return size;
+}
+/* get's hair (or keyed) particles state at the "path time" specified in state->time */
+void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, ParticleKey *state, int vel)
+{
+ ParticleSettings *part = psys->part;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ Material *ma = give_current_material(ob, part->omat);
+ ParticleData *pa;
+ ChildParticle *cpa;
+ ParticleTexture ptex;
+ ParticleKey tstate;
+ HairKey *hkey[2];
+ ParticleKey *par=0, keys[4];
+
+ float t, real_t, dfra, keytime;
+ float orco[3];
+ float imat[4][4], hairmat[4][4], cpa_1st[3];
+ float pa_clump = 0.0, pa_kink = 0.0;
+ int totparent = 0;
+ int totpart = psys->totpart;
+ int totchild = psys->totchild;
+ short between = 0, edit = 0;
+
+ float *cpa_fuv; int cpa_num; short cpa_from;
+
+ //if(psys_in_edit_mode(psys)){
+ // if((psys->edit_path->flag & PSYS_EP_SHOW_CHILD)==0)
+ // totchild=0;
+ // edit=1;
+ //}
+
+ if(G.rendering==0 && part->flag & PART_CHILD_RENDER)
+ totchild=0;
+
+ /* user want's cubic interpolation but only without sb it possible */
+ //if(interpolation==PART_INTER_CUBIC && baked && psys->softflag==OB_SB_ENABLE)
+ // interpolation=PART_INTER_BSPLINE;
+ //else if(baked==0) /* it doesn't make sense to use other types for keyed */
+ // interpolation=PART_INTER_CUBIC;
+
+ t=state->time;
+ CLAMP(t, 0.0, 1.0);
+
+ if(p<totpart){
+ pa = psys->particles + p;
+
+ if(pa->alive==PARS_DEAD && part->flag & PART_STICKY && pa->flag & PARS_STICKY && pa->stick_ob){
+ copy_particle_key(state,&pa->state,0);
+ key_from_object(pa->stick_ob,state);
+ return;
+ }
+
+ hkey[0] = pa->hair;
+ hkey[1] = pa->hair + 1;
+
+ real_t = hkey[0]->time + (hkey[0][pa->totkey-1].time - hkey[0]->time) * t;
+
+ while(hkey[1]->time < real_t)
+ hkey[1]++;
+
+ hkey[0] = hkey[1] - 1;
+
+ hair_to_particle(keys + 1, hkey[0]);
+ hair_to_particle(keys + 2, hkey[1]);
+
+ //if(soft){
+ // if(key[0] != sbel.keys)
+ // DB_copy_key(&k1,key[0]-1);
+ // else
+ // DB_copy_key(&k1,&k2);
+ //}
+ //else{
+ if(hkey[0] != pa->hair)
+ hair_to_particle(keys, hkey[0] - 1);
+ else
+ hair_to_particle(keys, hkey[0]);
+ //}
+
+ //if(soft){
+ // if(key[1] != sbel.keys + sbel.totkey-1)
+ // DB_copy_key(&k4,key[1]+1);
+ // else
+ // DB_copy_key(&k4,&k3);
+ //}
+ //else {
+ if(hkey[1] != pa->hair + pa->totkey - 1)
+ hair_to_particle(keys + 3, hkey[1] + 1);
+ else
+ hair_to_particle(keys + 3, hkey[1]);
+ //}
+
+ //psys_get_particle_on_path(bsys,p,t,bkey,ckey[0]);
+
+ //if(part->rotfrom==PART_ROT_KEYS)
+ // QuatInterpol(state->rot,k2.rot,k3.rot,keytime);
+ //else{
+ // /* TODO: different rotations */
+ // float nvel[3];
+ // float *q2;
+ // VECCOPY(nvel,state->vel);
+ // VecMulf(nvel,-1.0f);
+ // q2=vectoquat(nvel, OB_POSX, OB_POSZ);
+ // QUATCOPY(state->rot,q2);
+ //}
+
+ dfra = keys[2].time - keys[1].time;
+
+ keytime = (real_t - keys[1].time) / dfra;
+
+ interpolate_particle((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL
+ ,keys, keytime, state);
+
+ if((pa->flag & PARS_REKEY)==0) {
+ psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat);
+ Mat4MulVecfl(hairmat, state->co);
+
+ if(psys->effectors.first && (part->flag & PART_CHILD_GUIDE)==0) {
+ do_guide(state, p, state->time, &psys->effectors);
+ /* TODO: proper velocity handling */
+ }
+
+ if(psys->lattice && edit==0)
+ calc_latt_deform(state->co,1.0f);
+ }
+ }
+ else if(totchild){
+ Mat4Invert(imat,ob->obmat);
+
+ cpa=psys->child+p-totpart;
+
+ if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
+ totparent=(int)(totchild*part->parents*0.3);
+ /* part->parents could still be 0 so we can't test with totparent */
+ between=1;
+ }
+ if(between){
+ int w = 0;
+ float foffset;
+
+ /* get parent states */
+ while(w<4 && cpa->pa[w]>=0){
+ keys[w].time = t;
+ psys_get_particle_on_path(ob, psys, cpa->pa[w], keys+w, 1);
+ w++;
+ }
+
+ /* get the original coordinates (orco) for texture usage */
+ cpa_num=cpa->num;
+
+ foffset= cpa->foffset;
+ if(part->childtype == PART_CHILD_FACES)
+ foffset = -(2.0f + part->childspread);
+ cpa_fuv = cpa->fuv;
+ cpa_from = PART_FROM_FACE;
+
+ psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,0,0,0);
+
+ /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */
+ VECCOPY(cpa_1st,orco);
+
+ //w=0;
+ //while(w<4 && cpa->pa[w]>=0){
+ // pa=psys->particles+cpa->pa[w];
+ // psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->fuv,pa->foffset,vec,0,0,0);
+ // cpa_1st[0] -= cpa->w[w]*vec[0];
+ // cpa_1st[1] -= cpa->w[w]*vec[1];
+ // cpa_1st[2] -= cpa->w[w]*vec[2];
+ // w++;
+ //}
+
+ Mat4MulVecfl(ob->obmat,cpa_1st);
+
+ pa=0;
+ }
+ else{
+ /* get the parent state */
+
+ keys->time = t;
+ psys_get_particle_on_path(ob,psys,cpa->parent,keys,1);
+
+ /* get the original coordinates (orco) for texture usage */
+ pa=psys->particles+cpa->parent;
+
+ cpa_from=part->from;
+ cpa_num=pa->num;
+ cpa_fuv=pa->fuv;
+
+ psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,0,0,0);
+ }
+
+ /* correct child ipo timing */
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ calc_ipo(part->ipo, 100.0f*t);
+ execute_ipo((ID *)part, part->ipo);
+ }
+
+ /* get different child parameters from textures & vgroups */
+ ptex.clump=1.0;
+ ptex.kink=1.0;
+
+ get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE-MAP_PA_LENGTH);
+
+ pa_clump=ptex.clump;
+ pa_kink=ptex.kink;
+
+ /* TODO: vertex groups */
+
+ if(between){
+ int w=0;
+
+ state->co[0] = state->co[1] = state->co[2] = 0.0f;
+ state->vel[0] = state->vel[1] = state->vel[2] = 0.0f;
+
+ /* child position is the weighted sum of parent positions */
+ while(w<4 && cpa->pa[w]>=0){
+ state->co[0] += cpa->w[w] * keys[w].co[0];
+ state->co[1] += cpa->w[w] * keys[w].co[1];
+ state->co[2] += cpa->w[w] * keys[w].co[2];
+
+ state->vel[0] += cpa->w[w] * keys[w].vel[0];
+ state->vel[1] += cpa->w[w] * keys[w].vel[1];
+ state->vel[2] += cpa->w[w] * keys[w].vel[2];
+ w++;
+ }
+ /* apply offset for correct positioning */
+ VECADD(state->co,state->co,cpa_1st);
+ }
+ else{
+ /* offset the child from the parent position */
+ offset_child(cpa, keys, state, part->childflat, part->childrad);
+ }
+
+ par = keys;
+ //if(totparent){
+ // if(p-totpart>=totparent){
+ // key.time=t;
+ // psys_get_particle_on_path(ob,psys,totpart+cpa->parent,&key,1);
+ // bti->convert_dynamic_key(bsys,&key,par,cpar);
+ // }
+ // else
+ // par=0;
+ //}
+ //else
+ // DB_get_key_on_path(bsys,cpa->parent,t,par,cpar);
+
+ /* apply different deformations to the child path */
+ if(part->kink)
+ do_prekink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape,
+ part->kink_amp, part->kink, part->kink_axis, ob->obmat);
+
+ do_clump(state, par, t, part->clumpfac, part->clumppow, 1.0f);
+
+ if(part->kink)
+ do_postkink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape,
+ part->kink_amp, part->kink, part->kink_axis, ob->obmat);
+
+ if(part->rough1 != 0.0)
+ do_rough(orco, t, part->rough1, part->rough1_size, 0.0, state);
+
+ if(part->rough2 != 0.0)
+ do_rough(cpa->rand, t, part->rough2, part->rough2_size, part->rough2_thres, state);
+
+ if(part->rough_end != 0.0)
+ do_rough_end(cpa->rand, t, part->rough_end, part->rough_end_shape, state, par);
+
+ if(vel){
+ if(t>=0.001f){
+ tstate.time=t-0.001f;
+ psys_get_particle_on_path(ob,psys,p,&tstate,0);
+ VECSUB(state->vel,state->co,tstate.co);
+ }
+ else{
+ tstate.time=t+0.001f;
+ psys_get_particle_on_path(ob,psys,p,&tstate,0);
+ VECSUB(state->vel,tstate.co,state->co);
+ }
+ }
+
+ }
+}
+/* gets particle's state at a time, returns 1 if particle exists and can be seen and 0 if not */
+int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey *state, int always){
+ ParticleSettings *part=psys->part;
+ ParticleData *pa=0;
+ float cfra;
+ int totpart=psys->totpart, between=0;
+
+ if(state->time>0)
+ cfra=state->time;
+ else
+ cfra=bsystem_time(0,(float)G.scene->r.cfra,0.0);
+
+ if(psys->totchild && p>=totpart){
+ if(G.rendering==0 && part->flag&PART_CHILD_RENDER)
+ return 0;
+ if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
+ between=1;
+ }
+ else
+ pa=psys->particles+(psys->child+p-totpart)->parent;
+ }
+ else
+ pa=psys->particles+p;
+
+ if(between){
+ state->time = psys_get_child_time(psys,p-totpart,cfra);
+
+ if(always==0)
+ if((state->time<0.0 && (part->flag & PART_UNBORN)==0)
+ || (state->time>1.0 && (part->flag & PART_DIED)==0))
+ return 0;
+ }
+ else{
+ if(pa->alive==PARS_KILLED) return 0;
+ if(always==0)
+ if((pa->alive==PARS_UNBORN && (part->flag & PART_UNBORN)==0)
+ || (pa->alive==PARS_DEAD && (part->flag & PART_DIED)==0))
+ return 0;
+ }
+
+ //if(bsys->flag & (BSYS_DONE|BSYS_KEYED)){
+ // if(between){
+ // //ChildParticle *cpa=psys->child+p-totpart;
+ // //state->time= (cfra-(part->sta+(part->end-part->sta)*cpa->rand[0]))/(part->lifetime*cpa->rand[1]);
+ // }
+ // else
+ // state->time= (cfra-pa->time)/(pa->dietime-pa->time);//pa->lifetime;
+
+ // psys_get_particle_on_path(ob,psys,p,state,1);
+ // return 1;
+ //}
+ //else{
+ //if(psys->totchild && p>=psys->totpart){
+ // ChildParticle *cpa=psys->child+p-psys->totpart;
+ // ParticleKey *key1, skey;
+ // float t=(cfra-pa->time)/pa->lifetime, clump;
+
+ // pa=psys->particles+cpa->parent;
+
+ // if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){
+ // key1=&skey;
+ // copy_particle_key(key1,&pa->state,0);
+ // key_from_object(pa->stick_ob,key1);
+ // }
+ // else{
+ // key1=&pa->state;
+ // }
+ //
+ // offset_child(cpa, key1, state, part->childflat, part->childrad);
+ //
+ // CLAMP(t,0.0,1.0);
+ // if(part->kink) /* TODO: part->kink_freq*pa_kink */
+ // do_prekink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat);
+ //
+ // /* TODO: pa_clump vgroup */
+ // do_clump(state,key1,t,part->clumpfac,part->clumppow,0);
+
+ // if(part->kink) /* TODO: part->kink_freq*pa_kink */
+ // do_postkink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat);
+
+ //}
+ //else{
+ if (pa) { /* TODO PARTICLE - should this ever be NULL? - Campbell */
+ copy_particle_key(state,&pa->state,0);
+
+ if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){
+ key_from_object(pa->stick_ob,state);
+ }
+
+ if(psys->lattice)
+ calc_latt_deform(state->co,1.0f);
+ }
+ //}
+
+ return 1;
+ //}
+}
+
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
new file mode 100644
index 00000000000..0bcf6be0a4a
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -0,0 +1,4371 @@
+/* particle_system.c
+ *
+ *
+ * $Id: particle_system.c $
+ *
+ * ***** BEGIN GPL/BL DUAL 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. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License. See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * 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) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_particle_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_material_types.h"
+#include "DNA_ipo_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_group_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+
+#include "BLI_rand.h"
+#include "BLI_jitter.h"
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_kdtree.h"
+#include "BLI_linklist.h"
+
+#include "BKE_anim.h"
+#include "BKE_bad_level_calls.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_displist.h"
+
+#include "BKE_particle.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_object.h"
+#include "BKE_material.h"
+#include "BKE_ipo.h"
+#include "BKE_softbody.h"
+#include "BKE_depsgraph.h"
+#include "BKE_lattice.h"
+#include "BKE_pointcache.h"
+#include "BKE_modifier.h"
+
+#include "BSE_headerbuttons.h"
+
+#include "blendef.h"
+
+#include "RE_shader_ext.h"
+
+/************************************************/
+/* Reacting to system events */
+/************************************************/
+
+static int get_current_display_percentage(ParticleSystem *psys)
+{
+ ParticleSettings *part=psys->part;
+
+ if(G.rendering || (part->child_nbr && part->childtype))
+ return 100;
+
+ if(part->phystype==PART_PHYS_KEYED){
+ if(psys->flag & PSYS_FIRST_KEYED)
+ return psys->part->disp;
+ else
+ return 100;
+ }
+ else
+ return psys->part->disp;
+}
+
+static void alloc_particles(ParticleSystem *psys, int new_totpart)
+{
+ ParticleData *newpars = 0, *pa;
+ int i, totpart, totsaved = 0;
+
+ if(new_totpart<0){
+ if(psys->part->distr==PART_DISTR_GRID){
+ totpart= psys->part->grid_res;
+ totpart*=totpart*totpart;
+ }
+ else
+ totpart=psys->part->totpart;
+ }
+ else
+ totpart=new_totpart;
+
+ if(totpart)
+ newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles");
+ if(psys->particles){
+ totsaved=MIN2(psys->totpart,totpart);
+ /*save old pars*/
+ if(totsaved)
+ memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData));
+
+ for(i=totsaved, pa=psys->particles+totsaved; i<psys->totpart; i++, pa++)
+ if(pa->hair) MEM_freeN(pa->hair);
+
+ MEM_freeN(psys->particles);
+ }
+ psys->particles=newpars;
+
+ if(psys->part->child_nbr && psys->part->childtype){
+ if(psys->child)
+ MEM_freeN(psys->child);
+ psys->child = NULL;
+ if(totpart)
+ psys->child= MEM_callocN(totpart*psys->part->child_nbr*sizeof(ChildParticle), "child_particles");
+ psys->totchild=totpart*psys->part->child_nbr;
+ }
+ else if(psys->child){
+ MEM_freeN(psys->child);
+ psys->child=0;
+ psys->totchild=0;
+ }
+
+ psys->totpart=totpart;
+}
+
+/* only run this if from == PART_FROM_FACE */
+static void psys_calc_dmfaces(Object *ob, DerivedMesh *dm, ParticleSystem *psys)
+{
+ /* use for building derived mesh face-origin info,
+ node - the allocated links - total derived mesh face count
+ node_array - is the array of nodes alligned with the base mesh's faces, so each original face can reference its derived faces
+ */
+ Mesh *me= (Mesh*)ob->data;
+ ParticleData *pa= 0;
+ int p;
+
+ /* CACHE LOCATIONS */
+ if(!dm->deformedOnly) {
+ /* Will use later to speed up subsurf/derivedmesh */
+
+ int tot_dm_face = dm->getNumFaces(dm);
+ int totface = me->totface;
+ int *origindex = DM_get_face_data_layer(dm, CD_ORIGINDEX);
+ int i;
+ LinkNode *node, *node_dm_faces, **node_array;
+
+ node_dm_faces = node = MEM_callocN(sizeof(LinkNode)*tot_dm_face, "faceindicies");
+ node_array = MEM_callocN(sizeof(LinkNode *)*totface, "faceindicies array");
+
+ for(i=0; i < tot_dm_face; i++, origindex++, node++) {
+ node->link = (void *)i; // or use the index?
+ if(*origindex != -1) {
+ if(node_array[*origindex]) {
+ /* prepend */
+ node->next = node_array[*origindex];
+ node_array[*origindex] = node;
+ } else {
+ node_array[*origindex] = node;
+ }
+ }
+ }
+
+ /* cache the faces! */
+
+
+ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) {
+ //i = pa->num;
+ //if (i<totface) // should never happen
+ i = psys_particle_dm_face_lookup(ob, dm, pa->num, pa->fuv, node_array[pa->num]);
+ pa->num_dmcache = i;
+ }
+
+ //for (i=0; i < totface; i++) {
+ // i = psys_particle_dm_face_lookup(ob, dm, node_array[], fuv, (LinkNode*)NULL);
+ //}
+ MEM_freeN(node_array);
+ MEM_freeN(node_dm_faces);
+
+ } else {
+ /* set the num_dmcache to an invalid value, just incase */
+ /* TODO PARTICLE, make the following line unnecessary, each function should know to use the num or num_dmcache */
+
+ /*
+ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) {
+ pa->num_dmcache = pa->num;
+ }
+ */
+ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) {
+ pa->num_dmcache = -1;
+ }
+ }
+}
+
+static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys)
+{
+ ParticleData *pa=0;
+ float min[3], max[3], delta[3], d;
+ MVert *mv, *mvert = dm->getVertDataArray(dm,0);
+ int totvert=dm->getNumVerts(dm), from=psys->part->from;
+ int i, j, k, p, res=psys->part->grid_res, size[3], axis;
+
+ mv=mvert;
+
+ /* find bounding box of dm */
+ VECCOPY(min,mv->co);
+ VECCOPY(max,mv->co);
+ mv++;
+
+ for(i=1; i<totvert; i++, mv++){
+ min[0]=MIN2(min[0],mv->co[0]);
+ min[1]=MIN2(min[1],mv->co[1]);
+ min[2]=MIN2(min[2],mv->co[2]);
+
+ max[0]=MAX2(max[0],mv->co[0]);
+ max[1]=MAX2(max[1],mv->co[1]);
+ max[2]=MAX2(max[2],mv->co[2]);
+ }
+
+ VECSUB(delta,max,min);
+
+ /* determine major axis */
+ axis = (delta[0]>=delta[1])?0:((delta[1]>=delta[2])?1:2);
+
+ d = delta[axis]/(float)res;
+
+ size[axis]=res;
+ size[(axis+1)%3]=(int)ceil(delta[(axis+1)%3]/d);
+ size[(axis+2)%3]=(int)ceil(delta[(axis+2)%3]/d);
+
+ /* float errors grrr.. */
+ size[(axis+1)%3] = MIN2(size[(axis+1)%3],res);
+ size[(axis+2)%3] = MIN2(size[(axis+2)%3],res);
+
+ min[0]+=d/2.0f;
+ min[1]+=d/2.0f;
+ min[2]+=d/2.0f;
+
+ for(i=0,p=0,pa=psys->particles; i<res; i++){
+ for(j=0; j<res; j++){
+ for(k=0; k<res; k++,p++,pa++){
+ pa->fuv[0]=min[0]+(float)i*d;
+ pa->fuv[1]=min[1]+(float)j*d;
+ pa->fuv[2]=min[2]+(float)k*d;
+ pa->flag |= PARS_UNEXIST;
+ pa->loop=0; /* abused in volume calculation */
+ }
+ }
+ }
+
+ /* enable particles near verts/edges/faces/inside surface */
+ if(from==PART_FROM_VERT){
+ float vec[3];
+
+ pa=psys->particles;
+
+ min[0]-=d/2.0f;
+ min[1]-=d/2.0f;
+ min[2]-=d/2.0f;
+
+ for(i=0,mv=mvert; i<totvert; i++,mv++){
+ VecSubf(vec,mv->co,min);
+ vec[0]/=delta[0];
+ vec[1]/=delta[1];
+ vec[2]/=delta[2];
+ (pa +((int)(vec[0]*(size[0]-1))*res
+ +(int)(vec[1]*(size[1]-1)))*res
+ +(int)(vec[2]*(size[2]-1)))->flag &= ~PARS_UNEXIST;
+ }
+ }
+ else if(ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)){
+ float co1[3], co2[3];
+
+ MFace *mface=0;
+ float v1[3], v2[3], v3[3], v4[4], lambda;
+ int a, a1, a2, a0mul, a1mul, a2mul, totface;
+ int amax= from==PART_FROM_FACE ? 3 : 1;
+
+ totface=dm->getNumFaces(dm);
+ mface=dm->getFaceDataArray(dm,CD_MFACE);
+
+ for(a=0; a<amax; a++){
+ if(a==0){ a0mul=res*res; a1mul=res; a2mul=1; }
+ else if(a==1){ a0mul=res; a1mul=1; a2mul=res*res; }
+ else{ a0mul=1; a1mul=res*res; a2mul=res; }
+
+ for(a1=0; a1<size[(a+1)%3]; a1++){
+ for(a2=0; a2<size[(a+2)%3]; a2++){
+ mface=dm->getFaceDataArray(dm,CD_MFACE);
+
+ pa=psys->particles + a1*a1mul + a2*a2mul;
+ VECCOPY(co1,pa->fuv);
+ co1[a]-=d/2.0f;
+ VECCOPY(co2,co1);
+ co2[a]+=delta[a] + 0.001f*d;
+ co1[a]-=0.001f*d;
+
+ /* lets intersect the faces */
+ for(i=0; i<totface; i++,mface++){
+ VECCOPY(v1,mvert[mface->v1].co);
+ VECCOPY(v2,mvert[mface->v2].co);
+ VECCOPY(v3,mvert[mface->v3].co);
+
+ if(AxialLineIntersectsTriangle(a,co1, co2, v2, v3, v1, &lambda)){
+ if(from==PART_FROM_FACE)
+ (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ else /* store number of intersections */
+ (pa+(int)(lambda*size[a])*a0mul)->loop++;
+ }
+
+ if(mface->v4){
+ VECCOPY(v4,mvert[mface->v4].co);
+
+ if(AxialLineIntersectsTriangle(a,co1, co2, v4, v1, v3, &lambda)){
+ if(from==PART_FROM_FACE)
+ (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ else
+ (pa+(int)(lambda*size[a])*a0mul)->loop++;
+ }
+ }
+ }
+
+ if(from==PART_FROM_VOLUME){
+ int in=pa->loop%2;
+ if(in) pa->loop++;
+ for(i=0; i<size[0]; i++){
+ if(in || (pa+i*a0mul)->loop%2)
+ (pa+i*a0mul)->flag &= ~PARS_UNEXIST;
+ /* odd intersections == in->out / out->in */
+ /* even intersections -> in stays same */
+ in=(in + (pa+i*a0mul)->loop) % 2;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(psys->part->flag & PART_GRID_INVERT){
+ for(i=0,pa=psys->particles; i<size[0]; i++){
+ for(j=0; j<size[1]; j++){
+ pa=psys->particles + res*(i*res + j);
+ for(k=0; k<size[2]; k++, pa++){
+ pa->flag ^= PARS_UNEXIST;
+ }
+ }
+ }
+ }
+}
+
+/* modified copy from effect.c */
+static void init_mv_jit(float *jit, int num, int seed2, float amount)
+{
+ RNG *rng;
+ float *jit2, x, rad1, rad2, rad3;
+ int i, num2;
+
+ if(num==0) return;
+
+ rad1= (float)(1.0/sqrt((float)num));
+ rad2= (float)(1.0/((float)num));
+ rad3= (float)sqrt((float)num)/((float)num);
+
+ rng = rng_new(31415926 + num + seed2);
+ x= 0;
+ num2 = 2 * num;
+ for(i=0; i<num2; i+=2) {
+
+ jit[i]= x + amount*rad1*(0.5f - rng_getFloat(rng));
+ jit[i+1]= i/(2.0f*num) + amount*rad1*(0.5f - rng_getFloat(rng));
+
+ jit[i]-= (float)floor(jit[i]);
+ jit[i+1]-= (float)floor(jit[i+1]);
+
+ x+= rad3;
+ x -= (float)floor(x);
+ }
+
+ jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit");
+
+ for (i=0 ; i<4 ; i++) {
+ BLI_jitterate1(jit, jit2, num, rad1);
+ BLI_jitterate1(jit, jit2, num, rad1);
+ BLI_jitterate2(jit, jit2, num, rad2);
+ }
+ MEM_freeN(jit2);
+ rng_free(rng);
+}
+
+static void psys_uv_to_w(float u, float v, int quad, float *w)
+{
+ float vert[4][3], co[3];
+
+ if(!quad) {
+ if(u+v > 1.0f)
+ v= 1.0f-v;
+ else
+ u= 1.0f-u;
+ }
+
+ vert[0][0]= 0.0f; vert[0][1]= 0.0f; vert[0][2]= 0.0f;
+ vert[1][0]= 1.0f; vert[1][1]= 0.0f; vert[1][2]= 0.0f;
+ vert[2][0]= 1.0f; vert[2][1]= 1.0f; vert[2][2]= 0.0f;
+
+ co[0]= u;
+ co[1]= v;
+ co[2]= 0.0f;
+
+ if(quad) {
+ vert[3][0]= 0.0f; vert[3][1]= 1.0f; vert[3][2]= 0.0f;
+ MeanValueWeights(vert, 4, co, w);
+ }
+ else {
+ MeanValueWeights(vert, 3, co, w);
+ w[3]= 0.0f;
+ }
+}
+
+static int binary_search_distribution(float *sum, int n, float value)
+{
+ int mid, low=0, high=n;
+
+ while(low <= high) {
+ mid= (low + high)/2;
+ if(sum[mid] <= value && value <= sum[mid+1])
+ return mid;
+ else if(sum[mid] > value)
+ high= mid - 1;
+ else if(sum[mid] < value)
+ low= mid + 1;
+ else
+ return mid;
+ }
+
+ return low;
+}
+
+/* creates a distribution of coordinates on a DerivedMesh */
+/* */
+/* 1. lets check from what we are emitting */
+/* 2. now we know that we have something to emit from so */
+/* let's calculate some weights */
+/* 2.1 from even distribution */
+/* 2.2 and from vertex groups */
+/* 3. next we determine the indexes of emitting thing that */
+/* the particles will have */
+/* 4. let's do jitter if we need it */
+/* 5. now we're ready to set the indexes & distributions to */
+/* the particles */
+/* 6. and we're done! */
+
+/* This is to denote functionality that does not yet work with mesh - only derived mesh */
+#define ONLY_WORKING_WITH_PA_VERTS 0
+static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, ParticleSystem *psys, int from)
+{
+ Object *tob;
+ ParticleData *pa=0, *tpars=0, *tpa;
+ ParticleSettings *part;
+ ParticleSystem *tpsys;
+ ChildParticle *cpa=0;
+ KDTree *tree=0;
+ ParticleSeam *seams=0;
+ float *jit= NULL;
+ int p=0,i;
+ int no_distr=0, cfrom=0;
+ int tot=0, totpart, *index=0, children=0, totseam=0;
+ //int *vertpart=0;
+ int jitlevel= 1, intersect, distr;
+ float *weight=0,*sum=0,*jitoff=0;
+ float cur, maxweight=0.0,tweight;
+ float *v1, *v2, *v3, *v4, co[3], nor[3], co1[3], co2[3], nor1[3];
+ float cur_d, min_d;
+ DerivedMesh *dm= NULL;
+
+ if(ob==0 || psys==0 || psys->part==0)
+ return;
+
+ part=psys->part;
+ totpart=psys->totpart;
+ if(totpart==0)
+ return;
+
+ if (!finaldm->deformedOnly && !CustomData_has_layer( &finaldm->faceData, CD_ORIGINDEX ) ) {
+ error("Can't paint with the current modifier stack, disable destructive modifiers");
+ return;
+ }
+
+ BLI_srandom(31415926 + psys->seed);
+
+ if(from==PART_FROM_CHILD){
+ distr=PART_DISTR_RAND;
+ cpa=psys->child;
+ if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
+ dm= finaldm;
+ children=1;
+
+ tree=BLI_kdtree_new(totpart);
+
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++){
+ psys_particle_on_dm(ob,dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0);
+ BLI_kdtree_insert(tree, p, co, nor);
+ }
+
+ BLI_kdtree_balance(tree);
+
+ totpart=psys->totchild;
+ cfrom=from=PART_FROM_FACE;
+
+ if(part->flag&PART_CHILD_SEAMS){
+ MEdge *ed, *medge=dm->getEdgeDataArray(dm,CD_MEDGE);
+ MVert *mvert=dm->getVertDataArray(dm,CD_MVERT);
+ int totedge=dm->getNumEdges(dm);
+
+ for(p=0, ed=medge; p<totedge; p++,ed++)
+ if(ed->flag&ME_SEAM)
+ totseam++;
+
+ if(totseam){
+ ParticleSeam *cur_seam=seams=MEM_callocN(totseam*sizeof(ParticleSeam),"Child Distribution Seams");
+ float temp[3],temp2[3];
+
+ for(p=0, ed=medge; p<totedge; p++,ed++){
+ if(ed->flag&ME_SEAM){
+ VecCopyf(cur_seam->v0,(mvert+ed->v1)->co);
+ VecCopyf(cur_seam->v1,(mvert+ed->v2)->co);
+
+ VecSubf(cur_seam->dir,cur_seam->v1,cur_seam->v0);
+
+ cur_seam->length2=VecLength(cur_seam->dir);
+ cur_seam->length2*=cur_seam->length2;
+
+ temp[0]=(float)((mvert+ed->v1)->no[0]);
+ temp[1]=(float)((mvert+ed->v1)->no[1]);
+ temp[2]=(float)((mvert+ed->v1)->no[2]);
+ temp2[0]=(float)((mvert+ed->v2)->no[0]);
+ temp2[1]=(float)((mvert+ed->v2)->no[1]);
+ temp2[2]=(float)((mvert+ed->v2)->no[2]);
+
+ VecAddf(cur_seam->nor,temp,temp2);
+ Normalize(cur_seam->nor);
+
+ Crossf(cur_seam->tan,cur_seam->dir,cur_seam->nor);
+
+ Normalize(cur_seam->tan);
+
+ cur_seam++;
+ }
+ }
+ }
+
+ }
+ }
+ else{
+ /* no need to figure out distribution */
+ for(i=0; i<part->child_nbr; i++){
+ for(p=0; p<psys->totpart; p++,cpa++){
+ float length=2.0;
+ cpa->parent=p;
+
+ /* create even spherical distribution inside unit sphere */
+ while(length>=1.0f){
+ cpa->fuv[0]=2.0f*BLI_frand()-1.0f;
+ cpa->fuv[1]=2.0f*BLI_frand()-1.0f;
+ cpa->fuv[2]=2.0f*BLI_frand()-1.0f;
+ length=VecLength(cpa->fuv);
+ }
+
+ cpa->rand[0]=BLI_frand();
+ cpa->rand[1]=BLI_frand();
+ cpa->rand[2]=BLI_frand();
+
+ cpa->num=-1;
+ }
+ }
+
+ return;
+ }
+ }
+ else{
+ dm= CDDM_from_mesh((Mesh*)ob->data, ob);
+
+ /* special handling of grid distribution */
+ if(part->distr==PART_DISTR_GRID){
+ distribute_particles_in_grid(dm,psys);
+ dm->release(dm);
+ return;
+ }
+
+ distr=part->distr;
+ pa=psys->particles;
+ if(from==PART_FROM_VERT){
+ MVert *mv= dm->getVertDataArray(dm,0);
+ int totvert = dm->getNumVerts(dm);
+
+ tree=BLI_kdtree_new(totvert);
+
+ for(p=0; p<totvert; p++,mv++){
+ VECCOPY(co,mv->co);
+ BLI_kdtree_insert(tree,p,co,NULL);
+ }
+
+ BLI_kdtree_balance(tree);
+ }
+ }
+
+ /* 1. */
+ switch(from){
+ case PART_FROM_VERT:
+ tot = dm->getNumVerts(dm);
+ break;
+ case PART_FROM_VOLUME:
+ case PART_FROM_FACE:
+ tot = dm->getNumFaces(dm);
+ break;
+ case PART_FROM_PARTICLE:
+ if(psys->target_ob)
+ tob=psys->target_ob;
+ else
+ tob=ob;
+
+ if((tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1))){
+ tpars=tpsys->particles;
+ tot=tpsys->totpart;
+ }
+ break;
+ }
+
+ if(tot==0){
+ no_distr=1;
+ if(children){
+ fprintf(stderr,"Particle child distribution error: Nothing to emit from!\n");
+ for(p=0,cpa=psys->child; p<totpart; p++,cpa++){
+ cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0;
+ cpa->foffset= 0.0f;
+ cpa->parent=0;
+ cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
+ cpa->num= -1;
+ }
+ }
+ else {
+ fprintf(stderr,"Particle distribution error: Nothing to emit from!\n");
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++){
+ pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0;
+ pa->foffset= 0.0f;
+ pa->num= -1;
+ }
+ }
+
+ if(dm != finaldm) dm->release(dm);
+ return;
+ }
+
+ /* 2. */
+
+ weight=MEM_callocN(sizeof(float)*tot, "particle_distribution_weights");
+ index=MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes");
+ sum=MEM_callocN(sizeof(float)*(tot+1), "particle_distribution_sum");
+ jitoff=MEM_callocN(sizeof(float)*tot, "particle_distribution_jitoff");
+
+ /* 2.1 */
+ if((part->flag&PART_EDISTR || children) && ELEM(from,PART_FROM_PARTICLE,PART_FROM_VERT)==0){
+ float totarea=0.0;
+
+ for(i=0; i<tot; i++){
+ MFace *mf=dm->getFaceData(dm,i,CD_MFACE);
+ MVert *mv1=dm->getVertData(dm,mf->v1,CD_MVERT);
+ MVert *mv2=dm->getVertData(dm,mf->v2,CD_MVERT);
+ MVert *mv3=dm->getVertData(dm,mf->v3,CD_MVERT);
+
+ if (mf->v4){
+ MVert *mv4=dm->getVertData(dm,mf->v4,CD_MVERT);
+ cur= AreaQ3Dfl(mv1->co,mv2->co,mv3->co,mv4->co);
+ }
+ else
+ cur= AreaT3Dfl(mv1->co,mv2->co,mv3->co);
+
+ if(cur>maxweight)
+ maxweight=cur;
+
+ weight[i]= cur;
+ totarea+=cur;
+ }
+
+ for(i=0; i<tot; i++)
+ weight[i] /= totarea;
+
+ maxweight /= totarea;
+ }
+ else if(from==PART_FROM_PARTICLE){
+ float val=(float)tot/(float)totpart;
+ for(i=0; i<tot; i++)
+ weight[i]=val;
+ maxweight=val;
+ }
+ else{
+ float min=1.0f/(float)(MIN2(tot,totpart));
+ for(i=0; i<tot; i++)
+ weight[i]=min;
+ maxweight=min;
+ }
+
+ /* 2.2 */
+ if(ELEM3(from,PART_FROM_VERT,PART_FROM_FACE,PART_FROM_VOLUME)){
+ float *vweight= psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY);
+
+ if(vweight){
+ if(from==PART_FROM_VERT) {
+ for(i=0;i<tot; i++)
+ weight[i]*=vweight[i];
+ }
+ else { /* PART_FROM_FACE / PART_FROM_VOLUME */
+ for(i=0;i<tot; i++){
+ MFace *mf=dm->getFaceData(dm,i,CD_MFACE);
+ tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3];
+
+ if(mf->v4) {
+ tweight += vweight[mf->v4];
+ tweight /= 4.0;
+ }
+ else {
+ tweight /= 3.0;
+ }
+
+ weight[i]*=tweight;
+ }
+ }
+ MEM_freeN(vweight);
+ }
+ }
+
+ /* 3. */
+ sum[0]= 0.0f;
+ for(i=0;i<tot; i++)
+ sum[i+1]= sum[i]+weight[i];
+
+ if(part->flag&PART_TRAND){
+ float pos;
+
+ for(p=0; p<totpart; p++) {
+ pos= BLI_frand();
+ index[p]= binary_search_distribution(sum, tot, pos);
+ jitoff[index[p]]= pos;
+ }
+ }
+ else {
+ float step, pos;
+
+ step= (totpart <= 1)? 0.5f: 1.0f/(totpart-1);
+ pos= 0.0f;
+ i= 0;
+
+ for(p=0; p<totpart; p++, pos+=step) {
+ while((i < tot) && (pos > sum[i+1]))
+ i++;
+
+ index[p]= MIN2(tot-1, i);
+ jitoff[index[p]]= pos;
+ }
+ }
+
+ /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */
+ if(from==PART_FROM_PARTICLE){
+ for(i=0; i<tot; i++)
+ weight[i]=0.0f;
+ }
+
+ /* 4. */
+ if(distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
+ jitlevel= part->userjit;
+
+ if(jitlevel == 0) {
+ jitlevel= totpart/tot;
+ if(part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */
+ if(jitlevel<3) jitlevel= 3;
+ //if(jitlevel>100) jitlevel= 100;
+ }
+
+ jit= MEM_callocN(2+ jitlevel*2*sizeof(float), "jit");
+
+ init_mv_jit(jit, jitlevel, psys->seed, part->jitfac);
+ BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */
+ }
+
+ /* 5. */
+ if(children) from=PART_FROM_CHILD;
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++,cpa++){
+ switch(from){
+ case PART_FROM_VERT:
+ /* TODO_PARTICLE - use original index */
+ pa->num=index[p];
+ pa->fuv[0] = 1.0f;
+ pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0;
+ //pa->verts[0] = pa->verts[1] = pa->verts[2] = 0;
+
+#if ONLY_WORKING_WITH_PA_VERTS
+ if(tree){
+ KDTreeNearest ptn[3];
+ int w,maxw;
+
+ psys_particle_on_dm(ob,dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0);
+ maxw = BLI_kdtree_find_n_nearest(tree,3,co1,NULL,ptn);
+
+ for(w=0; w<maxw; w++){
+ pa->verts[w]=ptn->num;
+ }
+ }
+#endif
+ break;
+ case PART_FROM_FACE:
+ case PART_FROM_VOLUME:
+ {
+ MFace *mface;
+ pa->num = i = index[p];
+ mface = dm->getFaceData(dm,i,CD_MFACE);
+
+ switch(distr){
+ case PART_DISTR_JIT:
+ jitoff[i] = fmod(jitoff[i],(float)jitlevel);
+ psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mface->v4, pa->fuv);
+ jitoff[i]++;
+ //jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel);
+ break;
+ case PART_DISTR_RAND:
+ psys_uv_to_w(BLI_frand(), BLI_frand(), mface->v4, pa->fuv);
+ break;
+ }
+ pa->foffset= 0.0f;
+
+ /*
+ pa->verts[0] = mface->v1;
+ pa->verts[1] = mface->v2;
+ pa->verts[2] = mface->v3;
+ */
+
+ /* experimental */
+ if(from==PART_FROM_VOLUME){
+ MVert *mvert=dm->getVertDataArray(dm,CD_MVERT);
+
+ tot=dm->getNumFaces(dm);
+
+ psys_interpolate_face(mvert,mface,0,pa->fuv,co1,nor,0,0);
+
+ Normalize(nor);
+ VecMulf(nor,-100.0);
+
+ VECADD(co2,co1,nor);
+
+ min_d=2.0;
+ intersect=0;
+
+ for(i=0,mface=dm->getFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++){
+ if(i==pa->num) continue;
+
+ v1=mvert[mface->v1].co;
+ v2=mvert[mface->v2].co;
+ v3=mvert[mface->v3].co;
+
+ if(LineIntersectsTriangle(co1, co2, v2, v3, v1, &cur_d, 0)){
+ if(cur_d<min_d){
+ min_d=cur_d;
+ pa->foffset=cur_d*50.0f; /* to the middle of volume */
+ intersect=1;
+ }
+ }
+ if(mface->v4){
+ v4=mvert[mface->v4].co;
+
+ if(LineIntersectsTriangle(co1, co2, v4, v1, v3, &cur_d, 0)){
+ if(cur_d<min_d){
+ min_d=cur_d;
+ pa->foffset=cur_d*50.0f; /* to the middle of volume */
+ intersect=1;
+ }
+ }
+ }
+ }
+ if(intersect==0)
+ pa->foffset=0.0;
+ else switch(distr){
+ case PART_DISTR_JIT:
+ pa->foffset*= jit[2*(int)jitoff[i]];
+ break;
+ case PART_DISTR_RAND:
+ pa->foffset*=BLI_frand();
+ break;
+ }
+ }
+ break;
+ }
+ case PART_FROM_PARTICLE:
+
+ //pa->verts[0]=0; /* not applicable */
+ //pa->verts[1]=0;
+ //pa->verts[2]=0;
+
+ tpa=tpars+index[p];
+ pa->num=index[p];
+ pa->fuv[0]=tpa->fuv[0];
+ pa->fuv[1]=tpa->fuv[1];
+ /* abusing foffset a little for timing in near reaction */
+ pa->foffset=weight[index[p]];
+ weight[index[p]]+=maxweight;
+ break;
+ case PART_FROM_CHILD:
+ if(index[p]>=0){
+ MFace *mf;
+
+ mf=dm->getFaceData(dm,index[p],CD_MFACE);
+
+ //switch(distr){
+ // case PART_DISTR_JIT:
+ // i=index[p];
+ // psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mf->v4, cpa->fuv);
+ // jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel);
+ // break;
+ // case PART_DISTR_RAND:
+ psys_uv_to_w(BLI_frand(), BLI_frand(), mf->v4, cpa->fuv);
+ // break;
+ //}
+
+ cpa->rand[0] = BLI_frand();
+ cpa->rand[1] = BLI_frand();
+ cpa->rand[2] = BLI_frand();
+ cpa->num = index[p];
+
+ if(tree){
+ KDTreeNearest ptn[10];
+ int w,maxw, do_seams;
+ float maxd,mind,dd,totw=0.0;
+ int parent[10];
+ float pweight[10];
+
+ do_seams= (part->flag&PART_CHILD_SEAMS && seams);
+
+ psys_particle_on_dm(ob,dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0);
+ maxw = BLI_kdtree_find_n_nearest(tree,(do_seams)?10:4,co1,nor1,ptn);
+
+ maxd=ptn[maxw-1].dist;
+ mind=ptn[0].dist;
+ dd=maxd-mind;
+
+ /* the weights here could be done better */
+ for(w=0; w<maxw; w++){
+ parent[w]=ptn[w].index;
+ pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd));
+ //totw+=cpa->w[w];
+ }
+ for(;w<10; w++){
+ parent[w]=-1;
+ pweight[w]=0.0f;
+ }
+ if(do_seams){
+ ParticleSeam *seam=seams;
+ float temp[3],temp2[3],tan[3];
+ float inp,cur_len,min_len=10000.0f;
+ int min_seam=0, near_vert=0;
+ /* find closest seam */
+ for(i=0; i<totseam; i++, seam++){
+ VecSubf(temp,co1,seam->v0);
+ inp=Inpf(temp,seam->dir)/seam->length2;
+ if(inp<0.0f){
+ cur_len=VecLenf(co1,seam->v0);
+ }
+ else if(inp>1.0f){
+ cur_len=VecLenf(co1,seam->v1);
+ }
+ else{
+ VecCopyf(temp2,seam->dir);
+ VecMulf(temp2,inp);
+ cur_len=VecLenf(temp,temp2);
+ }
+ if(cur_len<min_len){
+ min_len=cur_len;
+ min_seam=i;
+ if(inp<0.0f) near_vert=-1;
+ else if(inp>1.0f) near_vert=1;
+ else near_vert=0;
+ }
+ }
+ seam=seams+min_seam;
+
+ VecCopyf(temp,seam->v0);
+
+ if(near_vert){
+ if(near_vert==-1)
+ VecSubf(tan,co1,seam->v0);
+ else{
+ VecSubf(tan,co1,seam->v1);
+ VecCopyf(temp,seam->v1);
+ }
+
+ Normalize(tan);
+ }
+ else{
+ VecCopyf(tan,seam->tan);
+ VecSubf(temp2,co1,temp);
+ if(Inpf(tan,temp2)<0.0f)
+ VecMulf(tan,-1.0f);
+ }
+ for(w=0; w<maxw; w++){
+ VecSubf(temp2,ptn[w].co,temp);
+ if(Inpf(tan,temp2)<0.0f){
+ parent[w]=-1;
+ pweight[w]=0.0f;
+ }
+ }
+
+ }
+
+ for(w=0,i=0; w<maxw && i<4; w++){
+ if(parent[w]>=0){
+ cpa->pa[i]=parent[w];
+ cpa->w[i]=pweight[w];
+ totw+=pweight[w];
+ i++;
+ }
+ }
+ for(;i<4; i++){
+ cpa->pa[i]=-1;
+ cpa->w[i]=0.0f;
+ }
+
+ if(totw>0.0f) for(w=0; w<4; w++)
+ cpa->w[w]/=totw;
+
+ cpa->parent=cpa->pa[0];
+ }
+ }
+ else{
+ cpa->num=0;
+ cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f;
+ cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
+ cpa->rand[0]=cpa->rand[1]=cpa->rand[2]=0.0f;
+ }
+ break;
+ }
+ }
+
+ /* 6. */
+ if(jit) MEM_freeN(jit);
+ if(sum) MEM_freeN(sum);
+ if(jitoff) MEM_freeN(jitoff);
+ if(weight){
+ MEM_freeN(weight);
+ weight=0;
+ }
+ if(index) MEM_freeN(index);
+ if(seams) MEM_freeN(seams);
+ //if(vertpart) MEM_freeN(vertpart);
+ BLI_kdtree_free(tree);
+
+ if (from == PART_FROM_FACE)
+ psys_calc_dmfaces(ob, finaldm, psys);
+
+ if(dm != finaldm) dm->release(dm);
+}
+
+/* ready for future use, to emit particles without geometry */
+static void distribute_particles_on_shape(Object *ob, ParticleSystem *psys, int from)
+{
+ ParticleData *pa;
+ int totpart=psys->totpart, p;
+
+ fprintf(stderr,"Shape emission not yet possible!\n");
+
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++){
+ pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0;
+ pa->foffset= 0.0f;
+ pa->num= -1;
+ }
+}
+static void distribute_particles(Object *ob, ParticleSystem *psys, int from)
+{
+ ParticleSystemModifierData *psmd=0;
+ int distr_error=0;
+ psmd=psys_get_modifier(ob,psys);
+
+ if(psmd){
+ if(psmd->dm)
+ distribute_particles_on_dm(psmd->dm,ob,psys,from);
+ else
+ distr_error=1;
+ }
+ else
+ distribute_particles_on_shape(ob,psys,from);
+
+ if(distr_error){
+ ParticleData *pa;
+ int totpart=psys->totpart, p;
+
+ fprintf(stderr,"Particle distribution error!\n");
+
+ for(p=0,pa=psys->particles; p<totpart; p++,pa++){
+ pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0;
+ pa->foffset= 0.0f;
+ pa->num= -1;
+ }
+ }
+}
+/* set particle parameters that don't change during particle's life */
+void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd)
+{
+ ParticleSettings *part;
+ ParticleTexture ptex;
+ Material *ma=0;
+ IpoCurve *icu=0;
+ int totpart;
+ float rand,length;
+
+ part=psys->part;
+
+ totpart=psys->totpart;
+
+ ptex.life=ptex.size=ptex.exist=ptex.length=1.0;
+ ptex.time=(float)p/(float)totpart;
+
+ BLI_srandom(psys->seed+p);
+
+ if(part->from!=PART_FROM_PARTICLE){
+ ma=give_current_material(ob,part->omat);
+
+ /* TODO: needs some work to make most blendtypes generally usefull */
+ psys_get_texture(ob,ma,psmd,psys,pa,&ptex,MAP_PA_INIT);
+ }
+
+ pa->lifetime= part->lifetime*ptex.life;
+
+ if(part->type==PART_HAIR)
+ pa->time=0.0f;
+ else if(part->type==PART_REACTOR && (part->flag&PART_REACT_STA_END)==0)
+ pa->time=MAXFRAMEF;
+ else{
+ //icu=find_ipocurve(psys->part->ipo,PART_EMIT_TIME);
+ //if(icu){
+ // calc_icu(icu,100*ptex.time);
+ // ptex.time=icu->curval;
+ //}
+
+ pa->time= part->sta + (part->end - part->sta)*ptex.time;
+ }
+
+
+ if(part->type==PART_HAIR){
+ pa->lifetime=100.0f;
+ }
+ else{
+ icu=find_ipocurve(psys->part->ipo,PART_EMIT_LIFE);
+ if(icu){
+ calc_icu(icu,100*ptex.time);
+ pa->lifetime*=icu->curval;
+ }
+
+ /* need to get every rand even if we don't use them so that randoms don't affect eachother */
+ rand= BLI_frand();
+ if(part->randlife!=0.0)
+ pa->lifetime*= 1.0f - part->randlife*rand;
+ }
+
+ pa->dietime= pa->time+pa->lifetime;
+
+ pa->sizemul= BLI_frand();
+
+ 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);
+ }
+
+ length=2.0f;
+ while(length>1.0){
+ pa->r_ave[0]=2.0f*(BLI_frand()-0.5f);
+ pa->r_ave[1]=2.0f*(BLI_frand()-0.5f);
+ pa->r_ave[2]=2.0f*(BLI_frand()-0.5f);
+ length=VecLength(pa->r_ave);
+ }
+
+ pa->r_rot[0]=2.0f*(BLI_frand()-0.5f);
+ pa->r_rot[1]=2.0f*(BLI_frand()-0.5f);
+ pa->r_rot[2]=2.0f*(BLI_frand()-0.5f);
+ pa->r_rot[3]=2.0f*(BLI_frand()-0.5f);
+
+ NormalQuat(pa->r_rot);
+
+ if(part->distr!=PART_DISTR_GRID){
+ /* any unique random number will do (r_ave[0]) */
+ if(ptex.exist < 0.5*(1.0+pa->r_ave[0]))
+ pa->flag |= PARS_UNEXIST;
+ else
+ pa->flag &= ~PARS_UNEXIST;
+ }
+
+ pa->loop=0;
+ /* we can't reset to -1 anymore since we've figured out correct index in distribute_particles */
+ /* usage other than straight after distribute has to handle this index by itself - jahka*/
+ //pa->num_dmcache = DMCACHE_NOTFOUND; /* assume we dont have a derived mesh face */
+}
+static void initialize_all_particles(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd)
+{
+ IpoCurve *icu=0;
+ ParticleData *pa;
+ int p, totpart=psys->totpart;
+
+ for(p=0, pa=psys->particles; p<totpart; p++, pa++)
+ initialize_particle(pa,p,ob,psys,psmd);
+
+ /* store the derived mesh face index for each particle */
+ //if(psys->part->from==PART_FROM_FACE)
+ // psys_calc_dmfaces(ob, psmd->dm, psys);
+
+ icu=find_ipocurve(psys->part->ipo,PART_EMIT_FREQ);
+ if(icu){
+ float time=psys->part->sta, end=psys->part->end;
+ float v1, v2, a=0.0f, t1,t2, d;
+
+ p=0;
+ pa=psys->particles;
+
+ calc_icu(icu,time);
+ v1=icu->curval;
+ if(v1<0.0f) v1=0.0f;
+
+ calc_icu(icu,time+1.0f);
+ v2=icu->curval;
+ if(v2<0.0f) v2=0.0f;
+
+ for(p=0, pa=psys->particles; p<totpart && time<end; p++, pa++){
+ while(a+0.5f*(v1+v2) < (float)(p+1) && time<end){
+ a+=0.5f*(v1+v2);
+ v1=v2;
+ time++;
+ calc_icu(icu,time+1.0f);
+ v2=icu->curval;
+ }
+ if(time<end){
+ if(v1==v2){
+ pa->time=time+((float)(p+1)-a)/v1;
+ }
+ else{
+ d=(float)sqrt(v1*v1-2.0f*(v2-v1)*(a-(float)(p+1)));
+ t1=(-v1+d)/(v2-v1);
+ t2=(-v1-d)/(v2-v1);
+
+ /* the root between 0-1 is the correct one */
+ if(t1>0.0f && t1<=1.0f)
+ pa->time=time+t1;
+ else
+ pa->time=time+t2;
+ }
+ }
+
+ pa->dietime = pa->time+pa->lifetime;
+ pa->flag &= ~PARS_UNEXIST;
+ }
+ for(; p<totpart; p++, pa++){
+ pa->flag |= PARS_UNEXIST;
+ }
+ }
+}
+/* sets particle to the emitter surface with initial velocity & rotation */
+void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifierData *psmd, Object *ob,
+ float dtime, float cfra, float *vg_vel, float *vg_tan, float *vg_rot)
+{
+ ParticleSettings *part;
+ ParticleTexture ptex;
+ ParticleKey state;
+ IpoCurve *icu=0;
+ float fac, nor[3]={0,0,0},loc[3],tloc[3],vel[3]={0.0,0.0,0.0},rot[4],*q2=0;
+ float r_vel[3],r_ave[3],r_rot[4],p_vel[3]={0.0,0.0,0.0};
+ float x_vec[3]={1.0,0.0,0.0}, utan[3]={0.0,1.0,0.0}, vtan[3]={0.0,0.0,1.0};
+
+ float q_one[4]={1.0,0.0,0.0,0.0}, q_phase[4];
+ part=psys->part;
+
+ ptex.ivel=1.0;
+
+ if(part->from==PART_FROM_PARTICLE){
+ Object *tob;
+ ParticleSystem *tpsys=0;
+ float speed;
+
+ tob=psys->target_ob;
+ if(tob==0)
+ tob=ob;
+
+ tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1);
+
+ /*TODO: get precise location of particle at birth*/
+
+ state.time=cfra;
+ psys_get_particle_state(tob,tpsys,pa->num,&state,1);
+ psys_get_from_key(&state,loc,nor,rot,0);
+
+ QuatMulVecf(rot,vtan);
+ QuatMulVecf(rot,utan);
+ VECCOPY(r_vel,pa->r_ve);
+ VECCOPY(r_rot,pa->r_rot);
+ VECCOPY(r_ave,pa->r_ave);
+
+ VECCOPY(p_vel,state.vel);
+ speed=Normalize(p_vel);
+ VecMulf(p_vel,Inpf(pa->r_ve,p_vel));
+ VECSUB(p_vel,pa->r_ve,p_vel);
+ Normalize(p_vel);
+ VecMulf(p_vel,speed);
+ }
+ else{
+ /* get precise emitter matrix if particle is born */
+ if(part->type!=PART_HAIR && pa->time < cfra && pa->time >= psys->cfra)
+ where_is_object_time(ob,pa->time);
+
+ /* get birth location from object */
+ psys_particle_on_emitter(ob,psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan);
+
+ /* save local coordinates for later */
+ VECCOPY(tloc,loc);
+
+ /* get possible textural influence */
+ psys_get_texture(ob,give_current_material(ob,part->omat),psmd,psys,pa,&ptex,MAP_PA_IVEL);
+
+ if(vg_vel){
+ ptex.ivel*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_vel);
+ }
+
+ /* particles live in global space so */
+ /* let's convert: */
+ /* -location */
+ Mat4MulVecfl(ob->obmat,loc);
+
+ /* -normal */
+ VECADD(nor,tloc,nor);
+ Mat4MulVecfl(ob->obmat,nor);
+ VECSUB(nor,nor,loc);
+ Normalize(nor);
+
+ /* -tangent */
+ if(part->tanfac!=0.0){
+ float phase=vg_rot?2.0f*(psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_rot)-0.5f):0.0f;
+ VecMulf(vtan,-(float)cos(M_PI*(part->tanphase+phase)));
+ fac=-(float)sin(M_PI*(part->tanphase+phase));
+ VECADDFAC(vtan,vtan,utan,fac);
+
+ VECADD(vtan,tloc,vtan);
+ Mat4MulVecfl(ob->obmat,vtan);
+ VECSUB(vtan,vtan,loc);
+
+ VECCOPY(utan,nor);
+ VecMulf(utan,Inpf(vtan,nor));
+ VECSUB(vtan,vtan,utan);
+
+ Normalize(vtan);
+ }
+
+
+ /* -velocity */
+ if(part->randfac!=0.0){
+ VECADD(r_vel,tloc,pa->r_ve);
+ Mat4MulVecfl(ob->obmat,r_vel);
+ VECSUB(r_vel,r_vel,loc);
+ Normalize(r_vel);
+ }
+
+ /* -angular velocity */
+ if(part->avemode==PART_AVE_RAND){
+ VECADD(r_ave,tloc,pa->r_ave);
+ Mat4MulVecfl(ob->obmat,r_ave);
+ VECSUB(r_ave,r_ave,loc);
+ Normalize(r_ave);
+ }
+
+ /* -rotation */
+ if(part->rotmode==PART_ROT_RAND){
+ QUATCOPY(r_rot,pa->r_rot);
+ Mat4ToQuat(ob->obmat,rot);
+ QuatMul(r_rot,r_rot,rot);
+ }
+ }
+ /* conversion done so now we apply new: */
+ /* -velocity from: */
+ /* *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(part->tanfac!=0.0)
+ VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_tan):1.0f));
+
+ /* *texture */
+ /* TODO */
+
+ /* *random */
+ if(part->randfac!=0.0)
+ VECADDFAC(vel,vel,r_vel,part->randfac);
+
+ /* *particle */
+ if(part->partfac!=0.0)
+ VECADDFAC(vel,vel,p_vel,part->partfac);
+
+ 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;
+ }
+
+ VecMulf(vel,ptex.ivel);
+
+ VECCOPY(pa->state.vel,vel);
+
+ /* -location from emitter */
+ VECCOPY(pa->state.co,loc);
+
+ /* -rotation */
+ pa->state.rot[0]=1.0;
+ pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0;
+
+ if(part->rotmode){
+ switch(part->rotmode){
+ case PART_ROT_NOR:
+ VecMulf(nor,-1.0);
+ q2= vectoquat(nor, OB_POSX, OB_POSZ);
+ VecMulf(nor,-1.0);
+ break;
+ case PART_ROT_VEL:
+ VecMulf(vel,-1.0);
+ q2= vectoquat(vel, OB_POSX, OB_POSZ);
+ VecMulf(vel,-1.0);
+ break;
+ case PART_ROT_RAND:
+ q2= r_rot;
+ break;
+ }
+ /* how much to rotate from rest position */
+ QuatInterpol(rot,q_one,q2,part->rotfac);
+
+ /* phase */
+ VecRotToQuat(x_vec,part->phasefac*(float)M_PI,q_phase);
+
+ /* combine amount & phase */
+ QuatMul(pa->state.rot,rot,q_phase);
+ }
+
+ /* -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);
+ }
+ }
+
+ pa->dietime=pa->time+pa->lifetime;
+
+ if(pa->time >= cfra)
+ pa->alive=PARS_UNBORN;
+
+ pa->state.time=cfra;
+
+ pa->stick_ob=0;
+ pa->flag&=~PARS_STICKY;
+}
+static void reset_all_particles(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float dtime, float cfra, int from)
+{
+ ParticleData *pa;
+ int p, totpart=psys->totpart;
+ float *vg_vel=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL);
+ float *vg_tan=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN);
+ float *vg_rot=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT);
+
+ //if (psys->part->from == PART_FROM_FACE)
+ // psys_calc_dmfaces(ob, psmd->dm, psys);
+
+ for(p=from, pa=psys->particles+from; p<totpart; p++, pa++)
+ reset_particle(pa, psys, psmd, ob, dtime, cfra, vg_vel, vg_tan, vg_rot);
+
+ if(vg_vel)
+ MEM_freeN(vg_vel);
+}
+/************************************************/
+/* Keyed particles */
+/************************************************/
+/* a bit of an unintuitive function :) counts objects in a keyed chain and returns 1 if some of them were selected (used in drawing) */
+int psys_count_keyed_targets(Object *ob, ParticleSystem *psys)
+{
+ ParticleSystem *kpsys=psys,*tpsys;
+ ParticleSettings *tpart;
+ Object *kob=ob,*tob;
+ int select=ob->flag&SELECT;
+ short totkeyed=0;
+ Base *base;
+
+ ListBase lb;
+ lb.first=lb.last=0;
+
+ tob=psys->keyed_ob;
+ while(tob){
+ if((tpsys=BLI_findlink(&tob->particlesystem,kpsys->keyed_psys-1))){
+ tpart=tpsys->part;
+
+ if(tpart->phystype==PART_PHYS_KEYED){
+ if(lb.first){
+ for(base=lb.first;base;base=base->next){
+ if(tob==base->object){
+ fprintf(stderr,"Error: loop in keyed chain!\n");
+ BLI_freelistN(&lb);
+ return select;
+ }
+ }
+ }
+ base=MEM_callocN(sizeof(Base), "keyed base");
+ base->object=tob;
+ BLI_addtail(&lb,base);
+
+ if(tob->flag&SELECT)
+ select++;
+ kob=tob;
+ kpsys=tpsys;
+ tob=tpsys->keyed_ob;
+ totkeyed++;
+ }
+ else{
+ tob=0;
+ totkeyed++;
+ }
+ }
+ else
+ tob=0;
+ }
+ psys->totkeyed=totkeyed;
+ BLI_freelistN(&lb);
+ return select;
+}
+void set_keyed_keys(Object *ob, ParticleSystem *psys)
+{
+ Object *kob = ob;
+ ParticleSystem *kpsys = psys;
+ ParticleData *pa;
+ ParticleKey state;
+ int totpart = psys->totpart, i, k, totkeys = psys->totkeyed + 1;
+ float prevtime, nexttime, keyedtime;
+
+ /* no proper targets so let's clear and bail out */
+ if(psys->totkeyed==0){
+ free_keyed_keys(psys);
+ psys->flag &= ~PSYS_KEYED;
+ return;
+ }
+
+ if(totpart && psys->particles->totkey != totkeys){
+ free_keyed_keys(psys);
+
+ psys->particles->keys = MEM_callocN(psys->totpart * totkeys * sizeof(ParticleKey),"Keyed keys");
+
+ psys->particles->totkey = totkeys;
+
+ for(i=1, pa=psys->particles+1; i<totpart; i++,pa++){
+ pa->keys = (pa-1)->keys + totkeys;
+ pa->totkey = totkeys;
+ }
+ }
+
+ psys->flag &= ~PSYS_KEYED;
+ state.time=-1.0;
+
+ for(k=0; k<totkeys; k++){
+ for(i=0,pa=psys->particles; i<totpart; i++, pa++){
+ psys_get_particle_state(kob, kpsys, i%kpsys->totpart, pa->keys + k, 1);
+
+ if(k==0)
+ pa->keys->time = pa->time;
+ else if(k==totkeys-1)
+ (pa->keys + k)->time = pa->time + pa->lifetime;
+ else{
+ if(psys->flag & PSYS_KEYED_TIME){
+ prevtime = (pa->keys + k - 1)->time;
+ nexttime = pa->time + pa->lifetime;
+ keyedtime = kpsys->part->keyed_time;
+ (pa->keys + k)->time = (1.0f - keyedtime) * prevtime + keyedtime * nexttime;
+ }
+ else
+ (pa->keys+k)->time = pa->time + (float)k / (float)(totkeys - 1) * pa->lifetime;
+ }
+ }
+ if(kpsys->keyed_ob){
+ kob = kpsys->keyed_ob;
+ kpsys = BLI_findlink(&kob->particlesystem, kpsys->keyed_psys - 1);
+ }
+ }
+
+ psys->flag |= PSYS_KEYED;
+}
+/************************************************/
+/* Reactors */
+/************************************************/
+static void push_reaction(Object* ob, ParticleSystem *psys, int pa_num, int event, ParticleKey *state)
+{
+ Object *rob;
+ ParticleSystem *rpsys;
+ ParticleSettings *rpart;
+ ParticleData *pa;
+ ListBase *lb=&psys->effectors;
+ ParticleEffectorCache *ec;
+ ParticleReactEvent *re;
+
+ if(lb->first) for(ec = lb->first; ec; ec= ec->next){
+ if(ec->type & PSYS_EC_REACTOR){
+ /* all validity checks already done in add_to_effectors */
+ rob=ec->ob;
+ rpsys=BLI_findlink(&rob->particlesystem,ec->psys_nbr);
+ rpart=rpsys->part;
+ if(rpsys->part->reactevent==event){
+ pa=psys->particles+pa_num;
+ re= MEM_callocN(sizeof(ParticleReactEvent), "react event");
+ re->event=event;
+ re->pa_num = pa_num;
+ re->ob = ob;
+ re->psys = psys;
+ re->size = pa->size;
+ copy_particle_key(&re->state,state,1);
+
+ switch(event){
+ case PART_EVENT_DEATH:
+ re->time=pa->dietime;
+ break;
+ case PART_EVENT_COLLIDE:
+ re->time=state->time;
+ break;
+ case PART_EVENT_NEAR:
+ re->time=state->time;
+ break;
+ }
+
+ BLI_addtail(&rpsys->reactevents, re);
+ }
+ }
+ }
+}
+static void react_to_events(ParticleSystem *psys, int pa_num)
+{
+ ParticleSettings *part=psys->part;
+ ParticleData *pa=psys->particles+pa_num;
+ ParticleReactEvent *re=psys->reactevents.first;
+ int birth=0;
+ float dist=0.0f;
+
+ for(re=psys->reactevents.first; re; re=re->next){
+ birth=0;
+ if(part->from==PART_FROM_PARTICLE){
+ if(pa->num==re->pa_num){
+ if(re->event==PART_EVENT_NEAR){
+ ParticleData *tpa = re->psys->particles+re->pa_num;
+ float pa_time=tpa->time + pa->foffset*tpa->lifetime;
+ if(re->time > pa_time){
+ pa->alive=PARS_ALIVE;
+ pa->time=pa_time;
+ pa->dietime=pa->time+pa->lifetime;
+ }
+ }
+ else{
+ if(pa->alive==PARS_UNBORN){
+ pa->alive=PARS_ALIVE;
+ pa->time=re->time;
+ pa->dietime=pa->time+pa->lifetime;
+ }
+ }
+ }
+ }
+ else{
+ dist=VecLenf(pa->state.co, re->state.co);
+ if(dist <= re->size){
+ if(pa->alive==PARS_UNBORN){
+ pa->alive=PARS_ALIVE;
+ pa->time=re->time;
+ pa->dietime=pa->time+pa->lifetime;
+ birth=1;
+ }
+ if(birth || part->flag&PART_REACT_MULTIPLE){
+ float vec[3];
+ VECSUB(vec,pa->state.co, re->state.co);
+ if(birth==0)
+ VecMulf(vec,(float)pow(1.0f-dist/re->size,part->reactshape));
+ VECADDFAC(pa->state.vel,pa->state.vel,vec,part->reactfac);
+ VECADDFAC(pa->state.vel,pa->state.vel,re->state.vel,part->partfac);
+ }
+ if(birth)
+ VecMulf(pa->state.vel,(float)pow(1.0f-dist/re->size,part->reactshape));
+ }
+ }
+ }
+}
+void psys_get_reactor_target(Object *ob, ParticleSystem *psys, Object **target_ob, ParticleSystem **target_psys)
+{
+ Object *tob;
+
+ tob=psys->target_ob;
+ if(tob==0)
+ tob=ob;
+
+ *target_psys=BLI_findlink(&tob->particlesystem,psys->target_psys-1);
+ if(*target_psys)
+ *target_ob=tob;
+ else
+ *target_ob=0;
+}
+/************************************************/
+/* Point Cache */
+/************************************************/
+void clear_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra)
+{
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
+ int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd);
+
+ BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, cfra, stack_index);
+}
+static void write_particles_to_cache(Object *ob, ParticleSystem *psys, int cfra)
+{
+ FILE *fp = NULL;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
+ ParticleData *pa;
+ int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd);
+ int i, totpart = psys->totpart;
+
+ if(totpart == 0) return;
+
+ fp = BKE_ptcache_id_fopen((ID *)ob, 'w', cfra, stack_index);
+ if(!fp) return;
+
+ for(i=0, pa=psys->particles; i<totpart; i++, pa++)
+ fwrite(&pa->state, sizeof(ParticleKey), 1, fp);
+
+ fclose(fp);
+}
+static int get_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra)
+{
+ FILE *fp = NULL;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
+ ParticleData *pa;
+ int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd);
+ int i, totpart = psys->totpart, ret = 1;
+
+ if(totpart == 0) return 0;
+
+ fp = BKE_ptcache_id_fopen((ID *)ob, 'r', cfra, stack_index);
+ if(!fp)
+ ret = 0;
+ else {
+ for(i=0, pa=psys->particles; i<totpart; i++, pa++)
+ if((fread(&pa->state, sizeof(ParticleKey), 1, fp)) != 1) {
+ ret = 0;
+ break;
+ }
+
+ fclose(fp);
+ }
+
+ return ret;
+}
+/************************************************/
+/* Effectors */
+/************************************************/
+static float effector_falloff(PartDeflect *pd, float *eff_velocity, float *vec_to_part)
+{
+ float eff_dir[3], temp[3];
+ float falloff=1.0, fac, r_fac;
+
+ VecCopyf(eff_dir,eff_velocity);
+ Normalize(eff_dir);
+
+ if(pd->flag & PFIELD_POSZ && Inpf(eff_dir,vec_to_part)<0.0f)
+ falloff=0.0f;
+ else switch(pd->falloff){
+ case PFIELD_FALL_SPHERE:
+ fac=VecLength(vec_to_part);
+ if(pd->flag&PFIELD_USEMAX && fac>pd->maxdist){
+ falloff=0.0f;
+ break;
+ }
+
+ if(pd->flag & PFIELD_USEMIN){
+ if(fac>pd->mindist)
+ fac+=1.0f-pd->mindist;
+ else
+ fac=1.0f;
+ }
+ else if(fac<0.001)
+ fac=0.001f;
+
+ falloff=1.0f/(float)pow((double)fac,(double)pd->f_power);
+ break;
+
+ case PFIELD_FALL_TUBE:
+ fac=Inpf(vec_to_part,eff_dir);
+ if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){
+ falloff=0.0f;
+ break;
+ }
+
+ VECADDFAC(temp,vec_to_part,eff_dir,-fac);
+ r_fac=VecLength(temp);
+ if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){
+ falloff=0.0f;
+ break;
+ }
+
+ fac=ABS(fac);
+
+
+ if(pd->flag & PFIELD_USEMIN){
+ if(fac>pd->mindist)
+ fac+=1.0f-pd->mindist;
+ else
+ fac=1.0f;
+ }
+ else if(fac<0.001)
+ fac=0.001f;
+
+ if(pd->flag & PFIELD_USEMINR){
+ if(r_fac>pd->minrad)
+ r_fac+=1.0f-pd->minrad;
+ else
+ r_fac=1.0f;
+ }
+ else if(r_fac<0.001)
+ r_fac=0.001f;
+
+ falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r));
+
+ break;
+ case PFIELD_FALL_CONE:
+ fac=Inpf(vec_to_part,eff_dir);
+ if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){
+ falloff=0.0f;
+ break;
+ }
+ r_fac=saacos(fac/VecLength(vec_to_part))*180.0f/(float)M_PI;
+ if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){
+ falloff=0.0f;
+ break;
+ }
+
+ if(pd->flag & PFIELD_USEMIN){
+ if(fac>pd->mindist)
+ fac+=1.0f-pd->mindist;
+ else
+ fac=1.0f;
+ }
+ else if(fac<0.001)
+ fac=0.001f;
+
+ if(pd->flag & PFIELD_USEMINR){
+ if(r_fac>pd->minrad)
+ r_fac+=1.0f-pd->minrad;
+ else
+ r_fac=1.0f;
+ }
+ else if(r_fac<0.001)
+ r_fac=0.001f;
+
+ falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r));
+
+ break;
+// case PFIELD_FALL_INSIDE:
+ //for(i=0; i<totface; i++,mface++){
+ // VECCOPY(v1,mvert[mface->v1].co);
+ // VECCOPY(v2,mvert[mface->v2].co);
+ // VECCOPY(v3,mvert[mface->v3].co);
+
+ // if(AxialLineIntersectsTriangle(a,co1, co2, v2, v3, v1, &lambda)){
+ // if(from==PART_FROM_FACE)
+ // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ // else /* store number of intersections */
+ // (pa+(int)(lambda*size[a])*a0mul)->loop++;
+ // }
+ //
+ // if(mface->v4){
+ // VECCOPY(v4,mvert[mface->v4].co);
+
+ // if(AxialLineIntersectsTriangle(a,co1, co2, v4, v1, v3, &lambda)){
+ // if(from==PART_FROM_FACE)
+ // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ // else
+ // (pa+(int)(lambda*size[a])*a0mul)->loop++;
+ // }
+ // }
+ //}
+
+// break;
+ }
+
+ return falloff;
+}
+static void do_physical_effector(short type, float force_val, float distance, float falloff, float size, float damp,
+ float *eff_velocity, float *vec_to_part, float *velocity, float *field, int planar)
+{
+ float mag_vec[3]={0,0,0};
+ float temp[3], temp2[3];
+ float eff_vel[3];
+
+ VecCopyf(eff_vel,eff_velocity);
+ Normalize(eff_vel);
+
+ switch(type){
+ case PFIELD_WIND:
+ VECCOPY(mag_vec,eff_vel);
+
+ VecMulf(mag_vec,force_val*falloff);
+ VecAddf(field,field,mag_vec);
+ break;
+
+ case PFIELD_FORCE:
+ if(planar)
+ Projf(mag_vec,vec_to_part,eff_vel);
+ else
+ VecCopyf(mag_vec,vec_to_part);
+
+ VecMulf(mag_vec,force_val*falloff);
+ VecAddf(field,field,mag_vec);
+ break;
+
+ case PFIELD_VORTEX:
+ Crossf(mag_vec,eff_vel,vec_to_part);
+ Normalize(mag_vec);
+
+ VecMulf(mag_vec,force_val*distance*falloff);
+ VecAddf(field,field,mag_vec);
+
+ break;
+ case PFIELD_MAGNET:
+ if(planar)
+ VecCopyf(temp,eff_vel);
+ else
+ /* magnetic field of a moving charge */
+ Crossf(temp,eff_vel,vec_to_part);
+
+ Crossf(temp2,velocity,temp);
+ VecAddf(mag_vec,mag_vec,temp2);
+
+ VecMulf(mag_vec,force_val*falloff);
+ VecAddf(field,field,mag_vec);
+ break;
+ case PFIELD_HARMONIC:
+ if(planar)
+ Projf(mag_vec,vec_to_part,eff_vel);
+ else
+ VecCopyf(mag_vec,vec_to_part);
+
+ VecMulf(mag_vec,force_val*falloff);
+ VecSubf(field,field,mag_vec);
+
+ VecCopyf(mag_vec,velocity);
+ /* 1.9 is an experimental value to get critical damping at damp=1.0 */
+ VecMulf(mag_vec,damp*1.9f*(float)sqrt(force_val));
+ VecSubf(field,field,mag_vec);
+ break;
+ case PFIELD_NUCLEAR:
+ /*pow here is root of cosine expression below*/
+ //rad=(float)pow(2.0,-1.0/power)*distance/size;
+ //VECCOPY(mag_vec,vec_to_part);
+ //Normalize(mag_vec);
+ //VecMulf(mag_vec,(float)cos(3.0*M_PI/2.0*(1.0-1.0/(pow(rad,power)+1.0)))/(rad+0.2f));
+ //VECADDFAC(field,field,mag_vec,force_val);
+ break;
+ }
+}
+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];
+ float tex_co[3], strength, mag_vec[3];
+ int i;
+
+ if(tex==0) return;
+
+ for(i=0; i<4; i++)
+ result[i].nor=0;
+
+ strength= force_val*falloff;///(float)pow((double)distance,(double)power);
+
+ VECCOPY(tex_co,pa_co);
+
+ if(is_2d){
+ float fac=-Inpf(tex_co,obmat[2]);
+ VECADDFAC(tex_co,tex_co,obmat[2],fac);
+ }
+
+ if(object){
+ VecSubf(tex_co,tex_co,obmat[3]);
+ Mat4Mul3Vecfl(obmat,tex_co);
+ }
+
+ multitex_ext(tex, tex_co, NULL,NULL, 1, result);
+
+ if(mode==PFIELD_TEX_RGB){
+ mag_vec[0]= (0.5f-result->tr)*strength;
+ mag_vec[1]= (0.5f-result->tg)*strength;
+ mag_vec[2]= (0.5f-result->tb)*strength;
+ }
+ else{
+ strength/=nabla;
+
+ tex_co[0]+= nabla;
+ multitex_ext(tex, tex_co, NULL,NULL, 1, result+1);
+
+ tex_co[0]-= nabla;
+ tex_co[1]+= nabla;
+ multitex_ext(tex, tex_co, NULL,NULL, 1, result+2);
+
+ tex_co[1]-= nabla;
+ tex_co[2]+= nabla;
+ multitex_ext(tex, tex_co, NULL,NULL, 1, result+3);
+
+ if(mode==PFIELD_TEX_GRAD){
+ mag_vec[0]= (result[0].tin-result[1].tin)*strength;
+ mag_vec[1]= (result[0].tin-result[2].tin)*strength;
+ mag_vec[2]= (result[0].tin-result[3].tin)*strength;
+ }
+ else{ /*PFIELD_TEX_CURL*/
+ float dbdy,dgdz,drdz,dbdx,dgdx,drdy;
+
+ dbdy= result[2].tb-result[0].tb;
+ dgdz= result[3].tg-result[0].tg;
+ drdz= result[3].tr-result[0].tr;
+ dbdx= result[1].tb-result[0].tb;
+ dgdx= result[1].tg-result[0].tg;
+ drdy= result[2].tr-result[0].tr;
+
+ mag_vec[0]=(dbdy-dgdz)*strength;
+ mag_vec[1]=(drdz-dbdx)*strength;
+ mag_vec[2]=(dgdx-drdy)*strength;
+ }
+ }
+
+ if(is_2d){
+ float fac=-Inpf(mag_vec,obmat[2]);
+ VECADDFAC(mag_vec,mag_vec,obmat[2],fac);
+ }
+
+ VecAddf(field,field,mag_vec);
+}
+static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSystem *psys)
+{
+ ParticleEffectorCache *ec;
+ PartDeflect *pd= ob->pd;
+ short type=0,i;
+
+ if(pd && ob != obsrc){
+ if(pd->forcefield == PFIELD_GUIDE) {
+ if(ob->type==OB_CURVE) {
+ Curve *cu= ob->data;
+ if(cu->flag & CU_PATH) {
+ if(cu->path==NULL || cu->path->data==NULL)
+ makeDispListCurveTypes(ob, 0);
+ if(cu->path && cu->path->data) {
+ type |= PSYS_EC_EFFECTOR;
+ }
+ }
+ }
+ }
+ else if(pd->forcefield)
+ type |= PSYS_EC_EFFECTOR;
+ }
+
+ if(pd && pd->deflect)
+ type |= PSYS_EC_DEFLECT;
+
+ if(type){
+ ec= MEM_callocN(sizeof(ParticleEffectorCache), "effector cache");
+ ec->ob= ob;
+ ec->type=type;
+ ec->distances=0;
+ ec->locations=0;
+ BLI_addtail(lb, ec);
+ }
+
+ type=0;
+
+ /* add particles as different effectors */
+ if(ob->particlesystem.first){
+ ParticleSystem *epsys=ob->particlesystem.first;
+ ParticleSettings *epart=0;
+ Object *tob;
+
+ for(i=0; epsys; epsys=epsys->next,i++){
+ type=0;
+ if(epsys!=psys){
+ epart=epsys->part;
+
+ if(epsys->part->pd && epsys->part->pd->forcefield)
+ type=PSYS_EC_PARTICLE;
+
+ if(epart->type==PART_REACTOR) {
+ tob=epsys->target_ob;
+ if(tob==0)
+ tob=ob;
+ if(BLI_findlink(&tob->particlesystem,epsys->target_psys-1)==psys)
+ type|=PSYS_EC_REACTOR;
+ }
+
+ if(type){
+ ec= MEM_callocN(sizeof(ParticleEffectorCache), "effector cache");
+ ec->ob= ob;
+ ec->type=type;
+ ec->psys_nbr=i;
+ BLI_addtail(lb, ec);
+ }
+ }
+ }
+
+ }
+}
+void psys_init_effectors(Object *obsrc, Group *group, ParticleSystem *psys)
+{
+ ListBase *listb=&psys->effectors;
+ Base *base;
+ unsigned int layer= obsrc->lay;
+
+ listb->first=listb->last=0;
+
+ if(group) {
+ GroupObject *go;
+
+ for(go= group->gobject.first; go; go= go->next) {
+ if( (go->ob->lay & layer) && (go->ob->pd || go->ob->particlesystem.first)) {
+ add_to_effectors(listb, go->ob, obsrc, psys);
+ }
+ }
+ }
+ else {
+ for(base = G.scene->base.first; base; base= base->next) {
+ if( (base->lay & layer) && (base->object->pd || base->object->particlesystem.first)) {
+ add_to_effectors(listb, base->object, obsrc, psys);
+ }
+ }
+ }
+}
+
+void psys_end_effectors(ParticleSystem *psys)
+{
+ ListBase *lb=&psys->effectors;
+ if(lb->first) {
+ ParticleEffectorCache *ec;
+ for(ec= lb->first; ec; ec= ec->next){
+ if(ec->distances)
+ MEM_freeN(ec->distances);
+
+ if(ec->locations)
+ MEM_freeN(ec->locations);
+
+ if(ec->face_minmax)
+ MEM_freeN(ec->face_minmax);
+
+ if(ec->vert_cos)
+ MEM_freeN(ec->vert_cos);
+
+ if(ec->tree)
+ BLI_kdtree_free(ec->tree);
+ }
+
+ BLI_freelistN(lb);
+ }
+}
+
+static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd)
+{
+ ListBase *lb=&psys->effectors;
+ ParticleEffectorCache *ec;
+ ParticleSettings *part=psys->part;
+ ParticleData *pa;
+ float vec2[3],loc[3],*co=0;
+ int p,totpart,totvert;
+
+ for(ec= lb->first; ec; ec= ec->next) {
+ PartDeflect *pd= ec->ob->pd;
+
+ if(ec->type==PSYS_EC_EFFECTOR && pd->forcefield==PFIELD_GUIDE && ec->ob->type==OB_CURVE
+ && part->phystype!=PART_PHYS_BOIDS) {
+ float vec[4];
+
+ where_on_path(ec->ob, 0.0, vec, vec2);
+
+ Mat4MulVecfl(ec->ob->obmat,vec);
+ Mat4Mul3Vecfl(ec->ob->obmat,vec2);
+
+ QUATCOPY(ec->firstloc,vec);
+ VECCOPY(ec->firstdir,vec2);
+
+ totpart=psys->totpart;
+
+ if(totpart){
+ ec->distances=MEM_callocN(totpart*sizeof(float),"particle distances");
+ ec->locations=MEM_callocN(totpart*3*sizeof(float),"particle locations");
+
+ for(p=0,pa=psys->particles; p<totpart; p++, pa++){
+ psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0);
+ Mat4MulVecfl(ob->obmat,loc);
+ ec->distances[p]=VecLenf(loc,vec);
+ VECSUB(loc,loc,vec);
+ VECCOPY(ec->locations+3*p,loc);
+ }
+ }
+ }
+ else if(ec->type==PSYS_EC_DEFLECT){
+ DerivedMesh *dm;
+ MFace *mface=0;
+ MVert *mvert=0;
+ int i, totface;
+ float v1[3],v2[3],v3[3],v4[4], *min, *max;
+
+ if(ob==ec->ob)
+ dm=psmd->dm;
+ else{
+ psys_disable_all(ec->ob);
+
+ dm=mesh_get_derived_final(ec->ob,0);
+
+ psys_enable_all(ec->ob);
+ }
+
+ if(dm){
+ totvert=dm->getNumVerts(dm);
+ totface=dm->getNumFaces(dm);
+ mface=dm->getFaceDataArray(dm,CD_MFACE);
+ mvert=dm->getVertDataArray(dm,CD_MVERT);
+
+ /* Decide which is faster to calculate by the amount of*/
+ /* matrice multiplications needed to convert spaces. */
+ /* With size deflect we have to convert allways because */
+ /* the object can be scaled nonuniformly (sphere->ellipsoid). */
+ if(totvert<2*psys->totpart || part->flag & PART_SIZE_DEFL){
+ co=ec->vert_cos=MEM_callocN(sizeof(float)*3*totvert,"Particle deflection vert cos");
+ /* convert vert coordinates to global (particle) coordinates */
+ for(i=0; i<totvert; i++, co+=3){
+ VECCOPY(co,mvert[i].co);
+ Mat4MulVecfl(ec->ob->obmat,co);
+ }
+ co=ec->vert_cos;
+ }
+ else
+ ec->vert_cos=0;
+
+ INIT_MINMAX(ec->ob_minmax,ec->ob_minmax+3);
+
+ min=ec->face_minmax=MEM_callocN(sizeof(float)*6*totface,"Particle deflection face minmax");
+ max=min+3;
+
+ for(i=0; i<totface; i++,mface++,min+=6,max+=6){
+ if(co){
+ VECCOPY(v1,co+3*mface->v1);
+ VECCOPY(v2,co+3*mface->v2);
+ VECCOPY(v3,co+3*mface->v3);
+ }
+ else{
+ VECCOPY(v1,mvert[mface->v1].co);
+ VECCOPY(v2,mvert[mface->v2].co);
+ VECCOPY(v3,mvert[mface->v3].co);
+ }
+ INIT_MINMAX(min,max);
+ DO_MINMAX(v1,min,max);
+ DO_MINMAX(v2,min,max);
+ DO_MINMAX(v3,min,max);
+
+ if(mface->v4){
+ if(co){
+ VECCOPY(v4,co+3*mface->v4);
+ }
+ else{
+ VECCOPY(v4,mvert[mface->v4].co);
+ }
+ DO_MINMAX(v4,min,max);
+ }
+
+ DO_MINMAX(min,ec->ob_minmax,ec->ob_minmax+3);
+ DO_MINMAX(max,ec->ob_minmax,ec->ob_minmax+3);
+ }
+ }
+ else
+ ec->face_minmax=0;
+ }
+ else if(ec->type==PSYS_EC_PARTICLE){
+ if(psys->part->phystype==PART_PHYS_BOIDS){
+ Object *eob = ec->ob;
+ ParticleSystem *epsys;
+ ParticleSettings *epart;
+ ParticleData *epa;
+ ParticleKey state;
+ PartDeflect *pd;
+ int totepart, p;
+ epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr);
+ epart= epsys->part;
+ pd= epart->pd;
+ totepart= epsys->totpart;
+ if(pd->forcefield==PFIELD_FORCE && totepart){
+ KDTree *tree;
+
+ tree=BLI_kdtree_new(totepart);
+ ec->tree=tree;
+
+ for(p=0, epa=epsys->particles; p<totepart; p++,epa++)
+ if(epa->alive==PARS_ALIVE && psys_get_particle_state(eob,epsys,p,&state,0))
+ BLI_kdtree_insert(tree, p, state.co, NULL);
+
+ BLI_kdtree_balance(tree);
+ }
+ }
+ }
+ }
+}
+
+
+/* calculate forces that all effectors apply to a particle*/
+static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object *ob, ParticleSystem *psys, float *force_field, float *vel,float framestep, float cfra)
+{
+ Object *eob;
+ ParticleSystem *epsys;
+ ParticleSettings *epart;
+ ParticleData *epa;
+ ParticleKey estate;
+ PartDeflect *pd;
+ ListBase *lb=&psys->effectors;
+ ParticleEffectorCache *ec;
+ float distance, vec_to_part[3];
+ float falloff;
+ int p;
+
+ /* check all effector objects for interaction */
+ if(lb->first){
+ for(ec = lb->first; ec; ec= ec->next){
+ eob= ec->ob;
+ if(ec->type & PSYS_EC_EFFECTOR){
+ pd=eob->pd;
+ if(psys->part->type!=PART_HAIR && psys->part->integrator)
+ where_is_object_time(eob,cfra);
+ /* Get IPO force strength and fall off values here */
+ //if (has_ipo_code(eob->ipo, OB_PD_FSTR))
+ // force_val = IPO_GetFloatValue(eob->ipo, OB_PD_FSTR, cfra);
+ //else
+ // force_val = pd->f_strength;
+
+ //if (has_ipo_code(eob->ipo, OB_PD_FFALL))
+ // ffall_val = IPO_GetFloatValue(eob->ipo, OB_PD_FFALL, cfra);
+ //else
+ // ffall_val = pd->f_power;
+
+ //if (has_ipo_code(eob->ipo, OB_PD_FMAXD))
+ // maxdist = IPO_GetFloatValue(eob->ipo, OB_PD_FMAXD, cfra);
+ //else
+ // maxdist = pd->maxdist;
+
+ /* use center of object for distance calculus */
+ //obloc= eob->obmat[3];
+ VecSubf(vec_to_part, state->co, eob->obmat[3]);
+ distance = VecLength(vec_to_part);
+
+ falloff=effector_falloff(pd,eob->obmat[2],vec_to_part);
+
+ 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, state->co, eob->obmat,
+ pd->f_strength, falloff, force_field);
+ else
+ do_physical_effector(pd->forcefield,pd->f_strength,distance,
+ falloff,pd->f_dist,pd->f_damp,eob->obmat[2],vec_to_part,
+ pa->state.vel,force_field,pd->flag&PFIELD_PLANAR);
+ }
+ if(ec->type & PSYS_EC_PARTICLE){
+ int totepart;
+ epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr);
+ epart= epsys->part;
+ pd= epart->pd;
+ totepart= epsys->totpart;
+
+ if(pd->forcefield==PFIELD_HARMONIC){
+ /* every particle is mapped to only one harmonic effector particle */
+ p= pa_no%epsys->totpart;
+ totepart= p+1;
+ }
+ else{
+ p=0;
+ }
+
+ epsys->lattice=psys_get_lattice(ob,psys);
+
+ for(; p<totepart; p++){
+ epa = epsys->particles + p;
+ estate.time=-1.0;
+ if(psys_get_particle_state(eob,epsys,p,&estate,0)){
+ VECSUB(vec_to_part, state->co, estate.co);
+ distance = VecLength(vec_to_part);
+
+ //if(pd->forcefield==PFIELD_HARMONIC){
+ // //if(cfra < epa->time + radius){ /* radius is fade-in in ui */
+ // // eforce*=(cfra-epa->time)/radius;
+ // //}
+ //}
+ //else{
+ // /* Limit minimum distance to effector particle so that */
+ // /* the force is not too big */
+ // if (distance < 0.001) distance = 0.001f;
+ //}
+
+ falloff=effector_falloff(pd,estate.vel,vec_to_part);
+
+ if(falloff<=0.0f)
+ ; /* don't do anything */
+ else
+ do_physical_effector(pd->forcefield,pd->f_strength,distance,
+ falloff,epart->size,pd->f_damp,estate.vel,vec_to_part,
+ state->vel,force_field,0);
+ }
+ else if(pd->forcefield==PFIELD_HARMONIC && cfra-framestep <= epa->dietime && cfra>epa->dietime){
+ /* first step after key release */
+ psys_get_particle_state(eob,epsys,p,&estate,1);
+ VECADD(vel,vel,estate.vel);
+ /* TODO: add rotation handling here too */
+ }
+ }
+
+ if(epsys->lattice){
+ end_latt_deform();
+ epsys->lattice=0;
+ }
+ }
+ }
+ }
+}
+
+/************************************************/
+/* Newtonian physics */
+/************************************************/
+/* gathers all forces that effect particles and calculates a new state for the particle */
+static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, float timestep, float dfra, float cfra, ParticleKey *state)
+{
+ ParticleKey states[5], tkey;
+ float force[3],tvel[3],dx[4][3],dv[4][3];
+ float dtime=dfra*timestep, time, pa_mass=part->mass, fac, fra=psys->cfra;
+ int i, steps=1;
+
+ /* maintain angular velocity */
+ VECCOPY(state->ave,pa->state.ave);
+
+ if(part->flag & PART_SIZEMASS)
+ pa_mass*=pa->size;
+
+ switch(part->integrator){
+ case PART_INT_EULER:
+ steps=1;
+ break;
+ case PART_INT_MIDPOINT:
+ steps=2;
+ break;
+ case PART_INT_RK4:
+ steps=4;
+ break;
+ }
+
+ copy_particle_key(states,&pa->state,1);
+
+ for(i=0; i<steps; i++){
+ force[0]=force[1]=force[2]=0.0;
+ tvel[0]=tvel[1]=tvel[2]=0.0;
+ /* add effectors */
+ do_effectors(pa_no,pa,states+i,ob,psys,force,tvel,dfra,fra);
+
+ /* calculate air-particle interaction */
+ if(part->dragfac!=0.0f){
+ fac=-part->dragfac*pa->size*pa->size*VecLength(states[i].vel);
+ VECADDFAC(force,force,states[i].vel,fac);
+ }
+
+ /* brownian force */
+ if(part->brownfac!=0.0){
+ force[0]+=(BLI_frand()-0.5f)*part->brownfac;
+ force[1]+=(BLI_frand()-0.5f)*part->brownfac;
+ force[2]+=(BLI_frand()-0.5f)*part->brownfac;
+ }
+
+ /* force to acceleration*/
+ VecMulf(force,1.0f/pa_mass);
+
+ /* add global acceleration (gravitation) */
+ VECADD(force,force,part->acc);
+
+ //VecMulf(force,dtime);
+
+ /* calculate next state */
+ VECADD(states[i].vel,states[i].vel,tvel);
+
+ //VecMulf(force,0.5f*dt);
+ switch(part->integrator){
+ case PART_INT_EULER:
+ VECADDFAC(state->co,states->co,states->vel,dtime);
+ VECADDFAC(state->vel,states->vel,force,dtime);
+ break;
+ case PART_INT_MIDPOINT:
+ if(i==0){
+ VECADDFAC(states[1].co,states->co,states->vel,dtime*0.5f);
+ VECADDFAC(states[1].vel,states->vel,force,dtime*0.5f);
+ fra=psys->cfra+0.5f*dfra;
+ }
+ else{
+ VECADDFAC(state->co,states->co,states[1].vel,dtime);
+ VECADDFAC(state->vel,states->vel,force,dtime);
+ }
+ break;
+ case PART_INT_RK4:
+ switch(i){
+ case 0:
+ VECCOPY(dx[0],states->vel);
+ VecMulf(dx[0],dtime);
+ VECCOPY(dv[0],force);
+ VecMulf(dv[0],dtime);
+
+ VECADDFAC(states[1].co,states->co,dx[0],0.5f);
+ VECADDFAC(states[1].vel,states->vel,dv[0],0.5f);
+ fra=psys->cfra+0.5f*dfra;
+ break;
+ case 1:
+ VECADDFAC(dx[1],states->vel,dv[0],0.5f);
+ VecMulf(dx[1],dtime);
+ VECCOPY(dv[1],force);
+ VecMulf(dv[1],dtime);
+
+ VECADDFAC(states[2].co,states->co,dx[1],0.5f);
+ VECADDFAC(states[2].vel,states->vel,dv[1],0.5f);
+ break;
+ case 2:
+ VECADDFAC(dx[2],states->vel,dv[1],0.5f);
+ VecMulf(dx[2],dtime);
+ VECCOPY(dv[2],force);
+ VecMulf(dv[2],dtime);
+
+ VECADD(states[3].co,states->co,dx[2]);
+ VECADD(states[3].vel,states->vel,dv[2]);
+ fra=cfra;
+ break;
+ case 3:
+ VECADD(dx[3],states->vel,dv[2]);
+ VecMulf(dx[3],dtime);
+ VECCOPY(dv[3],force);
+ VecMulf(dv[3],dtime);
+
+ VECADDFAC(state->co,states->co,dx[0],1.0f/6.0f);
+ VECADDFAC(state->co,state->co,dx[1],1.0f/3.0f);
+ VECADDFAC(state->co,state->co,dx[2],1.0f/3.0f);
+ VECADDFAC(state->co,state->co,dx[3],1.0f/6.0f);
+
+ VECADDFAC(state->vel,states->vel,dv[0],1.0f/6.0f);
+ VECADDFAC(state->vel,state->vel,dv[1],1.0f/3.0f);
+ VECADDFAC(state->vel,state->vel,dv[2],1.0f/3.0f);
+ VECADDFAC(state->vel,state->vel,dv[3],1.0f/6.0f);
+ }
+ break;
+ }
+ //VECADD(states[i+1].co,states[i+1].co,force);
+ }
+
+ /* damp affects final velocity */
+ if(part->dampfac!=0.0)
+ VecMulf(state->vel,1.0f-part->dampfac);
+
+ /* finally we do guides */
+ time=(cfra-pa->time)/pa->lifetime;
+ CLAMP(time,0.0,1.0);
+
+ VECCOPY(tkey.co,state->co);
+ VECCOPY(tkey.vel,state->vel);
+ tkey.time=state->time;
+ if(do_guide(&tkey,pa_no,time,&psys->effectors)){
+ VECCOPY(state->co,tkey.co);
+ /* guides don't produce valid velocity */
+ VECSUB(state->vel,tkey.co,pa->state.co);
+ VecMulf(state->vel,1.0f/dtime);
+ state->time=tkey.time;
+ }
+}
+static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra, float timestep, ParticleKey *state)
+{
+ float rotfac, rot1[4], rot2[4]={1.0,0.0,0.0,0.0}, dtime=dfra*timestep;
+
+ if((part->flag & PART_ROT_DYN)==0){
+ if(ELEM(part->avemode,PART_AVE_SPIN,PART_AVE_VEL)){
+ float angle;
+ float len1 = VecLength(pa->state.vel);
+ float len2 = VecLength(state->vel);
+
+ if(len1==0.0f || len2==0.0f)
+ state->ave[0]=state->ave[1]=state->ave[2]=0.0f;
+ else{
+ Crossf(state->ave,pa->state.vel,state->vel);
+ Normalize(state->ave);
+ angle=Inpf(pa->state.vel,state->vel)/(len1*len2);
+ VecMulf(state->ave,saacos(angle)/dtime);
+ }
+ }
+
+ if(part->avemode == PART_AVE_SPIN)
+ VecRotToQuat(state->vel,dtime*part->avefac,rot2);
+ }
+
+ rotfac=VecLength(state->ave);
+ if(rotfac==0.0){ /* QuatOne (in VecRotToQuat) doesn't give unit quat [1,0,0,0]?? */
+ rot1[0]=1.0;
+ rot1[1]=rot1[2]=rot1[3]=0;
+ }
+ else{
+ VecRotToQuat(state->ave,rotfac*dtime,rot1);
+ }
+ QuatMul(state->rot,rot1,pa->state.rot);
+ QuatMul(state->rot,rot2,state->rot);
+
+ /* keep rotation quat in good health */
+ NormalQuat(state->rot);
+}
+
+/* convert from triangle barycentric weights to quad mean value weights */
+static void intersect_dm_quad_weights(float *v1, float *v2, float *v3, float *v4, float *w)
+{
+ float co[3], vert[4][3];
+
+ VECCOPY(vert[0], v1);
+ VECCOPY(vert[1], v2);
+ VECCOPY(vert[2], v3);
+ VECCOPY(vert[3], v4);
+
+ co[0]= v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2] + v4[0]*w[3];
+ co[1]= v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2] + v4[1]*w[3];
+ co[2]= v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2] + v4[2]*w[3];
+
+ MeanValueWeights(vert, 4, co, w);
+}
+
+/* check intersection with a derivedmesh */
+int psys_intersect_dm(Object *ob, DerivedMesh *dm, float *vert_cos, float *co1, float* co2, float *min_d, int *min_face, float *min_w,
+ float *face_minmax, float *pa_minmax, float radius, float *ipoint)
+{
+ MFace *mface=0;
+ MVert *mvert=0;
+ int i, totface, intersect=0;
+ float cur_d, cur_uv[2], v1[3], v2[3], v3[3], v4[3], min[3], max[3], p_min[3],p_max[3];
+ float cur_ipoint[3];
+
+ if(dm==0){
+ psys_disable_all(ob);
+
+ dm=mesh_get_derived_final(ob,0);
+ if(dm==0)
+ mesh_get_derived_deform(ob,0);
+
+ psys_enable_all(ob);
+
+ if(dm==0)
+ return 0;
+ }
+
+
+
+ if(pa_minmax==0){
+ INIT_MINMAX(p_min,p_max);
+ DO_MINMAX(co1,p_min,p_max);
+ DO_MINMAX(co2,p_min,p_max);
+ }
+ else{
+ VECCOPY(p_min,pa_minmax);
+ VECCOPY(p_max,pa_minmax+3);
+ }
+
+ totface=dm->getNumFaces(dm);
+ mface=dm->getFaceDataArray(dm,CD_MFACE);
+ mvert=dm->getVertDataArray(dm,CD_MVERT);
+
+ /* lets intersect the faces */
+ for(i=0; i<totface; i++,mface++){
+ if(vert_cos){
+ VECCOPY(v1,vert_cos+3*mface->v1);
+ VECCOPY(v2,vert_cos+3*mface->v2);
+ VECCOPY(v3,vert_cos+3*mface->v3);
+ if(mface->v4)
+ VECCOPY(v4,vert_cos+3*mface->v4)
+ }
+ else{
+ VECCOPY(v1,mvert[mface->v1].co);
+ VECCOPY(v2,mvert[mface->v2].co);
+ VECCOPY(v3,mvert[mface->v3].co);
+ if(mface->v4)
+ VECCOPY(v4,mvert[mface->v4].co)
+ }
+
+ if(face_minmax==0){
+ INIT_MINMAX(min,max);
+ DO_MINMAX(v1,min,max);
+ DO_MINMAX(v2,min,max);
+ DO_MINMAX(v3,min,max);
+ if(mface->v4)
+ DO_MINMAX(v4,min,max)
+ if(AabbIntersectAabb(min,max,p_min,p_max)==0)
+ continue;
+ }
+ else{
+ VECCOPY(min, face_minmax+6*i);
+ VECCOPY(max, face_minmax+6*i+3);
+ if(AabbIntersectAabb(min,max,p_min,p_max)==0)
+ continue;
+ }
+
+ if(radius>0.0f){
+ if(SweepingSphereIntersectsTriangleUV(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)){
+ if(cur_d<*min_d){
+ *min_d=cur_d;
+ VECCOPY(ipoint,cur_ipoint);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ if(mface->v4){
+ if(SweepingSphereIntersectsTriangleUV(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)){
+ if(cur_d<*min_d){
+ *min_d=cur_d;
+ VECCOPY(ipoint,cur_ipoint);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ }
+ }
+ else{
+ if(LineIntersectsTriangle(co1, co2, v1, v2, v3, &cur_d, cur_uv)){
+ if(cur_d<*min_d){
+ *min_d=cur_d;
+ min_w[0]= 1.0 - cur_uv[0] - cur_uv[1];
+ min_w[1]= cur_uv[0];
+ min_w[2]= cur_uv[1];
+ min_w[3]= 0.0f;
+ if(mface->v4)
+ intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ if(mface->v4){
+ if(LineIntersectsTriangle(co1, co2, v1, v3, v4, &cur_d, cur_uv)){
+ if(cur_d<*min_d){
+ *min_d=cur_d;
+ min_w[0]= 1.0 - cur_uv[0] - cur_uv[1];
+ min_w[1]= 0.0f;
+ min_w[2]= cur_uv[0];
+ min_w[3]= cur_uv[1];
+ intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ }
+ }
+ }
+ return intersect;
+}
+/* particle - mesh collision code */
+/* in addition to basic point to surface collisions handles friction & damping,*/
+/* angular momentum <-> linear momentum and swept sphere - mesh collisions */
+/* 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(Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float dfra, float cfra, ParticleKey *state, int *pa_die){
+ Object *ob, *min_ob;
+ MFace *mface;
+ MVert *mvert;
+ DerivedMesh *dm;
+ ListBase *lb=&psys->effectors;
+ ParticleEffectorCache *ec;
+ ParticleKey cstate;
+ float imat[4][4];
+ float co1[3],co2[3],def_loc[3],def_nor[3],unit_nor[3],def_tan[3],dvec[3],def_vel[3],dave[3],dvel[3];
+ float pa_minmax[6];
+ float min_w[4], zerovec[3]={0.0,0.0,0.0}, ipoint[3];
+ float min_d,dotprod,damp,frict,o_len,d_len,radius=-1.0f;
+ int min_face=0, intersect=1, through=0;
+ short deflections=0, global=0;
+
+ VECCOPY(def_loc,pa->state.co);
+ VECCOPY(def_vel,pa->state.vel);
+
+ /* 10 iterations to catch multiple deflections */
+ if(lb->first) while(deflections<10){
+ intersect=0;
+ global=0;
+ min_d=20000.0;
+ min_ob=NULL;
+ /* 1. */
+ for(ec=lb->first; ec; ec=ec->next){
+ if(ec->type & PSYS_EC_DEFLECT){
+ ob= ec->ob;
+
+ if(part->type!=PART_HAIR)
+ where_is_object_time(ob,cfra);
+
+ if(ob==pob){
+ dm=psmd->dm;
+ /* particles should not collide with emitter at birth */
+ if(pa->time < cfra && pa->time >= psys->cfra)
+ continue;
+ }
+ else
+ dm=0;
+
+ VECCOPY(co1,def_loc);
+ VECCOPY(co2,state->co);
+
+ if(ec->vert_cos==0){
+ /* convert particle coordinates to object coordinates */
+ Mat4Invert(imat,ob->obmat);
+
+ Mat4MulVecfl(imat,co1);
+ Mat4MulVecfl(imat,co2);
+ }
+
+ INIT_MINMAX(pa_minmax,pa_minmax+3);
+ DO_MINMAX(co1,pa_minmax,pa_minmax+3);
+ DO_MINMAX(co2,pa_minmax,pa_minmax+3);
+ if(part->flag&PART_SIZE_DEFL){
+ pa_minmax[0]-=pa->size;
+ pa_minmax[1]-=pa->size;
+ pa_minmax[2]-=pa->size;
+ pa_minmax[3]+=pa->size;
+ pa_minmax[4]+=pa->size;
+ pa_minmax[5]+=pa->size;
+
+ radius=pa->size;
+ }
+
+ if(ec->face_minmax==0 || AabbIntersectAabb(pa_minmax,pa_minmax+3,ec->ob_minmax,ec->ob_minmax+3))
+ if(psys_intersect_dm(ob,dm,ec->vert_cos,co1,co2,&min_d,&min_face,min_w,
+ ec->face_minmax,pa_minmax,radius,ipoint)){
+ min_ob=ob;
+ if(ec->vert_cos)
+ global=1;
+ else
+ global=0;
+ }
+ }
+ }
+
+ /* 2. */
+ if(min_ob){
+ BLI_srandom((int)cfra+p);
+ ob=min_ob;
+
+ if(ob==pob){
+ dm=psmd->dm;
+ }
+ else{
+ psys_disable_all(ob);
+
+ dm=mesh_get_derived_final(ob,0);
+
+ psys_enable_all(ob);
+ }
+
+ mface=dm->getFaceDataArray(dm,CD_MFACE);
+ mface+=min_face;
+ mvert=dm->getVertDataArray(dm,CD_MVERT);
+
+
+ /* permeability check */
+ if(BLI_frand()<ob->pd->pdef_perm)
+ through=1;
+ else
+ through=0;
+
+ if(through==0 && (part->flag & PART_DIE_ON_COL || ob->pd->flag & PDEFLE_KILL_PART)){
+ pa->dietime = cfra-(1.0f-min_d)*dfra;
+ VecLerpf(def_loc,co1,co2,min_d);
+
+ if(global==0)
+ Mat4MulVecfl(ob->obmat,def_loc);
+
+ VECCOPY(state->co,def_loc);
+ VecLerpf(state->vel,pa->state.vel,state->vel,min_d);
+ QuatInterpol(state->rot,pa->state.rot,state->rot,min_d);
+ VecLerpf(state->ave,pa->state.ave,state->ave,min_d);
+
+ *pa_die=1;
+
+ /* particle is dead so we don't need to calculate further */
+ deflections=10;
+
+ /* store for reactors */
+ copy_particle_key(&cstate,state,0);
+
+ if(part->flag & PART_STICKY){
+ pa->stick_ob=ob;
+ pa->flag |= PARS_STICKY;
+ //stick_particle_to_object(ob,pa,state);
+ }
+ }
+ else{
+ VecLerpf(def_loc,co1,co2,min_d);
+
+ if(radius>0.0f){
+ VECSUB(unit_nor,def_loc,ipoint);
+ }
+ else{
+ /* get deflection point & normal */
+ psys_interpolate_face(mvert,mface,0,min_w,ipoint,unit_nor,0,0);
+ if(global){
+ Mat4Mul3Vecfl(ob->obmat,unit_nor);
+ Mat4MulVecfl(ob->obmat,ipoint);
+ }
+ }
+
+ Normalize(unit_nor);
+
+ VECSUB(dvec,co1,co2);
+ /* scale to remaining length after deflection */
+ VecMulf(dvec,1.0f-min_d);
+
+ /* flip normal to face particle */
+ if(Inpf(unit_nor,dvec)<0.0f)
+ VecMulf(unit_nor,-1.0f);
+
+ /* store for easy velocity calculation */
+ o_len=VecLength(dvec);
+
+ /* project particle movement to normal & create tangent */
+ dotprod=Inpf(dvec,unit_nor);
+ VECCOPY(def_nor,unit_nor);
+ VecMulf(def_nor,dotprod);
+ VECSUB(def_tan,def_nor,dvec);
+
+ damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f);
+
+ /* create location after deflection */
+ VECCOPY(dvec,def_nor);
+ damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f);
+ CLAMP(damp,0.0,1.0);
+ VecMulf(dvec,1.0f-damp);
+ if(through)
+ VecMulf(dvec,-1.0);
+
+ frict=ob->pd->pdef_frict+ob->pd->pdef_rfrict*2.0f*(BLI_frand()-0.5f);
+ CLAMP(frict,0.0,1.0);
+ VECADDFAC(dvec,dvec,def_tan,1.0f-frict);
+
+ /* store for easy velocity calculation */
+ d_len=VecLength(dvec);
+
+ /* just to be sure we don't hit the current face again */
+ if(through){
+ VECADDFAC(ipoint,ipoint,unit_nor,-0.0001f);
+ VECADDFAC(def_loc,def_loc,unit_nor,-0.0001f);
+
+ if(part->flag & PART_ROT_DYN){
+ VECADDFAC(def_tan,def_tan,unit_nor,-0.0001f);
+ VECADDFAC(def_nor,def_nor,unit_nor,-0.0001f);
+ }
+ }
+ else{
+ VECADDFAC(ipoint,ipoint,unit_nor,0.0001f);
+ VECADDFAC(def_loc,def_loc,unit_nor,0.0001f);
+
+ if(part->flag & PART_ROT_DYN){
+ VECADDFAC(def_tan,def_tan,unit_nor,0.0001f);
+ VECADDFAC(def_nor,def_nor,unit_nor,0.0001f);
+ }
+ }
+
+ /* lets get back to global space */
+ if(global==0){
+ Mat4Mul3Vecfl(ob->obmat,dvec);
+ Mat4MulVecfl(ob->obmat,ipoint);
+ Mat4MulVecfl(ob->obmat,def_loc);/* def_loc remains as intersection point for next iteration */
+ }
+
+ /* store for reactors */
+ VECCOPY(cstate.co,ipoint);
+ VecLerpf(cstate.vel,pa->state.vel,state->vel,min_d);
+ QuatInterpol(cstate.rot,pa->state.rot,state->rot,min_d);
+
+ /* slightly unphysical but looks nice enough */
+ if(part->flag & PART_ROT_DYN){
+ if(global==0){
+ Mat4Mul3Vecfl(ob->obmat,def_nor);
+ Mat4Mul3Vecfl(ob->obmat,def_tan);
+ }
+
+ Normalize(def_tan);
+ Normalize(def_nor);
+ VECCOPY(unit_nor,def_nor);
+
+ /* create normal velocity */
+ VecMulf(def_nor,Inpf(pa->state.vel,def_nor));
+
+ /* create tangential velocity */
+ VecMulf(def_tan,Inpf(pa->state.vel,def_tan));
+
+ /* angular velocity change due to tangential velocity */
+ Crossf(dave,unit_nor,def_tan);
+ VecMulf(dave,1.0f/pa->size);
+
+ /* linear velocity change due to angular velocity */
+ VecMulf(unit_nor,pa->size); /* point of impact from particle center */
+ Crossf(dvel,pa->state.ave,unit_nor);
+
+ if(through)
+ VecMulf(def_nor,-1.0);
+
+ VecMulf(def_nor,1.0f-damp);
+ VECSUB(dvel,dvel,def_nor);
+
+ VecMulf(dvel,1.0f-frict);
+ VecMulf(dave,1.0f-frict);
+ }
+
+ if(d_len<0.001 && VecLength(pa->state.vel)<0.001){
+ /* kill speed to stop slipping */
+ VECCOPY(state->vel,zerovec);
+ VECCOPY(state->co,def_loc);
+ if(part->flag & PART_ROT_DYN)
+ VECCOPY(state->ave,zerovec);
+ deflections=10;
+ }
+ else{
+
+ /* apply new coordinates */
+ VECADD(state->co,def_loc,dvec);
+
+ Normalize(dvec);
+
+ /* we have to use original velocity because otherwise we get slipping */
+ /* when forces like gravity balance out damping & friction */
+ VecMulf(dvec,VecLength(pa->state.vel)*(d_len/o_len));
+ VECCOPY(state->vel,dvec);
+
+ if(part->flag & PART_ROT_DYN){
+ VECADD(state->vel,state->vel,dvel);
+ VecMulf(state->vel,0.5);
+ VECADD(state->ave,state->ave,dave);
+ VecMulf(state->ave,0.5);
+ }
+ }
+ }
+ deflections++;
+
+ cstate.time=cfra-(1.0f-min_d)*dfra;
+ //particle_react_to_collision(min_ob,pob,psys,pa,p,&cstate);
+ push_reaction(pob,psys,p,PART_EVENT_COLLIDE,&cstate);
+ }
+ else
+ return;
+ }
+}
+/************************************************/
+/* Boid physics */
+/************************************************/
+static int boid_see_mesh(ListBase *lb, 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(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(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_deform(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,min_w,loc,nor,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, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc, int *pa_die)
+{
+ 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->state.vel);
+ bvf->Mulf(dvec,5.0f);
+ bvf->Addf(dvec,dvec,pa->state.co);
+ if(boid_see_mesh(&psys->effectors,ob,psys,pa->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->state.vel);
+
+ bvf->Subf(dvec,pa->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->state.co);
+
+ distance=Normalize(dvec);
+
+ if(part->flag & PART_DIE_ON_COL && distance < pd->mindist){
+ *pa_die=1;
+ 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){
+ count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2);
+ for(p=0; p<count; p++){
+ state.time=-1.0;
+ if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){
+ VECSUB(dvec, state.co, pa->state.co);
+
+ distance = Normalize(dvec);
+
+ if(part->flag & PART_DIE_ON_COL && distance < (epsys->particles+ptn2[p].index)->size){
+ *pa_die=1;
+ 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){
+ bvf->Subf(dvec,pa->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->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->state.vel);
+ if(cur_vel>0.0){
+ bvf->Copyf(dvec,pa->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->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->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->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){
+ count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2);
+ for(p=0; p<count; p++){
+ state.time=-1.0;
+ if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){
+ VECSUB(dvec, state.co, pa->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->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(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc, ParticleKey *state)
+{
+ float dvec[3], bvec[3], length, max_vel=part->max_vel;
+ float *q2, 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(state,&pa->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,state->vel);
+ bvf->Mulf(bvec,timestep);
+ bvf->Addf(dvec,dvec,bvec);
+ bvf->Addf(state->co,state->co,dvec);
+
+ /* air speed from wind 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;
+
+ if(pd->forcefield==PFIELD_WIND && pd->f_strength!=0.0){
+ float distance, wind[3];
+ VecCopyf(wind,eob->obmat[2]);
+ distance=VecLenf(state->co,eob->obmat[3]);
+
+ if (distance < 0.001) distance = 0.001f;
+
+ if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist)
+ ;
+ else{
+ Normalize(wind);
+ VecMulf(wind,pd->f_strength/(float)pow((double)distance,(double)pd->f_power));
+ bvf->Addf(state->co,state->co,wind);
+ }
+ }
+ }
+ }
+ }
+
+
+ if((part->flag & PART_BOIDS_2D)==0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0){
+ Crossf(yvec,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(state->vel,bank,q);
+
+ VECCOPY(dvec,state->vel);
+ VecMulf(dvec,-1.0f);
+ q2= vectoquat(dvec, OB_POSX, OB_POSZ);
+
+ QuatMul(state->rot,q,q2);
+
+ bvf->Mulf(acc,timestep);
+ bvf->Addf(state->vel,state->vel,acc);
+
+ if(part->flag & PART_BOIDS_2D){
+ state->vel[2]=0.0;
+ state->co[2]=part->groundz;
+
+ if(psys->keyed_ob){
+ Object *zob=psys->keyed_ob;
+ int min_face;
+ float co1[3],co2[3],min_d=2.0,min_w[4],imat[4][4];
+ VECCOPY(co1,state->co);
+ VECCOPY(co2,state->co);
+
+ co1[2]=1000.0f;
+ co2[2]=-1000.0f;
+
+ Mat4Invert(imat,zob->obmat);
+ Mat4MulVecfl(imat,co1);
+ Mat4MulVecfl(imat,co2);
+
+ if(psys_intersect_dm(zob,0,0,co1,co2,&min_d,&min_face,min_w,0,0,0,0)){
+ DerivedMesh *dm;
+ MFace *mface;
+ MVert *mvert;
+ float loc[3],nor[3],q1[4];
+
+ psys_disable_all(zob);
+ dm=mesh_get_derived_final(zob,0);
+ psys_enable_all(zob);
+
+ 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,min_w,loc,nor,0,0);
+
+ Mat4MulVecfl(zob->obmat,loc);
+ Mat4Mul3Vecfl(zob->obmat,nor);
+
+ Normalize(nor);
+
+ VECCOPY(state->co,loc);
+
+ zvec[2]=1.0;
+
+ Crossf(loc,zvec,nor);
+
+ bank=VecLength(loc);
+ if(bank>0.0){
+ bank=saasin(bank);
+
+ VecRotToQuat(loc,bank,q);
+
+ QUATCOPY(q1,state->rot);
+
+ QuatMul(state->rot,q,q1);
+ }
+ }
+ }
+ }
+
+ length=bvf->Length(state->vel);
+ if(length > max_vel)
+ bvf->Mulf(state->vel,max_vel/length);
+}
+/************************************************/
+/* Hair */
+/************************************************/
+void save_hair(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){
+ ParticleData *pa;
+ HairKey *key;
+ int totpart;
+ int i;
+
+ Mat4Invert(ob->imat,ob->obmat);
+
+ psys->lattice=psys_get_lattice(ob,psys);
+
+ if(psys->totpart==0) return;
+
+ totpart=psys->totpart;
+
+ /* save new keys for elements if needed */
+ for(i=0,pa=psys->particles; i<totpart; i++,pa++) {
+ /* first time alloc */
+ if(pa->totkey==0 || pa->hair==NULL) {
+ pa->hair = MEM_callocN((psys->part->hair_step + 1) * sizeof(HairKey), "HairKeys");
+ pa->totkey = 0;
+ }
+
+ key = pa->hair + pa->totkey;
+
+ /* convert from global to geometry space */
+ VecCopyf(key->co, pa->state.co);
+ Mat4MulVecfl(ob->imat, key->co);
+
+ if(pa->totkey) {
+ VECSUB(key->co, key->co, pa->hair->co);
+ psys_vec_rot_to_face(psmd->dm, pa, key->co);
+ }
+
+ key->time = pa->state.time;
+
+ key->weight = 1.0f - key->time / 100.0f;
+
+ pa->totkey++;
+
+ /* root is always in the origin of hair space so we set it to be so after the last key is saved*/
+ if(pa->totkey == psys->part->hair_step + 1)
+ pa->hair->co[0] = pa->hair->co[1] = pa->hair->co[2] = 0.0f;
+ }
+}
+/************************************************/
+/* System Core */
+/************************************************/
+/* unbaked particles are calculated dynamically */
+static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra,
+ float *vg_vel, float *vg_tan, float *vg_rot, float *vg_size)
+{
+ ParticleData *pa;
+ ParticleKey *outstate, *key;
+ ParticleSettings *part=psys->part;
+ KDTree *tree=0;
+ BoidVecFunc bvf;
+ IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE);
+ Material *ma=give_current_material(ob,part->omat);
+ float timestep;
+ int p, totpart, pa_die;
+ /* current time */
+ float ctime, ipotime;
+ /* frame & time changes */
+ float dfra, dtime, pa_dtime, pa_dfra=0.0;
+ float birthtime, dietime;
+
+ /* where have we gone in time since last time */
+ dfra= cfra - psys->cfra;
+
+ totpart=psys->totpart;
+
+ timestep=psys_get_timestep(part);
+ dtime= dfra*timestep;
+ ctime= cfra*timestep;
+ ipotime= cfra;
+
+ if(part->flag&PART_ABS_TIME && part->ipo){
+ calc_ipo(part->ipo, cfra);
+ execute_ipo((ID *)part, part->ipo);
+ }
+
+ if(dfra<0.0){
+ float *vg_size=0;
+ if(part->type==PART_REACTOR)
+ vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE);
+
+ for(p=0, pa=psys->particles; p<totpart; p++,pa++){
+ if(pa->flag & (PARS_NO_DISP+PARS_UNEXIST)) continue;
+
+ /* set correct ipo timing */
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ ipotime=100.0f*(cfra-pa->time)/pa->lifetime;
+ calc_ipo(part->ipo, ipotime);
+ execute_ipo((ID *)part, part->ipo);
+ }
+ pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size);
+
+ if(part->type==PART_REACTOR)
+ initialize_particle(pa,p,ob,psys,psmd);
+
+ reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot);
+
+ if(cfra>pa->time && part->flag & PART_LOOP && (part->flag & PART_LOOP_INSTANT)==0){
+ pa->loop=(short)((cfra-pa->time)/pa->lifetime)+1;
+ pa->alive=PARS_UNBORN;
+ }
+ else{
+ pa->loop=0;
+ if(cfra<=pa->time)
+ pa->alive=PARS_UNBORN;
+ /* without dynamics the state is allways known so no need to kill */
+ else if(ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)==0)
+ pa->alive=PARS_KILLED;
+ }
+ }
+
+ if(vg_size)
+ MEM_freeN(vg_size);
+
+ //if(part->phystype==PART_PHYS_SOLID)
+ // reset_to_first_fragment(psys);
+ }
+ else{
+ BLI_srandom(31415926 + (int)cfra + psys->seed);
+
+ /* outstate is used so that particles are updated in parallel */
+ outstate=MEM_callocN(totpart*sizeof(ParticleKey),"Particle Outstates");
+
+ /* update effectors */
+ if(psys->effectors.first)
+ psys_end_effectors(psys);
+
+ psys_init_effectors(ob,part->eff_group,psys);
+
+ if(psys->effectors.first)
+ precalc_effectors(ob,psys,psmd);
+
+ if(part->phystype==PART_PHYS_BOIDS){
+ /* create particle tree for fast inter-particle comparisons */
+ KDTree *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);
+ }
+ BLI_kdtree_balance(tree);
+ set_boid_vec_func(&bvf,part->flag&PART_BOIDS_2D);
+ }
+
+ /* main loop: calculate physics for all particles */
+ for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++){
+ if(pa->flag & (PARS_NO_DISP|PARS_UNEXIST)) continue;
+
+ copy_particle_key(key,&pa->state,1);
+
+ /* set correct ipo timing */
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ ipotime=100.0f*(cfra-pa->time)/pa->lifetime;
+ calc_ipo(part->ipo, ipotime);
+ execute_ipo((ID *)part, part->ipo);
+ }
+ pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size);
+
+ pa_die=0;
+
+ if(pa->alive==PARS_UNBORN || pa->alive==PARS_KILLED || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)){
+ /* allways reset particles to emitter before birth */
+ reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot);
+ copy_particle_key(key,&pa->state,1);
+ }
+
+ if(dfra>0.0 || psys->recalc){
+
+ if(psys->reactevents.first && ELEM(pa->alive,PARS_DEAD,PARS_KILLED)==0)
+ react_to_events(psys,p);
+
+ pa_dfra= dfra;
+ pa_dtime= dtime;
+
+ if(pa->flag & PART_LOOP && pa->flag & PART_LOOP_INSTANT)
+ birthtime=pa->dietime;
+ else
+ birthtime=pa->time+pa->loop*pa->lifetime;
+
+ dietime=birthtime+pa->lifetime;
+
+ if(birthtime < cfra && birthtime >= psys->cfra){
+ /* particle is born some time between this and last step*/
+ pa->alive=PARS_ALIVE;
+ pa_dfra= cfra - birthtime;
+ pa_dtime= pa_dfra*timestep;
+ }
+ else if(dietime <= cfra && psys->cfra < dietime){
+ /* particle dies some time between this and last step */
+ pa_dfra= dietime - psys->cfra;
+ pa_dtime= pa_dfra*timestep;
+ pa_die=1;
+ }
+ else if(dietime < cfra){
+ /* TODO: figure out if there's something to be done when particle is dead */
+ }
+
+ copy_particle_key(key,&pa->state,1);
+
+ if(dfra>0.0 && pa->alive==PARS_ALIVE){
+ switch(part->phystype){
+ case PART_PHYS_NEWTON:
+ /* do global forces & effectors */
+ apply_particle_forces(p,pa,ob,psys,part,timestep,pa_dfra,cfra,key);
+
+ /* deflection */
+ deflect_particle(ob,psmd,psys,part,pa,p,pa_dfra,cfra,key,&pa_die);
+
+ /* rotations */
+ rotate_particle(part,pa,pa_dfra,timestep,key);
+
+ break;
+ case PART_PHYS_BOIDS:
+ {
+ float acc[3];
+ boid_brain(&bvf,pa,ob,psys,part,tree,timestep,cfra,acc,&pa_die);
+ if(pa_die==0)
+ boid_body(&bvf,pa,psys,part,timestep,acc,key);
+ break;
+ }
+ }
+
+ push_reaction(ob,psys,p,PART_EVENT_NEAR,key);
+
+ if(pa_die){
+ push_reaction(ob,psys,p,PART_EVENT_DEATH,key);
+
+ if(part->flag & PART_LOOP){
+ pa->loop++;
+
+ if(part->flag & PART_LOOP_INSTANT){
+ reset_particle(pa,psys,psmd,ob,0.0,cfra,vg_vel,vg_tan,vg_rot);
+ pa->alive=PARS_ALIVE;
+ copy_particle_key(key,&pa->state,1);
+ }
+ else
+ pa->alive=PARS_UNBORN;
+ }
+ else{
+ pa->alive=PARS_DEAD;
+ key->time=pa->dietime;
+
+ if(pa->flag&PARS_STICKY)
+ psys_key_to_object(pa->stick_ob,key,0);
+ }
+ }
+ else
+ key->time=cfra;
+ }
+ }
+ }
+ /* apply outstates to particles */
+ for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++)
+ copy_particle_key(&pa->state,key,1);
+
+ MEM_freeN(outstate);
+ }
+ if(psys->reactevents.first)
+ BLI_freelistN(&psys->reactevents);
+
+ if(tree)
+ BLI_kdtree_free(tree);
+}
+
+/* check if path cache or children need updating and do it if needed */
+static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra)
+{
+ ParticleSettings *part=psys->part;
+ ParticleEditSettings *pset=&G.scene->toolsettings->particle;
+ int distr=0,alloc=0;
+
+ if((psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr) || psys->recalc&PSYS_ALLOC)
+ alloc=1;
+
+ if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT)))
+ distr=1;
+
+ if(distr){
+ if(alloc)
+ alloc_particles(psys,psys->totpart);
+
+ if(psys->totchild && part->childtype){
+ distribute_particles(ob,psys,PART_FROM_CHILD);
+
+ if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0)
+ psys_find_parents(ob,psmd,psys);
+ }
+ }
+
+ if((part->type==PART_HAIR || psys->flag&PSYS_KEYED) && (psys_in_edit_mode(psys)
+ || part->draw_as==PART_DRAW_PATH || part->draw&PART_DRAW_KEYS)){
+ psys_cache_paths(ob, psys, cfra, 0);
+
+ if(part->childtype){
+ if((G.rendering || (part->flag&PART_CHILD_RENDER)==0)
+ || (psys_in_edit_mode(psys) && (pset->flag&PE_SHOW_CHILD)))
+ psys_cache_child_paths(ob, psys, cfra, 0);
+ }
+ }
+ else if(psys->pathcache)
+ psys_free_path_cache(psys);
+}
+
+static void hair_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra)
+{
+ ParticleSettings *part = psys->part;
+
+ if(psys->recalc & PSYS_DISTR){
+ /* need this for changing subsurf levels */
+ psys_calc_dmfaces(ob, psmd->dm, psys);
+ }
+
+ if(psys->effectors.first)
+ psys_end_effectors(psys);
+
+ psys_init_effectors(ob,part->eff_group,psys);
+ if(psys->effectors.first)
+ precalc_effectors(ob,psys,psmd);
+
+ if(psys_in_edit_mode(psys))
+ PE_recalc_world_cos(ob, psys);
+
+ psys_update_path_cache(ob,psmd,psys,cfra);
+}
+
+/* updates cached particles' alive & other flags etc..*/
+static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra, float *vg_size)
+{
+ ParticleSettings *part=psys->part;
+ ParticleData *pa;
+ ParticleKey state;
+ IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE);
+ Material *ma=give_current_material(ob,part->omat);
+ int p;
+ float ipotime=cfra, disp;
+
+ /* deprecated */
+ //if(psys->recalc&PSYS_DISTR){
+ // /* The dm could have been changed so particle emitter element */
+ // /* indices might be wrong. There's really no "nice" way to handle*/
+ // /* this so we just try not to crash by correcting indices. */
+ // int totnum=-1;
+ // switch(part->from){
+ // case PART_FROM_VERT:
+ // totnum=psmd->dm->getNumVerts(psmd->dm);
+ // break;
+ // case PART_FROM_FACE:
+ // case PART_FROM_VOLUME:
+ // totnum=psmd->dm->getNumFaces(psmd->dm);
+ // break;
+ // }
+
+ // if(totnum==0){
+ // /* Now we're in real trouble, there's no emitter elements!! */
+ // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++)
+ // pa->num=-1;
+ // }
+ // else if(totnum>0){
+ // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++)
+ // pa->num=pa->num%totnum;
+ // }
+ //}
+
+ if(psys->effectors.first)
+ psys_end_effectors(psys);
+
+ //if(part->flag & (PART_BAKED_GUIDES+PART_BAKED_DEATHS)){
+ psys_init_effectors(ob,part->eff_group,psys);
+ if(psys->effectors.first)
+ precalc_effectors(ob,psys,psmd);
+ //}
+
+ disp= (float)get_current_display_percentage(psys)/50.0f-1.0f;
+
+ for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++){
+ if((part->flag&PART_ABS_TIME)==0 && part->ipo){
+ ipotime=100.0f*(cfra-pa->time)/pa->lifetime;
+ calc_ipo(part->ipo, ipotime);
+ execute_ipo((ID *)part, part->ipo);
+ }
+ pa->size= psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size);
+
+ psys->lattice=psys_get_lattice(ob,psys);
+
+ /* update alive status and push events */
+ if(pa->time>cfra)
+ pa->alive=PARS_UNBORN;
+ else if(pa->dietime<=cfra){
+ if(pa->dietime>psys->cfra){
+ state.time=pa->dietime;
+ psys_get_particle_state(ob,psys,p,&state,1);
+ push_reaction(ob,psys,p,PART_EVENT_DEATH,&state);
+ }
+ pa->alive=PARS_DEAD;
+ }
+ else{
+ pa->alive=PARS_ALIVE;
+ state.time=cfra;
+ psys_get_particle_state(ob,psys,p,&state,1);
+ state.time=cfra;
+ push_reaction(ob,psys,p,PART_EVENT_NEAR,&state);
+ }
+
+ if(psys->lattice){
+ end_latt_deform();
+ psys->lattice=0;
+ }
+
+ if(pa->r_rot[0] > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+}
+/* Calculates the next state for all particles of the system */
+/* In particles code most fra-ending are frames, time-ending are fra*timestep (seconds)*/
+static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra)
+{
+ ParticleSettings *part;
+ ParticleData *pa;
+ int totpart,oldtotpart=0,p;
+ float disp, *vg_vel=0, *vg_tan=0, *vg_rot=0, *vg_size=0;
+ int init=0,distr=0,alloc=0;
+
+ /*----start validity checks----*/
+
+ part=psys->part;
+
+ if(part->flag&PART_ABS_TIME && part->ipo){
+ calc_ipo(part->ipo, cfra);
+ execute_ipo((ID *)part, part->ipo);
+ }
+
+ if(part->from!=PART_FROM_PARTICLE)
+ vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE);
+
+ if(part->type == PART_HAIR) {
+ if(psys->flag & PSYS_HAIR_DONE) {
+ hair_step(ob, psmd, psys, cfra);
+ psys->cfra = cfra;
+ psys->recalc = 0;
+ return;
+ }
+ }
+ else {
+ if(psys->recalc)
+ clear_particles_from_cache(ob,psys,(int)cfra);
+ else if(get_particles_from_cache(ob, psys, (int)cfra)){
+ cached_step(ob,psmd,psys,cfra,vg_size);
+ psys->cfra=cfra;
+ psys->recalc = 0;
+ return;
+ }
+ }
+
+ /* if still here react to events */
+
+ if(psys->recalc&PSYS_TYPE) {
+ /* system type has changed so set sensible defaults and clear non applicable flags */
+ if(part->from == PART_FROM_PARTICLE) {
+ if(part->type != PART_REACTOR)
+ part->from = PART_FROM_FACE;
+ if(part->distr == PART_DISTR_GRID)
+ part->distr = PART_DISTR_JIT;
+ }
+
+ if(psys->part->phystype != PART_PHYS_KEYED)
+ psys->flag &= ~PSYS_KEYED;
+
+ if(part->type == PART_HAIR) {
+ part->draw_as = PART_DRAW_PATH;
+ part->rotfrom = PART_ROT_IINCR;
+ }
+ else
+ free_hair(psys);
+
+ psys->recalc &= ~PSYS_TYPE;
+ alloc = 1;
+ }
+ else
+ oldtotpart = psys->totpart;
+
+ if(part->distr == PART_DISTR_GRID)
+ totpart = part->grid_res * part->grid_res * part->grid_res;
+ else
+ totpart = psys->part->totpart;
+
+ if(oldtotpart != totpart || psys->recalc&PSYS_ALLOC || (psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr))
+ alloc = 1;
+
+ if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT) && ob==OBACT))
+ distr = 1;
+
+ if(distr || psys->recalc&PSYS_INIT)
+ init = 1;
+
+ if(init) {
+ if(distr) {
+ if(alloc)
+ alloc_particles(psys, totpart);
+
+ distribute_particles(ob, psys, part->from);
+
+ if(psys->totchild && part->childtype)
+ distribute_particles(ob, psys, PART_FROM_CHILD);
+ }
+ initialize_all_particles(ob, psys, psmd);
+
+ if(alloc)
+ reset_all_particles(ob, psys, psmd, 0.0, cfra, oldtotpart);
+
+ /* flag for possible explode modifiers after this system */
+ psmd->flag |= eParticleSystemFlag_Pars;
+ }
+
+
+ if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED)
+ psys_count_keyed_targets(ob,psys);
+
+ if(part->from!=PART_FROM_PARTICLE){
+ vg_vel=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL);
+ vg_tan=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN);
+ vg_rot=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT);
+ }
+
+ /* set particles to be not calculated */
+ disp= (float)get_current_display_percentage(psys)/50.0f-1.0f;
+
+ if(disp<1.0f) for(p=0, pa=psys->particles; p<totpart; p++,pa++){
+ if(pa->r_rot[0] > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+
+ /* ok now we're all set so let's go */
+ if(psys->totpart) {
+ //if(psys->part->from==PART_FROM_FACE) {
+ // psys_calc_dmfaces(ob, psmd->dm, psys);
+ //}
+ dynamics_step(ob,psys,psmd,cfra,vg_vel,vg_tan,vg_rot,vg_size);
+ }
+ psys->recalc = 0;
+ psys->cfra=cfra;
+
+ if(part->type!=PART_HAIR)
+ write_particles_to_cache(ob, psys, cfra);
+
+ /* for keyed particles the path is allways known so it can be drawn */
+ if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED){
+ set_keyed_keys(ob, psys);
+ psys_update_path_cache(ob,psmd,psys,(int)cfra);
+ }
+ else if(psys->pathcache)
+ psys_free_path_cache(psys);
+
+ if(vg_vel)
+ MEM_freeN(vg_vel);
+
+ if(psys->lattice){
+ end_latt_deform();
+ psys->lattice=0;
+ }
+}
+
+void psys_to_softbody(Object *ob, ParticleSystem *psys, int force_recalc)
+{
+ SoftBody *sb;
+ short softflag;
+
+ if((psys->softflag&OB_SB_ENABLE)==0) return;
+
+ if((ob->recalc&OB_RECALC_TIME)==0)
+ psys->softflag|=OB_SB_REDO;
+
+ /* let's replace the object's own softbody with the particle softbody */
+ /* a temporary solution before cloth simulation is implemented, jahka */
+
+ /* save these */
+ sb=ob->soft;
+ softflag=ob->softflag;
+
+ /* swich to new ones */
+ ob->soft=psys->soft;
+ ob->softflag=psys->softflag;
+
+ /* signal for before/free bake */
+ //if(psys->flag & PSYS_SOFT_BAKE || force_recalc){
+ // sbObjectToSoftbody(ob);
+ // psys->flag &= ~PSYS_SOFT_BAKE;
+ //}
+
+ /* do softbody */
+ sbObjectStep(ob, (float)G.scene->r.cfra, NULL, psys_count_keys(psys));
+
+ /* return things back to normal */
+ psys->soft=ob->soft;
+ psys->softflag=ob->softflag;
+
+ ob->soft=sb;
+ ob->softflag=softflag;
+}
+static int hair_needs_recalc(ParticleSystem *psys)
+{
+ if((psys->flag & PSYS_EDITED)==0 && (
+ (psys->flag & PSYS_HAIR_DONE)==0
+ || psys->recalc & PSYS_RECALC_HAIR)
+ ) {
+ psys->recalc &= ~PSYS_RECALC_HAIR;
+ return 1;
+ }
+
+ return 0;
+}
+/* main particle update call, checks that things are ok on the large scale before actual particle calculations */
+void particle_system_update(Object *ob, ParticleSystem *psys){
+
+ ParticleSystemModifierData *psmd=0;
+ float cfra;
+
+ if((psys->flag & PSYS_ENABLED)==0) return;
+
+ psmd=psys_get_modifier(ob,psys);
+
+ cfra=bsystem_time(ob,(float)CFRA,0.0);
+
+ /* system was already updated from modifier stack */
+ if(psmd->flag&eParticleSystemFlag_psys_updated){
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ /* make sure it really was updated to cfra */
+ if(psys->cfra==cfra)
+ return;
+ }
+
+ /* baked path softbody */
+ if(psys->part->type==PART_HAIR && psys->soft)
+ psys_to_softbody(ob, psys, 0);
+
+ /* not needed, this is all handled in hair_step */
+ ///* is the mesh changing under the edited particles? */
+ //if((psys->flag & PSYS_EDITED) && psys->part->type==PART_HAIR && psys->recalc & PSYS_RECALC_HAIR) {
+ // /* Just update the particles on the mesh */
+ // psys_update_edithair_dmfaces(ob, psmd->dm, psys);
+ //}
+
+ if(psys->part->type==PART_HAIR && hair_needs_recalc(psys)){
+ float hcfra=0.0f;
+ int i;
+ free_hair(psys);
+
+ /* first step is negative so particles get killed and reset */
+ psys->cfra=1.0f;
+
+ for(i=0; i<=psys->part->hair_step; i++){
+ hcfra=100.0f*(float)i/(float)psys->part->hair_step;
+ system_step(ob,psys,psmd,hcfra);
+ save_hair(ob,psys,psmd,hcfra);
+ }
+
+ psys->flag |= PSYS_HAIR_DONE;
+
+ if(psys->softflag&OB_SB_ENABLE)
+ psys_to_softbody(ob,psys,1);
+ }
+
+ system_step(ob,psys,psmd,cfra);
+
+ Mat4CpyMat4(psys->imat, ob->imat); /* used for duplicators */
+}
+
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
new file mode 100644
index 00000000000..9492754260c
--- /dev/null
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -0,0 +1,185 @@
+/**
+ *
+ * ***** BEGIN GPL/BL DUAL 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. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License. See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+* Contributor(s): Campbell Barton <ideasman42@gmail.com>
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "BKE_pointcache.h"
+
+#include "BKE_utildefines.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+
+#include "BLI_blenlib.h"
+#include "BKE_utildefines.h"
+#include "blendef.h"
+
+/* needed for directory lookup */
+#ifndef WIN32
+ #include <dirent.h>
+#else
+ #include "BLI_winstuff.h"
+#endif
+
+/* Takes an Object ID and returns a unique name
+ - id: object id
+ - cfra: frame for the cache, can be negative
+ - stack_index: index in the modifier stack. we can have cache for more then one stack_index
+*/
+
+static int ptcache_path(char *filename)
+{
+ sprintf(filename, PTCACHE_PATH);
+ BLI_convertstringcode(filename, G.sce, 0);
+ return strlen(filename);
+}
+
+int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_index, short do_path, short do_ext)
+{
+ int len=0;
+ char *idname;
+ char *newname;
+ filename[0] = '\0';
+ newname = filename;
+
+ /* start with temp dir */
+ if (do_path) {
+ len = ptcache_path(filename);
+ newname += len;
+ }
+ idname = (id->name+2);
+ /* convert chars to hex so they are always a valid filename */
+ while('\0' != *idname) {
+ sprintf(newname, "%02X", (char)(*idname++));
+ newname+=2;
+ len += 2;
+ }
+
+ if (do_ext) {
+ sprintf(newname, "_%06d_%02d"PTCACHE_EXT, cfra, stack_index); /* always 6 chars */
+ len += 16;
+ }
+
+ return len; /* make sure the above string is always 16 chars */
+}
+
+/* youll need to close yourself after! */
+FILE *BKE_ptcache_id_fopen(struct ID *id, char mode, int cfra, int stack_index)
+{
+ /* mode is same as fopen's modes */
+ FILE *fp = NULL;
+ char filename[(FILE_MAXDIR+FILE_MAXFILE)*2];
+
+ BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1);
+
+ if (mode=='r') {
+ if (!BLI_exists(filename)) {
+ return NULL;
+ }
+ fp = fopen(filename, "rb");
+ } else if (mode=='w') {
+ BLI_make_existing_file(filename); /* will create the dir if needs be, same as //textures is created */
+ fp = fopen(filename, "wb");
+ }
+
+ if (!fp) {
+ return NULL;
+ }
+
+ return fp;
+}
+
+/* youll need to close yourself after!
+ * mode,
+
+*/
+
+void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index)
+{
+ int len; /* store the length of the string */
+
+ /* mode is same as fopen's modes */
+ DIR *dir;
+ struct dirent *de;
+ char path[FILE_MAX];
+ char filename[(FILE_MAXDIR+FILE_MAXFILE)*2];
+ char path_full[(FILE_MAXDIR+FILE_MAXFILE)*2];
+
+ /* clear all files in the temp dir with the prefix of the ID and the ".bphys" suffix */
+ switch (mode) {
+ case PTCACHE_CLEAR_ALL:
+ case PTCACHE_CLEAR_BEFORE:
+ case PTCACHE_CLEAR_AFTER:
+ ptcache_path(path);
+ len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 0, 0); /* no path */
+
+ dir = opendir(path);
+ if (dir==NULL)
+ return;
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/
+ if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */
+ if (mode == PTCACHE_CLEAR_ALL) {
+ BLI_join_dirfile(path_full, path, de->d_name);
+ BLI_delete(path_full, 0, 0);
+ } else {
+ /* read the number of the file */
+ int frame, len2 = strlen(de->d_name);
+ char num[7];
+ if (len2 > 15) { /* could crash if trying to copy a string out of this range*/
+ strncpy(num, de->d_name + (strlen(de->d_name) - 15), 6);
+ frame = atoi(num);
+
+ if((mode==PTCACHE_CLEAR_BEFORE && frame < cfra) ||
+ (mode==PTCACHE_CLEAR_AFTER && frame > cfra) ) {
+
+ BLI_join_dirfile(path_full, path, de->d_name);
+ BLI_delete(path_full, 0, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir(dir);
+ break;
+
+ case PTCACHE_CLEAR_FRAME:
+ len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); /* no path */
+ BLI_delete(filename, 0, 0);
+ break;
+ }
+ return;
+}
+
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 88de43fa85d..aa9c489044a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -174,6 +174,8 @@ void free_scene(Scene *sce)
Scene *add_scene(char *name)
{
Scene *sce;
+ ParticleEditSettings *pset;
+ int a;
sce= alloc_libblock(&G.main->scene, ID_SCE, name);
sce->lay= 1;
@@ -232,6 +234,19 @@ Scene *add_scene(char *name)
sce->toolsettings->unwrapper = 1;
sce->toolsettings->select_thresh= 0.01f;
sce->toolsettings->jointrilimit = 0.8f;
+
+ pset= &sce->toolsettings->particle;
+ pset->flag= PE_KEEP_LENGTHS|PE_LOCK_FIRST|PE_DEFLECT_EMITTER;
+ pset->emitterdist= 0.25f;
+ pset->totrekey= 5;
+ pset->totaddkey= 5;
+ pset->brushtype= PE_BRUSH_NONE;
+ for(a=0; a<PE_TOT_BRUSH; a++) {
+ pset->brush[a].strength= 50;
+ pset->brush[a].size= 50;
+ pset->brush[a].step= 10;
+ }
+ pset->brush[PE_BRUSH_CUT].strength= 100;
sce->jumpframe = 10;
sce->audio.mixrate = 44100;
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 36d114cf023..bd9d1cb75ca 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -61,9 +61,11 @@ variables on the UI for now
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h" /* here is the softbody struct */
+#include "DNA_particle_types.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_lattice_types.h"
#include "DNA_scene_types.h"
@@ -75,9 +77,12 @@ variables on the UI for now
#include "BKE_global.h"
#include "BKE_key.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_softbody.h"
#include "BKE_utildefines.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_pointcache.h"
+#include "BKE_modifier.h"
#include "BIF_editdeform.h"
#include "BIF_graphics.h"
@@ -90,18 +95,6 @@ static int (*SB_localInterruptCallBack)(void) = NULL;
/* ********** soft body engine ******* */
-
-typedef struct BodyPoint {
- float origS[3], origE[3], origT[3], pos[3], vec[3], force[3];
- float goal;
- float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */
- int nofsprings; int *springs;
- float choke;
- float colball;
- short flag;
- char octantflag;
-} BodyPoint;
-
typedef struct BodySpring {
int v1, v2;
float len, strength, cf;
@@ -480,21 +473,31 @@ void ccd_build_deflector_hache(Object *vertexowner,GHash *hash)
while (base) {
/*Only proceed for mesh object in same layer */
if(base->object->type==OB_MESH && (base->lay & vertexowner->lay)) {
+ int particles=0;
ob= base->object;
- if((vertexowner) && (ob == vertexowner)){
- /* if vertexowner is given we don't want to check collision with owner object */
- base = base->next;
- continue;
+ if((vertexowner) && (ob == vertexowner)) {
+ if(vertexowner->soft->particles){
+ particles=1;
+ }
+ else {
+ /* if vertexowner is given we don't want to check collision with owner object */
+ base = base->next;
+ continue;
+ }
}
/*+++ only with deflecting set */
if(ob->pd && ob->pd->deflect && BLI_ghash_lookup(hash, ob) == 0) {
DerivedMesh *dm= NULL;
-
- if(ob->softflag & OB_SB_COLLFINAL) { /* so maybe someone wants overkill to collide with subsurfed */
- dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH);
- } else {
- dm = mesh_get_derived_deform(ob, CD_MASK_BAREMESH);
+
+ if(particles) {
+ dm = psys_get_modifier(ob,psys_get_current(ob))->dm;
+ }
+ else {
+ if(ob->softflag & OB_SB_COLLFINAL) /* so maybe someone wants overkill to collide with subsurfed */
+ dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH);
+ else
+ dm = mesh_get_derived_deform(ob, CD_MASK_BAREMESH);
}
if(dm){
@@ -852,10 +855,11 @@ static void renew_softbody(Object *ob, int totpoint, int totspring)
{
SoftBody *sb;
int i;
-
+ short softflag;
if(ob->soft==NULL) ob->soft= sbNew();
else free_softbody_intern(ob->soft);
sb= ob->soft;
+ softflag=ob->softflag;
if(totpoint) {
sb->totpoint= totpoint;
@@ -869,8 +873,8 @@ static void renew_softbody(Object *ob, int totpoint, int totspring)
for (i=0; i<totpoint; i++) {
BodyPoint *bp = &sb->bpoint[i];
- if(ob->softflag & OB_SB_GOAL) {
- bp->goal= ob->soft->defgoal;
+ if(softflag & OB_SB_GOAL) {
+ bp->goal= sb->defgoal;
}
else {
bp->goal= 0.0f;
@@ -900,7 +904,6 @@ static void free_softbody_baked(SoftBody *sb)
sb->keys= NULL;
sb->totkey= 0;
-
}
static void free_scratch(SoftBody *sb)
{
@@ -1203,9 +1206,9 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_
Crossf(d_nvect, edge2, edge1);
Normalize(d_nvect);
if (
- LineIntersectsTriangle(nv1, nv2, face_v1, face_v2, face_v3, &t) ||
- LineIntersectsTriangle(nv2, nv3, face_v1, face_v2, face_v3, &t) ||
- LineIntersectsTriangle(nv3, nv1, face_v1, face_v2, face_v3, &t) ){
+ LineIntersectsTriangle(nv1, nv2, face_v1, face_v2, face_v3, &t, NULL) ||
+ LineIntersectsTriangle(nv2, nv3, face_v1, face_v2, face_v3, &t, NULL) ||
+ LineIntersectsTriangle(nv3, nv1, face_v1, face_v2, face_v3, &t, NULL) ){
Vec3PlusStVec(force,-1.0f,d_nvect);
*damp=ob->pd->pdef_sbdamp;
deflected = 2;
@@ -1217,9 +1220,9 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_
Crossf(d_nvect, edge2, edge1);
Normalize(d_nvect);
if (
- LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t) ||
- LineIntersectsTriangle(nv3, nv4, face_v1, face_v2, face_v3, &t) ||
- LineIntersectsTriangle(nv4, nv1, face_v1, face_v2, face_v3, &t) ){
+ LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t, NULL) ||
+ LineIntersectsTriangle(nv3, nv4, face_v1, face_v2, face_v3, &t, NULL) ||
+ LineIntersectsTriangle(nv4, nv1, face_v1, face_v2, face_v3, &t, NULL) ){
Vec3PlusStVec(force,-1.0f,d_nvect);
*damp=ob->pd->pdef_sbdamp;
deflected = 2;
@@ -1402,7 +1405,7 @@ int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp
Crossf(d_nvect, edge2, edge1);
Normalize(d_nvect);
- if ( LineIntersectsTriangle(edge_v1, edge_v2, nv1, nv2, nv3, &t)){
+ if ( LineIntersectsTriangle(edge_v1, edge_v2, nv1, nv2, nv3, &t, NULL)){
float v1[3],v2[3];
float intrusiondepth,i1,i2;
VECSUB(v1, edge_v1, nv2);
@@ -1421,7 +1424,7 @@ int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp
Crossf(d_nvect, edge2, edge1);
Normalize(d_nvect);
- if (LineIntersectsTriangle( edge_v1, edge_v2,nv1, nv3, nv4, &t)){
+ if (LineIntersectsTriangle( edge_v1, edge_v2,nv1, nv3, nv4, &t, NULL)){
float v1[3],v2[3];
float intrusiondepth,i1,i2;
VECSUB(v1, edge_v1, nv4);
@@ -1550,7 +1553,7 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float *
Object *ob= NULL;
GHash *hash;
GHashIterator *ihash;
- float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3],d_nvect[3], dv1[3],ve[3],avel[3],
+ float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3],d_nvect[3], dv1[3],ve[3],avel[3]={0.0,0.0,0.0},
vv1[3], vv2[3], vv3[3], vv4[3], coledge[3], mindistedge = 1000.0f,
outerforceaccu[3],innerforceaccu[3],
facedist,n_mag,force_mag_norm,minx,miny,minz,maxx,maxy,maxz,
@@ -2953,6 +2956,103 @@ static void curve_surf_to_softbody(Object *ob)
}
+static void springs_from_particles(Object *ob)
+{
+ ParticleSystem *psys;
+ ParticleSystemModifierData *psmd=0;
+ ParticleData *pa=0;
+ HairKey *key=0;
+ SoftBody *sb;
+ BodyPoint *bp;
+ BodySpring *bs;
+ int a,k;
+ float hairmat[4][4];
+
+ psys= ob->soft->particles;
+ sb= ob->soft;
+ if(ob && sb && psys) {
+ psmd = psys_get_modifier(ob, psys);
+
+ bp= sb->bpoint;
+ for(a=0, pa=psys->particles; a<psys->totpart; a++, pa++) {
+ for(k=0, key=pa->hair; k<pa->totkey; k++, bp++, key++) {
+ VECCOPY(bp->origS, key->co);
+
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
+
+ Mat4MulVecfl(hairmat, bp->origS);
+ }
+ }
+
+ for(a=0, bs=sb->bspring; a<sb->totspring; a++, bs++)
+ bs->len= VecLenf(sb->bpoint[bs->v1].origS, sb->bpoint[bs->v2].origS);
+ }
+}
+
+static void particles_to_softbody(Object *ob)
+{
+ SoftBody *sb;
+ BodyPoint *bp;
+ BodySpring *bs;
+ ParticleData *pa;
+ HairKey *key;
+ ParticleSystem *psys= ob->soft->particles;
+ float goalfac;
+ int a, k, curpoint;
+ int totpoint= psys_count_keys(psys);
+ int totedge= totpoint-psys->totpart;
+
+ /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
+ renew_softbody(ob, totpoint, totedge);
+
+ psys->particles->bpi = 0;
+ for(a=1, pa=psys->particles+1; a<psys->totpart; a++, pa++)
+ pa->bpi = (pa-1)->bpi + pa->totkey;
+
+ /* we always make body points */
+ sb= ob->soft;
+ bp= sb->bpoint;
+ bs= sb->bspring;
+ goalfac= ABS(sb->maxgoal - sb->mingoal);
+
+ if((ob->softflag & OB_SB_GOAL)) {
+ for(a=0, pa=psys->particles; a<psys->totpart; a++, pa++) {
+ for(k=0, key=pa->hair; k<pa->totkey; k++,bp++,key++) {
+ if(k) {
+ bp->goal= key->weight;
+ bp->goal= sb->mingoal + bp->goal*goalfac;
+ bp->goal= (float)pow(bp->goal, 4.0f);
+ }
+ else{
+ /* hair roots are allways fixed fully to goal */
+ bp->goal= 1.0f;
+ }
+ }
+ }
+ }
+
+ bp= sb->bpoint;
+ curpoint=0;
+ for(a=0, pa=psys->particles; a<psys->totpart; a++, curpoint++, pa++) {
+ for(k=0; k<pa->totkey-1; k++,bs++,curpoint++) {
+ bs->v1=curpoint;
+ bs->v2=curpoint+1;
+ bs->strength= 1.0;
+ bs->order=1;
+ }
+ }
+
+ build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */
+ /* insert *other second order* springs if desired */
+ if(sb->secondspring > 0.0000001f) {
+ add_2nd_order_springs(ob,sb->secondspring*10.0); /* exploits the the first run of build_bps_springlist(ob);*/
+ build_bps_springlist(ob); /* yes we need to do it again*/
+ }
+ springs_from_particles(ob); /* write the 'rest'-lenght of the springs */
+ if(ob->softflag & OB_SB_SELF)
+ calculate_collision_balls(ob);
+}
+
/* copies softbody result back in object */
static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, int local)
{
@@ -2969,117 +3069,94 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts,
}
}
-/* return 1 if succesfully baked and applied step */
-static int softbody_baked_step(Object *ob, float framenr, float (*vertexCos)[3], int numVerts)
+void softbody_clear_cache(Object *ob, float framenr)
{
- SoftBody *sb= ob->soft;
- SBVertex *key0, *key1, *key2, *key3;
- BodyPoint *bp;
- float data[4], sfra, efra, cfra, dfra, fac; /* start, end, current, delta */
- int ofs1, a;
-
- /* precondition check */
- if(sb==NULL || sb->keys==NULL || sb->totkey==0) return 0;
- /* so we got keys, but no bodypoints... even without simul we need it for the bake */
- if(sb->bpoint==NULL) sb->bpoint= MEM_callocN( sb->totpoint*sizeof(BodyPoint), "bodypoint");
-
- /* convert cfra time to system time */
- sfra= (float)sb->sfra;
- cfra= bsystem_time(ob, framenr, 0.0);
- efra= (float)sb->efra;
- dfra= (float)sb->interval;
+ SoftBody *sb = ob->soft;
+ ModifierData *md = ob->modifiers.first;
+ int stack_index = -1;
+ int a;
- /* offset in keys array */
- ofs1= (int)floor( (cfra-sfra)/dfra );
+ if(sb==NULL) return;
- if(ofs1 < 0) {
- key0=key1=key2=key3= *sb->keys;
- }
- else if(ofs1 >= sb->totkey-1) {
- key0=key1=key2=key3= *(sb->keys+sb->totkey-1);
- }
+ if(sb->particles)
+ stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles));
else {
- key1= *(sb->keys+ofs1);
- key2= *(sb->keys+ofs1+1);
-
- if(ofs1>0) key0= *(sb->keys+ofs1-1);
- else key0= key1;
-
- if(ofs1<sb->totkey-2) key3= *(sb->keys+ofs1+2);
- else key3= key2;
- }
-
- sb->ctime= cfra; /* needed? */
-
- /* timing */
- fac= ((cfra-sfra)/dfra) - (float)ofs1;
- CLAMP(fac, 0.0, 1.0);
- set_four_ipo(fac, data, KEY_BSPLINE);
- if (key0&&key1&&key2&&key3) // may be null because we SHIFT_ESCAPED
- for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++, key0++, key1++, key2++, key3++) {
- bp->pos[0]= data[0]*key0->vec[0] + data[1]*key1->vec[0] + data[2]*key2->vec[0] + data[3]*key3->vec[0];
- bp->pos[1]= data[0]*key0->vec[1] + data[1]*key1->vec[1] + data[2]*key2->vec[1] + data[3]*key3->vec[1];
- bp->pos[2]= data[0]*key0->vec[2] + data[1]*key1->vec[2] + data[2]*key2->vec[2] + data[3]*key3->vec[2];
+ for(a=0; md; md=md->next, a++) {
+ if(md->type == eModifierType_Softbody) {
+ stack_index = a;
+ break;
+ }
+ }
}
-
- softbody_to_object(ob, vertexCos, numVerts, sb->local);
-
- return 1;
-}
-/* only gets called after succesfully doing softbody_step */
-/* already checked for OB_SB_BAKE flag */
-static void softbody_baked_add(Object *ob, float framenr)
+ BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, framenr, stack_index);
+}
+static void softbody_write_cache(Object *ob, float framenr)
{
- SoftBody *sb= ob->soft;
- SBVertex *key;
+ FILE *fp = NULL;
+ SoftBody *sb = ob->soft;
BodyPoint *bp;
- float sfra, efra, cfra, dfra, fac1; /* start, end, current, delta */
- int ofs1, a;
-
- /* convert cfra time to system time */
- sfra= (float)sb->sfra;
- fac1= ob->sf; ob->sf= 0.0f; /* disable startframe */
- cfra= bsystem_time(ob, framenr, 0.0);
- ob->sf= fac1;
- efra= (float)sb->efra;
- dfra= (float)sb->interval;
-
- if(sb->totkey==0) {
- if(sb->sfra >= sb->efra) return; /* safety, UI or py setting allows */
- if(sb->interval<1) sb->interval= 1; /* just be sure */
-
- sb->totkey= 1 + (int)(ceil( (efra-sfra)/dfra ) );
- sb->keys= MEM_callocN( sizeof(void *)*sb->totkey, "sb keys");
+ ModifierData *md = ob->modifiers.first;
+ int stack_index = -1;
+ int a;
+
+ if(sb->totpoint == 0) return;
+
+ if(sb->particles)
+ stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles));
+ else {
+ for(a=0; md; md=md->next, a++) {
+ if(md->type == eModifierType_Softbody) {
+ stack_index = a;
+ break;
+ }
+ }
}
+
+ fp = BKE_ptcache_id_fopen((ID *)ob, 'w', framenr, stack_index);
+ if(!fp) return;
+
+ for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++)
+ fwrite(&bp->pos, sizeof(float), 3, fp);
- /* inverse matrix might not be uptodate... */
- Mat4Invert(ob->imat, ob->obmat);
-
- /* now find out if we have to store a key */
-
- /* offset in keys array */
- if(cfra>=(efra)) {
- ofs1= sb->totkey-1;
- fac1= 0.0;
- }
+ fclose(fp);
+}
+static int softbody_read_cache(Object *ob, float framenr)
+{
+ FILE *fp = NULL;
+ SoftBody *sb = ob->soft;
+ BodyPoint *bp;
+ ModifierData *md = ob->modifiers.first;
+ int stack_index = -1;
+ int a, ret = 1;
+
+ if(sb->totpoint == 0) return 0;
+
+ if(sb->particles)
+ stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles));
else {
- ofs1= (int)floor( (cfra-sfra)/dfra );
- fac1= ((cfra-sfra)/dfra) - (float)ofs1;
- }
- if( fac1 < 1.0/dfra ) {
-
- key= *(sb->keys+ofs1);
- if(key == NULL) {
- *(sb->keys+ofs1)= key= MEM_mallocN(sb->totpoint*sizeof(SBVertex), "softbody key");
-
- for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++, key++) {
- VECCOPY(key->vec, bp->pos);
- if(sb->local)
- Mat4MulVecfl(ob->imat, key->vec);
+ for(a=0; md; md=md->next, a++) {
+ if(md->type == eModifierType_Softbody) {
+ stack_index = a;
+ break;
}
}
}
+
+ fp = BKE_ptcache_id_fopen((ID *)ob, 'r', framenr, stack_index);
+ if(!fp)
+ ret = 0;
+ else {
+ for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++)
+ if(fread(&bp->pos, sizeof(float), 3, fp) != 3) {
+ ret = 0;
+ break;
+ }
+
+ fclose(fp);
+ }
+
+ return ret;
}
/* +++ ************ maintaining scratch *************** */
void sb_new_scratch(SoftBody *sb)
@@ -3175,15 +3252,14 @@ void sbSetInterruptCallBack(int (*f)(void))
/* simulates one step. framenr is in frames */
void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts)
{
+ ParticleSystemModifierData *psmd=0;
+ ParticleData *pa=0;
SoftBody *sb;
+ HairKey *key= NULL;
BodyPoint *bp;
int a;
float dtime,ctime,forcetime,err;
-
- /* baking works with global time */
- if(!(ob->softflag & OB_SB_BAKEDO) )
- if(softbody_baked_step(ob, framenr, vertexCos, numVerts) ) return;
-
+ float hairmat[4][4];
/* This part only sets goals and springs, based on original mesh/curve/lattice data.
Copying coordinates happens in next chunk by setting softbody flag OB_SB_RESET */
@@ -3194,7 +3270,13 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
(numVerts!=ob->soft->totpoint) || /* should never happen, just to be safe */
((ob->softflag & OB_SB_EDGES) && !ob->soft->bspring && object_has_edges(ob))) /* happens when in UI edges was set */
{
- switch(ob->type) {
+ if(ob->soft && ob->soft->bpoint) /* don't clear on file load */
+ softbody_clear_cache(ob, framenr);
+
+ if(ob->soft->particles){
+ particles_to_softbody(ob);
+ }
+ else switch(ob->type) {
case OB_MESH:
mesh_to_softbody(ob);
break;
@@ -3220,6 +3302,10 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
/* still no points? go away */
if(sb->totpoint==0) return;
+ if(sb->particles){
+ psmd=psys_get_modifier(ob,sb->particles);
+ pa=sb->particles->particles;
+ }
/* checking time: */
@@ -3231,17 +3317,45 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
dtime= ctime - sb->ctime;
}
+ if(softbody_read_cache(ob, framenr)) {
+ if(sb->particles==0)
+ softbody_to_object(ob, vertexCos, numVerts, sb->local);
+ sb->ctime = ctime;
+ return;
+ }
+
/* the simulator */
/* update the vertex locations */
if (dtime!=0.0) {
+ if(sb->particles) {
+ pa=sb->particles->particles;
+ key = pa->hair;
+
+ psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat);
+ }
+
for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) {
/* store where goals are now */
VECCOPY(bp->origS, bp->origE);
/* copy the position of the goals at desired end time */
- VECCOPY(bp->origE, vertexCos[a]);
- /* vertexCos came from local world, go global */
- Mat4MulVecfl(ob->obmat, bp->origE);
+ if(sb->particles) {
+ if(key == pa->hair + pa->totkey) {
+ pa++;
+ key = pa->hair;
+
+ psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat);
+ }
+ VECCOPY(bp->origE, key->co);
+ Mat4MulVecfl(hairmat,bp->origE);
+
+ key++;
+ }
+ else{
+ VECCOPY(bp->origE, vertexCos[a]);
+ /* vertexCos came from local world, go global */
+ Mat4MulVecfl(ob->obmat, bp->origE);
+ }
/* just to be save give bp->origT a defined value
will be calulated in interpolate_exciter()*/
VECCOPY(bp->origT, bp->origE);
@@ -3253,9 +3367,29 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
(dtime>=9.9*G.scene->r.framelen) /* too far forward in time --> goals won't be accurate enough */
)
{
+ if(sb->particles) {
+ pa=sb->particles->particles;
+ key = pa->hair;
+
+ psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat);
+ }
+
for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) {
- VECCOPY(bp->pos, vertexCos[a]);
- Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/
+ if(sb->particles) {
+ if(key == pa->hair + pa->totkey) {
+ pa++;
+ key = pa->hair;
+
+ psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat);
+ }
+ VECCOPY(bp->pos, key->co);
+ Mat4MulVecfl(hairmat, bp->pos);
+ key++;
+ }
+ else {
+ VECCOPY(bp->pos, vertexCos[a]);
+ Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/
+ }
VECCOPY(bp->origS, bp->pos);
VECCOPY(bp->origE, bp->pos);
VECCOPY(bp->origT, bp->pos);
@@ -3283,23 +3417,22 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
sb_new_scratch(sb); /* make a new */
sb->scratch->needstobuildcollider=1;
-
- /* copy some info to scratch */
- switch(ob->type) {
- case OB_MESH:
- if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob);
- break;
- case OB_LATTICE:
- break;
- case OB_CURVE:
- case OB_SURF:
- break;
- default:
- break;
+ if((sb->particles)==0) {
+ /* copy some info to scratch */
+ switch(ob->type) {
+ case OB_MESH:
+ if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob);
+ break;
+ case OB_LATTICE:
+ break;
+ case OB_CURVE:
+ case OB_SURF:
+ break;
+ default:
+ break;
+ }
}
-
-
ob->softflag &= ~OB_SB_RESET;
}
else if(dtime>0.0) {
@@ -3411,10 +3544,10 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts
}
}
- softbody_to_object(ob, vertexCos, numVerts, 0);
+ if(sb->particles==0)
+ softbody_to_object(ob, vertexCos, numVerts, 0);
sb->ctime= ctime;
-
- if(ob->softflag & OB_SB_BAKEDO) softbody_baked_add(ob, framenr);
+ softbody_write_cache(ob, framenr);
}