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