//
// Example rendering engine
//
// There is only ever *one* instance of this plug-in running so global variables
// are "ok".
#include <xsi_application.h>
#include <xsi_camera.h>
#include <xsi_context.h>
#include <xsi_decl.h>
#include <xsi_longarray.h>
#include <xsi_math.h>
#include <xsi_pluginregistrar.h>
#include <xsi_renderer.h>
#include <xsi_renderercontext.h>
#include <xsi_customproperty.h>
#include <xsi_ppglayout.h>
#include <xsi_pass.h>
#include <xsi_project.h>
#include <xsi_scene.h>
#include <xsi_passcontainer.h>
#include <xsi_primitive.h>
#include <xsi_x3dobject.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
using namespace XSI;
SICALLBACK XSILoadPlugin( PluginRegistrar& in_reg )
{
in_reg.PutAuthor(L"Softimage");
in_reg.PutName(L"Color Renderer");
in_reg.PutEmail(L"support@softimage.com");
in_reg.PutURL(L"http://www.softimage.com");
in_reg.PutVersion(1,0);
in_reg.RegisterProperty( L"Color Renderer Options" );
in_reg.RegisterRenderer( L"Color Renderer" );
return CStatus::OK;
}
////////////////////////////////////////////////////////////////////////////////
// Rendering Engine Section
/*! Abort handling.
*/
static bool g_bAborted;
HANDLE g_hAbort;
CRITICAL_SECTION g_barrierAbort;
void setAbort( bool in_bAbort )
{
::EnterCriticalSection( &g_barrierAbort );
g_bAborted = in_bAbort;
if( in_bAbort )
::SetEvent( g_hAbort );
else
::ResetEvent( g_hAbort );
::LeaveCriticalSection( &g_barrierAbort );
}
bool isAborted( )
{
bool bAbort;
::EnterCriticalSection( &g_barrierAbort );
bAbort= g_bAborted;
::LeaveCriticalSection( &g_barrierAbort );
return( bAbort );
}
/*! Initialization function for the renderer, called when the
plug-in is loaded.
This is where the rendering engine tells the environment what
process types it can perform (render, export archives etc.),
which property to use for its options and which output formats
it supports (and how those formats are defined).
The renderer can perform any other one-time initialization here also.
*/
SICALLBACK ColorRenderer_Init( CRef &in_ctxt )
{
Context ctxt( in_ctxt );
Renderer renderer = ctxt.GetSource();
// Tell the render manager what render processes we support.
CLongArray process;
process.Add( siRenderProcessRender );
renderer.PutProcessTypes( process );
// Specify the custom property to use for the renderer options
renderer.AddProperty( siRenderPropertyOptions, L"Color Renderer.Color Renderer Options" );
// Add the Softimage PIC format as an output format.
renderer.AddOutputImageFormat( L"Softimage PIC", L"pic" );
renderer.AddOutputImageFormatSubType( siRenderChannelColorType, L"RGBA", siImageBitDepthInteger8 );
// And some arbitrary image format.
renderer.AddOutputImageFormat( L"Foo Format", L"foo" );
renderer.AddOutputImageFormatSubType( siRenderChannelColorType, L"RGBA", siImageBitDepthInteger8 );
renderer.AddOutputImageFormatSubType( siRenderChannelColorType, L"RGBA", siImageBitDepthInteger16 );
renderer.AddOutputImageFormatSubType( siRenderChannelColorType, L"RGB", siImageBitDepthInteger8 );
renderer.AddOutputImageFormatSubType( siRenderChannelGrayscaleType, L"Gray", siImageBitDepthInteger16 );
// Create the handles for a thread-safe abort
g_bAborted = false;
::InitializeCriticalSection( &g_barrierAbort );
g_hAbort = ::CreateEvent( NULL, FALSE, FALSE, NULL );
return( CStatus::OK );
}
/*! This is called when the plug-in is unloaded.
The rendering engine should shut down completely and clean
out any global data. Any rendering jobs using this engine
have already been terminated at this point.
*/
SICALLBACK ColorRenderer_Term( CRef &in_ctxt )
{
::DeleteObject( g_hAbort );
::DeleteCriticalSection( &g_barrierAbort );
g_hAbort = NULL;
::ZeroMemory( &g_barrierAbort, sizeof( g_barrierAbort ) );
return( CStatus::OK );
}
class MyFragment : public RendererImageFragment
{
public:
MyFragment(
unsigned int in_offX, unsigned int in_offY, unsigned int in_width, unsigned int in_height,
double in_color[ 4 ] )
{
offX = in_offX;
offY = in_offY;
width = in_width;
height = in_height;
unsigned int r, g, b, a;
r = (unsigned int)( in_color[ 0 ] * 255.0 );
g = (unsigned int)( in_color[ 1 ] * 255.0 );
b = (unsigned int)( in_color[ 2 ] * 255.0 );
a = (unsigned int)( in_color[ 3 ] * 255.0 );
color = ( a << 24 ) | ( b << 16 ) | ( g << 8 ) | ( r );
}
unsigned int GetOffsetX( ) const { return( offX ); }
unsigned int GetOffsetY( ) const { return( offY ); }
unsigned int GetWidth( ) const { return( width ); }
unsigned int GetHeight( ) const { return( height ); }
bool GetScanlineRGBA( unsigned int in_uiRow, siImageBitDepth in_eBitDepth, unsigned char *out_pScanline ) const
{
unsigned int *pScanline = (unsigned int *)out_pScanline;
for( unsigned int i = 0; i < width; i++ )
pScanline[ i ] = color;
return( true );
}
private:
unsigned int offX, offY, width, height;
unsigned int color;
};
/*! This is the main function that gets called by the render manager
whenever the rendering engine is requested to perform a process
(render a frame, export an archive, etc.).
It is called with a specialized Context object, called RendererContext.
The RendererContext allows to retrieving the renderer options property,
getting framebuffer information and sending tile data back to the render
manager.
*/
SICALLBACK ColorRenderer_Process( CRef &in_ctxt )
{
setAbort( false );
RendererContext ctxt( in_ctxt );
Renderer renderer = ctxt.GetSource();
// The LockSceneData method *must* be called before accessing any potential
// scene data. This is to ensure that multiple threads do not concurrently access
// and/or modify the scene data. It is also important that the renderer does *not*
// modify any scene data at all. It can modify its own private data but nothing
// that is a part of the scene or the current application state, unless explicitly
// allowed.
if( renderer.LockSceneData() != CStatus::OK )
return( CStatus::Abort );
Primitive camera_prim = ctxt.GetAttribute( L"Camera" );
X3DObject camera_obj = camera_prim.GetOwners( )[ 0 ];
Camera camera = camera_obj;
CString camera_name = camera_obj.GetName();
const wchar_t *wcsCameraName = camera_name.GetWideString();
// Get the size of the image to render (in pixels). The origin is defiend as the
// bottom-left corner of the image.
unsigned int width, height;
width = (ULONG)ctxt.GetAttribute( L"ImageWidth" );
height = (ULONG)ctxt.GetAttribute( L"ImageHeight" );
// Check if there is a crop area defined. If the offset is 0,0 and the crop
// width/height is the same as the image width/height, then no cropping should take
// place. The crop window is always fully inside of the rendered image.
unsigned int cropOffsetX, cropOffsetY;
unsigned int cropWidth, cropHeight;
cropOffsetX = (ULONG)ctxt.GetAttribute( L"CropLeft" );
cropOffsetY = (ULONG)ctxt.GetAttribute( L"CropBottom" );
cropWidth = (ULONG)ctxt.GetAttribute( L"CropWidth" );
cropHeight = (ULONG)ctxt.GetAttribute( L"CropHeight" );
// Get our render property evaluated at the correct time. If rendering fields, any
// parameter that is animated, needs to be evaluated at the half-frame in between the
// current frame and the next frame after. Same goes potentially for motion blur, unless
// the renderer is incapable of interpolating the data, in which case it should use
// the current frame as a base.
CTime evalTime = ctxt.GetTime();
Property myProp = ctxt.GetRendererProperty( evalTime );
double color[ 4 ];
color[ 0 ] = myProp.GetParameterValue( L"Color_R", evalTime );
color[ 1 ] = myProp.GetParameterValue( L"Color_G", evalTime );
color[ 2 ] = myProp.GetParameterValue( L"Color_B", evalTime );
color[ 3 ] = myProp.GetParameterValue( L"Color_A", evalTime );
// Unlock the scene data *before* we start rendering and sending tile data back.
renderer.UnlockSceneData();
// Check after the scene data has been evaluted whether the abort flag is set.
if( isAborted() )
return( CStatus::Abort );
// Notify the renderer manager that a new frame is about to begin. This is necessary so
// that any recipient tile sink can re-adjust its own size to accommodate.
ctxt.NewFrame( width, height );
unsigned int tileSize = 32;
for( unsigned y = 0; y <= ( cropHeight / tileSize ); y++ )
{
for( unsigned x = 0; x <= ( cropWidth / tileSize ); x++ )
{
unsigned int ox, oy, sx, sy;
ox = x * tileSize;
oy = y * tileSize;
sx = tileSize;
sy = tileSize;
if( ( ox + tileSize ) > cropWidth )
sx = width - ox;
else
sx = tileSize;
if( ( oy + tileSize ) > cropHeight )
sy = height - oy;
else
sy = tileSize;
MyFragment fragment(
ox + cropOffsetX, oy + cropOffsetY, sx, sy, color );
// Send back a new tile.
ctxt.NewFragment( fragment );
DWORD dwResult = ::WaitForSingleObject( g_hAbort, 40 );
if( dwResult != WAIT_TIMEOUT )
return( CStatus::Abort );
}
}
return( CStatus::OK );
}
/*! Called by the render manager when the renderer should do a full
cleanup of any data that got created by the Process function.
This is usually called when the current scene is being destroyed,
or if the specific render process (region, pass render, export)
requests that data be cleaned up after the process has completed.
*/
SICALLBACK ColorRenderer_Cleanup( CRef &in_ctxt )
{
Context ctxt( in_ctxt );
Renderer renderer = ctxt.GetSource();
return( CStatus::OK );
}
/*! Called when the render needs to be aborted. The function should
trigger an abort and return as quickly as possible. It should
*not* refer to any scene data and not perform any processing
besides triggering the abort. It is up to the Process function
to ensure a clean abort is done upon the receipt of an abort
signal.
*/
SICALLBACK ColorRenderer_Abort( CRef &in_ctxt )
{
Context ctxt( in_ctxt );
Renderer renderer = ctxt.GetSource();
setAbort( true );
return( CStatus::OK );
}
/*! This function serves two purposes: To return the current "quality"
level of the render options and to set a preset for the given
"quality" level. The calculated quality level should be simply
the value that corresponds to the closest match to a level
preset.
\note This mechanism might be aborted shortly and replaced by
something else that gets handled by Softimage directly.
*/
SICALLBACK ColorRenderer_Quality( CRef &in_ctxt )
{
Context ctxt( in_ctxt );
Renderer renderer = ctxt.GetSource();
CValue quality = ctxt.GetAttribute( L"Quality" );
Property prop = ctxt.GetAttribute( L"Property" );
static const double levels[ 5 ][ 4 ] = {
{ 1.0, 0.0, 0.0, 1.0 },
{ 0.0, 1.0, 0.0, 1.0 },
{ 1.0, 0.0, 1.0, 1.0 },
{ 1.0, 0.5, 0.5, 1.0 },
{ 0.7, 0.4, 0.3, 0.5 },
};
if( quality.IsEmpty() )
{
double color[ 4 ];
int closest = -1;
double maxclose = DBL_MAX;
color[ 0 ] = prop.GetParameterValue( L"Color_R", CTime() );
color[ 1 ] = prop.GetParameterValue( L"Color_G", CTime() );
color[ 2 ] = prop.GetParameterValue( L"Color_B", CTime() );
color[ 3 ] = prop.GetParameterValue( L"Color_A", CTime() );
// We're being asked for the quality value (0-4).
// Find the closest color match.
for( int i = 0; i < 5; i++ )
{
double close;
double dist = 0.0;
for( int j = 0; j < 4; j++ )
{
dist += ( color[ j ] - levels[ i ][ j ] ) * ( color[ j ] - levels[ i ][ j ] );
}
close = sqrt( dist );
if( close < maxclose )
{
maxclose = close;
closest = i;
}
}
ctxt.PutAttribute( L"Quality", closest );
}
else
{
// Set a quality value based on the five levels (0-4).
prop.PutParameterValue( L"Color_R", levels[ (ULONG)quality ][ 0 ] );
prop.PutParameterValue( L"Color_G", levels[ (ULONG)quality ][ 1 ] );
prop.PutParameterValue( L"Color_B", levels[ (ULONG)quality ][ 2 ] );
prop.PutParameterValue( L"Color_A", levels[ (ULONG)quality ][ 3 ] );
}
return( CStatus::OK );
}
////////////////////////////////////////////////////////////////////////////////
// Renderer Options Property
SICALLBACK ColorRendererOptions_Define( CRef& in_ctxt )
{
Context ctxt( in_ctxt );
CustomProperty oCustomProperty;
Parameter oParam;
oCustomProperty = ctxt.GetSource();
oCustomProperty.AddParameter(L"Color_R",CValue::siDouble,siPersistable,L"",L"",0l,0l,1l,0l,1l,oParam);
oCustomProperty.AddParameter(L"Color_G",CValue::siDouble,siPersistable,L"",L"",0l,0l,1l,0l,1l,oParam);
oCustomProperty.AddParameter(L"Color_B",CValue::siDouble,siPersistable,L"",L"",0l,0l,1l,0l,1l,oParam);
oCustomProperty.AddParameter(L"Color_A",CValue::siDouble,siPersistable,L"",L"",0l,0l,1l,0l,1l,oParam);
return CStatus::OK;
}
SICALLBACK ColorRendererOptions_DefineLayout( CRef& in_ctxt )
{
Context ctxt( in_ctxt );
PPGLayout oLayout;
PPGItem oItem;
oLayout = ctxt.GetSource();
oLayout.Clear();
oLayout.AddColor(L"Color_R",L"Color",true);
return CStatus::OK;
}
|