diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2015-05-12 10:50:24 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2015-05-12 14:06:36 +0300 |
commit | dbbe721c2afda4a3b5c8ea7e60cf3f2b011cc857 (patch) | |
tree | 5ebab38550265a62a2d62a923950eb6037caf9c5 | |
parent | ae00e42bc291c5ff3914ff604892f3d79eaa999c (diff) |
Depsgraph: Move update-related functions into own files
Currently it is just moving existing functions into a new file,
but in the future those new files will be grown much more due
to upcoming more granular scene updates.
Should be no functional changes.
-rw-r--r-- | source/blender/blenkernel/BKE_armature.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/armature.c | 492 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/armature_update.c | 549 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 142 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object_update.c | 204 |
7 files changed, 767 insertions, 631 deletions
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index de767d39216..ada07817d31 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -142,6 +142,11 @@ void b_bone_spline_setup(struct bPoseChannel *pchan, int rest, Mat4 result_array #define PBONE_SELECTABLE(arm, bone) \ (PBONE_VISIBLE(arm, bone) && !((bone)->flag & BONE_UNSELECTABLE)) +/* Evaluation helpers */ + +void BKE_pose_splineik_init_tree(struct Scene *scene, struct Object *ob, float ctime); +void BKE_splineik_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan_root, float ctime); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 0e0c779c033..d287eb4a057 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -178,6 +178,10 @@ void BKE_object_tfm_protected_restore(struct Object *ob, const ObjectTfmProtectedChannels *obtfm, const short protectflag); +/* Dependency graph evaluation callbacks. */ +void BKE_object_handle_data_update(struct EvaluationContext *eval_ctx, + struct Scene *scene, + struct Object *ob); void BKE_object_handle_update(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob); void BKE_object_handle_update_ex(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 67c8f0fc558..fe713e64c64 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -67,6 +67,7 @@ set(SRC intern/anim_sys.c intern/appdir.c intern/armature.c + intern/armature_update.c intern/autoexec.c intern/blender.c intern/bmfont.c @@ -134,6 +135,7 @@ set(SRC intern/object.c intern/object_deform.c intern/object_dupli.c + intern/object_update.c intern/ocean.c intern/outliner_treehash.c intern/packedFile.c diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index dc11bcb00aa..d5416b41b44 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -1835,494 +1835,6 @@ void BKE_pose_rebuild(Object *ob, bArmature *arm) BKE_pose_channels_hash_make(ob->pose); } - -/* ********************** SPLINE IK SOLVER ******************* */ - -/* Temporary evaluation tree data used for Spline IK */ -typedef struct tSplineIK_Tree { - struct tSplineIK_Tree *next, *prev; - - int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */ - - bool free_points; /* free the point positions array */ - short chainlen; /* number of bones in the chain */ - - float *points; /* parametric positions for the joints along the curve */ - bPoseChannel **chain; /* chain of bones to affect using Spline IK (ordered from the tip) */ - - bPoseChannel *root; /* bone that is the root node of the chain */ - - bConstraint *con; /* constraint for this chain */ - bSplineIKConstraint *ikData; /* constraint settings for this chain */ -} tSplineIK_Tree; - -/* ----------- */ - -/* Tag the bones in the chain formed by the given bone for IK */ -static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPoseChannel *pchan_tip) -{ - bPoseChannel *pchan, *pchanRoot = NULL; - bPoseChannel *pchanChain[255]; - bConstraint *con = NULL; - bSplineIKConstraint *ikData = NULL; - float boneLengths[255], *jointPoints; - float totLength = 0.0f; - bool free_joints = 0; - int segcount = 0; - - /* find the SplineIK constraint */ - for (con = pchan_tip->constraints.first; con; con = con->next) { - if (con->type == CONSTRAINT_TYPE_SPLINEIK) { - ikData = con->data; - - /* target can only be curve */ - if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE)) - continue; - /* skip if disabled */ - if ((con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF))) - continue; - - /* otherwise, constraint is ok... */ - break; - } - } - if (con == NULL) - return; - - /* make sure that the constraint targets are ok - * - this is a workaround for a depsgraph bug... - */ - if (ikData->tar) { - /* note: when creating constraints that follow path, the curve gets the CU_PATH set now, - * currently for paths to work it needs to go through the bevlist/displist system (ton) - */ - - /* only happens on reload file, but violates depsgraph still... fix! */ - if (ELEM(NULL, ikData->tar->curve_cache, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) { - BKE_displist_make_curveTypes(scene, ikData->tar, 0); - - /* path building may fail in EditMode after removing verts [#33268]*/ - if (ELEM(NULL, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) { - /* BLI_assert(cu->path != NULL); */ - return; - } - } - } - - /* find the root bone and the chain of bones from the root to the tip - * NOTE: this assumes that the bones are connected, but that may not be true... */ - for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen); pchan = pchan->parent, segcount++) { - /* store this segment in the chain */ - pchanChain[segcount] = pchan; - - /* if performing rebinding, calculate the length of the bone */ - boneLengths[segcount] = pchan->bone->length; - totLength += boneLengths[segcount]; - } - - if (segcount == 0) - return; - else - pchanRoot = pchanChain[segcount - 1]; - - /* perform binding step if required */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) { - float segmentLen = (1.0f / (float)segcount); - int i; - - /* setup new empty array for the points list */ - if (ikData->points) - MEM_freeN(ikData->points); - ikData->numpoints = ikData->chainlen + 1; - ikData->points = MEM_mallocN(sizeof(float) * ikData->numpoints, "Spline IK Binding"); - - /* bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint) */ - ikData->points[0] = 1.0f; - - /* perform binding of the joints to parametric positions along the curve based - * proportion of the total length that each bone occupies - */ - for (i = 0; i < segcount; i++) { - /* 'head' joints, traveling towards the root of the chain - * - 2 methods; the one chosen depends on whether we've got usable lengths - */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totLength == 0.0f)) { - /* 1) equi-spaced joints */ - ikData->points[i + 1] = ikData->points[i] - segmentLen; - } - else { - /* 2) to find this point on the curve, we take a step from the previous joint - * a distance given by the proportion that this bone takes - */ - ikData->points[i + 1] = ikData->points[i] - (boneLengths[i] / totLength); - } - } - - /* spline has now been bound */ - ikData->flag |= CONSTRAINT_SPLINEIK_BOUND; - } - - /* disallow negative values (happens with float precision) */ - CLAMP_MIN(ikData->points[segcount], 0.0f); - - /* apply corrections for sensitivity to scaling on a copy of the bind points, - * since it's easier to determine the positions of all the joints beforehand this way - */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (totLength != 0.0f)) { - float splineLen, maxScale; - int i; - - /* make a copy of the points array, that we'll store in the tree - * - although we could just multiply the points on the fly, this approach means that - * we can introduce per-segment stretchiness later if it is necessary - */ - jointPoints = MEM_dupallocN(ikData->points); - free_joints = 1; - - /* get the current length of the curve */ - /* NOTE: this is assumed to be correct even after the curve was resized */ - splineLen = ikData->tar->curve_cache->path->totdist; - - /* calculate the scale factor to multiply all the path values by so that the - * bone chain retains its current length, such that - * maxScale * splineLen = totLength - */ - maxScale = totLength / splineLen; - - /* apply scaling correction to all of the temporary points */ - /* TODO: this is really not adequate enough on really short chains */ - for (i = 0; i < segcount; i++) - jointPoints[i] *= maxScale; - } - else { - /* just use the existing points array */ - jointPoints = ikData->points; - free_joints = 0; - } - - /* make a new Spline-IK chain, and store it in the IK chains */ - /* TODO: we should check if there is already an IK chain on this, since that would take presidence... */ - { - /* make new tree */ - tSplineIK_Tree *tree = MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree"); - tree->type = CONSTRAINT_TYPE_SPLINEIK; - - tree->chainlen = segcount; - - /* copy over the array of links to bones in the chain (from tip to root) */ - tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain"); - memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount); - - /* store reference to joint position array */ - tree->points = jointPoints; - tree->free_points = free_joints; - - /* store references to different parts of the chain */ - tree->root = pchanRoot; - tree->con = con; - tree->ikData = ikData; - - /* AND! link the tree to the root */ - BLI_addtail(&pchanRoot->siktree, tree); - } - - /* mark root channel having an IK tree */ - pchanRoot->flag |= POSE_IKSPLINE; -} - -/* Tag which bones are members of Spline IK chains */ -static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime)) -{ - bPoseChannel *pchan; - - /* find the tips of Spline IK chains, which are simply the bones which have been tagged as such */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->constflag & PCHAN_HAS_SPLINEIK) - splineik_init_tree_from_pchan(scene, ob, pchan); - } -} - -/* ----------- */ - -/* Evaluate spline IK for a given bone */ -static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan, - int index, float ctime) -{ - bSplineIKConstraint *ikData = tree->ikData; - float poseHead[3], poseTail[3], poseMat[4][4]; - float splineVec[3], scaleFac, radius = 1.0f; - - /* firstly, calculate the bone matrix the standard way, since this is needed for roll control */ - BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); - - copy_v3_v3(poseHead, pchan->pose_head); - copy_v3_v3(poseTail, pchan->pose_tail); - - /* step 1: determine the positions for the endpoints of the bone */ - { - float vec[4], dir[3], rad; - float tailBlendFac = 1.0f; - - /* determine if the bone should still be affected by SplineIK */ - if (tree->points[index + 1] >= 1.0f) { - /* spline doesn't affect the bone anymore, so done... */ - pchan->flag |= POSE_DONE; - return; - } - else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) { - /* blending factor depends on the amount of the bone still left on the chain */ - tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]); - } - - /* tail endpoint */ - if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) { - /* apply curve's object-mode transforms to the position - * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) - */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) - mul_m4_v3(ikData->tar->obmat, vec); - - /* convert the position to pose-space, then store it */ - mul_m4_v3(ob->imat, vec); - interp_v3_v3v3(poseTail, pchan->pose_tail, vec, tailBlendFac); - - /* set the new radius */ - radius = rad; - } - - /* head endpoint */ - if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) { - /* apply curve's object-mode transforms to the position - * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) - */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) - mul_m4_v3(ikData->tar->obmat, vec); - - /* store the position, and convert it to pose space */ - mul_m4_v3(ob->imat, vec); - copy_v3_v3(poseHead, vec); - - /* set the new radius (it should be the average value) */ - radius = (radius + rad) / 2; - } - } - - /* step 2: determine the implied transform from these endpoints - * - splineVec: the vector direction that the spline applies on the bone - * - scaleFac: the factor that the bone length is scaled by to get the desired amount - */ - sub_v3_v3v3(splineVec, poseTail, poseHead); - scaleFac = len_v3(splineVec) / pchan->bone->length; - - /* step 3: compute the shortest rotation needed to map from the bone rotation to the current axis - * - this uses the same method as is used for the Damped Track Constraint (see the code there for details) - */ - { - float dmat[3][3], rmat[3][3], tmat[3][3]; - float raxis[3], rangle; - - /* compute the raw rotation matrix from the bone's current matrix by extracting only the - * orientation-relevant axes, and normalizing them - */ - copy_v3_v3(rmat[0], pchan->pose_mat[0]); - copy_v3_v3(rmat[1], pchan->pose_mat[1]); - copy_v3_v3(rmat[2], pchan->pose_mat[2]); - normalize_m3(rmat); - - /* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */ - normalize_v3(splineVec); - - /* calculate smallest axis-angle rotation necessary for getting from the - * current orientation of the bone, to the spline-imposed direction - */ - cross_v3_v3v3(raxis, rmat[1], splineVec); - - rangle = dot_v3v3(rmat[1], splineVec); - CLAMP(rangle, -1.0f, 1.0f); - rangle = acosf(rangle); - - /* multiply the magnitude of the angle by the influence of the constraint to - * control the influence of the SplineIK effect - */ - rangle *= tree->con->enforce; - - /* construct rotation matrix from the axis-angle rotation found above - * - this call takes care to make sure that the axis provided is a unit vector first - */ - axis_angle_to_mat3(dmat, raxis, rangle); - - /* combine these rotations so that the y-axis of the bone is now aligned as the spline dictates, - * while still maintaining roll control from the existing bone animation - */ - mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */ - normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */ - copy_m4_m3(poseMat, tmat); - } - - /* step 4: set the scaling factors for the axes */ - { - /* only multiply the y-axis by the scaling factor to get nice volume-preservation */ - mul_v3_fl(poseMat[1], scaleFac); - - /* set the scaling factors of the x and z axes from... */ - switch (ikData->xzScaleMode) { - case CONSTRAINT_SPLINEIK_XZS_ORIGINAL: - { - /* original scales get used */ - float scale; - - /* x-axis scale */ - scale = len_v3(pchan->pose_mat[0]); - mul_v3_fl(poseMat[0], scale); - /* z-axis scale */ - scale = len_v3(pchan->pose_mat[2]); - mul_v3_fl(poseMat[2], scale); - break; - } - case CONSTRAINT_SPLINEIK_XZS_INVERSE: - { - /* old 'volume preservation' method using the inverse scale */ - float scale; - - /* calculate volume preservation factor which is - * basically the inverse of the y-scaling factor - */ - if (fabsf(scaleFac) != 0.0f) { - scale = 1.0f / fabsf(scaleFac); - - /* we need to clamp this within sensible values */ - /* NOTE: these should be fine for now, but should get sanitised in future */ - CLAMP(scale, 0.0001f, 100000.0f); - } - else - scale = 1.0f; - - /* apply the scaling */ - mul_v3_fl(poseMat[0], scale); - mul_v3_fl(poseMat[2], scale); - break; - } - case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: - { - /* improved volume preservation based on the Stretch To constraint */ - float final_scale; - - /* as the basis for volume preservation, we use the inverse scale factor... */ - if (fabsf(scaleFac) != 0.0f) { - /* NOTE: The method here is taken wholesale from the Stretch To constraint */ - float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge); - - if (bulge > 1.0f) { - if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) { - float bulge_max = max_ff(ikData->bulge_max, 1.0f); - float hard = min_ff(bulge, bulge_max); - - float range = bulge_max - 1.0f; - float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2; - - bulge = interpf(soft, hard, ikData->bulge_smooth); - } - } - if (bulge < 1.0f) { - if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) { - float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f); - float hard = max_ff(bulge, bulge_min); - - float range = 1.0f - bulge_min; - float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2; - - bulge = interpf(soft, hard, ikData->bulge_smooth); - } - } - - /* compute scale factor for xz axes from this value */ - final_scale = sqrtf(bulge); - } - else { - /* no scaling, so scale factor is simple */ - final_scale = 1.0f; - } - - /* apply the scaling (assuming normalised scale) */ - mul_v3_fl(poseMat[0], final_scale); - mul_v3_fl(poseMat[2], final_scale); - break; - } - } - - /* finally, multiply the x and z scaling by the radius of the curve too, - * to allow automatic scales to get tweaked still - */ - if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) { - mul_v3_fl(poseMat[0], radius); - mul_v3_fl(poseMat[2], radius); - } - } - - /* step 5: set the location of the bone in the matrix */ - if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) { - /* when the 'no-root' option is affected, the chain can retain - * the shape but be moved elsewhere - */ - copy_v3_v3(poseHead, pchan->pose_head); - } - else if (tree->con->enforce < 1.0f) { - /* when the influence is too low - * - blend the positions for the 'root' bone - * - stick to the parent for any other - */ - if (pchan->parent) { - copy_v3_v3(poseHead, pchan->pose_head); - } - else { - /* FIXME: this introduces popping artifacts when we reach 0.0 */ - interp_v3_v3v3(poseHead, pchan->pose_head, poseHead, tree->con->enforce); - } - } - copy_v3_v3(poseMat[3], poseHead); - - /* finally, store the new transform */ - copy_m4_m4(pchan->pose_mat, poseMat); - copy_v3_v3(pchan->pose_head, poseHead); - - /* recalculate tail, as it's now outdated after the head gets adjusted above! */ - BKE_pose_where_is_bone_tail(pchan); - - /* done! */ - pchan->flag |= POSE_DONE; -} - -/* Evaluate the chain starting from the nominated bone */ -static void splineik_execute_tree(Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime) -{ - tSplineIK_Tree *tree; - - /* for each pose-tree, execute it if it is spline, otherwise just free it */ - while ((tree = pchan_root->siktree.first) != NULL) { - int i; - - /* walk over each bone in the chain, calculating the effects of spline IK - * - the chain is traversed in the opposite order to storage order (i.e. parent to children) - * so that dependencies are correct - */ - for (i = tree->chainlen - 1; i >= 0; i--) { - bPoseChannel *pchan = tree->chain[i]; - splineik_evaluate_bone(tree, scene, ob, pchan, i, ctime); - } - - /* free the tree info specific to SplineIK trees now */ - if (tree->chain) - MEM_freeN(tree->chain); - if (tree->free_points) - MEM_freeN(tree->points); - - /* free this tree */ - BLI_freelinkN(&pchan_root->siktree, tree); - } -} - /* ********************** THE POSE SOLVER ******************* */ /* loc/rot/size to given mat4 */ @@ -2623,7 +2135,7 @@ void BKE_pose_where_is(Scene *scene, Object *ob) * - this is not integrated as an IK plugin, since it should be able * to function in conjunction with standard IK */ - splineik_init_tree(scene, ob, ctime); + BKE_pose_splineik_init_tree(scene, ob, ctime); /* 3. the main loop, channels are already hierarchical sorted from root to children */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { @@ -2633,7 +2145,7 @@ void BKE_pose_where_is(Scene *scene, Object *ob) } /* 4b. if we find a Spline IK root, we handle it separated too */ else if (pchan->flag & POSE_IKSPLINE) { - splineik_execute_tree(scene, ob, pchan, ctime); + BKE_splineik_execute_tree(scene, ob, pchan, ctime); } /* 5. otherwise just call the normal solver */ else if (!(pchan->flag & POSE_DONE)) { diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c new file mode 100644 index 00000000000..f7aee3fa2d4 --- /dev/null +++ b/source/blender/blenkernel/intern/armature_update.c @@ -0,0 +1,549 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + * + * Defines and code for core node types + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_action.h" +#include "BKE_anim.h" +#include "BKE_armature.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_displist.h" +#include "BKE_fcurve.h" +#include "BKE_scene.h" + +#include "BIK_api.h" + +#include "BKE_global.h" +#include "BKE_main.h" + +/* ********************** SPLINE IK SOLVER ******************* */ + +/* Temporary evaluation tree data used for Spline IK */ +typedef struct tSplineIK_Tree { + struct tSplineIK_Tree *next, *prev; + + int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */ + + bool free_points; /* free the point positions array */ + short chainlen; /* number of bones in the chain */ + + float *points; /* parametric positions for the joints along the curve */ + bPoseChannel **chain; /* chain of bones to affect using Spline IK (ordered from the tip) */ + + bPoseChannel *root; /* bone that is the root node of the chain */ + + bConstraint *con; /* constraint for this chain */ + bSplineIKConstraint *ikData; /* constraint settings for this chain */ +} tSplineIK_Tree; + +/* ----------- */ + +/* Tag the bones in the chain formed by the given bone for IK */ +static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPoseChannel *pchan_tip) +{ + bPoseChannel *pchan, *pchanRoot = NULL; + bPoseChannel *pchanChain[255]; + bConstraint *con = NULL; + bSplineIKConstraint *ikData = NULL; + float boneLengths[255], *jointPoints; + float totLength = 0.0f; + bool free_joints = 0; + int segcount = 0; + + /* find the SplineIK constraint */ + for (con = pchan_tip->constraints.first; con; con = con->next) { + if (con->type == CONSTRAINT_TYPE_SPLINEIK) { + ikData = con->data; + + /* target can only be curve */ + if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE)) + continue; + /* skip if disabled */ + if ((con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF))) + continue; + + /* otherwise, constraint is ok... */ + break; + } + } + if (con == NULL) + return; + + /* make sure that the constraint targets are ok + * - this is a workaround for a depsgraph bug... + */ + if (ikData->tar) { + /* note: when creating constraints that follow path, the curve gets the CU_PATH set now, + * currently for paths to work it needs to go through the bevlist/displist system (ton) + */ + + /* only happens on reload file, but violates depsgraph still... fix! */ + if (ELEM(NULL, ikData->tar->curve_cache, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) { + BKE_displist_make_curveTypes(scene, ikData->tar, 0); + + /* path building may fail in EditMode after removing verts [#33268]*/ + if (ELEM(NULL, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) { + /* BLI_assert(cu->path != NULL); */ + return; + } + } + } + + /* find the root bone and the chain of bones from the root to the tip + * NOTE: this assumes that the bones are connected, but that may not be true... */ + for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen); pchan = pchan->parent, segcount++) { + /* store this segment in the chain */ + pchanChain[segcount] = pchan; + + /* if performing rebinding, calculate the length of the bone */ + boneLengths[segcount] = pchan->bone->length; + totLength += boneLengths[segcount]; + } + + if (segcount == 0) + return; + else + pchanRoot = pchanChain[segcount - 1]; + + /* perform binding step if required */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) { + float segmentLen = (1.0f / (float)segcount); + int i; + + /* setup new empty array for the points list */ + if (ikData->points) + MEM_freeN(ikData->points); + ikData->numpoints = ikData->chainlen + 1; + ikData->points = MEM_mallocN(sizeof(float) * ikData->numpoints, "Spline IK Binding"); + + /* bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint) */ + ikData->points[0] = 1.0f; + + /* perform binding of the joints to parametric positions along the curve based + * proportion of the total length that each bone occupies + */ + for (i = 0; i < segcount; i++) { + /* 'head' joints, traveling towards the root of the chain + * - 2 methods; the one chosen depends on whether we've got usable lengths + */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totLength == 0.0f)) { + /* 1) equi-spaced joints */ + ikData->points[i + 1] = ikData->points[i] - segmentLen; + } + else { + /* 2) to find this point on the curve, we take a step from the previous joint + * a distance given by the proportion that this bone takes + */ + ikData->points[i + 1] = ikData->points[i] - (boneLengths[i] / totLength); + } + } + + /* spline has now been bound */ + ikData->flag |= CONSTRAINT_SPLINEIK_BOUND; + } + + /* disallow negative values (happens with float precision) */ + CLAMP_MIN(ikData->points[segcount], 0.0f); + + /* apply corrections for sensitivity to scaling on a copy of the bind points, + * since it's easier to determine the positions of all the joints beforehand this way + */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (totLength != 0.0f)) { + float splineLen, maxScale; + int i; + + /* make a copy of the points array, that we'll store in the tree + * - although we could just multiply the points on the fly, this approach means that + * we can introduce per-segment stretchiness later if it is necessary + */ + jointPoints = MEM_dupallocN(ikData->points); + free_joints = 1; + + /* get the current length of the curve */ + /* NOTE: this is assumed to be correct even after the curve was resized */ + splineLen = ikData->tar->curve_cache->path->totdist; + + /* calculate the scale factor to multiply all the path values by so that the + * bone chain retains its current length, such that + * maxScale * splineLen = totLength + */ + maxScale = totLength / splineLen; + + /* apply scaling correction to all of the temporary points */ + /* TODO: this is really not adequate enough on really short chains */ + for (i = 0; i < segcount; i++) + jointPoints[i] *= maxScale; + } + else { + /* just use the existing points array */ + jointPoints = ikData->points; + free_joints = 0; + } + + /* make a new Spline-IK chain, and store it in the IK chains */ + /* TODO: we should check if there is already an IK chain on this, since that would take presidence... */ + { + /* make new tree */ + tSplineIK_Tree *tree = MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree"); + tree->type = CONSTRAINT_TYPE_SPLINEIK; + + tree->chainlen = segcount; + + /* copy over the array of links to bones in the chain (from tip to root) */ + tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain"); + memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount); + + /* store reference to joint position array */ + tree->points = jointPoints; + tree->free_points = free_joints; + + /* store references to different parts of the chain */ + tree->root = pchanRoot; + tree->con = con; + tree->ikData = ikData; + + /* AND! link the tree to the root */ + BLI_addtail(&pchanRoot->siktree, tree); + } + + /* mark root channel having an IK tree */ + pchanRoot->flag |= POSE_IKSPLINE; +} + +/* Tag which bones are members of Spline IK chains */ +static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime)) +{ + bPoseChannel *pchan; + + /* find the tips of Spline IK chains, which are simply the bones which have been tagged as such */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->constflag & PCHAN_HAS_SPLINEIK) + splineik_init_tree_from_pchan(scene, ob, pchan); + } +} + +/* ----------- */ + +/* Evaluate spline IK for a given bone */ +static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan, + int index, float ctime) +{ + bSplineIKConstraint *ikData = tree->ikData; + float poseHead[3], poseTail[3], poseMat[4][4]; + float splineVec[3], scaleFac, radius = 1.0f; + + /* firstly, calculate the bone matrix the standard way, since this is needed for roll control */ + BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + + copy_v3_v3(poseHead, pchan->pose_head); + copy_v3_v3(poseTail, pchan->pose_tail); + + /* step 1: determine the positions for the endpoints of the bone */ + { + float vec[4], dir[3], rad; + float tailBlendFac = 1.0f; + + /* determine if the bone should still be affected by SplineIK */ + if (tree->points[index + 1] >= 1.0f) { + /* spline doesn't affect the bone anymore, so done... */ + pchan->flag |= POSE_DONE; + return; + } + else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) { + /* blending factor depends on the amount of the bone still left on the chain */ + tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]); + } + + /* tail endpoint */ + if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) { + /* apply curve's object-mode transforms to the position + * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) + */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) + mul_m4_v3(ikData->tar->obmat, vec); + + /* convert the position to pose-space, then store it */ + mul_m4_v3(ob->imat, vec); + interp_v3_v3v3(poseTail, pchan->pose_tail, vec, tailBlendFac); + + /* set the new radius */ + radius = rad; + } + + /* head endpoint */ + if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) { + /* apply curve's object-mode transforms to the position + * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) + */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) + mul_m4_v3(ikData->tar->obmat, vec); + + /* store the position, and convert it to pose space */ + mul_m4_v3(ob->imat, vec); + copy_v3_v3(poseHead, vec); + + /* set the new radius (it should be the average value) */ + radius = (radius + rad) / 2; + } + } + + /* step 2: determine the implied transform from these endpoints + * - splineVec: the vector direction that the spline applies on the bone + * - scaleFac: the factor that the bone length is scaled by to get the desired amount + */ + sub_v3_v3v3(splineVec, poseTail, poseHead); + scaleFac = len_v3(splineVec) / pchan->bone->length; + + /* step 3: compute the shortest rotation needed to map from the bone rotation to the current axis + * - this uses the same method as is used for the Damped Track Constraint (see the code there for details) + */ + { + float dmat[3][3], rmat[3][3], tmat[3][3]; + float raxis[3], rangle; + + /* compute the raw rotation matrix from the bone's current matrix by extracting only the + * orientation-relevant axes, and normalizing them + */ + copy_v3_v3(rmat[0], pchan->pose_mat[0]); + copy_v3_v3(rmat[1], pchan->pose_mat[1]); + copy_v3_v3(rmat[2], pchan->pose_mat[2]); + normalize_m3(rmat); + + /* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */ + normalize_v3(splineVec); + + /* calculate smallest axis-angle rotation necessary for getting from the + * current orientation of the bone, to the spline-imposed direction + */ + cross_v3_v3v3(raxis, rmat[1], splineVec); + + rangle = dot_v3v3(rmat[1], splineVec); + CLAMP(rangle, -1.0f, 1.0f); + rangle = acosf(rangle); + + /* multiply the magnitude of the angle by the influence of the constraint to + * control the influence of the SplineIK effect + */ + rangle *= tree->con->enforce; + + /* construct rotation matrix from the axis-angle rotation found above + * - this call takes care to make sure that the axis provided is a unit vector first + */ + axis_angle_to_mat3(dmat, raxis, rangle); + + /* combine these rotations so that the y-axis of the bone is now aligned as the spline dictates, + * while still maintaining roll control from the existing bone animation + */ + mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */ + normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */ + copy_m4_m3(poseMat, tmat); + } + + /* step 4: set the scaling factors for the axes */ + { + /* only multiply the y-axis by the scaling factor to get nice volume-preservation */ + mul_v3_fl(poseMat[1], scaleFac); + + /* set the scaling factors of the x and z axes from... */ + switch (ikData->xzScaleMode) { + case CONSTRAINT_SPLINEIK_XZS_ORIGINAL: + { + /* original scales get used */ + float scale; + + /* x-axis scale */ + scale = len_v3(pchan->pose_mat[0]); + mul_v3_fl(poseMat[0], scale); + /* z-axis scale */ + scale = len_v3(pchan->pose_mat[2]); + mul_v3_fl(poseMat[2], scale); + break; + } + case CONSTRAINT_SPLINEIK_XZS_INVERSE: + { + /* old 'volume preservation' method using the inverse scale */ + float scale; + + /* calculate volume preservation factor which is + * basically the inverse of the y-scaling factor + */ + if (fabsf(scaleFac) != 0.0f) { + scale = 1.0f / fabsf(scaleFac); + + /* we need to clamp this within sensible values */ + /* NOTE: these should be fine for now, but should get sanitised in future */ + CLAMP(scale, 0.0001f, 100000.0f); + } + else + scale = 1.0f; + + /* apply the scaling */ + mul_v3_fl(poseMat[0], scale); + mul_v3_fl(poseMat[2], scale); + break; + } + case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: + { + /* improved volume preservation based on the Stretch To constraint */ + float final_scale; + + /* as the basis for volume preservation, we use the inverse scale factor... */ + if (fabsf(scaleFac) != 0.0f) { + /* NOTE: The method here is taken wholesale from the Stretch To constraint */ + float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge); + + if (bulge > 1.0f) { + if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) { + float bulge_max = max_ff(ikData->bulge_max, 1.0f); + float hard = min_ff(bulge, bulge_max); + + float range = bulge_max - 1.0f; + float scale = (range > 0.0f) ? 1.0f / range : 0.0f; + float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2; + + bulge = interpf(soft, hard, ikData->bulge_smooth); + } + } + if (bulge < 1.0f) { + if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) { + float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f); + float hard = max_ff(bulge, bulge_min); + + float range = 1.0f - bulge_min; + float scale = (range > 0.0f) ? 1.0f / range : 0.0f; + float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2; + + bulge = interpf(soft, hard, ikData->bulge_smooth); + } + } + + /* compute scale factor for xz axes from this value */ + final_scale = sqrtf(bulge); + } + else { + /* no scaling, so scale factor is simple */ + final_scale = 1.0f; + } + + /* apply the scaling (assuming normalised scale) */ + mul_v3_fl(poseMat[0], final_scale); + mul_v3_fl(poseMat[2], final_scale); + break; + } + } + + /* finally, multiply the x and z scaling by the radius of the curve too, + * to allow automatic scales to get tweaked still + */ + if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) { + mul_v3_fl(poseMat[0], radius); + mul_v3_fl(poseMat[2], radius); + } + } + + /* step 5: set the location of the bone in the matrix */ + if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) { + /* when the 'no-root' option is affected, the chain can retain + * the shape but be moved elsewhere + */ + copy_v3_v3(poseHead, pchan->pose_head); + } + else if (tree->con->enforce < 1.0f) { + /* when the influence is too low + * - blend the positions for the 'root' bone + * - stick to the parent for any other + */ + if (pchan->parent) { + copy_v3_v3(poseHead, pchan->pose_head); + } + else { + /* FIXME: this introduces popping artifacts when we reach 0.0 */ + interp_v3_v3v3(poseHead, pchan->pose_head, poseHead, tree->con->enforce); + } + } + copy_v3_v3(poseMat[3], poseHead); + + /* finally, store the new transform */ + copy_m4_m4(pchan->pose_mat, poseMat); + copy_v3_v3(pchan->pose_head, poseHead); + + /* recalculate tail, as it's now outdated after the head gets adjusted above! */ + BKE_pose_where_is_bone_tail(pchan); + + /* done! */ + pchan->flag |= POSE_DONE; +} + +/* Evaluate the chain starting from the nominated bone */ +static void splineik_execute_tree(Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime) +{ + tSplineIK_Tree *tree; + + /* for each pose-tree, execute it if it is spline, otherwise just free it */ + while ((tree = pchan_root->siktree.first) != NULL) { + int i; + + /* walk over each bone in the chain, calculating the effects of spline IK + * - the chain is traversed in the opposite order to storage order (i.e. parent to children) + * so that dependencies are correct + */ + for (i = tree->chainlen - 1; i >= 0; i--) { + bPoseChannel *pchan = tree->chain[i]; + splineik_evaluate_bone(tree, scene, ob, pchan, i, ctime); + } + + /* free the tree info specific to SplineIK trees now */ + if (tree->chain) + MEM_freeN(tree->chain); + if (tree->free_points) + MEM_freeN(tree->points); + + /* free this tree */ + BLI_freelinkN(&pchan_root->siktree, tree); + } +} + +void BKE_pose_splineik_init_tree(Scene *scene, Object *ob, float ctime) +{ + splineik_init_tree(scene, ob, ctime); +} + +void BKE_splineik_execute_tree(Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime) +{ + splineik_execute_tree(scene, ob, pchan_root, ctime); +} diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 5b29187e7cd..d5f070bc770 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -3046,147 +3046,7 @@ void BKE_object_handle_update_ex(EvaluationContext *eval_ctx, } if (ob->recalc & OB_RECALC_DATA) { - ID *data_id = (ID *)ob->data; - AnimData *adt = BKE_animdata_from_id(data_id); - Key *key; - float ctime = BKE_scene_frame_get(scene); - - if (G.debug & G_DEBUG_DEPSGRAPH) - printf("recalcdata %s\n", ob->id.name + 2); - - if (adt) { - /* evaluate drivers - datalevel */ - /* XXX: for mesh types, should we push this to derivedmesh instead? */ - BKE_animsys_evaluate_animdata(scene, data_id, adt, ctime, ADT_RECALC_DRIVERS); - } - - key = BKE_key_from_object(ob); - if (key && key->block.first) { - if (!(ob->shapeflag & OB_SHAPE_LOCK)) - BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS); - } - - /* includes all keys and modifiers */ - switch (ob->type) { - case OB_MESH: - { - BMEditMesh *em = (ob == scene->obedit) ? BKE_editmesh_from_object(ob) : NULL; - uint64_t data_mask = scene->customdata_mask | CD_MASK_BAREMESH; -#ifdef WITH_FREESTYLE - /* make sure Freestyle edge/face marks appear in DM for render (see T40315) */ - if (eval_ctx->mode != DAG_EVAL_VIEWPORT) { - data_mask |= CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; - } -#endif - if (em) { - makeDerivedMesh(scene, ob, em, data_mask, 0); /* was CD_MASK_BAREMESH */ - } - else { - makeDerivedMesh(scene, ob, NULL, data_mask, 0); - } - break; - } - case OB_ARMATURE: - if (ob->id.lib && ob->proxy_from) { - if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { - printf("Proxy copy error, lib Object: %s proxy Object: %s\n", - ob->id.name + 2, ob->proxy_from->id.name + 2); - } - } - else { - BKE_pose_where_is(scene, ob); - } - break; - - case OB_MBALL: - BKE_displist_make_mball(eval_ctx, scene, ob); - break; - - case OB_CURVE: - case OB_SURF: - case OB_FONT: - BKE_displist_make_curveTypes(scene, ob, 0); - break; - - case OB_LATTICE: - BKE_lattice_modifiers_calc(scene, ob); - break; - - case OB_EMPTY: - if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data) - if (BKE_image_is_animated(ob->data)) - BKE_image_user_check_frame_calc(ob->iuser, (int)ctime, 0); - break; - } - - /* related materials */ - /* XXX: without depsgraph tagging, this will always need to be run, which will be slow! - * However, not doing anything (or trying to hack around this lack) is not an option - * anymore, especially due to Cycles [#31834] - */ - if (ob->totcol) { - int a; - - for (a = 1; a <= ob->totcol; a++) { - Material *ma = give_current_material(ob, a); - - if (ma) { - /* recursively update drivers for this material */ - material_drivers_update(scene, ma, ctime); - } - } - } - else if (ob->type == OB_LAMP) - lamp_drivers_update(scene, ob->data, ctime); - - /* particles */ - if (ob != scene->obedit && ob->particlesystem.first) { - ParticleSystem *tpsys, *psys; - DerivedMesh *dm; - ob->transflag &= ~OB_DUPLIPARTS; - - psys = ob->particlesystem.first; - while (psys) { - /* ensure this update always happens even if psys is disabled */ - if (psys->recalc & PSYS_RECALC_TYPE) { - psys_changed_type(ob, psys); - } - - if (psys_check_enabled(ob, psys)) { - /* check use of dupli objects here */ - if (psys->part && (psys->part->draw_as == PART_DRAW_REND || eval_ctx->mode == DAG_EVAL_RENDER) && - ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) || - (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group))) - { - ob->transflag |= OB_DUPLIPARTS; - } - - particle_system_update(scene, ob, psys); - psys = psys->next; - } - else if (psys->flag & PSYS_DELETE) { - tpsys = psys->next; - BLI_remlink(&ob->particlesystem, psys); - psys_free(ob, psys); - psys = tpsys; - } - else - psys = psys->next; - } - - if (eval_ctx->mode == DAG_EVAL_RENDER && ob->transflag & OB_DUPLIPARTS) { - /* this is to make sure we get render level duplis in groups: - * the derivedmesh must be created before init_render_mesh, - * since object_duplilist does dupliparticles before that */ - dm = mesh_create_derived_render(scene, ob, CD_MASK_BAREMESH | CD_MASK_MTFACE | CD_MASK_MCOL); - dm->release(dm); - - for (psys = ob->particlesystem.first; psys; psys = psys->next) - psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; - } - } - - /* quick cache removed */ + BKE_object_handle_data_update(eval_ctx, scene, ob); } ob->recalc &= ~OB_RECALC_ALL; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c new file mode 100644 index 00000000000..196c85831e0 --- /dev/null +++ b/source/blender/blenkernel/intern/object_update.c @@ -0,0 +1,204 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 20014 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/object_update.c + * \ingroup bke + */ + +#include "DNA_anim_types.h" +#include "DNA_constraint_types.h" +#include "DNA_group_types.h" +#include "DNA_key_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_global.h" +#include "BKE_armature.h" +#include "BKE_action.h" +#include "BKE_constraint.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_animsys.h" +#include "BKE_displist.h" +#include "BKE_effect.h" +#include "BKE_key.h" +#include "BKE_lamp.h" +#include "BKE_lattice.h" +#include "BKE_editmesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_scene.h" +#include "BKE_material.h" +#include "BKE_image.h" + +void BKE_object_handle_data_update(EvaluationContext *eval_ctx, + Scene *scene, + Object *ob) +{ + ID *data_id = (ID *)ob->data; + AnimData *adt = BKE_animdata_from_id(data_id); + Key *key; + float ctime = BKE_scene_frame_get(scene); + + if (G.debug & G_DEBUG_DEPSGRAPH) + printf("recalcdata %s\n", ob->id.name + 2); + + /* TODO(sergey): Only used by legacy depsgraph. */ + if (adt) { + /* evaluate drivers - datalevel */ + /* XXX: for mesh types, should we push this to derivedmesh instead? */ + BKE_animsys_evaluate_animdata(scene, data_id, adt, ctime, ADT_RECALC_DRIVERS); + } + + /* TODO(sergey): Only used by legacy depsgraph. */ + key = BKE_key_from_object(ob); + if (key && key->block.first) { + if (!(ob->shapeflag & OB_SHAPE_LOCK)) + BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS); + } + + /* includes all keys and modifiers */ + switch (ob->type) { + case OB_MESH: + { + BMEditMesh *em = (ob == scene->obedit) ? BKE_editmesh_from_object(ob) : NULL; + uint64_t data_mask = scene->customdata_mask | CD_MASK_BAREMESH; +#ifdef WITH_FREESTYLE + /* make sure Freestyle edge/face marks appear in DM for render (see T40315) */ + if (eval_ctx->mode != DAG_EVAL_VIEWPORT) { + data_mask |= CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; + } +#endif + if (em) { + makeDerivedMesh(scene, ob, em, data_mask, 0); /* was CD_MASK_BAREMESH */ + } + else { + makeDerivedMesh(scene, ob, NULL, data_mask, 0); + } + break; + } + case OB_ARMATURE: + if (ob->id.lib && ob->proxy_from) { + if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { + printf("Proxy copy error, lib Object: %s proxy Object: %s\n", + ob->id.name + 2, ob->proxy_from->id.name + 2); + } + } + else { + BKE_pose_where_is(scene, ob); + } + break; + + case OB_MBALL: + BKE_displist_make_mball(eval_ctx, scene, ob); + break; + + case OB_CURVE: + case OB_SURF: + case OB_FONT: + BKE_displist_make_curveTypes(scene, ob, 0); + break; + + case OB_LATTICE: + BKE_lattice_modifiers_calc(scene, ob); + break; + + case OB_EMPTY: + if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data) + if (BKE_image_is_animated(ob->data)) + BKE_image_user_check_frame_calc(ob->iuser, (int)ctime, 0); + break; + } + + /* related materials */ + /* XXX: without depsgraph tagging, this will always need to be run, which will be slow! + * However, not doing anything (or trying to hack around this lack) is not an option + * anymore, especially due to Cycles [#31834] + */ + if (ob->totcol) { + int a; + for (a = 1; a <= ob->totcol; a++) { + Material *ma = give_current_material(ob, a); + if (ma) { + /* recursively update drivers for this material */ + material_drivers_update(scene, ma, ctime); + } + } + } + else if (ob->type == OB_LAMP) + lamp_drivers_update(scene, ob->data, ctime); + + /* particles */ + if (ob != scene->obedit && ob->particlesystem.first) { + ParticleSystem *tpsys, *psys; + DerivedMesh *dm; + ob->transflag &= ~OB_DUPLIPARTS; + psys = ob->particlesystem.first; + while (psys) { + /* ensure this update always happens even if psys is disabled */ + if (psys->recalc & PSYS_RECALC_TYPE) { + psys_changed_type(ob, psys); + } + + if (psys_check_enabled(ob, psys)) { + /* check use of dupli objects here */ + if (psys->part && (psys->part->draw_as == PART_DRAW_REND || eval_ctx->mode == DAG_EVAL_RENDER) && + ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) || + (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group))) + { + ob->transflag |= OB_DUPLIPARTS; + } + + particle_system_update(scene, ob, psys); + psys = psys->next; + } + else if (psys->flag & PSYS_DELETE) { + tpsys = psys->next; + BLI_remlink(&ob->particlesystem, psys); + psys_free(ob, psys); + psys = tpsys; + } + else + psys = psys->next; + } + + if (eval_ctx->mode == DAG_EVAL_RENDER && ob->transflag & OB_DUPLIPARTS) { + /* this is to make sure we get render level duplis in groups: + * the derivedmesh must be created before init_render_mesh, + * since object_duplilist does dupliparticles before that */ + dm = mesh_create_derived_render(scene, ob, CD_MASK_BAREMESH | CD_MASK_MTFACE | CD_MASK_MCOL); + dm->release(dm); + + for (psys = ob->particlesystem.first; psys; psys = psys->next) + psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; + } + } + + /* quick cache removed */ +} |