From 2291d5d0642b34c810626b37bffcbe1e47d0fab6 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 10 Sep 2014 16:41:42 +0100 Subject: [PATCH 1/7] Added HasBaseType to TypeTraits.h. --- include/IECore/TypeTraits.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/IECore/TypeTraits.h b/include/IECore/TypeTraits.h index 839eab3da6..58ef6ff53a 100644 --- a/include/IECore/TypeTraits.h +++ b/include/IECore/TypeTraits.h @@ -75,6 +75,9 @@ template struct HasVectorValueType< TypedData< std::vector< T > > > template struct HasVectorValueType< GeometricTypedData > > : public boost::true_type {}; template struct HasVectorValueType< const T > : public HasVectorValueType {}; +/// HasBaseType +template struct HasBaseType : public boost::mpl::not_< boost::is_void > {}; + namespace Detail { From 6663cffbc39b06a469919395c31b6417ba7ef51e Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 10 Sep 2014 18:11:45 +0100 Subject: [PATCH 2/7] Added Renderer::ExternalProcedural class. This is a lightweight class for use as a placeholder, specifying to a renderer backend that they must load the true procedural from disk. --- include/IECore/Renderer.h | 27 +++++++++++++++++++ src/IECore/Renderer.cpp | 40 ++++++++++++++++++++++++++++ src/IECorePython/RendererBinding.cpp | 11 ++++++++ 3 files changed, 78 insertions(+) diff --git a/include/IECore/Renderer.h b/include/IECore/Renderer.h index b02ccd66ac..3937153b40 100644 --- a/include/IECore/Renderer.h +++ b/include/IECore/Renderer.h @@ -356,6 +356,33 @@ class Renderer : public RunTimeTyped }; IE_CORE_DECLAREPTR( Procedural ); + /// A placeholder for specifying a procedural which the Renderer + /// must load from a file on disk. + class ExternalProcedural : public Procedural + { + public : + + IE_CORE_DECLAREMEMBERPTR( ExternalProcedural ) + + ExternalProcedural( const std::string &fileName, const Imath::Box3f &bound, const CompoundDataMap ¶meters ); + virtual ~ExternalProcedural(); + + const std::string &fileName() const; + const CompoundDataMap ¶meters() const; + + virtual Imath::Box3f bound() const; + virtual void render( Renderer *renderer ) const; + virtual MurmurHash hash() const; + + private : + + std::string m_fileName; + Imath::Box3f m_bound; + CompoundDataMap m_parameters; + + }; + IE_CORE_DECLAREPTR( ExternalProcedural ); + /// Renders a piece of procedural geometry. virtual void procedural( ProceduralPtr proc ) = 0; diff --git a/src/IECore/Renderer.cpp b/src/IECore/Renderer.cpp index 657d67217d..c96992b40e 100644 --- a/src/IECore/Renderer.cpp +++ b/src/IECore/Renderer.cpp @@ -55,3 +55,43 @@ Renderer::Procedural::Procedural() Renderer::Procedural::~Procedural() { } + +Renderer::ExternalProcedural::ExternalProcedural( const std::string &fileName, const Imath::Box3f &bound, const CompoundDataMap ¶meters ) + : m_fileName( fileName ), m_bound( bound ), m_parameters( parameters ) +{ +} + +Renderer::ExternalProcedural::~ExternalProcedural() +{ +} + +const std::string &Renderer::ExternalProcedural::fileName() const +{ + return m_fileName; +} + +const CompoundDataMap &Renderer::ExternalProcedural::parameters() const +{ + return m_parameters; +} + +Imath::Box3f Renderer::ExternalProcedural::bound() const +{ + return m_bound; +} + +void Renderer::ExternalProcedural::render( Renderer *renderer ) const +{ +} + +MurmurHash Renderer::ExternalProcedural::hash() const +{ + MurmurHash h; + h.append( m_fileName ); + for( CompoundDataMap::const_iterator it = m_parameters.begin(), eIt = m_parameters.end(); it != eIt; ++it ) + { + h.append( it->first ); + it->second->hash( h ); + } + return h; +} diff --git a/src/IECorePython/RendererBinding.cpp b/src/IECorePython/RendererBinding.cpp index 47373ccc13..1456be5aff 100644 --- a/src/IECorePython/RendererBinding.cpp +++ b/src/IECorePython/RendererBinding.cpp @@ -336,6 +336,13 @@ static void editBegin( Renderer &r, const std::string &name, const dict ¶met r.editBegin( name, p ); } +static Renderer::ExternalProceduralPtr externalProceduralConstructor( const char *fileName, const Imath::Box3f &bound, const dict ¶meters ) +{ + CompoundDataMap p; + fillCompoundDataMap( p, parameters ); + return new Renderer::ExternalProcedural( fileName, bound, p ); +} + void bindRenderer() { scope rendererScope = RunTimeTypedClass( "An abstract class to define a renderer" ) @@ -399,6 +406,10 @@ void bindRenderer() .def( "hash", &Renderer::Procedural::hash ) ; + RefCountedClass( "ExternalProcedural" ) + .def( "__init__", make_constructor( externalProceduralConstructor ) ) + ; + } } // namespace IECorePython From 538ecb60495c68a43fc76af8d65f90aae92abb7f Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 10 Sep 2014 18:30:38 +0100 Subject: [PATCH 3/7] Added ExternalProcedural object type. This derives from VisibleRenderable, allowing a representation of an external procedural to be edited, saved, loaded, copied, hashed, grouped etc. It is suitable for passing through a Gaffer graph, and will form the basis for representation external procedurals in Gaffer. When rendered, it is passed to the renderer as a lightweight Renderer::ExternalProcedural. --- include/IECore/ExternalProcedural.h | 79 +++++++++ include/IECore/TypeIds.h | 3 +- .../IECorePython/ExternalProceduralBinding.h | 45 +++++ src/IECore/ExternalProcedural.cpp | 157 ++++++++++++++++++ .../ExternalProceduralBinding.cpp | 67 ++++++++ src/IECorePython/TypeIdBinding.cpp | 1 + src/IECorePythonModule/IECore.cpp | 4 +- test/IECore/All.py | 1 + test/IECore/ExternalProceduralTest.py | 92 ++++++++++ 9 files changed, 447 insertions(+), 2 deletions(-) create mode 100644 include/IECore/ExternalProcedural.h create mode 100644 include/IECorePython/ExternalProceduralBinding.h create mode 100644 src/IECore/ExternalProcedural.cpp create mode 100644 src/IECorePython/ExternalProceduralBinding.cpp create mode 100644 test/IECore/ExternalProceduralTest.py diff --git a/include/IECore/ExternalProcedural.h b/include/IECore/ExternalProcedural.h new file mode 100644 index 0000000000..d724085a37 --- /dev/null +++ b/include/IECore/ExternalProcedural.h @@ -0,0 +1,79 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014, Image Engine Design Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Image Engine Design nor the names of any +// other contributors to this software may be used to endorse or +// promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////// + +#ifndef IECORE_EXTERNALPROCEDURAL_H +#define IECORE_EXTERNALPROCEDURAL_H + +#include "IECore/VisibleRenderable.h" + +namespace IECore +{ + +/// \ingroup renderingGroup +/// \ingroup coreGroup +class ExternalProcedural : public VisibleRenderable +{ + + public : + + ExternalProcedural( const std::string &fileName = "", const Imath::Box3f &bound = Imath::Box3f(), const CompoundData *parameters = NULL ); + virtual ~ExternalProcedural(); + + IE_CORE_DECLAREOBJECT( ExternalProcedural, VisibleRenderable ); + + void setFileName( const std::string &fileName ); + const std::string &getFileName() const; + + void setBound( const Imath::Box3f &bound ); + const Imath::Box3f &getBound() const; + + CompoundData *parameters(); + const CompoundData *parameters() const; + + virtual void render( Renderer *renderer ) const; + virtual Imath::Box3f bound() const; + + private : + + std::string m_fileName; + Imath::Box3f m_bound; + CompoundDataPtr m_parameters; + +}; + +IE_CORE_DECLAREPTR( ExternalProcedural ); + +} // namespace IECore + +#endif // IECORE_EXTERNALPROCEDURAL_H diff --git a/include/IECore/TypeIds.h b/include/IECore/TypeIds.h index 324ca5afc6..0232475e5e 100644 --- a/include/IECore/TypeIds.h +++ b/include/IECore/TypeIds.h @@ -438,7 +438,8 @@ enum TypeId TransferSmoothSkinningWeightsOpTypeId = 390, EXRDeepImageReaderTypeId = 391, EXRDeepImageWriterTypeId = 392, - + ExternalProceduralTypeId = 393, + // Remember to update TypeIdBinding.cpp !!! // If we ever get this far then the core library is too big. diff --git a/include/IECorePython/ExternalProceduralBinding.h b/include/IECorePython/ExternalProceduralBinding.h new file mode 100644 index 0000000000..8d3ee921e3 --- /dev/null +++ b/include/IECorePython/ExternalProceduralBinding.h @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014, Image Engine Design Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Image Engine Design nor the names of any +// other contributors to this software may be used to endorse or +// promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////// + +#ifndef IECOREPYTHON_EXTERNALPROCEDURALBINDING_H +#define IECOREPYTHON_EXTERNALPROCEDURALBINDING_H + +namespace IECorePython +{ + +void bindExternalProcedural(); + +} // namespace IECorePython + +#endif // IECOREPYTHON_EXTERNALPROCEDURALBINDING_H diff --git a/src/IECore/ExternalProcedural.cpp b/src/IECore/ExternalProcedural.cpp new file mode 100644 index 0000000000..684158e5a4 --- /dev/null +++ b/src/IECore/ExternalProcedural.cpp @@ -0,0 +1,157 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014, Image Engine Design Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Image Engine Design nor the names of any +// other contributors to this software may be used to endorse or +// promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////// + +#include "IECore/ExternalProcedural.h" +#include "IECore/Renderer.h" + +using namespace IECore; + +IE_CORE_DEFINEOBJECTTYPEDESCRIPTION( ExternalProcedural ); + +static const IndexedIO::EntryID g_fileNameEntry( "fileName" ); +static const IndexedIO::EntryID g_boundEntry( "bound" ); +static const IndexedIO::EntryID g_parametersEntry( "parameters" ); + +static const unsigned int g_ioVersion = 0; + +ExternalProcedural::ExternalProcedural( const std::string &fileName, const Imath::Box3f &bound, const CompoundData *parameters ) + : m_fileName( fileName ), m_bound( bound ), m_parameters( parameters ? parameters->copy() : new CompoundData ) +{ +} + +ExternalProcedural::~ExternalProcedural() +{ +} + +void ExternalProcedural::setFileName( const std::string &fileName ) +{ + m_fileName = fileName; +} + +const std::string &ExternalProcedural::getFileName() const +{ + return m_fileName; +} + +void ExternalProcedural::setBound( const Imath::Box3f &bound ) +{ + m_bound = bound; +} + +const Imath::Box3f &ExternalProcedural::getBound() const +{ + return m_bound; +} + +CompoundData *ExternalProcedural::parameters() +{ + return m_parameters.get(); +} + +const CompoundData *ExternalProcedural::parameters() const +{ + return m_parameters.get(); +} + +void ExternalProcedural::render( Renderer *renderer ) const +{ + renderer->procedural( new Renderer::ExternalProcedural( m_fileName, m_bound, m_parameters->readable() ) ); +} + +Imath::Box3f ExternalProcedural::bound() const +{ + return m_bound; +} + +void ExternalProcedural::copyFrom( const Object *other, CopyContext *context ) +{ + VisibleRenderable::copyFrom( other, context ); + const ExternalProcedural *tOther = static_cast( other ); + m_fileName = tOther->m_fileName; + m_bound = tOther->m_bound; + m_parameters = tOther->m_parameters->copy(); +} + +void ExternalProcedural::save( SaveContext *context ) const +{ + VisibleRenderable::save( context ); + IndexedIOPtr container = context->container( staticTypeName(), g_ioVersion ); + container->write( g_fileNameEntry, m_fileName ); + container->write( g_boundEntry, m_bound.min.getValue(), 6 ); + context->save( m_parameters.get(), container.get(), g_parametersEntry ); +} + +void ExternalProcedural::load( LoadContextPtr context ) +{ + VisibleRenderable::load( context ); + unsigned int v = g_ioVersion; + ConstIndexedIOPtr container = context->container( staticTypeName(), v ); + container->read( g_fileNameEntry, m_fileName ); + + float *b = m_bound.min.getValue(); + container->read( g_boundEntry, b, 6 ); + + m_parameters = context->load( container.get(), g_parametersEntry ); +} + +bool ExternalProcedural::isEqualTo( const Object *other ) const +{ + if( !VisibleRenderable::isEqualTo( other ) ) + { + return false; + } + + const ExternalProcedural *tOther = static_cast( other ); + + return + m_fileName == tOther->m_fileName && + m_bound == tOther->m_bound && + m_parameters->isEqualTo( tOther->m_parameters.get() ); +} + +void ExternalProcedural::memoryUsage( Object::MemoryAccumulator &a ) const +{ + VisibleRenderable::memoryUsage( a ); + a.accumulate( m_fileName.capacity() ); + a.accumulate( sizeof( m_bound ) ); + a.accumulate( m_parameters.get() ); +} + +void ExternalProcedural::hash( MurmurHash &h ) const +{ + VisibleRenderable::hash( h ); + h.append( m_fileName ); + h.append( m_bound ); + m_parameters->hash( h ); +} diff --git a/src/IECorePython/ExternalProceduralBinding.cpp b/src/IECorePython/ExternalProceduralBinding.cpp new file mode 100644 index 0000000000..7879be4743 --- /dev/null +++ b/src/IECorePython/ExternalProceduralBinding.cpp @@ -0,0 +1,67 @@ +////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014, Image Engine Design Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Image Engine Design nor the names of any +// other contributors to this software may be used to endorse or +// promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////// + +#include "boost/python.hpp" + +#include "IECore/ExternalProcedural.h" + +#include "IECorePython/ExternalProceduralBinding.h" +#include "IECorePython/RunTimeTypedBinding.h" + +using namespace boost::python; +using namespace IECore; + +namespace IECorePython +{ + +void bindExternalProcedural() +{ + RunTimeTypedClass() + .def( init( + ( + arg_( "fileName" ) = "", + arg_( "bound" ) = Imath::Box3f(), + arg_( "parameters" ) = object() + ) + ) + ) + .def( "setFileName", &ExternalProcedural::setFileName ) + .def( "getFileName", &ExternalProcedural::getFileName, return_value_policy() ) + .def( "setBound", &ExternalProcedural::setBound ) + .def( "getBound", &ExternalProcedural::getBound, return_value_policy() ) + .def( "parameters", (CompoundData * (ExternalProcedural::*)())&ExternalProcedural::parameters, return_value_policy() ) + ; +} + +} // namespace IECorePython diff --git a/src/IECorePython/TypeIdBinding.cpp b/src/IECorePython/TypeIdBinding.cpp index d2e7c62270..0b183d3b30 100644 --- a/src/IECorePython/TypeIdBinding.cpp +++ b/src/IECorePython/TypeIdBinding.cpp @@ -469,6 +469,7 @@ void bindTypeId() .value( "TransferSmoothSkinningWeightsOp", TransferSmoothSkinningWeightsOpTypeId ) .value( "EXRDeepImageReader", EXRDeepImageReaderTypeId ) .value( "EXRDeepImageWriter", EXRDeepImageWriterTypeId ) + .value( "ExternalProcedural", ExternalProceduralTypeId ) ; converter::registry::push_back( diff --git a/src/IECorePythonModule/IECore.cpp b/src/IECorePythonModule/IECore.cpp index b05c98a4c7..c77a03544a 100644 --- a/src/IECorePythonModule/IECore.cpp +++ b/src/IECorePythonModule/IECore.cpp @@ -307,6 +307,7 @@ #include "IECorePython/ObjectPoolBinding.h" #include "IECorePython/EXRDeepImageReaderBinding.h" #include "IECorePython/EXRDeepImageWriterBinding.h" +#include "IECorePython/ExternalProceduralBinding.h" #include "IECore/IECore.h" using namespace IECorePython; @@ -614,7 +615,8 @@ BOOST_PYTHON_MODULE(_IECore) bindStandardRadialLensModel(); bindLensDistortOp(); bindObjectPool(); - + bindExternalProcedural(); + #ifdef IECORE_WITH_DEEPEXR bindEXRDeepImageReader(); diff --git a/test/IECore/All.py b/test/IECore/All.py index a19d6b7aa3..2e76e1a677 100644 --- a/test/IECore/All.py +++ b/test/IECore/All.py @@ -259,6 +259,7 @@ from LensDistortOpTest import LensDistortOpTest from ObjectPoolTest import ObjectPoolTest from RefCountedTest import RefCountedTest +from ExternalProceduralTest import ExternalProceduralTest if IECore.withDeepEXR() : from EXRDeepImageReaderTest import EXRDeepImageReaderTest diff --git a/test/IECore/ExternalProceduralTest.py b/test/IECore/ExternalProceduralTest.py new file mode 100644 index 0000000000..6565ad4539 --- /dev/null +++ b/test/IECore/ExternalProceduralTest.py @@ -0,0 +1,92 @@ +########################################################################## +# +# Copyright (c) 2014, Image Engine Design Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Image Engine Design nor the names of any +# other contributors to this software may be used to endorse or +# promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################## + +import unittest + +import IECore + +class ExternalProceduralTest( unittest.TestCase ) : + + def test( self ) : + + p = IECore.ExternalProcedural( + "yeti.so", + IECore.Box3f( IECore.V3f( 1, 2, 3 ), IECore.V3f( 4, 5, 6 ) ), + IECore.CompoundData( { + "one" : 1, + "two" : 2, + } ) + ) + + self.assertEqual( p.getFileName(), "yeti.so" ) + self.assertEqual( p.getBound(), IECore.Box3f( IECore.V3f( 1, 2, 3 ), IECore.V3f( 4, 5, 6 ) ) ) + self.assertEqual( + p.parameters(), + IECore.CompoundData( { + "one" : 1, + "two" : 2, + } ) + ) + + p2 = p.copy() + + self.assertEqual( p2.getFileName(), "yeti.so" ) + self.assertEqual( p2.getBound(), IECore.Box3f( IECore.V3f( 1, 2, 3 ), IECore.V3f( 4, 5, 6 ) ) ) + self.assertEqual( + p2.parameters(), + IECore.CompoundData( { + "one" : 1, + "two" : 2, + } ) + ) + + self.assertEqual( p, p2 ) + self.assertEqual( p.hash(), p2.hash() ) + + p2.setFileName( "yeti2.so" ) + self.assertEqual( p2.getFileName(), "yeti2.so" ) + + self.assertNotEqual( p, p2 ) + self.assertNotEqual( p.hash(), p2.hash() ) + + m = IECore.MemoryIndexedIO( IECore.CharVectorData(), [], IECore.IndexedIO.OpenMode.Append ) + + p.save( m, "test" ) + + p3 = IECore.Object.load( m, "test" ) + + self.assertEqual( p3, p ) + +if __name__ == "__main__": + unittest.main() From 49af02cbc70ad16156e5a1476ab9cbb2a79d07ae Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 10 Sep 2014 19:32:39 +0100 Subject: [PATCH 4/7] Added external procedural support to IECoreRI::Renderer. ProcDynamicLoad and ProcDelayedReadArchive are supported but there is no support for ProcRunProgram yet. Parameters may be passed to ProcDynamicLoad either from a single ri:data string parameter, or from arbitrary parameters which are serialised automatically into a command line style. This seems a reasonable way of dealing with the "all in one string" limitations of RenderMan procedural parameters, and since Yeti uses a command line style parser, arbitrary parameters should be useable with at least one third party procedural we're interested in. --- include/IECoreRI/Renderer.h | 10 ++ .../IECoreRI/private/RendererImplementation.h | 3 + src/IECoreRI/RendererImplementation.cpp | 109 ++++++++++++++++-- test/IECoreRI/Renderer.py | 52 +++++++++ 4 files changed, 166 insertions(+), 8 deletions(-) diff --git a/include/IECoreRI/Renderer.h b/include/IECoreRI/Renderer.h index 7156f4b5eb..01fec02e71 100644 --- a/include/IECoreRI/Renderer.h +++ b/include/IECoreRI/Renderer.h @@ -256,6 +256,16 @@ class Renderer : public IECore::Renderer virtual void geometry( const std::string &type, const IECore::CompoundDataMap &topology, const IECore::PrimitiveVariableMap &primVars ); + /// ExternalProcedurals are treated as DelayedReadArchives if their filename ends with ".rib" + /// and as DynamicLoad procedurals otherwise. Because RenderMan has very poor support for passing + /// parameters to DynamicLoad procedurals (you can only pass a single string), the arbitrary parameters + /// of the ExternalProcedural are treated as follows : + /// + /// - If an "ri:data" StringData parameter exists, it is passed verbatim to the procedural. This + /// allows procedurals which require data in a specific format to be supported. + /// - All other parameters are serialised in a command line "--name value" style and concatenated. + /// This allows the convenience of arbitrary typed parameters provided that the procedural itself + /// uses a command line style parser. virtual void procedural( IECore::Renderer::ProceduralPtr proc ); virtual void instanceBegin( const std::string &name, const IECore::CompoundDataMap ¶meters ); diff --git a/include/IECoreRI/private/RendererImplementation.h b/include/IECoreRI/private/RendererImplementation.h index 5a560d22b2..1d1cda8b64 100644 --- a/include/IECoreRI/private/RendererImplementation.h +++ b/include/IECoreRI/private/RendererImplementation.h @@ -255,6 +255,9 @@ class RendererImplementation : public IECore::Renderer // This constructor is used to create a child renderer in procSubdivide() RendererImplementation( SharedData::Ptr sharedData, IECore::CompoundDataPtr options ); + void standardProcedural( Procedural *proc ); + void externalProcedural( ExternalProcedural *proc ); + static void procSubdivide( void *data, float detail ); static void procFree( void *data ); diff --git a/src/IECoreRI/RendererImplementation.cpp b/src/IECoreRI/RendererImplementation.cpp index 31d728f0f8..aa06b883f3 100644 --- a/src/IECoreRI/RendererImplementation.cpp +++ b/src/IECoreRI/RendererImplementation.cpp @@ -46,8 +46,11 @@ #include "IECore/MatrixTransform.h" #include "IECore/Group.h" #include "IECore/MurmurHash.h" +#include "IECore/DespatchTypedData.h" +#include "IECore/TypeTraits.h" #include "boost/algorithm/string/case_conv.hpp" +#include "boost/algorithm/string/predicate.hpp" #include "boost/format.hpp" #include @@ -1898,21 +1901,29 @@ void IECoreRI::RendererImplementation::emitPatchMeshPrimitive( const IECore::Pat void IECoreRI::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc ) { - ScopedContext scopedContext( m_context ); - Imath::Box3f bound = proc->bound(); if( bound.isEmpty() ) { return; } + ScopedContext scopedContext( m_context ); + + if( ExternalProcedural *externalProc = dynamic_cast( proc.get() ) ) + { + externalProcedural( externalProc ); + } + else + { + standardProcedural( proc.get() ); + } +} + +void IECoreRI::RendererImplementation::standardProcedural( Procedural *proc ) +{ + RtBound riBound; - riBound[0] = bound.min.x; - riBound[1] = bound.max.x; - riBound[2] = bound.min.y; - riBound[3] = bound.max.y; - riBound[4] = bound.min.z; - riBound[5] = bound.max.z; + convert( proc->bound(), riBound ); ProceduralData *data = new ProceduralData; data->procedural = proc; @@ -1948,6 +1959,88 @@ void IECoreRI::RendererImplementation::procedural( IECore::Renderer::ProceduralP } +namespace +{ + +struct Serialiser +{ + + typedef void ReturnType; + + Serialiser( std::ostringstream &stringStream ) + : m_stringStream( stringStream ) + { + } + + template + void operator() ( const T *data ) + { + const typename T::BaseType *b = data->baseReadable(); + for( size_t i = 0, e = data->baseSize(); i < e; ++i ) + { + m_stringStream << b[i]; + if( i != e - 1 ) + { + m_stringStream << " "; + } + } + } + + private : + + std::ostringstream &m_stringStream; + +}; + +} // namespace + +void IECoreRI::RendererImplementation::externalProcedural( ExternalProcedural *proc ) +{ + RtBound riBound; + convert( proc->bound(), riBound ); + + if( boost::algorithm::ends_with( proc->fileName(), ".rib" ) ) + { + // RiProcDelayedReadArchive + const char **data = (const char **)malloc( sizeof( char * ) ); + data[0] = proc->fileName().c_str(); + RiProcedural( data, riBound, RiProcDelayedReadArchive, RiProcFree ); + } + else + { + // RiProcDynamicLoad + const CompoundDataMap &parms = proc->parameters(); + + ostringstream dataStringStream; + CompoundDataMap::const_iterator it = parms.find( "ri:data" ); + if( it != parms.end() ) + { + if( const StringData *s = IECore::runTimeCast( it->second.get() ) ) + { + dataStringStream << s->readable(); + } + } + + Serialiser serialiser( dataStringStream ); + for( CompoundDataMap::const_iterator it = parms.begin(), eIt = parms.end(); it != eIt; ++it ) + { + if( it->first == "ri:data" ) + { + continue; + } + dataStringStream << " --" << it->first << " "; + IECore::despatchTypedData( it->second.get(), serialiser ); + } + + const std::string dataString = dataStringStream.str(); + + const char **data = (const char **)malloc( sizeof( char * ) * 2 ); + data[0] = proc->fileName().c_str(); + data[1] = dataString.c_str(); + RiProcedural( data, riBound, RiProcDynamicLoad, RiProcFree ); + } +} + void IECoreRI::RendererImplementation::procSubdivide( void *data, float detail ) { ProceduralData *proceduralData = reinterpret_cast( data ); diff --git a/test/IECoreRI/Renderer.py b/test/IECoreRI/Renderer.py index 876ba7ea52..c3661201c6 100644 --- a/test/IECoreRI/Renderer.py +++ b/test/IECoreRI/Renderer.py @@ -616,6 +616,58 @@ def testFrameBlock( self ) : self.assertTrue( "FrameBegin 10" in rib ) self.assertTrue( "FrameEnd" in rib ) + def testDynamicLoadProcedural( self ) : + + r = IECoreRI.Renderer( "test/IECoreRI/output/test.rib" ) + with WorldBlock( r ) : + + r.procedural( + r.ExternalProcedural( + "test.so", + Box3f( + V3f( 1, 2, 3 ), + V3f( 4, 5, 6 ) + ), + { + "ri:data" : "blah blah blah", + "colorParm" : Color3f( 1, 2, 3 ), + "stringParm" : "test", + "floatParm" : 1.5, + "intParm" : 2, + } + ) + ) + + rib = "".join( file( "test/IECoreRI/output/test.rib" ).readlines() ) + self.assertTrue( "Procedural \"DynamicLoad\"" in rib ) + self.assertTrue( "test.so" in rib ) + self.assertTrue( "\"blah blah blah" in rib ) + self.assertTrue( "--colorParm 1 2 3" in rib ) + self.assertTrue( "--stringParm test" in rib ) + self.assertTrue( "--floatParm 1.5" in rib ) + self.assertTrue( "--intParm 2" in rib ) + self.assertTrue( "[ 1 4 2 5 3 6 ]" in rib ) + + def testDelayedReadArchive( self ) : + + r = IECoreRI.Renderer( "test/IECoreRI/output/test.rib" ) + with WorldBlock( r ) : + + r.procedural( + r.ExternalProcedural( + "testArchive.rib", + Box3f( + V3f( 1, 2, 3 ), + V3f( 4, 5, 6 ) + ), + {} + ) + ) + + rib = "".join( file( "test/IECoreRI/output/test.rib" ).readlines() ) + self.assertTrue( "Procedural \"DelayedReadArchive\" [ \"testArchive.rib\" ]" in rib ) + self.assertTrue( "[ 1 4 2 5 3 6 ]" in rib ) + def tearDown( self ) : IECoreRI.TestCase.tearDown( self ) From 914035177533b52e5b22ad62e4b251a14f3f21a3 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 11 Sep 2014 13:54:31 +0100 Subject: [PATCH 5/7] Made ExternalProcedurals draw as wireframe boxes in IECoreGL::Renderer. --- src/IECoreGL/Renderer.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/IECoreGL/Renderer.cpp b/src/IECoreGL/Renderer.cpp index 0cf467a1a1..4b635934af 100644 --- a/src/IECoreGL/Renderer.cpp +++ b/src/IECoreGL/Renderer.cpp @@ -1781,7 +1781,19 @@ void IECoreGL::Renderer::procedural( IECore::Renderer::ProceduralPtr proc ) } if ( m_data->checkCulling( proc.get() ) ) { - m_data->implementation->addProcedural( proc, this ); + if( ExternalProcedural *externalProcedural = dynamic_cast( proc.get() ) ) + { + attributeBegin(); + setAttribute( "gl:primitive:wireframe", new BoolData( true ) ); + setAttribute( "gl:primitive:solid", new BoolData( false ) ); + setAttribute( "gl:curvesPrimitive:useGLLines", new BoolData( true ) ); + IECore::CurvesPrimitive::createBox( externalProcedural->bound() )->render( this ); + attributeEnd(); + } + else + { + m_data->implementation->addProcedural( proc, this ); + } } } From 709660300de3852d00c3cd130986559b7dcb322a Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 11 Sep 2014 13:57:34 +0100 Subject: [PATCH 6/7] Fixed include order in IECoreGL/Renderer.cpp. --- src/IECoreGL/Renderer.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/IECoreGL/Renderer.cpp b/src/IECoreGL/Renderer.cpp index 4b635934af..817506ef52 100644 --- a/src/IECoreGL/Renderer.cpp +++ b/src/IECoreGL/Renderer.cpp @@ -33,6 +33,25 @@ // ////////////////////////////////////////////////////////////////////////// +#include + +#include "tbb/mutex.h" + +#include "OpenEXR/ImathBoxAlgo.h" + +#include "IECore/MessageHandler.h" +#include "IECore/SimpleTypedData.h" +#include "IECore/BoxOps.h" +#include "IECore/Camera.h" +#include "IECore/Transform.h" +#include "IECore/MatrixAlgo.h" +#include "IECore/MeshPrimitive.h" +#include "IECore/MeshNormalsOp.h" +#include "IECore/SplineData.h" +#include "IECore/SplineToImage.h" +#include "IECore/CurvesPrimitive.h" +#include "IECore/PointsPrimitive.h" + #include "IECoreGL/Renderer.h" #include "IECoreGL/State.h" #include "IECoreGL/PointsPrimitive.h" @@ -64,25 +83,6 @@ #include "IECoreGL/DiskPrimitive.h" #include "IECoreGL/CachedConverter.h" -#include "IECore/MessageHandler.h" -#include "IECore/SimpleTypedData.h" -#include "IECore/BoxOps.h" -#include "IECore/Camera.h" -#include "IECore/Transform.h" -#include "IECore/MatrixAlgo.h" -#include "IECore/MeshPrimitive.h" -#include "IECore/MeshNormalsOp.h" -#include "IECore/SplineData.h" -#include "IECore/SplineToImage.h" -#include "IECore/CurvesPrimitive.h" -#include "IECore/PointsPrimitive.h" - -#include "OpenEXR/ImathBoxAlgo.h" - -#include "tbb/mutex.h" - -#include - using namespace IECore; using namespace IECoreGL; using namespace Imath; From b6553ae489058fe2706ff670f51580f648a90826 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 11 Sep 2014 14:48:01 +0100 Subject: [PATCH 7/7] Added support for external procedurals to IECoreArnold::Renderer. --- .../IECoreArnold/RendererImplementation.cpp | 35 +++++++++++------ .../test/IECoreArnold/RendererTest.py | 39 +++++++++++++++++++ 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 5bd74bdf0a..ad83778dee 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -638,22 +638,33 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu return; } - // we have to transform the bound, as we're not applying the current transform to the - // procedural node, but instead applying absolute transforms to the shapes the procedural - // generates. - bound = transform( bound, m_transformStack.top() ); - AtNode *procedural = AiNode( "procedural" ); - AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z ); - AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z ); + + if( ExternalProcedural *externalProc = dynamic_cast( proc.get() ) ) + { + AiNodeSetStr( procedural, "dso", externalProc->fileName().c_str() ); + ToArnoldConverter::setParameters( procedural, externalProc->parameters() ); + applyTransformToNode( procedural ); + } + else + { + + // we have to transform the bound, as we're not applying the current transform to the + // procedural node, but instead applying absolute transforms to the shapes the procedural + // generates. + bound = transform( bound, m_transformStack.top() ); - AiNodeSetPtr( procedural, "funcptr", (void *)procLoader ); + AiNodeSetPtr( procedural, "funcptr", (void *)procLoader ); - ProceduralData *data = new ProceduralData; - data->procedural = proc; - data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); + ProceduralData *data = new ProceduralData; + data->procedural = proc; + data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); - AiNodeSetPtr( procedural, "userptr", data ); + AiNodeSetPtr( procedural, "userptr", data ); + } + + AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z ); + AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z ); // we call addNode() rather than addShape() as we don't want to apply transforms and // shaders and attributes to procedurals. if we do, they override the things we set diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index e94cba65ac..14950eb0df 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -682,7 +682,46 @@ def testLight( self ) : self.assertTrue( result.floatPrimVar( e.R() ) > 0.2 ) self.assertAlmostEqual( result.floatPrimVar( e.R() ) * 0.5, result.floatPrimVar( e.G() ) ) self.assertAlmostEqual( result.floatPrimVar( e.R() ) * 0.25, result.floatPrimVar( e.B() ) ) + + def testExternalProcedural( self ) : + + r = IECoreArnold.Renderer( self.__assFileName ) + + with IECore.WorldBlock( r ) : + r.procedural( + r.ExternalProcedural( + "test.so", + IECore.Box3f( + IECore.V3f( 1, 2, 3 ), + IECore.V3f( 4, 5, 6 ) + ), + { + "colorParm" : IECore.Color3f( 1, 2, 3 ), + "stringParm" : "test", + "floatParm" : 1.5, + "intParm" : 2, + } + ) + ) + + + ass = "".join( file( self.__assFileName ).readlines() ) + print ass + + self.assertTrue( "procedural" in ass ) + self.assertTrue( "min 1 2 3" in ass ) + self.assertTrue( "max 4 5 6" in ass ) + self.assertTrue( "dso \"test.so\"" in ass ) + self.assertTrue( "declare stringParm constant STRING" in ass ) + self.assertTrue( "declare floatParm constant FLOAT" in ass ) + self.assertTrue( "declare intParm constant INT" in ass ) + self.assertTrue( "declare colorParm constant RGB" in ass ) + self.assertTrue( "stringParm \"test\"" in ass ) + self.assertTrue( "floatParm 1.5" in ass ) + self.assertTrue( "intParm 2" in ass ) + self.assertTrue( "colorParm 1 2 3" in ass ) + def tearDown( self ) : for f in [