-
Notifications
You must be signed in to change notification settings - Fork 9
Subclassing the View Object
The objects submitted to rendering/bounding/picking/writing loops are often groups which contain many other objects nested within. It is often useful to be able to intercept as it is just about to be rendered (or picked, etc.) in order to cull certain objects or modify their handling. This can be accomplished by defining a subclass of the view object.
The Geom Test sample uses a view subclass in order to display vertex or face normals of geometries. To accomplish this well enough for the example, it is only necessary to override one method, the one that submits a retained object to the renderer.
Every subclass must have a metahandler, a function that receives an integer indicating a kind of method, and returns a function pointer.
#!cpp
static TQ3XFunctionPointer
viewMetaHandler( TQ3XMethodType methodType )
{
TQ3XFunctionPointer theMethod = NULL;
switch (methodType)
{
case kQ3XMethodTypeViewSubmitRetainedRender:
theMethod = (TQ3XFunctionPointer) submitForRenderMethod;
break;
}
return theMethod;
}
If you need to override more methods, you would of course add more cases to the switch. The list of method types, and their corresponding function prototypes, can be found in the QuesaExtension.h public header.
Before we can create an instance of the subclass, we must register it. In the Geom Test example, we need several static variables:
#!cpp
static TQ3ObjectType sMyViewType = 0;
static TQ3XObjectClass sMyViewClass = NULL;
static TQ3XViewSubmitRetainedMethod sSubRenderStandardMethod = NULL;
The variable sMyViewType serves as a numerical identifier for the class, in functions such as Q3Object_GetLeafType and Q3Object_IsType. The object class pointer, sMyViewClass, will be needed in order to create an instance of the class. The function pointer sSubRenderStandardMethod is the view method before it was overridden.
Here is where we register the subclass:
#!cpp
if (sMyViewClass == NULL)
{
// Get a function pointer to the method of the standard view object
TQ3XObjectClass viewClass = Q3XObjectHierarchy_FindClassByType( kQ3ObjectTypeView );
sSubRenderStandardMethod = (TQ3XViewSubmitRetainedMethod)
Q3XObjectClass_GetMethod( viewClass,
kQ3XMethodTypeViewSubmitRetainedRender );
// Register the subclass
sMyViewClass = Q3XObjectHierarchy_RegisterClass( kQ3ObjectTypeView,
&sMyViewType, \"ToggleNormalView\", viewMetaHandler, NULL, 0, 0 );
}
The class name string, here shown as "ToggleNormalView", must be unique. If you want to ensure uniqueness, you may want to use a reverse DNS scheme.
Obviously, you must implement each override method mentioned in the metahandler. Here is the method from Geom Test:
#!cpp
static TQ3Status submitForRenderMethod( TQ3ViewObject inView, TQ3Object theObject )
{
TQ3Status theStatus = (*sSubRenderStandardMethod)( inView, theObject );
if (Q3Object_IsType( theObject, kQ3GeometryTypeTriMesh ))
{
if (gShowVertexNormals)
{
displayVertexNormals( theObject, inView );
}
if (gShowFaceNormals)
{
displayFaceNormals( theObject, inView );
}
}
return theStatus;
}
In this case, we call the standard submit method, then perform additional processing. In other cases, you might do something before calling the standard method, or you might skip the call to the standard method for certain objects.
In our example, we only handle TriMesh geometries. You might think that you would need to do more work for each other type of geometry, but not so. Every 2D geometry type (cone, ellipsoid, NURBS patch, etc.) is internally cached as one or more TriMesh, and it is these TriMeshes that are ultimately submitted to the renderer.
To create an instance of the view subclass, instead of calling Q3View_New(), you need to use the class pointer:
#!cpp
TQ3ViewObject theView = Q3XObjectHierarchy_NewObject( sMyViewClass, NULL );