| 
//
// 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;
}
 |