A custom object implements a set of functions that are called from the Mops core now and then.
Some functions are mandatory, some that would implement point editing support or notification are not.
Initialization of a custom object is done by a function named
$name_Init(Tcl_Interp *interp)
.
Note, that $name
is the name of the shared object (the file
name!) that gets loaded, and that the first character of the function
name has to be upcase, regardless of the case of the file name.
This function is called once when the custom objects code gets loaded into The Mops.
What does the initialization do? First, it registers the custom object at the Mops core by calling:
mops_register_custom_objecttype().
If the registration fails, the function returns a nonzero value. You should really check this return value, and if it is nonzero call mops_error() with it and return TCL_ERROR. The parameters of the function are as follows:
Some new Tcl commands may be registered now by calling Tcl_CreateCommand(). Usually new commands need to be created to get or set object type specific properties.
Finally, it is time to load some Tcl code from Tcl files into the interpreter. This code implements the property GUIs of the object type specific properties (do not worry, this is easy).
The creation callback (of type mops_create_custom_cb) gets called on creation of your custom object.
You should allocate memory for your new object, and initialize it
properly. Argc and argv are the command line of the
crtOb custom $yourtype
command.
(The user may deliver additional arguments
to this command.) The last argument is a pointer to a pointer
where you have to put the address of your new custom
object into.
This callback is called, when an objects needs to be deleted. You should free all memory allocated for the custom object, the argument c points to one of your custom objects.
You should allocate a new object and copy the custom object pointed to by source to the new allocated memory, and finally return a pointer to the new memory in dest.
Draw your custom object as wireframe. You do not get a pointer to your custom object as parameter, but a pointer to a Mops object, which is two steps higher in the object hierarchy!
This is, because you may freely decide whether to use the standard attributes stored with every Mops object. These are for instance color, opacity and the transformations.
If you want to use them as most other Mops objects simply call mops_render_attributes().
See the example source for information on how to finally get to your custom object.
This callback is basically the same as the draww callback, but the user expects to get a much better represention of the object.
This callback is not mandatory, and needs just to be implemented if your object supports single point editing. If you want to do this, you should draw with points just the points of your object that may be modified by a single point editing action if this function gets called.
This callback is for exporting your object to a RIB. Just like the drawing callbacks you get a pointer to a Mops object and not to your custom object, so that you can choose whether to use the standard attributes and shaders.
Using mops_wrib_attributes() and mops_wrib_shaders() you can mimic the behaviour of most other Mops objects.
These callbacks are for writing and reading Mops scene files. As you can see in the example source, simple fprintf(), fscanf() calls are currently in use. Note, that you do not have to worry about your child objects, if there are any, they will be saved automagically.
The notification callback is for custom objects that rely on other objects to be childs of them (see sweep, extrude or revolve).
The notification callback is to inform you, that something below your custom object has changed, and you should probably adapt the custom object to the change. The Revolve object e.g. redoes the revolution.
This callback enables all the single point editing facilities (including the selection mechanism for single points) for your object.
With this callback you get an object and a point in the local space of that object. You are asked to search through your internal structures for points of yours that match the given coordinates. If there are such points you should build an array of pointers to your points as the following example does:
{ double *control = NULL, min_distance = mops_prefs.pick_epsilon, distance = 0.0; /* first, we clear the old array */ if(mops_point_edit_coords) free(mops_point_edit_coords); mops_point_edit_coords = NULL; /* now we scan our points for the given coordinates*/ control = array_of_your_points; for(i = 0; i < max_points; i++) { distance = MOPS_VLEN((objX - control[j]), (objY - control[j+1]), (objZ - control[j+2])); if(distance < min_distance) { pecoords = &(control[j]); min_distance = distance; } j += 3; } if(!pecoords) return MOPS_OK; /* are the points homogenous? */ mops_point_edit_coords_homogenous = MOPS_FALSE; /* now we create the new array */ if(!(mops_point_edit_coords = calloc(1,sizeof(double*)))) return MOPS_OUTOFMEM; mops_point_edit_coords_number = 1; mops_point_edit_coords[0] = pecoords; }
The code above does just handle the selection of a single point,
it is possible, however, to put an arbitrary number of points in the
array at once!
The following global variables are in use: mops_point_edit_coords
(the adress of the array), mops_point_edit_coords_number
(an integer that tells The Mops, how many
pointers are in mops_point_edit_coords
),
mops_point_edit_coords_homogenous (a flag that tells The Mops, wether the
points are homogenous or not; note, that there can only be points
of one type in the array).
Also note, that the Mops core will poke into the memory you pointed it to later. The core expects the points itself to be arrays of doubles:
double a_non_homogenous_point[3]; double a_homogenous_point[4];
In this callback you tell the Tcl core of The Mops, which properties your custom object has. You do this by filling some special global lists in the Tcl context using Tcl_SetVar() or similar Tcl functions.
This function will be called whenever a single custom object has been selected in the selection listbox.
The following lists in the Tcl context need to be updated for every property: propertyList, propertyProcList, propertyGetProcList, propertySetProcList and propertyDataList.
Each object has automagically the Transformations and the Attributes property assigned. You can change that by unsetting the lists prior to your changes (using Tcl_UnsetVar() e.g.).
There are functions prepared for you that add the right things for shader properties to the lists: mops_add_surfshader_property(); e.g.
The list propertyList holds the names of the Properties as they shall appear in the property listbox.
The list propertyProcList holds the names of the procedures to call for each property that builds the corresponding property GUI in the Arguments canvas. Unless the property GUI marks itself as static this procedure will be called on every apply.
The lists propertyGetProcList and propertySetProcList contain procedure calls that are used to transfer parameters from C to Tcl (Get) and from Tcl to C (Set, called on Apply). These functions need to be implemented as Tcl commands, see CSphere_set_attrprop_tcmd() and CSphere_get_attrprop_tcmd() for an example. Also note how these commands are created in the Init function of the csphere custom object.
Finally, the list propertyDataList contains the names of the arrays where each property holds its parameters.