IECore::TransformPtr GafferScene::transform( const ScenePlug *scene, const ScenePlug::ScenePath &path, const Imath::V2f &shutter, bool motionBlur ) { int numSamples = 1; if( motionBlur ) { ConstCompoundObjectPtr attributes = scene->fullAttributes( path ); const IntData *transformBlurSegmentsData = attributes->member<IntData>( "gaffer:transformBlurSegments" ); numSamples = transformBlurSegmentsData ? transformBlurSegmentsData->readable() + 1 : 2; const BoolData *transformBlurData = attributes->member<BoolData>( "gaffer:transformBlur" ); if( transformBlurData && !transformBlurData->readable() ) { numSamples = 1; } } MatrixMotionTransformPtr result = new MatrixMotionTransform(); ContextPtr transformContext = new Context( *Context::current(), Context::Borrowed ); Context::Scope scopedContext( transformContext.get() ); for( int i = 0; i < numSamples; i++ ) { float frame = lerp( shutter[0], shutter[1], (float)i / std::max( 1, numSamples - 1 ) ); transformContext->setFrame( frame ); result->snapshots()[frame] = scene->fullTransform( path ); } return result; }
Imath::Box3f SceneProcedural::bound() const { /// \todo I think we should be able to remove this exception handling in the future. /// Either when we do better error handling in ValuePlug computations, or when /// the bug in IECoreGL that caused the crashes in SceneProceduralTest.testComputationErrors /// is fixed. try { ContextPtr timeContext = new Context( *m_context ); Context::Scope scopedTimeContext( timeContext ); /// \todo This doesn't take account of the unfortunate fact that our children may have differing /// numbers of segments than ourselves. To get an accurate bound we would need to know the different sample /// times the children may be using and evaluate a bound at those times as well. We don't want to visit /// the children to find the sample times out though, because that defeats the entire point of deferred loading. /// /// Here are some possible approaches : /// /// 1) Add a new attribute called boundSegments, which defines the number of segments used to calculate /// the bounding box. It would be the responsibility of the user to set this to an appropriate value /// at the parent levels, so that the parents calculate bounds appropriate for the children. /// This seems like a bit too much burden on the user. /// /// 2) Add a global option called "maxSegments" - this will clamp the number of segments used on anything /// and will be set to 1 by default. The user will need to increase it to allow the leaf level attributes /// to take effect, and all bounding boxes everywhere will be calculated using that number of segments /// (actually I think it'll be that number of segments and all nondivisible smaller numbers). This should /// be accurate but potentially slower, because we'll be doing the extra work everywhere rather than only /// where needed. It still places a burden on the user (increasing the global clamp appropriately), /// but not quite such a bad one as they don't have to figure anything out and only have one number to set. /// /// 3) Have the StandardOptions node secretly compute a global "maxSegments" behind the scenes. This would /// work as for 2) but remove the burden from the user. However, it would mean preventing any expressions /// or connections being used on the segments attributes, because they could be used to cheat the system. /// It could potentially be faster than 2) because it wouldn't have to do all nondivisible numbers - it /// could know exactly which numbers of segments were in existence. It still suffers from the /// "pay the price everywhere" problem. std::set<float> times; motionTimes( ( m_options.deformationBlur && m_attributes.deformationBlur ) ? m_attributes.deformationBlurSegments : 0, times ); motionTimes( ( m_options.transformBlur && m_attributes.transformBlur ) ? m_attributes.transformBlurSegments : 0, times ); Box3f result; for( std::set<float>::const_iterator it = times.begin(), eIt = times.end(); it != eIt; it++ ) { timeContext->setFrame( *it ); Box3f b = m_scenePlug->boundPlug()->getValue(); M44f t = m_scenePlug->transformPlug()->getValue(); result.extendBy( transform( b, t ) ); } return result; } catch( const std::exception &e ) { IECore::msg( IECore::Msg::Error, "SceneProcedural::bound()", e.what() ); } return Box3f(); }
void ExecutableNode::executeSequence( const std::vector<float> &frames ) const { ContextPtr context = new Context( *Context::current(), Context::Borrowed ); Context::Scope scopedContext( context.get() ); for ( std::vector<float>::const_iterator it = frames.begin(); it != frames.end(); ++it ) { context->setFrame( *it ); execute(); } }
void SceneWriter::executeSequence( const std::vector<float> &frames ) const { const ScenePlug *scene = inPlug()->getInput<ScenePlug>(); if( !scene ) { throw IECore::Exception( "No input scene" ); } ContextPtr context = new Context( *Context::current(), Context::Borrowed ); Context::Scope scopedContext( context.get() ); std::string fileName = context->substitute( fileNamePlug()->getValue() ); createDirectories( fileName ); SceneInterfacePtr output = SceneInterface::create( fileName, IndexedIO::Write ); for ( std::vector<float>::const_iterator it = frames.begin(); it != frames.end(); ++it ) { context->setFrame( *it ); double time = *it / g_frameRate; writeLocation( scene, ScenePlug::ScenePath(), context.get(), output.get(), time ); } }
bool ImageReader::computeFrameMask( const Context *context, ContextPtr &maskedContext ) const { int frameStartMask = startFramePlug()->getValue(); int frameEndMask = endFramePlug()->getValue(); FrameMaskMode frameStartMaskMode = (FrameMaskMode)startModePlug()->getValue(); FrameMaskMode frameEndMaskMode = (FrameMaskMode)endModePlug()->getValue(); int origFrame = (int)context->getFrame(); int maskedFrame = std::min( frameEndMask, std::max( frameStartMask, origFrame ) ); if( origFrame == maskedFrame ) { // no need for anything special when // we're within the mask range. return false; } FrameMaskMode maskMode = ( origFrame < maskedFrame ) ? frameStartMaskMode : frameEndMaskMode; if( maskMode == None ) { // no need for anything special when // we're in FrameMaskMode::None return false; } // we need to create the masked context // for both BlackOutSide and ClampToFrame, // because some plugs require valid data // from the mask range even in either way. maskedContext = new Gaffer::Context( *context, Context::Borrowed ); maskedContext->setFrame( maskedFrame ); return ( maskMode == BlackOutside ); }
void Dispatcher::dispatch( const std::vector<NodePtr> &nodes ) const { // clear job directory, so that if our node validation fails, // jobDirectory() won't return the result from the previous dispatch. m_jobDirectory = ""; // validate the nodes we've been given if ( nodes.empty() ) { throw IECore::Exception( getName().string() + ": Must specify at least one node to dispatch." ); } std::vector<ExecutableNodePtr> executables; const ScriptNode *script = (*nodes.begin())->scriptNode(); for ( std::vector<NodePtr>::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt ) { const ScriptNode *currentScript = (*nIt)->scriptNode(); if ( !currentScript || currentScript != script ) { throw IECore::Exception( getName().string() + ": Dispatched nodes must all belong to the same ScriptNode." ); } if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( nIt->get() ) ) { executables.push_back( executable ); } else if ( const Box *box = runTimeCast<const Box>( nIt->get() ) ) { for ( RecursiveOutputPlugIterator plugIt( box ); plugIt != plugIt.end(); ++plugIt ) { Node *sourceNode = plugIt->get()->source<Plug>()->node(); if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( sourceNode ) ) { executables.push_back( executable ); } } } else { throw IECore::Exception( getName().string() + ": Dispatched nodes must be ExecutableNodes or Boxes containing ExecutableNodes." ); } } // create the job directory now, so it's available in preDispatchSignal(). const Context *context = Context::current(); m_jobDirectory = createJobDirectory( context ); // this object calls this->preDispatchSignal() in its constructor and this->postDispatchSignal() // in its destructor, thereby guaranteeing that we always call this->postDispatchSignal(). DispatcherSignalGuard signalGuard( this, executables ); if ( signalGuard.cancelledByPreDispatch() ) { return; } std::vector<FrameList::Frame> frames; FrameListPtr frameList = frameRange( script, context ); frameList->asList( frames ); size_t i = 0; ExecutableNode::Tasks tasks; tasks.reserve( executables.size() * frames.size() ); for ( std::vector<FrameList::Frame>::const_iterator fIt = frames.begin(); fIt != frames.end(); ++fIt ) { for ( std::vector<ExecutableNodePtr>::const_iterator nIt = executables.begin(); nIt != executables.end(); ++nIt, ++i ) { ContextPtr frameContext = new Context( *context, Context::Borrowed ); frameContext->setFrame( *fIt ); tasks.push_back( ExecutableNode::Task( *nIt, frameContext ) ); } } TaskBatchPtr rootBatch = batchTasks( tasks ); if ( !rootBatch->requirements().empty() ) { doDispatch( rootBatch.get() ); } // inform the guard that the process has been completed, so it can pass this info to // postDispatchSignal(): signalGuard.success(); }
void SceneProcedural::render( RendererPtr renderer ) const { Context::Scope scopedContext( m_context ); /// \todo See above. try { // get all the attributes, and early out if we're not visibile ConstCompoundObjectPtr attributes = m_scenePlug->attributesPlug()->getValue(); const BoolData *visibilityData = attributes->member<BoolData>( "gaffer:visibility" ); if( visibilityData && !visibilityData->readable() ) { return; } // if we are visible then make an attribute block to contain everything, set the name // and get on with generating things. AttributeBlock attributeBlock( renderer ); std::string name = ""; for( ScenePlug::ScenePath::const_iterator it = m_scenePath.begin(), eIt = m_scenePath.end(); it != eIt; it++ ) { name += "/" + it->string(); } renderer->setAttribute( "name", new StringData( name ) ); // transform std::set<float> transformTimes; motionTimes( ( m_options.transformBlur && m_attributes.transformBlur ) ? m_attributes.transformBlurSegments : 0, transformTimes ); { ContextPtr timeContext = new Context( *m_context ); Context::Scope scopedTimeContext( timeContext ); MotionBlock motionBlock( renderer, transformTimes, transformTimes.size() > 1 ); for( std::set<float>::const_iterator it = transformTimes.begin(), eIt = transformTimes.end(); it != eIt; it++ ) { timeContext->setFrame( *it ); renderer->concatTransform( m_scenePlug->transformPlug()->getValue() ); } } // attributes for( CompoundObject::ObjectMap::const_iterator it = attributes->members().begin(), eIt = attributes->members().end(); it != eIt; it++ ) { if( const StateRenderable *s = runTimeCast<const StateRenderable>( it->second.get() ) ) { s->render( renderer ); } else if( const ObjectVector *o = runTimeCast<const ObjectVector>( it->second.get() ) ) { for( ObjectVector::MemberContainer::const_iterator it = o->members().begin(), eIt = o->members().end(); it != eIt; it++ ) { const StateRenderable *s = runTimeCast<const StateRenderable>( it->get() ); if( s ) { s->render( renderer ); } } } else if( const Data *d = runTimeCast<const Data>( it->second.get() ) ) { renderer->setAttribute( it->first, d ); } } // object std::set<float> deformationTimes; motionTimes( ( m_options.deformationBlur && m_attributes.deformationBlur ) ? m_attributes.deformationBlurSegments : 0, deformationTimes ); { ContextPtr timeContext = new Context( *m_context ); Context::Scope scopedTimeContext( timeContext ); unsigned timeIndex = 0; for( std::set<float>::const_iterator it = deformationTimes.begin(), eIt = deformationTimes.end(); it != eIt; it++, timeIndex++ ) { timeContext->setFrame( *it ); ConstObjectPtr object = m_scenePlug->objectPlug()->getValue(); if( const Primitive *primitive = runTimeCast<const Primitive>( object.get() ) ) { if( deformationTimes.size() > 1 && timeIndex == 0 ) { renderer->motionBegin( deformationTimes ); } primitive->render( renderer ); if( deformationTimes.size() > 1 && timeIndex == deformationTimes.size() - 1 ) { renderer->motionEnd(); } } else if( const Camera *camera = runTimeCast<const Camera>( object.get() ) ) { /// \todo This absolutely does not belong here, but until we have /// a mechanism for drawing manipulators, we don't have any other /// means of visualising the cameras. if( renderer->isInstanceOf( "IECoreGL::Renderer" ) ) { drawCamera( camera, renderer.get() ); } break; // no motion blur for these chappies. } else if( const Light *light = runTimeCast<const Light>( object.get() ) ) { /// \todo This doesn't belong here. if( renderer->isInstanceOf( "IECoreGL::Renderer" ) ) { drawLight( light, renderer.get() ); } break; // no motion blur for these chappies. } else if( const VisibleRenderable* renderable = runTimeCast< const VisibleRenderable >( object.get() ) ) { renderable->render( renderer ); break; // no motion blur for these chappies. } } } // children ConstInternedStringVectorDataPtr childNames = m_scenePlug->childNamesPlug()->getValue(); if( childNames->readable().size() ) { bool expand = true; if( m_pathsToExpand ) { expand = m_pathsToExpand->readable().match( m_scenePath ) & Filter::ExactMatch; } if( !expand ) { renderer->setAttribute( "gl:primitive:wireframe", new BoolData( true ) ); renderer->setAttribute( "gl:primitive:solid", new BoolData( false ) ); renderer->setAttribute( "gl:curvesPrimitive:useGLLines", new BoolData( true ) ); Box3f b = m_scenePlug->boundPlug()->getValue(); CurvesPrimitive::createBox( b )->render( renderer ); } else { ScenePlug::ScenePath childScenePath = m_scenePath; childScenePath.push_back( InternedString() ); // for the child name for( vector<InternedString>::const_iterator it=childNames->readable().begin(); it!=childNames->readable().end(); it++ ) { childScenePath[m_scenePath.size()] = *it; renderer->setAttribute( "name", new StringData( *it ) ); renderer->procedural( new SceneProcedural( *this, childScenePath ) ); } } } } catch( const std::exception &e ) { IECore::msg( IECore::Msg::Error, "SceneProcedural::render()", e.what() ); } }
void SceneProcedural::render( Renderer *renderer ) const { tbb::task_scheduler_init tsi( tbb::task_scheduler_init::deferred ); initializeTaskScheduler( tsi ); Context::Scope scopedContext( m_context.get() ); std::string name; ScenePlug::pathToString( m_scenePath, name ); /// \todo See above. try { // get all the attributes, and early out if we're not visibile const BoolData *visibilityData = m_attributesObject->member<BoolData>( g_visibleAttributeName ); if( visibilityData && !visibilityData->readable() ) { if( !m_rendered ) { decrementPendingProcedurals(); } m_rendered = true; return; } // if we are visible then make an attribute block to contain everything, set the name // and get on with generating things. AttributeBlock attributeBlock( renderer ); renderer->setAttribute( "name", new StringData( name ) ); // transform std::set<float> transformTimes; motionTimes( ( m_options.transformBlur && m_attributes.transformBlur ) ? m_attributes.transformBlurSegments : 0, transformTimes ); { ContextPtr timeContext = new Context( *m_context, Context::Borrowed ); Context::Scope scopedTimeContext( timeContext.get() ); MotionBlock motionBlock( renderer, transformTimes, transformTimes.size() > 1 ); for( std::set<float>::const_iterator it = transformTimes.begin(), eIt = transformTimes.end(); it != eIt; it++ ) { timeContext->setFrame( *it ); renderer->concatTransform( m_scenePlug->transformPlug()->getValue() ); } } // attributes outputAttributes( m_attributesObject.get(), renderer ); // object std::set<float> deformationTimes; motionTimes( ( m_options.deformationBlur && m_attributes.deformationBlur ) ? m_attributes.deformationBlurSegments : 0, deformationTimes ); { ContextPtr timeContext = new Context( *m_context, Context::Borrowed ); Context::Scope scopedTimeContext( timeContext.get() ); unsigned timeIndex = 0; for( std::set<float>::const_iterator it = deformationTimes.begin(), eIt = deformationTimes.end(); it != eIt; it++, timeIndex++ ) { timeContext->setFrame( *it ); ConstObjectPtr object = m_scenePlug->objectPlug()->getValue(); if( const Primitive *primitive = runTimeCast<const Primitive>( object.get() ) ) { if( deformationTimes.size() > 1 && timeIndex == 0 ) { renderer->motionBegin( deformationTimes ); } primitive->render( renderer ); if( deformationTimes.size() > 1 && timeIndex == deformationTimes.size() - 1 ) { renderer->motionEnd(); } } else if( const VisibleRenderable* renderable = runTimeCast< const VisibleRenderable >( object.get() ) ) { renderable->render( renderer ); break; // no motion blur for these chappies. } } } // children ConstInternedStringVectorDataPtr childNames = m_scenePlug->childNamesPlug()->getValue(); if( childNames->readable().size() ) { // Creating a SceneProcedural involves an attribute/bound evaluation, which are // potentially expensive, so we're parallelizing them. // allocate space for child procedurals: SceneProceduralCreate::SceneProceduralContainer childProcedurals( childNames->readable().size() ); // create procedurals in parallel: SceneProceduralCreate s( childProcedurals, *this, childNames->readable() ); tbb::parallel_for( tbb::blocked_range<int>( 0, childNames->readable().size() ), s ); // send to the renderer in series: std::vector<SceneProceduralPtr>::const_iterator procIt = childProcedurals.begin(), procEit = childProcedurals.end(); for( ; procIt != procEit; ++procIt ) { renderer->procedural( *procIt ); } } } catch( const std::exception &e ) { IECore::msg( IECore::Msg::Error, "SceneProcedural::render() " + name, e.what() ); } if( !m_rendered ) { decrementPendingProcedurals(); } m_rendered = true; }
void Dispatcher::dispatch( const std::vector<NodePtr> &nodes ) const { // clear job directory, so that if our node validation fails, // jobDirectory() won't return the result from the previous dispatch. m_jobDirectory = ""; // validate the nodes we've been given if ( nodes.empty() ) { throw IECore::Exception( getName().string() + ": Must specify at least one node to dispatch." ); } std::vector<TaskNodePtr> taskNodes; const ScriptNode *script = (*nodes.begin())->scriptNode(); for ( std::vector<NodePtr>::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt ) { const ScriptNode *currentScript = (*nIt)->scriptNode(); if ( !currentScript || currentScript != script ) { throw IECore::Exception( getName().string() + ": Dispatched nodes must all belong to the same ScriptNode." ); } if ( TaskNode *taskNode = runTimeCast<TaskNode>( nIt->get() ) ) { taskNodes.push_back( taskNode ); } else if ( const SubGraph *subGraph = runTimeCast<const SubGraph>( nIt->get() ) ) { for ( RecursiveOutputPlugIterator plugIt( subGraph ); !plugIt.done(); ++plugIt ) { Node *sourceNode = plugIt->get()->source<Plug>()->node(); if ( TaskNode *taskNode = runTimeCast<TaskNode>( sourceNode ) ) { taskNodes.push_back( taskNode ); } } } else { throw IECore::Exception( getName().string() + ": Dispatched nodes must be TaskNodes or SubGraphs containing TaskNodes." ); } } // create the job directory now, so it's available in preDispatchSignal(). ContextPtr context = new Context( *Context::current(), Context::Borrowed ); m_jobDirectory = createJobDirectory( context.get() ); context->set( g_jobDirectoryContextEntry, m_jobDirectory ); // this object calls this->preDispatchSignal() in its constructor and this->postDispatchSignal() // in its destructor, thereby guaranteeing that we always call this->postDispatchSignal(). DispatcherSignalGuard signalGuard( this, taskNodes ); if ( signalGuard.cancelledByPreDispatch() ) { return; } std::vector<FrameList::Frame> frames; FrameListPtr frameList = frameRange( script, context.get() ); frameList->asList( frames ); Batcher batcher; for( std::vector<FrameList::Frame>::const_iterator fIt = frames.begin(); fIt != frames.end(); ++fIt ) { for( std::vector<TaskNodePtr>::const_iterator nIt = taskNodes.begin(); nIt != taskNodes.end(); ++nIt ) { context->setFrame( *fIt ); batcher.addTask( TaskNode::Task( *nIt, context.get() ) ); } } executeAndPruneImmediateBatches( batcher.rootBatch() ); if( !batcher.rootBatch()->preTasks().empty() ) { doDispatch( batcher.rootBatch() ); } // inform the guard that the process has been completed, so it can pass this info to // postDispatchSignal(): signalGuard.success(); }
void Dispatcher::dispatch( const std::vector<NodePtr> &nodes ) const { if ( nodes.empty() ) { throw IECore::Exception( getName().string() + ": Must specify at least one node to dispatch." ); } std::vector<ExecutableNodePtr> executables; const ScriptNode *script = (*nodes.begin())->scriptNode(); for ( std::vector<NodePtr>::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt ) { const ScriptNode *currentScript = (*nIt)->scriptNode(); if ( !currentScript || currentScript != script ) { throw IECore::Exception( getName().string() + ": Dispatched nodes must all belong to the same ScriptNode." ); } if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( nIt->get() ) ) { executables.push_back( executable ); } else if ( const Box *box = runTimeCast<const Box>( nIt->get() ) ) { for ( RecursiveOutputPlugIterator plugIt( box ); plugIt != plugIt.end(); ++plugIt ) { Node *sourceNode = plugIt->get()->source<Plug>()->node(); if ( ExecutableNode *executable = runTimeCast<ExecutableNode>( sourceNode ) ) { executables.push_back( executable ); } } } else { throw IECore::Exception( getName().string() + ": Dispatched nodes must be ExecutableNodes or Boxes containing ExecutableNodes." ); } } if ( preDispatchSignal()( this, executables ) ) { /// \todo: communicate the cancellation to the user return; } const Context *context = Context::current(); std::vector<FrameList::Frame> frames; FrameListPtr frameList = frameRange( script, context ); frameList->asList( frames ); size_t i = 0; ExecutableNode::Tasks tasks; tasks.reserve( executables.size() * frames.size() ); for ( std::vector<FrameList::Frame>::const_iterator fIt = frames.begin(); fIt != frames.end(); ++fIt ) { for ( std::vector<ExecutableNodePtr>::const_iterator nIt = executables.begin(); nIt != executables.end(); ++nIt, ++i ) { ContextPtr frameContext = new Context( *context, Context::Borrowed ); frameContext->setFrame( *fIt ); tasks.push_back( ExecutableNode::Task( *nIt, frameContext ) ); } } TaskBatchPtr rootBatch = batchTasks( tasks ); if ( !rootBatch->requirements().empty() ) { doDispatch( rootBatch.get() ); } postDispatchSignal()( this, executables ); }