Fix depsgraph to compute more accurate links for collision & force.

Current implementation more or less indiscriminately links physics
objects to colliders and forces, ignoring precise details of layer
checks and collider groups. The new depsgraph seemed to lack some
such links at all. The relevant code in modifiers suffers from a
lot of duplication.

Different physics simulations use independent implementations of
collision and similar things, which results in a lot of variance:

* Cloth collides with objects on same or visible layer with dupli.
* Softbody collides with objects on same layer without dupli.
* Non-hair particles collide on same layer with dupli.
* Smoke uses same code as cloth, but needs different modifier.
* Dynamic paint "collides" with brushes on any layer without dupli.

Force fields with absorption also imply dependency on colliders:

* For most systems, colliders are selected from same layer as field.
* For non-hair particles, it uses the same exact set as the particles.

As a special quirk, smoke ignores smoke flow force fields; on the other
hand dependency on such field implies dependency on the smoke domain.

This introduces two utility functions each for old and new depsgraph
that are flexible enough to handle all these variations, and uses them
to handle particles, cloth, smoke, softbody and dynpaint.

One thing to watch out for is that depsgraph code shouldn't rely on
any properties that don't cause a graph rebuild when changed. This
was violated in the original code that was building force field links,
while taking zero field weights into account.

This change may cause new dependency cycles in cases where necessary
dependencies were missing, but may also remove cycles in situations
where unnecessary links were previously created. It's also now possible
to solve some cycles by switching to explicit groups, since they are
now properly taken into account for dependencies.

Differential Revision: https://developer.blender.org/D2141
This commit is contained in:
Alexander Gavrilov 2016-08-12 18:23:29 +03:00
parent a74dab86ee
commit 9368bdab01
17 changed files with 321 additions and 322 deletions

View File

@ -146,6 +146,10 @@ void collision_get_collider_velocity(float vel_old[3], float vel_new[3], struct
/////////////////////////////////////////////////
// used in effect.c
/////////////////////////////////////////////////
/* explicit control over layer mask and dupli recursion */
struct Object **get_collisionobjects_ext(struct Scene *scene, struct Object *self, struct Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli);
struct Object **get_collisionobjects(struct Scene *scene, struct Object *self, struct Group *group, unsigned int *numcollobj, unsigned int modifier_type);
typedef struct ColliderCache {

View File

@ -110,7 +110,7 @@ typedef struct EffectorCache {
} EffectorCache;
void free_partdeflect(struct PartDeflect *pd);
struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc);
struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation);
void pdEndEffectors(struct ListBase **effectors);
void pdPrecalculateEffectors(struct ListBase *effectors);
void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse);

View File

@ -34,6 +34,11 @@
#include "DNA_constraint_types.h"
#include "BKE_constraint.h"
struct Scene;
struct Group;
struct EffectorWeights;
struct ModifierData;
/* **** DAG relation types *** */
/* scene link to object */
@ -152,6 +157,11 @@ DagNode *dag_get_node(DagForest *forest, void *fob);
DagNode *dag_get_sub_node(DagForest *forest, void *fob);
void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, const char *name);
typedef bool (*DagCollobjFilterFunction)(struct Object *obj, struct ModifierData *md);
void dag_add_collision_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name);
void dag_add_forcefield_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name);
void graph_print_queue(DagNodeQueue *nqueue);
void graph_print_queue_dist(DagNodeQueue *nqueue);
void graph_print_adj_list(DagForest *dag);

View File

@ -503,12 +503,13 @@ static void add_collision_object(Object ***objs, unsigned int *numobj, unsigned
// return all collision objects in scene
// collision object will exclude self
Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type)
Object **get_collisionobjects_ext(Scene *scene, Object *self, Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli)
{
Base *base;
Object **objs;
GroupObject *go;
unsigned int numobj= 0, maxobj= 100;
int level = dupli ? 0 : 1;
objs= MEM_callocN(sizeof(Object *)*maxobj, "CollisionObjectsArray");
@ -516,16 +517,14 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned
if (group) {
/* use specified group */
for (go= group->gobject.first; go; go= go->next)
add_collision_object(&objs, &numobj, &maxobj, go->ob, self, 0, modifier_type);
add_collision_object(&objs, &numobj, &maxobj, go->ob, self, level, modifier_type);
}
else {
Scene *sce_iter;
/* add objects in same layer in scene */
for (SETLOOPER(scene, sce_iter, base)) {
/* Need to check for active layers, too.
Otherwise this check fails if the objects are not on the same layer - DG */
if ((base->lay & self->lay) || (base->lay & scene->lay))
add_collision_object(&objs, &numobj, &maxobj, base->object, self, 0, modifier_type);
if ( base->lay & layer )
add_collision_object(&objs, &numobj, &maxobj, base->object, self, level, modifier_type);
}
}
@ -535,6 +534,13 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned
return objs;
}
Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type)
{
/* Need to check for active layers, too.
Otherwise this check fails if the objects are not on the same layer - DG */
return get_collisionobjects_ext(scene, self, group, self->lay | scene->lay, numcollobj, modifier_type, true);
}
static void add_collider_cache_object(ListBase **objs, Object *ob, Object *self, int level)
{
CollisionModifierData *cmd= NULL;

View File

@ -59,11 +59,14 @@
#include "DNA_windowmanager_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
#include "DNA_modifier_types.h"
#include "DNA_rigidbody_types.h"
#include "BKE_anim.h"
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_DerivedMesh.h"
#include "BKE_collision.h"
#include "BKE_effect.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
@ -450,49 +453,51 @@ static void dag_add_lamp_driver_relations(DagForest *dag, DagNode *node, Lamp *l
la->id.tag &= ~LIB_TAG_DOIT;
}
static void check_and_create_collision_relation(DagForest *dag, Object *ob, DagNode *node, Object *ob1, int skip_forcefield, bool no_collision)
static void create_collision_relation(DagForest *dag, DagNode *node, Object *ob1, const char *name)
{
DagNode *node2;
if (ob1->pd && (ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) {
if ((skip_forcefield && ob1->pd->forcefield == skip_forcefield) || (no_collision && ob1->pd->forcefield == 0))
return;
node2 = dag_get_node(dag, ob1);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Collision");
}
DagNode *node2 = dag_get_node(dag, ob1);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, name);
}
static void dag_add_collision_field_relation(DagForest *dag, Scene *scene, Object *ob, DagNode *node, int skip_forcefield, bool no_collision)
void dag_add_collision_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name)
{
Base *base;
ParticleSystem *particle_system;
unsigned int numcollobj;
Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli);
for (particle_system = ob->particlesystem.first;
particle_system;
particle_system = particle_system->next)
{
EffectorWeights *effector_weights = particle_system->part->effector_weights;
if (effector_weights->group) {
GroupObject *group_object;
for (unsigned int i = 0; i < numcollobj; i++) {
Object *ob1 = collobjs[i];
for (group_object = effector_weights->group->gobject.first;
group_object;
group_object = group_object->next)
{
if ((group_object->ob->lay & ob->lay)) {
check_and_create_collision_relation(dag, ob, node, group_object->ob, skip_forcefield, no_collision);
if (!fn || fn(ob1, modifiers_findByType(ob1, modifier_type))) {
create_collision_relation(dag, node, ob1, name);
}
}
if (collobjs)
MEM_freeN(collobjs);
}
void dag_add_forcefield_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
{
ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
if (effectors) {
for (EffectorCache *eff = effectors->first; eff; eff = eff->next) {
if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) {
create_collision_relation(dag, node, eff->ob, name);
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
create_collision_relation(dag, node, eff->pd->f_source, "Smoke Force Domain");
}
if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
/* Actual code uses get_collider_cache */
dag_add_collision_relations(dag, scene, ob, node, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption");
}
}
}
}
/* would be nice to have a list of colliders here
* so for now walk all objects in scene check 'same layer rule' */
for (base = scene->base.first; base; base = base->next) {
if ((base->lay & ob->lay)) {
Object *ob1 = base->object;
check_and_create_collision_relation(dag, ob, node, ob1, skip_forcefield, no_collision);
}
}
pdEndEffectors(&effectors);
}
static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Object *ob, int mask)
@ -643,23 +648,13 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
/* softbody collision */
/* rigidbody force fields */
if ((ob->type == OB_MESH) || (ob->type == OB_CURVE) || (ob->type == OB_LATTICE)) {
if (ob->particlesystem.first ||
modifiers_isModifierEnabled(ob, eModifierType_Softbody) ||
modifiers_isModifierEnabled(ob, eModifierType_Cloth) ||
modifiers_isModifierEnabled(ob, eModifierType_DynamicPaint))
{
dag_add_collision_field_relation(dag, scene, ob, node, 0, false); /* TODO: use effectorweight->group */
}
else if (modifiers_isModifierEnabled(ob, eModifierType_Smoke)) {
dag_add_collision_field_relation(dag, scene, ob, node, PFIELD_SMOKEFLOW, false);
}
else if (ob->rigidbody_object) {
dag_add_collision_field_relation(dag, scene, ob, node, 0, true);
if (ob->rigidbody_object && scene->rigidbody_world) {
dag_add_forcefield_relations(dag, scene, ob, node, scene->rigidbody_world->effector_weights, true, 0, "Force Field");
}
}
/* object data drivers */
if (ob->data) {
AnimData *adt = BKE_animdata_from_id((ID *)ob->data);
@ -763,8 +758,6 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
BoidRule *rule = NULL;
BoidState *state = NULL;
ParticleSettings *part = psys->part;
ListBase *effectors = NULL;
EffectorCache *eff;
if (part->adt) {
dag_add_driver_relation(part->adt, dag, node, 1);
@ -803,18 +796,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
if (effectors) {
for (eff = effectors->first; eff; eff = eff->next) {
if (eff->psys) {
node2 = dag_get_node(dag, eff->ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field");
}
}
if (part->type != PART_HAIR) {
/* Actual code uses get_collider_cache */
dag_add_collision_relations(dag, scene, ob, node, part->collision_group, ob->lay, eModifierType_Collision, NULL, true, "Particle Collision");
}
pdEndEffectors(&effectors);
dag_add_forcefield_relations(dag, scene, ob, node, part->effector_weights, part->type == PART_HAIR, 0, "Particle Force Field");
if (part->boids) {
for (state = part->boids->states.first; state; state = state->next) {

View File

@ -155,15 +155,20 @@ static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSyste
eff->frame = -1;
return eff;
}
static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src)
static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src, bool for_simulation)
{
EffectorCache *eff = NULL;
if ( ob == ob_src || weights->weight[ob->pd->forcefield] == 0.0f )
if ( ob == ob_src )
return;
if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal )
return;
if (for_simulation) {
if (weights->weight[ob->pd->forcefield] == 0.0f )
return;
if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal )
return;
}
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
@ -175,7 +180,7 @@ static void add_object_to_effectors(ListBase **effectors, Scene *scene, Effector
BLI_addtail(*effectors, eff);
}
static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src)
static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src, bool for_simulation)
{
ParticleSettings *part= psys->part;
@ -185,14 +190,14 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec
if ( psys == psys_src && (part->flag & PART_SELF_EFFECT) == 0)
return;
if ( part->pd && part->pd->forcefield && weights->weight[part->pd->forcefield] != 0.0f) {
if ( part->pd && part->pd->forcefield && (!for_simulation || weights->weight[part->pd->forcefield] != 0.0f)) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd));
}
if (part->pd2 && part->pd2->forcefield && weights->weight[part->pd2->forcefield] != 0.0f) {
if (part->pd2 && part->pd2->forcefield && (!for_simulation || weights->weight[part->pd2->forcefield] != 0.0f)) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
@ -202,7 +207,7 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec
/* returns ListBase handle with objects taking part in the effecting */
ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
EffectorWeights *weights, bool precalc)
EffectorWeights *weights, bool for_simulation)
{
Base *base;
unsigned int layer= ob_src->lay;
@ -214,13 +219,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (go= weights->group->gobject.first; go; go= go->next) {
if ( (go->ob->lay & layer) ) {
if ( go->ob->pd && go->ob->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src);
add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src, for_simulation);
if ( go->ob->particlesystem.first ) {
ParticleSystem *psys= go->ob->particlesystem.first;
for ( ; psys; psys=psys->next )
add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src);
add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src, for_simulation);
}
}
}
@ -229,19 +234,19 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (base = scene->base.first; base; base= base->next) {
if ( (base->lay & layer) ) {
if ( base->object->pd && base->object->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, base->object, ob_src);
add_object_to_effectors(&effectors, scene, weights, base->object, ob_src, for_simulation);
if ( base->object->particlesystem.first ) {
ParticleSystem *psys= base->object->particlesystem.first;
for ( ; psys; psys=psys->next )
add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src);
add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src, for_simulation);
}
}
}
}
if (precalc)
if (for_simulation)
pdPrecalculateEffectors(effectors);
return effectors;

View File

@ -42,6 +42,8 @@ struct Depsgraph;
struct Main;
struct Scene;
struct Group;
struct EffectorWeights;
#ifdef __cplusplus
extern "C" {
@ -112,6 +114,12 @@ void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFi
/* TODO(sergey): Remove once all geometry update is granular. */
void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag);
/* Utility functions for physics modifiers */
typedef bool (*DEG_CollobjFilterFunction)(struct Object *obj, struct ModifierData *md);
void DEG_add_collision_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name);
void DEG_add_forcefield_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name);
/* ************************************************ */
#ifdef __cplusplus

View File

@ -65,6 +65,7 @@ extern "C" {
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "DNA_world_types.h"
#include "DNA_object_force.h"
#include "BKE_action.h"
#include "BKE_armature.h"
@ -72,6 +73,7 @@ extern "C" {
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_effect.h"
#include "BKE_collision.h"
#include "BKE_fcurve.h"
#include "BKE_group.h"
#include "BKE_key.h"
@ -243,6 +245,69 @@ void DepsgraphRelationBuilder::add_operation_relation(
}
}
void DepsgraphRelationBuilder::add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name)
{
unsigned int numcollobj;
Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, eModifierType_Collision, dupli);
for (unsigned int i = 0; i < numcollobj; i++)
{
Object *ob1 = collobjs[i];
ComponentKey trf_key(&ob1->id, DEPSNODE_TYPE_TRANSFORM);
add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, name);
ComponentKey coll_key(&ob1->id, DEPSNODE_TYPE_GEOMETRY);
add_relation(coll_key, key, DEPSREL_TYPE_STANDARD, name);
}
if (collobjs)
MEM_freeN(collobjs);
}
void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name)
{
ListBase *effectors = pdInitEffectors(scene, ob, psys, eff, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
if (eff->ob != ob) {
ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_TRANSFORM);
add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
}
if (eff->psys) {
if (eff->ob != ob) {
ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES);
add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
/* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
ComponentKey mod_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY);
add_relation(mod_key, key, DEPSREL_TYPE_STANDARD, name);
}
else if (eff->psys != psys) {
OperationKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, eff->psys->name);
add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
}
}
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
ComponentKey trf_key(&eff->pd->f_source->id, DEPSNODE_TYPE_TRANSFORM);
add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain");
ComponentKey eff_key(&eff->pd->f_source->id, DEPSNODE_TYPE_GEOMETRY);
add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain");
}
if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
add_collision_relations(key, scene, ob, NULL, eff->ob->lay, true, "Force Absorption");
}
}
}
pdEndEffectors(&effectors);
}
/* **** Functions to build relations between entities **** */
void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene)
@ -1137,20 +1202,13 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob)
}
#endif
/* effectors */
ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
if (eff->psys) {
// XXX: DAG_RL_DATA_DATA | DAG_RL_OB_DATA
ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); // xxx: particles instead?
add_relation(eff_key, psys_key, DEPSREL_TYPE_STANDARD, "Particle Field");
}
}
/* collisions */
if (part->type != PART_HAIR) {
add_collision_relations(psys_key, scene, ob, part->collision_group, ob->lay, true, "Particle Collision");
}
pdEndEffectors(&effectors);
/* effectors */
add_forcefield_relations(psys_key, scene, ob, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field");
/* boids */
if (part->boids) {

View File

@ -63,6 +63,8 @@ struct bConstraint;
struct Scene;
struct Tex;
struct World;
struct EffectorWeights;
struct ParticleSystem;
struct PropertyRNA;
@ -244,6 +246,9 @@ struct DepsgraphRelationBuilder
void build_compositor(Scene *scene);
void build_gpencil(ID *owner, bGPdata *gpd);
void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name);
void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name);
template <typename KeyType>
OperationDepsNode *find_operation_node(const KeyType &key);

View File

@ -36,11 +36,15 @@ extern "C" {
#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_force.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BKE_main.h"
#include "BKE_collision.h"
#include "BKE_effect.h"
#include "BKE_modifier.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_debug.h"
@ -304,3 +308,52 @@ void DEG_scene_graph_free(Scene *scene)
scene->depsgraph = NULL;
}
}
void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name)
{
unsigned int numcollobj;
Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli);
for (unsigned int i = 0; i < numcollobj; i++) {
Object *ob1 = collobjs[i];
if (!fn || fn(ob1, modifiers_findByType(ob1, (ModifierType)modifier_type))) {
DEG_add_object_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name);
DEG_add_object_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name);
}
}
if (collobjs)
MEM_freeN(collobjs);
}
void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
{
ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) {
if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) {
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_TRANSFORM, name);
if (eff->psys) {
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name);
/* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name);
}
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain");
DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_GEOMETRY, "Smoke Force Domain");
}
if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
DEG_add_collision_relations(handle, scene, ob, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption");
}
}
}
}
pdEndEffectors(&effectors);
}

View File

@ -56,6 +56,12 @@ static void rna_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
}
static void rna_cloth_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
DAG_relations_tag_update(bmain);
rna_cloth_update(bmain, scene, ptr);
}
static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->id.data;
@ -729,7 +735,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "group", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
RNA_def_property_update(prop, 0, "rna_cloth_update");
RNA_def_property_update(prop, 0, "rna_cloth_dependency_update");
prop = RNA_def_property(srna, "vertex_group_self_collisions", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, "rna_CollSettings_selfcol_vgroup_get", "rna_CollSettings_selfcol_vgroup_length",

View File

@ -730,6 +730,11 @@ static void rna_softbody_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Point
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
}
static void rna_softbody_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
DAG_relations_tag_update(bmain);
rna_softbody_update(bmain, scene, ptr);
}
static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRNA *ptr,
PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
@ -1378,7 +1383,7 @@ static void rna_def_field(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_absorption", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_VISIBILITY);
RNA_def_property_ui_text(prop, "Absorption", "Force gets absorbed by collision objects");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
RNA_def_property_update(prop, 0, "rna_FieldSettings_dependency_update");
prop = RNA_def_property(srna, "use_multiple_springs", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_MULTIPLE_SPRINGS);
@ -1855,7 +1860,7 @@ static void rna_def_softbody(BlenderRNA *brna)
prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
RNA_def_property_update(prop, 0, "rna_softbody_update");
RNA_def_property_update(prop, 0, "rna_softbody_dependency_update");
prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "effector_weights");

View File

@ -630,6 +630,12 @@ static void rna_Particle_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET);
}
static void rna_Particle_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr)
{
DAG_relations_tag_update(bmain);
rna_Particle_reset(bmain, scene, ptr);
}
static void rna_Particle_change_type(Main *bmain, Scene *scene, PointerRNA *ptr)
{
particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_TYPE);
@ -2744,7 +2750,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
RNA_def_property_update(prop, 0, "rna_Particle_reset");
RNA_def_property_update(prop, 0, "rna_Particle_reset_dependency");
/* global physical properties */
prop = RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE);

View File

@ -123,19 +123,11 @@ static void updateDepgraph(ModifierData *md, DagForest *forest,
{
ClothModifierData *clmd = (ClothModifierData *) md;
Base *base;
if (clmd) {
for (base = scene->base.first; base; base = base->next) {
Object *ob1 = base->object;
if (ob1 != ob) {
CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision);
if (coll_clmd) {
DagNode *curNode = dag_get_node(forest, ob1);
dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cloth Collision");
}
}
}
/* Actual code uses get_collisionobjects */
dag_add_collision_relations(forest, scene, ob, obNode, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision");
dag_add_forcefield_relations(forest, scene, ob, obNode, clmd->sim_parms->effector_weights, true, 0, "Cloth Field");
}
}
@ -147,16 +139,10 @@ static void updateDepsgraph(ModifierData *md,
{
ClothModifierData *clmd = (ClothModifierData *)md;
if (clmd != NULL) {
Base *base;
for (base = scene->base.first; base; base = base->next) {
Object *ob1 = base->object;
if (ob1 != ob) {
CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision);
if (coll_clmd) {
DEG_add_object_relation(node, ob1, DEG_OB_COMP_TRANSFORM, "Cloth Modifier");
}
}
}
/* Actual code uses get_collisionobjects */
DEG_add_collision_relations(node, scene, ob, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision");
DEG_add_forcefield_relations(node, scene, ob, clmd->sim_parms->effector_weights, true, 0, "Cloth Field");
}
}

View File

@ -114,6 +114,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
return dm;
}
static bool is_brush_cb(Object *UNUSED(ob), ModifierData *pmd)
{
return ((DynamicPaintModifierData*)pmd)->brush != NULL;
}
static void updateDepgraph(ModifierData *md, DagForest *forest,
struct Main *UNUSED(bmain),
struct Scene *scene,
@ -124,16 +129,13 @@ static void updateDepgraph(ModifierData *md, DagForest *forest,
/* add relation from canvases to all brush objects */
if (pmd && pmd->canvas) {
Base *base = scene->base.first;
for (; base; base = base->next) {
DynamicPaintModifierData *pmd2 =
(DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint);
if (pmd2 && pmd2->brush && ob != base->object) {
DagNode *brushNode = dag_get_node(forest, base->object);
dag_add_relation(forest, brushNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Dynamic Paint Brush");
for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
dag_add_forcefield_relations(forest, scene, ob, obNode, surface->effector_weights, true, 0, "Dynamic Paint Field");
}
/* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */
dag_add_collision_relations(forest, scene, ob, obNode, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush");
}
}
}
@ -147,13 +149,13 @@ static void updateDepsgraph(ModifierData *md,
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
/* Add relation from canvases to all brush objects. */
if (pmd->canvas != NULL) {
Base *base = scene->base.first;
for (; base; base = base->next) {
DynamicPaintModifierData *pmd2 =
(DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint);
if (pmd2 && pmd2->brush && ob != base->object) {
DEG_add_object_relation(node, base->object, DEG_OB_COMP_TRANSFORM, "Dynamic Paint Brush");
for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
DEG_add_forcefield_relations(node, scene, ob, surface->effector_weights, true, 0, "Dynamic Paint Field");
}
/* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */
DEG_add_collision_relations(node, scene, ob, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush");
}
}
}

View File

@ -117,219 +117,48 @@ static bool dependsOnTime(ModifierData *UNUSED(md))
return true;
}
static void update_depsgraph_flow_coll_object(DagForest *forest,
DagNode *obNode,
Object *object2)
static bool is_flow_cb(Object *UNUSED(ob), ModifierData *md)
{
SmokeModifierData *smd;
if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
return;
}
object2->id.tag &= ~LIB_TAG_DOIT;
smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke);
if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) ||
((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll)))
{
DagNode *curNode = dag_get_node(forest, object2);
dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow/Coll");
}
if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
GroupObject *go;
for (go = object2->dup_group->gobject.first;
go != NULL;
go = go->next)
{
if (go->ob == NULL) {
continue;
}
update_depsgraph_flow_coll_object(forest, obNode, go->ob);
}
}
SmokeModifierData *smd = (SmokeModifierData *) md;
return (smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow;
}
static void update_depsgraph_field_source_object(DagForest *forest,
DagNode *obNode,
Object *object,
Object *object2)
static bool is_coll_cb(Object *UNUSED(ob), ModifierData *md)
{
if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
return;
}
object2->id.tag &= ~LIB_TAG_DOIT;
if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) {
DagNode *node2 = dag_get_node(forest, object2);
dag_add_relation(forest, obNode, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Source Object");
}
if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
GroupObject *go;
for (go = object2->dup_group->gobject.first;
go != NULL;
go = go->next)
{
if (go->ob == NULL) {
continue;
}
update_depsgraph_field_source_object(forest, obNode, object, go->ob);
}
}
SmokeModifierData *smd = (SmokeModifierData *) md;
return (smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll;
}
static void updateDepgraph(ModifierData *md, DagForest *forest,
struct Main *bmain,
struct Main *UNUSED(bmain),
struct Scene *scene, struct Object *ob,
DagNode *obNode)
{
SmokeModifierData *smd = (SmokeModifierData *) md;
Base *base;
if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) {
if (smd->domain->fluid_group || smd->domain->coll_group) {
GroupObject *go = NULL;
if (smd->domain->fluid_group)
for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) {
if (go->ob) {
SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
/* check for initialized smoke object */
if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) {
DagNode *curNode = dag_get_node(forest, go->ob);
dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow");
}
}
}
/* Actual code uses get_collisionobjects */
dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow");
dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll");
if (smd->domain->coll_group)
for (go = smd->domain->coll_group->gobject.first; go; go = go->next) {
if (go->ob) {
SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
/* check for initialized smoke object */
if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) {
DagNode *curNode = dag_get_node(forest, go->ob);
dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Coll");
}
}
}
}
else {
BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
base = scene->base.first;
for (; base; base = base->next) {
update_depsgraph_flow_coll_object(forest, obNode, base->object);
}
}
/* add relation to all "smoke flow" force fields */
base = scene->base.first;
BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
for (; base; base = base->next) {
update_depsgraph_field_source_object(forest, obNode, ob, base->object);
}
}
}
static void update_depsgraph_flow_coll_object_new(struct DepsNodeHandle *node,
Object *object2)
{
SmokeModifierData *smd;
if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
return;
}
object2->id.tag &= ~LIB_TAG_DOIT;
smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke);
if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) ||
((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll)))
{
DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Smoke Flow/Coll");
DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Smoke Flow/Coll");
}
if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
GroupObject *go;
for (go = object2->dup_group->gobject.first;
go != NULL;
go = go->next)
{
if (go->ob == NULL) {
continue;
}
update_depsgraph_flow_coll_object_new(node, go->ob);
}
}
}
static void update_depsgraph_field_source_object_new(struct DepsNodeHandle *node,
Object *object,
Object *object2)
{
if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
return;
}
object2->id.tag &= ~LIB_TAG_DOIT;
if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) {
DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Field Source Object");
DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Field Source Object");
}
if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
GroupObject *go;
for (go = object2->dup_group->gobject.first;
go != NULL;
go = go->next)
{
if (go->ob == NULL) {
continue;
}
update_depsgraph_field_source_object_new(node, object, go->ob);
}
dag_add_forcefield_relations(forest, scene, ob, obNode, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field");
}
}
static void updateDepsgraph(ModifierData *md,
struct Main *bmain,
struct Main *UNUSED(bmain),
struct Scene *scene,
Object *ob,
struct DepsNodeHandle *node)
{
SmokeModifierData *smd = (SmokeModifierData *)md;
Base *base;
if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) {
if (smd->domain->fluid_group || smd->domain->coll_group) {
GroupObject *go = NULL;
if (smd->domain->fluid_group != NULL) {
for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) {
if (go->ob != NULL) {
SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
/* Check for initialized smoke object. */
if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) {
DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Flow");
}
}
}
}
if (smd->domain->coll_group != NULL) {
for (go = smd->domain->coll_group->gobject.first; go; go = go->next) {
if (go->ob != NULL) {
SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
/* Check for initialized smoke object. */
if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) {
DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Coll");
}
}
}
}
}
else {
BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
base = scene->base.first;
for (; base; base = base->next) {
update_depsgraph_flow_coll_object_new(node, base->object);
}
}
/* add relation to all "smoke flow" force fields */
base = scene->base.first;
BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
for (; base; base = base->next) {
update_depsgraph_field_source_object_new(node, ob, base->object);
}
/* Actual code uses get_collisionobjects */
DEG_add_collision_relations(node, scene, ob, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow");
DEG_add_collision_relations(node, scene, ob, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll");
DEG_add_forcefield_relations(node, scene, ob, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field");
}
}

View File

@ -35,6 +35,7 @@
#include <stdio.h>
#include "DNA_scene_types.h"
#include "DNA_object_force.h"
#include "BLI_utildefines.h"
@ -42,6 +43,9 @@
#include "BKE_particle.h"
#include "BKE_softbody.h"
#include "depsgraph_private.h"
#include "DEG_depsgraph_build.h"
#include "MOD_modifiertypes.h"
static void deformVerts(ModifierData *md, Object *ob,
@ -58,6 +62,31 @@ static bool dependsOnTime(ModifierData *UNUSED(md))
return true;
}
static void updateDepgraph(ModifierData *UNUSED(md), DagForest *forest,
struct Main *UNUSED(bmain),
Scene *scene, Object *ob, DagNode *obNode)
{
if (ob->soft) {
/* Actual code uses ccd_build_deflector_hash */
dag_add_collision_relations(forest, scene, ob, obNode, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision");
dag_add_forcefield_relations(forest, scene, ob, obNode, ob->soft->effector_weights, true, 0, "Softbody Field");
}
}
static void updateDepsgraph(ModifierData *UNUSED(md),
struct Main *UNUSED(bmain),
struct Scene *scene,
Object *ob,
struct DepsNodeHandle *node)
{
if (ob->soft) {
/* Actual code uses ccd_build_deflector_hash */
DEG_add_collision_relations(node, scene, ob, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision");
DEG_add_forcefield_relations(node, scene, ob, ob->soft->effector_weights, true, 0, "Softbody Field");
}
}
ModifierTypeInfo modifierType_Softbody = {
/* name */ "Softbody",
@ -80,8 +109,8 @@ ModifierTypeInfo modifierType_Softbody = {
/* requiredDataMask */ NULL,
/* freeData */ NULL,
/* isDisabled */ NULL,
/* updateDepgraph */ NULL,
/* updateDepsgraph */ NULL,
/* updateDepgraph */ updateDepgraph,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ dependsOnTime,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ NULL,