Files Needed:

DNA_constraint_types.h, readfile.c, writefile.c, editconstraint.c,

constraint.c, buttons_obj.c, butspace.h

1. Creating the Data Structure for our Constraint

First we have to create the constraints data structure. For this we have to edit

the DNA_constraint_types.h file. Locate the section containing Struct

Definitions for the constraints. They will be near the top of the file. At the

bottom of this list create a new struct for your constraint. Some of the

Required/standard data members are:

 

<ccode>Object *tar; /*for the constraints target object if there is one*/</ccode>

<ccode>char subtarget[32]; /*for the subtarget of course if there is one*/</ccode>

Besides creating the Struct we also have to #define our bconstraint.type constant. These are listed under the /* constraint.type */ comment. Simply add yours to the bottom of the list and take the next available integer.

 

2. Writing our constraint to a file

We want to be able to write our constraint to a file too. After all if we can't do

that then we the constraint won't be very useful will it?

 

We need to edit writefile.c there is only one function to worry about here, write_constraints. Look again for the switch block and add a case for your

constraint. in our case we need to add the following line:

<ccode>writestruct(wd, DATA, "bMyConstraintConstraint", 1, con->data);</ccode>

That line must be there for every constraint regardless of type. This block can never be skipped. The portion in quotes is the name of your constraints struct.

3. Reading our Constraint from a file

We want to be able to read our constraint from a file once it has been saved. To do this we have to edit two functions in readfile.c

 

The first is expand_constraints(). Look for the switch block in this function

and and add a case for your constraint. You will need a minimum of two lines

in your constraint's case.

<ccode>bMyConstraintConstraint *data = (bMyConstraintConstraint*)curcon->data; </ccode>

Retrieves your constraints data from the struct we created earlier.

<ccode>expand_doit(fd, mainvar, data->tar);</ccode>

Is only used for the constraints DNA data members. you must add expand_doit() lines for every pointer to a DNA member that is in your struct.

 

This function can be skipped if your constraint has no pointers to other DNA structs in its data members for instance: zero target constraints.

 

Next we edit lib_link_constraints in this function we do much the

same thing as before. If we have no data members which are pointers to

other DNA then we can again skip this function. For those Constraints

which do ,however, we must add a case to the switch block in this function

for our constraint like so:

<ccode>case CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data;

data= ((bMyConstraintConstraint*)con->data);

data->tar = newlibadr(fd, id->lib, data->tar);

};

break;</ccode>

as before you add a newlibadr call for every pointer to a DNA member.

4. Handling our constraint's existence

We have to be able to handle our constraints presence too. We have to edit

two files to handle this.

A. constraint.c has three functions to edit

<tab>

The new_constraint_data Function initializes our constraint with any

default values and ceates the constraint. Locate the Switch block and add a

case for our constraint. In our constraints case we need to first declare our

data variable like so:

<ccode>bMyConstraint *data; </ccode>

Then we need to allocate memory for our constraint.

<ccode>data = MEM_callocN(sizeof(bMyConstraint ), "myConstraint"); </ccode>

and finally we add the default values for our struct.

<ccode>data->myvalue = 1; </ccode>

The get_constraint_target function can be skipped if our constraint has no

target. If it does have a target then we need to add a case for our constraint

in the switch block like so:

<ccode>case CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data;

data = (bMyConstraintConstraint*)con->data;

if (data->tar){

constraint_target_to_mat4(data->tar, data->subtarget,

mat, size, ctime);

valid=1;

}

else

Mat4One (mat);

}

break;</ccode>

just change the constraint and struct names to yours like before.

 

Next we need to find the relink_constraints function. This function

handles linking constraints to their targets. If your constraint has a a target

then modify the switch block to include a case for your constraint like so:

<ccode>case CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data;

data = con->data;

ID_NEW(data->tar);

}

break;

</ccode>

Next we need to find the copy_constraints function. This function

behaves exactly like it sounds. It copies constraints. Every constraint

needs to be in this function. In the switch block add a case for your

constraint like so:

<ccode>CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data;

con->data = MEM_dupallocN (con->data);

data = (bMyConstraintConstraint*) con->data;

}

break;</ccode>

As before just modify the struct name and constants to match your struct.

 

Now we finally get to the meat of our constraint. The function:

evaluate_constraint is as you might expect where the constraint actually

does whatever it is that it does. Add a case for your Constraint in the

switch block and put your nifty Constraint handling code there.

I highly recommend creating a function which gets called for the

constraint. Trying to write it all in that case statement would make for

messy code.</tab>

B. editConstraint.c has just one function to edit

<tab>

editconstraint.c This file has one function to edit: detect_constraint_loop

This function detects looping constraints. Such a condition occurs when

The target (data->tar) has a constraint which targets this constraints object.

This is a bad thing so we want to catch them in the interest of good code.

However this can't occur if your constraint has zero targets. You can omit this

step if your constraint has no targets. If it does have a target though then

you will need a case for your constraint here:

<ccode>case CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data = curcon->data;

if (!exist_object(data->tar)) data->tar = NULL;

if (add_constraint_element (data->tar, data->subtarget, owner, substring))

{

curcon->flag |= CONSTRAINT_DISABLE;

result = 1;

break;

}

if (detect_constraint_loop (data->tar, data->subtarget, disable))

{

curcon->flag |= CONSTRAINT_DISABLE;

result = 1;

break;

}

}

break;</ccode>

as before just change the code with your constraint and struct names. </tab>

5. Setting up the GUI for our constraint

buttons_object.c is the file which will contain our GUI code for the constraint. There are several function we have to modify here.

 

get_constraint_typestring sets our display string for the constraint. Just copy a case and modify the text to reflect your case.

 

get_constraint_col sets the color of our constraints block in the GUI. Again

copy a case and modify it to reflect your case.

 

draw_constraint is where all the action is for our GUI Drawing.

We need to locate the switch block containing all the constraint type cases,

and add a case for our constraint. Here are the minimum contents of our

constraints case:

<ccode>case CONSTRAINT_TYPE_MYCONSTRAINT:

{

bMyConstraintConstraint *data = con->data;

 

bArmature *arm; /* needed if our constraint has a target. */

height = 46;

 

BIF_set_color(curCol, COLORSHADE_GREY);

 

glRects(*xco, *yco-height, *xco+width, *yco);

 

uiEmboss((float)*xco, (float)*yco-height,

(float)*xco+width, (float)*yco, 1);

 

/* needed if our constraint has a target. */

uiDefIDPoinBut(block, test_obpoin_but,

B_CONSTRAINT_CHANGETARGET, "OB:",

*xco+((width/2)-48), *yco-20, 96, 18,

&data->tar, "Target Object");

 

/* needed if our constraint has a target. */

arm = get_armature(data->tar);

 

if (arm)

{ /* needed if our constraint has a target. */

 

but=uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET,

"BO:", *xco+((width/2)-48), *yco-40,96,18,

&data->subtarget, 0, 24, 0, 0, "Bone");

 

}

else

{

strcpy (data->subtarget, "");

}

break;</ccode>

thats all there is to it. We now have a minimal GUI for our Constraint. Of

course you will want to add more to it depending on what your Constraints

modifiable parameters are but you should be able to take it from here.

 

There is just one final detail to take care of. We need to be able to add our

constraint to an object before we can use our nifty new GUI. We have to add

our constraint to the add_constraintmenu function. We do this by adding the

following line in the appropriate position.

 

<ccode>uiDefBut(block, BUTM, B_CONSTRAINT_ADD_MyConstraint,

"Limit Rotation", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 0,

"tooltip text here"); </ccode>

We also have to add the B_CONSTRAINT_ADD_MyConstraint to the

appropriate Enum in butspace.h

 

Then we have to edit do_constraintbuts to add a case in the switch

block for our new option like so:

<ccode>case B_CONSTRAINT_ADD_MyConstraint:

{

bConstraint *con;

 

con = add_new_constraint(CONSTRAINT_TYPE_MYCONSTRAINT);

add_constraint_to_client(con);

 

test_scene_constraints();

allqueue (REDRAWVIEW3D, 0);

allqueue (REDRAWBUTSOBJECT, 0);

}

break;</ccode>