Simple Collision Example

Hi,

I’m trying to set up a simple collision detection example as outlined in the manual.

The example suggests using:

cSphere_node->show();

however I get an error when trying to use it.

error C2039: ‘show’ : is not a member of ‘CollisionNode’

What should I use instead?

Also, I think that the following is incorrect:

// Setup collision detection for the camera.
PT(CollisionSphere) cs = new CollisionSphere();
PT(CollisionNode) cSphere_node = new CollisionNode("Sphere");
cSphere_node->add_solid(cs);

as it causes the following error:

.\Test.cpp(287) : error C2248: ‘CollisionSphere::CollisionSphere’ : cannot access protected member declared in class ‘CollisionSphere’
C:\Panda3D-1.7.0\include\collisionSphere.h(35) : see declaration of ‘CollisionSphere::CollisionSphere’
C:\Panda3D-1.7.0\include\collisionSphere.h(27) : see declaration of ‘CollisionSphere’

Also, once I’ve created the collisionSphere and attached it to a collisionNode, how do I then attach this node to the camera?

Create a NodePath to point to the CollisionNode, and you can call show()/hide() on that, and reparent_to it to the camera.

I’d like to have things set up so that the camera can’t pass through the ground plane.

There seems to be two ways to do this: (1) using standard collision detection, and (2) using CollisionHandlerFloor.

Is there a recommended way?

Another thing I’m not clear about is how the collision node gets attached to the node that represents the object (in this case, the camera).

I’ve tried the following:

NodePath groundPlane = window->load_model(framework.get_models(), mydir + "./../models/ground");
	groundPlane.set_two_sided(true);
	groundPlane.reparent_to(window->get_render());
	groundPlane.set_scale(1.0, 1.0, 1.0);
	groundPlane.set_pos(0, 0, 0);
	PT(CollisionPolygon) groundCollisionPoly = new CollisionPolygon(LPoint3f(-500, -500, 0), 
									   LPoint3f(500, -500, 0), 
									   LPoint3f(500, 500, 0), 
									   LPoint3f(-500, 500, 0));

	PT(CollisionNode) cGroundNode = new CollisionNode("Ground");
	cGroundNode->add_solid(groundCollisionPoly);
	NodePath groundCollisionNodePath = window->get_render().attach_new_node("groundCollisionNodePath");
	groundCollisionNodePath.reparent_to(mainCamera);
	groundCollisionNodePath.show();

However, I can’t see how to attach the CollisionNode to the NodePath.

How do I do this?

The following is my attempt at getting collision detection working. At the moment, all I’m trying to do is get the camera to collide with the ground and then get pushed back. However, nothing seems to be happening. What am I missing here?

//====================================================
// Task to check for camera collision.

AsyncTask::DoneStatus CameraCollisionTask(GenericAsyncTask* task, void* data) {    
	
	//std::cout << "Collision test..." << std::endl;
	
	// Check collisions, will call pusher collision handler
    // if a collision is detected.
    collTrav->traverse(window->get_render());     
	
	return AsyncTask::DS_cont;
}

...

// Load the ground plane.
	NodePath groundPlane = window->load_model(framework.get_models(), mydir + "./../models/ground");
	groundPlane.set_two_sided(true);
	groundPlane.reparent_to(window->get_render());
	groundPlane.set_scale(1.0, 1.0, 1.0);
	groundPlane.set_pos(0, 0, 0);
	PT(CollisionPolygon) groundCollisionPoly = new CollisionPolygon(LPoint3f(-500, -500, 0), 
									   LPoint3f(500, -500, 0), 
									   LPoint3f(500, 500, 0), 
									   LPoint3f(-500, 500, 0));

	PT(CollisionNode) cGroundNode = new CollisionNode("Ground");
	cGroundNode->add_solid(groundCollisionPoly);
	NodePath groundCollisionNodePath = groundPlane.attach_new_node(cGroundNode);
	groundCollisionNodePath.reparent_to(groundPlane);

...

// Get the main camera.
	mainCamera = window->get_camera_group();
	mainCamera.set_pos(0, -50, 2);

	// Setup general collision detection for the camera.
	PT(CollisionSphere) cSphere = new CollisionSphere(mainCamera.get_pos().get_x(),
												 mainCamera.get_pos().get_y(),
												 mainCamera.get_pos().get_z(),
												 1.0f);
	PT(CollisionNode) cSphereNode = new CollisionNode("Sphere");
	cSphereNode->add_solid(cSphere);
	NodePath cameraCollisionNodePath = mainCamera.attach_new_node(cSphereNode);
	//cameraCollisionNodePath.show();

	// Setup the collisions handler and pusher.
    CollisionHandlerPusher pusher;
    collTrav = new CollisionTraverser();
	
	// Camera will be pushed if a collision is detected
	pusher.add_collider(cameraCollisionNodePath, mainCamera);
	collTrav->add_collider(cameraCollisionNodePath, &pusher);

...

// Add the task to handle camera collision.
	taskMgr->add(new GenericAsyncTask("Checks for camera collisons", &CameraCollisionTask, (void*) NULL));
	

Any chance one of the forum gurus could have a look at this as I still can’t get it to work :smiley:

You could try trav->show_collisions(window->get_render()) to enable the collision visualiser and see if anything collides.

Collision bugs can be hard to diagnose. If it helps, you can try turning on spammy output with:

notify-level-collide spam

in your Config.prc file. This will report every node it visits during the collision traversal. “Interested” nodes are those whose bitmasks and bounding volumes intersect with that of your from object, so if you see “0 interested nodes” where you think there should be at least “1 interested nodes”, then investigate the bitmasks or bounding volumes at that point.

David

I’m still having trouble getting a simple collision detection example to work. I’ve tried using

trav->show_collisions(window->get_render())

What does this actually show?

I’ve also tried turning on

notify-level-collide spam

which seems to give a lot of output however I’m not sure what it means.

Below is the complete code example I’m using. Maybe someone could try running it to figure out what I’m doing wrong.

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

#include "genericAsyncTask.h"
#include "asyncTaskManager.h"

#include "collisionSphere.h"
#include "collisionPolygon.h"
#include "collisionNode.h"
#include "collisionHandlerPusher.h"

#include "pStatClient.h"

#include <cmath>
#include <vector>

//====================================================

PandaFramework framework;
WindowFramework* window;

Filename mydir;

NodePath mainCamera;

const float mvtDelta = 0.25f;
const float rotAngle = 1.0f;

CollisionTraverser* collTrav;

PT(AsyncTaskManager) taskMgr = AsyncTaskManager::get_global_ptr(); 
PT(ClockObject) globalClock = ClockObject::get_global_clock();

double currentTime = 0.0;
double lastUpdateTime = 0.0;

const float UPDATE_INTERVAL = 0.25;

//====================================================

void loadModels();
void setupKeyboard();
void setupCollisions();
void setupTasks();

//====================================================
// Task to check for camera collision.

AsyncTask::DoneStatus CameraCollisionTask(GenericAsyncTask* task, void* data) {    
	
	//std::cout << "Collision test..." << std::endl;
	
	// Check collisions, will call pusher collision handler if a collision is detected.
    collTrav->traverse(window->get_render());     
	
	return AsyncTask::DS_cont;
}

//====================================================

void moveFW(const Event* theEvent, void * data) {
	
	std::cout << "I moved forwards!" << std::endl;
	float x = mainCamera.get_pos().get_x();
	float y = mainCamera.get_pos().get_y();
	float heading_in_radians = (mainCamera.get_h() * 3.14159265) / 180.0;
	x -= mvtDelta * sin(heading_in_radians);
	y += mvtDelta * cos(heading_in_radians);

	mainCamera.set_pos(x, y, mainCamera.get_pos().get_z()); 
}

//====================================================

void moveBW(const Event* theEvent, void * data) {
	
	std::cout << "I moved backwards!" << std::endl;
	float x = mainCamera.get_pos().get_x();
	float y = mainCamera.get_pos().get_y();
	float heading_in_radians = (mainCamera.get_h() * 3.14159265) / 180.0;
	x += mvtDelta * sin(heading_in_radians);
	y -= mvtDelta * cos(heading_in_radians);

	mainCamera.set_pos(x, y, mainCamera.get_pos().get_z());  
}

//====================================================

void moveLeft(const Event* theEvent, void * data) {
	
	std::cout << "I moved left!" << std::endl;
	float x = mainCamera.get_pos().get_x();
	float y = mainCamera.get_pos().get_y();
	float heading_in_radians = (mainCamera.get_h() * 3.14159265) / 180.0;
	heading_in_radians += 3.14159265 / 2.0;
	x -= mvtDelta * sin(heading_in_radians);
	y += mvtDelta * cos(heading_in_radians);

	mainCamera.set_pos(x, y, mainCamera.get_pos().get_z()); 
}

//====================================================

void moveRight(const Event* theEvent, void * data) {
	
	std::cout << "I moved right!" << std::endl;
	float x = mainCamera.get_pos().get_x();
	float y = mainCamera.get_pos().get_y();
	float heading_in_radians = (mainCamera.get_h() * 3.14159265) / 180.0;
	heading_in_radians -= 3.14159265 / 2.0;
	x -= mvtDelta * sin(heading_in_radians);
	y += mvtDelta * cos(heading_in_radians);

	mainCamera.set_pos(x, y, mainCamera.get_pos().get_z()); 
}

//====================================================

void moveUp(const Event* theEvent, void * data) {
	
	std::cout << "I moved up!" << std::endl;
	float z = mainCamera.get_pos().get_z();
	z += mvtDelta;

	mainCamera.set_pos(mainCamera.get_pos().get_x(), mainCamera.get_pos().get_y(), z); 
}

//====================================================

void moveDown(const Event* theEvent, void * data) {
	
	std::cout << "I moved down!" << std::endl;
	float z = mainCamera.get_pos().get_z();
	z -= mvtDelta;

	mainCamera.set_pos(mainCamera.get_pos().get_x(), mainCamera.get_pos().get_y(), z); 
}

//====================================================

void rotateAC(const Event* theEvent, void * data) {
	
	std::cout << "I rotated anticlockwise!" << std::endl;
	mainCamera.set_h(mainCamera.get_h() + rotAngle); 
}

//====================================================

void rotateCW(const Event* theEvent, void * data) {
	
	std::cout << "I rotated clockwise!" << std::endl;
	mainCamera.set_h(mainCamera.get_h() - rotAngle); 
}

//====================================================

void connectToPStats(const Event* theEvent, void* data) {

	if(PStatClient::is_connected()) {  
		PStatClient::disconnect();
		std::cout << "Disconnected from PStats server." << std::endl;
	} else {
	
		string host = "";	// Empty = default config var value
		int port = -1;		// -1 = default config var value
		if(!PStatClient::connect(host, port)) {  
			std::cout << "Could not connect to PStats server." << std::endl;
		} else {
			std::cout << "Connected to PStats server." << std::endl;
		}
	}
}
//====================================================

int main(int argc, char *argv[]) {    
	
	//open a new window framework  
	framework.open_framework(argc, argv);    
	
	//set the window title to My Panda3D Window  
	framework.set_window_title("Collision Test");    
	
	//open the window  
	window = framework.open_window();

	// Get the main camera.
	mainCamera = window->get_camera_group();
	mainCamera.set_pos(0, -50, 2);

	// Get the location of the executable file I'm running.
	mydir = ExecutionEnvironment::get_binary_name();
	mydir = mydir.get_dirname();
	nout << mydir << "\n";

	setupKeyboard();
	loadModels();
	setupCollisions();	
	setupTasks();
			
	//do the main loop, equal to run() in python  
	framework.main_loop();    
	
	//close the window framework  
	framework.close_framework();  
	
	return (0);
}

//====================================================

void loadModels() {

	// Load the ground plane.
	NodePath groundPlane = window->load_model(framework.get_models(), mydir + "./../models/ground");
	groundPlane.reparent_to(window->get_render());
	groundPlane.set_scale(1.0, 1.0, 1.0);
	groundPlane.set_pos(0, 0, 0);

	CollisionNode* cGroundNode = new CollisionNode("Ground");
	cGroundNode->add_solid(new CollisionPolygon(LPoint3f(-500, -500, 0), 
									   LPoint3f(500, -500, 0), 
									   LPoint3f(500, 500, 0), 
									   LPoint3f(-500, 500, 0)));
	NodePath groundCollisionNodePath = groundPlane.attach_new_node(cGroundNode);
	groundCollisionNodePath.show();
}

//====================================================

void setupKeyboard() {

	// Setup the keyboard control.
	window->enable_keyboard();

	framework.define_key("arrow_up", "arrow_up", moveFW, 0); 
	framework.define_key("arrow_up-up", "arrow_up-up", moveFW, 0);
	framework.define_key("arrow_up-repeat", "arrow_up-repeat", moveFW, 0);

	framework.define_key("arrow_down", "arrow_down", moveBW, 0); 
	framework.define_key("arrow_down-up", "arrow_down-up", moveBW, 0);
	framework.define_key("arrow_down-repeat", "arrow_down-repeat", moveBW, 0);

	framework.define_key("arrow_left", "arrow_left", moveLeft, 0); 
	framework.define_key("arrow_left-up", "arrow_left-up", moveLeft, 0);
	framework.define_key("arrow_left-repeat", "arrow_left-repeat", moveLeft, 0);

	framework.define_key("arrow_right", "arrow_right", moveRight, 0); 
	framework.define_key("arrow_right-up", "arrow_right-up", moveRight, 0);
	framework.define_key("arrow_right-repeat", "arrow_right-repeat", moveRight, 0);

	framework.define_key("u", "u", moveUp, 0); 
	framework.define_key("u-up", "u-up", moveUp, 0);
	framework.define_key("u-repeat", "u-repeat", moveUp, 0);

	framework.define_key("d", "d", moveDown, 0); 
	framework.define_key("d-up", "d-up", moveDown, 0);
	framework.define_key("d-repeat", "d-repeat", moveDown, 0);

	framework.define_key("a", "a", rotateAC, 0); 
	framework.define_key("a-up", "a-up", rotateAC, 0);
	framework.define_key("a-repeat", "a-repeat", rotateAC, 0);

	framework.define_key("s", "s", rotateCW, 0); 
	framework.define_key("s-up", "s-up", rotateCW, 0);
	framework.define_key("s-repeat", "s-repeat", rotateCW, 0);

	framework.define_key("c", "c", connectToPStats, 0);
}

//====================================================

void setupCollisions() {

	// Setup general collision detection for the camera.
	CollisionNode* cSphereNode = new CollisionNode("Sphere");
	cSphereNode->add_solid(new CollisionSphere(mainCamera.get_pos().get_x(),
											   mainCamera.get_pos().get_y(),
											   mainCamera.get_pos().get_z(),
											   1.0f));

	NodePath cameraCollisionNodePath = mainCamera.attach_new_node(cSphereNode);
	cameraCollisionNodePath.show();

	// Setup the collision handler and pusher.
    CollisionHandlerPusher* pusher = new CollisionHandlerPusher();
    collTrav = new CollisionTraverser();
	
	// Camera will be pushed if a collision is detected
	pusher->add_collider(cameraCollisionNodePath, mainCamera);
	collTrav->add_collider(cameraCollisionNodePath, pusher);
	collTrav->show_collisions(window->get_render());
}

//====================================================

void setupTasks() {

	// Add the task to handle camera collision.
	taskMgr->add(new GenericAsyncTask("Checks for camera collisons", &CameraCollisionTask, (void*) NULL));
}

//====================================================

Hmm, that’s a lot of code to read. Scanning through it, I don’t see anything obviously wrong; I think you’ll have to look closer to find the problem.

It could be as simple as that your collision solids are not in the same space, for instance. Or maybe the bitmasks aren’t matching (but since the default mask is almost all bits on, and I don’t see you changing it, that’s probably not the case).

Do you see “1 interested colliders” (or more) anywhere in the spam output? That’s what you want to be looking for. That means that the collision traverser found a possible intersection and is looking closer. You want to see “1 interested colliders” all the way down to the node that it should actually be intersecting with. If you ever see “0 interested colliders” about some parent node of the node you should be intersecting with, then something is wrong at that point.

It might also be helpful to call show() on the NodePath containing your CollisionNodes, so you can see the collision solids visibly. This might shed some light on the lack of intersection detection.

show_collisions() just draws a visible collision solid whenever a collision is detected, or when its bounding volume is sufficiently close to warrant a closer look.

David

Here’s some of the output I’m getting from the code listed above. What I’ve attempted to do is wrap a sphere around the camera and create a collision plane for the ground. All I want to do is detect when the camera collides with the ground plane, and then stop the camera penetrating it. The camera is initialized with a z-value of 2 and has a collision sphere of radius 1 surrounding it.

The first section is the output when the program starts and there should be no collisions:

The second section is when I’ve moved the camera downwards so that it’s intersecting with the ground plane:

Does this shed any light on why the CollisionHandlerPusher isn’t pushing the sphere when it intersects with the ground plane?

Are you sure that the camera’s sphere is actually intersecting the geometry in ground.egg?

First, it appears that what you’re calling a “ground plane” is actually one or more polygons, not a CollisionPlane. That’s OK, but polygons are a little less reliable than planes, because they’re infinitely thin; and you should be careful with your terminology so you don’t get confused with Panda’s own terminology.

That’s probably not the problem here, though. I don’t know what the geometry of your ground polygons are, but I see that their center is at (0, 0, 0), and I assume they’re perfectly flat and horizontal. I also see that the center of your camera sphere is (0, -100, 2), and that its radius is 1, which means the bottom of the sphere is still at z = 1, above the ground.

David

Have you tried manually setting the collision bitmasks?
Some time ago I had a problem with undetected collisions and I solved it by just setting the bitmasks, it seems like the default one didn’t work…

Is there an example of how to set collision bitmasks anywhere? If not, would it be possible for someone to post a brief example of how to do this.

Also, would it be possible for someone to post a simple collision example. At the moment, what would help me a lot would be an example that handles a collision (and pushes back) between a flat plane representing the ground and a sphere around the camera. All I’m after is an example that shows how to stop the camera penetrating the ground.