IECore::ConstFloatVectorDataPtr Shape::computeChannelData( const std::string &channelName, const Imath::V2i &tileOrigin, const Gaffer::Context *context, const ImagePlug *parent ) const { assert( parent == shapePlug() ); if( channelName == g_shapeChannelName ) { // Private channel we use for caching the shape but don't advertise via channelNames. return computeShapeChannelData( tileOrigin, context ); } else { ConstFloatVectorDataPtr shape = parent->channelData( g_shapeChannelName, context->get<V2i>( ImagePlug::tileOriginContextName ) ); const float c = channelValue( parent, channelName ); if( c == 1 ) { return shape; } else { FloatVectorDataPtr resultData = shape->copy(); vector<float> &result = resultData->writable(); for( vector<float>::iterator it = result.begin(), eIt = result.end(); it != eIt; ++it ) { *it *= c; } return resultData; } } }
void operator()( const blocked_range2d<size_t>& r ) const { ContextPtr context = new Context( *m_parentContext ); const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) ); V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min ); V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max ); size_t imageStride = m_dataWindow.size().x + 1; for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize ) { for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize ) { for( vector<string>::const_iterator it = m_channelNames.begin(), eIt = m_channelNames.end(); it != eIt; it++ ) { context->set( ImagePlug::channelNameContextName, *it ); context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) ); Context::Scope scope( context.get() ); Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) ); Box2i b = boxIntersection( tileBound, operationWindow ); ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue(); for( int y = b.min.y; y<=b.max.y; y++ ) { const float *tilePtr = &(tileData->readable()[0]) + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX); float *channelPtr = m_imageChannelData[it-m_channelNames.begin()] + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x); for( int x = b.min.x; x <= b.max.x; x++ ) { *channelPtr++ = *tilePtr++; } } } } } }
Engine( const Box2i &displayWindow, const Box2i &tileBound, const Box2i &validTileBound, ConstFloatVectorDataPtr uData, ConstFloatVectorDataPtr vData, ConstFloatVectorDataPtr aData ) : m_displayWindow( displayWindow ), m_tileBound( tileBound ), m_uData( uData ), m_vData( vData ), m_aData( aData ), m_u( uData->readable() ), m_v( vData->readable() ), m_a( aData->readable() ) { V2i oP; for( oP.y = validTileBound.min.y; oP.y < validTileBound.max.y; ++oP.y ) { size_t i = index( V2i( validTileBound.min.x, oP.y ), tileBound ); for( oP.x = validTileBound.min.x; oP.x < validTileBound.max.x; ++oP.x, ++i ) { if( m_a[i] == 0.0f ) { continue; } const V2f iP = uvToPixel( V2f( m_u[i], m_v[i] ) ); m_inputWindow.extendBy( iP ); } } m_inputWindow.min -= V2i( 1 ); m_inputWindow.max += V2i( 1 ); }
Engine( const Box2i &displayWindow, const Box2i &tileBound, const Box2i &validTileBound, ConstFloatVectorDataPtr xData, ConstFloatVectorDataPtr yData, ConstFloatVectorDataPtr aData, VectorMode vectorMode, VectorUnits vectorUnits ) : m_displayWindow( displayWindow ), m_tileBound( tileBound ), m_xData( xData ), m_yData( yData ), m_aData( aData ), m_x( xData->readable() ), m_y( yData->readable() ), m_a( aData->readable() ), m_vectorMode( vectorMode ), m_vectorUnits( vectorUnits ) { }
virtual void imageData( const Imath::Box2i &box, const float *data, size_t dataSize ) { Box2i yUpBox = m_gafferFormat.yDownToFormatSpace( box ); const V2i boxMinTileOrigin = ImagePlug::tileOrigin( yUpBox.min ); const V2i boxMaxTileOrigin = ImagePlug::tileOrigin( yUpBox.max ); for( int tileOriginY = boxMinTileOrigin.y; tileOriginY <= boxMaxTileOrigin.y; tileOriginY += ImagePlug::tileSize() ) { for( int tileOriginX = boxMinTileOrigin.x; tileOriginX <= boxMaxTileOrigin.x; tileOriginX += ImagePlug::tileSize() ) { for( int channelIndex = 0, numChannels = channelNames().size(); channelIndex < numChannels; ++channelIndex ) { const V2i tileOrigin( tileOriginX, tileOriginY ); ConstFloatVectorDataPtr tileData = getTile( tileOrigin, channelIndex ); if( !tileData ) { // we've been sent data outside of the data window continue; } // we must create a new object to hold the updated tile data, // because the old one might well have been returned from // computeChannelData and be being held in the cache. FloatVectorDataPtr updatedTileData = tileData->copy(); vector<float> &updatedTile = updatedTileData->writable(); const Box2i tileBound( tileOrigin, tileOrigin + Imath::V2i( GafferImage::ImagePlug::tileSize() - 1 ) ); const Box2i transferBound = IECore::boxIntersection( tileBound, yUpBox ); for( int y = transferBound.min.y; y<=transferBound.max.y; ++y ) { int srcY = m_gafferFormat.formatToYDownSpace( y ); size_t srcIndex = ( ( srcY - box.min.y ) * ( box.size().x + 1 ) * numChannels ) + ( transferBound.min.x - box.min.x ) + channelIndex; size_t dstIndex = ( y - tileBound.min.y ) * ImagePlug::tileSize() + transferBound.min.x - tileBound.min.x; const size_t srcEndIndex = srcIndex + transferBound.size().x * numChannels; while( srcIndex <= srcEndIndex ) { updatedTile[dstIndex] = data[srcIndex]; srcIndex += numChannels; dstIndex++; } } setTile( tileOrigin, channelIndex, updatedTileData ); } } } dataReceivedSignal()( this, box ); }
IECore::ConstFloatVectorDataPtr CopyChannels::computeChannelData( const std::string &channelName, const Imath::V2i &tileOrigin, const Gaffer::Context *context, const ImagePlug *parent ) const { ConstCompoundObjectPtr mapping; { ImagePlug::GlobalScope c( context ); mapping = mappingPlug()->getValue(); } if( const IntData *i = mapping->member<const IntData>( channelName ) ) { const ImagePlug *inputImage = inPlugs()->getChild<ImagePlug>( i->readable() ); const V2i tileOrigin = context->get<V2i>( ImagePlug::tileOriginContextName ); const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) ); Box2i inputDataWindow; { ImagePlug::GlobalScope c( context ); inputDataWindow = inputImage->dataWindowPlug()->getValue(); } const Box2i validBound = BufferAlgo::intersection( tileBound, inputDataWindow ); if( validBound == tileBound ) { return inputImage->channelDataPlug()->getValue(); } else { FloatVectorDataPtr resultData = new FloatVectorData; vector<float> &result = resultData->writable(); result.resize( ImagePlug::tileSize() * ImagePlug::tileSize(), 0.0f ); if( !BufferAlgo::empty( validBound ) ) { ConstFloatVectorDataPtr inputData = inputImage->channelDataPlug()->getValue(); copyRegion( &inputData->readable().front(), tileBound, validBound, &result.front(), tileBound, validBound.min ); } return resultData; } } else { return ImagePlug::blackTile(); } }
void ToArnoldShapeConverter::convertRadius( const IECore::Primitive *primitive, AtNode *shape ) const { ConstFloatVectorDataPtr radius = primitive->variableData<FloatVectorData>( "radius" ); if( !radius ) { FloatVectorDataPtr calculatedRadius = new FloatVectorData(); if( const FloatData *constantRadius = primitive->variableData<FloatData>( "radius", PrimitiveVariable::Constant ) ) { calculatedRadius->writable().push_back( constantRadius->readable() ); } else if( const FloatVectorData *width = primitive->variableData<FloatVectorData>( "width" ) ) { calculatedRadius->writable().resize( width->readable().size() ); const std::vector<float>::iterator end = calculatedRadius->writable().end(); std::vector<float>::const_iterator wIt = width->readable().begin(); for( std::vector<float>::iterator it = calculatedRadius->writable().begin(); it != end; it++, wIt++ ) { *it = *wIt / 2.0f; } } else { const FloatData *constantWidth = primitive->variableData<FloatData>( "width", PrimitiveVariable::Constant ); if( !constantWidth ) { constantWidth = primitive->variableData<FloatData>( "constantwidth", PrimitiveVariable::Constant ); } float r = constantWidth ? constantWidth->readable() / 2.0f : 0.5f; calculatedRadius->writable().push_back( r ); } radius = calculatedRadius; } AiNodeSetArray( shape, "radius", AiArrayConvert( radius->readable().size(), 1, AI_TYPE_FLOAT, (void *)&( radius->readable()[0] ) ) ); }
void Unpremultiply::processChannelData( const Gaffer::Context *context, const ImagePlug *parent, const std::string &channel, FloatVectorDataPtr outData ) const { std::string alphaChannel = alphaChannelPlug()->getValue(); if ( channel == alphaChannel ) { return; } ConstStringVectorDataPtr inChannelNamesPtr; { ImagePlug::GlobalScope c( context ); inChannelNamesPtr = inPlug()->channelNamesPlug()->getValue(); } const std::vector<std::string> &inChannelNames = inChannelNamesPtr->readable(); if ( std::find( inChannelNames.begin(), inChannelNames.end(), alphaChannel ) == inChannelNames.end() ) { std::ostringstream channelError; channelError << "Channel '" << alphaChannel << "' does not exist"; throw( IECore::Exception( channelError.str() ) ); } ImagePlug::ChannelDataScope channelDataScope( context ); channelDataScope.setChannelName( alphaChannel ); ConstFloatVectorDataPtr aData = inPlug()->channelDataPlug()->getValue(); const std::vector<float> &a = aData->readable(); std::vector<float> &out = outData->writable(); std::vector<float>::const_iterator aIt = a.begin(); for ( std::vector<float>::iterator outIt = out.begin(), outItEnd = out.end(); outIt != outItEnd; ++outIt, ++aIt ) { if ( *aIt != 0.0f ) { *outIt /= *aIt; } } }
void operator()( const blocked_range3d<size_t>& r ) const { ContextPtr context = new Context( *m_parentContext, Context::Borrowed ); Context::Scope scope( context.get() ); const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) ); V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min ); V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max ); size_t imageStride = m_dataWindow.size().x + 1; for( size_t channelIndex = r.pages().begin(); channelIndex < r.pages().end(); ++channelIndex ) { context->set( ImagePlug::channelNameContextName, m_channelNames[channelIndex] ); float *channelBegin = m_imageChannelData[channelIndex]; for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize ) { for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize ) { context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) ); Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) ); Box2i b = boxIntersection( tileBound, operationWindow ); size_t tileStrideSize = sizeof(float) * ( b.size().x + 1 ); ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue(); const float *tileDataBegin = &(tileData->readable()[0]); for( int y = b.min.y; y<=b.max.y; y++ ) { const float *tilePtr = tileDataBegin + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX); float *channelPtr = channelBegin + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x); std::memcpy( channelPtr, tilePtr, tileStrideSize ); } } } } }
SmoothSkinningData::SmoothSkinningData( ConstStringVectorDataPtr influenceNames, ConstM44fVectorDataPtr influencePose, ConstIntVectorDataPtr pointIndexOffsets, ConstIntVectorDataPtr pointInfluenceCounts, ConstIntVectorDataPtr pointInfluenceIndices, ConstFloatVectorDataPtr pointInfluenceWeights) { assert( influenceNames ); assert( influencePose ); assert( pointIndexOffsets ); assert( pointInfluenceCounts ); assert( pointInfluenceIndices ); assert( pointInfluenceWeights ); m_influenceNames = influenceNames->copy(); m_influencePose = influencePose->copy(); m_pointIndexOffsets = pointIndexOffsets->copy(); m_pointInfluenceCounts = pointInfluenceCounts->copy(); m_pointInfluenceIndices = pointInfluenceIndices->copy(); m_pointInfluenceWeights = pointInfluenceWeights->copy(); }
SimpleSubsurface::SimpleSubsurface( ConstV3fVectorDataPtr p, ConstColor3fVectorDataPtr c, ConstFloatVectorDataPtr a, const SplinefColor3f &falloff ) { m_privateData = boost::shared_ptr<PrivateData>( new PrivateData ); m_privateData->points = p->copy(); m_privateData->colors = c->copy(); const vector<float> &areas = a->readable(); vector<Color3f> &colors = m_privateData->colors->writable(); for( size_t i=0; i<colors.size(); i++ ) { colors[i] *= areas[i]; } m_privateData->tree.init( m_privateData->points->readable().begin(), m_privateData->points->readable().end() ); m_privateData->falloff.init( SplineRemapper( falloff ), 0, 1, 100 ); m_privateData->nodeCentroids.resize( m_privateData->tree.numNodes() ); m_privateData->nodeColors.resize( m_privateData->tree.numNodes() ); m_privateData->nodeBounds.resize( m_privateData->tree.numNodes() ); buildWalk( m_privateData->tree.rootIndex() ); }
ObjectPtr EnvMapSHProjector::doOperation( const CompoundObject *operands ) { ImagePrimitive * image = runTimeCast< ImagePrimitive, Object >( m_envMapParameter->getValue() ); if ( image->getDisplayWindow() != image->getDataWindow() ) { throw Exception( "EnvMapSHProjector only works with images that display and data windows match." ); } unsigned bands = m_bandsParameter->getNumericValue(); unsigned samples = m_samplesParameter->getNumericValue(); bool rightHandSystem = m_rightHandSystemParameter->getTypedValue(); bool applyFilter = m_applyFilterParameter->getTypedValue(); Imath::M44f orientation = m_orientationParameter->getTypedValue(); int imgWidth = image->getDataWindow().size().x + 1; int imgHeight = image->getDataWindow().size().y + 1; // create SH projector IECore::SHProjectorf projector( samples ); projector.computeSamples( bands ); ConstFloatVectorDataPtr redData = image->getChannel< float >( "R" ); ConstFloatVectorDataPtr greenData = image->getChannel< float >( "G" ); ConstFloatVectorDataPtr blueData = image->getChannel< float >( "B" ); if ( !redData || !greenData || !blueData ) { throw Exception( "EnvMap does not have the three colour channels (R,G,B)!" ); } const std::vector<float> &chR = redData->readable(); const std::vector<float> &chG = greenData->readable(); const std::vector<float> &chB = blueData->readable(); // rotate coordinates along X axis so that the image maps Y coordinates to the vertical direction instead of Z. Imath::M44f rotX90 = Imath::Eulerf( M_PI * 0.5, 0, 0 ).toMatrix44(); // \todo: check if the order of multiplication is what we expect... orientation = orientation * rotX90; EuclideanToSphericalTransform< Imath::V3f, Imath::V2f > euc2sph; std::vector< Imath::V3f >::const_iterator cit = projector.euclideanCoordinates().begin(); SHColor3f sh( bands ); unsigned int i; unsigned actualSamples = projector.euclideanCoordinates().size(); Imath::V3f systemConversion(1); if ( !rightHandSystem ) { systemConversion[2] = -systemConversion[2]; } // image to SH for ( i = 0; i < actualSamples; i++, cit++ ) { Imath::V2f phiTheta = euc2sph.transform( ((*cit) * systemConversion) * orientation ); int ix = (int)(phiTheta.x * (float)imgWidth / ( M_PI * 2 )); int iy = (int)(phiTheta.y * (float)imgHeight / M_PI ); if ( ix > imgWidth ) ix = imgWidth; if ( iy > imgHeight ) iy = imgHeight; int offset = iy * imgWidth + ix; projector( i, Imath::Color3f( chR[ offset ], chG[ offset ], chB[ offset ] ), sh ); } // filter SH if ( applyFilter ) { // use author's suggestion for window size. IECore::windowingFilter( sh, 2*sh.bands() ); } Color3fVectorDataPtr result = new Color3fVectorData( sh.coefficients() ); return result; }
PatchMeshPrimitivePtr CurveExtrudeOp::buildPatchMesh( const CurvesPrimitive * curves, unsigned curveIndex, unsigned vertexOffset, unsigned varyingOffset ) const { if ( curves->periodic() ) { throw InvalidArgumentException( "CurveExtrudeOp: Cannot convert periodic curves" ); } PrimitiveVariableMap::const_iterator it = curves->variables.find( "P" ); if ( it == curves->variables.end() ) { throw InvalidArgumentException( "CurveExtrudeOp: Input curve has no 'P' primvar" ); } ConstV3fVectorDataPtr pData = runTimeCast< const V3fVectorData >( it->second.data ); if ( !pData ) { throw InvalidArgumentException( "CurveExtrudeOp: Input curve has no 'P' primvar of type V3fVectorData" ); } float width = 1.0f; it = curves->variables.find( "constantwidth" ); if ( it != curves->variables.end() ) { ConstFloatDataPtr widthData = 0; if ( it->second.interpolation == PrimitiveVariable::Constant ) { widthData = runTimeCast< const FloatData >( it->second.data ); } if ( widthData ) { width = widthData->readable(); } else { msg( Msg::Warning, "CurveExtrudeOp", "Ignoring malformed primvar 'constantwidth'" ); } } ConstFloatVectorDataPtr varyingWidthData = 0; ConstFloatVectorDataPtr vertexWidthData = 0; it = curves->variables.find( "width" ); if ( it != curves->variables.end() ) { if ( it->second.interpolation == PrimitiveVariable::Varying ) { varyingWidthData = runTimeCast< const FloatVectorData >( it->second.data ); } else if ( it->second.interpolation == PrimitiveVariable::Vertex ) { vertexWidthData = runTimeCast< const FloatVectorData >( it->second.data ); } if ( !varyingWidthData && !vertexWidthData) { msg( Msg::Warning, "CurveExtrudeOp", "Ignoring malformed primvar 'width'" ); } } const V2i &resolution = m_resolutionParameter->getTypedValue(); const unsigned int vPoints = resolution.y; const unsigned int uPoints = resolution.x; PatchMeshPrimitivePtr patchMesh = new PatchMeshPrimitive( uPoints, vPoints + 2, // End points are duplicated CubicBasisf::catmullRom(), CubicBasisf::catmullRom(), true, false ); for ( PrimitiveVariableMap::const_iterator it = curves->variables.begin(); it != curves->variables.end(); ++it ) { if ( it->second.interpolation == PrimitiveVariable::FaceVarying || it->second.interpolation == PrimitiveVariable::Varying ) { VaryingFn varyingFn( it->first, curves, curveIndex, varyingOffset, resolution ); assert( it->second.data ); patchMesh->variables[ it->first ] = PrimitiveVariable( it->second.interpolation, despatchTypedData<VaryingFn, TypeTraits::IsStrictlyInterpolableVectorTypedData>( it->second.data, varyingFn ) ); } else if ( it->second.interpolation == PrimitiveVariable::Vertex ) { VertexFn vertexFn( it->first, curves, curveIndex, vertexOffset, resolution ); assert( it->second.data ); patchMesh->variables[ it->first ] = PrimitiveVariable( it->second.interpolation, despatchTypedData<VertexFn, TypeTraits::IsStrictlyInterpolableVectorTypedData>( it->second.data, vertexFn ) ); } else if ( it->second.interpolation == PrimitiveVariable::Constant ) { patchMesh->variables[ it->first ] = PrimitiveVariable( it->second.interpolation, it->second.data->copy() ); } else if ( it->second.interpolation == PrimitiveVariable::Uniform ) { UniformFn uniformFn( it->first, curves, curveIndex ); patchMesh->variables[ it->first ] = PrimitiveVariable( PrimitiveVariable::Constant, despatchTypedData<UniformFn, TypeTraits::IsVectorTypedData>( it->second.data, uniformFn ) ); } } if ( varyingWidthData ) { assert( !vertexWidthData ); PrimitiveVariableMap::const_iterator it = patchMesh->variables.find( "width" ); assert( it != patchMesh->variables.end() ); varyingWidthData = runTimeCast< const FloatVectorData >( it->second.data ); assert( varyingWidthData ); } else if ( vertexWidthData ) { PrimitiveVariableMap::const_iterator it = patchMesh->variables.find( "width" ); assert( it != patchMesh->variables.end() ); vertexWidthData = runTimeCast< const FloatVectorData >( it->second.data ); assert( vertexWidthData ); } const V3fVectorData::ValueType &p = pData->readable(); V3fVectorData::ValueType resampledPoints; resampledPoints.reserve( vPoints ); V3fVectorData::ValueType resampledTangents; resampledPoints.reserve( vPoints ); /// \todo Make adaptive for ( unsigned v = 0; v < vPoints; v ++) { size_t iSeg; float fSeg; /// Make sure we don't fall off the end of the curve if ( v == vPoints - 1 ) { iSeg = curves->numSegments( curveIndex ) - 1; fSeg = 1.0f - std::numeric_limits<float>::epsilon(); } else { float curveParam = float(v) / ( vPoints - 1 ); fSeg = curveParam * curves->numSegments( curveIndex ); iSeg = (size_t)floor( fSeg ); fSeg = fSeg - iSeg; } size_t segmentStart = iSeg; size_t i0 = std::min( segmentStart + 0, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) ); size_t i1 = std::min( segmentStart + 1, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) ); size_t i2 = std::min( segmentStart + 2, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) ); size_t i3 = std::min( segmentStart + 3, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) ); const Imath::V3f &p0 = p[ vertexOffset + i0 ]; const Imath::V3f &p1 = p[ vertexOffset + i1 ]; const Imath::V3f &p2 = p[ vertexOffset + i2 ]; const Imath::V3f &p3 = p[ vertexOffset + i3 ]; Imath::V3f pt = curves->basis()( fSeg, p0, p1, p2, p3 ); resampledPoints.push_back( pt ); resampledTangents.push_back( curves->basis().derivative( fSeg, p0, p1, p2, p3 ).normalized() ); } assert( resampledPoints.size() == vPoints ); assert( resampledTangents.size() == vPoints ); std::vector< M44f > frames; buildReferenceFrames( resampledPoints, resampledTangents, frames ); assert( frames.size() == vPoints ); std::vector< V3f > patchP; patchP.reserve( uPoints * ( vPoints + 2 ) ); for ( unsigned int v = 0; v < vPoints; v++ ) { if ( varyingWidthData ) { assert( !vertexWidthData ); assert( v * uPoints < varyingWidthData->readable().size() ); width = varyingWidthData->readable()[v * uPoints]; } else if ( vertexWidthData ) { assert( (v+1) * uPoints < vertexWidthData->readable().size() ); width = vertexWidthData->readable()[(v+1) * uPoints]; } const float radius = width / 2.0f; /// Double up end points const int num = v == 0 || v == vPoints - 1 ? 2 : 1; for ( int x = 0; x < num; x++) { for( unsigned int u = 0; u < uPoints; u++ ) { /// We're periodic in 'u', so no need to close the curve. /// Go from -PI to PI, in order to make the periodicity work, and to give the /// surface the correct orientation. float theta = -2.0 * M_PI * float(u) / float(uPoints) - M_PI; V3f circlePoint( 0.0, radius * cos( theta ), radius * sin( theta ) ); circlePoint = circlePoint * frames[v]; patchP.push_back( circlePoint ); } } } patchMesh->variables["P"] = PrimitiveVariable( PrimitiveVariable::Vertex, new V3fVectorData( patchP ) ); assert( patchMesh->arePrimitiveVariablesValid() ); return patchMesh; }
ObjectPtr EnvMapSampler::doOperation( const CompoundObject * operands ) { ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy(); Box2i dataWindow = image->getDataWindow(); // find the rgb channels ConstFloatVectorDataPtr redData = image->getChannel<float>( "R" ); ConstFloatVectorDataPtr greenData = image->getChannel<float>( "G" ); ConstFloatVectorDataPtr blueData = image->getChannel<float>( "B" ); if( !(redData && greenData && blueData) ) { throw Exception( "Image does not contain valid RGB float channels." ); } const vector<float> &red = redData->readable(); const vector<float> &green = greenData->readable(); const vector<float> &blue = blueData->readable(); // get a luminance channel LuminanceOpPtr luminanceOp = new LuminanceOp(); luminanceOp->inputParameter()->setValue( image ); luminanceOp->copyParameter()->getTypedValue() = false; luminanceOp->removeColorPrimVarsParameter()->getTypedValue() = false; luminanceOp->operate(); // do the median cut thing to get some samples MedianCutSamplerPtr sampler = new MedianCutSampler; sampler->imageParameter()->setValue( image ); sampler->subdivisionDepthParameter()->setNumericValue( subdivisionDepthParameter()->getNumericValue() ); ConstCompoundObjectPtr samples = boost::static_pointer_cast<CompoundObject>( sampler->operate() ); const vector<V2f> ¢roids = boost::static_pointer_cast<V2fVectorData>( samples->members().find( "centroids" )->second )->readable(); const vector<Box2i> &areas = boost::static_pointer_cast<Box2iVectorData>( samples->members().find( "areas" )->second )->readable(); // get light directions and colors from the samples V3fVectorDataPtr directionsData = new V3fVectorData; Color3fVectorDataPtr colorsData = new Color3fVectorData; vector<V3f> &directions = directionsData->writable(); vector<Color3f> &colors = colorsData->writable(); float radiansPerPixel = M_PI / (dataWindow.size().y + 1); float angleAtTop = ( M_PI - radiansPerPixel ) / 2.0f; for( unsigned i=0; i<centroids.size(); i++ ) { const Box2i &area = areas[i]; Color3f color( 0 ); for( int y=area.min.y; y<=area.max.y; y++ ) { int yRel = y - dataWindow.min.y; float angle = angleAtTop - yRel * radiansPerPixel; float weight = cosf( angle ); int index = (area.min.x - dataWindow.min.x) + (dataWindow.size().x + 1 ) * yRel; for( int x=area.min.x; x<=area.max.x; x++ ) { color[0] += weight * red[index]; color[1] += weight * green[index]; color[2] += weight * blue[index]; index++; } } color /= red.size(); colors.push_back( color ); float phi = angleAtTop - (centroids[i].y - dataWindow.min.y) * radiansPerPixel; V3f direction; direction.y = sinf( phi ); float r = cosf( phi ); float theta = 2 * M_PI * lerpfactor( (float)centroids[i].x, (float)dataWindow.min.x, (float)dataWindow.max.x ); direction.x = r * cosf( theta ); direction.z = r * sinf( theta ); directions.push_back( -direction ); // negated so we output the direction the light shines in } // return the result CompoundObjectPtr result = new CompoundObject; result->members()["directions"] = directionsData; result->members()["colors"] = colorsData; return result; }
IECore::ConstFloatVectorDataPtr Mix::computeChannelData( const std::string &channelName, const Imath::V2i &tileOrigin, const Gaffer::Context *context, const ImagePlug *parent ) const { const float mix = mixPlug()->getValue(); if( mix == 0.0f ) { return inPlugs()->getChild< ImagePlug>( 0 )->channelDataPlug()->getValue(); } else if( mix == 1.0f && !maskPlug()->getInput<ValuePlug>() ) { return inPlugs()->getChild< ImagePlug >( 1 )->channelDataPlug()->getValue(); } const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) ); IECore::ConstStringVectorDataPtr maskChannelNamesData; Box2i maskDataWindow; { ImagePlug::GlobalScope c( Context::current() ); maskChannelNamesData = maskPlug()->channelNamesPlug()->getValue(); maskDataWindow = maskPlug()->dataWindowPlug()->getValue(); } const std::string &maskChannel = maskChannelPlug()->getValue(); ConstFloatVectorDataPtr maskData = NULL; Box2i maskValidBound; if( maskPlug()->getInput<ValuePlug>() && ImageAlgo::channelExists( maskChannelNamesData->readable(), maskChannel ) ) { maskData = maskPlug()->channelData( maskChannel, tileOrigin ); maskValidBound = boxIntersection( tileBound, maskDataWindow ); } ConstFloatVectorDataPtr channelData[2]; Box2i validBound[2]; int i = 0; for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it,++i ) { IECore::ConstStringVectorDataPtr channelNamesData; Box2i dataWindow; { ImagePlug::GlobalScope c( Context::current() ); channelNamesData = (*it)->channelNamesPlug()->getValue(); dataWindow = (*it)->dataWindowPlug()->getValue(); } const std::vector<std::string> &channelNames = channelNamesData->readable(); if( ImageAlgo::channelExists( channelNames, channelName ) ) { channelData[i] = (*it)->channelDataPlug()->getValue(); validBound[i] = boxIntersection( tileBound, dataWindow ); } else { channelData[i] = NULL; validBound[i] = Box2i(); } } FloatVectorDataPtr resultData = ImagePlug::blackTile()->copy(); float *R = &resultData->writable().front(); const float *A = channelData[0] ? &channelData[0]->readable().front() : NULL; const float *B = channelData[1] ? &channelData[1]->readable().front() : NULL; const float *M = maskData ? &maskData->readable().front() : NULL; for( int y = tileBound.min.y; y < tileBound.max.y; ++y ) { const bool yValidIn0 = y >= validBound[0].min.y && y < validBound[0].max.y; const bool yValidIn1 = y >= validBound[1].min.y && y < validBound[1].max.y; const bool yValidMask = y >= maskValidBound.min.y && y < maskValidBound.max.y; for( int x = tileBound.min.x; x < tileBound.max.x; ++x ) { float a = 0; if( yValidIn0 && x >= validBound[0].min.x && x < validBound[0].max.x ) { a = *A; } float b = 0; if( yValidIn1 && x >= validBound[1].min.x && x < validBound[1].max.x ) { b = *B; } float m = mix; if( yValidMask && x >= maskValidBound.min.x && x < maskValidBound.max.x ) { m *= std::max( 0.0f, std::min( 1.0f, *M ) ); } *R = a * ( 1 - m ) + b * m; ++R; ++A; ++B; ++M; } } return resultData; }
void NURBSPrimitive::setTopology( int uOrder, ConstFloatVectorDataPtr uKnot, float uMin, float uMax, int vOrder, ConstFloatVectorDataPtr vKnot, float vMin, float vMax ) { // check order isn't too small if( uOrder<2 ) { throw Exception( "Order in u direction too small." ); } if( vOrder<2 ) { throw Exception( "Order in v direction too small." ); } // check knots have enough entries for the order. // an order of N demands at least N control points // and numKnots==numControlPoints + order // so we need numKnots>=2*order if( (int)uKnot->readable().size() < uOrder * 2 ) { throw Exception( "Not enough knot values in u direction." ); } if( (int)vKnot->readable().size() < vOrder * 2 ) { throw Exception( "Not enough knot values in v direction." ); } // check knots are monotonically increasing const vector<float> &u = uKnot->readable(); float previous = u[0]; for( unsigned int i=0; i<u.size(); i++ ) { if( u[i]<previous ) { throw Exception( "Knots not monotonically increasing in u direction." ); } previous = u[i]; } const vector<float> &v = vKnot->readable(); previous = v[0]; for( unsigned int i=0; i<v.size(); i++ ) { if( v[i]<previous ) { throw Exception( "Knots not monotonically increasing in v direction." ); } previous = v[i]; } // check min and max parametric values are in range if( uMin > uMax ) { throw Exception( "uMin greater than uMax." ); } if( vMin > vMax ) { throw Exception( "vMin greater than vMax." ); } if( uMin < u[uOrder-2] ) { throw Exception( "uMin too small." ); } if( uMax > u[u.size()-uOrder+1] ) { throw Exception( "uMax too great." ); } if( vMin < v[vOrder-2] ) { throw Exception( "vMin too small." ); } if( vMax > v[v.size()-vOrder+1] ) { throw Exception( "vMax too great." ); } // set everything (taking copies of the data) m_uOrder = uOrder; m_uKnot = uKnot->copy(); m_uMin = uMin; m_uMax = uMax; m_vOrder = vOrder; m_vKnot = vKnot->copy(); m_vMin = vMin; m_vMax = vMax; }
void FaceAreaOp::modifyTypedPrimitive( MeshPrimitive * mesh, const CompoundObject * operands ) { string areaPrimVarName = parameters()->parameter<StringParameter>( "areaPrimVar" )->getTypedValue(); if( areaPrimVarName!="" ) { const string &pName = parameters()->parameter<StringParameter>( "pointPrimVar" )->getTypedValue(); ConstV3fVectorDataPtr pData = mesh->variableData<V3fVectorData>( pName, PrimitiveVariable::Vertex ); if( !pData ) { throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no \"%s\" primitive variable." ) % pName ) ); } const vector<V3f> &p = pData->readable(); FloatVectorDataPtr areasData = new FloatVectorData; vector<float> &areas = areasData->writable(); areas.reserve( mesh->variableSize( PrimitiveVariable::Uniform ) ); PolygonIterator faceEnd = mesh->faceEnd(); for( PolygonIterator pIt = mesh->faceBegin(); pIt!=faceEnd; pIt++ ) { typedef vector<V3f> PointVector; areas.push_back( polygonArea( pIt.vertexBegin( p.begin() ), pIt.vertexEnd( p.begin() ) ) ); } mesh->variables[areaPrimVarName] = PrimitiveVariable( PrimitiveVariable::Uniform, areasData ); } string textureAreaPrimVarName = parameters()->parameter<StringParameter>( "textureAreaPrimVar" )->getTypedValue(); if( textureAreaPrimVarName!="" ) { const string &sName = parameters()->parameter<StringParameter>( "sPrimVar" )->getTypedValue(); PrimitiveVariable::Interpolation sInterpolation = PrimitiveVariable::Vertex; ConstFloatVectorDataPtr sData = mesh->variableData<FloatVectorData>( sName, PrimitiveVariable::Vertex ); if( !sData ) { sData = mesh->variableData<FloatVectorData>( sName, PrimitiveVariable::FaceVarying ); if( !sData ) { throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no suitable \"%s\" primitive variable." ) % sName ) ); } sInterpolation = PrimitiveVariable::FaceVarying; } const vector<float> &s = sData->readable(); const string &tName = parameters()->parameter<StringParameter>( "tPrimVar" )->getTypedValue(); PrimitiveVariable::Interpolation tInterpolation = PrimitiveVariable::Vertex; ConstFloatVectorDataPtr tData = mesh->variableData<FloatVectorData>( tName, PrimitiveVariable::Vertex ); if( !tData ) { tData = mesh->variableData<FloatVectorData>( tName, PrimitiveVariable::FaceVarying ); if( !tData ) { throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no suitable \"%s\" primitive variable." ) % tName ) ); } tInterpolation = PrimitiveVariable::FaceVarying; } const vector<float> &t = tData->readable(); if( sInterpolation!=tInterpolation ) { throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : interpolation for \"%s\" and \"%s\" primitive variables don't match." ) % sName % tName ) ); } FloatVectorDataPtr textureAreasData = new FloatVectorData; vector<float> &textureAreas = textureAreasData->writable(); textureAreas.reserve( mesh->variableSize( PrimitiveVariable::Uniform ) ); PolygonIterator faceEnd = mesh->faceEnd(); for( PolygonIterator pIt = mesh->faceBegin(); pIt!=faceEnd; pIt++ ) { if( sInterpolation==PrimitiveVariable::Vertex ) { typedef PolygonVertexIterator<vector<float>::const_iterator> VertexIterator; typedef boost::tuple<VertexIterator, VertexIterator> IteratorTuple; typedef boost::zip_iterator<IteratorTuple> ZipIterator; typedef boost::transform_iterator<STTupleToV3f, ZipIterator> STIterator; STIterator begin( ZipIterator( IteratorTuple( pIt.vertexBegin( s.begin() ), pIt.vertexBegin( t.begin() ) ) ) ); STIterator end( ZipIterator( IteratorTuple( pIt.vertexEnd( s.begin() ), pIt.vertexEnd( t.begin() ) ) ) ); textureAreas.push_back( polygonArea( begin, end ) ); } else { assert( sInterpolation==PrimitiveVariable::FaceVarying ); typedef boost::tuple<vector<float>::const_iterator, vector<float>::const_iterator> IteratorTuple; typedef boost::zip_iterator<IteratorTuple> ZipIterator; typedef boost::transform_iterator<STTupleToV3f, ZipIterator> STIterator; STIterator begin( ZipIterator( IteratorTuple( pIt.faceVaryingBegin( s.begin() ), pIt.faceVaryingBegin( t.begin() ) ) ) ); STIterator end( ZipIterator( IteratorTuple( pIt.faceVaryingEnd( s.begin() ), pIt.faceVaryingEnd( t.begin() ) ) ) ); textureAreas.push_back( polygonArea( begin, end ) ); } } mesh->variables[textureAreaPrimVarName] = PrimitiveVariable( PrimitiveVariable::Uniform, textureAreasData ); } }
void RenderManShader::loadShaderParameters( const IECore::Shader *shader, Gaffer::CompoundPlug *parametersPlug, bool keepExistingValues ) { const CompoundData *typeHints = shader->blindData()->member<CompoundData>( "ri:parameterTypeHints", true ); const StringVectorData *orderedParameterNamesData = shader->blindData()->member<StringVectorData>( "ri:orderedParameterNames", true ); const vector<string> &orderedParameterNames = orderedParameterNamesData->readable(); const StringVectorData *outputParameterNamesData = shader->blindData()->member<StringVectorData>( "ri:outputParameterNames", true ); const vector<string> &outputParameterNames = outputParameterNamesData->readable(); const CompoundData *annotations = shader->blindData()->member<CompoundData>( "ri:annotations", true ); // if we're not preserving existing values then remove all existing parameter plugs - the various // plug creators above know that if a plug exists then they should preserve its values. if( !keepExistingValues ) { for( int i = parametersPlug->children().size() - 1; i >= 0; --i ) { parametersPlug->removeChild( parametersPlug->getChild<GraphComponent>( i ) ); } } // make sure we have a plug to represent each parameter, reusing plugs wherever possible. set<string> validPlugNames; for( vector<string>::const_iterator it = orderedParameterNames.begin(), eIt = orderedParameterNames.end(); it != eIt; it++ ) { if( std::find( outputParameterNames.begin(), outputParameterNames.end(), *it ) != outputParameterNames.end() ) { continue; } // splines are represented by two parameters matched by a naming convention, and we map // those two parameters to a single SplinePlug. const bool endsWithValues = ends_with( *it, "Values" ); const bool endsWithPositions = ends_with( *it, "Positions" ); if( endsWithPositions || endsWithValues ) { string plugName( *it, 0, it->size() - ( endsWithValues ? 6 : 9 ) ); if( validPlugNames.find( plugName ) != validPlugNames.end() ) { continue; } // must use a smart pointers here because we may assign the data the parser creates (and which we therefore own) ConstFloatVectorDataPtr positions = shader->parametersData()->member<FloatVectorData>( plugName + "Positions" ); ConstDataPtr values = shader->parametersData()->member<Data>( plugName + "Values" ); if( positions && values ) { const StringData *defaultValuesAnnotation = annotations->member<StringData>( plugName + "Values.defaultValue" ); const StringData *defaultPositionsAnnotation = annotations->member<StringData>( plugName + "Positions.defaultValue" ); if( defaultValuesAnnotation ) { DataPtr parsedValues; if( values->isInstanceOf( Color3fVectorData::staticTypeId() ) ) { parsedValues = parseColors( defaultValuesAnnotation->readable() ); } else { parsedValues = parseFloats( defaultValuesAnnotation->readable() ); } if( parsedValues ) { values = parsedValues; } else { msg( Msg::Warning, "RenderManShader::loadShaderParameters", boost::format( "Unable to parse default value \"%s\" for parameter \"%s\"" ) % defaultValuesAnnotation->readable() % ( plugName + "Values" ) ); } } if( defaultPositionsAnnotation ) { FloatVectorDataPtr parsedPositions = parseFloats( defaultPositionsAnnotation->readable() ); if( parsedPositions ) { positions = parsedPositions; } else { msg( Msg::Warning, "RenderManShader::loadShaderParameters", boost::format( "Unable to parse default value \"%s\" for parameter \"%s\"" ) % defaultPositionsAnnotation->readable() % ( plugName + "Positions" ) ); } } switch( values->typeId() ) { case FloatVectorDataTypeId : loadSplineParameter<SplineffPlug>( parametersPlug, plugName, positions.get(), values.get() ); break; case Color3fVectorDataTypeId : loadSplineParameter<SplinefColor3fPlug>( parametersPlug, plugName, positions.get(), values.get() ); break; default : msg( Msg::Warning, "RenderManShader::loadShaderParameters", boost::format( "Spline \"%s\" has unsupported value type \"%s\"" ) % plugName % values->typeName() ); } validPlugNames.insert( plugName ); continue; } } // the other parameter types map more simply to a single plug each. const StringData *typeHint = typeHints->member<StringData>( *it, false ); const Data *defaultValue = shader->parametersData()->member<Data>( *it ); switch( defaultValue->typeId() ) { case StringDataTypeId : if( typeHint && typeHint->readable() == "shader" ) { loadCoshaderParameter( parametersPlug, *it ); } else { loadParameter<StringPlug>( parametersPlug, *it, static_cast<const StringData *>( defaultValue )->readable() ); } break; case FloatDataTypeId : loadNumericParameter( parametersPlug, *it, static_cast<const FloatData *>( defaultValue )->readable(), annotations ); break; case Color3fDataTypeId : loadCompoundNumericParameter<Color3fPlug>( parametersPlug, *it, static_cast<const Color3fData *>( defaultValue )->readable(), annotations ); break; case V3fDataTypeId : loadCompoundNumericParameter<V3fPlug>( parametersPlug, *it, static_cast<const V3fData *>( defaultValue )->readable(), annotations ); break; case StringVectorDataTypeId : if( typeHint && typeHint->readable() == "shader" ) { loadCoshaderArrayParameter( parametersPlug, *it, static_cast<const StringVectorData *>( defaultValue ) ); } else { loadArrayParameter<StringVectorDataPlug>( parametersPlug, *it, defaultValue, annotations ); } break; case FloatVectorDataTypeId : loadArrayParameter<FloatVectorDataPlug>( parametersPlug, *it, defaultValue, annotations ); break; case Color3fVectorDataTypeId : loadArrayParameter<Color3fVectorDataPlug>( parametersPlug, *it, defaultValue, annotations ); break; case V3fVectorDataTypeId : loadArrayParameter<V3fVectorDataPlug>( parametersPlug, *it, defaultValue, annotations ); break; default : msg( Msg::Warning, "RenderManShader::loadShaderParameters", boost::format( "Parameter \"%s\" has unsupported type \"%s\"" ) % *it % defaultValue->typeName() ); } validPlugNames.insert( *it ); } // remove any old plugs which it turned out we didn't need if( keepExistingValues ) { for( int i = parametersPlug->children().size() - 1; i >= 0; --i ) { GraphComponent *child = parametersPlug->getChild<GraphComponent>( i ); if( validPlugNames.find( child->getName().string() ) == validPlugNames.end() ) { parametersPlug->removeChild( child ); } } } }
IECore::ConstFloatVectorDataPtr Merge::merge( F f, const std::string &channelName, const Imath::V2i &tileOrigin ) const { FloatVectorDataPtr resultData = NULL; // Temporary buffer for computing the alpha of intermediate composited layers. FloatVectorDataPtr resultAlphaData = NULL; const Box2i tileBound( tileOrigin, tileOrigin + V2i( ImagePlug::tileSize() ) ); for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it ) { if( !(*it)->getInput<ValuePlug>() ) { continue; } IECore::ConstStringVectorDataPtr channelNamesData = (*it)->channelNamesPlug()->getValue(); const std::vector<std::string> &channelNames = channelNamesData->readable(); ConstFloatVectorDataPtr channelData; ConstFloatVectorDataPtr alphaData; if( channelExists( channelNames, channelName ) ) { channelData = (*it)->channelDataPlug()->getValue(); } else { channelData = ImagePlug::blackTile(); } if( channelExists( channelNames, "A" ) ) { alphaData = (*it)->channelData( "A", tileOrigin ); } else { alphaData = ImagePlug::blackTile(); } const Box2i validBound = boxIntersection( tileBound, (*it)->dataWindowPlug()->getValue() ); if( !resultData ) { // The first connected layer, with which we must initialise our result. // There's no guarantee that this layer actually covers the full data // window though (the data window could have been expanded by the upper // layers) so we must take care to mask out any invalid areas of the input. /// \todo I'm not convinced this is correct - if we have no connection /// to in[0] then should that not be treated as being a black image, so /// we should unconditionally initaliase with in[0] and then always use /// the operation for in[1:], even if in[0] is disconnected. In other /// words, shouldn't multiplying a white constant over an unconnected /// in[0] produce black? resultData = channelData->copy(); resultAlphaData = alphaData->copy(); float *B = &resultData->writable().front(); float *b = &resultAlphaData->writable().front(); for( int y = tileBound.min.y; y < tileBound.max.y; ++y ) { const bool yValid = y >= validBound.min.y && y < validBound.max.y; for( int x = tileBound.min.x; x < tileBound.max.x; ++x ) { if( !yValid || x < validBound.min.x || x >= validBound.max.x ) { *B = *b = 0.0f; } ++B; ++b; } } } else { // A higher layer (A) which must be composited over the result (B). const float *A = &channelData->readable().front(); float *B = &resultData->writable().front(); const float *a = &alphaData->readable().front(); float *b = &resultAlphaData->writable().front(); for( int y = tileBound.min.y; y < tileBound.max.y; ++y ) { const bool yValid = y >= validBound.min.y && y < validBound.max.y; for( int x = tileBound.min.x; x < tileBound.max.x; ++x ) { const bool valid = yValid && x >= validBound.min.x && x < validBound.max.x; *B = f( valid ? *A : 0.0f, *B, valid ? *a : 0.0f, *b ); *b = f( valid ? *a : 0.0f, *b, valid ? *a : 0.0f, *b ); ++A; ++B; ++a; ++b; } } } } return resultData; }