//-***************************************************************************** void ReadParticles( const std::string &iFileName ) { IArchive archive( Alembic::AbcCoreOgawa::ReadArchive(), iFileName ); IObject topObj( archive, kTop ); IPoints points( topObj, "simpleParticles" ); IPointsSchema& pointsSchema = points.getSchema(); index_t numSamps = pointsSchema.getNumSamples(); std::cout << "\n\nReading points back in. Num frames: " << numSamps << std::endl; IV3fArrayProperty velProp( pointsSchema, "velocity" ); IC3fArrayProperty rgbProp( pointsSchema, "Cs" ); IFloatArrayProperty ageProp( pointsSchema, "age" ); for ( index_t samp = 0; samp < numSamps; ++samp ) { IPointsSchema::Sample psamp; pointsSchema.get( psamp, samp ); Box3f bounds; bounds.makeEmpty(); size_t numPoints = psamp.getPositions()->size(); for ( size_t p = 0; p < numPoints; ++p ) { bounds.extendBy( (*(psamp.getPositions()))[p] ); } std::cout << "Sample: " << samp << ", numPoints: " << numPoints << ", bounds: " << bounds.min << " to " << bounds.max << std::endl; } }
Imath::Box3f SceneNode::unionOfTransformedChildBounds( const ScenePath &path, const ScenePlug *out, const IECore::InternedStringVectorData *childNamesData ) const { ConstInternedStringVectorDataPtr computedChildNames; if( !childNamesData ) { computedChildNames = out->childNames( path ); childNamesData = computedChildNames.get(); } const vector<InternedString> &childNames = childNamesData->readable(); Box3f result; if( childNames.size() ) { ContextPtr tmpContext = new Context( *Context::current(), Context::Borrowed ); Context::Scope scopedContext( tmpContext.get() ); ScenePath childPath( path ); childPath.push_back( InternedString() ); // room for the child name for( vector<InternedString>::const_iterator it = childNames.begin(); it != childNames.end(); it++ ) { childPath[path.size()] = *it; tmpContext->set( ScenePlug::scenePathContextName, childPath ); Box3f childBound = out->boundPlug()->getValue(); childBound = transform( childBound, out->transformPlug()->getValue() ); result.extendBy( childBound ); } } return result; }
void BackdropNodeGadget::frame( const std::vector<Gaffer::Node *> &nodes ) { GraphGadget *graph = ancestor<GraphGadget>(); if( !graph ) { return; } Box3f b; for( std::vector<Node *>::const_iterator it = nodes.begin(), eIt = nodes.end(); it != eIt; ++it ) { NodeGadget *nodeGadget = graph->nodeGadget( *it ); if( nodeGadget ) { b.extendBy( nodeGadget->transformedBound( NULL ) ); } } if( b.isEmpty() ) { return; } graph->setNodePosition( node(), V2f( b.center().x, b.center().y ) ); V2f s( b.size().x / 2.0f, b.size().y / 2.0f ); boundPlug()->setValue( Box2f( V2f( -s ) - V2f( g_margin ), V2f( s ) + V2f( g_margin + 2.0f * g_margin ) ) ); }
Imath::Box3f Instancer::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const { ContextPtr ic = instanceContext( context, branchPath ); if( ic ) { Context::Scope scopedContext( ic ); return instancePlug()->boundPlug()->getValue(); } // branchPath == "/" Box3f result; ConstV3fVectorDataPtr p = sourcePoints( parentPath ); if( p ) { ScenePath branchChildPath( branchPath ); branchChildPath.push_back( InternedString() ); // where we'll place the instance index for( size_t i=0; i<p->readable().size(); i++ ) { /// \todo We could have a very fast InternedString( int ) constructor rather than all this lexical cast nonsense branchChildPath[branchChildPath.size()-1] = boost::lexical_cast<string>( i ); Box3f branchChildBound = computeBranchBound( parentPath, branchChildPath, context ); branchChildBound = transform( branchChildBound, computeBranchTransform( parentPath, branchChildPath, context ) ); result.extendBy( branchChildBound ); } } return result; }
Imath::Box3f Group::computeBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const { std::string groupName = namePlug()->getValue(); if( path.size() <= 1 ) { // either / or /groupName Box3f combinedBound; for( ScenePlugIterator it( inPlugs() ); it != it.end(); ++it ) { // we don't need to transform these bounds, because the SceneNode // guarantees that the transform for root nodes is always identity. Box3f bound = (*it)->bound( ScenePath() ); combinedBound.extendBy( bound ); } if( path.size() == 0 ) { combinedBound = transform( combinedBound, transformPlug()->matrix() ); } return combinedBound; } else { const ScenePlug *sourcePlug = 0; ScenePath source = sourcePath( path, groupName, &sourcePlug ); return sourcePlug->bound( source ); } }
Imath::Box3f SceneProcedural::bound() const { /// \todo I think we should be able to remove this exception handling in the future. /// Either when we do better error handling in ValuePlug computations, or when /// the bug in IECoreGL that caused the crashes in SceneProceduralTest.testComputationErrors /// is fixed. try { ContextPtr timeContext = new Context( *m_context ); Context::Scope scopedTimeContext( timeContext ); /// \todo This doesn't take account of the unfortunate fact that our children may have differing /// numbers of segments than ourselves. To get an accurate bound we would need to know the different sample /// times the children may be using and evaluate a bound at those times as well. We don't want to visit /// the children to find the sample times out though, because that defeats the entire point of deferred loading. /// /// Here are some possible approaches : /// /// 1) Add a new attribute called boundSegments, which defines the number of segments used to calculate /// the bounding box. It would be the responsibility of the user to set this to an appropriate value /// at the parent levels, so that the parents calculate bounds appropriate for the children. /// This seems like a bit too much burden on the user. /// /// 2) Add a global option called "maxSegments" - this will clamp the number of segments used on anything /// and will be set to 1 by default. The user will need to increase it to allow the leaf level attributes /// to take effect, and all bounding boxes everywhere will be calculated using that number of segments /// (actually I think it'll be that number of segments and all nondivisible smaller numbers). This should /// be accurate but potentially slower, because we'll be doing the extra work everywhere rather than only /// where needed. It still places a burden on the user (increasing the global clamp appropriately), /// but not quite such a bad one as they don't have to figure anything out and only have one number to set. /// /// 3) Have the StandardOptions node secretly compute a global "maxSegments" behind the scenes. This would /// work as for 2) but remove the burden from the user. However, it would mean preventing any expressions /// or connections being used on the segments attributes, because they could be used to cheat the system. /// It could potentially be faster than 2) because it wouldn't have to do all nondivisible numbers - it /// could know exactly which numbers of segments were in existence. It still suffers from the /// "pay the price everywhere" problem. std::set<float> times; motionTimes( ( m_options.deformationBlur && m_attributes.deformationBlur ) ? m_attributes.deformationBlurSegments : 0, times ); motionTimes( ( m_options.transformBlur && m_attributes.transformBlur ) ? m_attributes.transformBlurSegments : 0, times ); Box3f result; for( std::set<float>::const_iterator it = times.begin(), eIt = times.end(); it != eIt; it++ ) { timeContext->setFrame( *it ); Box3f b = m_scenePlug->boundPlug()->getValue(); M44f t = m_scenePlug->transformPlug()->getValue(); result.extendBy( transform( b, t ) ); } return result; } catch( const std::exception &e ) { IECore::msg( IECore::Msg::Error, "SceneProcedural::bound()", e.what() ); } return Box3f(); }
static Box3fDataPtr bound3( const P *pData, const R *rData, float rMult, typename V::ConstPtr vData, float vMult ) { typedef typename P::ValueType::value_type Point; typedef typename V::ValueType::value_type Vector; typedef typename R::ValueType::value_type Radius; Box3f result; const typename P::ValueType &pVector = pData->readable(); size_t vLength = 0; const Vector *v = 0; if( vData && vData->readable().size() ) { vLength = vData->readable().size(); v = &(vData->readable()[0]); } size_t rLength = 0; const Radius *r = 0; if( rData && rData->readable().size() ) { rLength = rData->readable().size(); r = &(rData->readable()[0]); } size_t i = 0; for( typename P::ValueType::const_iterator pIt = pVector.begin(); pIt!=pVector.end(); pIt++ ) { Box3f b; b.extendBy( *pIt ); if( v && i<vLength ) { b.extendBy( *pIt + v[i] * vMult ); } if( r && i<rLength ) { Point rr( r[i] * rMult ); b.min -= rr; b.max += rr; } result.extendBy( b ); i++; } return new Box3fData( result ); }
Imath::Box3f StandardConnectionGadget::bound() const { const_cast<StandardConnectionGadget *>( this )->setPositionsFromNodules(); Box3f r; r.extendBy( m_srcPos ); r.extendBy( m_dstPos ); return r; }
void SimpleSubsurface::buildWalk( Tree::NodeIndex nodeIndex ) { const Tree::Node &node = m_privateData->tree.node( nodeIndex ); if( node.isLeaf() ) { float totalWeight = 0; Color3f nodeColor( 0 ); V3f centroid( 0 ); Box3f bound; vector<V3f>::const_iterator pointsBegin = m_privateData->points->readable().begin(); for( Tree::Iterator *p = node.permFirst(); p!=node.permLast(); p++ ) { const Color3f &c = m_privateData->colors->readable()[*p - pointsBegin]; float weight = luminance( c ); nodeColor += c; centroid += **p * weight; totalWeight += weight; bound.extendBy( **p ); } m_privateData->nodeCentroids[nodeIndex] = centroid / ( totalWeight > 0.0f ? totalWeight : 1.0f ); m_privateData->nodeColors[nodeIndex] = nodeColor; m_privateData->nodeBounds[nodeIndex] = bound; } else { Tree::NodeIndex lowIndex = m_privateData->tree.lowChildIndex( nodeIndex ); Tree::NodeIndex highIndex = m_privateData->tree.highChildIndex( nodeIndex ); buildWalk( lowIndex ); buildWalk( highIndex ); Box3f bound; bound.extendBy( m_privateData->nodeBounds[lowIndex] ); bound.extendBy( m_privateData->nodeBounds[highIndex] ); m_privateData->nodeBounds[nodeIndex] = bound; float wLow = luminance( m_privateData->nodeColors[lowIndex] ); float wHigh = luminance( m_privateData->nodeColors[highIndex] ); float wSum = wLow + wHigh; m_privateData->nodeCentroids[nodeIndex] = ( wLow * m_privateData->nodeCentroids[lowIndex] + wHigh * m_privateData->nodeCentroids[highIndex] ) / ( wSum > 0.0f ? wSum : 1.0f ); m_privateData->nodeColors[nodeIndex] = m_privateData->nodeColors[lowIndex] + m_privateData->nodeColors[highIndex]; } }
Imath::Box3f Group::bound() const { Box3f result; for( ChildContainer::const_iterator it=children().begin(); it!=children().end(); it++ ) { result.extendBy( (*it)->bound() ); } return transform( result, m_transform ); }
void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc ) { Box3f bound = proc->bound(); if( bound.isEmpty() ) { return; } AtNode *procedural = AiNode( "procedural" ); if( ExternalProcedural *externalProc = dynamic_cast<ExternalProcedural *>( proc.get() ) ) { AiNodeSetStr( procedural, "dso", externalProc->fileName().c_str() ); ParameterAlgo::setParameters( procedural, externalProc->parameters() ); applyTransformToNode( procedural ); } else { // we have to transform the bound, as we're not applying the current transform to the // procedural node, but instead applying absolute transforms to the shapes the procedural // generates. if( bound != Procedural::noBound ) { Box3f transformedBound; for( size_t i = 0, e = m_transformStack.numSamples(); i < e; ++i ) { transformedBound.extendBy( transform( bound, m_transformStack.sample( i ) ) ); } bound = transformedBound; } AiNodeSetPtr( procedural, "funcptr", (void *)procLoader ); ProceduralData *data = new ProceduralData; data->procedural = proc; data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); AiNodeSetPtr( procedural, "userptr", data ); } if( bound != Procedural::noBound ) { AiNodeSetPnt( procedural, "min", bound.min.x, bound.min.y, bound.min.z ); AiNodeSetPnt( procedural, "max", bound.max.x, bound.max.y, bound.max.z ); } else { // No bound available - expand procedural immediately. AiNodeSetBool( procedural, "load_at_init", true ); } // we call addNode() rather than addShape() as we don't want to apply transforms and // shaders and attributes to procedurals. if we do, they override the things we set // on the nodes generated by the procedurals, which is frankly useless. addNode( procedural ); }
void run() { PackageManagerPtr myPackageManager( new PackageManager ); ScenePtr myScene = Scene::createStubs(myPackageManager); dom::NodePtr myMaterial = createColorMaterial(myScene, Vector4f(0.8f,0.8f,0.6f,1.0f)); Box3f myVoxelBox; myVoxelBox.makeEmpty(); myVoxelBox.extendBy( Point3f(0.0f, 0.0f, 0.0f)); //myVoxelBox.extendBy( Point3f(10.0, 20.0, 30.0f)); // 10x20x30 voxels myVoxelBox.extendBy( Point3f(1.0f, 1.0f, 1.0f)); // 1x2x4 voxels Vector3i myVolumeSize(10, 10, 10); Matrix4f myModelMatrix; Matrix4f myCameraMatrix; float mySampleRate = 1.0f; VectorOfVector3f myReference; VectorOfVector3f myCandidate; myModelMatrix.makeIdentity(); myCameraMatrix.makeIdentity(); dom::NodePtr myShape = createVoxelProxyGeometry(myScene, myVoxelBox, myModelMatrix, myCameraMatrix, myVolumeSize, mySampleRate, myMaterial->getAttributeString(ID_ATTRIB), "VoxelProxy"); ENSURE(extractPositions(myShape, myReference)); myCameraMatrix.makeXRotating( static_cast<float>(asl::PI/2) ); myShape = createVoxelProxyGeometry(myScene, myVoxelBox, myModelMatrix, myCameraMatrix, myVolumeSize, mySampleRate, myMaterial->getAttributeString(ID_ATTRIB), "VoxelProxy"); ENSURE(extractPositions(myShape, myCandidate)); //ENSURE(positionsEqual(myCandidate, myReference, myCameraMatrixf)); //myModelViewMatrix.rotateY(asl::PI * 0.125); //DPRINT( * myShape ); //dom::NodePtr myBody = createBody(myScene->getWorldRoot(), myShape->getAttributeString(ID_ATTRIBf)); //myScene->save("proxy.x60", false); }
Imath::Box3f computeBounds(const float* vertices, size_t numVertices) { using namespace Imath; Box3f bounds; bounds.makeEmpty(); V3f vertex; for(size_t v=0; v < numVertices; ++v) { vertex.x = vertices[3 * v + 0]; vertex.y = vertices[3 * v + 1]; vertex.z = vertices[3 * v + 2]; bounds.extendBy(vertex); } return bounds; }
Box3f selectionBound() const { if( m_selected ) { return m_bound; } else { Box3f childSelectionBound; for( std::vector<SceneGraph *>::const_iterator it = m_children.begin(), eIt = m_children.end(); it != eIt; ++it ) { const Box3f childBound = transform( (*it)->selectionBound(), (*it)->m_transform ); childSelectionBound.extendBy( childBound ); } return childSelectionBound; } }
Imath::Box3f Gadget::bound() const { Box3f result; for( ChildContainer::const_iterator it=children().begin(); it!=children().end(); it++ ) { // cast is safe because of the guarantees acceptsChild() gives us const Gadget *c = static_cast<const Gadget *>( it->get() ); if( !c->getVisible() ) { continue; } Imath::Box3f b = c->bound(); b = Imath::transform( b, c->getTransform() ); result.extendBy( b ); } return result; }
Imath::Box3f Primitive::bound() const { Box3f result; PrimitiveVariableMap::const_iterator it = variables.find( "P" ); if( it!=variables.end() ) { ConstV3fVectorDataPtr p = runTimeCast<const V3fVectorData>( it->second.data ); if( p ) { const vector<V3f> &pp = p->readable(); for( size_t i=0; i<pp.size(); i++ ) { result.extendBy( pp[i] ); } } } return result; }
bool Line::intersect( float angle, const Box3f& box ) const { if (box.isEmpty()) return false; const Vec3f &max = box.getMax(), &min = box.getMin(); float fuzz = 0.0; int i; if (angle < 0.0) { fuzz = - angle; } else { // Find the farthest point on the bounding box (where the pick // cone will be largest). The amount of fuzz at this point will // be the minimum we can use. Expand the box by that amount and // do an intersection. double tanA = tan(angle); for(i = 0; i < 8; i++) { Vec3f point(i & 01 ? min[0] : max[0], i & 02 ? min[1] : max[1], i & 04 ? min[2] : max[2]); // how far is point from line origin?? Vec3f diff(point - getPosition()); double thisFuzz = sqrt(diff.dot(diff)) * tanA; if (thisFuzz > fuzz) fuzz = float(thisFuzz); } } Box3f fuzzBox = box; fuzzBox.extendBy(Vec3f(min[0] - fuzz, min[1] - fuzz, min[2] - fuzz)); fuzzBox.extendBy(Vec3f(max[0] + fuzz, max[1] + fuzz, max[2] + fuzz)); Vec3f scratch1, scratch2; return intersect(fuzzBox, scratch1, scratch2); }
Imath::Box3f SplinePlugGadget::bound() const { Box3f result; for( size_t i = 0, e = m_splines->size(); i < e ; i++ ) { SplineffPlugPtr spline = IECore::runTimeCast<SplineffPlug>( m_splines->member( i ) ); if( spline ) { unsigned n = spline->numPoints(); for( unsigned i=0; i<n; i++ ) { V3f p( 0 ); p.x = spline->pointXPlug( i )->getValue(); p.y = spline->pointYPlug( i )->getValue(); result.extendBy( p ); } } } return result; }
Imath::Box3f BranchCreator::computeBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const { ConstCompoundDataPtr mapping = staticPointerCast<const CompoundData>( mappingPlug()->getValue() ); ScenePath parentPath, branchPath; Filter::Result parentMatch = parentAndBranchPaths( mapping, path, parentPath, branchPath ); if( parentMatch == Filter::AncestorMatch ) { return computeBranchBound( parentPath, branchPath, context ); } else if( parentMatch == Filter::ExactMatch || parentMatch == Filter::DescendantMatch ) { Box3f result = inPlug()->boundPlug()->getValue(); result.extendBy( unionOfTransformedChildBounds( path, outPlug() ) ); return result; } else { return inPlug()->boundPlug()->getValue(); } }
Imath::Box3f RenderableGadget::selectionBound( IECoreGL::Group *group ) const { IECoreGL::State *state = group->getState(); IECoreGL::NameStateComponent *nameState = state->get<IECoreGL::NameStateComponent>(); if( nameState && m_selection.find( nameState->name() ) != m_selection.end() ) { return group->bound(); } else { Box3f childSelectionBound; const IECoreGL::Group::ChildContainer &children = group->children(); for( IECoreGL::Group::ChildContainer::const_iterator it = children.begin(), eIt = children.end(); it != eIt; it++ ) { IECoreGL::Group *childGroup = IECore::runTimeCast<IECoreGL::Group>( (*it).get() ); if( childGroup ) { childSelectionBound.extendBy( selectionBound( childGroup ) ); } } return transform( childSelectionBound, group->getTransform() ); } }
void CurvesPrimitiveEvaluator::buildTree() { if( m_haveTree ) { return; } TreeMutex::scoped_lock lock( m_treeMutex ); if( m_haveTree ) { // another thread may have built the tree while we waited for the mutex return; } bool linear = m_curvesPrimitive->basis() == CubicBasisf::linear(); const std::vector<V3f> &p = static_cast<const V3fVectorData *>( m_p.data.get() )->readable(); PrimitiveEvaluator::ResultPtr result = createResult(); size_t numCurves = m_curvesPrimitive->numCurves(); for( size_t curveIndex = 0; curveIndex<numCurves; curveIndex++ ) { if( linear ) { int numVertices = m_verticesPerCurve[curveIndex]; int vertIndex = m_vertexDataOffsets[curveIndex]; float prevV = 0.0f; for( int i=0; i<numVertices; i++, vertIndex++ ) { float v = clamp( (float)i/(float)(numVertices-1), 0.0f, 1.0f ); if( i!=0 ) { Box3f b; b.extendBy( p[vertIndex-1] ); b.extendBy( p[vertIndex] ); m_treeBounds.push_back( b ); m_treeLines.push_back( Line( p[vertIndex-1], p[vertIndex], curveIndex, prevV, v ) ); } prevV = v; } } else { unsigned numSegments = m_curvesPrimitive->numSegments( curveIndex ); int steps = numSegments * Line::linesPerCurveSegment(); V3f prevP( 0 ); float prevV = 0; for( int i=0; i<steps; i++ ) { float v = clamp( (float)i/(float)(steps-1), 0.0f, 1.0f ); pointAtV( curveIndex, v, result.get() ); V3f p = result->point(); if( i!=0 ) { Box3f b; b.extendBy( prevP ); b.extendBy( p ); m_treeBounds.push_back( b ); m_treeLines.push_back( Line( prevP, p, curveIndex, prevV, v ) ); } prevP = p; prevV = v; } } } m_tree.init( m_treeBounds.begin(), m_treeBounds.end() ); m_haveTree = true; }
void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::ProceduralPtr proc ) { Box3f bound = proc->bound(); if( bound.isEmpty() ) { return; } AtNode *node = NULL; std::string nodeType = "procedural"; if( const ExternalProcedural *externalProc = dynamic_cast<ExternalProcedural *>( proc.get() ) ) { // Allow a parameter "ai:nodeType" == "volume" to create a volume shape rather // than a procedural shape. Volume shapes provide "dso", "min" and "max" parameters // just as procedural shapes do, so the mapping is a fairly natural one. CompoundDataMap::const_iterator nodeTypeIt = externalProc->parameters().find( "ai:nodeType" ); if( nodeTypeIt != externalProc->parameters().end() && nodeTypeIt->second->isInstanceOf( StringData::staticTypeId() ) ) { nodeType = static_cast<const StringData *>( nodeTypeIt->second.get() )->readable(); } node = AiNode( nodeType.c_str() ); AiNodeSetStr( node, "dso", externalProc->fileName().c_str() ); ParameterAlgo::setParameters( node, externalProc->parameters() ); applyTransformToNode( node ); } else { node = AiNode( nodeType.c_str() ); // we have to transform the bound, as we're not applying the current transform to the // procedural node, but instead applying absolute transforms to the shapes the procedural // generates. if( bound != Procedural::noBound ) { Box3f transformedBound; for( size_t i = 0, e = m_transformStack.numSamples(); i < e; ++i ) { transformedBound.extendBy( transform( bound, m_transformStack.sample( i ) ) ); } bound = transformedBound; } AiNodeSetPtr( node, "funcptr", (void *)procLoader ); ProceduralData *data = new ProceduralData; data->procedural = proc; data->renderer = new IECoreArnold::Renderer( new RendererImplementation( *this ) ); AiNodeSetPtr( node, "userptr", data ); } if( bound != Procedural::noBound ) { AiNodeSetPnt( node, "min", bound.min.x, bound.min.y, bound.min.z ); AiNodeSetPnt( node, "max", bound.max.x, bound.max.y, bound.max.z ); } else { // No bound available - expand procedural immediately. AiNodeSetBool( node, "load_at_init", true ); } if( nodeType == "procedural" ) { // We call addNode() rather than addShape() as we don't want to apply transforms and // shaders and attributes to procedurals. If we do, they override the things we set // on the nodes generated by the procedurals, which is frankly useless. addNode( node ); } else { addShape( node ); } }
void MeshPrimitiveImplicitSurfaceOp::modifyTypedPrimitive( MeshPrimitive * typedPrimitive, const CompoundObject * operands ) { const float threshold = m_thresholdParameter->getNumericValue(); bool automaticBound = static_cast<const BoolData *>( m_automaticBoundParameter->getValue() )->readable(); Box3f bound; if (automaticBound) { bound.makeEmpty(); PrimitiveVariableMap::const_iterator it = typedPrimitive->variables.find("P"); if (it != typedPrimitive->variables.end()) { const DataPtr &verticesData = it->second.data; /// \todo Use depatchTypedData if (runTimeCast<V3fVectorData>(verticesData)) { ConstV3fVectorDataPtr p = runTimeCast<V3fVectorData>(verticesData); for ( V3fVectorData::ValueType::const_iterator it = p->readable().begin(); it != p->readable().end(); ++it) { bound.extendBy( *it ); } } else if (runTimeCast<V3dVectorData>(verticesData)) { ConstV3dVectorDataPtr p = runTimeCast<V3dVectorData>(verticesData); for ( V3dVectorData::ValueType::const_iterator it = p->readable().begin(); it != p->readable().end(); ++it) { bound.extendBy( *it ); } } else { throw InvalidArgumentException("MeshPrimitive has no primitive variable \"P\" of type V3fVectorData/V3dVectorData in MeshPrimitiveImplicitSurfaceOp"); } } else { throw InvalidArgumentException("MeshPrimitive has no primitive variable \"P\" in MeshPrimitiveImplicitSurfaceOp"); } } else { bound = static_cast<const Box3fData *>( m_boundParameter->getValue() )->readable(); } float boundExtend = m_boundExtendParameter->getNumericValue(); bound.min -= V3f( boundExtend, boundExtend, boundExtend ); bound.max += V3f( boundExtend, boundExtend, boundExtend ); V3i resolution; int gridMethod = m_gridMethodParameter->getNumericValue(); if ( gridMethod == Resolution ) { resolution = static_cast<const V3iData *>( m_resolutionParameter->getValue() )->readable(); } else if ( gridMethod == DivisionSize ) { V3f divisionSize = static_cast<const V3fData *>( m_divisionSizeParameter->getValue() )->readable(); resolution.x = (int)((bound.max.x - bound.min.x) / divisionSize.x); resolution.y = (int)((bound.max.y - bound.min.y) / divisionSize.y); resolution.z = (int)((bound.max.z - bound.min.z) / divisionSize.z); } else { assert( false ); } resolution.x = std::max( 1, resolution.x ); resolution.y = std::max( 1, resolution.y ); resolution.z = std::max( 1, resolution.z ); /// Calculate a tolerance which is half the size of the smallest grid division double cacheTolerance = ((bound.max.x - bound.min.x) / (double)resolution.x) / 2.0; cacheTolerance = std::min(cacheTolerance, ((bound.max.y - bound.min.y) / (double)resolution.y) / 2.0 ); cacheTolerance = std::min(cacheTolerance, ((bound.max.z - bound.min.z) / (double)resolution.z) / 2.0 ); MeshPrimitiveBuilderPtr builder = new MeshPrimitiveBuilder(); typedef MarchingCubes< CachedImplicitSurfaceFunction< V3f, float > > Marcher ; MeshPrimitiveImplicitSurfaceFunctionPtr fn = new MeshPrimitiveImplicitSurfaceFunction( typedPrimitive ); Marcher::Ptr m = new Marcher ( new CachedImplicitSurfaceFunction< V3f, float >( fn, cacheTolerance ), builder ); m->march( Box3f( bound.min, bound.max ), resolution, threshold ); MeshPrimitivePtr resultMesh = builder->mesh(); typedPrimitive->variables.clear(); typedPrimitive->setTopology( resultMesh->verticesPerFace(), resultMesh->vertexIds() ); typedPrimitive->variables["P"] = PrimitiveVariable( resultMesh->variables["P"].interpolation, resultMesh->variables["P"].data->copy() ); typedPrimitive->variables["N"] = PrimitiveVariable( resultMesh->variables["N"].interpolation, resultMesh->variables["N"].data->copy() ); }
void XfBox3f::extendBy( const XfBox3f& bb ) { if ( bb.isEmpty() ) { // bb is empty, no change return; } else if ( isEmpty() ) { // we're empty, use bb *this = bb; } else if ( xformInv[0][0] != std::numeric_limits<float>::max() && bb.xformInv[0][0] != std::numeric_limits<float>::max()) { // Neither box is empty and they are in different spaces. To // get the best results, we'll perform the merge of the two // boxes in each of the two spaces. Whichever merge ends up // being smaller is the one we'll use. // Note that we don't perform a project() as part of the test. // This is because projecting almost always adds a little extra // space. It also gives an unfair advantage to the // box more closely aligned with world space. In the simplest // case this might be preferable. However, over many objects, // we are better off going with the minimum in local space, // and not worrying about projecting until the very end. XfBox3f xfbox1, xfbox2; Box3f box1, box2; // Convert bb into this's space to get box1 xfbox1 = bb; // Rather than calling transform(), which calls inverse(), // we'll do it ourselves, since we already know the inverse matrix. // I.e., we could call: xfbox1.transform(xformInv); xfbox1.xform *= xformInv; xfbox1.xformInv.multRight(xform); box1 = xfbox1.project(); // Convert this into bb's space to get box2 xfbox2 = *this; // Same here for: xfbox2.transform(bb.xformInv); xfbox2.xform *= bb.xformInv; xfbox2.xformInv.multRight(bb.xform); box2 = xfbox2.project(); // Extend this by box1 to get xfbox1 xfbox1 = *this; xfbox1.Box3f::extendBy(box1); // Use Box3f method; box1 is already in xfbox1's space // (otherwise, we'll get an infinite loop!) // Extend bb by box2 to get xfbox2 xfbox2 = bb; xfbox2.Box3f::extendBy(box2); // Use Box3f method; box2 is already in xfbox2's space // (otherwise, we'll get an infinite loop!) float vol1 = xfbox1.getVolume(); float vol2 = xfbox2.getVolume(); // Take the smaller result and extend appropriately if (vol1 <= vol2) { Box3f::extendBy(box1); } else { *this = bb; Box3f::extendBy(box2); } } else if (xformInv[0][0] == std::numeric_limits<float>::max()) { if (bb.xformInv[0][0] == std::numeric_limits<float>::max()) { // Both boxes are degenerate; project them both and // combine them: Box3f box = this->project(); box.extendBy(bb.project()); *this = XfBox3f(box); } else { // this is degenerate; transform our min/max into bb's // space, and combine there: Box3f box(getMin(), getMax()); box.transform(xform*bb.xformInv); *this = bb; Box3f::extendBy(box); } } else { // bb is degenerate; transform it into our space and combine: Box3f box(bb.getMin(), bb.getMax()); box.transform(bb.xform*xformInv); Box3f::extendBy(box); } }