// Copyright 2019 Diffuse Logic, all rights reserved.

#include "bella_scene.h"
#include "bella_nodeapi.h"
#include "api.h"
#include "../dl_core/dl_vector.h"
#include "../dl_core/dl_path.h"
#include "../dl_core/dl_fs.h"
#include "../dl_core/dl_os.h"
#include "../dl_core/dl_file.h"
#include <thread>
#include <mutex>

using namespace dl::bella_sdk::interfaces;

//=================================================================================================
// extern functions.
// 
// If you do not need to create scenes, and will only receive interfaces pointers (e.g. INode*)
// from a running implementation (e.g. you are writing a node implementation that will be loaded
// into bella or a plugin) then you will not try to link the functions below, and your bella Scene,
// Node, etc will simply be wrappers around those interface pointers.
//=================================================================================================

#define HAVE_BELLA_SDK_LIB 0

#if HAVE_BELLA_SDK_LIB

const dl::ds::Buffer&       DL_STDCALL impl_previewBsz();
dl::StringVector            DL_STDCALL impl_bellaSvgNames();
dl::String                  DL_STDCALL impl_findBellaSvg( dl::String name );
ISvg*                       DL_STDCALL impl_renderSvg( dl::String name, dl::Int32 xres, dl::Int32 yres, dl::UInt32 bg = 0x00000000, dl::Bool bgr = false );
DL_C_EXPORT bool            DL_STDCALL impl_bellaCoreNodesLinked() { return dl::os::dlSym( "bellaCoreNodesLinked" ) != nullptr; }

extern "C" IScene*          DL_STDCALL impl_createScene();
extern "C" IPbrTextureSet*  DL_STDCALL impl_createTextureSet();
extern "C" ISvg*            DL_STDCALL impl_createSvg();
extern "C" dl::Int          DL_STDCALL impl_sceneInstanceCount();
extern "C" dl::Int          DL_STDCALL impl_nodeInstanceCount();
extern "C" dl::Int          DL_STDCALL impl_inputInstanceCount();
extern "C" dl::Int          DL_STDCALL impl_outputInstanceCount();

#else

const dl::ds::Buffer&       DL_STDCALL impl_previewBsz() { static dl::ds::Buffer s_buf; return s_buf; }
dl::StringVector            DL_STDCALL impl_bellaSvgNames() { return dl::StringVector(); }
dl::String                  DL_STDCALL impl_findBellaSvg( dl::String /*name*/ ) { return dl::String(); }
ISvg*                       DL_STDCALL impl_renderSvg( dl::String /*name*/, dl::Int32 /*xres*/, dl::Int32 /*yres*/, dl::UInt32 bg = 0x00000000, dl::Bool bgr = false ) { DL_UNUSED( bg ); DL_UNUSED( bgr ); return nullptr; }
DL_C_EXPORT bool            DL_STDCALL impl_bellaCoreNodesLinked() { return false; }

extern "C" IScene*          DL_STDCALL impl_createScene() { return nullptr; }
extern "C" IPbrTextureSet*  DL_STDCALL impl_createTextureSet() { return nullptr; }
extern "C" ISvg*            DL_STDCALL impl_createSvg() { return nullptr; }
extern "C" dl::Int          DL_STDCALL impl_sceneInstanceCount() { return 0; }
extern "C" dl::Int          DL_STDCALL impl_nodeInstanceCount() { return 0; }
extern "C" dl::Int          DL_STDCALL impl_inputInstanceCount() { return 0; }
extern "C" dl::Int          DL_STDCALL impl_outputInstanceCount() { return 0; }

namespace dl {
namespace bella_sdk {

Vec2                        fitFilmToResolution( FilmFit, Vec2, Vec2, Real ) { return Vec2::zero; }
Bool                        isFilmFitHorizontal( FilmFit, Vec2, Vec2, Real ) { return false; }
dl::Region                  constrainRegion( Vec2u, Vec2i, Vec2u ) { return dl::Region(); }
dl::Region                  constrainRegion( Vec2, Vec2, Vec2 ) { return dl::Region(); }
Vec2u                       renderedResolution( Vec2u, Bool ) { return Vec2u::zero; }
Mat4                        panCamera( Path, Vec2, Bool ) { return Mat4::identity; }
Mat4                        orbitCamera( Path, Vec2 ) { return Mat4::identity; }
Mat4                        zoomCamera( Path, Vec2, Bool ) { return Mat4::identity; }
Mat4                        zoomExtents( Path, Vec3, Real ) { return Mat4::identity; }
UInt                        parseMonth( String ) { return 0; }
Real                        computeTimeOfDay( Real, Real, Real, Real, Bool ) { return 0.0; }
UInt                        computeDayOfYear( Int, Int, Int ) { return 0; }
UInt                        computeJulianDay( Int, Int, Int ) { return 0; }
Real                        computeJulianDate( Int, Int, Int, Int, Int, Real, Real, Bool ) { return 0.0; }
Vec2                        computeSunAngles( Real, Real, Real ) { return Vec2::zero; }
Vec2                        computeSunAngles( Node, Node ) { return Vec2::zero; }
Mat3                        computeSunDirection( Real, Real, Real ) { return Mat3::identity; }
Mat3                        computeSunDirection( Node, Node ) { return Mat3::identity; }
Obb                         computeObb( BufRef, Int ) { return Obb(); }

} // bella_sdk
} // dl

#endif

#define IMPL_CALL(CALL,D)       impl() ? impl()->CALL : D
#define IMPL_FCALL(TYPE,CALL,D) impl() ? DL_DECONST(TYPE,impl())->CALL : D
#define IMPL_VOID(CALL)         if ( impl() ) impl()->CALL
#define IMPL_INFO_CALL(CALL,D)  impl() && impl()->info() ? impl()->info()->CALL : D
#define IMPL_GET(T,D)           T t; return ( impl() && impl()->get( t, eval, ctx) ) ? T( t ) : D
#define IMPL_EVAL(T,D)          T t; return ( impl() && impl()->eval( t, ctx ) ) ? T( t ) : D
#define IMPL_SET(T,...)         return impl() && impl()->set##T( __VA_ARGS__ )

namespace dl {
namespace bella_sdk {

//=================================================================================================
// Vector wrapper generator for iterator interfaces.
//=================================================================================================

template<typename Vec, typename It> static inline Vec toVector( It* it )
{
    Vec ret;
    if ( !it )
        return ret;

    DeRefScope<It> iterator( it );
    for ( auto i = iterator->first(); i; i = iterator->next() )
        ret.push_back( i );

    return ret;
}

//=================================================================================================
// Svg.
//=================================================================================================

Svg::Svg() : RefersTo( impl_createSvg() ) { }

Bool            Svg::render( Int32 xres, Int32 yres, String svg, UInt32 bg, Bool bgr ) { return IMPL_CALL( render( xres, yres, svg, bg, bgr ), false ); }
void            Svg::clear()                                                        { IMPL_VOID( clear() ); }

String          Svg::svg() const                                                    { return IMPL_CALL( svg(), String()); }
Bool            Svg::ok() const                                                     { return IMPL_CALL( ok(), false ); }
String          Svg::status() const                                                 { return IMPL_CALL( status(), String()); }

Int32           Svg::xres() const                                                   { return IMPL_CALL( xres(), -1 ); }
Int32           Svg::yres() const                                                   { return IMPL_CALL( yres(), -1 ); }
BufRef          Svg::buf() const                                                    { return IMPL_CALL( buf(), BufRef() ); }
UInt32          Svg::bg() const                                                     { return IMPL_CALL( bg(), 0x00000000 ); }
Bool            Svg::bgr() const                                                    { return IMPL_CALL( bgr(), false ); }

Bool            Svg::swapRB()                                                       { return IMPL_CALL( swapRB(), false ); }
Rgba8           Svg::getPixel( Int32 x, Int32 y ) const                             { return IMPL_CALL( getPixel( x, y ), Rgba8::make( 0, 0, 0, 0 ) ); }
Bool            Svg::write( String path ) const                                     { return IMPL_CALL( write( path ), false ); }

//=================================================================================================
// EnumItem.
//=================================================================================================

Int             EnumItem::index() const                                             { return IMPL_CALL( index(), -1 ); }
Bool            EnumItem::isValid() const                                           { return IMPL_CALL( isValid(), false ); }
Int             EnumItem::intValue() const                                          { return IMPL_CALL( intValue(), ( Int )-1 ); }
Real            EnumItem::realValue() const                                         { return IMPL_CALL( realValue(), -1.0 ); }
String          EnumItem::strValue() const                                          { return IMPL_CALL( strValue(), String() ); }
String          EnumItem::label( String lang ) const                                { return IMPL_CALL( label( lang ), String() ); }
String          EnumItem::help( String lang ) const                                 { return IMPL_CALL( help( lang ), String() ); }

//=================================================================================================
// Input.
//=================================================================================================

String          Input::name() const                                                 { return IMPL_CALL( name(), String() ); }
AttrType        Input::type() const                                                 { return IMPL_CALL( type(), AttrType_Null ); }
String          Input::typeString() const                                           { return IMPL_CALL( typeString(), "Null" ); }
String          Input::desc() const                                                 { return IMPL_CALL( desc(), "" ); }

Int             Input::instanceCount()                                              { return impl_inputInstanceCount(); }
UInt64          Input::hash( StringVector excludes /*= { }*/ ) const                { return IMPL_CALL( hash( excludes ), 0 ); }

Input           Input::meta( String name, AttrType type )                           { return IMPL_CALL( meta( name, type ), nullptr ); }
Bool            Input::isMeta() const                                               { return IMPL_CALL( isMeta(), false ); }
Node            Input::node() const                                                 { return IMPL_CALL( node(), nullptr ); }
String          Input::path() const                                                 { return IMPL_CALL( path(), String() ); }

UInt            Input::index() const                                                { if ( impl() ) { return impl()->index(); } logError( "index() called on orphan Input, returning 0" ); return 0; }
Input           Input::parent() const                                               { return IMPL_CALL( parent(), nullptr ); }

AttrType        Input::arrayType() const                                            { return IMPL_CALL( arrayType(), AttrType_Null ); }
DataType        Input::bufferType() const                                           { return IMPL_CALL( bufferType(), DataType_Null ); }

Bool            Input::canConnect( Output output ) const                            { return IMPL_CALL( canConnect( output.impl() ), false ); }
Bool            Input::connect( Output output )                                     { return IMPL_CALL( connect( output.impl() ), false ); }
void            Input::disconnect()                                                 { IMPL_VOID( disconnect() ); }
void            Input::operator|=( Output output )                                  { IMPL_VOID( connect( output.impl() ) ); }

Bool            Input::connected() const                                            { return IMPL_CALL( connected(), false ); }
Output          Input::output() const                                               { return IMPL_CALL( output(), nullptr ); }

Bool            Input::canDisableOutput() const                                     { return IMPL_CALL( canDisableOutput(), false ); }
Bool            Input::outputDisabled() const                                       { return IMPL_CALL( outputDisabled(), false ); }
Bool            Input::disableOutput( Bool disable )                                { return IMPL_CALL( disableOutput( disable ), false ); }
Output          Input::disabledOutput() const                                       { return IMPL_CALL( disabledOutput(), nullptr ); }

Bool            Input::isDisabledOutputMeta() const                                 { return IMPL_CALL( isDisabledOutputMeta(), false ); }
Input           Input::disabledInput() const                                        { return IMPL_CALL( disabledInput(), nullptr ); }

Input           Input::createInput( String name, AttrType type )                    { return IMPL_CALL( createInput( name, type ), nullptr ); }
Input           Input::createArray( String name, AttrType type )                    { return IMPL_CALL( createArray( name, type ), nullptr ); }
Input           Input::appendElement()                                              { return IMPL_CALL( appendElement(), nullptr ); }
Bool            Input::containsElement( Input input ) const                         { return IMPL_CALL( containsElement( input.impl() ), false ); }

Bool            Input::hasInput( UInt index, AttrType type ) const                  { return IMPL_CALL( hasInput( index, type ), false ); }
Bool            Input::hasInput( String name, AttrType type ) const                 { return IMPL_CALL( hasInput( name, type ), false ); }

UInt            Input::inputCount() const                                           { return IMPL_CALL( inputCount(), 0 ); }
Input           Input::input( String name ) const                                   { return IMPL_FCALL( IInput*, input( name ), nullptr ); }
Input           Input::input( String name )                                         { return IMPL_CALL( input( name ), nullptr ); }
Input           Input::input( UInt index ) const                                    { return IMPL_CALL( input( index ), nullptr ); }
Input           Input::input( UInt index )                                          { return IMPL_CALL( input( index ), nullptr ); }

Input           Input::operator[]( String name ) const                              { return IMPL_FCALL( IInput*, input( name ), nullptr ); }
Input           Input::operator[]( String name )                                    { return IMPL_CALL( input( name ), nullptr ); }
Input           Input::operator[]( UInt index ) const                               { return IMPL_CALL( input( index ), nullptr ); }
Input           Input::operator[]( UInt index )                                     { return IMPL_CALL( input( index ), nullptr ); }

Bool            Input::removeInput( UInt index )                                    { return IMPL_CALL( removeInput( index ), false ); }
Bool            Input::removeInput( String name )                                   { return IMPL_CALL( removeInput( name ), false ); }
Bool            Input::removeLastInput()                                            { return IMPL_CALL( removeLastInput(), false ); }
Bool            Input::clearInputs()                                                { return IMPL_CALL( clearInputs(), false ); }

Bool            Input::isBool() const                                               { return IMPL_CALL( isBool(), false ); }
Bool            Input::isInt() const                                                { return IMPL_CALL( isInt(), false ); }
Bool            Input::isUInt() const                                               { return IMPL_CALL( isUInt(), false ); }
Bool            Input::isReal() const                                               { return IMPL_CALL( isReal(), false ); }
Bool            Input::isVec2() const                                               { return IMPL_CALL( isVec2(), false ); }
Bool            Input::isVec3() const                                               { return IMPL_CALL( isVec3(), false ); }
Bool            Input::isVec4() const                                               { return IMPL_CALL( isVec4(), false ); }
Bool            Input::isPos2() const                                               { return IMPL_CALL( isPos2(), false ); }
Bool            Input::isPos3() const                                               { return IMPL_CALL( isPos3(), false ); }
Bool            Input::isQuat() const                                               { return IMPL_CALL( isQuat(), false ); }
Bool            Input::isRgba() const                                               { return IMPL_CALL( isRgba(), false ); }
Bool            Input::isMat3() const                                               { return IMPL_CALL( isMat3(), false ); }
Bool            Input::isMat4() const                                               { return IMPL_CALL( isMat4(), false ); }
Bool            Input::isString() const                                             { return IMPL_CALL( isString(), false ); }
Bool            Input::isNode() const                                               { return IMPL_CALL( isNode(), false ); }
Bool            Input::isBuffer( DataType ofType ) const                            { return IMPL_CALL( isBuffer( ofType ), false ); }
Bool            Input::isArray( AttrType ofType ) const                             { return IMPL_CALL( isArray( ofType ), false ); }
Bool            Input::isArrayElement( AttrType ofType ) const                      { return IMPL_CALL( isArrayElement( ofType ), false ); }
Bool            Input::isObject() const                                             { return IMPL_CALL( isObject(), false ); }
Bool            Input::isObjectArray() const                                        { return IMPL_CALL( isObjectArray(), false ); }

Bool            Input::asBool( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Bool, false ); }
Int             Input::asInt( Bool eval, EvalCtx* ctx ) const                       { IMPL_GET( Int, 0 ); }
UInt            Input::asUInt( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( UInt, 0 ); }
Real            Input::asReal( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Real, 0.0 ); }
Vec2            Input::asVec2( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Vec2, Vec2::zero ); }
Vec3            Input::asVec3( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Vec3, Vec3::zero ); }
Vec4            Input::asVec4( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Vec4, Vec4::zero ); }
Pos2            Input::asPos2( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Pos2, Pos2::zero ); }
Pos3            Input::asPos3( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Pos3, Pos3::zero ); }
Quat            Input::asQuat( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Quat, Quat::identity ); }
Rgba            Input::asRgba( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Rgba, Rgba::black ); }
Mat3            Input::asMat3( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Mat3, Mat3::identity ); }
Mat4            Input::asMat4( Bool eval, EvalCtx* ctx ) const                      { IMPL_GET( Mat4, Mat4::identity ); }
String          Input::asString( Bool eval, EvalCtx* ctx ) const                    { IMPL_GET( String, String() ); }
BufRef          Input::asBuffer( Bool eval, EvalCtx* ctx ) const                    { IMPL_GET( BufRef, BufRef() ); }
Node            Input::asNode( Bool eval, EvalCtx* ctx ) const                      { INode* t; return impl() && impl()->get( t, eval, ctx ) ? t : nullptr; }

String          Input::valueToString() const                                        { return IMPL_CALL( valueToString(), String() ); }
String          Input::defaultToString() const                                      { return IMPL_CALL( defaultToString(), String() ); }

Bool            Input::set( Input input )                                           { return IMPL_CALL( set( input.impl() ), false ); }
Bool            Input::set( Bool value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Int value )                                             { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( UInt value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Real value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Vec2 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Vec3 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Vec4 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Pos2 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Pos3 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Quat value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Rgba value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Mat3 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Mat4 value )                                            { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( String value )                                          { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( const char* value )                                     { return IMPL_CALL( set( value ), false ); }
Bool            Input::set( Node value )                                            { return IMPL_CALL( set( value.impl() ), false ); }
Bool            Input::set( BufRef buffer )                                         { return IMPL_CALL( set( buffer ), false ); }

Bool            Input::setBool( Bool value )                                        { IMPL_SET( Bool, value ); }
Bool            Input::setInt( Int value )                                          { IMPL_SET( Int, value ); }
Bool            Input::setUInt( UInt value )                                        { IMPL_SET( UInt, value ); }
Bool            Input::setReal( Real value )                                        { IMPL_SET( Real, value ); }
Bool            Input::setVec2( Vec2 value )                                        { IMPL_SET( Vec2, value ); }
Bool            Input::setVec3( Vec3 value )                                        { IMPL_SET( Vec3, value ); }
Bool            Input::setVec4( Vec4 value )                                        { IMPL_SET( Vec4, value ); }
Bool            Input::setPos2( Pos2 value )                                        { IMPL_SET( Pos2, value ); }
Bool            Input::setPos3( Pos3 value )                                        { IMPL_SET( Pos3, value ); }
Bool            Input::setQuat( Quat value )                                        { IMPL_SET( Quat, value ); }
Bool            Input::setRgba( Rgba value )                                        { IMPL_SET( Rgba, value ); }
Bool            Input::setMat3( Mat3 value )                                        { IMPL_SET( Mat3, value ); }
Bool            Input::setMat4( Mat4 value )                                        { IMPL_SET( Mat4, value ); }
Bool            Input::setString( String value )                                    { IMPL_SET( String, value ); }
Bool            Input::setString( const char* value )                               { IMPL_SET( String, value ); }
Bool            Input::setNode( Node value )                                        { IMPL_SET( Node, value.impl() ); }
Bool            Input::setBuffer( BufRef value )                                    { IMPL_SET( Buffer, value ); }

void            Input::operator=( Bool value )                                      { setBool( value ); }
void            Input::operator=( Int  value )                                      { setInt( value ); }
void            Input::operator=( UInt value )                                      { setUInt( value ); }
void            Input::operator=( Real value )                                      { setReal( value ); }
void            Input::operator=( Vec2 value )                                      { setVec2( value ); }
void            Input::operator=( Vec3 value )                                      { setVec3( value ); }
void            Input::operator=( Vec4 value )                                      { setVec4( value ); }
void            Input::operator=( Pos2 value )                                      { setPos2( value ); }
void            Input::operator=( Pos3 value )                                      { setPos3( value ); }
void            Input::operator=( Quat value )                                      { setQuat( value ); }
void            Input::operator=( Rgba value )                                      { setRgba( value ); }
void            Input::operator=( Mat3 value )                                      { setMat3( value ); }
void            Input::operator=( Mat4 value )                                      { setMat4( value ); }
void            Input::operator=( String value )                                    { setString( value ); }
void            Input::operator=( const char* value )                               { setString( value ); }
void            Input::operator=( Node value )                                      { setNode( value ); }
void            Input::operator=( BufRef value )                                    { setBuffer( value ); }

Bool            Input::accepts( String type ) const                                 { return IMPL_CALL( accepts( type ), false ); }
Bool            Input::accepts( Node node ) const                                   { return IMPL_CALL( accepts( node.impl() ), false ); }

StringVector    Input::accepted() const                                             { return IMPL_CALL( accepted(), StringVector() ); }
Bool            Input::setAccepted( StringVector accepted )                         { return IMPL_CALL( setAccepted( accepted ), false ); }

String          Input::label( String lang ) const                                   { return IMPL_CALL( label( lang ), name() ); }
Bool            Input::setLabel( String label, String lang )                        { return IMPL_CALL( setLabel( label, lang ), false ); }

String          Input::help( String lang ) const                                    { return IMPL_CALL( help( lang ), "(no help available)" ); }
Bool            Input::setHelp( String help, String lang )                          { return IMPL_CALL( setHelp( help, lang ), false ); }

UInt            Input::precision() const                                            { return IMPL_INFO_CALL( precision(), 3 ); }
Bool            Input::setPrecision( UInt precision )                               { return IMPL_INFO_CALL( setPrecision( precision ), false ); }

Real            Input::step() const                                                 { return IMPL_INFO_CALL( step(), 1.0 ); }
Bool            Input::setStep( Real step )                                         { return IMPL_INFO_CALL( setStep( step ), false ); }

Bool            Input::realtime() const                                             { return IMPL_INFO_CALL( realtime(), true ); }
Bool            Input::setRealtime( Bool realtime )                                 { return IMPL_INFO_CALL( setRealtime( realtime ), false ); }

String          Input::units() const                                                { return IMPL_INFO_CALL( units(), String() ); }
Bool            Input::setUnits( String units )                                     { return IMPL_INFO_CALL( setUnits( units ), false ); }

Bool            Input::isEnum() const                                               { return IMPL_INFO_CALL( isEnum(), false ); }
UInt            Input::enumItemCount() const                                        { return IMPL_INFO_CALL( enumItemCount(), 0 ); }
EnumItem        Input::enumItem( UInt index ) const                                 { return IMPL_INFO_CALL( enumItem( index ), nullptr ); }

EnumItem        Input::findEnumItem( Int intValue ) const                           { return IMPL_INFO_CALL( findEnumItem( intValue ), nullptr ); }
EnumItem        Input::findEnumItem( Real realValue ) const                         { return IMPL_INFO_CALL( findEnumItem( realValue ), nullptr ); }
EnumItem        Input::findEnumItem( String strValue ) const                        { return IMPL_INFO_CALL( findEnumItem( strValue ), nullptr ); }

EnumItem        Input::addEnumItem( Int value, String label, String help, String lang )    { return IMPL_INFO_CALL( addEnumItem( value, label, help, lang ), nullptr ); }
EnumItem        Input::addEnumItem( Real value, String label, String help, String lang )   { return IMPL_INFO_CALL( addEnumItem( value, label, help, lang ), nullptr ); }
EnumItem        Input::addEnumItem( String value, String label, String help, String lang ) { return IMPL_INFO_CALL( addEnumItem( value, label, help, lang ), nullptr ); }

InputVector Input::inputs() const
{
    InputVector ret;
    for ( UInt i = 0; i < inputCount(); ++i )
        ret.push_back( input( i ) );

    return ret;
}

EnumItemVector Input::enumItems() const
{
    EnumItemVector ret;
    for ( UInt i = 0; i < enumItemCount(); ++i )
        ret.push_back( enumItem( i ) );

    return ret;
}

//=================================================================================================
// Output.
//=================================================================================================

String          Output::name() const                                                { return IMPL_CALL( name(), String() ); }
AttrType        Output::type() const                                                { return IMPL_CALL( type(), AttrType_Null ); }
String          Output::typeString() const                                          { return IMPL_CALL( typeString(), "Null" ); }
String          Output::desc() const                                                { return IMPL_CALL( desc(), "" ); }

Int             Output::instanceCount()                                             { return impl_outputInstanceCount(); }
UInt64          Output::hash( StringVector excludes ) const                         { return IMPL_CALL( hash( excludes ), 0 ); }

DataType        Output::bufferType() const                                          { return IMPL_CALL( bufferType(), DataType_Null ); }

Node            Output::node() const                                                { return IMPL_CALL( node(), nullptr ); }
String          Output::path() const                                                { return IMPL_CALL( path(), String() ); }
UInt            Output::index() const                                               { if ( impl() ) { return impl()->index(); } logError( "index() called on orphan Output, returning 0" ); return 0; }

Bool            Output::canConnect( Input input ) const                             { return IMPL_CALL( canConnect( input.impl() ), false ); }
Bool            Output::connect( Input input )                                      { return IMPL_CALL( connect( input.impl() ), false ); }
void            Output::disconnect( Input input )                                   { IMPL_VOID( disconnect( input.impl() ) ); }

Bool            Output::operator+=( Input input )                                   { return IMPL_CALL( connect( input.impl() ), false ); }
void            Output::operator-=( Input input )                                   { IMPL_VOID( disconnect( input.impl() ) ); }

Bool            Output::connected() const                                           { return IMPL_CALL( connected(), false ); }
Bool            Output::connectedTo( Input input )                                  { return IMPL_CALL( connectedTo( input.impl() ), false ); }

UInt            Output::inputCount() const                                          { return IMPL_CALL( inputCount(), 0 ); }
Input           Output::input( UInt index )                                         { return IMPL_CALL( input( index ), nullptr ); }
Input           Output::input( UInt index ) const                                   { return IMPL_CALL( input( index ), nullptr ); }
Input           Output::operator[]( UInt index )                                    { return IMPL_CALL( input( index ), nullptr ); }
Input           Output::operator[]( UInt index ) const                              { return IMPL_CALL( input( index ), nullptr ); }

Bool            Output::isBool() const                                              { return IMPL_CALL( isBool(), false ); }
Bool            Output::isInt() const                                               { return IMPL_CALL( isInt(), false ); }
Bool            Output::isUInt() const                                              { return IMPL_CALL( isUInt(), false ); }
Bool            Output::isReal() const                                              { return IMPL_CALL( isReal(), false ); }
Bool            Output::isVec2() const                                              { return IMPL_CALL( isVec2(), false ); }
Bool            Output::isVec3() const                                              { return IMPL_CALL( isVec3(), false ); }
Bool            Output::isVec4() const                                              { return IMPL_CALL( isVec4(), false ); }
Bool            Output::isPos2() const                                              { return IMPL_CALL( isPos2(), false ); }
Bool            Output::isPos3() const                                              { return IMPL_CALL( isPos3(), false ); }
Bool            Output::isQuat() const                                              { return IMPL_CALL( isQuat(), false ); }
Bool            Output::isRgba() const                                              { return IMPL_CALL( isRgba(), false ); }
Bool            Output::isMat3() const                                              { return IMPL_CALL( isMat3(), false ); }
Bool            Output::isMat4() const                                              { return IMPL_CALL( isMat4(), false ); }
Bool            Output::isString() const                                            { return IMPL_CALL( isString(), false ); }
Bool            Output::isNode() const                                              { return IMPL_CALL( isNode(), false ); }
Bool            Output::isBuffer( DataType ofType ) const                           { return IMPL_CALL( isBuffer( ofType ), false ); }

Bool            Output::asBool( EvalCtx* ctx ) const                                { IMPL_EVAL( Bool, false ); }
Int             Output::asInt( EvalCtx* ctx ) const                                 { IMPL_EVAL( Int, 0 ); }
UInt            Output::asUInt( EvalCtx* ctx ) const                                { IMPL_EVAL( UInt, 0 ); }
Real            Output::asReal( EvalCtx* ctx ) const                                { IMPL_EVAL( Real, 0.0 ); }
Vec2            Output::asVec2( EvalCtx* ctx ) const                                { IMPL_EVAL( Vec2, Vec2::zero ); }
Vec3            Output::asVec3( EvalCtx* ctx ) const                                { IMPL_EVAL( Vec3, Vec3::zero ); }
Vec4            Output::asVec4( EvalCtx* ctx ) const                                { IMPL_EVAL( Vec4, Vec4::zero ); }
Pos2            Output::asPos2( EvalCtx* ctx ) const                                { IMPL_EVAL( Pos2, Pos2::zero ); }
Pos3            Output::asPos3( EvalCtx* ctx ) const                                { IMPL_EVAL( Pos3, Pos3::zero ); }
Quat            Output::asQuat( EvalCtx* ctx ) const                                { IMPL_EVAL( Quat, Quat::identity ); }
Rgba            Output::asRgba( EvalCtx* ctx ) const                                { IMPL_EVAL( Rgba, Rgba::black ); }
Mat3            Output::asMat3( EvalCtx* ctx ) const                                { IMPL_EVAL( Mat3, Mat3::identity ); }
Mat4            Output::asMat4( EvalCtx* ctx ) const                                { IMPL_EVAL( Mat4, Mat4::identity ); }
String          Output::asString( EvalCtx* ctx ) const                              { IMPL_EVAL( String, String() ); }
BufRef          Output::asBuffer( EvalCtx* ctx ) const                              { IMPL_EVAL( BufRef, BufRef() ); }
Node            Output::asNode( EvalCtx* ctx ) const                                { INode* t; return impl() && impl()->eval( t, ctx ) ? t : nullptr; }

Bool            Output::accepts( String type ) const                                { return IMPL_CALL( accepts( type ), false ); }
Bool            Output::accepts( Node node ) const                                  { return IMPL_CALL( accepts( node.impl() ), false ); }
                                                                                    
StringVector    Output::accepted() const                                            { return IMPL_CALL( accepted(), StringVector() ); }
Bool            Output::setAccepted( StringVector accepted )                        { return IMPL_CALL( setAccepted( accepted ), false ); }
                                                                                    
String          Output::label( String lang ) const                                  { return IMPL_CALL( label( lang ), name() ); }
Bool            Output::setLabel( String label, String lang )                       { return IMPL_CALL( setLabel( label, lang ), false ); }
                                                                                    
String          Output::help( String lang ) const                                   { return IMPL_CALL( help( lang ), "(no help available)" ); }
Bool            Output::setHelp( String help, String lang )                         { return IMPL_CALL( setHelp( help, lang ), false ); }

InputVector Output::inputs() const
{
    InputVector ret;
    for ( UInt i = 0; i < inputCount(); ++i )
        ret.push_back( input( i ) );

    return ret;
}

Rgba Output::sample( Vec2 uv ) const
{
    Rgba out = Rgba::black;

    if ( !impl() )
        return out;

    auto ctx = EvalCtx::make( impl() );
    ctx.useScratchUVs();
    ctx.setUV0( uv );
    ctx.setLocalPosition( Pos3 { uv.u, uv.v, 0.0 } );

    switch ( impl()->type() )
    {
    case AttrType_Vec2:
    case AttrType_Pos2:
        {
            Vec2 v;
            if ( impl()->eval( v, &ctx ) )
                out = Rgba::make( v.x, v.y, 0.0, 1.0 );
        }
        break;
    case AttrType_Vec3:
    case AttrType_Pos3:
        {
            Vec3 v;
            if ( impl()->eval( v, &ctx ) )
                out = Rgba::make( v.x, v.y, v.z, 1.0 );
        }
        break;
    case AttrType_Vec4:
    case AttrType_Quat:
        {
            Vec4 v;
            if ( impl()->eval( v, &ctx ) )
                out = Rgba::make( v.x, v.y, v.z, v.w );
        }
        break;
    case AttrType_Rgba:
        {
            Rgba v;
            if ( impl()->eval( v, &ctx ) )
                out = v;
        }
        break;
    default:
        break;
    }

    return out;
}

//=================================================================================================
// Layout.
//=================================================================================================

LayoutType      Layout::type() const                                                { return IMPL_CALL( type(), LayoutType_Invalid ); }
String          Layout::name() const                                                { return IMPL_CALL( name(), "" ); }
String          Layout::path() const                                                { return IMPL_CALL( path(), "" ); }
String          Layout::label( String lang ) const                                  { return IMPL_CALL( label( lang ), "" ); }
Bool            Layout::expand() const                                              { return IMPL_CALL( expand(), true ); }
AttrType        Layout::attrType() const                                            { return IMPL_CALL( attrType(), AttrType_Null ); }
AttrType        Layout::arrayType() const                                           { return IMPL_CALL( arrayType(), AttrType_Null ); }
UInt            Layout::layoutCount() const                                         { return IMPL_CALL( layoutCount(), 0 ); }
Layout          Layout::layout( UInt index ) const                                  { return IMPL_CALL( layout( index ), nullptr ); }
Layout          Layout::operator[]( UInt index ) const                              { return IMPL_CALL( layout( index ), nullptr ); }

Bool            Layout::isInvalid() const                                           { return IMPL_CALL( isInvalid(), false ); }
Bool            Layout::isLayout() const                                            { return IMPL_CALL( isLayout(), false ); }
Bool            Layout::isHeader() const                                            { return IMPL_CALL( isHeader(), false ); }
Bool            Layout::isInput() const                                             { return IMPL_CALL( isInput(), false ); }
Bool            Layout::isObject() const                                            { return IMPL_CALL( isObject(), false ); }
Bool            Layout::isObjectArray() const                                       { return IMPL_CALL( isObjectArray(), false ); }
Bool            Layout::isSeparator() const                                         { return IMPL_CALL( isSeparator(), false ); }

LayoutVector Layout::layouts() const
{
    auto ret = LayoutVector();
    for ( UInt i = 0; i < layoutCount(); ++i )
        ret.push_back( layout( i ) );

    return ret;
}

//=================================================================================================
// Node.
//=================================================================================================
//
// SVG icon returned for any node lacking a specific one. NOTE: this will not render if there is
// a line-feed at the beginning (i.e. note that it is put at the very start of the string).
//
static const String s_missingSvgIcon = R"(<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="288" height="288" viewBox="0 0 288.00 288.00" enable-background="new 0 0 288.00 288.00" xml:space="preserve">
	<g>
		<path fill="#B6B6B6" fill-opacity="1" stroke-linejoin="round" d="M 135.533,88.4434C 127.807,88.4434 120.413,89.3159 113.353,91.063C 106.294,92.8105 98.9005,95.6411 91.1738,99.5542L 79.9943,75.1079C 88.2334,71.7285 97.6267,69.0317 108.174,67.0176C 118.721,65.0059 129.55,63.9976 140.661,63.9976C 153.995,63.9976 164.995,65.2183 173.662,67.6621C 182.328,70.1035 189.218,73.1714 194.329,76.8628C 199.44,80.5542 202.995,84.6157 204.995,89.0493C 206.995,93.4824 207.995,97.77 207.995,101.914C 207.995,106.937 206.628,111.447 203.893,115.439C 201.157,119.431 197.713,123.129 193.559,126.534C 189.405,129.936 184.918,133.153 180.098,136.187C 175.277,139.218 170.79,142.326 166.636,145.509C 162.482,148.692 159.038,152.053 156.302,155.596C 153.568,159.138 152.2,163.13 152.2,167.574C 152.2,168.316 152.2,169.166 152.2,170.127C 152.2,171.087 152.303,172.008 152.508,172.887L 111.123,172.887C 110.61,171.429 110.217,169.867 109.943,168.201C 109.669,166.533 109.533,164.97 109.533,163.512C 109.533,158.742 110.892,154.472 113.61,150.699C 116.328,146.926 119.772,143.443 123.943,140.249C 128.114,137.055 132.61,134.08 137.43,131.324C 142.251,128.571 146.747,125.815 150.918,123.062C 155.089,120.306 158.533,117.476 161.251,114.571C 163.969,111.666 165.328,108.465 165.328,104.97C 165.328,100.203 162.85,96.2559 157.892,93.1294C 152.935,90.0054 145.482,88.4434 135.533,88.4434 Z M 162.046,206.325C 162.046,211.556 159.533,215.811 154.507,219.086C 149.482,222.361 143.243,223.998 135.789,223.998C 128.576,223.998 122.396,222.361 117.251,219.086C 112.105,215.811 109.533,211.556 109.533,206.325C 109.533,201.117 112.105,196.833 117.251,193.479C 122.396,190.122 128.576,188.442 135.789,188.442C 143.243,188.442 149.482,190.122 154.507,193.479C 159.533,196.833 162.046,201.117 162.046,206.325 Z "/>
	</g>
</svg>
)";

String          Node::id() const                                                    { return IMPL_CALL( id(), String() ); }
String          Node::name() const                                                  { return IMPL_CALL( name(), String() ); }
String          Node::displayName() const                                           { return IMPL_CALL( displayName(), String() ); }
Bool            Node::setDisplayName( String name )                                 { return IMPL_CALL( setDisplayName( name ), false ); }

String          Node::label( String lang ) const                                    { return IMPL_CALL( label( lang ), String() ); }
String          Node::help( String lang ) const                                     { return IMPL_CALL( help( lang ), String() ); }
Layout          Node::layout() const                                                { return IMPL_CALL( layout(), nullptr ); }

Int             Node::instanceCount()                                               { return impl_nodeInstanceCount(); }

Bool            Node::subscribe( NodeObserver* observer )                           { return IMPL_CALL( subscribe( observer ), false ); }
Bool            Node::unsubscribe( NodeObserver* observer )                         { return IMPL_CALL( unsubscribe( observer ), false ); }
Bool            Node::unsubscribeAll()                                              { return IMPL_CALL( unsubscribeAll(), false ); }

Input           Node::meta() const                                                  { return IMPL_CALL( meta(), nullptr ); }
Scene           Node::scene( Bool local ) const                                     { return IMPL_CALL( scene( local ), nullptr ); }
UInt64          Node::hash( StringVector excludes ) const                           { return IMPL_CALL( hash( excludes ), 0 ); }

Bool            Node::dirty( Bool checkDeps ) const                                 { return IMPL_CALL( dirty( checkDeps ), false ); }
void            Node::setDirty( Bool dirty )                                        { IMPL_VOID( setDirty( dirty ) ); }

String          Node::type() const                                                  { return IMPL_CALL( type(), String() ); }
StringVector    Node::bases() const                                                 { return IMPL_CALL( bases(), StringVector() ); }

Bool            Node::isTypeOf( String type ) const                                 { return IMPL_CALL( isTypeOf( type ), false ); }
Bool            Node::isTypeOf( StringVector types ) const                          { return IMPL_CALL( isTypeOf( types ), false ); }
String          Node::typeDesc() const                                              { return IMPL_CALL( typeDesc(), String() ); }

Bool            Node::isAbstract() const                                            { return IMPL_CALL( isAbstract(), false ); }
Bool            Node::isInternal() const                                            { return IMPL_CALL( isInternal(), false ); }

Node            Node::convertTo( String type )                                      { return IMPL_CALL( convertTo( type ), nullptr ); }

Input           Node::findInput( String path ) const                                { return IMPL_CALL( findInput( path ), nullptr ); }
Output          Node::findOutput( String path ) const                               { return IMPL_CALL( findOutput( path ), nullptr ); }

Bool            Node::hasInput( UInt index, AttrType type ) const                   { return IMPL_CALL( hasInput( index, type ), false ); }
Bool            Node::hasInput( String name, AttrType type ) const                  { return IMPL_CALL( hasInput( name, type ), false ); }

Bool            Node::hasOutput( UInt index, AttrType type ) const                  { return IMPL_CALL( hasOutput( index, type ), false ); }
Bool            Node::hasOutput( String name, AttrType type ) const                 { return IMPL_CALL( hasOutput( name, type ), false ); }

UInt            Node::inputCount() const                                            { return IMPL_CALL( inputCount(), 0 ); }
Input           Node::input( String name ) const                                    { return IMPL_FCALL( INode*, input( name ), nullptr ); }
Input           Node::input( String name )                                          { return IMPL_CALL( input( name ), nullptr ); }
Input           Node::input( UInt index ) const                                     { return IMPL_CALL( input( index ), nullptr ); }
Input           Node::input( UInt index )                                           { return IMPL_CALL( input( index ), nullptr ); }

Input           Node::operator[]( String name ) const                               { return IMPL_FCALL( INode*, input( name ), nullptr ); }
Input           Node::operator[]( String name )                                     { return IMPL_CALL( input( name ), nullptr ); }
Input           Node::operator[]( UInt index ) const                                { return IMPL_CALL( input( index ), nullptr ); }
Input           Node::operator[]( UInt index )                                      { return IMPL_CALL( input( index ), nullptr ); }

UInt            Node::outputCount() const                                           { return IMPL_CALL( outputCount(), 0 ); }
Output          Node::output( UInt index ) const                                    { return IMPL_CALL( output( index ), nullptr ); }
Output          Node::output( String name ) const                                   { return IMPL_CALL( output( name ), nullptr ); }

Input           Node::parseInputPath( String path )                                 { return IMPL_CALL( parseInputPath( path ), nullptr ); }

Input           Node::createInput( String name, AttrType type )                     { return IMPL_CALL( createInput( name, type ), nullptr ); }
Input           Node::createArray( String name, AttrType type )                     { return IMPL_CALL( createArray( name, type ), nullptr ); }
Output          Node::createOutput( String name, AttrType type )                    { return IMPL_CALL( createOutput( name, type ), nullptr ); }

Bool            Node::removeInput( String name )                                    { return IMPL_CALL( removeInput( name ), false ); }
Bool            Node::removeInput( UInt index )                                     { return IMPL_CALL( removeInput( index ), false ); }
Bool            Node::removeLastInput()                                             { return IMPL_CALL( removeLastInput(), false ); }
Bool            Node::clearInputs()                                                 { return IMPL_CALL( clearInputs(), false ); }

Bool            Node::removeOutput( String name )                                   { return IMPL_CALL( removeOutput( name ), false ); }
Bool            Node::removeOutput( UInt index )                                    { return IMPL_CALL( removeOutput( index ), false ); }
Bool            Node::removeLastOutput()                                            { return IMPL_CALL( removeLastOutput(), false ); }
Bool            Node::clearOutputs()                                                { return IMPL_CALL( clearOutputs(), false ); }

Bool            Node::isPath() const                                                { return IMPL_CALL( isPath(), false ); }
String          Node::asPath() const                                                { return IMPL_CALL( asPath(), String() ); }
Bool            Node::setPath( String dir, String file, String ext )                { return IMPL_CALL( setPath( dir, file, ext ), false ); }
Bool            Node::setPath( String path )                                        { return IMPL_CALL( setPath( path ), false ); }

Int             Node::indexIn( Node parent ) const                                  { return IMPL_CALL( indexIn( parent.impl() ), -1 ); }
Bool            Node::parentTo( Node parent )                                       { return IMPL_CALL( parentTo( parent.impl() ), false ); }
Bool            Node::unparentFrom( Node parent )                                   { return IMPL_CALL( unparentFrom( parent.impl() ), false ); }

PathVector      Node::paths( String type, Bool excludeHidden ) const                { return toVector<PathVector, IPathIterator>( IMPL_CALL( paths( type, excludeHidden ), nullptr ) ); }
PathVector      Node::paths( StringVector types, Bool excludeHidden ) const         { return toVector<PathVector, IPathIterator>( IMPL_CALL( paths( types, excludeHidden ), nullptr ) ); }

PathVector      Node::pathsTo( Node node, Bool excludeHidden ) const                { return toVector<PathVector, IPathIterator>( IMPL_CALL( pathsTo( node.impl(), excludeHidden ), nullptr ) ); }
PathVector      Node::pathsTo( String name, Bool excludeHidden ) const              { return toVector<PathVector, IPathIterator>( IMPL_CALL( pathsTo( name, excludeHidden ), nullptr ) ); }

Path            Node::firstPathTo( Node node, Bool excludeHidden ) const            { Path p = (IMPL_CALL( firstPathTo( node.impl(), excludeHidden ), nullptr )); if ( p ) { p.impl()->decRef(); } return p; }
Path            Node::firstPathTo( String name, Bool excludeHidden ) const          { Path p = (IMPL_CALL( firstPathTo( name, excludeHidden ), nullptr )); if ( p ) { p.impl()->decRef(); } return p; }

LibInfo         Node::libInfo() const                                               { return IMPL_CALL( libInfo(), LibInfo() ); }
String          Node::libPath() const                                               { return IMPL_CALL( libPath(), String() ); }
String          Node::libID() const                                                 { return IMPL_CALL( libID(), 0 ); }

Bool            Node::hasNodeInit() const                                           { return IMPL_CALL( hasNodeInit(), false ); }
Bool            Node::hasNodePrep() const                                           { return IMPL_CALL( hasNodePrep(), false ); }
Bool            Node::hasNodeEval() const                                           { return IMPL_CALL( hasNodeEval(), false ); }
Bool            Node::hasNodeModify() const                                         { return IMPL_CALL( hasNodeModify( ), false ); }
Bool            Node::hasNodeChanged() const                                        { return IMPL_CALL( hasNodeChanged( ), false ); }
Bool            Node::hasNodeFree() const                                           { return IMPL_CALL( hasNodeFree(), false ); }

Bool            Node::nodePrep()                                                    { return IMPL_CALL( nodePrep(), false ); }
Bool            Node::nodeModify( ModifyFlags* flags, Node node )                   { return IMPL_CALL( nodeModify( flags, node.impl() ), false ); }
Bool            Node::nodeChanged( Input input )                                    { return IMPL_CALL( nodeChanged( input.impl() ), false ); }

const Ptr       Node::data() const                                                  { return IMPL_CALL( data(), nullptr ); }

//String        Node::svgIcon( Bool returnEmptyStringIfMissing ) const              { return IMPL_CALL( svgIcon( returnEmptyStringIfMissing ), String() ); }
String          Node::htmlHelp( Bool asStandaloneDoc, String lang ) const           { return IMPL_CALL( htmlHelp( asStandaloneDoc, lang ), String() ); }

String          Node::checkMesh() const                                             { return IMPL_CALL( checkMesh(), String() ); }

UInt            Node::pointCloudCount() const                                       { return IMPL_CALL( pointCloudCount(), 0 ); }
BufRef          Node::pointCloud( Real detail, Int maxPoints )                      { return IMPL_CALL( pointCloud( detail, maxPoints ), BufRef() ); }

Svg             Node::renderSvg( Int32 xres, Int32 yres, UInt32 bg, Bool bgr ) const { return IMPL_CALL( renderSvg( xres, yres, bg, bgr ), nullptr ); }

// Allow obtaining the missing icon from a null node.
//
String Node::svgIcon( Bool returnEmptyStringIfMissing /*= false*/ ) const
{
    if ( impl() )
        return impl()->svgIcon( returnEmptyStringIfMissing );

    return returnEmptyStringIfMissing ? String() : s_missingSvgIcon.removeUtf8Bom();
}

InputVector Node::inputs() const
{
    InputVector ret;
    for ( UInt i = 0; i < inputCount(); ++i )
        ret.push_back( input( i ) );

    return ret;
}

OutputVector Node::outputs() const
{
    OutputVector ret;
    for ( UInt i = 0; i < outputCount(); ++i )
        ret.push_back( output( i ) );

    return ret;
}

NodeVector Node::children( Int depth /*= -1*/ ) const
{
    return impl() ? toVector<NodeVector, INodeIterator>( impl()->children( depth ) ) : NodeVector();
}

NodeVector Node::dependencies( Bool topoSort /*= false*/, Bool followOutputs /*= false*/ ) const
{
    return impl() ? toVector<NodeVector, INodeIterator>( impl()->dependencies( topoSort, followOutputs ) ) : NodeVector();
}

String Node::serialize( Bool withDeps, Bool writeDefaults ) const
{
    return IMPL_CALL( serialize( withDeps, writeDefaults ), String() );
}

Node Node::clone( Bool withDeps /*= true*/ )
{
    return IMPL_CALL( clone( withDeps ), nullptr );
}

template<> Node Input::defaultVal<Node>( Node def ) const
{
    INode* t = nullptr;
    return (impl() && impl()->defaultVal( t )) ? Node( t ) : def;
}

//=================================================================================================
// Path.
//=================================================================================================

String          Path::path() const                                                  { return IMPL_CALL( path(), String() ); }
UInt64          Path::hash() const                                                  { return IMPL_CALL( hash(), 0 ); }
Bool            Path::dirty() const                                                 { return IMPL_CALL( dirty(), false ); }
UInt            Path::count() const                                                 { return IMPL_CALL( count(), 0 ); }
Bool            Path::contains( Node node ) const                                   { return IMPL_CALL( contains( node.impl() ), false ); }
NodeVector      Path::nodes() const                                                 { return toVector<NodeVector, INodeIterator>( IMPL_CALL( nodes(), nullptr ) ); }
Node            Path::root() const                                                  { return IMPL_CALL( root(), nullptr ); }
Node            Path::leaf() const                                                  { return IMPL_CALL( leaf(), nullptr ); }
Node            Path::parent( UInt revIdx ) const                                   { return IMPL_CALL( parent( revIdx ), nullptr ); }
Mat4            Path::transform( Real time, UInt instancerIdx ) const               { return IMPL_CALL( transform( time, instancerIdx ), Mat4::identity ); }
Bool            Path::visible() const                                               { return IMPL_CALL( visible(), true ); }
Node            Path::material() const                                              { return IMPL_CALL( material(), nullptr ); }
Node            Path::subdivision() const                                           { return IMPL_CALL( subdivision(), nullptr ); }

//=================================================================================================
// Scene.
//=================================================================================================

Scene::Scene() : RefersTo( impl_createScene() )
{
#if HAVE_BELLA_SDK_LIB
    if (!impl_bellaCoreNodesLinked() )
        logWarning( "Core node implementations are not linked." );
#endif
}

Int             Scene::instanceCount()                                              { return impl_sceneInstanceCount(); }

Int64           Scene::id() const                                                   { return IMPL_CALL( id(), -1 ); }
UInt64          Scene::hash( StringVector excludes ) const                          { return IMPL_CALL( hash( excludes ), 0 ); }

UInt64          Scene::fileVersion() const                                          { return IMPL_CALL( fileVersion(), 0 ); }
String          Scene::fileTime() const                                             { return IMPL_CALL( fileTime(), String() ); }
StringVector    Scene::fileComments() const                                         { return IMPL_CALL( fileComments(), StringVector() ); }

Bool            Scene::subscribe( SceneObserver* observer )                         { return IMPL_CALL( subscribe( observer ), false ); }
Bool            Scene::unsubscribe( SceneObserver* observer )                       { return IMPL_CALL( unsubscribe( observer ), false ); }
Bool            Scene::unsubscribeAll()                                             { return IMPL_CALL( unsubscribeAll(), false ); }

StringVector    Scene::libIDs() const                                               { return IMPL_CALL( libIDs(), StringVector() ); }
StringVector    Scene::libPaths() const                                             { return IMPL_CALL( libPaths(), StringVector() ); }
LibInfo         Scene::libInfo( String pathOrID ) const                             { return IMPL_CALL( libInfo( pathOrID ), nullptr ); }

NodeVector      Scene::defs( String category /*= ""*/ ) const                       { return toVector<NodeVector, INodeIterator>( IMPL_CALL( defs( category ), nullptr ) ); }
NodeVector      Scene::defs( LibInfo forLib ) const                                 { return toVector<NodeVector, INodeIterator>( IMPL_CALL( defs( forLib.impl() ), nullptr ) ); }
UInt            Scene::defCount() const                                             { return IMPL_CALL( defCount(), 0 ); }
StringVector    Scene::defCategories() const                                        { return IMPL_CALL( defCategories(), StringVector() ); }

UInt            Scene::clearDefs()                                                  { return IMPL_CALL( clearDefs(), 0 ); }
Bool            Scene::parseDefs( String bndText )                                  { return IMPL_CALL( parseDefs( bndText ), false ); }
Bool            Scene::loadDefs( String path )                                      { return IMPL_CALL( loadDefs( path ), false ); }

Node            Scene::defineNode( String name )                                    { return IMPL_CALL( defineNode( name ), nullptr ); }
Node            Scene::findDef( String type ) const                                 { return IMPL_CALL( findDef( type ), nullptr ); }
Node            Scene::findDef( UInt index ) const                                  { return IMPL_CALL( findDef( index ), nullptr ); }

Node            Scene::createNode( String type, String name, String dname )         { return IMPL_CALL( createNode( type, name, dname ), nullptr ); }
Node            Scene::cloneNode( Node node, Bool withDeps, Bool adjustDisplayNames ) { return IMPL_CALL( cloneNode( node.impl(), withDeps, adjustDisplayNames ), nullptr); }
Node            Scene::cloneNode( String name, Bool withDeps, Bool adjustDisplayNames ) { return IMPL_CALL( cloneNode( name, withDeps, adjustDisplayNames ), nullptr); }
NodeVector      Scene::cloneNodes( StringVector names, Bool withDeps, Bool adjustDisplayNames ) { return toVector<NodeVector, INodeIterator>( IMPL_CALL( cloneNodes( names, withDeps, adjustDisplayNames ), nullptr) ); }
Node            Scene::convertNode( Node node, String newType )                     { return IMPL_CALL( convertNode( node.impl(), newType), nullptr); }

Bool            Scene::canRemoveNode( Node node ) const                             { return IMPL_CALL( canRemoveNode( node.impl() ), false ); }
Bool            Scene::removeNode( Node node )                                      { return IMPL_CALL( removeNode( node.impl() ), false ); }
Bool            Scene::removeNode( String name )                                    { return IMPL_CALL( removeNode( name ), false ); }
StringVector    Scene::removeNodes( String ofType )                                 { return IMPL_CALL( removeNodes( ofType ), false ); }
StringVector    Scene::removeNodes( StringVector names )                            { return IMPL_CALL( removeNodes( names ), false ); }
UInt            Scene::clearNodes( Bool unreferencedOnly, StringVector unrefTypesToKeep ) { return IMPL_CALL( clearNodes( unreferencedOnly, unrefTypesToKeep ), false ); }
UInt            Scene::nodeCount() const                                            { return IMPL_CALL( nodeCount(), 0 ); }

Node            Scene::restoreNode( String name )                                   { return IMPL_CALL( restoreNode( name ), nullptr ); }

Node            Scene::findNode( String name ) const                                { return IMPL_CALL( findNode( name ), nullptr ); }
NodeVector      Scene::findNodes( StringVector names, Bool topoSort, Bool withDeps ) const { return toVector<NodeVector, INodeIterator>( IMPL_CALL( findNodes( names, topoSort, withDeps ), nullptr ) ); }
Input           Scene::parseInputPath( String path )                                { return IMPL_CALL( parseInputPath( path ), nullptr ); }

StringVector    Scene::nodeTypes() const                                            { return IMPL_CALL( nodeTypes(), StringVector() ); }
StringVector    Scene::nodeNames( StringVector types ) const                        { return IMPL_CALL( nodeNames( types ), StringVector() ); }
StringVector    Scene::nodeNames( String type ) const                               { return IMPL_CALL( nodeNames( type ), StringVector() ); }

NodeVector      Scene::nodes( String type, Bool topoSort ) const                    { return toVector<NodeVector, INodeIterator>( IMPL_CALL( nodes( type, topoSort ), nullptr ) ); }
NodeVector      Scene::nodes( StringVector types, Bool topoSort ) const             { return toVector<NodeVector, INodeIterator>( IMPL_CALL( nodes( types, topoSort ), nullptr ) ); }
NodeVector      Scene::nodes( LibInfo forLib ) const                                { return toVector<NodeVector, INodeIterator>( IMPL_CALL( nodes( forLib.impl() ), nullptr ) ); }

PathVector      Scene::paths( String type, Bool excludeHidden ) const               { return toVector<PathVector, IPathIterator>( IMPL_CALL( paths( type, excludeHidden ), nullptr ) ); }
PathVector      Scene::paths( StringVector types, Bool excludeHidden ) const        { return toVector<PathVector, IPathIterator>( IMPL_CALL( paths( types, excludeHidden ), nullptr ) ); }

PathVector      Scene::pathsTo( Node node, Bool excludeHidden ) const               { return toVector<PathVector, IPathIterator>( IMPL_CALL( pathsTo( node.impl(), excludeHidden ), nullptr ) ); }
PathVector      Scene::pathsTo( String name, Bool excludeHidden ) const             { return toVector<PathVector, IPathIterator>( IMPL_CALL( pathsTo( name, excludeHidden ), nullptr ) ); }

// NOTE how these are returned. The IPath* returned is not owned by a scene or iterator, so it has to
// be returned to us with refcount=1 before we are able to put it in a bella_sdk::Path. Therefore, it
// will have refcount=2 once wrapped, and we must decref it once. It is the same for any other similar
// cases of un-owned refcounted instances.

Path            Scene::firstPathTo( Node node, Bool excludeHidden ) const           { Path p = ( IMPL_CALL( firstPathTo( node.impl(), excludeHidden ), nullptr ) ); if ( p ) { p.impl()->decRef(); } return p; }
Path            Scene::firstPathTo( String name, Bool excludeHidden ) const         { Path p = ( IMPL_CALL( firstPathTo( name, excludeHidden ), nullptr ) ); if ( p ) { p.impl()->decRef(); } return p; }

Node            Scene::global( Bool create ) const                                  { return IMPL_CALL( global( create ), nullptr ); }
Node            Scene::state( Bool create ) const                                   { return IMPL_CALL( state( create ), nullptr ); }
Node            Scene::world( Bool create ) const                                   { return IMPL_CALL( world( create ), nullptr ); }
Node            Scene::settings( Bool create ) const                                { return IMPL_CALL( settings( create ), nullptr ); }

String          Scene::ocioConfig() const                                           { return IMPL_CALL( ocioConfig(), "OCIO" ); }
String          Scene::applyOcioConfig() const                                      { return IMPL_CALL( applyOcioConfig(), "Cannot apply OCIO config." ); }
String          Scene::colorSpace() const                                           { return IMPL_CALL( colorSpace(), "sRGB" ); }

Node            Scene::camera() const                                               { return IMPL_CALL( camera(), nullptr ); }
Node            Scene::environment() const                                          { return IMPL_CALL( environment(), nullptr ); }
Node            Scene::sun() const                                                  { return IMPL_CALL( sun(), nullptr ); }
Node            Scene::solarContext() const                                         { return IMPL_CALL( solarContext(), nullptr ); }
Node            Scene::groundPlane() const                                          { return IMPL_CALL( groundPlane(), nullptr ); }

Mat4            Scene::axisSystem() const                                           { return IMPL_CALL( axisSystem(), Mat4::identity ); }
Vec3            Scene::scale() const                                                { return IMPL_CALL( scale(), Vec3::one ); }

Node            Scene::beautyPass() const                                           { return IMPL_CALL( beautyPass(), nullptr ); }
NodeVector      Scene::beautyPasses() const                                         { return toVector<NodeVector, INodeIterator>( IMPL_CALL( beautyPasses(), nullptr ) ); }
NodeVector      Scene::extraPasses() const                                          { return toVector<NodeVector, INodeIterator>( IMPL_CALL( extraPasses(), nullptr ) ); }
NodeVector      Scene::allPasses() const                                            { return toVector<NodeVector, INodeIterator>( IMPL_CALL( allPasses(), nullptr ) ); }

Node            Scene::defaultMaterial() const                                      { return IMPL_CALL( defaultMaterial(), nullptr ); }
Node            Scene::overrideMaterial() const                                     { return IMPL_CALL( overrideMaterial(), nullptr ); }

Bool            Scene::parse( String text )                                         { return IMPL_CALL( parse( text ), false ); }
String          Scene::serialize( Bool writeDefaults, SceneProgress* progress ) const { return IMPL_CALL( serialize( writeDefaults, progress ), String() ); }
StringVector    Scene::supportedFileTypes() const                                   { return IMPL_CALL( supportedFileTypes(), StringVector() ); }
Bool            Scene::canRead( String path ) const                                 { return IMPL_CALL( canRead( path ), false ); }
Node            Scene::read( String path, Bool asFragment, SceneProgress* progress ) { return IMPL_CALL( read( path, asFragment, progress ), nullptr ); }
Node            Scene::import( String pathOrFragment, SceneProgress * progress, Bool adjustDisplayNames ) { return IMPL_CALL( import( pathOrFragment, progress, adjustDisplayNames ), nullptr ); }
Bool            Scene::write( String path, SceneProgress* progress )                { return IMPL_CALL( write( path, progress ), false ); }
Scene           Scene::clone() const                                                { return IMPL_CALL( clone(), nullptr ); }

String          Scene::applyMacros( String path ) const                             { return IMPL_CALL( applyMacros( path ), String() ); }

String          Scene::location( Bool applyMacros ) const                           { return IMPL_CALL( location( applyMacros ), String() ); }
String          Scene::setLocation( String path )                                   { return IMPL_CALL( setLocation( path ), String() ); }

String          Scene::outputName( Bool applyMacros ) const                         { return IMPL_CALL( outputName( applyMacros ), String() ); }
String          Scene::outputDir( Bool useFallbacks ) const                         { return IMPL_CALL( outputDir( useFallbacks ), String() ); }
String          Scene::outputPath( Node node ) const                                { return IMPL_CALL( outputPath( node.impl() ), String() ); }

NodeVector      Scene::stateDependencies( StringVector types, Bool topoSort ) const { return toVector<NodeVector, INodeIterator>( IMPL_CALL( stateDependencies( types, topoSort ), nullptr ) ); }
NodeVector      Scene::unreferencedNodes( StringVector types ) const                { return toVector<NodeVector, INodeIterator>( IMPL_CALL( unreferencedNodes( types ), nullptr ) ); }

String          Scene::resolveInputPath( Node inputPath, Bool updateNode )          { return IMPL_CALL( resolveInputPath( inputPath.impl(), updateNode ), String() ); }
Bool            Scene::resolveInputPaths( Bool updateNodes )                        { return IMPL_CALL( resolveInputPaths( updateNodes ), false ); }

UInt            Scene::numThreads( Bool forIPR ) const                              { return IMPL_CALL( numThreads( forIPR ), Scene::computeNumThreads() ); }
String          Scene::bsiPath( Node beautyPass ) const                             { return IMPL_CALL( bsiPath( beautyPass.impl() ), String() ); }
String          Scene::bsiTempPath( Node beautyPass ) const                         { return IMPL_CALL( bsiTempPath( beautyPass.impl() ), String() ); }
Bool            Scene::canResumeBsi( Node beautyPass ) const                        { return IMPL_CALL( canResumeBsi( beautyPass.impl() ), false ); }

String          Scene::findSvg( String name )                                       { return impl_findBellaSvg( name ); }
Svg             Scene::renderSvg( String name, Int32 xres, Int32 yres, UInt32 bg, Bool bgr ) { return impl_renderSvg( name, xres, yres, bg, bgr ); }

String          Scene::htmlHelp( String lang ) const                                { return IMPL_CALL( htmlHelp( lang ), String() ); }
String          Scene::writeHtmlHelp( String& status, String lang )                 { return IMPL_CALL( writeHtmlHelp( status, lang ), String() ); }
Bool            Scene::showHtmlHelp( String& status, String lang )                  { return IMPL_CALL( showHtmlHelp( status, lang ), false ); }

void            Scene::enableUndo()                                                 { IMPL_VOID( enableUndo() ); }
void            Scene::disableUndo()                                                { IMPL_VOID( disableUndo() ); }
Bool            Scene::undoEnabled() const                                          { return IMPL_CALL( undoEnabled(), false ); }

Int             Scene::undoLimit() const                                            { return IMPL_CALL( undoLimit(), -1 ); }
void            Scene::setUndoLimit( Int num /*= -1*/ )                             { IMPL_VOID( setUndoLimit( num ) ); }
void            Scene::clearUndo()                                                  { IMPL_VOID( clearUndo() ); }

Bool            Scene::canUndo() const                                              { return IMPL_CALL( canUndo(), false ); }
Bool            Scene::canRedo() const                                              { return IMPL_CALL( canRedo(), false ); }

Bool            Scene::undo( UInt num )                                             { return IMPL_CALL( undo( num ), false ); }
Bool            Scene::redo( UInt num )                                             { return IMPL_CALL( redo( num ), false ); }

StringVector    Scene::undoList() const                                             { return IMPL_CALL( undoList(), StringVector() ); }
StringVector    Scene::redoList() const                                             { return IMPL_CALL( redoList(), StringVector() ); }

void            Scene::pushUndoGroup()                                              { IMPL_VOID( pushUndoGroup() ); }
void            Scene::popUndoGroup( String undoDesc, String redoDesc )             { IMPL_VOID( popUndoGroup( undoDesc, redoDesc ) ); }
Bool            Scene::inUndoGroup() const                                          { return IMPL_CALL( inUndoGroup(), false ); }

void            Scene::enableEvents()                                               { IMPL_VOID( enableEvents() ); }
void            Scene::disableEvents()                                              { IMPL_VOID( disableEvents() ); }
Bool            Scene::eventsEnabled() const                                        { return IMPL_CALL( eventsEnabled(), false ); }

void            Scene::pushEventGroup()                                             { IMPL_VOID( pushEventGroup() ); }
void            Scene::popEventGroup()                                              { IMPL_VOID( popEventGroup() ); }
Bool            Scene::inEventGroup() const                                         { return IMPL_CALL( inEventGroup(), false ); }

Bool            Scene::isDirty() const                                              { return IMPL_CALL( isDirty(), false ); }

StringVector    Scene::allResources( Bool includeMissing )                          { return IMPL_CALL( allResources( includeMissing ), StringVector() ); }
StringVector    Scene::referencedResources( Bool includeMissing )                   { return IMPL_CALL( referencedResources( includeMissing ), StringVector() ); }
StringVector    Scene::missingResources( Bool referencedOnly )                      { return IMPL_CALL( missingResources( referencedOnly ), StringVector() ); }
Bool            Scene::gatherResources( Bool referencedOnly )                       { return IMPL_CALL( gatherResources( referencedOnly ), false ); }

StringVector Scene::svgIconNames()
{
    return impl_bellaSvgNames();
}

Path Scene::cameraPath() const
{
    auto c = camera();
    return c ? firstPathTo( c, false ) : Path();
}

Path Scene::sunPath() const
{
    auto s = sun();
    return s ? firstPathTo( s, true ) : Path();
}

static Real resolutionScale( Node camera, Bool forIPR )
{
    auto scale = 1.0;

    if ( camera && forIPR )
        scale = camera.scene().settings()["iprScale"].asReal( true ) * 0.01;

    return math::clamp( scale, 0.1, 1.0 );
}

Vec2u Scene::resolution( Node camera, Bool forIPR, Bool isLicensed )
{
    if ( !camera )
        return Vec2u::zero;

    auto scale = resolutionScale( camera, forIPR );
    auto resd = ( camera["resolution"].asVec2( true ) * scale ).round();

    auto resu = Vec2u::make(
                math::max<UInt32>( 0, ( Int32 )resd.x ),
                math::max<UInt32>( 0, ( Int32 )resd.y )
                );

    // NOTE: make sure we have minimum 32x32 regardless of iprScale.
    //
    if ( resu.x < 32 ) resu.x = 32;
    if ( resu.y < 32 ) resu.y = 32;

    return renderedResolution( resu, isLicensed );
}

Region Scene::region( Node camera, Bool forIPR, Bool isLicensed, Bool constrain )
{
    if ( !camera )
        return Region();

    auto node = camera["region"].asNode();
    if ( !node )
        return Region();

    auto scale  = resolutionScale( camera, forIPR );
    auto offset = node["offset"].asVec2( true ) * scale; // NOTE: using eval.
    auto size   = node["size"].asVec2( true ) * scale;
    auto res    = resolution( camera, forIPR, isLicensed );

    if ( constrain )
        return constrainRegion( size, offset, Vec2::make( res.x, res.y ) );

    return Region( Vec2i::make( math::round<UInt32>( math::max( 0.0, size.x ) ),
                                math::round<UInt32>( math::max( 0.0, size.y ) ) ),
                   Vec2i::make( math::round<UInt32>( offset.x ),
                                math::round<UInt32>( offset.y ) ) );
}

UInt Scene::computeNumThreads( Int userThreads /*= 0*/ )
{
    auto cpuThreads = ( Int )os::numLogicalProcessors();

    if ( userThreads == 0 )
    {
#if !DL_IS_RELEASE
        return 1; // NOTE: force 1 thread in debug if autodetect ( default )
#else
        return ( UInt )cpuThreads;
#endif
    }

    if ( userThreads > 0 )
        return ( UInt )userThreads;

    cpuThreads += userThreads;

    return cpuThreads < 0 ? 1 : cpuThreads;
}

StringVector Scene::removeNodes( NodeVector nodes )
{
    auto names = StringVector();
    for ( auto node : nodes )
        names.push_back( node.id() );

    return removeNodes( names );
}

//=================================================================================================
// LibInfo.
//=================================================================================================

Bool            LibInfo::load( String path )                                        { return IMPL_CALL( load( path ), false ); }
Bool            LibInfo::ok() const                                                 { return IMPL_CALL( ok(), false ); }
String          LibInfo::status() const                                             { return IMPL_CALL( status(), String() ); }
String          LibInfo::summary( Scene scene ) const                               { return IMPL_CALL( summary( scene.impl() ), String() ); }

String          LibInfo::name() const                                               { return IMPL_CALL( name(), String() ); }
String          LibInfo::author() const                                             { return IMPL_CALL( author(), String() ); }
String          LibInfo::description() const                                        { return IMPL_CALL( description(), String() ); }
String          LibInfo::url() const                                                { return IMPL_CALL( url(), String() ); }
String          LibInfo::prefix() const                                             { return IMPL_CALL( prefix(), String() ); }
String          LibInfo::version() const                                            { return IMPL_CALL( version(), String() ); }
String          LibInfo::sdkVersion() const                                         { return IMPL_CALL( sdkVersion(), String() ); }

String          LibInfo::id() const                                                 { return IMPL_CALL( id(), String() ); }
String          LibInfo::file() const                                               { return IMPL_CALL( file(), String() ); }
String          LibInfo::libPath() const                                            { return IMPL_CALL( libPath(), String() ); }
String          LibInfo::libHash() const                                            { return IMPL_CALL( libHash(), String() ); }
String          LibInfo::bndText() const                                            { return IMPL_CALL( bndText(), String() ); }

String          LibInfo::svgIcon() const                                            { return IMPL_CALL( svgIcon(), String() ); }
Svg             LibInfo::renderSvg( Int32 xres, Int32 yres, UInt32 bg, Bool bgr ) const { return IMPL_CALL( renderSvg( xres, yres, bg, bgr ), nullptr ); }

//=================================================================================================
// PbrTextureSet.
//=================================================================================================

PbrTextureSet::PbrTextureSet() : RefersTo( impl_createTextureSet() ) { }

UInt            PbrTextureSet::resolve( String path, Node pbr )                     { return IMPL_CALL( resolve( path, pbr.impl() ), false ); }
String          PbrTextureSet::summary() const                                      { return IMPL_CALL( summary(), String() ); }

void            PbrTextureSet::addMatch( String type, String match )                { IMPL_VOID( addMatch( type, match ) ); }
void            PbrTextureSet::setMatches( String type, StringVector matches )      { IMPL_VOID( setMatches( type, matches ) ); }
StringVector    PbrTextureSet::getMatches( String type ) const                      { return IMPL_CALL( getMatches( type ), StringVector() ); }

String          PbrTextureSet::inputPath() const                                    { return IMPL_CALL( inputPath(), String() ); }
StringVector    PbrTextureSet::resolvedPaths() const                                { return IMPL_CALL( resolvedPaths(), StringVector() ); }
UInt            PbrTextureSet::numResolvedPaths() const                             { return IMPL_CALL( numResolvedPaths(), 0 ); }

Bool            PbrTextureSet::applyTo( Node node, Bool setPath ) const             { return IMPL_CALL( applyTo( node.impl(), setPath ), false ); }

String          PbrTextureSet::opacity() const                                      { return IMPL_CALL( opacity(), String() ); }
String          PbrTextureSet::color() const                                        { return IMPL_CALL( color(), String() ); }
String          PbrTextureSet::specular() const                                     { return IMPL_CALL( specular(), String() ); }
String          PbrTextureSet::glossiness() const                                   { return IMPL_CALL( glossiness(), String() ); }
String          PbrTextureSet::metallic() const                                     { return IMPL_CALL( metallic(), String() ); }
String          PbrTextureSet::roughness() const                                    { return IMPL_CALL( roughness(), String() ); }
String          PbrTextureSet::aniso() const                                        { return IMPL_CALL( aniso(), String() ); }
String          PbrTextureSet::anisoRotation() const                                { return IMPL_CALL( anisoRotation(), String() ); }
String          PbrTextureSet::bump() const                                         { return IMPL_CALL( bump(), String() ); }
String          PbrTextureSet::normal() const                                       { return IMPL_CALL( normal(), String() ); }
String          PbrTextureSet::displacement() const                                 { return IMPL_CALL( displacement(), String() ); }
String          PbrTextureSet::emission() const                                     { return IMPL_CALL( emission(), String() ); }
String          PbrTextureSet::scattering() const                                   { return IMPL_CALL( scattering(), String() ); }
String          PbrTextureSet::ao() const                                           { return IMPL_CALL( ao(), String() ); }

Bool            PbrTextureSet::hasOpacity() const                                   { return !opacity().isEmpty(); }
Bool            PbrTextureSet::hasColor() const                                     { return !color().isEmpty(); }
Bool            PbrTextureSet::hasSpecular() const                                  { return !specular().isEmpty(); }
Bool            PbrTextureSet::hasGlossiness() const                                { return !glossiness().isEmpty(); }
Bool            PbrTextureSet::hasMetallic() const                                  { return !metallic().isEmpty(); }
Bool            PbrTextureSet::hasRoughness() const                                 { return !roughness().isEmpty(); }
Bool            PbrTextureSet::hasAniso() const                                     { return !aniso().isEmpty(); }
Bool            PbrTextureSet::hasAnisoRotation() const                             { return !anisoRotation().isEmpty(); }
Bool            PbrTextureSet::hasBump() const                                      { return !bump().isEmpty(); }
Bool            PbrTextureSet::hasNormal() const                                    { return !normal().isEmpty(); }
Bool            PbrTextureSet::hasDisplacement() const                              { return !displacement().isEmpty(); }
Bool            PbrTextureSet::hasEmission() const                                  { return !emission().isEmpty(); }
Bool            PbrTextureSet::hasScattering() const                                { return !scattering().isEmpty(); }
Bool            PbrTextureSet::hasAO() const                                        { return !ao().isEmpty(); }

//=================================================================================================
// Vectors.
//=================================================================================================

DL_IMPL_REF_VECTOR( Input );
DL_IMPL_REF_VECTOR( Output );
DL_IMPL_REF_VECTOR( Node );
DL_IMPL_REF_VECTOR( Layout );
DL_IMPL_REF_VECTOR( EnumItem );
DL_IMPL_REF_VECTOR( Path );
DL_IMPL_REF_VECTOR( Svg );


//=================================================================================================
// Utilities.
//=================================================================================================

FilmFit filmFitFromString( String str )
{
    str = str.trim();

    if ( str.compareNoCase( "vertical" ) == 0 )
        return FilmFit_Vertical;

    if ( str.compareNoCase( "fill" ) == 0 )
        return FilmFit_Fill;

    if ( str.compareNoCase( "overscan" ) == 0 )
        return FilmFit_Overscan;

    return FilmFit_Horizontal;
}

String filmFitToString( FilmFit ff )
{
    switch ( ff )
    {
    case FilmFit_Vertical:  return "vertical";
    case FilmFit_Fill:      return "fill";
    case FilmFit_Overscan:  return "overscan";
    default:                return "horizontal";
    }
}

String previewPath()
{
    auto dir = String();
    if ( !fs::getBellaPreviewDir( dir, true ) )
    {
        logError( "Unable to find preview BSZ: failed to find/create preview directory." );
        return String();
    }

    auto bsz = dl::Path::join( dir, String::format( "preview-%s.bsz", bellaSdkVersion().toString().buf() ) );
    auto bsx = dl::Path::changeExt( bsz, "bsx" );

    if ( dl::Path::isFile( bsx ) )
    {
        return bsx;
    }

    if ( !dl::Path::isFile( bsz ) )
    {
        auto file = dl::File();
        if ( !file.openWriteBinary( bsz ) )
        {
            logError( "Unable to find preview BSZ: failed to open '%s' for writing.", bsz.buf() );
            return String();
        }

        if ( !file.write( impl_previewBsz(), Compression_None ) )
        {
            logError( "Unable to find preview BSZ: failed to write data to '%s'.", bsz.buf() );
            return String();
        }
    }

    auto scene = Scene();
    scene.loadDefs();

    if ( !scene.read( bsz ) )
    {
        logError( "Unable to find preview BSZ: failed to read '%s'.", bsz.buf() );
        return String();
    }

    return scene.location();
}

} // bella_sdk
} // dl
