void SceneShapeUI::selectionRayToWorldSpacePoint( const MDagPath &camera, const MSelectInfo &selectInfo, float depth, MPoint &worldIntersectionPoint ) const { MPoint localRayOrigin; MVector localRayDirection; selectInfo.getLocalRay( localRayOrigin, localRayDirection ); MFnCamera fnCamera( camera.node() ); float near = fnCamera.nearClippingPlane(); float far = fnCamera.farClippingPlane(); float z = -1; if( fnCamera.isOrtho() ) { z = Imath::lerp( near, far, depth ); } 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 / ( depth - a ); } MMatrix localToCamera = selectInfo.selectPath().inclusiveMatrix() * camera.inclusiveMatrix().inverse(); MPoint cameraRayOrigin = localRayOrigin * localToCamera; MVector cameraRayDirection = localRayDirection * localToCamera; MPoint cameraIntersectionPoint = cameraRayOrigin + cameraRayDirection * ( -( z - near ) / cameraRayDirection.z ); worldIntersectionPoint = cameraIntersectionPoint * camera.inclusiveMatrix(); }
/* override */ bool apiSimpleShapeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const // // Description: // // Main selection routine // // Arguments: // // selectInfo - the selection state information // selectionList - the list of selected items to add to // worldSpaceSelectPts - // { bool selected = false; bool componentSelected = false; bool hilited = false; hilited = (selectInfo.displayStatus() == M3dView::kHilite); if ( hilited ) { componentSelected = selectVertices( selectInfo, selectionList, worldSpaceSelectPts ); selected = selected || componentSelected; } if ( !selected ) { // NOTE: If the geometry has an intersect routine it should // be called here with the selection ray to determine if the // the object was selected. selected = true; MSelectionMask priorityMask( MSelectionMask::kSelectNurbsSurfaces ); MSelectionList item; item.add( selectInfo.selectPath() ); MPoint xformedPt; if ( selectInfo.singleSelection() ) { MPoint center = surfaceShape()->boundingBox().center(); xformedPt = center; xformedPt *= selectInfo.selectPath().inclusiveMatrix(); } selectInfo.addSelection( item, xformedPt, selectionList, worldSpaceSelectPts, priorityMask, false ); } return selected; }
bool BaseShapeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const { // Should never happen // if (!s_drawData.initialized) { std::cerr << "Can't do selection, OpenGL context not initialized!" << std::endl; return false; } //BaseShape *shape = (BaseShape *) surfaceShape(); MStatus status; M3dView view = selectInfo.view(); const MDagPath & path = selectInfo.multiPath(); view.beginSelect(); glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, Data::BackboneArrowVerts); glDrawArrays(GL_TRIANGLES, 0, Data::BackboneArrowNumVerts); glPopClientAttrib(); if ( view.endSelect() > 0 ) // Hit count > 0 { MSelectionMask priorityMask( MSelectionMask::kSelectObjectsMask ); MSelectionList item; item.add( selectInfo.selectPath() ); MPoint xformedPt; xformedPt *= path.inclusiveMatrix(); selectInfo.addSelection( item, xformedPt, selectionList, worldSpaceSelectPts, priorityMask, false ); return true; } return false; }
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; }
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; }
bool apiSimpleShapeUI::selectVertices( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const // // Description: // // Vertex selection. // // Arguments: // // selectInfo - the selection state information // selectionList - the list of selected items to add to // worldSpaceSelectPts - // { bool selected = false; M3dView view = selectInfo.view(); MPoint xformedPoint; MPoint currentPoint; MPoint selectionPoint; double z,previousZ = 0.0; int closestPointVertexIndex = -1; const MDagPath & path = selectInfo.multiPath(); // Create a component that will store the selected vertices // MFnSingleIndexedComponent fnComponent; MObject surfaceComponent = fnComponent.create( MFn::kMeshVertComponent ); int vertexIndex; // if the user did a single mouse click and we find > 1 selection // we will use the alignmentMatrix to find out which is the closest // MMatrix alignmentMatrix; MPoint singlePoint; bool singleSelection = selectInfo.singleSelection(); if( singleSelection ) { alignmentMatrix = selectInfo.getAlignmentMatrix(); } // Get the geometry information // apiSimpleShape* shape = (apiSimpleShape*) surfaceShape(); MVectorArray* geomPtr = shape->getControlPoints(); MVectorArray& geom = *geomPtr; // Loop through all vertices of the mesh and // see if they lie withing the selection area // int numVertices = geom.length(); for ( vertexIndex=0; vertexIndex<numVertices; vertexIndex++ ) { const MVector& point = geom[ vertexIndex ]; // Sets OpenGL's render mode to select and stores // selected items in a pick buffer // view.beginSelect(); glBegin( GL_POINTS ); glVertex3f( (float)point[0], (float)point[1], (float)point[2] ); glEnd(); if ( view.endSelect() > 0 ) // Hit count > 0 { selected = true; if ( singleSelection ) { xformedPoint = currentPoint; xformedPoint.homogenize(); xformedPoint*= alignmentMatrix; z = xformedPoint.z; if ( closestPointVertexIndex < 0 || z > previousZ ) { closestPointVertexIndex = vertexIndex; singlePoint = currentPoint; previousZ = z; } } else { // multiple selection, store all elements // fnComponent.addElement( vertexIndex ); } } } // If single selection, insert the closest point into the array // if ( selected && selectInfo.singleSelection() ) { fnComponent.addElement(closestPointVertexIndex); // need to get world space position for this vertex // selectionPoint = singlePoint; selectionPoint *= path.inclusiveMatrix(); } // Add the selected component to the selection list // if ( selected ) { MSelectionList selectionItem; selectionItem.add( path, surfaceComponent ); MSelectionMask mask( MSelectionMask::kSelectComponentsMask ); selectInfo.addSelection( selectionItem, selectionPoint, selectionList, worldSpaceSelectPts, mask, true ); } return selected; }
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; }