Problem with Ray Casting

I want to set up the ray casting. I saw some topics about it (e. g this) and “Click on 3D object” manual, but I don’t understand yet why my code doesn’t work. Can someone help me please?

Note: Ground.bam is created with gltf2bam.

#include <iostream>

#include <pandaFramework.h>
#include <pandaSystem.h>

#include <asyncTask.h>
#include <collisionHandlerQueue.h>
#include <collisionRay.h>
#include <CollisionBox.h>

#include <mouseWatcher.h>

PT(MouseWatcher) mouseWatcher;
WindowFramework* window;

PT(CollisionRay) pickerRay;
PT(CollisionNode) pickerNode;
NodePath pickerNP;
CollisionTraverser myTraverser = CollisionTraverser("ctraverser");

PT(CollisionHandlerQueue) myHandler;

void func(const Event* event, void* data) {
    // This gives up the screen coordinates of the mouse.
    MouseData mpos = window->get_graphics_window()->get_pointer(0);
    std::cout << window->get_graphics_window()->get_pointer(0).get_x() << window->get_graphics_window()->get_pointer(0).get_y() << "\n";

    // This makes the ray's origin the camera and makes the ray point
    // to the screen coordinates of the mouse.
    pickerRay->set_from_lens(window->get_camera(0), mpos.get_x(), mpos.get_y());

    myTraverser.traverse(window->get_render());
    for (int i = 0; i < myHandler->get_num_entries(); ++i) {
        CollisionEntry* entry = myHandler->get_entry(i);
        std::cout << *entry << "\n";
    }
    if (myHandler->get_num_entries() == 0) {
        std::cout << "No collisions :(\n";
    }
}

int main(int argc, char* argv[]) {

    PandaFramework framework;
    if (argc > 0) {
        framework.open_framework(argc, argv);
    }
    else {
        framework.open_framework();
    }
    framework.set_window_title("Raycast");
    window = framework.open_window(); 
    window->enable_keyboard();

    //Setup the camera
    NodePath camera = window->get_camera_group();
    camera.set_pos(20 * sin(0), -20.0 * cos(0), 3);
    camera.set_hpr(0, 0, 0);

    //Setup mouse and collisions
    mouseWatcher = new MouseWatcher("Watcher");
    pickerNode = new CollisionNode("mouseRay");
    pickerNP = camera.attach_new_node(pickerNode);
    pickerNode->set_from_collide_mask(GeomNode::get_default_collide_mask());
    pickerRay = new CollisionRay();
    pickerNode->add_solid(pickerRay);
    myHandler = new CollisionHandlerQueue();
    myTraverser.add_collider(pickerNP, myHandler);
    myTraverser.show_collisions(window->get_render());

    //Loading models
    NodePath ground = window->load_model(window->get_render(), "models/Ground.bam"); ground.set_scale(16, 10, 1); ground.set_pos(0, 5, 0);
    ground.set_tag("RayCastEnabled", "1"); 
    /*PT(CollisionNode) grcn = new CollisionNode("grcnode");
    for (int i = 0; i < ground.get_num_nodes(); i++) {
        if (ground.get_node(i)->is_geom_node()) {
            grcn->set_into_collide_mask(static_cast<GeomNode*>(ground.get_node(i))->get_into_collide_mask());
        }
    }
    ground.attach_new_node(grcn);*/

    //Event handler
    //framework.define_key("mouse1", "event", &func, nullptr);
    framework.get_event_handler().add_hook("mouse1", &func, nullptr); 


    framework.main_loop();
    framework.close_framework();
    return (0);
}

A collision mask must be specified for the ground.

ground->set_from_collide_mask(GeomNode::get_default_collide_mask());

However, the GeomNode has its own method.

ground.set_collide_mask(new_mask, bits_to_change, node_type);

I tried this code:

PT(CollisionNode) grcn = new CollisionNode("grcnode");
grcn->set_from_collide_mask(GeomNode::get_default_collide_mask());
ground.attach_new_node(grcn); 

And this:

ground.set_collide_mask(GeomNode::get_default_collide_mask());

But it doesn’t work.

If it is important: now I use Panda 1.10.16.

Here is the short code.

#include "pandaFramework.h"
#include "pandaSystem.h"

#include "collisionNode.h"
#include "collisionHandlerQueue.h"
#include "collisionRay.h"
#include "pandabase.h"
#include "mouseWatcher.h"

#include <iostream>
using namespace std;

WindowFramework* window;
PT(MouseWatcher) mouseWatcher;
PT(CollisionRay) pickerRay = new CollisionRay();
PT(CollisionHandlerQueue) myHandler = new CollisionHandlerQueue();
CollisionTraverser myTraverser = CollisionTraverser("ctraverser");

void keyEvent(const Event * e, void * data) {
    if (mouseWatcher->has_mouse()) {
        if (window->get_graphics_window()) {
            LPoint2f mpos = mouseWatcher->get_mouse();
            pickerRay->set_from_lens(window->get_camera(0), mpos.get_x(), mpos.get_y());

            myTraverser.traverse(window->get_render());
            myHandler->sort_entries();
            
            if (myHandler->get_num_entries() > 0){
                NodePath obj = myHandler->get_entry(0)->get_into_node_path();
                cout << obj << endl;
            }
        }
    }
}

int main(int argc, char* argv[]) {

    PandaFramework framework;
    framework.open_framework(argc, argv);
    
    window = framework.open_window(); 
    window->enable_keyboard();
    window->setup_trackball();

    NodePath render = window->get_render();
    NodePath camera = window->get_camera_group();
    mouseWatcher = DCAST(MouseWatcher, window->get_mouse().node());

    PT(CollisionNode) pickerNode = new CollisionNode("mouseRay");
    NodePath pickerNP = camera.attach_new_node(pickerNode);

    pickerNode->set_from_collide_mask(BitMask32::bit(1));
    pickerNode->add_solid(pickerRay);

    myTraverser.add_collider(pickerNP, myHandler);
    myTraverser.show_collisions(render);

    NodePath ground = window->load_model(render, "groung.obj");
    ground.set_collide_mask(BitMask32::bit(1));

    framework.define_key("mouse1", "", &keyEvent, nullptr);

    framework.main_loop();
    framework.close_framework();

    return (0);
}
1 Like

Thank you a lot! It works.

A quick note: be aware that, as I understand it, colliding with normal, visible geometry may be slower than with dedicated collision geometry (even if that geometry is generated from the same base mesh).

Of course, if you’re not hitting a performance issue, then well and good!

This is more a caveat to have in mind should you hit performance issues later.

1 Like

This warning comes from a misinterpretation of the visible geometry. The whole point is that the geometry for the collision needs to be modeled separately from the geometry for rendering. Accordingly, it should be simplified as much as possible to calculate the collision.

I do not know where this term came from in the Panda3D game engine, but it is very wrong. Because the geometry for the collision is also modeled in a 3d editor, and there is no other way.

I seem to think that I’ve seen drwr or some other such dev comment to that effect.

And indeed, looking around, I found this post:

Note that drwr there indicates that visible geometry is less efficient for collision than even collision geometry of the exact same shape. And further that collision geometry is optimised for the purpose.

I’d guess that Panda has a different internal structure for collision geometry than for visible geometry, allowing faster traversals.

[edit]
Here’s an even clearer post:

In particular, note this line:

Although it is true that simpler geometry is faster still, collision geometry would be about 100 times faster even if you exactly duplicated your visible geometry 1:1 in the scene graph.