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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>2007-11-27 01:09:57 +0300
committerBrecht Van Lommel <brechtvanlommel@pandora.be>2007-11-27 01:09:57 +0300
commit7da56f4a9ba0bdd0cdcd40b8ca6e69d776d26abe (patch)
tree663c13aae5606937571ac1e7a4c77ca2866e75dd /source/blender/blenkernel/intern/particle.c
parent121dab1bcd9467bd8e11d0a82e83a1621758fd8e (diff)
parent770291b9ea1ec03d98b6bae4fd2a2d3f0091be41 (diff)
Particles
========= Merge of the famous particle patch by Janne Karhu, a full rewrite of the Blender particle system. This includes: - Emitter, Hair and Reactor particle types. - Newtonian, Keyed and Boids physics. - Various particle visualisation and rendering types. - Vertex group and texture control for various properties. - Interpolated child particles from parents. - Hair editing with combing, growing, cutting, .. . - Explode modifier. - Harmonic, Magnetic fields, and multiple falloff types. .. and lots of other things, some more info is here: http://wiki.blender.org/index.php/BlenderDev/Particles_Rewrite http://wiki.blender.org/index.php/BlenderDev/Particles_Rewrite_Doc The new particle system cannot be backwards compatible. Old particle systems are being converted to the new system, but will require tweaking to get them looking the same as before. Point Cache =========== The new system to replace manual baking, based on automatic caching on disk. This is currently used by softbodies and the particle system. See the Cache API section on: http://wiki.blender.org/index.php/BlenderDev/PhysicsSprint Documentation ============= These new features still need good docs for the release logs, help for this is appreciated.
Diffstat (limited to 'source/blender/blenkernel/intern/particle.c')
-rw-r--r--source/blender/blenkernel/intern/particle.c2978
1 files changed, 2978 insertions, 0 deletions
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;
+ //}
+}
+