New Track To Constraint: "Damped"

This is effectively a C-port of Nathan Vegdahl's "No Twist" TrackTo PyConstraint, and has been added as a separate type of constraint to be consistent with the existing constraints (Locked Track, and Track To). 

In general, this works considerably better than the existing "Track To" constraint, since it works by determining the smallest rotation necessary to get the current orientation of the owner to an orientation which would be tracking the target. It is also a much more straightforward approach than the weird old method the old Track To uses.

I've made a few tweaks to the code to deal with the (hopefully rare) cases where the target and the constrained are coincident. These don't appear to cause too much trouble in general.

TODO:
- Probably the naming of the constraints will change, to better convey their purposes. Naming suggestions welcome.
This commit is contained in:
Joshua Leung 2009-10-30 06:33:40 +00:00
parent fe68d2672d
commit ae3cf92491
6 changed files with 189 additions and 13 deletions

@ -568,6 +568,13 @@ class ConstraintButtonsPanel(bpy.types.Panel):
row.itemR(con, "axis_x") row.itemR(con, "axis_x")
row.itemR(con, "axis_y") row.itemR(con, "axis_y")
row.itemR(con, "axis_z") row.itemR(con, "axis_z")
def DAMPED_TRACK(self, context, layout, con):
self.target_template(layout, con)
row = layout.row()
row.itemL(text="To:")
row.itemR(con, "track", expand=True)
class OBJECT_PT_constraints(ConstraintButtonsPanel): class OBJECT_PT_constraints(ConstraintButtonsPanel):
__label__ = "Constraints" __label__ = "Constraints"

@ -2179,7 +2179,7 @@ static void locktrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *
Projf(vec2, vec, cob->matrix[1]); Projf(vec2, vec, cob->matrix[1]);
VecSubf(totmat[2], vec, vec2); VecSubf(totmat[2], vec, vec2);
Normalize(totmat[2]); Normalize(totmat[2]);
/* the y axis is fixed */ /* the y axis is fixed */
totmat[1][0] = cob->matrix[1][0]; totmat[1][0] = cob->matrix[1][0];
totmat[1][1] = cob->matrix[1][1]; totmat[1][1] = cob->matrix[1][1];
@ -2311,20 +2311,20 @@ static void locktrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *
break; break;
default: default:
{ {
totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0;
totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0;
totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1;
} }
break; break;
} }
} }
break; break;
default: default:
{ {
totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0;
totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0;
totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1;
} }
break; break;
} }
/* Block to keep matrix heading */ /* Block to keep matrix heading */
@ -3327,7 +3327,124 @@ static bConstraintTypeInfo CTI_SHRINKWRAP = {
shrinkwrap_evaluate /* evaluate */ shrinkwrap_evaluate /* evaluate */
}; };
/* --------- Damped Track ---------- */
static void damptrack_new_data (void *cdata)
{
bDampTrackConstraint *data= (bDampTrackConstraint *)cdata;
data->trackflag = TRACK_Y;
}
static int damptrack_get_tars (bConstraint *con, ListBase *list)
{
if (con && list) {
bDampTrackConstraint *data= con->data;
bConstraintTarget *ct;
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
return 1;
}
return 0;
}
static void damptrack_flush_tars (bConstraint *con, ListBase *list, short nocopy)
{
if (con && list) {
bDampTrackConstraint *data= con->data;
bConstraintTarget *ct= list->first;
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy)
}
}
/* array of direction vectors for the tracking flags */
static const float track_dir_vecs[6][3] = {
{+1,0,0}, {0,+1,0}, {0,0,+1}, /* TRACK_X, TRACK_Y, TRACK_Z */
{-1,0,0}, {0,-1,0}, {0,0,-1} /* TRACK_NX, TRACK_NY, TRACK_NZ */
};
static void damptrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets)
{
bDampTrackConstraint *data= con->data;
bConstraintTarget *ct= targets->first;
if (VALID_CONS_TARGET(ct)) {
float obvec[3], tarvec[3], obloc[3];
float raxis[3], rangle;
float rmat[3][3], tmat[4][4];
/* find the (unit) direction that the axis we're interested in currently points
* - Mat4Mul3Vecfl() only takes the 3x3 (rotation+scaling) components of the 4x4 matrix
* - the normalisation step at the end should take care of any unwanted scaling
* left over in the 3x3 matrix we used
*/
VECCOPY(obvec, track_dir_vecs[data->trackflag]);
Mat4Mul3Vecfl(cob->matrix, obvec);
if (Normalize(obvec) == 0.0f) {
/* exceptional case - just use the track vector as appropriate */
VECCOPY(obvec, track_dir_vecs[data->trackflag]);
}
/* find the (unit) direction vector going from the owner to the target */
VECCOPY(obloc, cob->matrix[3]);
VecSubf(tarvec, ct->matrix[3], obloc);
if (Normalize(tarvec) == 0.0f) {
/* the target is sitting on the owner, so just make them use the same direction vectors */
// FIXME: or would it be better to use the pure direction vector?
VECCOPY(tarvec, obvec);
//VECCOPY(tarvec, track_dir_vecs[data->trackflag]);
}
/* determine the axis-angle rotation, which represents the smallest possible rotation
* between the two rotation vectors (i.e. the 'damping' referred to in the name)
* - we take this to be the rotation around the normal axis/vector to the plane defined
* by the current and destination vectors, which will 'map' the current axis to the
* destination vector
* - the min/max wrappers around (obvec . tarvec) result (stored temporarily in rangle)
* are used to ensure that the smallest angle is chosen
*/
Crossf(raxis, obvec, tarvec);
rangle= Inpf(obvec, tarvec);
rangle= acos( MAX2(-1.0f, MIN2(1.0f, rangle)) );
/* construct rotation matrix from the axis-angle rotation found above
* - this call takes care to make sure that the axis provided is a unit vector first
*/
AxisAngleToMat3(raxis, rangle, rmat);
/* rotate the owner in the way defined by this rotation matrix, then reapply the location since
* we may have destroyed that in the process of multiplying the matrix
*/
Mat4One(tmat);
Mat4MulMat34(tmat, rmat, cob->matrix); // m1, m3, m2
Mat4CpyMat4(cob->matrix, tmat);
VECCOPY(cob->matrix[3], obloc);
}
}
static bConstraintTypeInfo CTI_DAMPTRACK = {
CONSTRAINT_TYPE_DAMPTRACK, /* type */
sizeof(bDampTrackConstraint), /* size */
"Damped Track", /* name */
"bDampTrackConstraint", /* struct name */
NULL, /* free data */
NULL, /* relink data */
NULL, /* copy data */
damptrack_new_data, /* new data */
damptrack_get_tars, /* get constraint targets */
damptrack_flush_tars, /* flush constraint targets */
default_get_tarmat, /* get target matrix */
damptrack_evaluate /* evaluate */
};
/* ************************* Constraints Type-Info *************************** */ /* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out /* All of the constraints api functions use bConstraintTypeInfo structs to carry out
@ -3361,6 +3478,7 @@ static void constraints_init_typeinfo () {
constraintsTypeInfo[18]= &CTI_CLAMPTO; /* ClampTo Constraint */ constraintsTypeInfo[18]= &CTI_CLAMPTO; /* ClampTo Constraint */
constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */ constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */
constraintsTypeInfo[20]= &CTI_SHRINKWRAP; /* Shrinkwrap Constraint */ constraintsTypeInfo[20]= &CTI_SHRINKWRAP; /* Shrinkwrap Constraint */
constraintsTypeInfo[21]= &CTI_DAMPTRACK; /* Damped TrackTo Constraint */
} }
/* This function should be used for getting the appropriate type-info when only /* This function should be used for getting the appropriate type-info when only

@ -131,6 +131,7 @@ float Normalize(float *n)
return d; return d;
} }
/* Crossf stores the cross product c = a x b */
void Crossf(float *c, float *a, float *b) void Crossf(float *c, float *a, float *b)
{ {
c[0] = a[1] * b[2] - a[2] * b[1]; c[0] = a[1] * b[2] - a[2] * b[1];

@ -219,6 +219,14 @@ typedef struct bLockTrackConstraint {
char subtarget[32]; char subtarget[32];
} bLockTrackConstraint; } bLockTrackConstraint;
/* Damped Tracking constraint */
typedef struct bDampTrackConstraint {
Object *tar;
int trackflag;
int pad;
char subtarget[32];
} bDampTrackConstraint;
/* Follow Path constraints */ /* Follow Path constraints */
typedef struct bFollowPathConstraint { typedef struct bFollowPathConstraint {
Object *tar; /* Must be path object */ Object *tar; /* Must be path object */
@ -331,6 +339,7 @@ typedef struct bDistLimitConstraint {
int pad; int pad;
} bDistLimitConstraint; } bDistLimitConstraint;
/* ShrinkWrap Constraint */
typedef struct bShrinkwrapConstraint { typedef struct bShrinkwrapConstraint {
Object *target; Object *target;
float dist; /* distance to kept from target */ float dist; /* distance to kept from target */
@ -368,10 +377,10 @@ typedef enum B_CONSTAINT_TYPES {
CONSTRAINT_TYPE_CLAMPTO, /* clampto constraint */ CONSTRAINT_TYPE_CLAMPTO, /* clampto constraint */
CONSTRAINT_TYPE_TRANSFORM, /* transformation (loc/rot/size -> loc/rot/size) constraint */ CONSTRAINT_TYPE_TRANSFORM, /* transformation (loc/rot/size -> loc/rot/size) constraint */
CONSTRAINT_TYPE_SHRINKWRAP, /* shrinkwrap (loc/rot) constraint */ CONSTRAINT_TYPE_SHRINKWRAP, /* shrinkwrap (loc/rot) constraint */
CONSTRAINT_TYPE_DAMPTRACK, /* New Tracking constraint that minimises twisting */
/* NOTE: everytime a new constraint is added, update this */ /* NOTE: everytime a new constraint is added, update this */
NUM_CONSTRAINT_TYPES= CONSTRAINT_TYPE_SHRINKWRAP NUM_CONSTRAINT_TYPES= CONSTRAINT_TYPE_DAMPTRACK
} B_CONSTRAINT_TYPES; } B_CONSTRAINT_TYPES;
/* bConstraint->flag */ /* bConstraint->flag */

@ -172,6 +172,7 @@ extern StructRNA RNA_CurveMapPoint;
extern StructRNA RNA_CurveMapping; extern StructRNA RNA_CurveMapping;
extern StructRNA RNA_CurveModifier; extern StructRNA RNA_CurveModifier;
extern StructRNA RNA_CurvePoint; extern StructRNA RNA_CurvePoint;
extern StructRNA RNA_DampedTrackConstraint;
extern StructRNA RNA_DecimateModifier; extern StructRNA RNA_DecimateModifier;
extern StructRNA RNA_DelaySensor; extern StructRNA RNA_DelaySensor;
extern StructRNA RNA_DisplaceModifier; extern StructRNA RNA_DisplaceModifier;

@ -49,8 +49,9 @@ EnumPropertyItem constraint_type_items[] ={
{CONSTRAINT_TYPE_SIZELIMIT, "LIMIT_SCALE", ICON_CONSTRAINT_DATA, "Limit Scale", ""}, {CONSTRAINT_TYPE_SIZELIMIT, "LIMIT_SCALE", ICON_CONSTRAINT_DATA, "Limit Scale", ""},
{CONSTRAINT_TYPE_DISTLIMIT, "LIMIT_DISTANCE", ICON_CONSTRAINT_DATA, "Limit Distance", ""}, {CONSTRAINT_TYPE_DISTLIMIT, "LIMIT_DISTANCE", ICON_CONSTRAINT_DATA, "Limit Distance", ""},
{0, "", 0, "Tracking", ""}, {0, "", 0, "Tracking", ""},
{CONSTRAINT_TYPE_TRACKTO, "TRACK_TO", ICON_CONSTRAINT_DATA, "Track To", ""}, {CONSTRAINT_TYPE_TRACKTO, "TRACK_TO", ICON_CONSTRAINT_DATA, "Track To", "Legacy tracking constraint prone to twisting artifacts"},
{CONSTRAINT_TYPE_LOCKTRACK, "LOCKED_TRACK", ICON_CONSTRAINT_DATA, "Locked Track", ""}, {CONSTRAINT_TYPE_LOCKTRACK, "LOCKED_TRACK", ICON_CONSTRAINT_DATA, "Locked Track", "Tracking along a single axis"},
{CONSTRAINT_TYPE_DAMPTRACK, "DAMPED_TRACK", ICON_CONSTRAINT_DATA, "Damped Track", "Tracking by taking the shortest path"},
{CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To", ""}, {CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To", ""},
{CONSTRAINT_TYPE_STRETCHTO, "STRETCH_TO",ICON_CONSTRAINT_DATA, "Stretch To", ""}, {CONSTRAINT_TYPE_STRETCHTO, "STRETCH_TO",ICON_CONSTRAINT_DATA, "Stretch To", ""},
{CONSTRAINT_TYPE_KINEMATIC, "IK", ICON_CONSTRAINT_DATA, "Inverse Kinematics", ""}, {CONSTRAINT_TYPE_KINEMATIC, "IK", ICON_CONSTRAINT_DATA, "Inverse Kinematics", ""},
@ -144,6 +145,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
return &RNA_LimitDistanceConstraint; return &RNA_LimitDistanceConstraint;
case CONSTRAINT_TYPE_SHRINKWRAP: case CONSTRAINT_TYPE_SHRINKWRAP:
return &RNA_ShrinkwrapConstraint; return &RNA_ShrinkwrapConstraint;
case CONSTRAINT_TYPE_DAMPTRACK:
return &RNA_DampedTrackConstraint;
default: default:
return &RNA_UnknownType; return &RNA_UnknownType;
} }
@ -1633,6 +1636,42 @@ static void rna_def_constraint_shrinkwrap(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
} }
static void rna_def_constraint_damped_track(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem damptrack_items[] = {
{TRACK_X, "TRACK_X", 0, "X", ""},
{TRACK_Y, "TRACK_Y", 0, "Y", ""},
{TRACK_Z, "TRACK_Z", 0, "Z", ""},
{TRACK_nX, "TRACK_NEGATIVE_X", 0, "-X", ""},
{TRACK_nY, "TRACK_NEGATIVE_Y", 0, "-Y", ""},
{TRACK_nZ, "TRACK_NEGATIVE_Z", 0, "-Z", ""},
{0, NULL, 0, NULL, NULL}};
srna= RNA_def_struct(brna, "DampedTrackConstraint", "Constraint");
RNA_def_struct_ui_text(srna, "Damped Track Constraint", "Points toward target by taking the shortest rotation path.");
RNA_def_struct_sdna_from(srna, "bDampTrackConstraint", "data");
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_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_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", "");
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update");
prop= RNA_def_property(srna, "track", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "trackflag");
RNA_def_property_enum_items(prop, damptrack_items);
RNA_def_property_ui_text(prop, "Track Axis", "Axis that points to the target object.");
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
}
/* base struct for constraints */ /* base struct for constraints */
void RNA_def_constraint(BlenderRNA *brna) void RNA_def_constraint(BlenderRNA *brna)
{ {
@ -1733,6 +1772,7 @@ void RNA_def_constraint(BlenderRNA *brna)
rna_def_constraint_location_limit(brna); rna_def_constraint_location_limit(brna);
rna_def_constraint_transform(brna); rna_def_constraint_transform(brna);
rna_def_constraint_shrinkwrap(brna); rna_def_constraint_shrinkwrap(brna);
rna_def_constraint_damped_track(brna);
} }
#endif #endif