|
Houdini Development Toolkit - Version 6.5
Side Effects Software Inc. 2004
|
Render Output
Output Drivers
Generation of custom Output Drivers can be very simple, or it can be
the most complex of the custom operators. In the simpler cases, the
Output Driver deals with simple data structures (i.e. saving geometry
from a specified SOP). In the most complex case, it by-passes the IFD
API and traverses all the data structures independently. The complex
case requires a full understanding of how all the operators in Houdini
are hooked up together. It also requires an intimate knowledge of how
parameters and channels are evaluated.
One of the simplest cases is to generate a new IFD_Output class (see
IFD_SimpleRib.C or
IFD_CustomRender.C).
In this case, there's actually no need to write a custom Output Driver
since the IFD_Output will appear automatically in the general
scene renderer. However, there may be cases when you need additional
information passed down to the IFD_Output class. This requires
writing a custom Output Driver.
In reality, there are really two types of Output Drivers, those which
use the IFD classes to do the rendering, and those which don't. The
following table shows which renderers use the IFD interface and which
are non-IFD renderers:
IFD Drivers |
Non-IFD Drivers |
Mantra3
RenderMan
Object Scene
|
Composite
Geometry
Network
|
Non-IFD Output Drivers
One of the tricky things about the Output Drivers is the way that they
specify parameter templates (PRM_Template). The base class (ROP_Node)
has several parameters which must be defined (and defined in the same
location for each and every Output Driver). This is accomplished
using the PRM_TemplatePair class to simulate inheritance of parameter
types.
When defining the parameters for the new Output Driver, simply build a
standard PRM_Template array which defines the custom parameters for
the Output Driver. For example, if we needed parameters for
resolution, the PRM_Template array might look something like:
static PRM_Template customTemplate[] = {
PRM_Template(PRM_INT, 2, &resolutionName, resolutionDefault),
PRM_Template()
};
Then, when the Output Driver operator is built, a PRM_TemplatePair
class must be constructed. This is done in the following code sample.
The order of construction is very very important. If the order is
incorrect, all applications will core dump when different Drivers are
evaluated.
OP_TemplatePair *
ROP_MyDriver::getCustomTemplatePair()
{
static OP_TemplatePair *finalTemplate = 0;
if (!finalTemplate)
{
OP_TemplatePair *custom;
custom = new OP_TemplatePair(customTemplate);
finalTemplate = new OP_TemplatePair(
ROP_Node::getROPbaseTemplate(), custom);
}
return finalTemplate;
}
Of course, the above method should be declared as a static method in
the class definition, since this code is called before any of the
nodes have been instantiated.
Unlike many other OPs, the ROP_Node class doesn't have a "cook"
method. Instead, there are three virtual methods which may be
defined by a sub-class:
Each of these methods should return 1 on success or 0 if there was a
failure. For example, the geometry renderer simply has the following
for these methods:
// don't care about parameters
int ROP_Geometry::startRender(int, float, float)
{
myObject = getObject(OBJNAME());
mySOP = getSOP(myObject, SOPNAME());
if (!mySOP)
{
addError(ROP_NO_OUTPUT, (const char *)getName());
return 0;
}
return 1;
}
int
ROP_Geometry::renderFrame(float time, UT_Interrupt *)
{
OP_Context context(time);
const GU_Detail *gdp = mySOP->getCookedGeo(context);
if (!gdp)
{
addError(...)
return 0;
}
if (gdp->save(GETSAVEPATH(time)) < 0)
{
addError(...)
return 0;
}
return 1;
}
int
ROP_Geometry::endRender()
{
return 1;
}
IFD Output Drivers
These drivers should be sub-classed from ROP_IFD.
As it was stated before, the simplest case of an IFD Output Driver is
simply to use the generalized Object Scene Output Driver. This
requires no custom code what so ever. However, in some cases,
additional parameters need to be passed down to the IFD classes.
Once again, when specifying parameters through the PRM_Template
mechanism, the IFD classes are quite tricky. There is a static method
in ROP_Node::getROPifdTemplate(PRM_Template *command). This method
must be used to generate your PRM_TemplatePair for the OP_Operator
construction. It is possible to add parameters by passing in a
template which contains the additional parameters. However, the
first parameter in the addional template must have the PRM_Name
specified by ROPcommand (in ROP_Shared.h). For example:
static PRM_Default dialogScriptDefault(PRM_DialogRenderers,
"custom_command -custom options");
static PRM_Template customTemplate[] = {
PRM_Template(PRM_COMMAND, 1, &ROPcommand, &dialogScriptDefault),
...
additional parameters here
...
PRM_Template()
};
static PRM_TemplatePair *
ROP_MyIFD_Driver::getTemplatePair()
{
return ROP_Node:::getROPifdTemplate(customTemplate);
}
Since this class is sub-classed from ROP_IFD, the actual
startRender, renderFrame, and endRender methods are actually
already written for you. However, there are still a few things that
need to be filled out for the class to work correctly.
Firstly, when the OP_Operator is being built, you should register your
renderer as a "special renderer". This prevents the IFD_Output from
showing up in the Object Scene Driver as well as in your custom
driver. So, the code might look like this:
(static) void
ROP_Custom::registerMe()
{
// The name passed in should be the name from the IFD_Entry
ROP_IFD::registerSpecialRenderer("custom");
}
void
newDriverOperator(OP_OperatorTable *table)
{
table->addOperator(new OP_Operator("custom",
"Custom Render",
ROP_Custom::myConstructor,
ROP_Custom::getTemplatePair(),
1,
1,
0));
ROP_Custom::registerMe();
}
Then the only other thing to do is to make sure that you get the
correct IFD_Entry for rendering. This can be guaranteed by
overriding the ::getRenderType method. For example:
IFD_Entry *
ROP_Custom::getRenderType(const char *)
{
IFD_Table *table = IFDgetTable();
IFD_Entry *entry;
for (entry = (IFD_Entry *)table->iterateInit(); entry;
entry = (IFD_Entry *)table->iterateNext(entry))
{
// Return once we've found the correct IFD_Entry.
if (!strcmp(entry->name, "custom"))
return entry;
}
return 0;
}
One last thing with a custom IFD renderer. The whole purpose was to
allow the passing of additional parameters. Unfortunately, since the
API is relatively closed, there's no easy mechanism for passing in
these parameter values. The only way is actually a kludge and
requires hacks on both the ROP_Node side and the IFD_Output side. The
only way of passing in additional information is to over-load one of
the existing parameters and stuff the additional information into it.
For example, if the IFD_Output doesn't allow rendering to a script,
simply fill in the script name with a string containing your
additional information (make sure to leave the output script toggle
off!). Or, if the IFD_Output doesn't use the device field, you can
specify the device (by over-riding the virtual ROP_IFD::getFrameDevice());
It's not pretty, but it does work.
Table of Contents
Operators |
Surface Operations |
Particle Operations |
Composite Operators |
Channel Operators
Material & Texture |
Objects |
Command and Expression |
Render Output |
Mantra Shaders |
Utility Classes |
Geometry Library |
Image Library |
Clip Library
Customizing UI |
Questions & Answers
Copyright © 2004 Side Effects Software Inc.
477 Richmond Street West, Toronto, Ontario, Canada M5V 3E7