From ae3cf92491be73f353b278f1632a68990d0e44d9 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Fri, 30 Oct 2009 06:33:40 +0000 Subject: [PATCH] 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. --- .../scripts/ui/buttons_object_constraint.py | 7 + source/blender/blenkernel/intern/constraint.c | 136 ++++++++++++++++-- source/blender/blenlib/intern/arithb.c | 1 + .../blender/makesdna/DNA_constraint_types.h | 13 +- source/blender/makesrna/RNA_access.h | 1 + .../blender/makesrna/intern/rna_constraint.c | 44 +++++- 6 files changed, 189 insertions(+), 13 deletions(-) diff --git a/release/scripts/ui/buttons_object_constraint.py b/release/scripts/ui/buttons_object_constraint.py index 63fe27f2e4b..07ea5f43907 100644 --- a/release/scripts/ui/buttons_object_constraint.py +++ b/release/scripts/ui/buttons_object_constraint.py @@ -568,6 +568,13 @@ class ConstraintButtonsPanel(bpy.types.Panel): row.itemR(con, "axis_x") row.itemR(con, "axis_y") 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): __label__ = "Constraints" diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index cf919f024b8..93a4ba80d7c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2179,7 +2179,7 @@ static void locktrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * Projf(vec2, vec, cob->matrix[1]); VecSubf(totmat[2], vec, vec2); Normalize(totmat[2]); - + /* the y axis is fixed */ totmat[1][0] = cob->matrix[1][0]; totmat[1][1] = cob->matrix[1][1]; @@ -2311,20 +2311,20 @@ static void locktrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * break; default: { - 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[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; + 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[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; } break; } } break; default: - { - 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[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; - } + { + 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[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; + } break; } /* Block to keep matrix heading */ @@ -3327,7 +3327,124 @@ static bConstraintTypeInfo CTI_SHRINKWRAP = { 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 *************************** */ /* 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[19]= &CTI_TRANSFORM; /* Transformation 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 diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index 68f56620e21..c42e21d5234 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -131,6 +131,7 @@ float Normalize(float *n) return d; } +/* Crossf stores the cross product c = a x b */ void Crossf(float *c, float *a, float *b) { c[0] = a[1] * b[2] - a[2] * b[1]; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 7232042c876..7b0b1c3d814 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -219,6 +219,14 @@ typedef struct bLockTrackConstraint { char subtarget[32]; } bLockTrackConstraint; +/* Damped Tracking constraint */ +typedef struct bDampTrackConstraint { + Object *tar; + int trackflag; + int pad; + char subtarget[32]; +} bDampTrackConstraint; + /* Follow Path constraints */ typedef struct bFollowPathConstraint { Object *tar; /* Must be path object */ @@ -331,6 +339,7 @@ typedef struct bDistLimitConstraint { int pad; } bDistLimitConstraint; +/* ShrinkWrap Constraint */ typedef struct bShrinkwrapConstraint { Object *target; float dist; /* distance to kept from target */ @@ -368,10 +377,10 @@ typedef enum B_CONSTAINT_TYPES { CONSTRAINT_TYPE_CLAMPTO, /* clampto constraint */ CONSTRAINT_TYPE_TRANSFORM, /* transformation (loc/rot/size -> loc/rot/size) 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 */ - NUM_CONSTRAINT_TYPES= CONSTRAINT_TYPE_SHRINKWRAP + NUM_CONSTRAINT_TYPES= CONSTRAINT_TYPE_DAMPTRACK } B_CONSTRAINT_TYPES; /* bConstraint->flag */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 191c8781f18..1d4aa100fce 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -172,6 +172,7 @@ extern StructRNA RNA_CurveMapPoint; extern StructRNA RNA_CurveMapping; extern StructRNA RNA_CurveModifier; extern StructRNA RNA_CurvePoint; +extern StructRNA RNA_DampedTrackConstraint; extern StructRNA RNA_DecimateModifier; extern StructRNA RNA_DelaySensor; extern StructRNA RNA_DisplaceModifier; diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 6a7885c3a16..7a9a07939b1 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -49,8 +49,9 @@ EnumPropertyItem constraint_type_items[] ={ {CONSTRAINT_TYPE_SIZELIMIT, "LIMIT_SCALE", ICON_CONSTRAINT_DATA, "Limit Scale", ""}, {CONSTRAINT_TYPE_DISTLIMIT, "LIMIT_DISTANCE", ICON_CONSTRAINT_DATA, "Limit Distance", ""}, {0, "", 0, "Tracking", ""}, - {CONSTRAINT_TYPE_TRACKTO, "TRACK_TO", ICON_CONSTRAINT_DATA, "Track To", ""}, - {CONSTRAINT_TYPE_LOCKTRACK, "LOCKED_TRACK", ICON_CONSTRAINT_DATA, "Locked Track", ""}, + {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", "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_STRETCHTO, "STRETCH_TO",ICON_CONSTRAINT_DATA, "Stretch To", ""}, {CONSTRAINT_TYPE_KINEMATIC, "IK", ICON_CONSTRAINT_DATA, "Inverse Kinematics", ""}, @@ -144,6 +145,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr) return &RNA_LimitDistanceConstraint; case CONSTRAINT_TYPE_SHRINKWRAP: return &RNA_ShrinkwrapConstraint; + case CONSTRAINT_TYPE_DAMPTRACK: + return &RNA_DampedTrackConstraint; default: 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"); } +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 */ 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_transform(brna); rna_def_constraint_shrinkwrap(brna); + rna_def_constraint_damped_track(brna); } #endif