diff options
Diffstat (limited to 'source/blender/blenkernel')
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); } |