Skip to content

Commit

Permalink
FIX : Instancer : Switch to renderInstance method on Prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldresser-ie committed Nov 9, 2023
1 parent 04869d4 commit 4b1ca2c
Showing 1 changed file with 102 additions and 69 deletions.
171 changes: 102 additions & 69 deletions src/GafferScene/Instancer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,76 @@ struct Prototype
}
}

// This internal interface for rendering an instance from a prototype is mostly pretty clean,
// except that instanceTransforms is used as scratch memory instead of being const - this is
// fine in the one place it's called from.
template< typename F >
inline void renderInstance(
const std::string &name, const std::vector<float> &transformSampleTimes,
std::vector<M44f> &instanceTransforms, F &&attributeProcessor,
IECoreScenePreview::Renderer *renderer
) const
{
if( !m_object.size() )
{
// No object to render. This could happen if the protype didn't meet the
// RenderOptions::purposeIncluded test.
return;
}

IECoreScenePreview::Renderer::AttributesInterfacePtr attribsStorage;
IECoreScenePreview::Renderer::AttributesInterface *attribs;
if( m_rendererAttributes )
{
attribs = m_rendererAttributes.get();
}
else
{
CompoundObjectPtr currentAttributes = new CompoundObject();

// Since we're not going to modify any existing members (only add new ones),
// and our result is only read in this function, and never written, we can
// directly reference the prototype members in our result without copying. Be
// careful not to modify them though!
currentAttributes->members() = m_attributes->members();

attributeProcessor( *currentAttributes );

attribsStorage = renderer->attributes( currentAttributes.get() );
attribs = attribsStorage.get();
}

IECoreScenePreview::Renderer::ObjectInterfacePtr objectInterface;
if( m_objectSampleTimes.size() )
{
objectInterface = renderer->object(
name, m_objectPointers, m_objectSampleTimes, attribs
);
}
else
{
objectInterface = renderer->object(
name, m_object[0].get(), attribs
);
}

if( transformSampleTimes.size() == 1 )
{
objectInterface->transform( m_transforms[0] * instanceTransforms[0] );
}
else
{
for( unsigned int i = 0; i < transformSampleTimes.size(); i++ )
{
instanceTransforms[i] = m_transforms[i] * instanceTransforms[i];
}

objectInterface->transform( instanceTransforms, transformSampleTimes );
}
}

private:

std::vector<ConstObjectPtr> m_object;

// Rather awkwardly, we need to store the objects as raw pointers as well, because Renderer::object
Expand Down Expand Up @@ -2761,19 +2831,44 @@ void Instancer::InstancerCapsule::render( IECoreScenePreview::Renderer *renderer
{
Context::EditableScope prototypeScope( threadState );

vector<M44f> pointTransforms( sampleTimes.size() );
// Performance is important enough here that we don't want to be doing extra allocations,
// so this vector is used both for transferring transforms to Prototype::render, and as
// working memory by Prototype::render. It must be reset each iteration
vector<M44f> transforms( sampleTimes.size() );
std::string name;
IECoreScenePreview::Renderer::AttributesInterfacePtr attribsStorage;


for( size_t idx = r.begin(); idx != r.end(); ++idx )
{
int pointIndex = pointIndicesForPrototype[idx];

const Prototype *proto;
int instanceId = engines[0]->instanceId( pointIndex );

// Start preparing the transforms for the instance by putting in the instance transforms
for( unsigned int i = 0; i < engines.size(); i++ )
{
int curPointIndex = i == 0 ? pointIndex : engines[i]->pointIndex( instanceId );
transforms[i] = engines[i]->instanceTransform( curPointIndex );
}

// We are running inside a procedural, so we don't need globally unique name. We are making a whole
// lot of these names for instances, so we make these names as absolutely minimal as possible.
name.resize( std::numeric_limits< int >::digits10 + 1 );
name.resize( std::to_chars( &name[0], &(*name.end()), instanceId ).ptr - &name[0] );

// Set up a lambda function that can be used to apply instance attributes.
// Will only be used if the prototype was not passed prepareRendererAttributes.
auto attributeProcessor = [&engines, &pointIndex]( CompoundObject &attributes )
{
engines[0]->instanceAttributes( pointIndex, attributes );
};

// We now have the data we need to pass to renderInstance() on the Prototype.
// The prototype may be constant, or we may need to find it by using the prototypeCache.
if( constantPrototype )
{
proto = &(*constantPrototype);
constantPrototype->renderInstance(
name, sampleTimes, transforms, attributeProcessor, renderer
);
}
else
{
Expand All @@ -2793,72 +2888,10 @@ void Instancer::InstancerCapsule::render( IECoreScenePreview::Renderer *renderer

engines[0]->setPrototypeContextVariables( pointIndex, prototypeScope );

proto = prototypeCache.get( PrototypeCacheGetterKey( prototypeScope.context() ) );
}

if( !proto->m_object.size() )
{
// No object to render. This could happen if the protype didn't meet the
// RenderOptions::purposeIncluded test.
continue;
}

IECoreScenePreview::Renderer::AttributesInterface *attribs;
if( hasAttributes )
{
CompoundObjectPtr currentAttributes = new CompoundObject();

// Since we're not going to modify any existing members (only add new ones),
// and our result is only read in this function, and never written, we can
// directly reference the input members in our result without copying. Be
// careful not to modify them though!
currentAttributes->members() = proto->m_attributes->members();

engines[0]->instanceAttributes( pointIndex, *currentAttributes );
attribsStorage = renderer->attributes( currentAttributes.get() );
attribs = attribsStorage.get();
}
else
{
attribs = proto->m_rendererAttributes.get();
}

int instanceId = engines[0]->instanceId( pointIndex );

// We are running inside a procedural, so we don't need globally unique name. We are making a whole
// lot of these names for instances, so we make these names as absolutely minimal as possible.
name.resize( std::numeric_limits< int >::digits10 + 1 );
name.resize( std::to_chars( &name[0], &(*name.end()), instanceId ).ptr - &name[0] );

IECoreScenePreview::Renderer::ObjectInterfacePtr objectInterface;
if( proto->m_objectSampleTimes.size() )
{
objectInterface = renderer->object(
name, proto->m_objectPointers, proto->m_objectSampleTimes, attribs
);
}
else
{
objectInterface = renderer->object(
name, proto->m_object[0].get(), attribs
prototypeCache.get( PrototypeCacheGetterKey( prototypeScope.context() ) )->renderInstance(
name, sampleTimes, transforms, attributeProcessor, renderer
);
}

if( sampleTimes.size() == 1 )
{
objectInterface->transform( proto->m_transforms[0] * engines[0]->instanceTransform( pointIndex ) );
}
else
{
for( unsigned int i = 0; i < engines.size(); i++ )
{
int curPointIndex = i == 0 ? pointIndex : engines[i]->pointIndex( instanceId );
pointTransforms[i] = proto->m_transforms[i] * engines[i]->instanceTransform( curPointIndex );
}

objectInterface->transform( pointTransforms, sampleTimes );
}

}
},
taskGroupContext
Expand Down

0 comments on commit 4b1ca2c

Please sign in to comment.