Files
test2/source/blender/blenkernel/intern/effect.c
Janne Karhu d2186508da Particle collisions upgrade:
- 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.
2008-09-13 18:09:41 +00:00

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);
}
}
}
}