Max API  8.2.0
OB3D QuickStart

The purpose of this chapter is to give a quick and high level overview of how to develop a simple Jitter OpenGL object which draws geometry within a named rendering context - we refer to such an object as an OB3D.

For this task, we will use the jit.gl.simple SDK example. More details such as how to make an OpenGL object which deals with resources such as display lists and textures, wishes to support matrix input/output, or needs greater access to OpenGL state will appear in the following chapter. This chapter assumes familiarity with Jitter's OpenGL object suite used from the Max patcher, as discussed in the Jitter Tutorial, and the preceding chapters on the Jitter object model and Max wrapper classes.

Defining the OB3D Jitter Class

Jitter OB3Ds typically are defined to have all or most of the common OB3D attributes and methods discussed in the Group-OB3D section of the Jitter HTML object reference. These include attributes and methods to set the rendering destination name, object name, color, lighting, texturing, modelview transform, depth buffering, polygon mode, and several other common tasks. These common attributes and methods are added by the call to the jit_ob3d_setup() function in your Jitter class definition, after calling jit_class_new, but typically prior to defining other methods and attributes. For an OB3D, Jitter needs to store additional information in your object. This information is stored in an opaque pointer in your object struct, typically named ob3d. The byte offset to your OB3D data pointer is passed into jit_ob3d_setup(). You can override any default attributes and methods added by jit_ob3d_setup() with the following flags:

#define JIT_OB3D_NO_ROTATION_SCALE 1 << 0
#define JIT_OB3D_NO_POLY_VARS 1 << 1
#define JIT_OB3D_NO_BLEND 1 << 2
#define JIT_OB3D_NO_TEXTURE 1 << 3
#define JIT_OB3D_NO_MATRIXOUTPUT 1 << 4
#define JIT_OB3D_AUTO_ONLY 1 << 5
#define JIT_OB3D_DOES_UI 1 << 6
#define JIT_OB3D_NO_DEPTH 1 << 7
#define JIT_OB3D_NO_ANTIALIAS 1 << 8
#define JIT_OB3D_NO_FOG 1 << 9
#define JIT_OB3D_NO_LIGHTING_MATERIAL 1 << 10
#define JIT_OB3D_HAS_LIGHTS 1 << 11
#define JIT_OB3D_HAS_CAMERA 1 << 12
#define JIT_OB3D_IS_RENDERER 1 << 13
#define JIT_OB3D_NO_COLOR 1 << 14

Aside from the attributes and methods added to your class by jit_ob3d_setup(), you need to define a private, untyped method bound to the symbol ob3d_draw. This method is where your object does all its drawing. It is called by the standard OB3D draw and drawraw methods. The OB3D draw method sets up all of the OpenGL state associated with the common OB3D attributes before calling your private ob3d_draw method. The drawraw method simply sets the context before calling your private ob3d_draw method. Because OB3Ds support being named for use within jit.gl.sketch*'s drawobject command, you must also add a private, untyped "register" method associated with the jit_object_register() function. Let's examine the *jit.gl.simple SDK project as an example:

t_jit_err jit_gl_simple_init(void)
{
long ob3d_flags = JIT_OB3D_NO_MATRIXOUTPUT; // no matrix output
void *ob3d;
_jit_gl_simple_class = jit_class_new("jit_gl_simple",
(method)jit_gl_simple_new, (method)jit_gl_simple_free,
sizeof(t_jit_gl_simple),0L);
// set up object extension for 3d object, customized with flags
ob3d = jit_ob3d_setup(_jit_gl_simple_class,
calcoffset(t_jit_gl_simple, ob3d),
ob3d_flags);
// define the OB3D draw method. called in automatic mode by
// jit.gl.render or otherwise through ob3d when banged. this
// method is A_CANT because our draw setup needs to happen
// in the ob3d beforehand to initialize OpenGL state
jit_class_addmethod(_jit_gl_simple_class,
(method)jit_gl_simple_draw, "ob3d_draw", A_CANT, 0L);
// define the dest_closing and dest_changed methods.
// these methods are called by jit.gl.render when the
// destination context closes or changes: for example, when
// the user moves the window from one monitor to another. Any
// resources your object keeps in the OpenGL machine
// (e.g. textures, display lists, vertex shaders, etc.)
// will need to be freed when closing, and rebuilt when it has
// changed. In this object, these functions do nothing, and
// could be omitted.
jit_class_addmethod(_jit_gl_simple_class,
(method)jit_gl_simple_dest_closing, "dest_closing", A_CANT, 0L);
jit_class_addmethod(_jit_gl_simple_class,
(method)jit_gl_simple_dest_changed, "dest_changed", A_CANT, 0L);
// must register for ob3d use
jit_class_addmethod(_jit_gl_simple_class,
(method)jit_object_register, "register", A_CANT, 0L);
jit_class_register(_jit_gl_simple_class);
return JIT_ERR_NONE;
}
@ A_CANT
cannot typecheck args
Definition: ext_mess.h:287
t_jit_err jit_class_register(void *c)
Registers class in the class registry.
Definition: jit.foundation.c:534
void * jit_class_new(C74_CONST char *name, method mnew, method mfree, long size,...)
Creates a new class with the name specified by the name argument.
Definition: jit.foundation.c:286
t_jit_err jit_class_addmethod(void *c, method m, const char *name,...)
Adds a named method to a class.
Definition: jit.foundation.c:331
BEGIN_USING_C_LINKAGE typedef void *(* method)(void *)
Function pointer type for generic methods.
Definition: ext_mess.h:25
@ JIT_OB3D_NO_MATRIXOUTPUT
ob3d flag
Definition: jit.gl.common.h:71
#define calcoffset(x, y)
Find byte offset of a named member of a struct, relative to the beginning of that struct.
Definition: ext_prefix.h:143
void * jit_object_register(void *x, t_symbol *s)
Registers an object in the named object registry.
Definition: jit.foundation.c:1115

The Jitter Class Constructor/Destructor

In your OB3D Jitter Class constructor, you need to pass in your rendering destination name as the first argument. You should call the jit_ob3d_new() function with your destination name argument to initialize the OB3D data pointer, associating it with your rendering destination. In your destructor, you need to free your OB3D data pointer with jit_ob3d_free(). The jit.gl.simple constructor and destructors are below as an example.

t_jit_gl_simple *jit_gl_simple_new(t_symbol *dest_name)
{
t_jit_gl_simple *x;
// make jit object
if (x = (t_jit_gl_simple *)jit_object_alloc(_jit_gl_simple_class))
{
// create and attach ob3d
jit_ob3d_new(x, dest_name);
}
else
{
x = NULL;
}
return x;
}
void jit_gl_simple_free(t_jit_gl_simple *x)
{
// free ob3d data
jit_ob3d_free(x);
}
The symbol.
Definition: ext_mess.h:102

The OB3D draw Method

Your OB3D draw method, bound to the ob3d_draw symbol, is where all of your drawing code takes place. It is called automatically when your associated jit.gl.render object receives a bang, if your automatic and enabled attributes are turned on, as they are by default. It is also called if your Max wrapper object receives a bang, or the draw or drawraw messages. With the exception of the drawraw message, all of the standard OB3D object state is setup prior to calling your ob3d_draw method, so you needn't setup things like the modelview transform, color, lighting properties, texture information, if your object doesn't have special needs. The following example from jit.gl.simple, just draws a simple quadrilateral.

t_jit_err jit_gl_simple_draw(t_jit_gl_simple *x)
{
t_jit_err result = JIT_ERR_NONE;
// draw our OpenGL geometry.
glBegin(GL_QUADS);
glVertex3f(-1,-1,0);
glVertex3f(-1,1,0);
glVertex3f(1,1,0);
glVertex3f(1,-1,0);
glEnd();
return result;
}

Since this example is meant only to show a minimal object which draws geometry with standard OpenGL calls, there is no texture information or vertex normals specified. However, all standard OpenGL calls should work within the ob3d_draw method. This example also doesn't show matrix output, as accomplished by jit_ob3d_draw_chunk(), which will be discussed in the following chapter on OB3D details.

Defining the OB3D Max Wrapper Class

For OB3Ds, the Max wrapper class has less extra work than for MOPs. In your Max wrapper class definition, you need only add a call to the max_ob3d_setup() function to add your standard drawing methods, and the max_jit_ob3d_assist() function as your assist method, unless you wish to define your own custom assist method. Everything else is similar to the standard technique of wrapping a Jitter Class demonstrated in the Max Wrapper Class chapter.

void ext_main(void *r)
{
void *classex, *jitclass;
// initialize Jitter class
jit_gl_simple_init();
// create Max class
setup((t_messlist **)&max_jit_gl_simple_class,
(method)max_jit_gl_simple_new, (method)max_jit_gl_simple_free,
(short)sizeof(t_max_jit_gl_simple), 0L, A_GIMME, 0);
// specify a byte offset to keep additional information about our object
classex = max_jit_classex_setup(calcoffset(t_max_jit_gl_simple, obex));
// look up Jitter class in the class registry
jitclass = jit_class_findbyname(gensym("jit_gl_simple"));
// wrap Jitter class with the standard methods for Jitter objects
max_jit_classex_standard_wrap(classex, jitclass, 0);
// use standard ob3d assist method
addmess((method)max_jit_ob3d_assist, "assist", A_CANT,0);
// add methods for 3d drawing
max_ob3d_setup();
}
@ A_GIMME
request that args be passed as an array, the routine will check the types itself.
Definition: ext_mess.h:286
void addmess(method f, char *s, short type,...)
Use addmess() to bind a function to a message other than the standard ones covered by addbang(),...
BEGIN_USING_C_LINKAGE void setup(t_messlist **ident, method makefun, method freefun, t_getbytes_size size, method menufun, short type,...)
Use the setup() function to initialize your class by informing Max of its size, the name of your func...
void ext_main(void *r)
ext_main() is the entry point for an extern to be loaded, which all externs must implement this share...
Definition: myob.c:35
void * jit_class_findbyname(t_symbol *classname)
Retrieves class pointer associated with name provided.
Definition: jit.foundation.c:621
void * max_jit_classex_setup(long oboffset)
Allocates and initializes special t_max_jit_classex data, used by the Max wrapper class.
Definition: jit.max.c:2042
void max_jit_classex_standard_wrap(void *mclass, void *jclass, long flags)
Adds standard Jitter methods, as well as public methods and attributes of the specified Jitter class.
Definition: jit.max.c:955
t_symbol * gensym(C74_CONST char *s)
Given a C-string, fetch the matching t_symbol pointer from the symbol table, generating the symbol if...
A list of symbols and their corresponding methods, complete with typechecking information.
Definition: ext_mess.h:157

The Max Class Constructor/Destructor

Your Max class' constructor should be similar to the standard Max wrapper constructor, but the differences worth noting are that you should pass your first normal argument, which is the rendering destination, on to your Jitter OB3D constructor, and create a second outlet for matrix output, attached to your object's OB3D data. For your destructor, there is nothing additional you need to do for OB3D. The jit.gl.simple Max class' constructor and destructor are provided as examples.

void *max_jit_gl_simple_new(t_symbol *s, long argc, t_atom *argv)
{
t_max_jit_gl_simple *x;
void *jit_ob;
long attrstart;
t_symbol *dest_name_sym = _jit_sym_nothing;
if (x = (t_max_jit_gl_simple *) max_jit_obex_new(
max_jit_gl_simple_class, gensym("jit_gl_simple")))
{
// get first normal arg, the destination name
attrstart = max_jit_attr_args_offset(argc,argv);
if (attrstart&&argv)
{
jit_atom_arg_getsym(&dest_name_sym, 0, attrstart, argv);
}
// instantiate Jitter object with dest_name arg
if (jit_ob = jit_object_new(
gensym("jit_gl_simple"), dest_name_sym))
{
// set internal jitter object instance
// add a general purpose outlet (rightmost)
// process attribute arguments
max_jit_attr_args(x, argc, argv);
// attach the jit object's ob3d to a new outlet
// this outlet is used in matrixoutput mode
max_jit_ob3d_attach(x, jit_ob, outlet_new(x, "jit_matrix"));
}
else
{
error("jit.gl.simple: could not allocate object");
x = NULL;
}
}
return (x);
}
void max_jit_gl_simple_free(t_max_jit_gl_simple *x)
{
// lookup our internal Jitter object instance and free
// free resources associated with our obex entry
}
long jit_atom_arg_getsym(t_symbol **c, long idx, long ac, t_atom *av)
Retrieves atom argument at index as symbol pointer if present.
Definition: jit.atom.c:236
void freeobject(void *op)
Release the memory used by a Max object.
void error(C74_CONST char *fmt,...)
Print an error to the Max window.
void * outlet_new(void *x, C74_CONST char *s)
Use outlet_new() to create an outlet that can send a specific non-standard message,...
JIT_EX_DATA t_symbol * _jit_sym_nothing
cached t_symbol
Definition: jit.symbols.h:16
void * max_jit_obex_jitob_get(void *x)
Retrieves the wrapped Jitter object from a Max wrapper object.
Definition: jit.max.c:2613
long max_jit_attr_args_offset(short ac, t_atom *av)
Determines argument offset to first attribute argument.
Definition: jit.max.c:392
void max_jit_obex_dumpout_set(void *x, void *outlet)
Sets the Max wrapper object's dump outlet's outlet pointer.
Definition: jit.max.c:2826
void max_jit_obex_jitob_set(void *x, void *jitob)
Sets the wrapped Jitter object for a Max wrapper object.
Definition: jit.max.c:2631
void max_jit_obex_free(void *x)
Frees additional resources for the Max wrapper object instance.
Definition: jit.max.c:2391
void max_jit_attr_args(void *x, short ac, t_atom *av)
Processes attribute arguments.
Definition: jit.max.c:408
void * max_jit_obex_new(void *mc, t_symbol *classname)
Allocates an initializes a new Max wrapper object instance.
Definition: jit.max.c:2339
t_jit_err jit_object_free(void *x)
Frees an object.
Definition: jit.foundation.c:936
An atom is a typed datum.
Definition: ext_mess.h:323
The structure for the head of any object which wants to have inlets or outlets, or support attributes...
Definition: ext_mess.h:191