- Particle now use the deflector objects collision modifier data to collide with deflectors and as a result can now use the velocity of the colliding object for more realistic collisions. - Dynamic rotations are also quite a bit more realistic and are related to the friction setting of the deflector (to get any dynamic rotations there has to be some friction). This is largely due to the separate handling of rolling friction (approximated to be 1% of normal sliding friction). - Collisions should be a bit faster on complex deflectors due to the tree structure used by the collision modifier. - Collision should also generally be a bit more accurate. To be noted: Only the average velocity of individual deflector faces is used, so collisions with rotating or deforming objects can't be handled accurately - this would require much more complex calculations. Subdividing the deflector object surface to smaller faces can help with this as the individual face velocities become more linear.
569 lines
14 KiB
C
569 lines
14 KiB
C
/* 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 <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#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_bad_level_calls.h"
|
|
#include "BKE_blender.h"
|
|
#include "BKE_collision.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 <zlib.h>
|
|
#include <string.h>
|
|
#endif // DISABLE_ELBEEM
|
|
|
|
/* temporal struct, used for reading return of mesh_get_mapped_verts_nors() */
|
|
typedef struct VeNoCo {
|
|
float co[3], no[3];
|
|
} VeNoCo;
|
|
|
|
/* ***************** PARTICLES ***************** */
|
|
|
|
/* deprecated, only keep this for readfile.c */
|
|
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 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;
|
|
}
|
|
}
|
|
|
|
/* -------------------------- 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) {
|
|
|
|
if(pd->forcefield == PFIELD_WIND)
|
|
{
|
|
pd->rng = rng_new(1);
|
|
rng_srandom(pd->rng, (unsigned int)(ceil(PIL_check_seconds_timer()))); // use better seed
|
|
}
|
|
|
|
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)
|
|
{
|
|
if(ec->ob->pd && (ec->ob->pd->forcefield == PFIELD_WIND))
|
|
rng_free(ec->ob->pd->rng);
|
|
|
|
*(ec->ob)= ec->obcopy;
|
|
}
|
|
|
|
BLI_freelistN(lb);
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************/
|
|
/* Effectors */
|
|
/************************************************/
|
|
|
|
// triangle - ray callback function
|
|
static void eff_tri_ray_hit(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
|
|
{
|
|
// whenever we hit a bounding box, we don't check further
|
|
hit->dist = -1;
|
|
hit->index = 1;
|
|
}
|
|
|
|
// get visibility of a wind ray
|
|
static float eff_calc_visibility(Object *ob, float *co, float *dir)
|
|
{
|
|
CollisionModifierData **collobjs = NULL;
|
|
int numcollobj = 0, i;
|
|
float norm[3], len = 0.0;
|
|
float visibility = 1.0;
|
|
|
|
collobjs = get_collisionobjects(ob, &numcollobj);
|
|
|
|
if(!collobjs)
|
|
return 0;
|
|
|
|
VECCOPY(norm, dir);
|
|
VecMulf(norm, -1.0);
|
|
len = Normalize(norm);
|
|
|
|
// check all collision objects
|
|
for(i = 0; i < numcollobj; i++)
|
|
{
|
|
CollisionModifierData *collmd = collobjs[i];
|
|
|
|
if(collmd->bvhtree)
|
|
{
|
|
BVHTreeRayHit hit;
|
|
|
|
hit.index = -1;
|
|
hit.dist = len + FLT_EPSILON;
|
|
|
|
// check if the way is blocked
|
|
if(BLI_bvhtree_ray_cast(collmd->bvhtree, co, norm, 0.0f, &hit, eff_tri_ray_hit, NULL)>=0)
|
|
{
|
|
// visibility is only between 0 and 1, calculated from 1-absorption
|
|
visibility *= MAX2(0.0, MIN2(1.0, (1.0-((float)collmd->absorption)*0.01)));
|
|
|
|
if(visibility <= 0.0f)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeN(collobjs);
|
|
|
|
return visibility;
|
|
}
|
|
|
|
// noise function for wind e.g.
|
|
static float wind_func(struct RNG *rng, float strength)
|
|
{
|
|
int random = (rng_getInt(rng)+1) % 65535; // max 2357
|
|
float force = rng_getFloat(rng) + 1.0f;
|
|
float ret;
|
|
float sign = 0;
|
|
|
|
sign = (random > 32000.0) ? 1.0: -1.0; // dividing by 2 is not giving equal sign distribution
|
|
|
|
ret = sign*((float)random / force)*strength/65535.0f;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static float falloff_func(float fac, int usemin, float mindist, int usemax, float maxdist, float power)
|
|
{
|
|
if(!usemin)
|
|
mindist= 0.0f;
|
|
|
|
if(fac < mindist) {
|
|
return 1.0f;
|
|
}
|
|
else if(usemax) {
|
|
if(fac>maxdist || (maxdist-mindist)<=0.0f)
|
|
return 0.0f;
|
|
|
|
fac= (fac-mindist)/(maxdist-mindist);
|
|
return 1.0f - (float)pow((double)fac, (double)power);
|
|
}
|
|
else
|
|
return pow((double)1.0f+fac-mindist, (double)-power);
|
|
}
|
|
|
|
static float falloff_func_dist(PartDeflect *pd, float fac)
|
|
{
|
|
return falloff_func(fac, pd->flag&PFIELD_USEMIN, pd->mindist, pd->flag&PFIELD_USEMAX, pd->maxdist, pd->f_power);
|
|
}
|
|
|
|
static float falloff_func_rad(PartDeflect *pd, float fac)
|
|
{
|
|
return falloff_func(fac, pd->flag&PFIELD_USEMINR, pd->minrad, pd->flag&PFIELD_USEMAXR, pd->maxrad, pd->f_power_r);
|
|
}
|
|
|
|
float effector_falloff(PartDeflect *pd, float *eff_velocity, float *vec_to_part)
|
|
{
|
|
float eff_dir[3], temp[3];
|
|
float falloff=1.0, fac, r_fac;
|
|
|
|
if(pd->forcefield==PFIELD_LENNARDJ)
|
|
return falloff; /* Lennard-Jones field has it's own falloff built in */
|
|
|
|
VecCopyf(eff_dir,eff_velocity);
|
|
Normalize(eff_dir);
|
|
|
|
if(pd->flag & PFIELD_POSZ && Inpf(eff_dir,vec_to_part)<0.0f)
|
|
falloff=0.0f;
|
|
else switch(pd->falloff){
|
|
case PFIELD_FALL_SPHERE:
|
|
fac=VecLength(vec_to_part);
|
|
falloff= falloff_func_dist(pd, fac);
|
|
break;
|
|
|
|
case PFIELD_FALL_TUBE:
|
|
fac=Inpf(vec_to_part,eff_dir);
|
|
falloff= falloff_func_dist(pd, ABS(fac));
|
|
if(falloff == 0.0f)
|
|
break;
|
|
|
|
VECADDFAC(temp,vec_to_part,eff_dir,-fac);
|
|
r_fac=VecLength(temp);
|
|
falloff*= falloff_func_rad(pd, r_fac);
|
|
break;
|
|
case PFIELD_FALL_CONE:
|
|
fac=Inpf(vec_to_part,eff_dir);
|
|
falloff= falloff_func_dist(pd, ABS(fac));
|
|
if(falloff == 0.0f)
|
|
break;
|
|
|
|
r_fac=saacos(fac/VecLength(vec_to_part))*180.0f/(float)M_PI;
|
|
falloff*= falloff_func_rad(pd, r_fac);
|
|
|
|
break;
|
|
}
|
|
|
|
return falloff;
|
|
}
|
|
|
|
void do_physical_effector(Object *ob, float *opco, short type, float force_val, float distance, float falloff, float size, float damp, float *eff_velocity, float *vec_to_part, float *velocity, float *field, int planar, struct RNG *rng, float noise_factor, float charge, float pa_size)
|
|
{
|
|
float mag_vec[3]={0,0,0};
|
|
float temp[3], temp2[3];
|
|
float eff_vel[3];
|
|
float noise = 0, visibility;
|
|
|
|
// calculate visibility
|
|
visibility = eff_calc_visibility(ob, opco, vec_to_part);
|
|
if(visibility <= 0.0)
|
|
return;
|
|
falloff *= visibility;
|
|
|
|
VecCopyf(eff_vel,eff_velocity);
|
|
Normalize(eff_vel);
|
|
|
|
switch(type){
|
|
case PFIELD_WIND:
|
|
VECCOPY(mag_vec,eff_vel);
|
|
|
|
// add wind noise here, only if we have wind
|
|
if((noise_factor > 0.0f) && (force_val > FLT_EPSILON))
|
|
noise = wind_func(rng, noise_factor);
|
|
|
|
VecMulf(mag_vec,(force_val+noise)*falloff);
|
|
VecAddf(field,field,mag_vec);
|
|
break;
|
|
|
|
case PFIELD_FORCE:
|
|
if(planar)
|
|
Projf(mag_vec,vec_to_part,eff_vel);
|
|
else
|
|
VecCopyf(mag_vec,vec_to_part);
|
|
|
|
Normalize(mag_vec);
|
|
|
|
VecMulf(mag_vec,force_val*falloff);
|
|
VecAddf(field,field,mag_vec);
|
|
break;
|
|
|
|
case PFIELD_VORTEX:
|
|
Crossf(mag_vec,eff_vel,vec_to_part);
|
|
|
|
Normalize(mag_vec);
|
|
|
|
VecMulf(mag_vec,force_val*distance*falloff);
|
|
VecAddf(field,field,mag_vec);
|
|
|
|
break;
|
|
case PFIELD_MAGNET:
|
|
if(planar)
|
|
VecCopyf(temp,eff_vel);
|
|
else
|
|
/* magnetic field of a moving charge */
|
|
Crossf(temp,eff_vel,vec_to_part);
|
|
|
|
Normalize(temp);
|
|
|
|
Crossf(temp2,velocity,temp);
|
|
VecAddf(mag_vec,mag_vec,temp2);
|
|
|
|
VecMulf(mag_vec,force_val*falloff);
|
|
VecAddf(field,field,mag_vec);
|
|
break;
|
|
case PFIELD_HARMONIC:
|
|
if(planar)
|
|
Projf(mag_vec,vec_to_part,eff_vel);
|
|
else
|
|
VecCopyf(mag_vec,vec_to_part);
|
|
|
|
Normalize(mag_vec);
|
|
|
|
VecMulf(mag_vec,force_val*falloff);
|
|
VecSubf(field,field,mag_vec);
|
|
|
|
VecCopyf(mag_vec,velocity);
|
|
/* 1.9 is an experimental value to get critical damping at damp=1.0 */
|
|
VecMulf(mag_vec,damp*1.9f*(float)sqrt(force_val));
|
|
VecSubf(field,field,mag_vec);
|
|
break;
|
|
case PFIELD_CHARGE:
|
|
if(planar)
|
|
Projf(mag_vec,vec_to_part,eff_vel);
|
|
else
|
|
VecCopyf(mag_vec,vec_to_part);
|
|
|
|
Normalize(mag_vec);
|
|
|
|
VecMulf(mag_vec,charge*force_val*falloff);
|
|
VecAddf(field,field,mag_vec);
|
|
break;
|
|
case PFIELD_LENNARDJ:
|
|
{
|
|
float fac;
|
|
|
|
if(planar) {
|
|
Projf(mag_vec,vec_to_part,eff_vel);
|
|
distance = VecLength(mag_vec);
|
|
}
|
|
else
|
|
VecCopyf(mag_vec,vec_to_part);
|
|
|
|
/* at this distance the field is 60 times weaker than maximum */
|
|
if(distance > 2.22 * (size+pa_size))
|
|
break;
|
|
|
|
fac = pow((size+pa_size)/distance,6.0);
|
|
|
|
fac = - fac * (1.0 - fac) / distance;
|
|
|
|
/* limit the repulsive term drastically to avoid huge forces */
|
|
fac = ((fac>2.0) ? 2.0 : fac);
|
|
|
|
/* 0.003715 is the fac value at 2.22 times (size+pa_size),
|
|
substracted to avoid discontinuity at the border
|
|
*/
|
|
VecMulf(mag_vec, force_val * (fac-0.0037315));
|
|
VecAddf(field,field,mag_vec);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------- 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 *obloc;
|
|
|
|
float distance, vec_to_part[3];
|
|
float falloff;
|
|
|
|
/* 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 */
|
|
where_is_object_time(ob,cur_time);
|
|
|
|
/* use center of object for distance calculus */
|
|
VecSubf(vec_to_part, opco, ob->obmat[3]);
|
|
distance = VecLength(vec_to_part);
|
|
|
|
falloff=effector_falloff(pd,ob->obmat[2],vec_to_part);
|
|
|
|
if(falloff<=0.0f)
|
|
; /* don't do anything */
|
|
else {
|
|
float field[3]={0,0,0}, tmp[3];
|
|
VECCOPY(field, force);
|
|
do_physical_effector(ob, opco, pd->forcefield,pd->f_strength,distance,
|
|
falloff,pd->f_dist,pd->f_damp,ob->obmat[2],vec_to_part,
|
|
speed,force,pd->flag&PFIELD_PLANAR, pd->rng, pd->f_noise, 0.0f, 0.0f);
|
|
|
|
// for softbody backward compatibility
|
|
if(flags & PE_WIND_AS_SPEED){
|
|
VECSUB(tmp, force, field);
|
|
VECSUB(speed, speed, tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|