size_t FromHoudiniGroupConverter::regroup( GU_Detail *geo, AttributePrimIdGroupMap &groupMap, GA_ROAttributeRef attrRef ) const { const GA_Attribute *attr = attrRef.getAttribute(); const GA_AIFStringTuple *attrAIF = attrRef.getAIFStringTuple(); AttributePrimIdGroupMapIterator it; const GA_PrimitiveList &primitives = geo->getPrimitiveList(); for ( GA_Iterator pIt=geo->getPrimitiveRange().begin(); !pIt.atEnd(); ++pIt ) { GA_Primitive *prim = primitives.get( pIt.getOffset() ); unsigned primType = prim->getTypeId().get(); std::string value = ""; const char *tmp = attrAIF->getString( attr, pIt.getOffset() ); if ( tmp ) { value = tmp; } AttributePrimIdPair key( value, primType ); it = groupMap.find( key ); if ( it == groupMap.end() ) { AttributePrimIdGroupPair pair( key, static_cast<GA_PrimitiveGroup*>( geo->createInternalElementGroup( GA_ATTRIB_PRIMITIVE, ( boost::format( "FromHoudiniGroupConverter__typedPrimitives%d%s" ) % primType % value ).str().c_str() ) ) ); it = groupMap.insert( pair ).first; } it->second->add( prim ); } return groupMap.size(); }
// general cortex render function, takes a gu_detail and uses the NodePassData attribute // to call the required render method void GR_Cortex::render( GU_Detail *gdp, const IECoreGL::State *displayState ) { // gl scene from a parameterised procedural const GA_ROAttributeRef attrRef = gdp->findAttribute( GA_ATTRIB_DETAIL, GA_SCOPE_PRIVATE, "IECoreHoudiniNodePassData" ); if ( attrRef.isInvalid() ) { return; } const GA_Attribute *attr = attrRef.getAttribute(); const GA_AIFBlindData *blindData = attr->getAIFBlindData(); const NodePassData passData = blindData->getValue<NodePassData>( attr, 0 ); switch( passData.type() ) { case IECoreHoudini::NodePassData::CORTEX_OPHOLDER : { SOP_OpHolder *sop = dynamic_cast<SOP_OpHolder*>( const_cast<OP_Node*>( passData.nodePtr() ) ); if ( !sop ) { return; } IECore::OpPtr op = IECore::runTimeCast<IECore::Op>( sop->getParameterised() ); if ( !op ) { return; } const IECore::Parameter *result_parameter = op->resultParameter(); const IECore::Object *result_object = result_parameter->getValue(); renderObject( result_object, displayState ); break; } case IECoreHoudini::NodePassData::CORTEX_PROCEDURALHOLDER : { SOP_ProceduralHolder *sop = dynamic_cast<SOP_ProceduralHolder*>( const_cast<OP_Node*>( passData.nodePtr() ) ); if ( !sop ) { return; } IECoreGL::ConstScenePtr scene = sop->scene(); if ( !scene ) { return; } scene->render( const_cast<IECoreGL::State *>( displayState ) ); break; } default : { break; } } }
bool HoudiniScene::hasObject() const { OP_Node *node = retrieveNode( true ); if ( node->isManager() ) { return false; } OBJ_Node *objNode = node->castToOBJNode(); if ( !objNode ) { return false; } OBJ_OBJECT_TYPE type = objNode->getObjectType(); if ( type == OBJ_GEOMETRY ) { OP_Context context( getDefaultTime() ); const GU_Detail *geo = objNode->getRenderGeometry( context, false ); // multiple named shapes define children that contain each object /// \todo: similar attribute logic is repeated in several places. unify in a single function if possible GA_ROAttributeRef nameAttrRef = geo->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" ); if ( !nameAttrRef.isValid() ) { return true; } const GA_Attribute *nameAttr = nameAttrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple(); GA_Size numShapes = tuple->getTableEntries( nameAttr ); if ( !numShapes ) { return true; } for ( GA_Size i=0; i < numShapes; ++i ) { const char *currentName = tuple->getTableString( nameAttr, tuple->validateTableHandle( nameAttr, i ) ); const char *match = matchPath( currentName ); if ( match && *match == *emptyString ) { // exact match return true; } } return false; } /// \todo: need to account for OBJ_CAMERA and OBJ_LIGHT return false; }
FromHoudiniGeometryConverter::Convertability FromHoudiniCurvesConverter::canConvert( const GU_Detail *geo ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); unsigned numPrims = geo->getNumPrimitives(); GA_Iterator firstPrim = geo->getPrimitiveRange().begin(); if ( !numPrims || !compatiblePrimitive( primitives.get( firstPrim.getOffset() )->getTypeId() ) ) { return Inapplicable; } const GEO_Curve *firstCurve = (const GEO_Curve*)primitives.get( firstPrim.getOffset() ); bool periodic = firstCurve->isClosed(); unsigned order = firstCurve->getOrder(); for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); if ( !compatiblePrimitive( prim->getTypeId() ) ) { return Inapplicable; } const GEO_Curve *curve = (const GEO_Curve*)prim; if ( curve->getOrder() != order ) { return Inapplicable; } if ( curve->isClosed() != periodic ) { return Inapplicable; } } // is there a single named shape? GA_ROAttributeRef attrRef = geo->findPrimitiveAttribute( "name" ); if ( attrRef.isValid() && attrRef.isString() ) { const GA_Attribute *nameAttr = attrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple(); GA_StringTableStatistics stats; tuple->getStatistics( nameAttr, stats ); if ( stats.getEntries() < 2 ) { return Ideal; } } return Suitable; }
OP_ERROR SOP_SceneCacheSource::cookMySop( OP_Context &context ) { // make sure the state is valid if ( boost::indeterminate( m_static ) ) { sceneChanged(); } flags().setTimeDep( bool( !m_static ) ); std::string file; if ( !ensureFile( file ) ) { addError( SOP_ATTRIBUTE_INVALID, ( file + " is not a valid .scc" ).c_str() ); gdp->clearAndDestroy(); return error(); } std::string path = getPath(); Space space = getSpace(); GeometryType geometryType = (GeometryType)this->evalInt( pGeometryType.getToken(), 0, 0 ); UT_String tagFilterStr; getTagFilter( tagFilterStr ); UT_StringMMPattern tagFilter; tagFilter.compile( tagFilterStr ); UT_String shapeFilterStr; getShapeFilter( shapeFilterStr ); UT_StringMMPattern shapeFilter; shapeFilter.compile( shapeFilterStr ); UT_String p( "P" ); UT_String attributeFilter; getAttributeFilter( attributeFilter ); if ( !p.match( attributeFilter ) ) { attributeFilter += " P"; } UT_String attributeCopy; getAttributeCopy( attributeCopy ); UT_String fullPathName; getFullPathName( fullPathName ); ConstSceneInterfacePtr scene = this->scene( file, path ); if ( !scene ) { addError( SOP_ATTRIBUTE_INVALID, ( path + " is not a valid location in " + file ).c_str() ); gdp->clearAndDestroy(); return error(); } MurmurHash hash; hash.append( file ); hash.append( path ); hash.append( space ); hash.append( tagFilterStr ); hash.append( shapeFilterStr ); hash.append( attributeFilter ); hash.append( attributeCopy ); hash.append( fullPathName ); hash.append( geometryType ); hash.append( getObjectOnly() ); if ( !m_loaded || m_hash != hash ) { gdp->clearAndDestroy(); } double readTime = time( context ); Imath::M44d transform = ( space == World ) ? worldTransform( file, path, readTime ) : Imath::M44d(); SceneInterface::Path rootPath; scene->path( rootPath ); UT_Interrupt *progress = UTgetInterrupt(); if ( !progress->opStart( ( "Cooking objects for " + getPath() ).c_str() ) ) { addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted before it started" ); gdp->clearAndDestroy(); return error(); } Parameters params; UT_String attribFilter; getAttributeFilter( attribFilter ); params.attributeFilter = attribFilter.toStdString(); params.attributeCopy = attributeCopy.toStdString(); params.fullPathName = fullPathName.toStdString(); params.geometryType = getGeometryType(); getShapeFilter( params.shapeFilter ); getTagFilter( params.tagFilter ); // Building a map from shape name to primitive range, which will be used during // convertObject() to do a lazy update of animated primvars where possible, and // to destroy changing topology shapes when necessary. GA_ROAttributeRef nameAttrRef = gdp->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" ); if ( nameAttrRef.isValid() ) { const GA_Attribute *attr = nameAttrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = attr->getAIFSharedStringTuple(); std::map<std::string, GA_OffsetList> offsets; GA_Range primRange = gdp->getPrimitiveRange(); for ( GA_Iterator it = primRange.begin(); !it.atEnd(); ++it ) { std::string current = ""; if ( const char *value = tuple->getString( attr, it.getOffset() ) ) { current = value; } std::map<std::string, GA_OffsetList>::iterator oIt = offsets.find( current ); if ( oIt == offsets.end() ) { oIt = offsets.insert( std::pair<std::string, GA_OffsetList>( current, GA_OffsetList() ) ).first; } oIt->second.append( it.getOffset() ); } for ( std::map<std::string, GA_OffsetList>::iterator oIt = offsets.begin(); oIt != offsets.end(); ++oIt ) { params.namedRanges[oIt->first] = GA_Range( gdp->getPrimitiveMap(), oIt->second ); } } loadObjects( scene.get(), transform, readTime, space, params, rootPath.size() ); if ( progress->opInterrupt( 100 ) ) { addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted" ); gdp->clearAndDestroy(); m_loaded = false; m_hash = MurmurHash(); } else { m_loaded = true; m_hash = hash; } progress->opEnd(); return error(); }
void SOP_ParameterisedHolder::setInputParameterValues( float now ) { for ( unsigned int i=0; i < m_inputParameters.size(); i++ ) { useInputSource( i, m_dirty, false ); IECore::ParameterPtr inputParameter = m_inputParameters[i]; GU_DetailHandle inputHandle = inputGeoHandle( i ); GU_DetailHandleAutoReadLock readHandle( inputHandle ); const GU_Detail *inputGdp = readHandle.getGdp(); if ( !inputGdp ) { continue; } const GA_ROAttributeRef attrRef = inputGdp->findAttribute( GA_ATTRIB_DETAIL, GA_SCOPE_PRIVATE, "IECoreHoudiniNodePassData" ); if ( attrRef.isValid() ) { // looks like data passed from another ParameterisedHolder const GA_Attribute *attr = attrRef.getAttribute(); const GA_AIFBlindData *blindData = attr->getAIFBlindData(); const NodePassData passData = blindData->getValue<NodePassData>( attr, 0 ); SOP_ParameterisedHolder *sop = dynamic_cast<SOP_ParameterisedHolder*>( const_cast<OP_Node*>( passData.nodePtr() ) ); IECore::ConstObjectPtr result = 0; if ( passData.type() == IECoreHoudini::NodePassData::CORTEX_OPHOLDER ) { IECore::OpPtr op = IECore::runTimeCast<IECore::Op>( sop->getParameterised() ); result = op->resultParameter()->getValue(); } else if ( passData.type() == IECoreHoudini::NodePassData::CORTEX_PROCEDURALHOLDER ) { IECore::ParameterisedProcedural *procedural = IECore::runTimeCast<IECore::ParameterisedProcedural>( sop->getParameterised() ); IECore::CapturingRendererPtr renderer = new IECore::CapturingRenderer(); // We are acquiring and releasing the GIL here to ensure that it is released when we render. This has // to be done because a procedural might jump between c++ and python a few times (i.e. if it spawns // subprocedurals that are implemented in python). In a normal call to cookMySop, this wouldn't be an // issue, but if cookMySop was called from HOM, hou.Node.cook appears to be holding onto the GIL. IECorePython::ScopedGILLock gilLock; { IECorePython::ScopedGILRelease gilRelease; { IECore::WorldBlock worldBlock( renderer ); procedural->render( renderer ); } } result = IECore::runTimeCast<const IECore::Object>( renderer->world() ); } else { continue; } try { inputParameter->setValidatedValue( IECore::constPointerCast<IECore::Object>( result ) ); } catch ( const IECore::Exception &e ) { addError( SOP_MESSAGE, e.what() ); } } else { // looks like a regular Houdini detail IECore::ObjectParameterPtr objectParameter = IECore::runTimeCast<IECore::ObjectParameter>( inputParameter ); if ( !objectParameter ) { continue; } FromHoudiniGeometryConverterPtr converter = FromHoudiniGeometryConverter::create( inputHandle, objectParameter->validTypes() ); if ( !converter ) { continue; } // set converter parameters from the node values const CompoundParameter::ParameterVector &converterParameters = converter->parameters()->orderedParameters(); for ( CompoundParameter::ParameterVector::const_iterator it=converterParameters.begin(); it != converterParameters.end(); ++it ) { updateParameter( *it, now, "parm_" + inputParameter->name() + "_" ); } try { IECore::ObjectPtr converted = converter->convert(); if ( converted ) { inputParameter->setValidatedValue( converted ); } } catch ( const IECore::Exception &e ) { addError( SOP_MESSAGE, e.what() ); } catch ( std::runtime_error &e ) { addError( SOP_MESSAGE, e.what() ); } } } }
OP_Node *HoudiniScene::retrieveChild( const Name &name, Path &contentPath, MissingBehaviour missingBehaviour ) const { OP_Node *node = retrieveNode( false, missingBehaviour ); OP_Node *contentBaseNode = retrieveNode( true, missingBehaviour ); if ( !node || !contentBaseNode ) { return 0; } OBJ_Node *objNode = node->castToOBJNode(); OBJ_Node *contentNode = contentBaseNode->castToOBJNode(); // check subnet children if ( node->isManager() || ( objNode && objNode->getObjectType() == OBJ_SUBNET ) ) { for ( int i=0; i < node->getNchildren(); ++i ) { OP_Node *child = node->getChild( i ); // the contentNode is actually an extension of ourself if ( child == contentNode ) { continue; } if ( child->getName().equal( name.c_str() ) && !hasInput( child ) ) { return child; } } } if ( contentNode ) { // check connected outputs for ( unsigned i=0; i < contentNode->nOutputs(); ++i ) { OP_Node *child = contentNode->getOutput( i ); if ( child->getName().equal( name.c_str() ) ) { return child; } } // check child shapes within the geo if ( contentNode->getObjectType() == OBJ_GEOMETRY ) { OP_Context context( getDefaultTime() ); const GU_Detail *geo = contentNode->getRenderGeometry( context, false ); GA_ROAttributeRef nameAttrRef = geo->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" ); if ( nameAttrRef.isValid() ) { const GA_Attribute *nameAttr = nameAttrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple(); GA_Size numShapes = tuple->getTableEntries( nameAttr ); for ( GA_Size i=0; i < numShapes; ++i ) { const char *currentName = tuple->getTableString( nameAttr, tuple->validateTableHandle( nameAttr, i ) ); const char *match = matchPath( currentName ); if ( match && *match != *emptyString ) { std::pair<const char *, size_t> childMarker = nextWord( match ); std::string child( childMarker.first, childMarker.second ); if ( name == child ) { size_t contentSize = ( m_contentIndex ) ? m_path.size() - m_contentIndex : 0; if ( contentSize ) { contentPath.resize( contentSize ); std::copy( m_path.begin() + m_contentIndex, m_path.end(), contentPath.begin() ); } contentPath.push_back( name ); return contentNode; } } } } } } if ( missingBehaviour == SceneInterface::ThrowIfMissing ) { Path p; path( p ); std::string pStr; pathToString( p, pStr ); throw Exception( "IECoreHoudini::HoudiniScene::retrieveChild: Path \"" + pStr + "\" has no child named " + name.string() + "." ); } return 0; }
void HoudiniScene::childNames( NameList &childNames ) const { OP_Node *node = retrieveNode(); OBJ_Node *objNode = node->castToOBJNode(); OBJ_Node *contentNode = retrieveNode( true )->castToOBJNode(); // add subnet children if ( node->isManager() || ( objNode && objNode->getObjectType() == OBJ_SUBNET ) ) { for ( int i=0; i < node->getNchildren(); ++i ) { OP_Node *child = node->getChild( i ); // ignore children that have incoming connections, as those are actually grandchildren // also ignore the contentNode, which is actually an extension of ourself if ( child != contentNode && !hasInput( child ) ) { childNames.push_back( Name( child->getName() ) ); } } } if ( !contentNode ) { return; } // add connected outputs for ( unsigned i=0; i < contentNode->nOutputs(); ++i ) { childNames.push_back( Name( contentNode->getOutput( i )->getName() ) ); } // add child shapes within the geometry if ( contentNode->getObjectType() == OBJ_GEOMETRY ) { OP_Context context( getDefaultTime() ); const GU_Detail *geo = contentNode->getRenderGeometry( context, false ); GA_ROAttributeRef nameAttrRef = geo->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" ); if ( !nameAttrRef.isValid() ) { return; } const GA_Attribute *nameAttr = nameAttrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple(); GA_Size numShapes = tuple->getTableEntries( nameAttr ); for ( GA_Size i=0; i < numShapes; ++i ) { const char *currentName = tuple->getTableString( nameAttr, tuple->validateTableHandle( nameAttr, i ) ); const char *match = matchPath( currentName ); if ( match && *match != *emptyString ) { std::pair<const char *, size_t> childMarker = nextWord( match ); std::string child( childMarker.first, childMarker.second ); if ( std::find( childNames.begin(), childNames.end(), child ) == childNames.end() ) { childNames.push_back( child ); } } } } }
FromHoudiniGeometryConverter::Convertability FromHoudiniGroupConverter::canConvert( const GU_Detail *geo ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); // are there multiple primitives? unsigned numPrims = geo->getNumPrimitives(); if ( numPrims < 2 ) { return Admissible; } // are there mixed primitive types? GA_Iterator firstPrim = geo->getPrimitiveRange().begin(); GA_PrimitiveTypeId firstPrimId = primitives.get( firstPrim.getOffset() )->getTypeId(); for ( GA_Iterator it=firstPrim; !it.atEnd(); ++it ) { if ( primitives.get( it.getOffset() )->getTypeId() != firstPrimId ) { return Ideal; } } // are there multiple named shapes? GA_ROAttributeRef attrRef = geo->findPrimitiveAttribute( "name" ); if ( attrRef.isValid() && attrRef.isString() ) { const GA_Attribute *nameAttr = attrRef.getAttribute(); const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple(); if ( tuple->getTableEntries( nameAttr ) > 1 ) { return Ideal; } } // are the primitives split into groups? UT_PtrArray<const GA_ElementGroup*> primGroups; geo->getElementGroupList( GA_ATTRIB_PRIMITIVE, primGroups ); if ( primGroups.isEmpty() ) { return Admissible; } bool externalGroups = false; for ( unsigned i=0; i < primGroups.entries(); ++i ) { const GA_ElementGroup *group = primGroups[i]; if ( group->getInternal() ) { continue; } if ( group->entries() == numPrims ) { return Admissible; } externalGroups = true; } if ( externalGroups ) { return Ideal; } return Admissible; }