In this recipe, we will calculate the intersection of the mouse cursor with a 3D model.
Include the necessary files to draw using OpenGL, use textures and load images, load 3D models, define OpenGL lights and materials, and use Cinder's Maya camera.
#include "cinder/gl/gl.h" #include "cinder/gl/Texture.h" #include "cinder/gl/Light.h" #include "cinder/gl/Material.h" #include "cinder/TriMesh.h" #include "cinder/ImageIo.h" #include "cinder/MayaCamUI.h"
Also, add the following using
statements:
using namespace ci; using namespace ci::app; using namespace std;
We will use a 3D model, so place a file and its texture in the assets
folder. For this example, we will be using a mesh file named ducky.msh
and a texture named ducky.png
.
ci::CameraPersp
and ci::Ray
classes to convert the mouse coordinates to our rotated 3D scene and calculate the intersection with a 3D model.ci::MayaCamUI
object for easy navigation, and a ci::gl::Material
for lighting:TriMesh mMesh; gl::Texture mTexture; MayaCamUI mCam; bool mIntersects; Vec3f mNormal, mHitPos; AxisAlignedBox3f mMeshBounds; gl::Material mMaterial;
ci::Ray
class and the triangles that make up mMesh
.void calcIntersectionWithMeshTriangles( const ci::Ray& ray );
setup
method, lets load the model and texture and calculate its bounding box:mMesh.read( loadAsset( "ducky.msh" ) ); mTexture = loadImage( loadAsset( "ducky.png" ) ); mMeshBounds = mMesh.calcBoundingBox();
setup
method:CameraPersp cam; Vec3f modelCenter = mMeshBounds.getCenter(); cam.setEyePoint( modelCenter + Vec3f( 0.0f, 0.0f, 20.0f ) ); cam.setCenterOfInterestPoint( modelCenter ); mCam.setCurrentCam( cam );
mMaterial.setAmbient( Color::black() ); mMaterial.setDiffuse( Color::white() ); mMaterial.setEmission( Color::black() );
mouseDown
and mouseDrag
events.void mouseDown( MouseEvent event ); void mouseDrag( MouseEvent event );
mCam
:void MyApp::mouseDown( MouseEvent event ){ mCam.mouseDown( event.getPos() ); } void MyApp::mouseDrag( MouseEvent event ){ mCam.mouseDrag( event.getPos(), event.isLeftDown(), event.isMiddleDown(), event.isRightDown() ); }
update
method and calculate the intersection between the mouse cursor and our model. Let's begin by getting the mouse position and then calculate ci::Ray
emitting from our camera:Vec2f mousePos = getMousePos(); float u = mousePos.x / (float)getWindowWidth(); float v = mousePos.y / (float)getWindowHeight(); CameraPersp cameraPersp = mCam.getCamera(); Ray ray = cameraPersp.generateRay( u, 1.0f - v, cameraPersp.getAspectRatio() );
true
, we will call the calcIntersectionWithMeshTriangles
method.if( mMeshBounds.intersects( ray ) == false ){ mIntersects = false; } else { calcIntersectionWithMeshTriangles( ray ); }
calcIntersectionWithMeshTriangles
method. We will iterate over all the triangles of our model and calculate the nearest intersection and store its index.float distance = 0.0f; float resultDistance = 999999999.9f; int resultIndex = -1; int numTriangles = mMesh.getNumTriangles(); for( int i=0; i<numTriangles; i++ ){ Vec3f v1, v2, v3; mMesh.getTriangleVertices( i, &v1, &v2, &v3 ); if( ray.calcTriangleIntersection( v1, v2, v3, &distance ) ){ if( distance <resultDistance ){ resultDistance = distance; resultIndex = i; } } }
mIntersects
to false
.if( resultIndex> -1 ){ mHitPos = ray.calcPosition( resultDistance ); mIntersects = true; Vec3f v1, v2, v3; mMesh.getTriangleVertices( resultIndex, &v1, &v2, &v3 ); mNormal = ( v2 - v1 ).cross( v3 - v1 ); mNormal.normalize(); } else { mIntersects = false; }
draw
method:gl::clear( Color( 0, 0, 0 ) ); gl::setMatrices( mCam.getCamera() ); gl::enableDepthRead(); gl::enableDepthWrite();
gl::Light light( gl::Light::POINT, 0 ); light.setPosition( mCam.getCamera().getEyePoint() ); light.setAttenuation( 1.0f, 0.0f, 0.0f ); glEnable( GL_LIGHTING ); light.enable(); mMaterial.apply();
mTexture.enableAndBind(); gl::draw( mMesh ); mTexture.unbind(); glDisable( GL_LIGHTING );
mIntersects
is true
and draw a sphere at the intersection point and the normal vector.if( mIntersects ){ gl::color( Color::white() ); gl::drawSphere( mHitPos, 0.2f ); gl::drawVector( mHitPos, mHitPos + ( mNormal * 2.0f ) ); }
To calculate the intersection of the mouse with the model in 3D, we generated a ray from the mouse position towards the view direction of the camera.
For performance reasons, we first calculate if the ray intersects with the model's bounding box. In case there is an intersection with the model, we further calculate the intersection between the ray and each triangle that makes up the model. For every intersection found, we check its distance and calculate the intersection point and the normal of only the nearest intersection.
3.133.128.145