/* effect.c * * * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ #include #include #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" #include "DNA_effect_types.h" #include "DNA_group_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_listBase.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" #include "DNA_texture_types.h" #include "DNA_scene_types.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "BLI_jitter.h" #include "BLI_rand.h" #include "BKE_action.h" #include "BKE_anim.h" /* needed for where_on_path */ #include "BKE_armature.h" #include "BKE_blender.h" #include "BKE_constraint.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" #include "BKE_displist.h" #include "BKE_DerivedMesh.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_group.h" #include "BKE_ipo.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_mesh.h" #include "BKE_material.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_utildefines.h" #include "PIL_time.h" #include "RE_render_ext.h" /* fluid sim particle import */ #ifndef DISABLE_ELBEEM #include "DNA_object_fluidsim.h" #include "LBM_fluidsim.h" #include "elbeem.h" #include #include #endif // DISABLE_ELBEEM //XXX #include "BIF_screen.h" /* temporal struct, used for reading return of mesh_get_mapped_verts_nors() */ typedef struct VeNoCo { float co[3], no[3]; } VeNoCo; Effect *add_effect(int type) { Effect *eff=0; PartEff *paf; int a; switch(type) { case EFF_PARTICLE: paf= MEM_callocN(sizeof(PartEff), "neweff"); eff= (Effect *)paf; paf->sta= 1.0; paf->end= 100.0; paf->lifetime= 50.0; for(a=0; alife[a]= 50.0; paf->child[a]= 4; paf->mat[a]= 1; } paf->totpart= 1000; paf->totkey= 8; paf->staticstep= 5; paf->defvec[2]= 1.0f; paf->nabla= 0.05f; paf->disp = 100; paf->speedtex = 8; paf->omat = 1; paf->flag= PAF_FACE; break; } eff->type= eff->buttype= type; eff->flag |= SELECT; return eff; } void free_effect(Effect *eff) { PartEff *paf; if(eff->type==EFF_PARTICLE) { paf= (PartEff *)eff; if(paf->keys) MEM_freeN(paf->keys); } MEM_freeN(eff); } void free_effects(ListBase *lb) { Effect *eff; eff= lb->first; while(eff) { BLI_remlink(lb, eff); free_effect(eff); eff= lb->first; } } Effect *copy_effect(Effect *eff) { Effect *effn; effn= MEM_dupallocN(eff); if(effn->type==EFF_PARTICLE) ((PartEff *)effn)->keys= 0; return effn; } void copy_act_effect(Object *ob) { /* return a copy of the active effect */ Effect *effn, *eff; eff= ob->effect.first; while(eff) { if(eff->flag & SELECT) { effn= copy_effect(eff); BLI_addtail(&ob->effect, effn); eff->flag &= ~SELECT; return; } eff= eff->next; } /* when it comes here: add new effect */ eff= add_effect(EFF_PARTICLE); BLI_addtail(&ob->effect, eff); } void copy_effects(ListBase *lbn, ListBase *lb) { Effect *eff, *effn; lbn->first= lbn->last= 0; eff= lb->first; while(eff) { effn= copy_effect(eff); BLI_addtail(lbn, effn); eff= eff->next; } } void deselectall_eff(Object *ob) { Effect *eff= ob->effect.first; while(eff) { eff->flag &= ~SELECT; eff= eff->next; } } /* ***************** PARTICLES ***************** */ static Particle *new_particle(PartEff *paf) { static Particle *pa; static int cur; /* we agree: when paf->keys==0: alloc */ if(paf->keys==NULL) { pa= paf->keys= MEM_callocN( paf->totkey*paf->totpart*sizeof(Particle), "particlekeys" ); cur= 0; } else { if(cur && curtotpart) pa+=paf->totkey; cur++; } return pa; } PartEff *give_parteff(Object *ob) { PartEff *paf; paf= ob->effect.first; while(paf) { if(paf->type==EFF_PARTICLE) return paf; paf= paf->next; } return 0; } void where_is_particle(PartEff *paf, Particle *pa, float ctime, float *vec) { Particle *p[4]; float dt, t[4]; int a; if(paf->totkey==1 || ctime < pa->time) { VECCOPY(vec, pa->co); return; } /* first find the first particlekey */ a= (int)((paf->totkey-1)*(ctime-pa->time)/pa->lifetime); if(a>=paf->totkey) a= paf->totkey-1; else if(a<0) a= 0; pa+= a; if(a>0) p[0]= pa-1; else p[0]= pa; p[1]= pa; if(a+1totkey) p[2]= pa+1; else p[2]= pa; if(a+2totkey) p[3]= pa+2; else p[3]= p[2]; if(p[1]==p[2] || p[2]->time == p[1]->time) dt= 0.0; else dt= (ctime-p[1]->time)/(p[2]->time - p[1]->time); if(paf->flag & PAF_BSPLINE) set_four_ipo(dt, t, KEY_BSPLINE); else set_four_ipo(dt, t, KEY_CARDINAL); vec[0]= t[0]*p[0]->co[0] + t[1]*p[1]->co[0] + t[2]*p[2]->co[0] + t[3]*p[3]->co[0]; vec[1]= t[0]*p[0]->co[1] + t[1]*p[1]->co[1] + t[2]*p[2]->co[1] + t[3]*p[3]->co[1]; vec[2]= t[0]*p[0]->co[2] + t[1]*p[1]->co[2] + t[2]*p[2]->co[2] + t[3]*p[3]->co[2]; } static void particle_tex(MTex *mtex, PartEff *paf, float *co, float *no) { float tin, tr, tg, tb, ta; float old; externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); if(paf->texmap==PAF_TEXINT) { tin*= paf->texfac; no[0]+= tin*paf->defvec[0]; no[1]+= tin*paf->defvec[1]; no[2]+= tin*paf->defvec[2]; } else if(paf->texmap==PAF_TEXRGB) { no[0]+= (tr-0.5f)*paf->texfac; no[1]+= (tg-0.5f)*paf->texfac; no[2]+= (tb-0.5f)*paf->texfac; } else { /* PAF_TEXGRAD */ old= tin; co[0]+= paf->nabla; externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); no[0]+= (old-tin)*paf->texfac; co[0]-= paf->nabla; co[1]+= paf->nabla; externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); no[1]+= (old-tin)*paf->texfac; co[1]-= paf->nabla; co[2]+= paf->nabla; externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); no[2]+= (old-tin)*paf->texfac; } } /* -------------------------- Effectors ------------------ */ static void add_to_effectorcache(ListBase *lb, Object *ob, Object *obsrc) { pEffectorCache *ec; PartDeflect *pd= ob->pd; if(pd->forcefield == PFIELD_GUIDE) { if(ob->type==OB_CURVE && obsrc->type==OB_MESH) { /* guides only do mesh particles */ 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) { ec= MEM_callocN(sizeof(pEffectorCache), "effector cache"); ec->ob= ob; BLI_addtail(lb, ec); } } } } else if(pd->forcefield) { ec= MEM_callocN(sizeof(pEffectorCache), "effector cache"); ec->ob= ob; BLI_addtail(lb, ec); } } /* returns ListBase handle with objects taking part in the effecting */ ListBase *pdInitEffectors(Object *obsrc, Group *group) { static ListBase listb={NULL, NULL}; pEffectorCache *ec; Base *base; unsigned int layer= obsrc->lay; if(group) { GroupObject *go; for(go= group->gobject.first; go; go= go->next) { if( (go->ob->lay & layer) && go->ob->pd && go->ob!=obsrc) { add_to_effectorcache(&listb, go->ob, obsrc); } } } else { for(base = G.scene->base.first; base; base= base->next) { if( (base->lay & layer) && base->object->pd && base->object!=obsrc) { add_to_effectorcache(&listb, base->object, obsrc); } } } /* make a full copy */ for(ec= listb.first; ec; ec= ec->next) { ec->obcopy= *(ec->ob); } if(listb.first) return &listb; return NULL; } void pdEndEffectors(ListBase *lb) { if(lb) { pEffectorCache *ec; /* restore full copy */ for(ec= lb->first; ec; ec= ec->next) *(ec->ob)= ec->obcopy; BLI_freelistN(lb); } } /* local for this c file, only for guides now */ static void precalc_effectors(Object *ob, PartEff *paf, Particle *pa, ListBase *lb) { pEffectorCache *ec; for(ec= lb->first; ec; ec= ec->next) { PartDeflect *pd= ec->ob->pd; ec->oldspeed[0]= ec->oldspeed[1]= ec->oldspeed[2]= 0.0f; if(pd->forcefield==PFIELD_GUIDE && ec->ob->type==OB_CURVE) { float vec[4], dir[3]; if(!(paf->flag & PAF_STATIC)) where_is_object_time(ec->ob, pa->time); /* scale corrects speed vector to curve size */ if(paf->totkey>1) ec->scale= (paf->totkey-1)/pa->lifetime; else ec->scale= 1.0f; /* time_scale is for random life */ if(pa->lifetime>paf->lifetime) ec->time_scale= paf->lifetime/pa->lifetime; else ec->time_scale= pa->lifetime/paf->lifetime; /* distance of first path point to particle origin */ where_on_path(ec->ob, 0.0f, vec, dir); VECCOPY(ec->oldloc, vec); /* store local coord for differences */ Mat4MulVecfl(ec->ob->obmat, vec); /* for static we need to move to global space */ if(paf->flag & PAF_STATIC) { VECCOPY(dir, pa->co); Mat4MulVecfl(ob->obmat, dir); ec->guide_dist= VecLenf(vec, dir); } else ec->guide_dist= VecLenf(vec, pa->co); } } } /* -------- pdDoEffectors() -------- generic force/speed system, now used for particles and softbodies lb = listbase with objects that take part in effecting opco = global coord, as input force = force accumulator speed = actual current speed which can be altered cur_time = "external" time in frames, is constant for static particles loc_time = "local" time in frames, range <0-1> for the lifetime of particle par_layer = layer the caller is in flags = only used for softbody wind now guide = old speed of particle */ void pdDoEffectors(ListBase *lb, float *opco, float *force, float *speed, float cur_time, float loc_time, unsigned int flags) { /* Modifies the force on a particle according to its relation with the effector object Different kind of effectors include: Forcefields: Gravity-like attractor (force power is related to the inverse of distance to the power of a falloff value) Vortex fields: swirling effectors (particles rotate around Z-axis of the object. otherwise, same relation as) (Forcefields, but this is not done through a force/acceleration) Guide: particles on a path (particles are guided along a curve bezier or old nurbs) (is independent of other effectors) */ Object *ob; pEffectorCache *ec; PartDeflect *pd; float vect_to_vert[3]; float f_force, force_vec[3]; float *obloc; float distance, force_val, ffall_val; float guidecollect[3], guidedist= 0.0f; int cur_frame; guidecollect[0]= guidecollect[1]= guidecollect[2]=0.0f; /* Cycle through collected objects, get total of (1/(gravity_strength * dist^gravity_power)) */ /* Check for min distance here? (yes would be cool to add that, ton) */ for(ec = lb->first; ec; ec= ec->next) { /* object effectors were fully checked to be OK to evaluate! */ ob= ec->ob; pd= ob->pd; /* Get IPO force strength and fall off values here */ if (has_ipo_code(ob->ipo, OB_PD_FSTR)) force_val = IPO_GetFloatValue(ob->ipo, OB_PD_FSTR, cur_time); else force_val = pd->f_strength; if (has_ipo_code(ob->ipo, OB_PD_FFALL)) ffall_val = IPO_GetFloatValue(ob->ipo, OB_PD_FFALL, cur_time); else ffall_val = pd->f_power; /* Need to set r.cfra for paths (investigate, ton) (uses ob->ctime now, ton) */ if(ob->ctime!=cur_time) { cur_frame = G.scene->r.cfra; G.scene->r.cfra = (int)cur_time; where_is_object_time(ob, cur_time); G.scene->r.cfra = cur_frame; } /* use center of object for distance calculus */ obloc= ob->obmat[3]; VECSUB(vect_to_vert, obloc, opco); distance = VecLength(vect_to_vert); if((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist && pd->forcefield != PFIELD_GUIDE) ; /* don't do anything */ else if(pd->forcefield == PFIELD_WIND) { VECCOPY(force_vec, ob->obmat[2]); /* wind works harder perpendicular to normal, would be nice for softbody later (ton) */ /* Limit minimum distance to vertex so that */ /* the force is not too big */ if (distance < 0.001) distance = 0.001f; f_force = (force_val)*(1/(1000 * (float)pow((double)distance, (double)ffall_val))); /* this option for softbody only */ if(flags && PE_WIND_AS_SPEED){ speed[0] -= (force_vec[0] * f_force ); speed[1] -= (force_vec[1] * f_force ); speed[2] -= (force_vec[2] * f_force ); } else{ force[0] += force_vec[0]*f_force; force[1] += force_vec[1]*f_force; force[2] += force_vec[2]*f_force; } } else if(pd->forcefield == PFIELD_FORCE) { /* only use center of object */ obloc= ob->obmat[3]; /* Now calculate the gravitational force */ VECSUB(vect_to_vert, obloc, opco); distance = VecLength(vect_to_vert); /* Limit minimum distance to vertex so that */ /* the force is not too big */ if (distance < 0.001) distance = 0.001f; f_force = (force_val)*(1.0/(1000.0 * (float)pow((double)distance, (double)ffall_val))); force[0] += (vect_to_vert[0] * f_force ); force[1] += (vect_to_vert[1] * f_force ); force[2] += (vect_to_vert[2] * f_force ); } else if(pd->forcefield == PFIELD_VORTEX) { float vortexvec[3]; /* only use center of object */ obloc= ob->obmat[3]; /* Now calculate the vortex force */ VECSUB(vect_to_vert, obloc, opco); distance = VecLength(vect_to_vert); Crossf(force_vec, ob->obmat[2], vect_to_vert); Normalize(force_vec); /* Limit minimum distance to vertex so that */ /* the force is not too big */ if (distance < 0.001) distance = 0.001f; f_force = (force_val)*(1.0/(100.0 * (float)pow((double)distance, (double)ffall_val))); vortexvec[0]= -(force_vec[0] * f_force ); vortexvec[1]= -(force_vec[1] * f_force ); vortexvec[2]= -(force_vec[2] * f_force ); /* this option for softbody only */ if(flags &&PE_WIND_AS_SPEED) { speed[0]+= vortexvec[0]; speed[1]+= vortexvec[1]; speed[2]+= vortexvec[2]; } else { /* since vortex alters the speed, we have to correct for the previous vortex result */ speed[0]+= vortexvec[0] - ec->oldspeed[0]; speed[1]+= vortexvec[1] - ec->oldspeed[1]; speed[2]+= vortexvec[2] - ec->oldspeed[2]; VECCOPY(ec->oldspeed, vortexvec); } } else if(pd->forcefield == PFIELD_GUIDE) { float guidevec[4], guidedir[3]; float mindist= force_val; /* force_val is actually mindist in the UI */ distance= ec->guide_dist; /* WARNING: bails out with continue here */ if((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist) continue; /* calculate contribution factor for this guide */ if(distance<=mindist) f_force= 1.0f; else if(pd->flag & PFIELD_USEMAX) { if(distance>pd->maxdist || mindist>=pd->maxdist) f_force= 0.0f; else { f_force= 1.0f - (distance-mindist)/(pd->maxdist - mindist); if(ffall_val!=0.0f) f_force = (float)pow(f_force, ffall_val+1.0); } } else { f_force= 1.0f/(1.0f + distance-mindist); if(ffall_val!=0.0f) f_force = (float)pow(f_force, ffall_val+1.0); } /* now derive path point from loc_time */ if(pd->flag & PFIELD_GUIDE_PATH_ADD) where_on_path(ob, f_force*loc_time*ec->time_scale, guidevec, guidedir); else where_on_path(ob, loc_time*ec->time_scale, guidevec, guidedir); VECSUB(guidedir, guidevec, ec->oldloc); VECCOPY(ec->oldloc, guidevec); Mat4Mul3Vecfl(ob->obmat, guidedir); VecMulf(guidedir, ec->scale); /* correction for lifetime and speed */ /* we subtract the speed we gave it previous step */ VECCOPY(guidevec, guidedir); VECSUB(guidedir, guidedir, ec->oldspeed); VECCOPY(ec->oldspeed, guidevec); /* if it fully contributes, we stop */ if(f_force==1.0) { VECCOPY(guidecollect, guidedir); guidedist= 1.0f; break; } else if(guidedist<1.0f) { VecMulf(guidedir, f_force); VECADD(guidecollect, guidecollect, guidedir); guidedist += f_force; } } } /* all guides are accumulated here */ if(guidedist!=0.0f) { if(guidedist!=1.0f) VecMulf(guidecollect, 1.0f/guidedist); VECADD(speed, speed, guidecollect); } } static void cache_object_vertices(Object *ob) { Mesh *me; MVert *mvert; float *fp; int a; me= ob->data; if(me->totvert==0) return; fp= ob->sumohandle= MEM_mallocN(3*sizeof(float)*me->totvert, "cache particles"); mvert= me->mvert; a= me->totvert; while(a--) { VECCOPY(fp, mvert->co); Mat4MulVecfl(ob->obmat, fp); mvert++; fp+= 3; } } static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3], float npno[3], float life, float force[3], int def_depth, float cur_time, unsigned int par_layer, int *last_object, int *last_face, int *same_face) { /* Particle deflection code */ /* The code is in two sections: the first part checks whether a particle has */ /* intersected a face of a deflector mesh, given its old and new co-ords, opco and npco */ /* and which face it hit first */ /* The second part calculates the new co-ordinates given that collision and updates */ /* the new co-ordinates accordingly */ Base *base; Object *ob, *deflection_object = NULL; Mesh *def_mesh; MFace *mface, *deflection_face = NULL; float *v1, *v2, *v3, *v4, *vcache=NULL; float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3]; float dv1[3] = {0}, dv2[3] = {0}, dv3[3] = {0}; float vect_to_int[3], refl_vel[3]; float d_intersect_co[3], d_intersect_vect[3], d_nvect[3], d_i_co_above[3]; float forcec[3]; float k_point3, dist_to_plane; float first_dist, ref_plane_mag; float dk_plane=0, dk_point1=0; float icalctop, icalcbot, n_mag; float mag_iv, x_m,y_m,z_m; float damping, perm_thresh; float perm_val, rdamp_val; int a, deflected=0, deflected_now=0; float t,t2, min_t; float mat[3][3], obloc[3] = {0}; int cur_frame; float time_before, time_after; float force_mag_norm; int d_object=0, d_face=0, ds_object=0, ds_face=0; first_dist = 200000; min_t = 200000; /* The first part of the code, finding the first intersected face*/ base= G.scene->base.first; while (base) { /*Only proceed for mesh object in same layer */ if(base->object->type==OB_MESH && (base->lay & par_layer)) { ob= base->object; /* only with deflecting set */ if(ob->pd && ob->pd->deflect) { def_mesh= ob->data; d_object = d_object + 1; d_face = d_face + 1; mface= def_mesh->mface; a = def_mesh->totface; if(ob->parent==NULL && ob->ipo==NULL) { // static if(ob->sumohandle==NULL) cache_object_vertices(ob); vcache= ob->sumohandle; } else { /*Find out where the object is at this time*/ cur_frame = G.scene->r.cfra; G.scene->r.cfra = (int)cur_time; where_is_object_time(ob, cur_time); G.scene->r.cfra = cur_frame; /*Pass the values from ob->obmat to mat*/ /*and the location values to obloc */ Mat3CpyMat4(mat,ob->obmat); obloc[0] = ob->obmat[3][0]; obloc[1] = ob->obmat[3][1]; obloc[2] = ob->obmat[3][2]; vcache= NULL; } while (a--) { if(vcache) { v1= vcache+ 3*(mface->v1); VECCOPY(nv1, v1); v1= vcache+ 3*(mface->v2); VECCOPY(nv2, v1); v1= vcache+ 3*(mface->v3); VECCOPY(nv3, v1); v1= vcache+ 3*(mface->v4); VECCOPY(nv4, v1); } else { /* Calculate the global co-ordinates of the vertices*/ v1= (def_mesh->mvert+(mface->v1))->co; v2= (def_mesh->mvert+(mface->v2))->co; v3= (def_mesh->mvert+(mface->v3))->co; v4= (def_mesh->mvert+(mface->v4))->co; VECCOPY(nv1, v1); VECCOPY(nv2, v2); VECCOPY(nv3, v3); VECCOPY(nv4, v4); /*Apply the objects deformation matrix*/ Mat3MulVecfl(mat, nv1); Mat3MulVecfl(mat, nv2); Mat3MulVecfl(mat, nv3); Mat3MulVecfl(mat, nv4); VECADD(nv1, nv1, obloc); VECADD(nv2, nv2, obloc); VECADD(nv3, nv3, obloc); VECADD(nv4, nv4, obloc); } deflected_now = 0; // 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, NULL) ) { if (t < min_t) { deflected = 1; deflected_now = 1; } } // else if (mface->v4 && (t>=0.0 && t<=1.0)) { // no, you can't skip testing the other triangle // 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, NULL) ) { if (t2 < min_t) { deflected = 1; deflected_now = 2; } } } if ((deflected_now > 0) && ((t < min_t) ||(t2 < min_t))) { min_t = t; ds_object = d_object; ds_face = d_face; deflection_object = ob; deflection_face = mface; if (deflected_now==1) { min_t = t; VECCOPY(dv1, nv1); VECCOPY(dv2, nv2); VECCOPY(dv3, nv3); } else { min_t = t2; VECCOPY(dv1, nv1); VECCOPY(dv2, nv3); VECCOPY(dv3, nv4); } } mface++; } } } base = base->next; } /* Here's the point to do the permeability calculation */ /* Set deflected to 0 if a random number is below the value */ /* Get the permeability IPO here*/ if (deflected) { if (has_ipo_code(deflection_object->ipo, OB_PD_PERM)) perm_val = IPO_GetFloatValue(deflection_object->ipo, OB_PD_PERM, cur_time); else perm_val = deflection_object->pd->pdef_perm; perm_thresh = rng_getFloat(rng) - perm_val; if (perm_thresh < 0 ) { deflected = 0; } } /* Now for the second part of the deflection code - work out the new speed */ /* and position of the particle if a collision occurred */ if (deflected) { VECSUB(edge1, dv1, dv2); VECSUB(edge2, dv3, dv2); Crossf(d_nvect, edge2, edge1); n_mag = Normalize(d_nvect); dk_plane = INPR(d_nvect, nv1); dk_point1 = INPR(d_nvect,opco); VECSUB(d_intersect_vect, npco, opco); d_intersect_co[0] = opco[0] + (min_t * (npco[0] - opco[0])); d_intersect_co[1] = opco[1] + (min_t * (npco[1] - opco[1])); d_intersect_co[2] = opco[2] + (min_t * (npco[2] - opco[2])); d_i_co_above[0] = (d_intersect_co[0] + (0.001f * d_nvect[0])); d_i_co_above[1] = (d_intersect_co[1] + (0.001f * d_nvect[1])); d_i_co_above[2] = (d_intersect_co[2] + (0.001f * d_nvect[2])); mag_iv = Normalize(d_intersect_vect); VECCOPY(npco, d_intersect_co); VECSUB(vect_to_int, opco, d_intersect_co); first_dist = Normalize(vect_to_int); /* Work out the lengths of time before and after collision*/ time_before = (life*(first_dist / (mag_iv))); time_after = (life*((mag_iv - first_dist) / (mag_iv))); /* We have to recalculate what the speed would have been at the */ /* point of collision, not the key frame time */ npno[0]= opno[0] + time_before*force[0]; npno[1]= opno[1] + time_before*force[1]; npno[2]= opno[2] + time_before*force[2]; /* Reflect the speed vector in the face */ x_m = (2 * npno[0] * d_nvect[0]); y_m = (2 * npno[1] * d_nvect[1]); z_m = (2 * npno[2] * d_nvect[2]); refl_vel[0] = npno[0] - (d_nvect[0] * (x_m + y_m + z_m)); refl_vel[1] = npno[1] - (d_nvect[1] * (x_m + y_m + z_m)); refl_vel[2] = npno[2] - (d_nvect[2] * (x_m + y_m + z_m)); /*A random variation in the damping factor........ */ /*Get the IPO values for damping here*/ if (has_ipo_code(deflection_object->ipo, OB_PD_SDAMP)) damping = IPO_GetFloatValue(deflection_object->ipo, OB_PD_SDAMP, cur_time); else damping = deflection_object->pd->pdef_damp; if (has_ipo_code(deflection_object->ipo, OB_PD_RDAMP)) rdamp_val = IPO_GetFloatValue(deflection_object->ipo, OB_PD_RDAMP, cur_time); else rdamp_val = deflection_object->pd->pdef_rdamp; damping = damping + ((1.0f - damping) * rng_getFloat(rng) *rdamp_val); damping = damping * damping; ref_plane_mag = INPR(refl_vel,d_nvect); if (damping > 0.999) damping = 0.999f; /* Now add in the damping force - only damp in the direction of */ /* the faces normal vector */ npno[0] = (refl_vel[0] - (d_nvect[0] * ref_plane_mag * damping)); npno[1] = (refl_vel[1] - (d_nvect[1] * ref_plane_mag * damping)); npno[2] = (refl_vel[2] - (d_nvect[2] * ref_plane_mag * damping)); /* Now reset opno */ VECCOPY(opno,npno); VECCOPY(forcec, force); /* If the particle has bounced more than four times on the same */ /* face within this cycle (depth > 4, same face > 4 ) */ /* Then set the force to be only that component of the force */ /* in the same direction as the face normal */ /* i.e. subtract the component of the force in the direction */ /* of the face normal from the actual force */ if ((ds_object == *last_object) && (ds_face == *last_face)) { /* Increment same_face */ *same_face = *same_face + 1; if ((*same_face > 3) && (def_depth > 3)) { force_mag_norm = INPR(forcec, d_nvect); forcec[0] = forcec[0] - (d_nvect[0] * force_mag_norm); forcec[1] = forcec[1] - (d_nvect[1] * force_mag_norm); forcec[2] = forcec[2] - (d_nvect[2] * force_mag_norm); } } else *same_face = 1; *last_object = ds_object; *last_face = ds_face; /* We have the particles speed at the point of collision */ /* Now we want the particles speed at the current key frame */ npno[0]= npno[0] + time_after*forcec[0]; npno[1]= npno[1] + time_after*forcec[1]; npno[2]= npno[2] + time_after*forcec[2]; /* Now we have to recalculate pa->co for the remainder*/ /* of the time since the intersect*/ npco[0]= npco[0] + time_after*npno[0]; npco[1]= npco[1] + time_after*npno[1]; npco[2]= npco[2] + time_after*npno[2]; /* And set the old co-ordinates back to the point just above the intersection */ VECCOPY(opco, d_i_co_above); /* Finally update the time */ life = time_after; cur_time += time_before; /* The particle may have fallen through the face again by now!!*/ /* So check if the particle has changed sides of the plane compared*/ /* the co-ordinates at the last keyframe*/ /* But only do this as a last resort, if we've got to the end of the */ /* number of collisions allowed */ if (def_depth==9) { k_point3 = INPR(d_nvect,npco); if (((dk_plane > k_point3) && (dk_plane < dk_point1))||((dk_plane < k_point3) && (dk_plane > dk_point1))) { /* Yup, the pesky particle may have fallen through a hole!!! */ /* So we'll cheat a bit and move the particle along the normal vector */ /* until it's just the other side of the plane */ icalctop = (dk_plane - d_nvect[0]*npco[0] - d_nvect[1]*npco[1] - d_nvect[2]*npco[2]); icalcbot = (d_nvect[0]*d_nvect[0] + d_nvect[1]*d_nvect[1] + d_nvect[2]*d_nvect[2]); dist_to_plane = icalctop / icalcbot; /* Now just increase the distance a little to place */ /* the point the other side of the plane */ dist_to_plane *= 1.1f; npco[0]= npco[0] + (dist_to_plane * d_nvect[0]); npco[1]= npco[1] + (dist_to_plane * d_nvect[1]); npco[2]= npco[2] + (dist_to_plane * d_nvect[2]); } } } return deflected; } /* rng= random number generator ob = object that spawns the particles depth = for fireworks nr = index nr of current particle paf = the particle system part = current particle force = force vector deform = flag to indicate lattice deform */ static void make_particle_keys(RNG *rng, Object *ob, int depth, int nr, PartEff *paf, Particle *part, float *force, int deform, MTex *mtex, ListBase *effectorbase) { Particle *pa, *opa = NULL; float damp, deltalife, life; float cur_time, maxspeed= paf->maxlen/(float)paf->totkey; float opco[3], opno[3], npco[3], npno[3], new_force[3], new_speed[3]; int b, rt1, rt2, deflected, deflection, finish_defs, def_count; int last_ob, last_fc, same_fc; damp= 1.0f-paf->damp; pa= part; /* start speed: random */ if(paf->randfac!=0.0) { pa->no[0]+= paf->randfac*(rng_getFloat(rng) - 0.5f); pa->no[1]+= paf->randfac*(rng_getFloat(rng) - 0.5f); pa->no[2]+= paf->randfac*(rng_getFloat(rng) - 0.5f); } /* start speed: texture */ if(mtex && paf->texfac!=0.0) { particle_tex(mtex, paf, pa->co, pa->no); } /* effectors here? */ if(effectorbase) precalc_effectors(ob, paf, pa, effectorbase); if(paf->totkey>1) deltalife= pa->lifetime/(paf->totkey-1); else deltalife= pa->lifetime; /* longer lifetime results in longer distance covered */ VecMulf(pa->no, deltalife); opa= pa; pa++; for(b=1; btotkey; b++) { /* new time */ pa->time= opa->time+deltalife; cur_time = pa->time; /* set initial variables */ VECCOPY(opco, opa->co); VECCOPY(new_force, force); VECCOPY(new_speed, opa->no); VecMulf(new_speed, 1.0f/deltalife); //new_speed[0] = new_speed[1] = new_speed[2] = 0.0f; /* handle differences between static (local coords, fixed frame) and dynamic */ if(effectorbase) { float loc_time= ((float)b)/(float)(paf->totkey-1); if(paf->flag & PAF_STATIC) { float opco1[3], new_force1[3]; /* move co and force to global coords */ VECCOPY(opco1, opco); Mat4MulVecfl(ob->obmat, opco1); VECCOPY(new_force1, new_force); Mat4Mul3Vecfl(ob->obmat, new_force1); Mat4Mul3Vecfl(ob->obmat, new_speed); cur_time = G.scene->r.cfra; /* force fields */ pdDoEffectors(effectorbase, opco1, new_force1, new_speed, cur_time, loc_time, 0); /* move co, force and newspeed back to local */ VECCOPY(opco, opco1); Mat4MulVecfl(ob->imat, opco); VECCOPY(new_force, new_force1); Mat4Mul3Vecfl(ob->imat, new_force); Mat4Mul3Vecfl(ob->imat, new_speed); } else { /* force fields */ pdDoEffectors(effectorbase, opco, new_force, new_speed, cur_time, loc_time, 0); } } /* new speed */ pa->no[0]= deltalife * (new_speed[0] + new_force[0]); pa->no[1]= deltalife * (new_speed[1] + new_force[1]); pa->no[2]= deltalife * (new_speed[2] + new_force[2]); /* speed limitor */ if((paf->flag & PAF_STATIC) && maxspeed!=0.0f) { float len= VecLength(pa->no); if(len > maxspeed) VecMulf(pa->no, maxspeed/len); } /* new location */ pa->co[0]= opa->co[0] + pa->no[0]; pa->co[1]= opa->co[1] + pa->no[1]; pa->co[2]= opa->co[2] + pa->no[2]; /* Particle deflection code */ if((paf->flag & PAF_STATIC)==0) { deflection = 0; finish_defs = 1; def_count = 0; VECCOPY(opno, opa->no); VECCOPY(npco, pa->co); VECCOPY(npno, pa->no); life = deltalife; cur_time -= deltalife; last_ob = -1; last_fc = -1; same_fc = 0; /* First call the particle deflection check for the particle moving */ /* between the old co-ordinates and the new co-ordinates */ /* If a deflection occurs, call the code again, this time between the */ /* intersection point and the updated new co-ordinates */ /* Bail out if we've done the calculation 10 times - this seems ok */ /* for most scenes I've tested */ while (finish_defs) { deflected = pdDoDeflection(rng, opco, npco, opno, npno, life, new_force, def_count, cur_time, ob->lay, &last_ob, &last_fc, &same_fc); if (deflected) { def_count = def_count + 1; deflection = 1; if (def_count==10) finish_defs = 0; } else { finish_defs = 0; } } /* Only update the particle positions and speed if we had a deflection */ if (deflection) { pa->co[0] = npco[0]; pa->co[1] = npco[1]; pa->co[2] = npco[2]; pa->no[0] = npno[0]; pa->no[1] = npno[1]; pa->no[2] = npno[2]; } } /* speed: texture */ if(mtex && paf->texfac!=0.0) { particle_tex(mtex, paf, pa->co, pa->no); } if(damp!=1.0) { pa->no[0]*= damp; pa->no[1]*= damp; pa->no[2]*= damp; } opa= pa; pa++; /* opa is used later on too! */ } if(deform) { /* deform all keys */ pa= part; b= paf->totkey; while(b--) { calc_latt_deform(pa->co, 1.0f); pa++; } } /* the big multiplication */ if(depthmult[depth]!=0.0) { /* new 'child' emerges from an average 'mult' part from the particles */ damp = (float)nr; rt1= (int)(damp*paf->mult[depth]); rt2= (int)((damp+1.0)*paf->mult[depth]); if(rt1!=rt2) { for(b=0; bchild[depth]; b++) { pa= new_particle(paf); *pa= *opa; pa->lifetime= paf->life[depth]; if(paf->randlife!=0.0) { pa->lifetime*= 1.0f + paf->randlife*(rng_getFloat(rng) - 0.5f); } pa->mat_nr= paf->mat[depth]; make_particle_keys(rng, ob, depth+1, b, paf, pa, force, deform, mtex, effectorbase); } } } } static void init_mv_jit(float *jit, int num, int seed2) { 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; iseed); int i, tot; if(paf->flag & PAF_TRAND) tot= partnr; else tot= JIT_RAND; /* arbitrary... allows JIT_RAND times more particles in a face for jittered distro */ trands= MEM_callocN(2+2*tot*sizeof(float), "trands"); for(i=0; iflag & PAF_TRAND)==0) { jitlevel= paf->userjit; if(jitlevel == 0) { jitlevel= partnr/subnr; if(paf->flag & PAF_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, paf->seed); BLI_array_randomize(jit, 2*sizeof(float), jitlevel, paf->seed); /* for custom jit or even distribution */ } return; } if(paf->flag & PAF_TRAND) { u= trands[2*partnr]; v= trands[2*partnr+1]; } else { /* jittered distribution gets fixed random offset */ if(subnr>=jitlevel) { int jitrand= (subnr/jitlevel) % JIT_RAND; subnr %= jitlevel; u= jit[2*subnr] + trands[2*jitrand]; v= jit[2*subnr+1] + trands[2*jitrand+1]; if(u > 1.0f) u-= 1.0f; if(v > 1.0f) v-= 1.0f; } else { u= jit[2*subnr]; v= jit[2*subnr+1]; } } v1= (noco+(mface->v1))->co; v2= (noco+(mface->v2))->co; v3= (noco+(mface->v3))->co; n1= (noco+(mface->v1))->no; n2= (noco+(mface->v2))->no; n3= (noco+(mface->v3))->no; if(mface->v4) { float uv= u*v; float muv= (1.0f-u)*(v); float umv= (u)*(1.0f-v); float mumv= (1.0f-u)*(1.0f-v); v4= (noco+(mface->v4))->co; n4= (noco+(mface->v4))->no; co[0]= mumv*v1[0] + muv*v2[0] + uv*v3[0] + umv*v4[0]; co[1]= mumv*v1[1] + muv*v2[1] + uv*v3[1] + umv*v4[1]; co[2]= mumv*v1[2] + muv*v2[2] + uv*v3[2] + umv*v4[2]; no[0]= mumv*n1[0] + muv*n2[0] + uv*n3[0] + umv*n4[0]; no[1]= mumv*n1[1] + muv*n2[1] + uv*n3[1] + umv*n4[1]; no[2]= mumv*n1[2] + muv*n2[2] + uv*n3[2] + umv*n4[2]; } else { /* mirror triangle uv coordinates when on other side */ if(u + v > 1.0f) { u= 1.0f-u; v= 1.0f-v; } co[0]= v1[0] + u*(v3[0]-v1[0]) + v*(v2[0]-v1[0]); co[1]= v1[1] + u*(v3[1]-v1[1]) + v*(v2[1]-v1[1]); co[2]= v1[2] + u*(v3[2]-v1[2]) + v*(v2[2]-v1[2]); no[0]= n1[0] + u*(n3[0]-n1[0]) + v*(n2[0]-n1[0]); no[1]= n1[1] + u*(n3[1]-n1[1]) + v*(n2[1]-n1[1]); no[2]= n1[2] + u*(n3[2]-n1[2]) + v*(n2[2]-n1[2]); } } /* Gets a MDeformVert's weight in group (0 if not in group) */ /* note; this call could be in mesh.c or deform.c, but OK... it's in armature.c too! (ton) */ 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; } /* Gets a faces average weight in a group, helper for below, face and weights are always set */ static float face_weight(MFace *face, float *weights) { float tweight; tweight = weights[face->v1] + weights[face->v2] + weights[face->v3]; if(face->v4) { tweight += weights[face->v4]; tweight /= 4.0; } else { tweight /= 3.0; } return tweight; } /* helper function for build_particle_system() */ static void make_weight_tables(PartEff *paf, Mesh *me, int totpart, VeNoCo *vertlist, int totvert, MFace *facelist, int totface, float **vweights, float **fweights) { MFace *mface; float *foweights=NULL, *voweights=NULL; float totvweight=0.0f, totfweight=0.0f; int a; if((paf->flag & PAF_FACE)==0) totface= 0; /* collect emitting vertices & faces if vert groups used */ if(paf->vertgroup && me->dvert) { /* allocate weights array for all vertices, also for lookup of faces later on. note it's a malloc */ *vweights= voweights= MEM_mallocN( totvert*sizeof(float), "pafvoweights" ); totvweight= 0.0f; for(a=0; advert+a, paf->vertgroup-1); totvweight+= voweights[a]; } if(totface) { /* allocate weights array for faces, note it's a malloc */ *fweights= foweights= MEM_mallocN(totface*sizeof(float), "paffoweights" ); for(a=0, mface=facelist; aflag & PAF_EDISTR)) { float maxfarea= 0.0f, curfarea; /* two cases for area distro, second case we already have group weights */ if(foweights==NULL) { /* allocate weights array for faces, note it's a malloc */ *fweights= foweights= MEM_mallocN(totface*sizeof(float), "paffoweights" ); for(a=0, mface=facelist; av4) curfarea= AreaQ3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co, vertlist[mface->v4].co); else curfarea= AreaT3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co); if(curfarea>maxfarea) maxfarea = curfarea; foweights[a]= curfarea; } } else { for(a=0, mface=facelist; av4) curfarea= AreaQ3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co, vertlist[mface->v4].co); else curfarea= AreaT3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co); if(curfarea>maxfarea) maxfarea = curfarea; foweights[a]*= curfarea; } } } /* normalize weights for max face area, calculate tot */ if(maxfarea!=0.0f) { maxfarea= 1.0f/maxfarea; for(a=0; a< totface; a++) { if(foweights[a]!=0.0) { foweights[a] *= maxfarea; totfweight+= foweights[a]; } } } } else if(foweights) { /* only add totfweight value */ for(a=0; a< totface; a++) { if(foweights[a]!=0.0) { totfweight+= foweights[a]; } } } /* if weight arrays, we turn these arrays into the amount of particles */ if(totvert && voweights) { float mult= (float)totpart/totvweight; for(a=0; a< totvert; a++) { if(voweights[a]!=0.0) voweights[a] *= mult; } } if(totface && foweights) { float mult= (float)totpart/totfweight; for(a=0; a< totface; a++) { if(foweights[a]!=0.0) foweights[a] *= mult; } } } /* helper function for build_particle_system() */ static void make_length_tables(PartEff *paf, Mesh *me, int totvert, MFace *facelist, int totface, float **vlengths, float **flengths) { MFace *mface; float *folengths=NULL, *volengths=NULL; int a; if((paf->flag & PAF_FACE)==0) totface= 0; /* collect emitting vertices & faces if vert groups used */ if(paf->vertgroup_v && me->dvert) { /* allocate lengths array for all vertices, also for lookup of faces later on. note it's a malloc */ *vlengths= volengths= MEM_mallocN( totvert*sizeof(float), "pafvolengths" ); for(a=0; advert+a, paf->vertgroup_v-1); } if(totface) { /* allocate lengths array for faces, note it's a malloc */ *flengths= folengths= MEM_mallocN(totface*sizeof(float), "paffolengths" ); for(a=0, mface=facelist; aid.idnew! */ /* error: this function changes ob->recalc of other objects... */ static pMatrixCache *cache_object_matrices(Object *ob, int start, int end) { pMatrixCache *mcache, *mc; Group *group= NULL; Object *obcopy; Base *base; float framelenold, cfrao, sfo; /* object can be linked in group... stupid exception */ if(NULL==object_in_scene(ob, G.scene)) group= find_group(ob); mcache= mc= MEM_mallocN( (end-start+1)*sizeof(pMatrixCache), "ob matrix cache"); framelenold= G.scene->r.framelen; G.scene->r.framelen= 1.0f; cfrao= G.scene->r.cfra; sfo= ob->sf; ob->sf= 0.0f; /* clear storage, copy recalc tag (bad loop) */ for(obcopy= G.main->object.first; obcopy; obcopy= obcopy->id.next) { obcopy->id.newid= NULL; obcopy->recalco= obcopy->recalc; obcopy->recalc= 0; } /* all objects get tagged recalc that influence this object (does group too) */ /* note that recalco has the real recalc tags, set by callers of this function */ ob->recalc |= OB_RECALC_OB; /* make sure a recalc gets flushed */ DAG_object_update_flags(G.scene, ob, -1); for(G.scene->r.cfra= start; G.scene->r.cfra<=end; G.scene->r.cfra++, mc++) { if(group) { GroupObject *go; for(go= group->gobject.first; go; go= go->next) { if(go->ob->recalc) { where_is_object(go->ob); do_ob_key(go->ob); if(go->ob->type==OB_ARMATURE) { do_all_pose_actions(go->ob); // only does this object actions where_is_pose(go->ob); } } } } else { for(base= G.scene->base.first; base; base= base->next) { if(base->object->recalc) { if(base->object->id.newid==NULL) base->object->id.newid= MEM_dupallocN(base->object); where_is_object(base->object); do_ob_key(base->object); if(base->object->type==OB_ARMATURE) { do_all_pose_actions(base->object); // only does this object actions where_is_pose(base->object); } } } } Mat4CpyMat4(mc->obmat, ob->obmat); Mat4Invert(ob->imat, ob->obmat); Mat3CpyMat4(mc->imat, ob->imat); Mat3Transp(mc->imat); } /* restore */ G.scene->r.cfra= cfrao; G.scene->r.framelen= framelenold; ob->sf= sfo; if(group) { GroupObject *go; for(go= group->gobject.first; go; go= go->next) { if(go->ob->recalc) { where_is_object(go->ob); do_ob_key(go->ob); if(go->ob->type==OB_ARMATURE) { do_all_pose_actions(go->ob); // only does this object actions where_is_pose(go->ob); } } } } else { for(base= G.scene->base.first; base; base= base->next) { if(base->object->recalc) { if(base->object->id.newid) { obcopy= (Object *)base->object->id.newid; *(base->object) = *(obcopy); MEM_freeN(obcopy); base->object->id.newid= NULL; } do_ob_key(base->object); if(base->object->type==OB_ARMATURE) { do_all_pose_actions(base->object); // only does this object actions where_is_pose(base->object); } } } } /* copy recalc tag (bad loop) */ for(obcopy= G.main->object.first; obcopy; obcopy= obcopy->id.next) obcopy->recalc= obcopy->recalco; return mcache; } /* for fluidsim win32 debug messages */ #if defined(WIN32) && (!(defined snprintf)) #define snprintf _snprintf #endif /* main particle building function one day particles should become dynamic (realtime) with the current method as a 'bake' (ton) */ void build_particle_system(Object *ob) { RNG *rng; PartEff *paf; Particle *pa; Mesh *me; Base *base; MTex *mtexmove=0, *mtextime=0; Material *ma; MFace *facelist= NULL; pMatrixCache *mcache=NULL, *mcnow, *mcprev; ListBase *effectorbase; VeNoCo *vertexcosnos; double startseconds= PIL_check_seconds_timer(); float ftime, dtime, force[3], vec[3], fac, co[3], no[3]; float *voweights= NULL, *foweights= NULL, maxw=1.0f; float *volengths= NULL, *folengths= NULL; int deform=0, a, totpart, paf_sta, paf_end; int waitcursor_set= 0, totvert, totface, curface, curvert; #ifndef DISABLE_ELBEEM int readMask, activeParts, fileParts; #endif /* return conditions */ if(ob->type!=OB_MESH) return; me= ob->data; paf= give_parteff(ob); if(paf==NULL) return; if(G.rendering==0 && paf->disp==0) return; if(paf->keys) MEM_freeN(paf->keys); /* free as early as possible, for returns */ paf->keys= NULL; //printf("build particles\n"); /* fluid sim particle import handling, actual loading of particles from file */ #ifndef DISABLE_ELBEEM if( (1) && (ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && // broken, disabled for now! (ob->fluidsimSettings) && (ob->fluidsimSettings->type == OB_FLUIDSIM_PARTICLE)) { char *suffix = "fluidsurface_particles_#"; char *suffix2 = ".gz"; char filename[256]; char debugStrBuffer[256]; int curFrame = G.scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading int j, numFileParts; gzFile gzf; float vel[3]; if(ob==G.obedit) { // off... paf->totpart = 0; // 1 or 0? return; } // ok, start loading strcpy(filename, ob->fluidsimSettings->surfdataPath); strcat(filename, suffix); BLI_convertstringcode(filename, G.sce, curFrame); // fixed #frame-no strcat(filename, suffix2); gzf = gzopen(filename, "rb"); if (!gzf) { snprintf(debugStrBuffer,256,"readFsPartData::error - Unable to open file for reading '%s' \n", filename); //elbeemDebugOut(debugStrBuffer); paf->totpart = 0; return; } gzread(gzf, &totpart, sizeof(totpart)); numFileParts = totpart; totpart = (G.rendering)?totpart:(paf->disp*totpart)/100; paf->totpart= totpart; paf->totkey= 1; /* initialize particles */ new_particle(paf); ftime = 0.0; // unused... // set up reading mask readMask = ob->fluidsimSettings->typeFlags; activeParts=0; fileParts=0; for(a=0; atime= ftime; pa->lifetime= ftime + 10000.; // add large number to make sure they are displayed, G.scene->r.efra +1.0; pa->co[0] = 0.0; pa->co[1] = pa->co[2] = 1.0*(float)a / (float)totpart; pa->no[0] = pa->no[1] = pa->no[2] = 0.0; pa->mat_nr= paf->omat; gzread(gzf, &convertSize, sizeof( float )); // convert range of 1.0-10.0 to shorts 1000-10000) shsize = (short)(convertSize*1000.0); pa->rt = shsize; for(j=0; j<3; j++) { float wrf; gzread(gzf, &wrf, sizeof( wrf )); pa->co[j] = wrf; //fprintf(stderr,"Rj%d ",j); } for(j=0; j<3; j++) { float wrf; gzread(gzf, &wrf, sizeof( wrf )); vel[j] = wrf; } //if(a<25) fprintf(stderr,"FSPARTICLE debug set %s , a%d = %f,%f,%f , life=%f \n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime ); } else { // skip... for(j=0; j<2*3+1; j++) { float wrf; gzread(gzf, &wrf, sizeof( wrf )); } } fileParts++; } gzclose( gzf ); totpart = paf->totpart = activeParts; snprintf(debugStrBuffer,256,"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d \n", paf->totpart,activeParts,fileParts,readMask); elbeemDebugOut(debugStrBuffer); return; } // fluid sim particles done #endif // DISABLE_ELBEEM if(paf->end < paf->sta) return; if( (paf->flag & PAF_OFACE) && (paf->flag & PAF_FACE)==0) return; if(me->totvert==0) return; if(ob==G.obedit) return; totpart= (G.rendering)?paf->totpart:(paf->disp*paf->totpart)/100; if(totpart==0) return; /* No returns after this line! */ /* material */ ma= give_current_material(ob, paf->omat); if(ma) { if(paf->speedtex) mtexmove= ma->mtex[paf->speedtex-1]; mtextime= ma->mtex[paf->timetex-1]; } disable_speed_curve(1); /* check this... */ /* initialize particles */ new_particle(paf); /* reset deflector cache, sumohandle is free, but its still sorta abuse... (ton) */ for(base= G.scene->base.first; base; base= base->next) base->object->sumohandle= NULL; /* all object positions from start to end */ paf_sta= (int)floor(paf->sta); paf_end= (int)ceil(paf->end); if((paf->flag & PAF_STATIC)==0) mcache= cache_object_matrices(ob, paf_sta, paf_end); /* mult generations? */ for(a=0; amult[a]!=0.0) { /* interesting formula! this way after 'x' generations the total is paf->totpart */ totpart= (int)(totpart / (1.0+paf->mult[a]*paf->child[a])); } else break; } /* for static particles, calculate system on current frame (? ton) */ if(ma) do_mat_ipo(ma); /* matrix invert for static too */ Mat4Invert(ob->imat, ob->obmat); Mat4CpyMat4(paf->imat, ob->imat); /* used for duplicators */ /* new random generator */ rng = rng_new(paf->seed); /* otherwise it goes way too fast */ force[0]= paf->force[0]*0.05f; force[1]= paf->force[1]*0.05f; force[2]= paf->force[2]*0.05f; if( paf->flag & PAF_STATIC ) deform= 0; else { Object *parlatt= modifiers_isDeformedByLattice(ob); if(parlatt) { deform= 1; init_latt_deform(parlatt, 0); } } /* get the effectors */ effectorbase= pdInitEffectors(ob, paf->group); /* init geometry, return is 6 x float * me->totvert in size */ vertexcosnos= (VeNoCo *)mesh_get_mapped_verts_nors(ob); facelist= me->mface; totvert= me->totvert; totface= me->totface; /* if vertexweights or even distribution, it makes weight tables, also checks where it emits from */ make_weight_tables(paf, me, totpart, vertexcosnos, totvert, facelist, totface, &voweights, &foweights); /* vertexweights can define lengths too */ make_length_tables(paf, me, totvert, facelist, totface, &volengths, &folengths); /* now define where to emit from, if there are face weights we skip vertices */ if(paf->flag & PAF_OFACE) totvert= 0; if((paf->flag & PAF_FACE)==0) totface= 0; if(foweights) totvert= 0; /* initialize give_mesh_particle_coord */ if(totface) give_mesh_particle_coord(paf, vertexcosnos, facelist, totpart, totface, NULL, NULL); /* correction for face timing when using weighted average */ if(totface && foweights) { maxw= (paf->end-paf->sta)/foweights[0]; } else if(totvert && voweights) { maxw= (paf->end-paf->sta)/voweights[0]; } /* for loop below */ if (paf->flag & PAF_STATIC) { ftime = G.scene->r.cfra; dtime= 0.0f; } else { ftime= paf->sta; dtime= (paf->end - paf->sta)/(float)totpart; } curface= curvert= 0; for(a=0; a 0.5) { //XXX waitcursor(1); waitcursor_set= 1; } } pa= new_particle(paf); pa->time= ftime; /* get coordinates from faces, only when vertices set to zero */ if(totvert==0 && totface) { int curjit; /* use weight table, we have to do faces in order to be able to use jitter table... */ if(foweights) { if(foweights[curface] < 1.0f) { float remainder= 0.0f; while(remainder + foweights[curface] < 1.0f && curfaceend-paf->sta)/foweights[curface]; } if(foweights[curface]==0.0f) break; /* WARN skips here out of particle generating */ else { if(foweights[curface] >= 1.0f) /* note the >= here, this because of the < 1.0f above, it otherwise will stick to 1 face forever */ foweights[curface] -= 1.0f; curjit= (int) foweights[curface]; give_mesh_particle_coord(paf, vertexcosnos, facelist+curface, a, curjit, co, no); /* time correction to make particles appear evenly, maxw does interframe (0-1) */ pa->time= paf->sta + maxw*foweights[curface]; } } else { curface= a % totface; curjit= a/totface; give_mesh_particle_coord(paf, vertexcosnos, facelist+curface, a, curjit, co, no); } } /* get coordinates from vertices */ if(totvert) { /* use weight table */ if(voweights) { if(voweights[curvert] < 1.0f) { float remainder= 0.0f; while(remainder + voweights[curvert] < 1.0f && curvertend-paf->sta)/voweights[curvert]; } if(voweights[curvert]==0.0f) break; /* WARN skips here out of particle generating */ else { if(voweights[curvert] > 1.0f) voweights[curvert] -= 1.0f; /* time correction to make particles appear evenly */ pa->time= paf->sta + maxw*voweights[curvert]; } } else { curvert= a % totvert; if(a >= totvert && totface) totvert= 0; } VECCOPY(co, vertexcosnos[curvert].co); VECCOPY(no, vertexcosnos[curvert].no); } VECCOPY(pa->co, co); /* dynamic options */ if((paf->flag & PAF_STATIC)==0) { int cur; /* particle retiming with texture */ if(mtextime && (paf->flag2 & PAF_TEXTIME)) { float tin, tr, tg, tb, ta, orco[3]; /* calculate normalized orco */ orco[0] = (co[0]-me->loc[0])/me->size[0]; orco[1] = (co[1]-me->loc[1])/me->size[1]; orco[2] = (co[2]-me->loc[2])/me->size[2]; externtex(mtextime, orco, &tin, &tr, &tg, &tb, &ta); if(paf->flag2neg & PAF_TEXTIME) pa->time = paf->sta + (paf->end - paf->sta)*tin; else pa->time = paf->sta + (paf->end - paf->sta)*(1.0f-tin); } /* set ob at correct time, we use cached matrices */ cur= (int)floor(pa->time) + 1 ; /* + 1 has a reason: (obmat/prevobmat) otherwise comet-tails start too late */ if(cur <= paf_end) mcnow= mcache + cur - paf_sta; else mcnow= mcache + paf_end - paf_sta; if(cur > paf_sta) mcprev= mcnow-1; else mcprev= mcache; /* move to global space */ Mat4MulVecfl(mcnow->obmat, pa->co); VECCOPY(vec, co); Mat4MulVecfl(mcprev->obmat, vec); /* first start speed: object */ VECSUB(pa->no, pa->co, vec); VecMulf(pa->no, paf->obfac); /* calculate the correct inter-frame */ fac= (pa->time- (float)floor(pa->time)); pa->co[0]= fac*pa->co[0] + (1.0f-fac)*vec[0]; pa->co[1]= fac*pa->co[1] + (1.0f-fac)*vec[1]; pa->co[2]= fac*pa->co[2] + (1.0f-fac)*vec[2]; /* start speed: normal */ if(paf->normfac!=0.0) { /* imat is transpose ! */ VECCOPY(vec, no); Mat3MulVecfl(mcnow->imat, vec); Normalize(vec); VecMulf(vec, paf->normfac); VECADD(pa->no, pa->no, vec); } } else { if(paf->normfac!=0.0) { VECCOPY(pa->no, no); Normalize(pa->no); VecMulf(pa->no, paf->normfac); } } pa->lifetime= paf->lifetime; if(paf->randlife!=0.0) { pa->lifetime*= 1.0f + paf->randlife*(rng_getFloat(rng) - 0.5f); } pa->mat_nr= paf->omat; if(folengths) pa->lifetime*= folengths[curface]; make_particle_keys(rng, ob, 0, a, paf, pa, force, deform, mtexmove, effectorbase); } /* free stuff */ give_mesh_particle_coord(NULL, NULL, NULL, 0, 0, NULL, NULL); MEM_freeN(vertexcosnos); if(voweights) MEM_freeN(voweights); if(foweights) MEM_freeN(foweights); if(volengths) MEM_freeN(volengths); if(folengths) MEM_freeN(folengths); if(mcache) MEM_freeN(mcache); rng_free(rng); if(deform) end_latt_deform(); if(effectorbase) pdEndEffectors(effectorbase); /* reset deflector cache */ for(base= G.scene->base.first; base; base= base->next) { if(base->object->sumohandle) { MEM_freeN(base->object->sumohandle); base->object->sumohandle= NULL; } } disable_speed_curve(0); //XXX if(waitcursor_set) waitcursor(0); }