Index: release/scripts/startup/bl_ui/properties_object_constraint.py =================================================================== --- release/scripts/startup/bl_ui/properties_object_constraint.py (revision 55180) +++ release/scripts/startup/bl_ui/properties_object_constraint.py (working copy) @@ -837,7 +837,22 @@ def SCRIPT(self, context, layout, con): layout.label("Blender 2.6 doesn't support python constraints yet") + + 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 55180) +++ source/blender/blenkernel/intern/constraint.c (working copy) @@ -4091,9 +4091,345 @@ camerasolver_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, FALSE, 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 = BKE_pose_channel_find_name(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 */ + 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 */ +}; + + + /* ----------- Object Solver ------------- */ -static void objectsolver_new_data(void *cdata) +static void objectsolver_new_data (void *cdata) { bObjectSolverConstraint *data = (bObjectSolverConstraint *)cdata; @@ -4203,9 +4539,10 @@ constraintsTypeInfo[23] = &CTI_TRANSLIKE; /* Copy Transforms Constraint */ constraintsTypeInfo[24] = &CTI_SAMEVOL; /* Maintain Volume Constraint */ constraintsTypeInfo[25] = &CTI_PIVOT; /* Pivot Constraint */ - constraintsTypeInfo[26] = &CTI_FOLLOWTRACK; /* Follow Track Constraint */ - constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */ - constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */ + constraintsTypeInfo[26]= &CTI_FOLLOWTRACK; /* Follow Track Constraint */ + constraintsTypeInfo[27]= &CTI_CAMERASOLVER; /* Camera Solver Constraint */ + constraintsTypeInfo[28]= &CTI_OBJECTSOLVER; /* Object Solver Constraint */ + constraintsTypeInfo[29]= &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 55180) +++ source/blender/blenloader/intern/readfile.c (working copy) @@ -2680,6 +2680,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: { bKinematicConstraint *data = con->data; Index: source/blender/blenloader/intern/writefile.c =================================================================== --- source/blender/blenloader/intern/writefile.c (revision 55180) +++ source/blender/blenloader/intern/writefile.c (working copy) @@ -1273,6 +1273,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/makesdna/DNA_constraint_types.h =================================================================== --- source/blender/makesdna/DNA_constraint_types.h (revision 55180) +++ source/blender/makesdna/DNA_constraint_types.h (working copy) @@ -428,6 +428,32 @@ int flag, pad; } bCameraSolverConstraint; + +/* 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; + + /* Camera Solver constraints */ typedef struct bObjectSolverConstraint { struct MovieClip *clip; @@ -437,6 +463,7 @@ struct Object *camera; } bObjectSolverConstraint; + /* ------------------------------------------ */ /* bConstraint->type @@ -473,7 +500,7 @@ CONSTRAINT_TYPE_FOLLOWTRACK = 26, /* Follow Track Constraint */ CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */ CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */ - + CONSTRAINT_TYPE_SPRING, /* Simple spring dynamics */ /* NOTE: no constraints are allowed to be added after this */ NUM_CONSTRAINT_TYPES } eBConstraint_Types; @@ -792,11 +819,20 @@ CAMERASOLVER_ACTIVECLIP = (1<<0) } eCameraSolver_Flags; + +/* Spring Constraint -> flag */ +typedef enum eSpring_Flags { + DO_USE_RESET = (1<<0), +} eSpring_Flags; + + + /* ObjectSolver Constraint -> flag */ typedef enum eObjectSolver_Flags { OBJECTSOLVER_ACTIVECLIP = (1<<0) } eObjectSolver_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 55180) +++ source/blender/makesrna/intern/rna_constraint.c (working copy) @@ -80,8 +80,10 @@ {CONSTRAINT_TYPE_PIVOT, "PIVOT", ICON_CONSTRAINT_DATA, "Pivot", ""}, {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", ""}, - {0, NULL, 0, NULL, NULL} + {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[] = { @@ -191,6 +193,8 @@ return &RNA_CameraSolverConstraint; case CONSTRAINT_TYPE_OBJECTSOLVER: return &RNA_ObjectSolverConstraint; + case CONSTRAINT_TYPE_SPRING: + return &RNA_SpringConstraint; default: return &RNA_UnknownType; } @@ -2292,9 +2296,109 @@ prop = RNA_def_property(srna, "use_active_clip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAMERASOLVER_ACTIVECLIP); RNA_def_property_ui_text(prop, "Active Clip", "Use active clip defined in scene"); - 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_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"); + + + +} + + + static void rna_def_constraint_object_solver(BlenderRNA *brna) { StructRNA *srna; @@ -2450,6 +2554,7 @@ rna_def_constraint_follow_track(brna); rna_def_constraint_camera_solver(brna); rna_def_constraint_object_solver(brna); + rna_def_constraint_spring(brna); } -#endif +#endif \ No newline at end of file