Page MenuHome

.spring_patch_r41542.patch

File Metadata

Author
Yousef Harfoush (bat3a)
Created
Nov 13 2013, 3:22 PM

.spring_patch_r41542.patch

Index: release/scripts/startup/bl_ui/properties_object_constraint.py
===================================================================
--- release/scripts/startup/bl_ui/properties_object_constraint.py (revision 41542)
+++ release/scripts/startup/bl_ui/properties_object_constraint.py (working copy)
@@ -755,7 +755,22 @@
def SCRIPT(self, context, layout, con):
layout.label("Blender 2.5 has no py-constraints")
+
+ def SPRING(self, context, layout, con):
+ self.target_template(layout, con)
+
+ row = layout.row()
+ layout.prop(con, "speed")
+ layout.prop(con, "damping")
+ layout.prop(con, "gravity")
+ layout.prop(con, "dist_threshold")
+ layout.prop(con, "fast_factor")
+ layout.prop(con, "reset_on_frame")
+
+ layout.prop(con, "stiffness_x")
+ layout.prop(con, "stiffness_y")
+ layout.prop(con, "stiffness_z")
class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
bl_label = "Object Constraints"
Index: source/blender/blenkernel/intern/constraint.c
===================================================================
--- source/blender/blenkernel/intern/constraint.c (revision 41542)
+++ source/blender/blenkernel/intern/constraint.c (working copy)
@@ -3924,6 +3924,343 @@
pivotcon_evaluate /* evaluate */
};
+/* ----------- Spring ------------- */
+
+static void spring_id_looper (bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ bSpringConstraint *data= con->data;
+
+ /* target only */
+ func(con, (ID**)&data->tar, userdata);
+}
+
+static int spring_get_tars (bConstraint *con, ListBase *list)
+{
+ if (con && list) {
+ bSpringConstraint *data= con->data;
+ bConstraintTarget *ct;
+
+ /* standard target-getting macro for single-target constraints */
+ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list)
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void spring_flush_tars (bConstraint *con, ListBase *list, short nocopy)
+{
+ if (con && list) {
+ bSpringConstraint *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)
+ }
+}
+
+
+static void spring_new_data (void *cdata)
+{
+ bSpringConstraint *data= (bSpringConstraint *)cdata;
+
+ data->speed = 1.0f;
+ data->damping = 0.5f;
+ data->gravity = 0.0f;
+ data->stiffness_x = 0.0f;
+ data->stiffness_y = 0.0f;
+ data->stiffness_z = 0.0f;
+ data->dist_threshold = 1.0f;
+ data->fast_factor = 3.0f;
+ data->realtime = 0;
+ data->reset_on_frame = 1;
+
+ if(data->spring_buffer)
+ {
+ MEM_freeN( data->spring_buffer );
+ }
+ data->spring_buffer = NULL;
+ data->spring_buffer_size = -1;
+}
+
+static void spring_free (bConstraint *con)
+{
+ bSpringConstraint *data= con->data;
+
+ if (data->spring_buffer)
+ MEM_freeN(data->spring_buffer);
+ data->spring_buffer = NULL;
+}
+
+
+static void spring_copy (bConstraint *con, bConstraint *srccon)
+{
+ bSpringConstraint *src= srccon->data;
+ bSpringConstraint *dst= con->data;
+
+ /* copy the binding array */
+ dst->spring_buffer= MEM_dupallocN(src->spring_buffer);
+
+ dst->speed = src->speed;
+ dst->damping = src->damping;
+ dst->gravity = src->gravity;
+ dst->stiffness_x = src->stiffness_x;
+ dst->stiffness_y = src->stiffness_y;
+ dst->stiffness_z = src->stiffness_z;
+ dst->dist_threshold = src->dist_threshold;
+ dst->fast_factor = src->fast_factor;
+ dst->reset_on_frame = src->reset_on_frame;
+}
+
+
+static void spring_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets)
+{
+ bSpringConstraint *data= con->data;
+ bConstraintTarget *ct= targets->first;
+
+ float current_pos[3], out_pos[3];
+ float dist;
+ short n;
+ float local_pos[3];
+ float mat[3][3], smat[3][3], rotmat[3][3];
+ float crossed[3];
+ float cross_with[3];
+ float upvec[3], size[3];
+ float fast_factor;
+ short spring_update;
+ float bone_len;
+
+ float pos_vec[3];
+ float current_tail_pos[3];
+ int index_frame, index_mem, len;
+ bPoseChannel *pchan = NULL;
+
+ // Allocate Buffer if buffer is empty or frame range has changed
+ if ( cob->scene->r.efra - cob->scene->r.sfra != data->spring_buffer_size || !data->spring_buffer)
+ {
+ len = (cob->scene->r.efra - cob->scene->r.sfra );
+
+ if(data->spring_buffer)
+ {
+ MEM_freeN( data->spring_buffer );
+ data->spring_buffer = NULL;
+ }
+ data->spring_buffer = MEM_mallocN( len*sizeof(float)*6, "Spr Buf");
+ data->spring_buffer_size = cob->scene->r.efra - cob->scene->r.sfra;
+
+ }
+
+
+ { // get bone length
+ float vec[3];
+ pchan = get_pose_channel(ct->tar->pose, ct->subtarget);
+ sub_v3_v3v3( vec, pchan->pose_tail, pchan->pose_head);
+ bone_len = len_v3( vec );
+ }
+
+ // Get tail pos (global)
+ pos_vec[0] = pos_vec[2] = 0.0f;
+ pos_vec[1] = bone_len;
+ mul_v3_m4v3( current_tail_pos, ct->matrix, pos_vec );
+
+
+
+
+ // check if the frame has changed, only update if yes
+ if( data->frame == cob->scene->r.cfra)
+ {
+ spring_update = FALSE;
+ }
+ else
+ {
+ if( cob->scene->r.cfra < data->frame )
+ { // If user scrubbed backwards, reset the kinematics
+ data->momentum[0] = data->momentum[1] = data->momentum[2] = 0.0f;
+ VECCOPY( data->last_pos, current_tail_pos );
+
+ index_frame = cob->scene->r.cfra - cob->scene->r.sfra;
+ index_mem = index_frame*6;
+ if( index_frame >= 0 && index_frame < data->spring_buffer_size)
+ {
+ VECCOPY( data->momentum, &data->spring_buffer[ index_mem ]);
+ VECCOPY( data->last_pos, &data->spring_buffer[ index_mem+3 ]);
+ }
+ }
+ // When user jumps forward, read from buffer.
+ if( cob->scene->r.cfra > data->frame && (abs(data->frame - cob->scene->r.cfra)) > 1)
+ {
+ data->momentum[0] = data->momentum[1] = data->momentum[2] = 0.0f;
+ VECCOPY( data->last_pos, current_tail_pos );
+
+ index_frame = cob->scene->r.cfra - cob->scene->r.sfra;
+ index_mem = index_frame*6;
+ if( index_frame >= 0 && index_frame < data->spring_buffer_size)
+ {
+ VECCOPY( data->momentum, &data->spring_buffer[ index_mem ]);
+ VECCOPY( data->last_pos, &data->spring_buffer[ index_mem+3 ]);
+ }
+
+ }
+
+ if( data->frame == data->reset_on_frame)
+ {
+ data->momentum[0] = data->momentum[1] = data->momentum[2] = 0.0f;
+ VECCOPY( data->last_pos, current_tail_pos );
+ }
+
+ spring_update = TRUE;
+
+
+ if(data->spring_buffer)
+ {
+ index_frame = cob->scene->r.cfra - cob->scene->r.sfra;
+ index_mem = index_frame*6;
+ //printf("Integrity: %d\n", MEM_check_memory_integrity() );
+ if( index_frame >= 0 && index_frame < data->spring_buffer_size)
+ {
+ VECCOPY( &data->spring_buffer[ index_mem ], data->momentum );
+ VECCOPY( &data->spring_buffer[ index_mem+3 ], data->last_pos);
+
+ }
+ }
+ data->frame = cob->scene->r.cfra;
+
+ }
+
+ if(data->realtime>=1.0f) spring_update = TRUE;
+
+ // If no update is required, reuse the output matrix from the last time (glitchy behaviour otherwise)
+ if( spring_update == FALSE )
+ {
+ copy_m4_m4( cob->matrix, data->mat );
+ }
+
+ {
+
+ /*
+ First part - the spring position
+ */
+
+
+ VECCOPY( current_pos, current_tail_pos)
+ VECCOPY( out_pos, data->last_pos);
+
+ if(spring_update == TRUE)
+ {
+
+ for(n=0; n<3; n++) /* procedure for all 3 axes */
+ {
+ dist = current_pos[n] - data->last_pos[n];
+ if( dist < 0.0f ) dist *= -1; // abs
+
+ if( dist > data->dist_threshold )
+ fast_factor = data->fast_factor;
+ else fast_factor = 1.0f;
+
+ if( current_pos[n] > data->last_pos[n])
+ data->momentum[n] += data->speed * 0.05f * (dist*fast_factor);
+ else if ( current_pos[n] < data->last_pos[n] )
+ data->momentum[n] -= data->speed * 0.05f * (dist*fast_factor);
+
+ out_pos[n] += data->momentum[n];
+
+ // Damping
+ data->momentum[n] *= 1.0f - data->damping;
+ }
+
+ // Gravity
+ data->momentum[2] -= data->gravity * 0.01f;
+ }
+
+ /*
+ Second part - align the bone to point at the spring position
+ */
+
+ if(spring_update == TRUE)
+ {
+ // (1)
+ VECSUB( local_pos, out_pos, ct->matrix[3] ); // get local pos
+
+ copy_m3_m4( mat, ct->matrix ); // get rotation matrix
+ invert_m3( mat );
+ mul_m3_v3( mat, local_pos); // mul with inverted rotation matrix, so we get the local position
+
+
+ // to avoid flipping on quick up/down motion
+ if( local_pos[1] < 0.1f) local_pos[1] = 0.1f;
+
+ // local damping / stiffness
+ local_pos[0] *= 1.0f - data->stiffness_x;
+ local_pos[1] *= 1.0f - data->stiffness_y;
+ local_pos[2] *= 1.0f - data->stiffness_z;
+
+ // (2)
+ normalize_v3( local_pos );
+ cross_with[0] = -1.0f;
+ cross_with[1] = local_pos[0];
+ cross_with[2] = 0.0f;
+ cross_v3_v3v3( crossed, local_pos, cross_with );
+
+ // (3)
+ cross_v3_v3v3( upvec, local_pos, crossed );
+ normalize_v3( local_pos );
+ normalize_v3( upvec );
+ normalize_v3( crossed );
+
+ VECCOPY( mat[0], upvec );
+ VECCOPY( mat[1], local_pos );
+ VECCOPY( mat[2], crossed);
+
+
+ size[0] = 1.0f; size[1] = 1.0f; size[2]=1.0f;
+ size_to_mat3(smat,size);
+
+ mul_m3_m3m3(rotmat, mat, smat);
+
+
+ {
+ float tmp_out[4][4] = MAT4_UNITY;
+ float pos_ori[3];
+ VECCOPY( pos_ori, ct->matrix[3] );
+ mul_m4_m4m3(tmp_out, ct->matrix, rotmat);
+ copy_m4_m4(cob->matrix, tmp_out);
+ VECCOPY( cob->matrix[3], pos_ori);
+ copy_m4_m4( data->mat, cob->matrix );
+ }
+
+ }
+
+ // remember this position for the next iteration
+ data->last_pos[0] = out_pos[0];
+ data->last_pos[1] = out_pos[1];
+ data->last_pos[2] = out_pos[2];
+
+ return;
+
+ }
+
+}
+
+
+static bConstraintTypeInfo CTI_SPRING = {
+ CONSTRAINT_TYPE_SPRING, /* type */
+ sizeof(bSpringConstraint), /* size */
+ "Spring", /* name */
+ "bSpringConstraint", /* struct name */
+ spring_free , /* free data */
+ NULL, /* relink data */
+ spring_id_looper, /* id looper */
+ spring_copy, /* copy data */
+ spring_new_data, /* new data */
+ spring_get_tars, /* get constraint targets */
+ spring_flush_tars, /* flush constraint targets */
+ default_get_tarmat, /* get target matrix */
+ spring_evaluate /* evaluate */
+};
+
+
+
/* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out
* and operations that involve constraint specific code.
@@ -3962,6 +4299,7 @@
constraintsTypeInfo[23]= &CTI_TRANSLIKE; /* Copy Transforms Constraint */
constraintsTypeInfo[24]= &CTI_SAMEVOL; /* Maintain Volume Constraint */
constraintsTypeInfo[25]= &CTI_PIVOT; /* Pivot Constraint */
+ constraintsTypeInfo[26]= &CTI_SPRING; /* Pivot Constraint */
}
/* This function should be used for getting the appropriate type-info when only
Index: source/blender/blenloader/intern/readfile.c
===================================================================
--- source/blender/blenloader/intern/readfile.c (revision 41542)
+++ source/blender/blenloader/intern/readfile.c (working copy)
@@ -2341,6 +2341,13 @@
data->points= newdataadr(fd, data->points);
}
break;
+ case CONSTRAINT_TYPE_SPRING:
+ {
+ bSpringConstraint *data= con->data;
+
+ data->spring_buffer= newdataadr(fd, data->spring_buffer);
+ }
+ break;
case CONSTRAINT_TYPE_KINEMATIC:
{
con->lin_error = 0.f;
Index: source/blender/blenloader/intern/writefile.c
===================================================================
--- source/blender/blenloader/intern/writefile.c (revision 41542)
+++ source/blender/blenloader/intern/writefile.c (working copy)
@@ -1204,6 +1204,14 @@
writedata(wd, DATA, sizeof(float)*(data->numpoints), data->points);
}
break;
+ case CONSTRAINT_TYPE_SPRING:
+ {
+ bSpringConstraint *data= (bSpringConstraint*)con->data;
+
+ /* write points array */
+ writedata(wd, DATA, sizeof(float)*(data->spring_buffer_size)*6, data->spring_buffer);
+ }
+ break;
}
}
Index: source/blender/editors/object/object_constraint.c
===================================================================
--- source/blender/editors/object/object_constraint.c (revision 41542)
+++ source/blender/editors/object/object_constraint.c (working copy)
@@ -664,6 +664,7 @@
edit_constraint_properties(ot);
}
+
/* ------------- Child-Of Constraint ------------------ */
/* ChildOf Constraint - set inverse callback */
Index: source/blender/makesdna/DNA_constraint_types.h
===================================================================
--- source/blender/makesdna/DNA_constraint_types.h (revision 41542)
+++ source/blender/makesdna/DNA_constraint_types.h (working copy)
@@ -406,7 +406,31 @@
char pad[9];
} bShrinkwrapConstraint;
+/* Spring Constraint */
+typedef struct bSpringConstraint {
+ struct Object *tar;
+ char subtarget[32];
+ int realtime;
+ int flag;
+ float speed;
+ float damping;
+ float gravity;
+ float last_pos[3];
+ float momentum[3];
+ float stiffness_x;
+ float stiffness_y;
+ float stiffness_z;
+ float dist_threshold;
+ float fast_factor;
+ float *spring_buffer;
+ int frame;
+ int spring_buffer_size;
+ int reset_on_frame;
+ float mat[4][4];
+// int fourbytes;
+} bSpringConstraint;
+
/* ------------------------------------------ */
/* bConstraint->type
@@ -440,6 +464,7 @@
CONSTRAINT_TYPE_TRANSLIKE, /* Copy transform matrix */
CONSTRAINT_TYPE_SAMEVOL, /* Maintain volume during scaling */
CONSTRAINT_TYPE_PIVOT, /* Pivot Constraint */
+ CONSTRAINT_TYPE_SPRING, /* Simple spring dynamics */
/* NOTE: no constraints are allowed to be added after this */
NUM_CONSTRAINT_TYPES
@@ -737,6 +762,12 @@
PIVOTCON_FLAG_ROTACT_NEG = (1<<1)
} ePivotConstraint_Flag;
+/* Spring Constraint -> flag */
+typedef enum eSpring_Flags {
+ DO_USE_RESET = (1<<0),
+} eSpring_Flags;
+
+
/* Rigid-Body Constraint */
#define CONSTRAINT_DRAW_PIVOT 0x40
#define CONSTRAINT_DISABLE_LINKED_COLLISION 0x80
Index: source/blender/makesrna/intern/rna_constraint.c
===================================================================
--- source/blender/makesrna/intern/rna_constraint.c (revision 41542)
+++ source/blender/makesrna/intern/rna_constraint.c (working copy)
@@ -71,6 +71,8 @@
{CONSTRAINT_TYPE_RIGIDBODYJOINT, "RIGID_BODY_JOINT", ICON_CONSTRAINT_DATA, "Rigid Body Joint", ""},
{CONSTRAINT_TYPE_PYTHON, "SCRIPT", ICON_CONSTRAINT_DATA, "Script", ""},
{CONSTRAINT_TYPE_SHRINKWRAP, "SHRINKWRAP", ICON_CONSTRAINT_DATA, "Shrinkwrap", ""},
+ {CONSTRAINT_TYPE_SPRING, "SPRING", ICON_CONSTRAINT_DATA, "Spring", ""},
+
{0, NULL, 0, NULL, NULL}};
static EnumPropertyItem target_space_pchan_items[] = {
@@ -156,6 +158,8 @@
return &RNA_CopyTransformsConstraint;
case CONSTRAINT_TYPE_PIVOT:
return &RNA_PivotConstraint;
+ case CONSTRAINT_TYPE_SPRING:
+ return &RNA_SpringConstraint;
default:
return &RNA_UnknownType;
}
@@ -2026,6 +2030,104 @@
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
}
+static void rna_def_constraint_spring(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna= RNA_def_struct(brna, "SpringConstraint", "Constraint");
+ RNA_def_struct_ui_text(srna, "Spring Constraint", "Copies the location of the target");
+ RNA_def_struct_sdna_from(srna, "bSpringConstraint", "data");
+
+/* prop= RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, "bConstraint", "headtail");
+ RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+*/
+ 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, "speed", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "speed");
+ RNA_def_property_range(prop, 0.0, 100.0f);
+ RNA_def_property_ui_text(prop, "speed", "Speed property");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "damping", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "damping");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "damping", "Damping property");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "gravity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "gravity");
+ RNA_def_property_range(prop, 0.0, 100.0f);
+ RNA_def_property_ui_text(prop, "gravity", "Gravity property");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "stiffness_x", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "stiffness_x");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "Stiffness x", "Stiffness x");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "stiffness_y", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "stiffness_y");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "Stiffness y", "Stiffness y");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "stiffness_z", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "stiffness_z");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "Stiffness z", "Stiffness z");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "dist_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "dist_threshold");
+ RNA_def_property_range(prop, 0.0, 10.0f);
+ RNA_def_property_ui_text(prop, "Distance Threshold", "Distance Threshold");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ prop= RNA_def_property(srna, "fast_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fast_factor");
+ RNA_def_property_range(prop, 1.0, 10.0f);
+ RNA_def_property_ui_text(prop, "Fast factor", "Fast factor");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+ /*
+ prop= RNA_def_property(srna, "realtime", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "realtime", 1);
+ //RNA_def_property_range(prop, 0,1);
+ RNA_def_property_ui_text(prop, "realtime", "Realtime Updates");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+ */
+
+ prop= RNA_def_property(srna, "reset_on_frame", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "reset_on_frame");
+ RNA_def_property_range(prop, -99000, 99000);
+ RNA_def_property_ui_text(prop, "Reset on frame", "Start frame");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+
+ prop= RNA_def_property(srna, "do_use_reset", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", DO_USE_RESET);
+ RNA_def_property_ui_text(prop, "Use reset", "Whether or not to reset pose on first frame");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
+
+
+
+}
+
+
/* base struct for constraints */
void RNA_def_constraint(BlenderRNA *brna)
{
@@ -2136,6 +2238,7 @@
rna_def_constraint_damped_track(brna);
rna_def_constraint_spline_ik(brna);
rna_def_constraint_pivot(brna);
+ rna_def_constraint_spring(brna);
}
#endif

Event Timeline