This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/object/object_constraint.c

2001 lines
58 KiB
C
Raw Normal View History

/*
* ***** 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,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Joshua Leung, Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
2011-02-27 20:29:51 +00:00
/** \file blender/editors/object/object_constraint.c
* \ingroup edobj
*/
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_dynstr.h"
#include "BLI_utildefines.h"
#include "BLF_translation.h"
#include "DNA_anim_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_scene_types.h"
#include "DNA_text_types.h"
#include "DNA_object_types.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
#include "BKE_tracking.h"
#include "BIK_api.h"
#ifdef WITH_PYTHON
#include "BPY_extern.h"
#endif
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "ED_object.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "object_intern.h"
/* -------------- Get Active Constraint Data ---------------------- */
/* if object in posemode, active bone constraints, else object constraints */
2012-04-28 15:42:27 +00:00
ListBase *get_active_constraints(Object *ob)
{
if (ob == NULL)
return NULL;
if (ob->mode & OB_MODE_POSE) {
bPoseChannel *pchan;
pchan = BKE_pose_channel_active(ob);
if (pchan)
return &pchan->constraints;
}
else
return &ob->constraints;
return NULL;
}
/* Find the list that a given constraint belongs to, and/or also get the posechannel this is from (if applicable) */
ListBase *get_constraint_lb(Object *ob, bConstraint *con, bPoseChannel **r_pchan)
{
if (r_pchan)
*r_pchan = NULL;
if (ELEM(NULL, ob, con))
return NULL;
/* try object constraints first */
if ((BLI_findindex(&ob->constraints, con) != -1)) {
return &ob->constraints;
}
/* if armature, try pose bones too */
if (ob->pose) {
bPoseChannel *pchan;
/* try each bone in order
* NOTE: it's not possible to directly look up the active bone yet, so this will have to do
*/
2012-04-28 15:42:27 +00:00
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if ((BLI_findindex(&pchan->constraints, con) != -1)) {
if (r_pchan)
*r_pchan = pchan;
return &pchan->constraints;
}
}
}
/* done */
return NULL;
}
/* single constraint */
2012-04-28 15:42:27 +00:00
bConstraint *get_active_constraint(Object *ob)
{
return BKE_constraints_active_get(get_active_constraints(ob));
}
/* -------------- Constraint Management (Add New, Remove, Rename) -------------------- */
#ifdef WITH_PYTHON
/* ------------- PyConstraints ------------------ */
/* this callback sets the text-file to be used for selected menu item */
2012-04-28 15:42:27 +00:00
static void validate_pyconstraint_cb(void *arg1, void *arg2)
{
bPythonConstraint *data = arg1;
2012-04-28 15:42:27 +00:00
Text *text = NULL;
int index = *((int *)arg2);
int i;
/* exception for no script */
if (index) {
/* innovative use of a for...loop to search */
2012-04-28 15:42:27 +00:00
for (text = G.main->text.first, i = 1; text && index != i; i++, text = text->id.next) ;
}
data->text = text;
}
/* this returns a string for the list of usable pyconstraint script names */
2012-04-28 15:42:27 +00:00
static char *buildmenu_pyconstraints(Text *con_text, int *pyconindex)
{
2012-04-28 15:42:27 +00:00
DynStr *pupds = BLI_dynstr_new();
Text *text;
char *str;
char buf[64];
int i;
/* add title first */
sprintf(buf, "Scripts: %%t|[None]%%x0|");
BLI_dynstr_append(pupds, buf);
/* init active-index first */
if (con_text == NULL)
2012-04-28 15:42:27 +00:00
*pyconindex = 0;
/* loop through markers, adding them */
2012-04-28 15:42:27 +00:00
for (text = G.main->text.first, i = 1; text; i++, text = text->id.next) {
/* this is important to ensure that right script is shown as active */
if (text == con_text) *pyconindex = i;
/* only include valid pyconstraint scripts */
if (BPY_is_pyconstraint(text)) {
2012-04-28 15:42:27 +00:00
BLI_dynstr_append(pupds, text->id.name + 2);
sprintf(buf, "%%x%d", i);
BLI_dynstr_append(pupds, buf);
if (text->id.next)
BLI_dynstr_append(pupds, "|");
}
}
/* convert to normal MEM_malloc'd string */
2012-04-28 15:42:27 +00:00
str = BLI_dynstr_get_cstring(pupds);
BLI_dynstr_free(pupds);
return str;
}
#endif /* WITH_PYTHON */
#if 0 // UNUSED, until pyconstraints are added back.
/* this callback gets called when the 'refresh' button of a pyconstraint gets pressed */
2012-04-28 15:42:27 +00:00
static void update_pyconstraint_cb(void *arg1, void *arg2)
{
#ifndef WITH_PYTHON
2010-10-23 16:03:31 +00:00
(void)arg1; /* unused */
(void)arg2; /* unused */
#else
2012-04-28 15:42:27 +00:00
Object *owner = (Object *)arg1;
bConstraint *con = (bConstraint *)arg2;
if (owner && con)
BPY_pyconstraint_update(owner, con);
#endif
}
#endif // UNUSED
/* helper function for add_constriant - sets the last target for the active constraint */
2012-04-28 15:42:27 +00:00
static void set_constraint_nth_target(bConstraint *con, Object *target, const char subtarget[], int index)
{
bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
int num_targets, i;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
2012-04-28 15:42:27 +00:00
num_targets = BLI_countlist(&targets);
if (index < 0) {
if (abs(index) < num_targets)
2012-04-28 15:42:27 +00:00
index = num_targets - abs(index);
else
2012-04-28 15:42:27 +00:00
index = num_targets - 1;
}
else if (index >= num_targets) {
2012-04-28 15:42:27 +00:00
index = num_targets - 1;
}
2012-04-28 15:42:27 +00:00
for (ct = targets.first, i = 0; ct; ct = ct->next, i++) {
if (i == index) {
2012-04-28 15:42:27 +00:00
ct->tar = target;
BLI_strncpy(ct->subtarget, subtarget, sizeof(ct->subtarget));
break;
}
}
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 0);
}
}
/* ------------- Constraint Sanity Testing ------------------- */
/* checks validity of object pointers, and NULLs,
* if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag.
*/
2012-04-28 15:42:27 +00:00
static void test_constraints(Object *owner, bPoseChannel *pchan)
{
bConstraint *curcon;
2012-04-28 15:42:27 +00:00
ListBase *conlist = NULL;
int type;
2012-04-28 15:42:27 +00:00
if (owner == NULL) return;
/* Check parents */
if (pchan) {
switch (owner->type) {
case OB_ARMATURE:
type = CONSTRAINT_OBTYPE_BONE;
break;
default:
type = CONSTRAINT_OBTYPE_OBJECT;
break;
}
}
else
type = CONSTRAINT_OBTYPE_OBJECT;
/* Get the constraint list for this object */
switch (type) {
case CONSTRAINT_OBTYPE_OBJECT:
conlist = &owner->constraints;
break;
case CONSTRAINT_OBTYPE_BONE:
conlist = &pchan->constraints;
break;
}
/* Check all constraints - is constraint valid? */
if (conlist) {
2012-04-28 15:42:27 +00:00
for (curcon = conlist->first; curcon; curcon = curcon->next) {
bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* clear disabled-flag first */
curcon->flag &= ~CONSTRAINT_DISABLE;
if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = curcon->data;
/* bad: we need a separate set of checks here as poletarget is
* optional... otherwise poletarget must exist too or else
* the constraint is deemed invalid
*/
/* default IK check ... */
if (BKE_object_exists_check(data->tar) == 0) {
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
if (data->poletar) {
if (BKE_object_exists_check(data->poletar) == 0) {
data->poletar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->poletar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
}
/* ... can be overwritten here */
BIK_test_constraint(owner, curcon);
/* targets have already been checked for this */
continue;
}
== Pivot Constraint == This constraint allows an object or bone to have their rotations applied as if their origin/pivot-point was located elsewhere. The most obvious uses include foot-roll, see-saws, but could also include more complicated rolling-box examples. == Usage Examples == === Foot Roll === 1. Add 'Pivot' Constraint to the bone without any target. 2. Set the 'Y' value of the offset to the length of the bone. Usually this should be negative (if you rig with feet facing 'forwards' along -Y axis). This gives you a pivot point relative to the bone's (preconstraint) location, which should be at the tip of the bone here. Disabling the 'Use Relative Offset' would make this offset be relative to 0,0,0 instead of to the owner/bone-head. 3. Ensure that the 'Pivot When' setting is set to '-X Rot', (default) which means that the pivot will only used when the rotation on the X-Axis is negative to get tip-toe 'roll'. === See Saw === 1. Add a 'Pivot' constraint too see-saw plank object, this time with a target that you wish to have as the pivot-point. It's possible to do this without too (as before), but is less intuitive. 2. Optionally, if you want the plank slightly raised, set the z-offset value, which should make the pivot-point used to be relative to the target with the z-offset applied. 3. Ensure that 'Pivot When' is set to 'Always', which means that the pivot will always be used, irrespective of the rotation. == Notes == * The 'Pivot When' setting has been integrated in the constraint, since this is something that will often be required for these setups. Having to set up additional drivers to drive the constraint to do this kindof beats the purpose of providing this. * The 'Offset' functionality is probably not presented as clearly as it could be. We may need to go over this again. * For foot-roll - if any scaling of the foot is required, simply set up a driver on the y-offset to make this dynamically respond to the "scale" RNA property of the bones (don't use the "Transform Channel" vartype since that won't work correct here). However, this shouldn't be common enough to warrant special treatment.
2010-05-27 10:50:06 +00:00
else if (curcon->type == CONSTRAINT_TYPE_PIVOT) {
bPivotConstraint *data = curcon->data;
/* target doesn't have to exist, but if it is non-null, it must exist! */
if (data->tar && BKE_object_exists_check(data->tar) == 0) {
== Pivot Constraint == This constraint allows an object or bone to have their rotations applied as if their origin/pivot-point was located elsewhere. The most obvious uses include foot-roll, see-saws, but could also include more complicated rolling-box examples. == Usage Examples == === Foot Roll === 1. Add 'Pivot' Constraint to the bone without any target. 2. Set the 'Y' value of the offset to the length of the bone. Usually this should be negative (if you rig with feet facing 'forwards' along -Y axis). This gives you a pivot point relative to the bone's (preconstraint) location, which should be at the tip of the bone here. Disabling the 'Use Relative Offset' would make this offset be relative to 0,0,0 instead of to the owner/bone-head. 3. Ensure that the 'Pivot When' setting is set to '-X Rot', (default) which means that the pivot will only used when the rotation on the X-Axis is negative to get tip-toe 'roll'. === See Saw === 1. Add a 'Pivot' constraint too see-saw plank object, this time with a target that you wish to have as the pivot-point. It's possible to do this without too (as before), but is less intuitive. 2. Optionally, if you want the plank slightly raised, set the z-offset value, which should make the pivot-point used to be relative to the target with the z-offset applied. 3. Ensure that 'Pivot When' is set to 'Always', which means that the pivot will always be used, irrespective of the rotation. == Notes == * The 'Pivot When' setting has been integrated in the constraint, since this is something that will often be required for these setups. Having to set up additional drivers to drive the constraint to do this kindof beats the purpose of providing this. * The 'Offset' functionality is probably not presented as clearly as it could be. We may need to go over this again. * For foot-roll - if any scaling of the foot is required, simply set up a driver on the y-offset to make this dynamically respond to the "scale" RNA property of the bones (don't use the "Transform Channel" vartype since that won't work correct here). However, this shouldn't be common enough to warrant special treatment.
2010-05-27 10:50:06 +00:00
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
== Pivot Constraint == This constraint allows an object or bone to have their rotations applied as if their origin/pivot-point was located elsewhere. The most obvious uses include foot-roll, see-saws, but could also include more complicated rolling-box examples. == Usage Examples == === Foot Roll === 1. Add 'Pivot' Constraint to the bone without any target. 2. Set the 'Y' value of the offset to the length of the bone. Usually this should be negative (if you rig with feet facing 'forwards' along -Y axis). This gives you a pivot point relative to the bone's (preconstraint) location, which should be at the tip of the bone here. Disabling the 'Use Relative Offset' would make this offset be relative to 0,0,0 instead of to the owner/bone-head. 3. Ensure that the 'Pivot When' setting is set to '-X Rot', (default) which means that the pivot will only used when the rotation on the X-Axis is negative to get tip-toe 'roll'. === See Saw === 1. Add a 'Pivot' constraint too see-saw plank object, this time with a target that you wish to have as the pivot-point. It's possible to do this without too (as before), but is less intuitive. 2. Optionally, if you want the plank slightly raised, set the z-offset value, which should make the pivot-point used to be relative to the target with the z-offset applied. 3. Ensure that 'Pivot When' is set to 'Always', which means that the pivot will always be used, irrespective of the rotation. == Notes == * The 'Pivot When' setting has been integrated in the constraint, since this is something that will often be required for these setups. Having to set up additional drivers to drive the constraint to do this kindof beats the purpose of providing this. * The 'Offset' functionality is probably not presented as clearly as it could be. We may need to go over this again. * For foot-roll - if any scaling of the foot is required, simply set up a driver on the y-offset to make this dynamically respond to the "scale" RNA property of the bones (don't use the "Transform Channel" vartype since that won't work correct here). However, this shouldn't be common enough to warrant special treatment.
2010-05-27 10:50:06 +00:00
curcon->flag |= CONSTRAINT_DISABLE;
}
}
/* targets have already been checked for this */
continue;
}
else if (curcon->type == CONSTRAINT_TYPE_ACTION) {
bActionConstraint *data = curcon->data;
/* validate action */
if (data->act == NULL) {
/* must have action */
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->act->idroot != ID_OB) {
/* only object-rooted actions can be used */
data->act = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
}
else if (curcon->type == CONSTRAINT_TYPE_FOLLOWPATH) {
bFollowPathConstraint *data = curcon->data;
/* don't allow track/up axes to be the same */
2012-04-28 15:42:27 +00:00
if (data->upflag == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
2012-04-28 15:42:27 +00:00
if (data->upflag + 3 == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_TRACKTO) {
bTrackToConstraint *data = curcon->data;
/* don't allow track/up axes to be the same */
2012-04-28 15:42:27 +00:00
if (data->reserved2 == data->reserved1)
curcon->flag |= CONSTRAINT_DISABLE;
2012-04-28 15:42:27 +00:00
if (data->reserved2 + 3 == data->reserved1)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_LOCKTRACK) {
bLockTrackConstraint *data = curcon->data;
2012-04-28 15:42:27 +00:00
if (data->lockflag == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
2012-04-28 15:42:27 +00:00
if (data->lockflag + 3 == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = curcon->data;
/* if the number of points does not match the amount required by the chain length,
* free the points array and request a rebind...
*/
2012-04-28 15:42:27 +00:00
if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) {
/* free the points array */
if (data->points) {
MEM_freeN(data->points);
data->points = NULL;
}
/* clear the bound flag, forcing a rebind next time this is evaluated */
data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
}
}
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
else if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = curcon->data;
2012-04-28 15:42:27 +00:00
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) {
if (data->clip != NULL && data->track[0]) {
2012-04-28 15:42:27 +00:00
MovieTracking *tracking = &data->clip->tracking;
MovieTrackingObject *tracking_object;
if (data->object[0])
tracking_object = BKE_tracking_object_get_named(tracking, data->object);
else
tracking_object = BKE_tracking_object_get_camera(tracking);
if (!tracking_object) {
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
curcon->flag |= CONSTRAINT_DISABLE;
}
else {
if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track))
curcon->flag |= CONSTRAINT_DISABLE;
}
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
}
else {
curcon->flag |= CONSTRAINT_DISABLE;
}
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
}
}
else if (curcon->type == CONSTRAINT_TYPE_CAMERASOLVER) {
bCameraSolverConstraint *data = curcon->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = curcon->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
curcon->flag |= CONSTRAINT_DISABLE;
}
/* Check targets for constraints */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(curcon, &targets);
/* disable and clear constraints targets that are incorrect */
2012-04-28 15:42:27 +00:00
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
if (BKE_object_exists_check(ct->tar) == 0) {
/* object doesn't exist, but constraint requires target */
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (ct->tar == owner) {
if (type == CONSTRAINT_OBTYPE_BONE) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) {
/* bone must exist in armature... */
/* TODO: clear subtarget? */
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (strcmp(pchan->name, ct->subtarget) == 0) {
/* cannot target self */
ct->subtarget[0] = '\0';
curcon->flag |= CONSTRAINT_DISABLE;
}
}
else {
/* cannot use self as target */
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
}
/* target checks for specific constraints */
if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
if (ct->tar->type != OB_CURVE) {
2012-04-28 15:42:27 +00:00
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else {
2012-04-28 15:42:27 +00:00
Curve *cu = ct->tar->data;
/* auto-set 'Path' setting on curve so this works */
cu->flag |= CU_PATH;
}
}
}
}
/* free any temporary targets */
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(curcon, &targets, 0);
}
}
}
}
2012-04-28 15:42:27 +00:00
void object_test_constraints(Object *owner)
{
if (owner->constraints.first)
test_constraints(owner, NULL);
2012-04-28 15:42:27 +00:00
if (owner->type == OB_ARMATURE && owner->pose) {
bPoseChannel *pchan;
2012-04-28 15:42:27 +00:00
for (pchan = owner->pose->chanbase.first; pchan; pchan = pchan->next) {
if (pchan->constraints.first)
test_constraints(owner, pchan);
}
}
}
/************************ generic functions for operators using constraint names and data context *********************/
2012-04-28 15:42:27 +00:00
#define EDIT_CONSTRAINT_OWNER_OBJECT 0
#define EDIT_CONSTRAINT_OWNER_BONE 1
static EnumPropertyItem constraint_owner_items[] = {
{EDIT_CONSTRAINT_OWNER_OBJECT, "OBJECT", 0, "Object", "Edit a constraint on the active object"},
{EDIT_CONSTRAINT_OWNER_BONE, "BONE", 0, "Bone", "Edit a constraint on the active bone"},
{0, NULL, 0, NULL, NULL}};
static int edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type);
Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
if (!ptr.data) {
CTX_wm_operator_poll_msg_set(C, "Context missing 'constraint'");
return 0;
}
if (!ob) {
CTX_wm_operator_poll_msg_set(C, "Context missing active object");
return 0;
}
if (ob->id.lib || (ptr.id.data && ((ID *)ptr.id.data)->lib)) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
return 0;
}
return 1;
}
static int edit_constraint_poll(bContext *C)
{
return edit_constraint_poll_generic(C, &RNA_Constraint);
}
static void edit_constraint_properties(wmOperatorType *ot)
{
RNA_def_string(ot->srna, "constraint", NULL, MAX_NAME, "Constraint", "Name of the constraint to edit");
RNA_def_enum(ot->srna, "owner", constraint_owner_items, 0, "Owner", "The owner of this constraint");
}
static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
bConstraint *con;
ListBase *list;
if (RNA_struct_property_is_set(op->ptr, "constraint") && RNA_struct_property_is_set(op->ptr, "owner"))
return 1;
if (ptr.data) {
con = ptr.data;
RNA_string_set(op->ptr, "constraint", con->name);
list = get_constraint_lb(ob, con, NULL);
if (&ob->constraints == list)
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_OBJECT);
else
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE);
return 1;
}
return 0;
}
static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type)
{
char constraint_name[MAX_NAME];
int owner = RNA_enum_get(op->ptr, "owner");
bConstraint *con;
2012-04-28 15:42:27 +00:00
ListBase *list = NULL;
RNA_string_get(op->ptr, "constraint", constraint_name);
if (owner == EDIT_CONSTRAINT_OWNER_OBJECT) {
list = &ob->constraints;
}
else if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
bPoseChannel *pchan = BKE_pose_channel_active(ob);
if (pchan)
list = &pchan->constraints;
else {
//if (G.debug & G_DEBUG)
2013-03-31 03:28:46 +00:00
//printf("edit_constraint_property_get: No active bone for object '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
return NULL;
}
}
else {
//if (G.debug & G_DEBUG)
//printf("edit_constraint_property_get: defaulting to getting list in the standard way\n");
list = get_active_constraints(ob);
}
con = BKE_constraints_find_name(list, constraint_name);
//if (G.debug & G_DEBUG)
2013-03-31 03:28:46 +00:00
//printf("constraint found = %p, %s\n", (void *)con, (con) ? con->name : "<Not found>");
if (con && (type != 0) && (con->type != type))
con = NULL;
return con;
}
/* ********************** CONSTRAINT-SPECIFIC STUFF ********************* */
/* ---------- Distance-Dependent Constraints ---------- */
/* StretchTo, Limit Distance */
2012-04-28 15:42:27 +00:00
static int stretchto_reset_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_STRETCHTO);
2012-04-28 15:42:27 +00:00
bStretchToConstraint *data = (con) ? (bStretchToConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL)
return OPERATOR_CANCELLED;
/* just set original length to 0.0, which will cause a reset on next recalc */
data->orglength = 0.0f;
ED_object_constraint_update(ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
static int stretchto_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return stretchto_reset_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Original Length";
ot->idname = "CONSTRAINT_OT_stretchto_reset";
ot->description = "Reset original length of bone for Stretch To Constraint";
/* callbacks */
ot->invoke = stretchto_reset_invoke;
ot->exec = stretchto_reset_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
2012-04-28 15:42:27 +00:00
static int limitdistance_reset_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_DISTLIMIT);
2012-04-28 15:42:27 +00:00
bDistLimitConstraint *data = (con) ? (bDistLimitConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL)
return OPERATOR_CANCELLED;
/* just set original length to 0.0, which will cause a reset on next recalc */
data->dist = 0.0f;
ED_object_constraint_update(ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
static int limitdistance_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return limitdistance_reset_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Distance";
ot->idname = "CONSTRAINT_OT_limitdistance_reset";
ot->description = "Reset limiting distance for Limit Distance Constraint";
/* callbacks */
ot->invoke = limitdistance_reset_invoke;
ot->exec = limitdistance_reset_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/* ------------- Child-Of Constraint ------------------ */
static void child_get_inverse_matrix(Scene *scene, Object *ob, bConstraint *con, float invmat[4][4], const int owner)
{
/* nullify inverse matrix first */
unit_m4(invmat);
if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
bPoseChannel *pchan;
/* try to find a pose channel - assume that this is the constraint owner */
/* TODO: get from context instead? */
if (ob && ob->pose && (pchan = BKE_pose_channel_active(ob))) {
bConstraint *con_last;
/* calculate/set inverse matrix:
* We just calculate all transform-stack eval up to but not including this constraint.
* This is because inverse should just inverse correct for just the constraint's influence
* when it gets applied; that is, at the time of application, we don't know anything about
* what follows.
*/
float imat[4][4], tmat[4][4];
float pmat[4][4];
/* make sure we passed the correct constraint */
BLI_assert(BLI_findindex(&pchan->constraints, con) != -1);
/* 1. calculate posemat where inverse doesn't exist yet (inverse was cleared above),
* to use as baseline ("pmat") to derive delta from. This extra calc saves users
* from having pressing "Clear Inverse" first
*/
BKE_pose_where_is(scene, ob);
copy_m4_m4(pmat, pchan->pose_mat);
/* 2. knock out constraints starting from this one */
con_last = pchan->constraints.last;
pchan->constraints.last = con->prev;
if (con->prev) {
/* new end must not point to this one, else this chain cutting is useless */
con->prev->next = NULL;
}
else {
/* constraint was first */
pchan->constraints.first = NULL;
}
/* 3. solve pose without disabled constraints */
BKE_pose_where_is(scene, ob);
/* 4. determine effect of constraint by removing the newly calculated
* pchan->pose_mat from the original pchan->pose_mat, thus determining
* the effect of the constraint
*/
invert_m4_m4(imat, pchan->pose_mat);
mul_m4_m4m4(tmat, pmat, imat);
invert_m4_m4(invmat, tmat);
/* 5. restore constraints */
pchan->constraints.last = con_last;
if (con->prev) {
/* hook up prev to this one again */
con->prev->next = con;
}
else {
/* set as first again */
pchan->constraints.first = con;
}
/* 6. recalculate pose with new inv-mat applied */
BKE_pose_where_is(scene, ob);
}
}
if (owner == EDIT_CONSTRAINT_OWNER_OBJECT) {
if (ob) {
Object workob;
/* make sure we passed the correct constraint */
BLI_assert(BLI_findindex(&ob->constraints, con) != -1);
/* use BKE_object_workob_calc_parent to find inverse - just like for normal parenting */
BKE_object_workob_calc_parent(scene, ob, &workob);
invert_m4_m4(invmat, workob.obmat);
}
}
}
/* ChildOf Constraint - set inverse callback */
2012-04-28 15:42:27 +00:00
static int childof_set_inverse_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
2012-04-28 15:42:27 +00:00
bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
const int owner = RNA_enum_get(op->ptr, "owner");
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
2012-04-28 15:42:27 +00:00
printf("DEBUG: Child-Of Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
BKE_report(op->reports, RPT_ERROR, "Could not find constraint data for Child-Of Set Inverse");
return OPERATOR_CANCELLED;
}
child_get_inverse_matrix(scene, ob, con, data->invmat, owner);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int childof_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return childof_set_inverse_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Inverse";
ot->idname = "CONSTRAINT_OT_childof_set_inverse";
ot->description = "Set inverse correction for ChildOf constraint";
/* callbacks */
ot->invoke = childof_set_inverse_invoke;
ot->exec = childof_set_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/* ChildOf Constraint - clear inverse callback */
2012-04-28 15:42:27 +00:00
static int childof_clear_inverse_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
2012-04-28 15:42:27 +00:00
bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
2012-04-28 15:42:27 +00:00
if (data == NULL) {
2012-10-13 13:40:05 +00:00
BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
return OPERATOR_CANCELLED;
}
/* simply clear the matrix */
unit_m4(data->invmat);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return childof_clear_inverse_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Inverse";
ot->idname = "CONSTRAINT_OT_childof_clear_inverse";
ot->description = "Clear inverse correction for ChildOf constraint";
/* callbacks */
ot->invoke = childof_clear_inverse_invoke;
ot->exec = childof_clear_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/* --------------- Follow Path Constraint ------------------ */
static int followpath_path_animate_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_FOLLOWPATH);
bFollowPathConstraint *data = (con) ? (bFollowPathConstraint *)con->data : NULL;
bAction *act = NULL;
FCurve *fcu = NULL;
int sfra = RNA_int_get(op->ptr, "frame_start");
int len = RNA_int_get(op->ptr, "length");
float standardRange = 1.0;
/* nearly impossible sanity check */
if (data == NULL) {
BKE_report(op->reports, RPT_ERROR, "Follow Path constraint not found");
return OPERATOR_CANCELLED;
}
/* add F-Curve as appropriate */
if (data->tar) {
Curve *cu = (Curve *)data->tar->data;
2014-01-12 22:05:24 +11:00
if (ELEM(NULL, cu->adt, cu->adt->action) ||
(list_find_fcurve(&cu->adt->action->curves, "eval_time", 0) == NULL))
{
/* create F-Curve for path animation */
act = verify_adt_action(&cu->id, 1);
fcu = verify_fcurve(act, NULL, NULL, "eval_time", 0, 1);
/* standard vertical range - 1:1 = 100 frames */
standardRange = 100.0f;
}
else {
/* path anim exists already - abort for now as this may well be what was intended */
BKE_report(op->reports, RPT_WARNING, "Path is already animated");
return OPERATOR_CANCELLED;
}
}
else {
/* animate constraint's "fixed offset" */
PointerRNA ptr;
PropertyRNA *prop;
char *path;
/* get RNA pointer to constraint's "offset_factor" property - to build RNA path */
RNA_pointer_create(&ob->id, &RNA_FollowPathConstraint, con, &ptr);
prop = RNA_struct_find_property(&ptr, "offset_factor");
path = RNA_path_from_ID_to_property(&ptr, prop);
/* create F-Curve for constraint */
act = verify_adt_action(&ob->id, 1);
fcu = verify_fcurve(act, NULL, NULL, path, 0, 1);
/* standard vertical range - 0.0 to 1.0 */
standardRange = 1.0f;
/* enable "Use Fixed Position" so that animating this has effect */
data->followflag |= FOLLOWPATH_STATIC;
/* path needs to be freed */
if (path)
MEM_freeN(path);
}
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working
* and define basic slope of this curve based on the properties
*/
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
FMod_Generator *gen = fcm->data;
/* Assume that we have the following equation:
* y = Ax + B
* 1 0 <-- coefficients array indices
*/
float A = standardRange / (float)(len);
float B = (float)(-sfra) * A;
gen->coefficients[1] = A;
gen->coefficients[0] = B;
}
/* updates... */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int followpath_path_animate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
/* hook up invoke properties for figuring out which constraint we're dealing with */
if (edit_constraint_invoke_properties(C, op)) {
return followpath_path_animate_exec(C, op);
}
else {
return OPERATOR_CANCELLED;
}
}
void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Auto Animate Path";
ot->idname = "CONSTRAINT_OT_followpath_path_animate";
ot->description = "Add default animation for path used by constraint if it isn't animated already";
/* callbacks */
ot->invoke = followpath_path_animate_invoke;
ot->exec = followpath_path_animate_exec;
ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
edit_constraint_properties(ot);
RNA_def_int(ot->srna, "frame_start", 1, MINAFRAME, MAXFRAME, "Start Frame",
"First frame of path animation", MINAFRAME, MAXFRAME);
RNA_def_int(ot->srna, "length", 100, 0, MAXFRAME, "Length",
"Number of frames that path animation should take", 0, MAXFRAME);
}
/* ------------- Object Solver Constraint ------------------ */
2012-04-28 15:42:27 +00:00
static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
2012-04-28 15:42:27 +00:00
bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
const int owner = RNA_enum_get(op->ptr, "owner");
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
2012-04-28 15:42:27 +00:00
printf("DEBUG: Child-Of Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
BKE_report(op->reports, RPT_ERROR, "Could not find constraint data for Child-Of Set Inverse");
return OPERATOR_CANCELLED;
}
child_get_inverse_matrix(scene, ob, con, data->invmat, owner);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int objectsolver_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return objectsolver_set_inverse_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_objectsolver_set_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Inverse";
ot->idname = "CONSTRAINT_OT_objectsolver_set_inverse";
ot->description = "Set inverse correction for ObjectSolver constraint";
/* callbacks */
ot->invoke = objectsolver_set_inverse_invoke;
ot->exec = objectsolver_set_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
2012-04-28 15:42:27 +00:00
static int objectsolver_clear_inverse_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
2012-04-28 15:42:27 +00:00
bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
2012-04-28 15:42:27 +00:00
if (data == NULL) {
2012-10-13 13:40:05 +00:00
BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
return OPERATOR_CANCELLED;
}
/* simply clear the matrix */
unit_m4(data->invmat);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int objectsolver_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return objectsolver_clear_inverse_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_objectsolver_clear_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Inverse";
ot->idname = "CONSTRAINT_OT_objectsolver_clear_inverse";
ot->description = "Clear inverse correction for ObjectSolver constraint";
/* callbacks */
ot->invoke = objectsolver_clear_inverse_invoke;
ot->exec = objectsolver_clear_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/***************************** BUTTONS ****************************/
void ED_object_constraint_set_active(Object *ob, bConstraint *con)
{
ListBase *lb = get_constraint_lb(ob, con, NULL);
/* lets be nice and escape if its active already */
2012-07-07 22:51:57 +00:00
/* NOTE: this assumes that the stack doesn't have other active ones set... */
if ((lb && con) && (con->flag & CONSTRAINT_ACTIVE))
return;
BKE_constraints_active_set(lb, con);
}
void ED_object_constraint_update(Object *ob)
{
if (ob->pose) BKE_pose_update_constraint_flags(ob->pose);
object_test_constraints(ob);
if (ob->type == OB_ARMATURE)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
else
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
2013-02-22 00:51:58 +00:00
void ED_object_constraint_dependency_update(Main *bmain, Object *ob)
{
ED_object_constraint_update(ob);
2012-04-28 15:42:27 +00:00
if (ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels
DAG_relations_tag_update(bmain);
}
static int constraint_poll(bContext *C)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
return (ptr.id.data && ptr.data);
}
2012-04-28 15:42:27 +00:00
static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *ob = ptr.id.data;
bConstraint *con = ptr.data;
ListBase *lb = get_constraint_lb(ob, con, NULL);
const bool is_ik = ELEM(con->type, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_SPLINEIK);
/* free the constraint */
if (BKE_constraint_remove(lb, con)) {
/* there's no active constraint now, so make sure this is the case */
BKE_constraints_active_set(lb, NULL);
ED_object_constraint_update(ob); /* needed to set the flags on posebones correctly */
/* ITASC needs to be rebuilt once a constraint is removed [#26920] */
if (is_ik) {
BIK_clear_data(ob->pose);
}
/* notifiers */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
return OPERATOR_FINISHED;
}
else {
/* couldn't remove due to some invalid data */
return OPERATOR_CANCELLED;
}
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Constraint";
ot->idname = "CONSTRAINT_OT_delete";
ot->description = "Remove constraint from constraint stack";
/* callbacks */
ot->exec = constraint_delete_exec;
ot->poll = constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
2012-04-28 15:42:27 +00:00
static int constraint_move_down_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, 0);
if (con && con->next) {
2012-04-28 15:42:27 +00:00
ListBase *conlist = get_constraint_lb(ob, con, NULL);
bConstraint *nextCon = con->next;
/* insert the nominated constraint after the one that used to be after it */
BLI_remlink(conlist, con);
BLI_insertlinkafter(conlist, nextCon, con);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int constraint_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return constraint_move_down_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_move_down(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Constraint Down";
ot->idname = "CONSTRAINT_OT_move_down";
ot->description = "Move constraint down in constraint stack";
/* callbacks */
ot->invoke = constraint_move_down_invoke;
ot->exec = constraint_move_down_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
2012-04-28 15:42:27 +00:00
static int constraint_move_up_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, 0);
if (con && con->prev) {
2012-04-28 15:42:27 +00:00
ListBase *conlist = get_constraint_lb(ob, con, NULL);
bConstraint *prevCon = con->prev;
/* insert the nominated constraint before the one that used to be before it */
BLI_remlink(conlist, con);
BLI_insertlinkbefore(conlist, prevCon, con);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op))
return constraint_move_up_exec(C, op);
else
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_move_up(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Constraint Up";
ot->idname = "CONSTRAINT_OT_move_up";
ot->description = "Move constraint up in constraint stack";
/* callbacks */
ot->exec = constraint_move_up_exec;
ot->invoke = constraint_move_up_invoke;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
}
/***************************** OPERATORS ****************************/
/************************ remove constraint operators *********************/
static int pose_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
/* free constraints for all selected bones */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
{
BKE_constraints_free(&pchan->constraints);
2012-04-28 15:42:27 +00:00
pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_SPLINEIK | PCHAN_HAS_CONST);
}
CTX_DATA_END;
/* force depsgraph to get recalculated since relationships removed */
DAG_relations_tag_update(bmain);
2012-03-18 07:38:51 +00:00
/* note, calling BIK_clear_data() isn't needed here */
/* do updates */
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
return OPERATOR_FINISHED;
}
void POSE_OT_constraints_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Pose Constraints";
ot->idname = "POSE_OT_constraints_clear";
ot->description = "Clear all the constraints for the selected bones";
/* callbacks */
ot->exec = pose_constraints_clear_exec;
ot->poll = ED_operator_posemode_exclusive; // XXX - do we want to ensure there are selected bones too?
}
static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
/* do freeing */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
{
BKE_constraints_free(&ob->constraints);
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
CTX_DATA_END;
/* force depsgraph to get recalculated since relationships removed */
DAG_relations_tag_update(bmain);
/* do updates */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, NULL);
return OPERATOR_FINISHED;
}
void OBJECT_OT_constraints_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Object Constraints";
ot->idname = "OBJECT_OT_constraints_clear";
ot->description = "Clear all the constraints for the active Object only";
/* callbacks */
ot->exec = object_constraints_clear_exec;
ot->poll = ED_operator_object_active_editable;
}
/************************ copy all constraints operators *********************/
static int pose_constraint_copy_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
bPoseChannel *pchan = CTX_data_active_pose_bone(C);
ListBase lb;
CollectionPointerLink *link;
/* don't do anything if bone doesn't exist or doesn't have any constraints */
if (ELEM(NULL, pchan, pchan->constraints.first)) {
BKE_report(op->reports, RPT_ERROR, "No active bone with constraints for copying");
return OPERATOR_CANCELLED;
}
/* copy all constraints from active posebone to all selected posebones */
CTX_data_selected_pose_bones(C, &lb);
for (link = lb.first; link; link = link->next) {
Object *ob = link->ptr.id.data;
bPoseChannel *chan = link->ptr.data;
/* if we're not handling the object we're copying from, copy all constraints over */
if (pchan != chan) {
BKE_constraints_copy(&chan->constraints, &pchan->constraints, true);
/* update flags (need to add here, not just copy) */
chan->constflag |= pchan->constflag;
ob->pose->flag |= POSE_RECALC;
DAG_id_tag_update((ID *)ob, OB_RECALC_DATA);
}
}
BLI_freelistN(&lb);
/* force depsgraph to get recalculated since new relationships added */
DAG_relations_tag_update(bmain);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
void POSE_OT_constraints_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Constraints to Selected Bones";
ot->idname = "POSE_OT_constraints_copy";
ot->description = "Copy constraints to other selected bones";
/* api callbacks */
ot->exec = pose_constraint_copy_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int object_constraint_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Object *obact = ED_object_active_context(C);
/* copy all constraints from active object to all selected objects */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
{
/* if we're not handling the object we're copying from, copy all constraints over */
if (obact != ob) {
BKE_constraints_copy(&ob->constraints, &obact->constraints, true);
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
}
}
CTX_DATA_END;
/* force depsgraph to get recalculated since new relationships added */
DAG_relations_tag_update(bmain);
/* notifiers for updates */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, NULL);
return OPERATOR_FINISHED;
}
void OBJECT_OT_constraints_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Constraints to Selected Objects";
ot->idname = "OBJECT_OT_constraints_copy";
ot->description = "Copy constraints to other selected objects";
/* api callbacks */
ot->exec = object_constraint_copy_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************ add constraint operators *********************/
/* get the Object and/or PoseChannel to use as target */
static short get_new_constraint_target(bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, short add)
{
2012-04-28 15:42:27 +00:00
Object *obact = ED_object_active_context(C);
bPoseChannel *pchanact = BKE_pose_channel_active(obact);
2012-04-28 15:42:27 +00:00
short only_curve = 0, only_mesh = 0, only_ob = 0;
short found = 0;
/* clear tar_ob and tar_pchan fields before use
* - assume for now that both always exist...
*/
2012-04-28 15:42:27 +00:00
*tar_ob = NULL;
*tar_pchan = NULL;
/* check if constraint type doesn't requires a target
* - if so, no need to get any targets
*/
switch (con_type) {
/* no-target constraints --------------------------- */
2012-04-28 15:42:27 +00:00
/* null constraint - shouldn't even be added! */
case CONSTRAINT_TYPE_NULL:
2012-04-28 15:42:27 +00:00
/* limit constraints - no targets needed */
case CONSTRAINT_TYPE_LOCLIMIT:
case CONSTRAINT_TYPE_ROTLIMIT:
case CONSTRAINT_TYPE_SIZELIMIT:
case CONSTRAINT_TYPE_SAMEVOL:
return 0;
/* restricted target-type constraints -------------- */
/* NOTE: for these, we cannot try to add a target object if no valid ones are found, since that doesn't work */
2012-04-28 15:42:27 +00:00
/* curve-based constraints - set the only_curve and only_ob flags */
case CONSTRAINT_TYPE_CLAMPTO:
case CONSTRAINT_TYPE_FOLLOWPATH:
case CONSTRAINT_TYPE_SPLINEIK:
2012-04-28 15:42:27 +00:00
only_curve = 1;
only_ob = 1;
add = 0;
break;
2012-04-28 15:42:27 +00:00
/* mesh only? */
case CONSTRAINT_TYPE_SHRINKWRAP:
2012-04-28 15:42:27 +00:00
only_mesh = 1;
only_ob = 1;
add = 0;
break;
2012-04-28 15:42:27 +00:00
/* object only - add here is ok? */
case CONSTRAINT_TYPE_RIGIDBODYJOINT:
2012-04-28 15:42:27 +00:00
only_ob = 1;
break;
}
/* if the active Object is Armature, and we can search for bones, do so... */
if ((obact->type == OB_ARMATURE) && (only_ob == 0)) {
/* search in list of selected Pose-Channels for target */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
{
/* just use the first one that we encounter, as long as it is not the active one */
if (pchan != pchanact) {
2012-04-28 15:42:27 +00:00
*tar_ob = obact;
*tar_pchan = pchan;
found = 1;
break;
}
}
CTX_DATA_END;
}
/* if not yet found, try selected Objects... */
if (found == 0) {
/* search in selected objects context */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
{
/* just use the first object we encounter (that isn't the active object)
* and which fulfills the criteria for the object-target that we've got
*/
if ((ob != obact) &&
((!only_curve) || (ob->type == OB_CURVE)) &&
((!only_mesh) || (ob->type == OB_MESH)))
{
/* set target */
2012-04-28 15:42:27 +00:00
*tar_ob = ob;
found = 1;
/* perform some special operations on the target */
if (only_curve) {
/* Curve-Path option must be enabled for follow-path constraints to be able to work */
2012-04-28 15:42:27 +00:00
Curve *cu = (Curve *)ob->data;
cu->flag |= CU_PATH;
}
break;
}
}
CTX_DATA_END;
}
/* if still not found, add a new empty to act as a target (if allowed) */
if ((found == 0) && (add)) {
Main *bmain = CTX_data_main(C);
2012-04-28 15:42:27 +00:00
Scene *scene = CTX_data_scene(C);
Base *base = BASACT, *newbase = NULL;
Object *obt;
/* add new target object */
obt = BKE_object_add(bmain, scene, OB_EMPTY);
/* set layers OK */
2012-04-28 15:42:27 +00:00
newbase = BASACT;
newbase->lay = base->lay;
obt->lay = newbase->lay;
/* transform cent to global coords for loc */
if (pchanact) {
/* since by default, IK targets the tip of the last bone, use the tip of the active PoseChannel
* if adding a target for an IK Constraint
*/
if (con_type == CONSTRAINT_TYPE_KINEMATIC)
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_tail);
else
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_head);
}
else {
copy_v3_v3(obt->loc, obact->obmat[3]);
}
/* restore, BKE_object_add sets active */
2012-04-28 15:42:27 +00:00
BASACT = base;
base->flag |= SELECT;
/* make our new target the new object */
2012-04-28 15:42:27 +00:00
*tar_ob = obt;
found = 1;
}
/* return whether there's any target */
return found;
}
/* used by add constraint operators to add the constraint required */
2014-02-03 18:55:59 +11:00
static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase *list, int type, const bool setTarget)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
bPoseChannel *pchan;
bConstraint *con;
if (list == &ob->constraints) {
2012-04-28 15:42:27 +00:00
pchan = NULL;
}
else {
pchan = BKE_pose_channel_active(ob);
/* ensure not to confuse object/pose adding */
if (pchan == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active pose bone to add a constraint to");
return OPERATOR_CANCELLED;
}
}
/* check if constraint to be added is valid for the given constraints stack */
if (type == CONSTRAINT_TYPE_NULL) {
return OPERATOR_CANCELLED;
}
if ((type == CONSTRAINT_TYPE_RIGIDBODYJOINT) && (list != &ob->constraints)) {
BKE_report(op->reports, RPT_ERROR, "Rigid Body Joint constraint can only be added to objects");
return OPERATOR_CANCELLED;
}
if ((type == CONSTRAINT_TYPE_KINEMATIC) && ((!pchan) || (list != &pchan->constraints))) {
BKE_report(op->reports, RPT_ERROR, "IK constraint can only be added to bones");
return OPERATOR_CANCELLED;
}
if ((type == CONSTRAINT_TYPE_SPLINEIK) && ((!pchan) || (list != &pchan->constraints))) {
BKE_report(op->reports, RPT_ERROR, "Spline IK constraint can only be added to bones");
return OPERATOR_CANCELLED;
}
/* create a new constraint of the type requried, and add it to the active/given constraints list */
if (pchan)
con = BKE_constraint_add_for_pose(ob, pchan, NULL, type);
else
con = BKE_constraint_add_for_object(ob, NULL, type);
/* get the first selected object/bone, and make that the target
* - apart from the buttons-window add buttons, we shouldn't add in this way
*/
if (setTarget) {
2012-04-28 15:42:27 +00:00
Object *tar_ob = NULL;
bPoseChannel *tar_pchan = NULL;
/* get the target objects, adding them as need be */
if (get_new_constraint_target(C, type, &tar_ob, &tar_pchan, 1)) {
/* method of setting target depends on the type of target we've got
2012-03-08 04:12:11 +00:00
* - by default, just set the first target (distinction here is only for multiple-targeted constraints)
*/
if (tar_pchan)
set_constraint_nth_target(con, tar_ob, tar_pchan->name, 0);
else
set_constraint_nth_target(con, tar_ob, "", 0);
}
}
/* do type-specific tweaking to the constraint settings */
switch (type) {
case CONSTRAINT_TYPE_PYTHON: /* FIXME: this code is not really valid anymore */
{
#ifdef WITH_PYTHON
char *menustr;
2012-04-28 15:42:27 +00:00
int scriptint = 0;
/* popup a list of usable scripts */
menustr = buildmenu_pyconstraints(NULL, &scriptint);
/* XXX scriptint = pupmenu(menustr); */
MEM_freeN(menustr);
/* only add constraint if a script was chosen */
if (scriptint) {
/* add constraint */
validate_pyconstraint_cb(con->data, &scriptint);
/* make sure target allowance is set correctly */
BPY_pyconstraint_update(ob, con);
}
#endif
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
break;
}
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
default:
break;
}
/* make sure all settings are valid - similar to above checks, but sometimes can be wrong */
object_test_constraints(ob);
if (pchan)
BKE_pose_update_constraint_flags(ob->pose);
/* force depsgraph to get recalculated since new relationships added */
DAG_relations_tag_update(bmain);
2012-04-28 15:42:27 +00:00
if ((ob->type == OB_ARMATURE) && (pchan)) {
ob->pose->flag |= POSE_RECALC; /* sort pose channels */
DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
}
else
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
/* notifiers for updates */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob);
return OPERATOR_FINISHED;
}
/* ------------------ */
/* dummy operator callback */
static int object_constraint_add_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Object *ob = ED_object_active_context(C);
int type = RNA_enum_get(op->ptr, "type");
short with_targets = 0;
if (!ob) {
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
return OPERATOR_CANCELLED;
}
/* hack: set constraint targets from selected objects in context is allowed when
* operator name included 'with_targets', since the menu doesn't allow multiple properties
*/
if (strstr(op->idname, "with_targets"))
2012-04-28 15:42:27 +00:00
with_targets = 1;
return constraint_add_exec(C, op, ob, &ob->constraints, type, with_targets);
}
/* dummy operator callback */
static int pose_constraint_add_exec(bContext *C, wmOperator *op)
{
Object *ob = BKE_object_pose_armature_get(ED_object_active_context(C));
2012-04-28 15:42:27 +00:00
int type = RNA_enum_get(op->ptr, "type");
short with_targets = 0;
if (!ob) {
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
return OPERATOR_CANCELLED;
}
/* hack: set constraint targets from selected objects in context is allowed when
* operator name included 'with_targets', since the menu doesn't allow multiple properties
*/
if (strstr(op->idname, "with_targets"))
2012-04-28 15:42:27 +00:00
with_targets = 1;
return constraint_add_exec(C, op, ob, get_active_constraints(ob), type, with_targets);
}
/* ------------------ */
void OBJECT_OT_constraint_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint";
ot->description = "Add a constraint to the active object";
ot->idname = "OBJECT_OT_constraint_add";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = object_constraint_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
}
void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint (with Targets)";
ot->description = "Add a constraint to the active object, with target (where applicable) set to the selected Objects/Bones";
ot->idname = "OBJECT_OT_constraint_add_with_targets";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = object_constraint_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
}
void POSE_OT_constraint_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint";
ot->description = "Add a constraint to the active bone";
ot->idname = "POSE_OT_constraint_add";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = pose_constraint_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
}
void POSE_OT_constraint_add_with_targets(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint (with Targets)";
ot->description = "Add a constraint to the active bone, with target (where applicable) set to the selected Objects/Bones";
ot->idname = "POSE_OT_constraint_add_with_targets";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = pose_constraint_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
}
/************************ IK Constraint operators *********************/
/* NOTE: only for Pose-Channels */
// TODO: should these be here, or back in editors/armature/poseobject.c again?
/* present menu with options + validation for targets to use */
static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
bPoseChannel *pchan = BKE_pose_channel_active(ob);
2012-04-28 15:42:27 +00:00
bConstraint *con = NULL;
uiPopupMenu *pup;
uiLayout *layout;
2012-04-28 15:42:27 +00:00
Object *tar_ob = NULL;
bPoseChannel *tar_pchan = NULL;
/* must have active bone */
if (ELEM(NULL, ob, pchan)) {
BKE_report(op->reports, RPT_ERROR, "Must have an active bone to add IK constraint to");
return OPERATOR_CANCELLED;
}
/* bone must not have any constraints already */
2012-04-28 15:42:27 +00:00
for (con = pchan->constraints.first; con; con = con->next) {
if (con->type == CONSTRAINT_TYPE_KINEMATIC) break;
}
if (con) {
BKE_report(op->reports, RPT_ERROR, "Bone already has an IK constraint");
return OPERATOR_CANCELLED;
}
/* prepare popup menu to choose targetting options */
pup = uiPupMenuBegin(C, IFACE_("Add IK"), ICON_NONE);
2012-04-28 15:42:27 +00:00
layout = uiPupMenuLayout(pup);
/* the type of targets we'll set determines the menu entries to show... */
if (get_new_constraint_target(C, CONSTRAINT_TYPE_KINEMATIC, &tar_ob, &tar_pchan, 0)) {
/* bone target, or object target?
* - the only thing that matters is that we want a target...
*/
if (tar_pchan)
uiItemBooleanO(layout, IFACE_("To Active Bone"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
else
uiItemBooleanO(layout, IFACE_("To Active Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
}
else {
/* we have a choice of adding to a new empty, or not setting any target (targetless IK) */
uiItemBooleanO(layout, IFACE_("To New Empty Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
uiItemBooleanO(layout, IFACE_("Without Targets"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 0);
}
/* finish building the menu, and process it (should result in calling self again) */
uiPupMenuEnd(C, pup);
return OPERATOR_INTERFACE;
}
/* call constraint_add_exec() to add the IK constraint */
static int pose_ik_add_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Object *ob = CTX_data_active_object(C);
2014-02-03 18:55:59 +11:00
const bool with_targets = RNA_boolean_get(op->ptr, "with_targets");
/* add the constraint - all necessary checks should have been done by the invoke() callback already... */
return constraint_add_exec(C, op, ob, get_active_constraints(ob), CONSTRAINT_TYPE_KINEMATIC, with_targets);
}
void POSE_OT_ik_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add IK to Bone";
ot->description = "Add IK Constraint to the active Bone";
ot->idname = "POSE_OT_ik_add";
/* api callbacks */
ot->invoke = pose_ik_add_invoke;
ot->exec = pose_ik_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna, "with_targets", 1, "With Targets", "Assign IK Constraint with targets derived from the select bones/objects");
}
/* ------------------ */
/* remove IK constraints from selected bones */
static int pose_ik_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
/* only remove IK Constraints */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones)
{
bConstraint *con, *next;
2012-07-07 22:51:57 +00:00
/* TODO: should we be checking if these contraints were local before we try and remove them? */
2012-04-28 15:42:27 +00:00
for (con = pchan->constraints.first; con; con = next) {
next = con->next;
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
BKE_constraint_remove(&pchan->constraints, con);
}
}
2012-04-28 15:42:27 +00:00
pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_TARGET);
}
CTX_DATA_END;
/* refresh depsgraph */
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
/* note, notifier might evolve */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
return OPERATOR_FINISHED;
}
void POSE_OT_ik_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove IK";
ot->description = "Remove all IK Constraints from selected bones";
ot->idname = "POSE_OT_ik_clear";
/* api callbacks */
ot->exec = pose_ik_clear_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}