Implement an Armature constraint that mimics the modifier.

The main use one can imagine for this is adding tweak controls to
parts of a model that are already deformed by multiple other major
bones. It is natural to expect such locations to deform as if the
tweaks aren't there by default; however currently there is no easy
way to make a bone follow multiple other bones.

This adds a new constraint that implements the math behind the Armature
modifier, with support for explicit weights, bone envelopes, and dual
quaternion blending. It can also access bones from multiple armatures
at the same time (mainly because it's easier to code it that way.)

This also fixes dquat_to_mat4, which wasn't used anywhere before.

Differential Revision: https://developer.blender.org/D3664
This commit is contained in:
Alexander Gavrilov 2018-07-15 20:39:02 +03:00
parent a817613be5
commit 798cdaeeb6
Notes: blender-bot 2023-02-14 05:04:47 +01:00
Referenced by issue #57679, Grease Pencil Origin Object Becomes Invisible When Going Into An Armature Pose/Edit Mode
Referenced by issue #57680, Armature Bone Tip Rotation Not Functional
14 changed files with 663 additions and 12 deletions

View File

@ -29,6 +29,7 @@ _modules = [
"anim",
"clip",
"console",
"constraint",
"file",
"image",
"mask",

View File

@ -0,0 +1,76 @@
# ##### 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.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8-80 compliant>
import bpy
from bpy.types import (
Operator,
)
from bpy.props import (
IntProperty,
)
class CONSTRAINT_OT_add_target(Operator):
"""Add a target to the constraint"""
bl_idname = "constraint.add_target"
bl_label = "Add Target"
bl_options = {'UNDO', 'INTERNAL'}
def execute(self, context):
context.constraint.targets.new()
return {'FINISHED'}
class CONSTRAINT_OT_remove_target(Operator):
"""Remove the target from the constraint"""
bl_idname = "constraint.remove_target"
bl_label = "Remove Target"
bl_options = {'UNDO', 'INTERNAL'}
index = IntProperty()
def execute(self, context):
tgts = context.constraint.targets
tgts.remove(tgts[self.index])
return {'FINISHED'}
class CONSTRAINT_OT_normalize_target_weights(Operator):
"""Normalize weights of all target bones"""
bl_idname = "constraint.normalize_target_weights"
bl_label = "Normalize Weights"
bl_options = {'UNDO', 'INTERNAL'}
def execute(self, context):
tgts = context.constraint.targets
total = sum(t.weight for t in tgts)
if total > 0:
for t in tgts:
t.weight = t.weight / total
return {'FINISHED'}
classes = (
CONSTRAINT_OT_add_target,
CONSTRAINT_OT_remove_target,
CONSTRAINT_OT_normalize_target_weights,
)

View File

@ -919,6 +919,46 @@ class ConstraintButtonsPanel:
def SCRIPT(self, context, layout, con):
layout.label(text="Blender 2.6 doesn't support python constraints yet")
def ARMATURE(self, context, layout, con):
topcol = layout.column()
topcol.use_property_split = True
topcol.operator("constraint.add_target", text="Add Target Bone")
if not con.targets:
box = topcol.box()
box.label(text="No target bones were added", icon="ERROR")
for i, tgt in enumerate(con.targets):
box = topcol.box()
has_target = tgt.target is not None
header = box.row()
header.use_property_split = False
split = header.split(factor=0.45, align=True)
split.prop(tgt, "target", text="")
row = split.row(align=True)
row.active = has_target
if has_target:
row.prop_search(tgt, "subtarget", tgt.target.data, "bones", text="")
else:
row.prop(tgt, "subtarget", text="", icon="BONE_DATA")
header.operator("constraint.remove_target", icon="REMOVE", text="").index = i
col = box.column()
col.active = has_target and tgt.subtarget != ""
col.prop(tgt, "weight", slider=True)
topcol.operator("constraint.normalize_target_weights")
topcol.prop(con, "use_deform_preserve_volume")
topcol.prop(con, "use_bone_envelopes")
if context.pose_bone:
topcol.prop(con, "use_current_location")
class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
bl_label = "Object Constraints"

View File

@ -131,11 +131,15 @@ void BKE_constraints_id_loop(struct ListBase *list, ConstraintIDFunc func, void
void BKE_constraint_free_data(struct bConstraint *con);
void BKE_constraint_free_data_ex(struct bConstraint *con, bool do_id_user);
bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct);
/* Constraint API function prototypes */
struct bConstraint *BKE_constraints_active_get(struct ListBase *list);
void BKE_constraints_active_set(ListBase *list, struct bConstraint *con);
struct bConstraint *BKE_constraints_find_name(struct ListBase *list, const char *name);
struct bConstraint *BKE_constraint_find_from_target(struct Object *ob, struct bConstraintTarget *tgt);
struct bConstraint *BKE_constraint_add_for_object(struct Object *ob, const char *name, short type);
struct bConstraint *BKE_constraint_add_for_pose(struct Object *ob, struct bPoseChannel *pchan, const char *name, short type);

View File

@ -2098,6 +2098,206 @@ static bConstraintTypeInfo CTI_PYTHON = {
pycon_evaluate /* evaluate */
};
/* ----------- Armature Constraint -------------- */
static void armdef_free(bConstraint *con)
{
bArmatureConstraint *data = con->data;
/* Target list. */
BLI_freelistN(&data->targets);
}
static void armdef_copy(bConstraint *con, bConstraint *srccon)
{
bArmatureConstraint *pcon = (bArmatureConstraint *)con->data;
bArmatureConstraint *opcon = (bArmatureConstraint *)srccon->data;
BLI_duplicatelist(&pcon->targets, &opcon->targets);
}
static int armdef_get_tars(bConstraint *con, ListBase *list)
{
if (con && list) {
bArmatureConstraint *data = con->data;
*list = data->targets;
return BLI_listbase_count(&data->targets);
}
return 0;
}
static void armdef_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
{
bArmatureConstraint *data = con->data;
bConstraintTarget *ct;
/* Target list. */
for (ct = data->targets.first; ct; ct = ct->next) {
func(con, (ID **)&ct->tar, false, userdata);
}
}
/* Compute the world space pose matrix of the target bone. */
static void armdef_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
bConstraint *UNUSED(con), bConstraintOb *UNUSED(cob),
bConstraintTarget *ct, float UNUSED(ctime))
{
if (ct != NULL) {
if (ct->tar && ct->tar->type == OB_ARMATURE) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
if (pchan != NULL) {
mul_m4_m4m4(ct->matrix, ct->tar->obmat, pchan->pose_mat);
return;
}
}
unit_m4(ct->matrix);
}
}
/* Compute and accumulate transformation for a single target bone. */
static void armdef_accumulate_bone(bConstraintTarget *ct, bPoseChannel *pchan, const float wco[3], bool force_envelope, float *r_totweight, float r_sum_mat[4][4], DualQuat *r_sum_dq)
{
float mat[4][4], iobmat[4][4], iamat[4][4], basemat[4][4], co[3];
Bone *bone = pchan->bone;
float weight = ct->weight;
/* Our object's location in target pose space. */
invert_m4_m4(iobmat, ct->tar->obmat);
mul_v3_m4v3(co, iobmat, wco);
/* Inverted rest pose matrix: bone->chan_mat may not be final yet. */
invert_m4_m4(iamat, bone->arm_mat);
/* Multiply by the envelope weight when appropriate. */
if (force_envelope || (bone->flag & BONE_MULT_VG_ENV)) {
weight *= distfactor_to_bone(co, bone->arm_head, bone->arm_tail,
bone->rad_head, bone->rad_tail, bone->dist);
}
/* Find the correct bone transform matrix in world space. */
if (bone->segments > 1) {
/* The target is a B-Bone:
* FIRST: find the segment (see b_bone_deform in armature.c)
* Need to transform co back to bonespace, only need y. */
float y = iamat[0][1] * co[0] + iamat[1][1] * co[1] + iamat[2][1] * co[2] + iamat[3][1];
float segment = bone->length / ((float)bone->segments);
int a = (int)(y / segment);
CLAMP(a, 0, bone->segments - 1);
/* SECOND: compute the matrix (see pchan_b_bone_defmats in armature.c) */
Mat4 b_bone[MAX_BBONE_SUBDIV], b_bone_rest[MAX_BBONE_SUBDIV];
float irmat[4][4];
b_bone_spline_setup(pchan, false, b_bone);
b_bone_spline_setup(pchan, true, b_bone_rest);
invert_m4_m4(irmat, b_bone_rest[a].mat);
mul_m4_series(mat, ct->matrix, b_bone[a].mat, irmat, iamat, iobmat);
}
else {
/* Simple bone. */
mul_m4_series(mat, ct->matrix, iamat, iobmat);
}
/* Accumulate the transformation. */
*r_totweight += weight;
if (r_sum_dq != NULL) {
DualQuat tmpdq;
mul_m4_series(basemat, ct->tar->obmat, bone->arm_mat, iobmat);
mat4_to_dquat(&tmpdq, basemat, mat);
add_weighted_dq_dq(r_sum_dq, &tmpdq, weight);
}
else {
mul_m4_fl(mat, weight);
add_m4_m4m4(r_sum_mat, r_sum_mat, mat);
}
}
static void armdef_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
{
bArmatureConstraint *data = con->data;
float sum_mat[4][4], input_co[3];
DualQuat sum_dq;
float weight = 0.0f;
/* Prepare for blending. */
zero_m4(sum_mat);
memset(&sum_dq, 0, sizeof(sum_dq));
DualQuat *pdq = (data->flag & CONSTRAINT_ARMATURE_QUATERNION) ? &sum_dq : NULL;
bool use_envelopes = (data->flag & CONSTRAINT_ARMATURE_ENVELOPE) != 0;
if (cob->pchan && cob->pchan->bone && !(data->flag & CONSTRAINT_ARMATURE_CUR_LOCATION)) {
/* For constraints on bones, use the rest position to bind b-bone segments
* and envelopes, to allow safely changing the bone location as if parented. */
copy_v3_v3(input_co, cob->pchan->bone->arm_head);
mul_m4_v3(cob->ob->obmat, input_co);
}
else {
copy_v3_v3(input_co, cob->matrix[3]);
}
/* Process all targets. */
for (bConstraintTarget *ct = targets->first; ct; ct = ct->next) {
if (ct->weight <= 0.0f) {
continue;
}
/* Lookup the bone and abort if failed. */
if (!VALID_CONS_TARGET(ct) || ct->tar->type != OB_ARMATURE) {
return;
}
bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
if (pchan == NULL || pchan->bone == NULL) {
return;
}
armdef_accumulate_bone(ct, pchan, input_co, use_envelopes, &weight, sum_mat, pdq);
}
/* Compute the final transform. */
if (weight > 0.0f) {
if (pdq != NULL) {
normalize_dq(pdq, weight);
dquat_to_mat4(sum_mat, pdq);
}
else {
mul_m4_fl(sum_mat, 1.0f / weight);
}
/* Apply the transform to the result matrix. */
mul_m4_m4m4(cob->matrix, sum_mat, cob->matrix);
}
}
static bConstraintTypeInfo CTI_ARMATURE = {
CONSTRAINT_TYPE_ARMATURE, /* type */
sizeof(bArmatureConstraint), /* size */
"Armature", /* name */
"bArmatureConstraint", /* struct name */
armdef_free, /* free data */
armdef_id_looper, /* id looper */
armdef_copy, /* copy data */
NULL, /* new data */
armdef_get_tars, /* get constraint targets */
NULL, /* flush constraint targets */
armdef_get_tarmat, /* get target matrix */
armdef_evaluate /* evaluate */
};
/* -------- Action Constraint ----------- */
static void actcon_new_data(void *cdata)
@ -4469,6 +4669,7 @@ static void constraints_init_typeinfo(void)
constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */
constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */
constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */
constraintsTypeInfo[30] = &CTI_ARMATURE; /* Armature Constraint */
}
/* This function should be used for getting the appropriate type-info when only
@ -4684,6 +4885,11 @@ static bConstraint *add_new_constraint(Object *ob, bPoseChannel *pchan, const ch
return con;
}
bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *UNUSED(ct))
{
return (con->flag & CONSTRAINT_BBONE_SHAPE) || (con->type == CONSTRAINT_TYPE_ARMATURE);
}
/* ......... */
/* Add new constraint for the given bone */
@ -4829,6 +5035,48 @@ void BKE_constraints_active_set(ListBase *list, bConstraint *con)
}
}
static bConstraint *constraint_list_find_from_target(ListBase *constraints, bConstraintTarget *tgt)
{
for (bConstraint *con = constraints->first; con; con = con->next) {
ListBase *targets = NULL;
if (con->type == CONSTRAINT_TYPE_PYTHON) {
targets = &((bPythonConstraint*)con->data)->targets;
}
else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
targets = &((bArmatureConstraint*)con->data)->targets;
}
if (targets && BLI_findindex(targets, tgt) != -1) {
return con;
}
}
return NULL;
}
/* Finds the constraint that owns the given target within the object. */
bConstraint *BKE_constraint_find_from_target(Object *ob, bConstraintTarget *tgt)
{
bConstraint *result = constraint_list_find_from_target(&ob->constraints, tgt);
if (result != NULL) {
return result;
}
if (ob->pose != NULL) {
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
result = constraint_list_find_from_target(&pchan->constraints, tgt);
if (result != NULL) {
return result;
}
}
}
return NULL;
}
/* -------- Constraints and Proxies ------- */
/* Rescue all constraints tagged as being CONSTRAINT_PROXY_LOCAL (i.e. added to bone that's proxy-synced in this file) */

View File

@ -1872,19 +1872,24 @@ void dquat_to_mat4(float mat[4][4], const DualQuat *dq)
/* normalize */
len = sqrtf(dot_qtqt(q0, q0));
if (len != 0.0f)
mul_qt_fl(q0, 1.0f / len);
if (len != 0.0f) {
len = 1.0f / len;
}
mul_qt_fl(q0, len);
/* rotation */
quat_to_mat4(mat, q0);
/* translation */
t = dq->trans;
mat[3][0] = 2.0f * (-t[0] * q0[1] + t[1] * q0[0] - t[2] * q0[3] + t[3] * q0[2]);
mat[3][1] = 2.0f * (-t[0] * q0[2] + t[1] * q0[3] + t[2] * q0[0] - t[3] * q0[1]);
mat[3][2] = 2.0f * (-t[0] * q0[3] - t[1] * q0[2] + t[2] * q0[1] + t[3] * q0[0]);
mat[3][0] = 2.0f * (-t[0] * q0[1] + t[1] * q0[0] - t[2] * q0[3] + t[3] * q0[2]) * len;
mat[3][1] = 2.0f * (-t[0] * q0[2] + t[1] * q0[3] + t[2] * q0[0] - t[3] * q0[1]) * len;
mat[3][2] = 2.0f * (-t[0] * q0[3] - t[1] * q0[2] + t[2] * q0[1] + t[3] * q0[0]) * len;
/* note: this does not handle scaling */
/* scaling */
if (dq->scale_weight) {
mul_m4_m4m4(mat, mat, dq->scale);
}
}
void add_weighted_dq_dq(DualQuat *dqsum, const DualQuat *dq, float weight)

View File

@ -3501,6 +3501,14 @@ static void direct_link_constraints(FileData *fd, ListBase *lb)
IDP_DirectLinkGroup_OrFree(&data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
break;
}
case CONSTRAINT_TYPE_ARMATURE:
{
bArmatureConstraint *data= con->data;
link_list(fd, &data->targets);
break;
}
case CONSTRAINT_TYPE_SPLINEIK:
{
bSplineIKConstraint *data= con->data;

View File

@ -1536,6 +1536,18 @@ static void write_constraints(WriteData *wd, ListBase *conlist)
break;
}
case CONSTRAINT_TYPE_ARMATURE:
{
bArmatureConstraint *data = con->data;
bConstraintTarget *ct;
/* write targets */
for (ct = data->targets.first; ct; ct = ct->next) {
writestruct(wd, DATA, bConstraintTarget, 1, ct);
}
break;
}
case CONSTRAINT_TYPE_SPLINEIK:
{
bSplineIKConstraint *data = con->data;

View File

@ -965,7 +965,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
opcode);
add_relation(target_key, constraint_op_key, cti->name);
/* if needs bbone shape, also reference handles */
if (con->flag & CONSTRAINT_BBONE_SHAPE) {
if (BKE_constraint_target_uses_bbone(con, ct)) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ct->tar->pose, ct->subtarget);
/* actually a bbone */
if (pchan && pchan->bone && pchan->bone->segments > 1) {

View File

@ -810,6 +810,7 @@ static bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop)
switch (con->type) {
/* multi-transform constraints */
case CONSTRAINT_TYPE_CHILDOF:
case CONSTRAINT_TYPE_ARMATURE:
return true;
case CONSTRAINT_TYPE_TRANSFORM:
case CONSTRAINT_TYPE_TRANSLIKE:

View File

@ -430,6 +430,11 @@ static void test_constraint(Main *bmain, Object *owner, bPoseChannel *pchan, bCo
if (check_targets && cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
/* constraints with empty target list that actually require targets */
if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
con->flag |= CONSTRAINT_DISABLE;
}
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
@ -473,6 +478,18 @@ static void test_constraint(Main *bmain, Object *owner, bPoseChannel *pchan, bCo
}
}
}
else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
if (ct->tar) {
if (ct->tar->type != OB_ARMATURE) {
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar), ct->subtarget)) {
/* bone must exist in armature... */
con->flag |= CONSTRAINT_DISABLE;
}
}
}
}
/* free any temporary targets */
@ -1245,7 +1262,9 @@ void ED_object_constraint_tag_update(Main *bmain, Object *ob, bConstraint *con)
BKE_pose_tag_update_constraint_flags(ob->pose);
}
object_test_constraint(bmain, ob, con);
if (con) {
object_test_constraint(bmain, ob, con);
}
if (ob->type == OB_ARMATURE)
DEG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);

View File

@ -5599,6 +5599,7 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list)
if (ELEM(con->type,
CONSTRAINT_TYPE_FOLLOWPATH,
CONSTRAINT_TYPE_CLAMPTO,
CONSTRAINT_TYPE_ARMATURE,
CONSTRAINT_TYPE_OBJECTSOLVER,
CONSTRAINT_TYPE_FOLLOWTRACK))
{

View File

@ -94,6 +94,8 @@ typedef struct bConstraintTarget {
short flag; /* runtime settings (for editor, etc.) */
short type; /* type of target (eConstraintObType) */
short rotOrder; /* rotation order for target (as defined in BLI_math.h) */
float weight; /* weight for armature deform */
char pad[4];
} bConstraintTarget;
/* bConstraintTarget -> flag */
@ -180,6 +182,13 @@ typedef struct bSplineIKConstraint {
float bulge_smooth;
} bSplineIKConstraint;
/* Armature Constraint */
typedef struct bArmatureConstraint {
int flag; /* general settings/state indicators accessed by bitmapping */
char pad[4];
ListBase targets; /* a list of targets that this constraint has (bConstraintTarget-s) */
} bArmatureConstraint;
/* Single-target subobject constraints --------------------- */
@ -504,6 +513,7 @@ typedef enum eBConstraint_Types {
CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */
CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */
CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */
CONSTRAINT_TYPE_ARMATURE = 30, /* Armature Deform Constraint */
/* NOTE: no constraints are allowed to be added after this */
NUM_CONSTRAINT_TYPES
@ -747,6 +757,13 @@ typedef enum eSplineIK_XZScaleModes {
CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC = 3
} eSplineIK_XZScaleModes;
/* bArmatureConstraint -> flag */
typedef enum eArmature_Flags {
CONSTRAINT_ARMATURE_QUATERNION = (1<<0), /* use dual quaternion blending */
CONSTRAINT_ARMATURE_ENVELOPE = (1<<1), /* use envelopes */
CONSTRAINT_ARMATURE_CUR_LOCATION = (1<<2), /* use current bone location */
} eArmature_Flags;
/* MinMax (floor) flags */
typedef enum eFloor_Flags {
MINMAX_STICKY = (1<<0),

View File

@ -27,6 +27,9 @@
#include <stdlib.h>
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "MEM_guardedalloc.h"
#include "BLT_translation.h"
@ -106,6 +109,8 @@ const EnumPropertyItem rna_enum_constraint_type_items[] = {
"Custom constraint(s) written in Python (Not yet implemented)"}, */
{CONSTRAINT_TYPE_SHRINKWRAP, "SHRINKWRAP", ICON_CONSTRAINT, "Shrinkwrap",
"Restrict movements to surface of target mesh"},
{CONSTRAINT_TYPE_ARMATURE, "ARMATURE", ICON_CONSTRAINT, "Armature",
"Apply weight-blended transformation from multiple bones like the Armature modifier"},
{0, NULL, 0, NULL, NULL}
};
@ -192,6 +197,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
return &RNA_MaintainVolumeConstraint;
case CONSTRAINT_TYPE_PYTHON:
return &RNA_PythonConstraint;
case CONSTRAINT_TYPE_ARMATURE:
return &RNA_ArmatureConstraint;
case CONSTRAINT_TYPE_ACTION:
return &RNA_ActionConstraint;
case CONSTRAINT_TYPE_LOCKTRACK:
@ -235,6 +242,17 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
}
}
static void rna_ConstraintTargetBone_target_set(PointerRNA *ptr, PointerRNA value)
{
bConstraintTarget *tgt = (bConstraintTarget *)ptr->data;
Object *ob = value.data;
if (!ob || ob->type == OB_ARMATURE) {
id_lib_extern((ID *)ob);
tgt->tar = ob;
}
}
static void rna_Constraint_name_set(PointerRNA *ptr, const char *value)
{
bConstraint *con = ptr->data;
@ -260,10 +278,8 @@ static void rna_Constraint_name_set(PointerRNA *ptr, const char *value)
BKE_animdata_fix_paths_rename_all(NULL, "constraints", oldname, con->name);
}
static char *rna_Constraint_path(PointerRNA *ptr)
static char *rna_Constraint_do_compute_path(Object *ob, bConstraint *con)
{
Object *ob = ptr->id.data;
bConstraint *con = ptr->data;
bPoseChannel *pchan;
ListBase *lb = get_constraint_lb(ob, con, &pchan);
@ -285,6 +301,55 @@ static char *rna_Constraint_path(PointerRNA *ptr)
}
}
static char *rna_Constraint_path(PointerRNA *ptr)
{
Object *ob = ptr->id.data;
bConstraint *con = ptr->data;
return rna_Constraint_do_compute_path(ob, con);
}
static bConstraint* rna_constraint_from_target(PointerRNA *ptr)
{
Object *ob = ptr->id.data;
bConstraintTarget *tgt = ptr->data;
return BKE_constraint_find_from_target(ob, tgt);
}
static char *rna_ConstraintTarget_path(PointerRNA *ptr)
{
Object *ob = ptr->id.data;
bConstraintTarget *tgt = ptr->data;
bConstraint *con = rna_constraint_from_target(ptr);
int index = -1;
if (con != NULL) {
if (con->type == CONSTRAINT_TYPE_ARMATURE) {
bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
index = BLI_findindex(&acon->targets, tgt);
}
else if (con->type == CONSTRAINT_TYPE_PYTHON) {
bPythonConstraint *pcon = (bPythonConstraint*)con->data;
index = BLI_findindex(&pcon->targets, tgt);
}
}
if (index >= 0) {
char *con_path = rna_Constraint_do_compute_path(ob, con);
char *result = BLI_sprintfN("%s.targets[%d]", con_path, index);
MEM_freeN(con_path);
return result;
}
else {
printf("%s: internal error, constraint '%s' of object '%s' does not contain the target\n",
__func__, con->name, ob->id.name);
}
return NULL;
}
static void rna_Constraint_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_tag_update(bmain, ptr->id.data, ptr->data);
@ -295,6 +360,16 @@ static void rna_Constraint_dependency_update(Main *bmain, Scene *UNUSED(scene),
ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, ptr->data);
}
static void rna_ConstraintTarget_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_tag_update(bmain, ptr->id.data, rna_constraint_from_target(ptr));
}
static void rna_ConstraintTarget_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, rna_constraint_from_target(ptr));
}
static void rna_Constraint_influence_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob = ptr->id.data;
@ -360,6 +435,42 @@ static const EnumPropertyItem *rna_Constraint_target_space_itemf(bContext *UNUSE
return space_object_items;
}
static bConstraintTarget *rna_ArmatureConstraint_target_new(ID *id, bConstraint *con, Main *bmain)
{
bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
bConstraintTarget *tgt = MEM_callocN(sizeof(bConstraintTarget), "Constraint Target");
tgt->weight = 1.0f;
BLI_addtail(&acon->targets, tgt);
ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
return tgt;
}
static void rna_ArmatureConstraint_target_remove(ID *id, bConstraint *con, Main *bmain, ReportList *reports, PointerRNA *target_ptr)
{
bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
bConstraintTarget *tgt = target_ptr->data;
if (BLI_findindex(&acon->targets, tgt) < 0) {
BKE_reportf(reports, RPT_ERROR, "Target is not in the constraint target list");
return;
}
BLI_freelinkN(&acon->targets, tgt);
ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
}
static void rna_ArmatureConstraint_target_clear(ID *id, bConstraint *con, Main *bmain)
{
bArmatureConstraint *acon = (bArmatureConstraint*)con->data;
BLI_freelistN(&acon->targets);
ED_object_constraint_dependency_tag_update(bmain, (Object*)id, con);
}
static void rna_ActionConstraint_minmax_range(PointerRNA *ptr, float *min, float *max,
float *UNUSED(softmin), float *UNUSED(softmax))
{
@ -564,16 +675,58 @@ static void rna_def_constraint_target_common(StructRNA *srna)
static void rna_def_constrainttarget(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ConstraintTarget", NULL);
RNA_def_struct_ui_text(srna, "Constraint Target", "Target object for multi-target constraints");
RNA_def_struct_path_func(srna, "rna_ConstraintTarget_path");
RNA_def_struct_sdna(srna, "bConstraintTarget");
rna_def_constraint_target_common(srna);
prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "tar");
RNA_def_property_ui_text(prop, "Target", "Target object");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "subtarget");
RNA_def_property_ui_text(prop, "Sub-Target", "Armature bone, mesh or lattice vertex group, ...");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
/* space, flag and type still to do */
}
static void rna_def_constrainttarget_bone(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ConstraintTargetBone", NULL);
RNA_def_struct_ui_text(srna, "Constraint Target Bone", "Target bone for multi-target constraints");
RNA_def_struct_path_func(srna, "rna_ConstraintTarget_path");
RNA_def_struct_sdna(srna, "bConstraintTarget");
prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "tar");
RNA_def_property_ui_text(prop, "Target", "Target armature");
RNA_def_property_pointer_funcs(prop, NULL, "rna_ConstraintTargetBone_target_set", NULL, "rna_Armature_object_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "subtarget");
RNA_def_property_ui_text(prop, "Sub-Target", "Target armature bone");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_dependency_update");
prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "weight");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Blend Weight", "Blending weight of this bone");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_ConstraintTarget_update");
}
static void rna_def_constraint_childof(BlenderRNA *brna)
{
StructRNA *srna;
@ -673,6 +826,70 @@ static void rna_def_constraint_python(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Script Error", "The linked Python script has thrown an error");
}
static void rna_def_constraint_armature_deform_targets(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "ArmatureConstraintTargets");
srna = RNA_def_struct(brna, "ArmatureConstraintTargets", NULL);
RNA_def_struct_sdna(srna, "bConstraint");
RNA_def_struct_ui_text(srna, "Armature Deform Constraint Targets", "Collection of target bones and weights");
func = RNA_def_function(srna, "new", "rna_ArmatureConstraint_target_new");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "Add a new target to the constraint");
parm = RNA_def_pointer(func, "target", "ConstraintTargetBone", "", "New target bone");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_ArmatureConstraint_target_remove");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Delete target from the constraint");
parm = RNA_def_pointer(func, "target", "ConstraintTargetBone", "", "Target to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
func = RNA_def_function(srna, "clear", "rna_ArmatureConstraint_target_clear");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "Delete all targets from object");
}
static void rna_def_constraint_armature_deform(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ArmatureConstraint", "Constraint");
RNA_def_struct_ui_text(srna, "Armature Constraint", "Applies transformations done by the Armature modifier");
RNA_def_struct_sdna_from(srna, "bArmatureConstraint", "data");
prop = RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "targets", NULL);
RNA_def_property_struct_type(prop, "ConstraintTargetBone");
RNA_def_property_ui_text(prop, "Targets", "Target Bones");
rna_def_constraint_armature_deform_targets(brna, prop);
prop = RNA_def_property(srna, "use_deform_preserve_volume", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_QUATERNION);
RNA_def_property_ui_text(prop, "Preserve Volume", "Deform rotation interpolation with quaternions");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "use_bone_envelopes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_ENVELOPE);
RNA_def_property_ui_text(prop, "Use Envelopes",
"Multiply weights by envelope for all bones, instead of acting like Vertex Group based blending. "
"The specified weights are still used, and only the listed bones are considered");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "use_current_location", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_ARMATURE_CUR_LOCATION);
RNA_def_property_ui_text(prop, "Use Current Location",
"Use the current bone location for envelopes and choosing B-Bone segments instead of rest position");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
}
static void rna_def_constraint_kinematic(BlenderRNA *brna)
{
StructRNA *srna;
@ -2465,9 +2682,11 @@ void RNA_def_constraint(BlenderRNA *brna)
/* pointers */
rna_def_constrainttarget(brna);
rna_def_constrainttarget_bone(brna);
rna_def_constraint_childof(brna);
rna_def_constraint_python(brna);
rna_def_constraint_armature_deform(brna);
rna_def_constraint_stretch_to(brna);
rna_def_constraint_follow_path(brna);
rna_def_constraint_locked_track(brna);