Any addons for picking objects in scene?

Does anyone know any addon which allow pick objects in scene?

There must be something, right? :slight_smile: No one did any work in this direction, like picking ofMesh’es or ofNode’s? I can try to do that, just don’t want to waste time if someone already has a working solution.

this might be relevant: problem with picking objects in opengl

also I know there is a camera addon that essentially allows you to grab objects in 3d to rotate around:
https://github.com/elliotwoods/ofxGrabCam – maybe this is a helpful starting point?

Yea, this might be a good start indeed, he reads z buffer under the cursor to know the depth. That’s something already.

Thanks, Zach!

cool – you might look at transform feedback as a way of doing picking:

https://www.opengl.org/wiki/Transform_Feedback
https://open.gl/feedback
http://www.glprogramming.com/red/chapter13.html

I haven’t worked with it, but Memo’s interactive object (https://github.com/memo/ofxMSAInteractiveObject) could be interesting.

It’s for 2D objects only, but thanks for pointing that out!

I did this before I copied the Ray class from cinder. Here is the ray class as well as an example for picking

ofRay.h

#pragma once
#include "ofMain.h"

class ofRay {
public:
    ofRay() {}
    ofRay( const ofVec3f &aOrigin, const ofVec3f &aDirection ) : mOrigin( aOrigin ) { setDirection( aDirection ); }
    
    void            setOrigin( const ofVec3f &aOrigin ) { mOrigin = aOrigin; }
    const ofVec3f&    getOrigin() const { return mOrigin; }
    
    void setDirection( const ofVec3f &aDirection ) {
        mDirection = aDirection;
        mInvDirection = ofVec3f( 1.0f / mDirection.x, 1.0f / mDirection.y, 1.0f / mDirection.z );
        mSignX = ( mDirection.x < 0.0f ) ? 1 : 0;
        mSignY = ( mDirection.y < 0.0f ) ? 1 : 0;
        mSignZ = ( mDirection.z < 0.0f ) ? 1 : 0;
    }
    const ofVec3f&    getDirection() const { return mDirection; }
    const ofVec3f&    getInverseDirection() const { return mInvDirection; }
    
    char    getSignX() const { return mSignX; }
    char    getSignY() const { return mSignY; }
    char    getSignZ() const { return mSignZ; }
    
    ofVec3f calcPosition( float t ) const { return mOrigin + mDirection * t; }
    
    bool calcTriangleIntersection( const ofVec3f &vert0, const ofVec3f &vert1, const ofVec3f &vert2, float *result ) const;
    bool calcPlaneIntersection( const ofVec3f &origin, const ofVec3f &normal, float *result ) const;
    
    friend class ofMeshFace;
    
protected:
    ofVec3f    mOrigin;
    ofVec3f    mDirection;
    // these are helpful to certain ray intersection algorithms
    char    mSignX, mSignY, mSignZ;
    ofVec3f    mInvDirection;
};

ofRay.cpp

#include "ofRay.h"

// algorithm from "Fast, Minimum Storage Ray-Triangle Intersection"
bool ofRay::calcTriangleIntersection( const ofVec3f &vert0, const ofVec3f &vert1, const ofVec3f &vert2, float *result ) const
{
    
    ofVec3f edge1, edge2, tvec, pvec, qvec;
    float det;
    float u, v;
    const float EPSILON = 0.000001f;
    
    edge1 = vert1 - vert0;
    edge2 = vert2 - vert0;
    
    pvec = getDirection().getCrossed( edge2 );
    det = edge1.dot( pvec );
    
#if 0 // we don't want to backface cull
    if ( det < EPSILON )
        return false;
    tvec = getOrigin() - vert0;
    
    u = tvec.dot( pvec );
    if ( ( u < 0.0f ) || ( u > det ) )
        return false;
    
    qvec = tvec.getCrossed(edge1);
    v = getDirection().dot( qvec );
    if ( v < 0.0f || u + v > det )
        return false;
    
    *result = edge2.dot( qvec ) / det;
    return true;
#else
    if( det > -EPSILON && det < EPSILON )
        return false;
    
    float inv_det = 1.0f / det;
    tvec = getOrigin() - vert0;
    u = tvec.dot( pvec ) * inv_det;
    if( u < 0.0f || u > 1.0f )
        return false;
    
    qvec = tvec.getCrossed(edge1);
    
    v = getDirection().dot( qvec ) * inv_det;
    if( v < 0.0f || u + v > 1.0f )
        return 0;
    
    *result = edge2.dot( qvec ) * inv_det;
    return true;
#endif
}

bool ofRay::calcPlaneIntersection( const ofVec3f &planeOrigin, const ofVec3f &planeNormal, float *result ) const
{
    float denom = planeNormal.dot(getDirection());
    
    if(denom != 0.0f){
        *result = planeNormal.dot(planeOrigin - getOrigin()) / denom;
        return true;
    }
    return false;
}

ofApp.h

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{
    
public:
    void setup();
    void update();
    void draw();
    
    void keyPressed  (int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y );
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);
    
    ofSpherePrimitive sphere;
    ofEasyCam cam;
};


ofApp.cpp

#include "ofApp.h"
#include "ofRay.h"

//--------------------------------------------------------------
void ofApp::setup(){
    sphere.setRadius(100);
}

//--------------------------------------------------------------

void ofApp::draw() {
    cam.begin();
    sphere.drawWireframe();
    cam.end();
}

void ofApp::mousePressed(int x, int y, int button){
    ofVec3f screenToWorld = cam.screenToWorld(ofVec3f(x,y,0.0));
    ofRay ray(cam.getPosition(),screenToWorld - cam.getPosition());
    const vector<ofMeshFace>& faces = sphere.getMesh().getUniqueFaces();
    bool intersection = false;
    float t = 0;
    for (int i = 0; i < faces.size() ; i++) {
        const ofMeshFace &face = faces[i];
        ofVec3f one = face.getVertex(0);
        ofVec3f two = face.getVertex(1);
        ofVec3f three = face.getVertex(2);
        intersection = ray.calcTriangleIntersection(one,two,three, &t);
        if (intersection) {
            break;
        }
    }
    if (intersection) {
        ofLog() << "intersection Point = " << ray.calcPosition(t);
        ofSetColor(ofColor::red);
    }

}

void ofApp::mouseReleased(int x, int y, int button){
    ofSetColor(ofColor::white);
}


I should probably try to send a pull request for ofRay.

@Ahbee, your code is picking a point on a sphere, it’s not an answer to my question… If you suggest to use rays to get an object under the cursor - it is a fairly slow process if you doing it on CPU. You should either iterate over all objects in scene or use kd-trees to speed up the process, things get hairy at that point. Speaking of Cinder, I’ll probably stick to Paul Houx’s picking by color method for now.

@procedural. Not sure what you mean? In my example when the user clicks on the sphere it turns red, if he clicks outside the sphere nothing happens. Isnt that 3d picking?. You can replace the sphere with any ofMesh. The color coded way is a faster way to do 3d picking. But does that method give you the exact 3d coordinates where you clicked? Thanks.

No, it is not, you hardcoded sphere and polygon tests against it. I can’t replace a sphere with any object because I don’t know how many objects will be created at runtime, and if I create many of them, iterating over all of them will be pretty performance expensive.

Getting exact coordinates on an object wasn’t my question :0) But thanks for attention!

@procedural, I did not hard code anything. You can create a function that takes a generic ofMesh, and it will tell you if your mouse is over it or not. What’s wrong with this?

bool intersectsMesh(const ofVec2f &screenCoordinates,const ofMesh &mesh,const ofCamera &cam,const ofMatrix4x4 &toWorldSpace){
    ofVec3f screenToWorld = cam.screenToWorld(ofVec3f(screenCoordinates.x,screenCoordinates.y,0.0));
    ofRay ray(cam.getPosition(),screenToWorld - cam.getPosition());
    const vector<ofMeshFace>& faces = mesh.getUniqueFaces();
    bool intersection = false;
    float t = 0;
    for (int i = 0; i < faces.size() ; i++) {
        const ofMeshFace &face = faces[i];
        // intersections are done worldSpace
        ofVec3f one = face.getVertex(0) * toWorldSpace;
        ofVec3f two = face.getVertex(1) * toWorldSpace;
        ofVec3f three = face.getVertex(2) * toWorldSpace;
        intersection = ray.calcTriangleIntersection(one,two,three, &t);
        if (intersection) {
            break;
        }
    }
    return intersection;
}


Nothing wrong with this approach, @Ahbee ! However in my case I can’t iterate over all objects in scene to know onto which object start casting a ray, also I don’t need a point on an object nor its attributes for now, so I’ll stick to more “cheaper” approach of color based selection or maybe something else, hope for understanding!

Hey I tried the ray from picking by iterating through a vector … but it is not working… can you help me? I just tried to use your code but it didn’t work correctly…

here is the code:

void ofApp::setup(){
    for(int i = 0; i< 10; i++){
        
        ofSpherePrimitive sphere = ofSpherePrimitive(10, 10);
        sphere.setPosition((i+1)*10, i*30, i);
        test.push_back(sphere);
    }
}

void ofApp::draw() {
    cam.begin();
    
    for(int i = 0; i< test.size(); i++){
        ofSpherePrimitive &sphere = test.at(i);
        sphere.draw();
    }
    cam.end();
}

void ofApp::update(){
    
}

void ofApp::mousePressed(int x, int y, int button){
    ofVec3f screenToWorld = cam.screenToWorld(ofVec3f(x,y,0.0));
    Ray ray(cam.getPosition(),screenToWorld - cam.getPosition());
    bool intersection = false;
    float t = 0;
    for (int k = 0; k< test.size(); k++){

    ofSpherePrimitive sphere = test.at(k);
    vector<ofMeshFace> faces = sphere.getMesh().getUniqueFaces();

    for (int i = 0; i < faces.size() ; i++) {
        ofMeshFace &face = faces[i];
        ofVec3f one = face.getVertex(0);
        ofVec3f two = face.getVertex(1);
        ofVec3f three = face.getVertex(2);
        intersection = ray.calcTriangleIntersection(one,two,three, &t);
        if (intersection) {
            break;
        }
    }
    if (intersection) {
        ofLog() << "intersection Point = " << ray.calcPosition(t) << "INTERSECT AT " << k;
        ofSetColor(ofColor::red);
        return;
    }
}

PROBLEM: It is only selecting the first element and not the others…
Any help appreciated…
Thank you !!!

thats cause you are not transforming to world space try this


bool intersectsMesh(const ofVec2f &screenCoordinates,const ofMesh &mesh,const ofCamera &cam,const ofMatrix4x4 &toWorldSpace){
    ofVec3f screenToWorld = cam.screenToWorld(ofVec3f(screenCoordinates.x,screenCoordinates.y,0.0));
    ofRay ray(cam.getPosition(),screenToWorld - cam.getPosition());
    const vector<ofMeshFace>& faces = mesh.getUniqueFaces();
    bool intersection = false;
    float t = 0;
    for (int i = 0; i < faces.size() ; i++) {
        const ofMeshFace &face = faces[i];
        // intersections are done worldSpace
        ofVec3f one = face.getVertex(0) * toWorldSpace;
        ofVec3f two = face.getVertex(1) * toWorldSpace;
        ofVec3f three = face.getVertex(2) * toWorldSpace;
        intersection = ray.calcTriangleIntersection(one,two,three, &t);
        if (intersection) {
            break;
        }
    }
    return intersection;
}


//--------------------------------------------------------------
void ofApp::setup(){
    for(int i = 0; i< 10; i++){
        ofSpherePrimitive sphere = ofSpherePrimitive(10, 10,OF_PRIMITIVE_TRIANGLES);
        sphere.setPosition((i+1)*10, i*30, i);
        test.push_back(sphere);
    }
}

void ofApp::draw() {
    cam.begin();
    for(int i = 0; i< test.size(); i++){
        ofSpherePrimitive &sphere = test.at(i);
        sphere.draw();
    }
    cam.end();
}

void ofApp::update(){
    
}

void ofApp::mousePressed(int x, int y, int button){
    for (int k = 0; k < test.size(); k++) {
        const ofSpherePrimitive& sphere = test[k];
        if (intersectsMesh(ofVec2f(x,y), sphere.getMesh(), cam,sphere.getGlobalTransformMatrix())) {
            ofSetColor(ofColor::red);
        }
    }
   
}

Thank you so much it works now… Picking is really tricky I was reading so many references but didn’t make it to work … than I found this forum post and with your help it works!!! What kind of references are you recommending? I want to understand it total…

Thank you
best regards

http://antongerdelan.net/opengl/raycasting.html

1 Like