void draw_string_into (Imath::M44d m, char* string, std::vector< std::vector<vec_t<2> > >* polygons) { Imath::M44d nm = m; for (int i = 0; i < strlen(string); i++) { draw_letter_into(nm, string[i], polygons); nm.translate(Imath::V3d(1, 0, 0)); } }
const Matrix4 getConcatMatrix( IObject iObj, chrono_t curTime , bool interpolate) { Imath::M44d xf; xf.makeIdentity(); IObject parent = iObj.getParent(); // Once the Archive's Top Object is reached, IObject::getParent() will // return an invalid IObject, and that will evaluate to False. while ( parent ) { accumXform( xf, parent, curTime, interpolate ); parent = parent.getParent(); } Matrix4 ret_matrix = convert(xf); return ret_matrix; }
Imath::M44d SceneNode::getGlobalTransDouble(double time) { Imath::M44d ret; ret.makeIdentity(); return ret; }
bool DrawableHolderUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const { MStatus s; // early out if we're not selectable. we always allow components to be selected if we're highlighted, // but we don't allow ourselves to be selected as a whole unless meshes are in the selection mask. // it's not ideal that we act like a mesh, but it's at least consistent with the drawing mask we use. if( selectInfo.displayStatus() != M3dView::kHilite ) { MSelectionMask meshMask( MSelectionMask::kSelectMeshes ); if( !selectInfo.selectable( meshMask ) ) { return false; } } // early out if we have no scene to draw DrawableHolder *drawableHolder = static_cast<DrawableHolder *>( surfaceShape() ); IECoreGL::ConstScenePtr scene = drawableHolder->scene(); if( !scene ) { return false; } // we want to perform the selection using an IECoreGL::Selector, so we // can avoid the performance penalty associated with using GL_SELECT mode. // that means we don't really want to call view.beginSelect(), but we have to // call it just to get the projection matrix for our own selection, because as far // as i can tell, there is no other way of getting it reliably. M3dView view = selectInfo.view(); view.beginSelect(); Imath::M44d projectionMatrix; glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() ); view.endSelect(); view.beginGL(); glMatrixMode( GL_PROJECTION ); glLoadMatrixd( projectionMatrix.getValue() ); IECoreGL::Selector::Mode selectionMode = IECoreGL::Selector::IDRender; if( selectInfo.displayStatus() == M3dView::kHilite && !selectInfo.singleSelection() ) { selectionMode = IECoreGL::Selector::OcclusionQuery; } std::vector<IECoreGL::HitRecord> hits; { IECoreGL::Selector selector( Imath::Box2f( Imath::V2f( 0 ), Imath::V2f( 1 ) ), selectionMode, hits ); IECoreGL::State::bindBaseState(); selector.baseState()->bind(); scene->render( selector.baseState() ); } view.endGL(); if( !hits.size() ) { return false; } // find the depth of the closest hit: MIntArray componentIndices; float depthMin = std::numeric_limits<float>::max(); for( int i=0, e = hits.size(); i < e; i++ ) { if( hits[i].depthMin < depthMin ) { depthMin = hits[i].depthMin; } } // figure out the world space location of the closest hit MDagPath camera; view.getCamera( camera ); MFnCamera fnCamera( camera.node() ); float near = fnCamera.nearClippingPlane(); float far = fnCamera.farClippingPlane(); float z = -1; if( fnCamera.isOrtho() ) { z = Imath::lerp( near, far, depthMin ); } else { // perspective camera - depth isn't linear so linearise to get z float a = far / ( far - near ); float b = far * near / ( near - far ); z = b / ( depthMin - a ); } MPoint localRayOrigin; MVector localRayDirection; selectInfo.getLocalRay( localRayOrigin, localRayDirection ); MMatrix localToCamera = selectInfo.selectPath().inclusiveMatrix() * camera.inclusiveMatrix().inverse(); MPoint cameraRayOrigin = localRayOrigin * localToCamera; MVector cameraRayDirection = localRayDirection * localToCamera; MPoint cameraIntersectionPoint = cameraRayOrigin + cameraRayDirection * ( -( z - near ) / cameraRayDirection.z ); MPoint worldIntersectionPoint = cameraIntersectionPoint * camera.inclusiveMatrix(); MSelectionList item; item.add( selectInfo.selectPath() ); selectInfo.addSelection( item, worldIntersectionPoint, selectionList, worldSpaceSelectPts, MSelectionMask::kSelectMeshes, false ); return true; }
ConstObjectPtr SOP_SceneCacheSource::transformObject( const IECore::Object *object, const Imath::M44d &transform, Parameters ¶ms ) { if ( const Primitive *primitive = IECore::runTimeCast<const Primitive>( object ) ) { TransformOpPtr transformer = new TransformOp(); transformer->inputParameter()->setValue( const_cast<Primitive*>( primitive ) ); // safe because we set the copy parameter transformer->copyParameter()->setTypedValue( true ); transformer->matrixParameter()->setValue( new M44dData( transform ) ); // add all Point and Normal prim vars to the transformation list, except for rest/Pref const PrimitiveVariableMap &variables = primitive->variables; std::vector<std::string> &primVars = transformer->primVarsParameter()->getTypedValue(); primVars.clear(); for ( PrimitiveVariableMap::const_iterator it = variables.begin(); it != variables.end(); ++it ) { if ( despatchTypedData<TransformGeometricData, IECore::TypeTraits::IsGeometricTypedData, DespatchTypedDataIgnoreError>( it->second.data.get() ) ) { // we don't want to alter rest/Pref because Houdini excepts these to be non-transforming prim vars if ( it->first == "rest" || it->first == "Pref" ) { continue; } primVars.push_back( it->first ); // add the transforming prim vars to the animated list if ( std::find( params.animatedPrimVars.begin(), params.animatedPrimVars.end(), it->first ) == params.animatedPrimVars.end() ) { params.animatedPrimVars.push_back( it->first ); params.hasAnimatedPrimVars = true; } } } return transformer->operate(); } else if ( const Group *group = IECore::runTimeCast<const Group>( object ) ) { GroupPtr result = group->copy(); MatrixTransformPtr matTransform = matrixTransform( transform ); if ( const Transform *transform = group->getTransform() ) { matTransform->matrix *= transform->transform(); } result->setTransform( matTransform ); return result; } else if ( const CoordinateSystem *coord = IECore::runTimeCast<const CoordinateSystem>( object ) ) { CoordinateSystemPtr result = coord->copy(); MatrixTransformPtr matTransform = matrixTransform( transform ); if ( const Transform *transform = coord->getTransform() ) { matTransform->matrix *= transform->transform(); } result->setTransform( matTransform ); return result; } return object; }
bool ProceduralHolderUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const { MStatus s; // early out if we're not selectable. we always allow components to be selected if we're highlighted, // but we don't allow ourselves to be selected as a whole unless meshes are in the selection mask. // it's not ideal that we act like a mesh, but it's at least consistent with the drawing mask we use. if( selectInfo.displayStatus() != M3dView::kHilite ) { MSelectionMask meshMask( MSelectionMask::kSelectMeshes ); if( !selectInfo.selectable( meshMask ) ) { return false; } } // early out if we have no scene to draw ProceduralHolder *proceduralHolder = static_cast<ProceduralHolder *>( surfaceShape() ); IECoreGL::ConstScenePtr scene = proceduralHolder->scene(); if( !scene ) { return false; } // we want to perform the selection using an IECoreGL::Selector, so we // can avoid the performance penalty associated with using GL_SELECT mode. // that means we don't really want to call view.beginSelect(), but we have to // call it just to get the projection matrix for our own selection, because as far // as i can tell, there is no other way of getting it reliably. M3dView view = selectInfo.view(); view.beginSelect(); Imath::M44d projectionMatrix; glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() ); view.endSelect(); view.beginGL(); glMatrixMode( GL_PROJECTION ); glLoadMatrixd( projectionMatrix.getValue() ); IECoreGL::Selector::Mode selectionMode = IECoreGL::Selector::IDRender; if( selectInfo.displayStatus() == M3dView::kHilite && !selectInfo.singleSelection() ) { selectionMode = IECoreGL::Selector::OcclusionQuery; } std::vector<IECoreGL::HitRecord> hits; { IECoreGL::Selector selector( Imath::Box2f( Imath::V2f( 0 ), Imath::V2f( 1 ) ), selectionMode, hits ); IECoreGL::State::bindBaseState(); selector.baseState()->bind(); scene->render( selector.baseState() ); if( selectInfo.displayStatus() != M3dView::kHilite ) { // we're not in component selection mode. we'd like to be able to select the procedural // object using the bounding box so we draw it too. MPlug pDrawBound( proceduralHolder->thisMObject(), ProceduralHolder::aDrawBound ); bool drawBound = true; pDrawBound.getValue( drawBound ); if( drawBound ) { IECoreGL::BoxPrimitive::renderWireframe( IECore::convert<Imath::Box3f>( proceduralHolder->boundingBox() ) ); } } } view.endGL(); if( !hits.size() ) { return false; } // iterate over the hits, converting them into components and also finding // the closest one. MIntArray componentIndices; float depthMin = std::numeric_limits<float>::max(); int depthMinIndex = -1; for( int i=0, e = hits.size(); i < e; i++ ) { if( hits[i].depthMin < depthMin ) { depthMin = hits[i].depthMin; depthMinIndex = componentIndices.length(); } ProceduralHolder::ComponentsMap::const_iterator compIt = proceduralHolder->m_componentsMap.find( hits[i].name.value() ); assert( compIt != proceduralHolder->m_componentsMap.end() ); componentIndices.append( compIt->second.first ); } assert( depthMinIndex >= 0 ); // figure out the world space location of the closest hit MDagPath camera; view.getCamera( camera ); MFnCamera fnCamera( camera.node() ); float near = fnCamera.nearClippingPlane(); float far = fnCamera.farClippingPlane(); float z = -1; if( fnCamera.isOrtho() ) { z = Imath::lerp( near, far, depthMin ); } else { // perspective camera - depth isn't linear so linearise to get z float a = far / ( far - near ); float b = far * near / ( near - far ); z = b / ( depthMin - a ); } MPoint localRayOrigin; MVector localRayDirection; selectInfo.getLocalRay( localRayOrigin, localRayDirection ); MMatrix localToCamera = selectInfo.selectPath().inclusiveMatrix() * camera.inclusiveMatrix().inverse(); MPoint cameraRayOrigin = localRayOrigin * localToCamera; MVector cameraRayDirection = localRayDirection * localToCamera; MPoint cameraIntersectionPoint = cameraRayOrigin + cameraRayDirection * ( -( z - near ) / cameraRayDirection.z ); MPoint worldIntersectionPoint = cameraIntersectionPoint * camera.inclusiveMatrix(); // turn the processed hits into appropriate changes to the current selection if( selectInfo.displayStatus() == M3dView::kHilite ) { // selecting components MFnSingleIndexedComponent fnComponent; MObject component = fnComponent.create( MFn::kMeshPolygonComponent, &s ); assert( s ); if( selectInfo.singleSelection() ) { fnComponent.addElement( componentIndices[depthMinIndex] ); } else { fnComponent.addElements( componentIndices ); } MSelectionList items; items.add( selectInfo.multiPath(), component ); selectInfo.addSelection( items, worldIntersectionPoint, selectionList, worldSpaceSelectPts, MSelectionMask::kSelectMeshFaces, true ); } else { // selecting objects MSelectionList item; item.add( selectInfo.selectPath() ); selectInfo.addSelection( item, worldIntersectionPoint, selectionList, worldSpaceSelectPts, MSelectionMask::kSelectMeshes, false ); } return true; }
void testProcrustesImp () { // Test the empty case: Imath::M44d id = procrustesRotationAndTranslation ((Imath::Vec3<T>*) 0, (Imath::Vec3<T>*) 0, (T*) 0, 0); assert (id == Imath::M44d()); id = procrustesRotationAndTranslation ((Imath::Vec3<T>*) 0, (Imath::Vec3<T>*) 0, 0); assert (id == Imath::M44d()); // First we'll test with a bunch of known translation/rotation matrices // to make sure we get back exactly the same points: Imath::M44d m; m.makeIdentity(); testTranslationRotationMatrix<T> (m); m.translate (Imath::V3d(3.0, 5.0, -0.2)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(M_PI, 0, 0)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(0, M_PI/4.0, 0)); testTranslationRotationMatrix<T> (m); m.rotate (Imath::V3d(0, 0, -3.0/4.0 * M_PI)); testTranslationRotationMatrix<T> (m); m.makeIdentity(); testWithTranslateRotateAndScale<T> (m); m.translate (Imath::V3d(0.4, 6.0, 10.0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(M_PI, 0, 0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(0, M_PI/4.0, 0)); testWithTranslateRotateAndScale<T> (m); m.rotate (Imath::V3d(0, 0, -3.0/4.0 * M_PI)); testWithTranslateRotateAndScale<T> (m); m.scale (Imath::V3d(2.0, 2.0, 2.0)); testWithTranslateRotateAndScale<T> (m); m.scale (Imath::V3d(0.01, 0.01, 0.01)); testWithTranslateRotateAndScale<T> (m); // Now we'll test with some random point sets and verify // the various Procrustes properties: std::vector<Imath::Vec3<T> > fromPoints; std::vector<Imath::Vec3<T> > toPoints; fromPoints.clear(); toPoints.clear(); for (size_t i = 0; i < 4; ++i) { const T theta = T(2*i) / T(M_PI); fromPoints.push_back (Imath::Vec3<T>(cos(theta), sin(theta), 0)); toPoints.push_back (Imath::Vec3<T>(cos(theta + M_PI/3.0), sin(theta + M_PI/3.0), 0)); } verifyProcrustes (fromPoints, toPoints); Imath::Rand48 random (1209); for (size_t numPoints = 1; numPoints < 10; ++numPoints) { fromPoints.clear(); toPoints.clear(); for (size_t i = 0; i < numPoints; ++i) { fromPoints.push_back (Imath::Vec3<T>(random.nextf(), random.nextf(), random.nextf())); toPoints.push_back (Imath::Vec3<T>(random.nextf(), random.nextf(), random.nextf())); } } verifyProcrustes (fromPoints, toPoints); // Test with some known matrices of varying degrees of quality: testProcrustesWithMatrix<T> (m); m.translate (Imath::Vec3<T>(3, 4, 1)); testProcrustesWithMatrix<T> (m); m.translate (Imath::Vec3<T>(-10, 2, 1)); testProcrustesWithMatrix<T> (m); Imath::Eulerd rot (M_PI/3.0, 3.0*M_PI/4.0, 0); m = m * rot.toMatrix44(); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1.5, 6.4, 2.0)); testProcrustesWithMatrix<T> (m); Imath::Eulerd rot2 (1.0, M_PI, M_PI/3.0); m = m * rot.toMatrix44(); m.scale (Imath::Vec3<T>(-1, 1, 1)); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1, 0.001, 1)); testProcrustesWithMatrix<T> (m); m.scale (Imath::Vec3<T>(1, 1, 0)); testProcrustesWithMatrix<T> (m); }
void verifyProcrustes (const std::vector<Imath::Vec3<T> >& from, const std::vector<Imath::Vec3<T> >& to) { typedef Imath::Vec3<T> V3; const T eps = std::sqrt(std::numeric_limits<T>::epsilon()); const size_t n = from.size(); // Validate that passing in uniform weights gives the same answer as // passing in no weights: std::vector<T> weights (from.size()); for (size_t i = 0; i < weights.size(); ++i) weights[i] = 1; Imath::M44d m1 = procrustesRotationAndTranslation (&from[0], &to[0], n); Imath::M44d m2 = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n); for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) assert (std::abs(m1[i][j] - m2[i][j]) < eps); // Now try the weighted version: for (size_t i = 0; i < weights.size(); ++i) weights[i] = i+1; Imath::M44d m = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n); // with scale: Imath::M44d ms = procrustesRotationAndTranslation (&from[0], &to[0], &weights[0], n, true); // Verify that it's orthonormal w/ positive determinant. const T det = m.determinant(); assert (std::abs(det - T(1)) < eps); // Verify orthonormal: Imath::M33d upperLeft; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) upperLeft[i][j] = m[i][j]; Imath::M33d product = upperLeft * upperLeft.transposed(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { const double expected = (i == j ? 1.0 : 0.0); assert (std::abs(product[i][j] - expected) < eps); } } // Verify that nearby transforms are worse: const size_t numTries = 10; Imath::Rand48 rand (1056); const double delta = 1e-3; for (size_t i = 0; i < numTries; ++i) { // Construct an orthogonal rotation matrix using Euler angles: Imath::Eulerd diffRot (delta * rand.nextf(), delta * rand.nextf(), delta * rand.nextf()); assert (procrustesError (&from[0], &to[0], &weights[0], n, m * diffRot.toMatrix44()) > procrustesError (&from[0], &to[0], &weights[0], n, m)); // Try a small translation: Imath::V3d diffTrans (delta * rand.nextf(), delta * rand.nextf(), delta * rand.nextf()); Imath::M44d translateMatrix; translateMatrix.translate (diffTrans); assert (procrustesError (&from[0], &to[0], &weights[0], n, m * translateMatrix) > procrustesError (&from[0], &to[0], &weights[0], n, m)); } // Try a small scale: Imath::M44d newMat = ms; const double scaleDiff = delta; for (size_t i = 0; i < 3; ++i) for (size_t j = 0; j < 3; ++j) newMat[i][j] = ms[i][j] * (1.0 + scaleDiff); assert (procrustesError (&from[0], &to[0], &weights[0], n, newMat) > procrustesError (&from[0], &to[0], &weights[0], n, ms)); for (size_t i = 0; i < 3; ++i) for (size_t j = 0; j < 3; ++j) newMat[i][j] = ms[i][j] * (1.0 - scaleDiff); assert (procrustesError (&from[0], &to[0], &weights[0], n, newMat) > procrustesError (&from[0], &to[0], &weights[0], n, ms)); // // Verify the magical property that makes shape springs work: // when the displacements Q*A-B, times the weights, // are applied as forces at B, // there is zero net force and zero net torque. // { Imath::V3d center (0, 0, 0); Imath::V3d netForce(0); Imath::V3d netTorque(0); for (int iPoint = 0; iPoint < n; ++iPoint) { const Imath::V3d force = weights[iPoint] * (from[iPoint]*m - to[iPoint]); netForce += force; netTorque += to[iPoint].cross (force); } assert (netForce.length2() < eps); assert (netTorque.length2() < eps); } }
bool SceneShapeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const { MStatus s; // early out if we're not selectable. we always allow components to be selected if we're highlighted, // but we don't allow ourselves to be selected as a whole unless meshes are in the selection mask. // it's not ideal that we act like a mesh, but it's at least consistent with the drawing mask we use. if( selectInfo.displayStatus() != M3dView::kHilite ) { MSelectionMask meshMask( MSelectionMask::kSelectMeshes ); // Apparently selectInfo.selectable() still returns true when meshes are not // displayed by the M3dView, so we are also testing the objectDisplay status. // This was last confirmed in Maya 2014, and is presumably a Maya bug. if( !selectInfo.selectable( meshMask ) || !selectInfo.objectDisplayStatus( M3dView::kDisplayMeshes ) ) { return false; } } // early out if we have no scene to draw SceneShape *sceneShape = static_cast<SceneShape *>( surfaceShape() ); if( !sceneShape->getSceneInterface() ) { return false; } IECoreGL::ConstScenePtr scene = sceneShape->glScene(); if( !scene ) { return false; } // we want to perform the selection using an IECoreGL::Selector, so we // can avoid the performance penalty associated with using GL_SELECT mode. // that means we don't really want to call view.beginSelect(), but we have to // call it just to get the projection matrix for our own selection, because as far // as I can tell, there is no other way of getting it reliably. M3dView view = selectInfo.view(); view.beginSelect(); Imath::M44d projectionMatrix; glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() ); view.endSelect(); view.beginGL(); glMatrixMode( GL_PROJECTION ); glLoadMatrixd( projectionMatrix.getValue() ); IECoreGL::Selector::Mode selectionMode = IECoreGL::Selector::IDRender; if( selectInfo.displayStatus() == M3dView::kHilite && !selectInfo.singleSelection() ) { selectionMode = IECoreGL::Selector::OcclusionQuery; } std::vector<IECoreGL::HitRecord> hits; { IECoreGL::Selector selector( Imath::Box2f( Imath::V2f( 0 ), Imath::V2f( 1 ) ), selectionMode, hits ); IECoreGL::State::bindBaseState(); selector.baseState()->bind(); scene->render( selector.baseState() ); if( selectInfo.displayStatus() != M3dView::kHilite ) { // We're not in component selection mode. We'd like to be able to select the scene shape // using the bounding box so we draw it too but only if it is visible MPlug pDrawBound( sceneShape->thisMObject(), SceneShape::aDrawRootBound ); bool drawBound; pDrawBound.getValue( drawBound ); if( drawBound ) { IECoreGL::BoxPrimitive::renderWireframe( IECore::convert<Imath::Box3f>( sceneShape->boundingBox() ) ); } } } view.endGL(); if( hits.empty() ) { return false; } // iterate over the hits, converting them into components and also finding // the closest one. MIntArray componentIndices; float depthMin = std::numeric_limits<float>::max(); int depthMinIndex = -1; for( unsigned int i=0, e = hits.size(); i < e; i++ ) { if( hits[i].depthMin < depthMin ) { depthMin = hits[i].depthMin; depthMinIndex = componentIndices.length(); } int index = sceneShape->selectionIndex( IECoreGL::NameStateComponent::nameFromGLName( hits[i].name ) ); componentIndices.append( index ); } assert( depthMinIndex >= 0 ); // figure out the world space location of the closest hit MDagPath camera; view.getCamera( camera ); MPoint worldIntersectionPoint; selectionRayToWorldSpacePoint( camera, selectInfo, depthMin, worldIntersectionPoint ); // turn the processed hits into appropriate changes to the current selection if( selectInfo.displayStatus() == M3dView::kHilite ) { // selecting components MFnSingleIndexedComponent fnComponent; MObject component = fnComponent.create( MFn::kMeshPolygonComponent, &s ); assert( s ); if( selectInfo.singleSelection() ) { fnComponent.addElement( componentIndices[depthMinIndex] ); } else { fnComponent.addElements( componentIndices ); } MSelectionList items; items.add( selectInfo.multiPath(), component ); MDagPath path = selectInfo.multiPath(); selectInfo.addSelection( items, worldIntersectionPoint, selectionList, worldSpaceSelectPts, MSelectionMask::kSelectMeshFaces, true ); } else { // Check if we should be able to select that object MPlug pObjectOnly( sceneShape->thisMObject(), SceneShape::aObjectOnly ); bool objectOnly; pObjectOnly.getValue( objectOnly ); if( objectOnly && !sceneShape->getSceneInterface()->hasObject() ) { return true; } // selecting objects MSelectionList item; item.add( selectInfo.selectPath() ); selectInfo.addSelection( item, worldIntersectionPoint, selectionList, worldSpaceSelectPts, MSelectionMask::kSelectMeshes, false ); } return true; }
bool SceneShapeUI::snap( MSelectInfo &snapInfo ) const { MStatus s; if( snapInfo.displayStatus() != M3dView::kHilite ) { MSelectionMask meshMask( MSelectionMask::kSelectMeshes ); if( !snapInfo.selectable( meshMask ) ) { return false; } } // early out if we have no scene to draw SceneShape *sceneShape = static_cast<SceneShape *>( surfaceShape() ); const IECore::SceneInterface *sceneInterface = sceneShape->getSceneInterface().get(); if( !sceneInterface ) { return false; } IECoreGL::ConstScenePtr scene = sceneShape->glScene(); if( !scene ) { return false; } // Get the viewport that the snapping operation is taking place in. M3dView view = snapInfo.view(); // Use an IECoreGL::Selector to find the point in world space that we wish to snap to. // We do this by first getting the origin of the selection ray and transforming it into // NDC space using the OpenGL projection and transformation matrices. Once we have the // point in NDC we can use it to define the viewport that the IECoreGL::Selector will use. MPoint localRayOrigin; MVector localRayDirection; snapInfo.getLocalRay( localRayOrigin, localRayDirection ); Imath::V3d org( localRayOrigin[0], localRayOrigin[1], localRayOrigin[2] ); MDagPath camera; view.getCamera( camera ); MMatrix localToCamera = snapInfo.selectPath().inclusiveMatrix() * camera.inclusiveMatrix().inverse(); view.beginSelect(); Imath::M44d projectionMatrix; glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() ); view.endSelect(); double v[4][4]; localToCamera.get( v ); Imath::M44d cam( v ); Imath::V3d ndcPt3d = ( (org * cam ) * projectionMatrix + Imath::V3d( 1. ) ) * Imath::V3d( .5 ); Imath::V2d ndcPt( std::max( std::min( ndcPt3d[0], 1. ), 0. ), 1. - std::max( std::min( ndcPt3d[1], 1. ), 0. ) ); view.beginGL(); glMatrixMode( GL_PROJECTION ); glLoadMatrixd( projectionMatrix.getValue() ); float radius = .001; // The radius of the selection area in NDC. double aspect = double( view.portWidth() ) / view.portHeight(); Imath::V2f selectionWH( radius, radius * aspect ); std::vector<IECoreGL::HitRecord> hits; { IECoreGL::Selector selector( Imath::Box2f( ndcPt - selectionWH, ndcPt + selectionWH ), IECoreGL::Selector::IDRender, hits ); IECoreGL::State::bindBaseState(); selector.baseState()->bind(); scene->render( selector.baseState() ); } view.endGL(); if( hits.empty() ) { return false; } // Get the closest mesh hit. float depthMin = std::numeric_limits<float>::max(); int depthMinIndex = -1; for( unsigned int i=0, e = hits.size(); i < e; i++ ) { if( hits[i].depthMin < depthMin ) { depthMin = hits[i].depthMin; depthMinIndex = i; } } // Get the absolute path of the hit object. IECore::SceneInterface::Path objPath; std::string objPathStr; sceneInterface->path( objPath ); IECore::SceneInterface::pathToString( objPath, objPathStr ); objPathStr += IECoreGL::NameStateComponent::nameFromGLName( hits[depthMinIndex].name ); IECore::SceneInterface::stringToPath( objPathStr, objPath ); // Validate the hit selection. IECore::ConstSceneInterfacePtr childInterface; try { childInterface = sceneInterface->scene( objPath ); } catch(...) { return false; } if( !childInterface ) { return false; } if( !childInterface->hasObject() ) { return false; } // Get the mesh primitive so that we can query it's vertices. double time = sceneShape->time(); IECore::ConstObjectPtr object = childInterface->readObject( time ); IECore::ConstMeshPrimitivePtr meshPtr = IECore::runTimeCast<const IECore::MeshPrimitive>( object.get() ); if ( !meshPtr ) { return false; } // Calculate the snap point in object space. MPoint worldIntersectionPoint; selectionRayToWorldSpacePoint( camera, snapInfo, depthMin, worldIntersectionPoint ); Imath::V3f pt( worldIntersectionPoint[0], worldIntersectionPoint[1], worldIntersectionPoint[2] ); Imath::M44f objToWorld( worldTransform( childInterface.get(), time ) ); pt = pt * objToWorld.inverse(); // Get the list of vertices in the mesh. IECore::V3fVectorData::ConstPtr pointData( meshPtr->variableData<IECore::V3fVectorData>( "P", IECore::PrimitiveVariable::Vertex ) ); const std::vector<Imath::V3f> &vertices( pointData->readable() ); // Find the vertex that is closest to the snap point. Imath::V3d closestVertex; float closestDistance = std::numeric_limits<float>::max(); for( std::vector<Imath::V3f>::const_iterator it( vertices.begin() ); it != vertices.end(); ++it ) { Imath::V3d vert( *it ); float d( ( pt - vert ).length() ); // Calculate the distance between the vertex and the snap point. if( d < closestDistance ) { closestDistance = d; closestVertex = vert; } } // Snap to the vertex. closestVertex *= objToWorld; snapInfo.setSnapPoint( MPoint( closestVertex[0], closestVertex[1], closestVertex[2] ) ); return true; }