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();
}
Beispiel #2
0
// 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;
		}
	}
}
Beispiel #3
0
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() );
			}
		}
	}
}
Beispiel #7
0
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;
}
Beispiel #8
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;
}