void ToHoudiniGeometryConverter::transferAttribValues( const Primitive *primitive, GU_Detail *geo, const GA_Range &points, const GA_Range &prims, PrimitiveVariable::Interpolation vertexInterpolation, PrimitiveVariable::Interpolation primitiveInterpolation, PrimitiveVariable::Interpolation pointInterpolation, PrimitiveVariable::Interpolation detailInterpolation ) const { GA_OffsetList offsets; if ( prims.isValid() ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); size_t numPrimVerts = prim->getVertexCount(); for ( size_t v=0; v < numPrimVerts; v++ ) { if ( prim->getTypeId() == GEO_PRIMPOLY ) { offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) ); } else { offsets.append( prim->getVertexOffset( v ) ); } } } } GA_Range vertRange( geo->getVertexMap(), offsets ); UT_String filter( attributeFilterParameter()->getTypedValue() ); bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue(); // process all primvars with UV interpretation for ( const auto &it : primitive->variables) { if ( !UT_String( it.first ).multiMatch( filter ) ) { continue; } if (const V2fVectorData *uvData = runTimeCast<const V2fVectorData> ( it.second.data.get() ) ) { if ( uvData->getInterpretation() != GeometricData::UV ) { continue; } PrimitiveVariable::IndexedView<Imath::V2f> uvIndexedView ( it.second ); // Houdini prefers a V3f uvw rather than V2f uv, // though they advise setting the 3rd component to 0. std::vector<Imath::V3f> uvw; uvw.reserve( uvIndexedView.size() ); for ( size_t i=0; i < uvIndexedView.size(); ++i ) { uvw.emplace_back( uvIndexedView[i][0], uvIndexedView[i][1], 0 ); } GA_Range range = vertRange; if ( it.second.interpolation == pointInterpolation ) { range = points; } V3fVectorData::Ptr uvwData = new V3fVectorData( uvw ); uvwData->setInterpretation( GeometricData::UV ); ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( uvwData.get() ); converter->convert( it.first, geo, range ); filter += " ^" + it.first; } } UT_StringMMPattern attribFilter; attribFilter.compile( filter ); // add the primitive variables to the various GEO_AttribDicts based on interpolation type for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ ) { if( !primitive->isPrimitiveVariableValid( it->second ) ) { IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." ); continue; } UT_String varName( it->first ); if ( !varName.multiMatch( attribFilter ) ) { continue; } PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second ); DataPtr data = nullptr; ToHoudiniAttribConverterPtr converter = nullptr; if( primVar.indices && primVar.data->typeId() == StringVectorDataTypeId ) { // we want to process the indexed strings rather than the expanded strings converter = ToHoudiniAttribConverter::create( primVar.data.get() ); if( ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter.get() ) ) { stringVectorConverter->indicesParameter()->setValidatedValue( primVar.indices.get() ); } } else { // all other primitive variables must be expanded data = primVar.expandedData(); converter = ToHoudiniAttribConverter::create( data.get() ); } if ( !converter ) { continue; } const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first; if ( primVar.interpolation == detailInterpolation ) { // add detail attribs try { converter->convert( name, geo ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Detail Attrib: " + e.what() ); } } else if ( primVar.interpolation == pointInterpolation ) { #if UT_MAJOR_VERSION_INT < 15 // add point attribs if ( name == "P" ) { // special case for P transferP( runTimeCast<const V3fVectorData>( primVar.data.get() ), geo, points ); } else #endif { try { GA_RWAttributeRef attrRef = converter->convert( name, geo, points ); // mark rest as non-transforming so it doesn't get manipulated once inside Houdini if ( name == "rest" || name == "Pref" ) { #if UT_MAJOR_VERSION_INT >= 15 attrRef.setTypeInfo( GA_TYPE_VOID ); #else attrRef.getAttribute()->setNonTransforming( true ); #endif } } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Point Attrib: " + e.what() ); } } } else if ( primVar.interpolation == primitiveInterpolation ) { // add primitive attribs try { converter->convert( name, geo, prims ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Primitive Attrib: " + e.what() ); } } else if ( primVar.interpolation == vertexInterpolation ) { // add vertex attribs try { converter->convert( name, geo, vertRange ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Vertex Attrib: " + e.what() ); } } } // backwards compatibility with older data const StringData *nameData = primitive->blindData()->member<StringData>( "name" ); if ( nameData && prims.isValid() ) { ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims ); } }
void ToHoudiniGeometryConverter::transferAttribValues( const Primitive *primitive, GU_Detail *geo, const GA_Range &points, const GA_Range &prims, PrimitiveVariable::Interpolation vertexInterpolation, PrimitiveVariable::Interpolation primitiveInterpolation, PrimitiveVariable::Interpolation pointInterpolation, PrimitiveVariable::Interpolation detailInterpolation ) const { GA_OffsetList offsets; if ( prims.isValid() ) { const GA_PrimitiveList &primitives = geo->getPrimitiveList(); for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); size_t numPrimVerts = prim->getVertexCount(); for ( size_t v=0; v < numPrimVerts; v++ ) { if ( prim->getTypeId() == GEO_PRIMPOLY ) { offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) ); } else { offsets.append( prim->getVertexOffset( v ) ); } } } } GA_Range vertRange( geo->getVertexMap(), offsets ); UT_String filter( attributeFilterParameter()->getTypedValue() ); // match all the string variables to each associated indices variable /// \todo: replace all this logic with IECore::IndexedData once it exists... PrimitiveVariableMap stringsToIndices; for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ ) { if ( !primitive->isPrimitiveVariableValid( it->second ) ) { IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." ); filter += UT_String( " ^" + it->first ); continue; } ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data.get() ); if ( !converter ) { continue; } if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) ) { std::string indicesVariableName = it->first + "Indices"; PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName ); if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) ) { stringsToIndices[it->first] = indices->second; filter += UT_String( " ^" + indicesVariableName ); } } } bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue(); if ( convertStandardAttributes && UT_String( "s" ).multiMatch( filter ) && UT_String( "t" ).multiMatch( filter ) ) { // convert s and t to uv PrimitiveVariableMap::const_iterator sPrimVar = primitive->variables.find( "s" ); PrimitiveVariableMap::const_iterator tPrimVar = primitive->variables.find( "t" ); if ( sPrimVar != primitive->variables.end() && tPrimVar != primitive->variables.end() ) { if ( sPrimVar->second.interpolation == tPrimVar->second.interpolation ) { const FloatVectorData *sData = runTimeCast<const FloatVectorData>( sPrimVar->second.data.get() ); const FloatVectorData *tData = runTimeCast<const FloatVectorData>( tPrimVar->second.data.get() ); if ( sData && tData ) { const std::vector<float> &s = sData->readable(); const std::vector<float> &t = tData->readable(); std::vector<Imath::V3f> uvw; uvw.reserve( s.size() ); for ( size_t i=0; i < s.size(); ++i ) { uvw.push_back( Imath::V3f( s[i], 1 - t[i], 0 ) ); } GA_Range range = vertRange; if ( sPrimVar->second.interpolation == pointInterpolation ) { range = points; } ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( new V3fVectorData( uvw ) ); converter->convert( "uv", geo, range ); filter += " ^s ^t"; } } } } UT_StringMMPattern attribFilter; attribFilter.compile( filter ); // add the primitive variables to the various GEO_AttribDicts based on interpolation type for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ ) { UT_String varName( it->first ); if ( !varName.multiMatch( attribFilter ) ) { continue; } PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second ); ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( primVar.data.get() ); if ( !converter ) { continue; } PrimitiveVariable::Interpolation interpolation = primVar.interpolation; if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) ) { PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first ); if ( indices != stringsToIndices.end() ) { ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter.get() ); PrimitiveVariable indicesPrimVar = processPrimitiveVariable( primitive, indices->second ); stringVectorConverter->indicesParameter()->setValidatedValue( indicesPrimVar.data ); interpolation = indices->second.interpolation; } } const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first; if ( interpolation == detailInterpolation ) { // add detail attribs try { converter->convert( name, geo ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Detail Attrib: " + e.what() ); } } else if ( interpolation == pointInterpolation ) { // add point attribs if ( name == "P" ) { // special case for P transferP( runTimeCast<const V3fVectorData>( primVar.data.get() ), geo, points ); } else { try { GA_RWAttributeRef attrRef = converter->convert( name, geo, points ); // mark rest as non-transforming so it doesn't get manipulated once inside Houdini if ( name == "rest" || name == "Pref" ) { attrRef.getAttribute()->setNonTransforming( true ); } } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Point Attrib: " + e.what() ); } } } else if ( interpolation == primitiveInterpolation ) { // add primitive attribs try { converter->convert( name, geo, prims ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Primitive Attrib: " + e.what() ); } } else if ( interpolation == vertexInterpolation ) { // add vertex attribs try { converter->convert( name, geo, vertRange ); } catch ( std::exception &e ) { throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Vertex Attrib: " + e.what() ); } } } // backwards compatibility with older data const StringData *nameData = primitive->blindData()->member<StringData>( "name" ); if ( nameData && prims.isValid() ) { ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims ); } }
OP_ERROR SOP_InterpolatedCacheReader::cookMySop( OP_Context &context ) { flags().setTimeDep( true ); if ( lockInputs( context ) >= UT_ERROR_ABORT ) { return error(); } gdp->stashAll(); float time = context.getTime(); float frame = context.getFloatFrame(); UT_String paramVal; evalString( paramVal, "cacheSequence", 0, time ); std::string cacheFileName = paramVal.toStdString(); evalString( paramVal, "objectFixes", 0, time ); std::string objectPrefix = paramVal.toStdString(); evalString( paramVal, "objectFixes", 1, time ); std::string objectSuffix = paramVal.toStdString(); evalString( paramVal, "attributeFixes", 0, time ); std::string attributePrefix = paramVal.toStdString(); evalString( paramVal, "attributeFixes", 1, time ); std::string attributeSuffix = paramVal.toStdString(); evalString( paramVal, "transformAttribute", 0, time ); std::string transformAttribute = paramVal.toStdString(); int samplesPerFrame = evalInt( "samplesPerFrame", 0, time ); InterpolatedCache::Interpolation interpolation = (InterpolatedCache::Interpolation)evalInt( "interpolation", 0, time ); GroupingMode groupingMode = (GroupingMode)evalInt( "groupingMode", 0, time ); // create the InterpolatedCache if ( cacheFileName.compare( m_cacheFileName ) != 0 || samplesPerFrame != m_samplesPerFrame || interpolation != m_interpolation ) { try { float fps = OPgetDirector()->getChannelManager()->getSamplesPerSec(); OversamplesCalculator calc( fps, samplesPerFrame ); m_cache = new InterpolatedCache( cacheFileName, interpolation, calc ); } catch ( IECore::InvalidArgumentException e ) { addWarning( SOP_ATTRIBUTE_INVALID, e.what() ); unlockInputs(); return error(); } m_cacheFileName = cacheFileName; m_samplesPerFrame = samplesPerFrame; m_interpolation = interpolation; } if ( !m_cache ) { addWarning( SOP_MESSAGE, "SOP_InterpolatedCacheReader: Cache Sequence not found" ); unlockInputs(); return error(); } std::vector<InterpolatedCache::ObjectHandle> objects; std::vector<InterpolatedCache::AttributeHandle> attrs; try { m_cache->objects( frame, objects ); } catch ( IECore::Exception e ) { addWarning( SOP_ATTRIBUTE_INVALID, e.what() ); unlockInputs(); return error(); } duplicatePointSource( 0, context ); GA_ElementGroupTable *groups = 0; if ( groupingMode == PointGroup ) { groups = &gdp->pointGroups(); } else if ( groupingMode == PrimitiveGroup ) { groups = &gdp->primitiveGroups(); } for ( GA_GroupTable::iterator<GA_ElementGroup> it=groups->beginTraverse(); !it.atEnd(); ++it ) { GA_ElementGroup *group = it.group(); if ( group->getInternal() || group->isEmpty() ) { continue; } // match GA_ElementGroup name to InterpolatedCache::ObjectHandle std::string searchName = objectPrefix + group->getName().toStdString() + objectSuffix; std::vector<InterpolatedCache::ObjectHandle>::iterator oIt = find( objects.begin(), objects.end(), searchName ); if ( oIt == objects.end() ) { continue; } CompoundObjectPtr attributes = 0; try { m_cache->attributes( frame, *oIt, attrs ); attributes = m_cache->read( frame, *oIt ); } catch ( IECore::Exception e ) { addError( SOP_ATTRIBUTE_INVALID, e.what() ); unlockInputs(); return error(); } const CompoundObject::ObjectMap &attributeMap = attributes->members(); GA_Range pointRange; GA_Range primRange; GA_Range vertexRange; if ( groupingMode == PointGroup ) { pointRange = gdp->getPointRange( (GA_PointGroup*)it.group() ); } else if ( groupingMode == PrimitiveGroup ) { primRange = gdp->getPrimitiveRange( (GA_PrimitiveGroup*)it.group() ); const GA_PrimitiveList &primitives = gdp->getPrimitiveList(); GA_OffsetList pointOffsets; GA_OffsetList vertOffsets; for ( GA_Iterator it=primRange.begin(); !it.atEnd(); ++it ) { const GA_Primitive *prim = primitives.get( it.getOffset() ); GA_Range primPointRange = prim->getPointRange(); for ( GA_Iterator pIt=primPointRange.begin(); !pIt.atEnd(); ++pIt ) { pointOffsets.append( pIt.getOffset() ); } size_t numPrimVerts = prim->getVertexCount(); for ( size_t v=0; v < numPrimVerts; v++ ) { if ( prim->getTypeId() == GEO_PRIMPOLY ) { vertOffsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) ); } else { vertOffsets.append( prim->getVertexOffset( v ) ); } } } pointOffsets.sortAndRemoveDuplicates(); pointRange = GA_Range( gdp->getPointMap(), pointOffsets ); vertexRange = GA_Range( gdp->getVertexMap(), vertOffsets ); } // transfer the InterpolatedCache::Attributes onto the GA_ElementGroup for ( CompoundObject::ObjectMap::const_iterator aIt=attributeMap.begin(); aIt != attributeMap.end(); aIt++ ) { Data *data = IECore::runTimeCast<Data>( aIt->second ); if ( !data ) { continue; } ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( data ); if ( !converter ) { continue; } // strip the prefix/suffix from the GA_Attribute name std::string attrName = aIt->first.value(); size_t prefixLength = attributePrefix.length(); if ( prefixLength && ( search( attrName.begin(), attrName.begin()+prefixLength, attributePrefix.begin(), attributePrefix.end() ) == attrName.begin() ) ) { attrName.erase( attrName.begin(), attrName.begin() + prefixLength ); } size_t suffixLength = attributeSuffix.length(); if ( suffixLength && ( search( attrName.end() - suffixLength, attrName.end(), attributeSuffix.begin(), attributeSuffix.end() ) == ( attrName.end() - suffixLength ) ) ) { attrName.erase( attrName.end() - suffixLength, attrName.end() ); } if ( attrName == "P" ) { const V3fVectorData *positions = IECore::runTimeCast<const V3fVectorData>( data ); if ( !positions ) { continue; } size_t index = 0; size_t entries = pointRange.getEntries(); const std::vector<Imath::V3f> &pos = positions->readable(); // Attempting to account for the vertex difference between an IECore::CurvesPrimitive and Houdini curves. // As Houdini implicitly triples the endpoints of a curve, a cache generated from a single CurvesPrimitive // will have exactly four extra vertices. In this case, we adjust the cache by ignoring the first two and // last two V3fs. In all other cases, we report a warning and don't apply the cache to these points. if ( pos.size() - 4 == entries ) { index = 2; } else if ( pos.size() != entries ) { addWarning( SOP_ATTRIBUTE_INVALID, ( boost::format( "Geometry/Cache mismatch: %s contains %d points, while cache expects %d values for P." ) % group->getName().toStdString() % entries % pos.size() ).str().c_str() ); continue; } /// \todo: try multi-threading this with a GA_SplittableRange for ( GA_Iterator it=pointRange.begin(); !it.atEnd(); ++it, ++index ) { gdp->setPos3( it.getOffset(), IECore::convert<UT_Vector3>( pos[index] ) ); } } else if ( groupingMode == PrimitiveGroup ) { GA_Range currentRange; unsigned size = despatchTypedData<TypedDataSize, TypeTraits::IsVectorTypedData, DespatchTypedDataIgnoreError>( data ); // check for existing attributes if ( gdp->findPrimitiveAttribute( attrName.c_str() ).isValid() && size == primRange.getEntries() ) { currentRange = primRange; } else if ( gdp->findPointAttribute( attrName.c_str() ).isValid() && size == pointRange.getEntries() ) { currentRange = pointRange; } else if ( gdp->findVertexAttribute( attrName.c_str() ).isValid() && size == vertexRange.getEntries() ) { currentRange = vertexRange; } // fall back to Cortex standard inferred order else if ( size == primRange.getEntries() ) { currentRange = primRange; } else if ( size == pointRange.getEntries() ) { currentRange = pointRange; } else if ( size == vertexRange.getEntries() ) { currentRange = vertexRange; } else { addWarning( SOP_ATTRIBUTE_INVALID, ( boost::format( "Geometry/Cache mismatch: %s: cache expects %d values for %s." ) % group->getName().toStdString() % size % attrName ).str().c_str() ); continue; } converter->convert( attrName, gdp, currentRange ); } else { converter->convert( attrName, gdp, pointRange ); } } // if transformAttribute is specified, use to to transform the points if ( transformAttribute != "" ) { const TransformationMatrixdData *transform = attributes->member<TransformationMatrixdData>( transformAttribute ); if ( transform ) { UT_Matrix4 matrix( IECore::convert<UT_Matrix4T<double> >( transform->readable().transform() ) ); gdp->transformGroup( matrix, *group ); } else { const TransformationMatrixfData *transform = attributes->member<TransformationMatrixfData>( transformAttribute ); if ( transform ) { UT_Matrix4 matrix = IECore::convert<UT_Matrix4>( transform->readable().transform() ); gdp->transformGroup( matrix, *group ); } } } } unlockInputs(); return error(); }