Skip to content

Commit 4ae3ab4

Browse files
committed
Merge pull request #466 from johnhaddon/proceduralBounds
Procedurals without bounds
2 parents 80b4104 + 98297ef commit 4ae3ab4

8 files changed

Lines changed: 146 additions & 34 deletions

File tree

contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,11 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu
671671
// we have to transform the bound, as we're not applying the current transform to the
672672
// procedural node, but instead applying absolute transforms to the shapes the procedural
673673
// generates.
674-
bound = transform( bound, m_transformStack.top() );
675-
674+
if( bound != Procedural::noBound )
675+
{
676+
bound = transform( bound, m_transformStack.top() );
677+
}
678+
676679
AiNodeSetPtr( procedural, "funcptr", (void *)procLoader );
677680

678681
ProceduralData *data = new ProceduralData;
@@ -682,9 +685,17 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu
682685
AiNodeSetPtr( procedural, "userptr", data );
683686
}
684687

685-
AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z );
686-
AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z );
687-
688+
if( bound != Procedural::noBound )
689+
{
690+
AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z );
691+
AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z );
692+
}
693+
else
694+
{
695+
// No bound available - expand procedural immediately.
696+
AiNodeSetBool( procedural, "load_at_init", true );
697+
}
698+
688699
// we call addNode() rather than addShape() as we don't want to apply transforms and
689700
// shaders and attributes to procedurals. if we do, they override the things we set
690701
// on the nodes generated by the procedurals, which is frankly useless.

contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
from __future__ import with_statement
3636

37+
import re
3738
import arnold
3839
import unittest
3940

@@ -222,6 +223,28 @@ def hash( self ):
222223
r.procedural( EmptyProcedural() )
223224

224225
self.failIf( "ignoring parameter max" in self.__arnoldMessages )
225-
226+
227+
def testNoBound( self ) :
228+
229+
r = IECoreArnold.Renderer( "/tmp/test.ass" )
230+
231+
with IECore.WorldBlock( r ) :
232+
233+
r.procedural(
234+
r.ExternalProcedural(
235+
"test.so",
236+
r.Procedural.noBound,
237+
{}
238+
)
239+
)
240+
241+
l = "".join( open( "/tmp/test.ass" ).readlines() )
242+
243+
self.assertTrue( "procedural" in l )
244+
self.assertFalse( "inf" in l )
245+
self.assertFalse( re.search( r"\bmin\b", l ) )
246+
self.assertFalse( re.search( r"\bmax\b", l ) )
247+
self.assertTrue( "load_at_init" in l )
248+
226249
if __name__ == "__main__":
227250
unittest.main()

include/IECore/Renderer.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,13 @@ class IECORE_API Renderer : public RunTimeTyped
357357
/// and this feature will be disabled.
358358
virtual MurmurHash hash() const = 0;
359359

360+
/// Sentinel value which may be returned by bound()
361+
/// implementations if a bound is not available, or
362+
/// computing one would be prohibitively expensive.
363+
/// Renderers should expand such procedurals
364+
/// unconditionally.
365+
static const Imath::Box3f noBound;
366+
360367
};
361368
IE_CORE_DECLAREPTR( Procedural );
362369

@@ -428,7 +435,7 @@ class IECORE_API Renderer : public RunTimeTyped
428435
/// Ends the current scene edit.
429436
virtual void editEnd() = 0;
430437
//@}
431-
438+
432439
};
433440

434441
}

src/IECore/Renderer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@
3636
#include "IECore/CompoundObject.h"
3737
#include "IECore/CompoundParameter.h"
3838

39+
using namespace Imath;
3940
using namespace IECore;
4041

42+
const Box3f Renderer::Procedural::noBound( V3f( -std::numeric_limits<float>::infinity() ), V3f( std::numeric_limits<float>::infinity() ) );
43+
4144
IE_CORE_DEFINERUNTIMETYPED( Renderer );
4245

4346
Renderer::Renderer()

src/IECoreGL/Renderer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ struct IECoreGL::Renderer::MemberData
229229
}
230230

231231
Imath::Box3f b = p->bound();
232+
if( b == Procedural::noBound )
233+
{
234+
return true;
235+
}
236+
232237
switch( implementation->getState<CullingSpaceStateComponent>()->value() )
233238
{
234239
case ObjectSpace :

src/IECorePython/RendererBinding.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class ProceduralWrap : public Renderer::Procedural, public Wrapper<Renderer::Pro
8787
}
8888
return Imath::Box3f(); // empty
8989
}
90-
90+
9191
virtual void render( Renderer *r ) const
9292
{
9393
ScopedGILLock gilLock;
@@ -118,7 +118,7 @@ class ProceduralWrap : public Renderer::Procedural, public Wrapper<Renderer::Pro
118118
msg( Msg::Error, "ProceduralWrap::render", "Caught unknown exception" );
119119
}
120120
}
121-
121+
122122
virtual MurmurHash hash() const
123123
{
124124
ScopedGILLock gilLock;
@@ -394,7 +394,7 @@ void bindRenderer()
394394
.def("instance", &Renderer::instance)
395395

396396
.def("command", &command)
397-
397+
398398
.def("editBegin", &editBegin)
399399
.def("editEnd", &Renderer::editEnd)
400400
;
@@ -404,6 +404,7 @@ void bindRenderer()
404404
.def( "bound", &Renderer::Procedural::bound )
405405
.def( "render", &Renderer::Procedural::render )
406406
.def( "hash", &Renderer::Procedural::hash )
407+
.def_readonly( "noBound", &Renderer::Procedural::noBound )
407408
;
408409

409410
RefCountedClass<Renderer::ExternalProcedural, Renderer::Procedural>( "ExternalProcedural" )

src/IECoreRI/RendererImplementation.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,19 +1940,13 @@ void IECoreRI::RendererImplementation::emitPatchMeshPrimitive( const IECore::Pat
19401940
pv.n(), pv.tokens(), pv.values()
19411941
);
19421942
}
1943-
1943+
19441944
/////////////////////////////////////////////////////////////////////////////////////////
19451945
// procedurals
19461946
/////////////////////////////////////////////////////////////////////////////////////////
19471947

19481948
void IECoreRI::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc )
19491949
{
1950-
Imath::Box3f bound = proc->bound();
1951-
if( bound.isEmpty() )
1952-
{
1953-
return;
1954-
}
1955-
19561950
ScopedContext scopedContext( m_context );
19571951

19581952
if( ExternalProcedural *externalProc = dynamic_cast<ExternalProcedural *>( proc.get() ) )
@@ -1967,9 +1961,14 @@ void IECoreRI::RendererImplementation::procedural( IECore::Renderer::ProceduralP
19671961

19681962
void IECoreRI::RendererImplementation::standardProcedural( Procedural *proc )
19691963
{
1964+
const Imath::Box3f bound = proc->bound();
1965+
if( bound.isEmpty() )
1966+
{
1967+
return;
1968+
}
19701969

19711970
RtBound riBound;
1972-
convert( proc->bound(), riBound );
1971+
convert( bound, riBound );
19731972

19741973
ProceduralData *data = new ProceduralData;
19751974
data->procedural = proc;
@@ -2050,8 +2049,14 @@ void dynamicLoadFree( void *voidData )
20502049

20512050
void IECoreRI::RendererImplementation::externalProcedural( ExternalProcedural *proc )
20522051
{
2052+
const Imath::Box3f bound = proc->bound();
2053+
if( bound.isEmpty() )
2054+
{
2055+
return;
2056+
}
2057+
20532058
RtBound riBound;
2054-
convert( proc->bound(), riBound );
2059+
convert( bound, riBound );
20552060

20562061
if( boost::algorithm::ends_with( proc->fileName(), ".rib" ) )
20572062
{

test/IECoreRI/Renderer.py

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,51 @@
3939
import os
4040

4141
from IECore import *
42+
import IECore
4243
import IECoreRI
4344

4445
class SimpleProcedural( Renderer.Procedural ) :
4546

46-
def __init__( self, scale ) :
47+
def __init__( self, scale, computeBound = True ) :
4748

4849
Renderer.Procedural.__init__( self )
4950
self.__scale = scale
51+
self.__computeBound = computeBound
5052
self.__t = StringData( "hello" )
5153
self.__c = CompoundData()
5254
self.__c["a"] = IntData( 4 )
5355

56+
self.numBoundCalls = 0
57+
self.numRenderCalls = 0
58+
5459
def bound( self ) :
5560

56-
return Box3f( V3f( -self.__scale ), V3f( self.__scale ) )
61+
self.numBoundCalls += 1
62+
63+
if self.__computeBound :
64+
return Box3f( V3f( -self.__scale ), V3f( self.__scale ) )
65+
else :
66+
return self.noBound
5767

5868
def render( self, renderer ) :
5969

70+
self.numRenderCalls += 1
6071
self.rendererTypeName = renderer.typeName()
6172
self.rendererTypeId = renderer.typeId()
6273

63-
renderer.transformBegin()
74+
with IECore.TransformBlock( renderer ) :
6475

65-
m = M44f()
66-
m.scale( V3f( self.__scale ) )
67-
renderer.concatTransform( m )
76+
m = M44f()
77+
m.scale( V3f( self.__scale ) )
78+
renderer.concatTransform( m )
79+
80+
if self.__computeBound :
81+
renderer.procedural( SimpleProcedural( 1, False ) )
82+
else :
83+
renderer.sphere( 1, -1, 1, 360, {} )
6884

69-
renderer.transformEnd()
70-
7185
def hash( self ):
72-
86+
7387
h = MurmurHash()
7488
return h
7589

@@ -215,13 +229,13 @@ def testCompoundDataAttributes( self ) :
215229
def testProcedural( self ) :
216230

217231
r = IECoreRI.Renderer( "test/IECoreRI/output/testProcedural.rib" )
218-
r.worldBegin()
219-
220-
p = SimpleProcedural( 10.5 )
221-
r.procedural( p )
232+
with WorldBlock( r ) :
222233

223-
r.worldEnd()
234+
p = SimpleProcedural( 10.5 )
235+
r.procedural( p )
224236

237+
self.assertEqual( p.numBoundCalls, 1 )
238+
self.assertEqual( p.numRenderCalls, 1 )
225239
self.assertEqual( p.rendererTypeId, IECoreRI.Renderer.staticTypeId() )
226240
self.assertEqual( p.rendererTypeName, "IECoreRI::Renderer" )
227241
self.assertEqual( p.rendererTypeName, IECoreRI.Renderer.staticTypeName() )
@@ -713,10 +727,53 @@ def testLightPrefixes( self ) :
713727
self.assertTrue( 'LightSource "renderManLight"' in rib )
714728
self.assertFalse( "arnold" in rib )
715729

730+
def testProceduralWithoutBounds( self ) :
731+
732+
r = IECoreRI.Renderer( "" )
733+
734+
r.camera(
735+
"main",
736+
{
737+
"projection" : IECore.StringData( "orthographic" ),
738+
"resolution" : IECore.V2iData( IECore.V2i( 256 ) ),
739+
"clippingPlanes" : IECore.V2fData( IECore.V2f( 0.1, 1000 ) ),
740+
"screenWindow" : IECore.Box2fData( IECore.Box2f( IECore.V2f( -1 ), IECore.V2f( 1 ) ) ),
741+
}
742+
)
743+
r.display(
744+
"test", "ieDisplay", "rgba",
745+
{
746+
"driverType" : "ImageDisplayDriver",
747+
"handle" : "test",
748+
"quantize" : FloatVectorData( [ 0, 0, 0, 0 ] ),
749+
}
750+
)
751+
752+
# Must use the raytrace hider in order to use unspecified
753+
# procedural bounds in 3delight - it is not supported in
754+
# REYES mode.
755+
r.setOption( "ri:hider", "raytrace" )
756+
757+
with IECore.WorldBlock( r ) :
758+
759+
r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0, 0, -10 ) ) )
760+
761+
procedural = SimpleProcedural( 1, computeBound = False )
762+
r.procedural( procedural )
763+
764+
self.assertEqual( procedural.numRenderCalls, 1 )
765+
766+
image = IECore.ImageDisplayDriver.removeStoredImage( "test" )
767+
768+
e = IECore.PrimitiveEvaluator.create( image )
769+
result = e.createResult()
770+
e.pointAtUV( IECore.V2f( 0.5, 0.5 ), result )
771+
self.assertEqual( result.floatPrimVar( e.A() ), 1 )
772+
716773
def tearDown( self ) :
717774

718775
IECoreRI.TestCase.tearDown( self )
719-
776+
720777
files = [
721778
"test/IECoreRI/shaders/types.sdl",
722779
]

0 commit comments

Comments
 (0)