[SOLVED]CollisionRay & Chess Game

Ok, I’m trying to porting the chess game that cames in Panda3D from Python to C++ and I’m having some problems. I can make this game without the other stuff (like the mouse with MouseWatcher and the lights effects), but I would really need to be able to pick up a piece. I could load all the models and put them on screen in the right order and when I try to do the collisions it just doesn’t works :S

I’ll try to post only the collision part but if some more code is needed please tell me.

cWorld::Init method
m_oWindow         = framework->open_window();
	m_oCamera         = m_oWindow->get_camera_group();

        m_oTraverser      = new CollisionTraverser    ();
	m_oHandlerQueue   = new CollisionHandlerQueue ();

	m_oCollisionRay   = new CollisionRay          ();

        CollisionRay::init_type                       ();
        m_oCollisionSolid = m_oCollisionRay->make_copy ();

	m_oCollisionNode  = new CollisionNode         ("pickRay");
	m_oCollisionNode->add_solid                   (m_oCollisionSolid);
	m_oCollisionNode->set_from_collide_mask       (BitMask32::bit(1));
	m_oCollisionNode->set_into_collide_mask       (BitMask32::all_off());
	m_oPickerNP       = m_oCamera.attach_new_node (m_oCollisionNode);

	m_oTraverser->add_collider                    (m_oPickerNP, m_oHandlerQueue);
	m_oTraverser->show_collisions (P3D_RENDER);
	m_oTraverser->traverse (m_oWindow->get_render());


LPoint3f cWorld::GetOrigin (void)
{
return cWorld::m_oWindow->get_render().get_relative_point (m_oCamera, m_oCollisionRay->get_origin());
}

LPoint3f cWorld::GetDirection (void) 
{ 
	return cWorld::m_oWindow->get_render().get_relative_vector (m_oCamera, m_oCollisionRay->get_direction());
}

void cWorld::TraverseModel (NodePath oModel)
{
	m_oTraverser->traverse (oModel);
}

bool cWorld::Collide (void)
{
	cout << "m_oHandlerQueue->get_num_entries() := " << m_oHandlerQueue->get_num_entries();
	if (m_oHandlerQueue->get_num_entries() > 0)
		return true;
	return false;
}

int cWorld::GetCollisionIndex (char *sCollideTag)
{
	m_oHandlerQueue->sort_entries();

	return atoi(m_oHandlerQueue->get_entry(0)->get_into_node()->get_tag(sCollideTag).c_str());
}


void cWorld::SetCollisionRay (void)
{
	bool bColRay = false;
	LPoint3f normalized;

	normalized.set_x (GetMouseX());
	normalized.set_y (GetMouseY());
	normalized.set_z (0);

	if (normalized.normalize())
	{
		bColRay = m_oCollisionRay->set_from_lens (m_oWindow->get_camera(0), normalized.get_x(), normalized.get_y());
		if (bColRay)
		{
			cout << "Yes!!!!\n";
		}
		else
		{
			cout << "No\n";
		}
	}
}


From a board function that updates the values...

		cWorld::SetCollisionRay();

		if (m_nDraggingPiece != -1)
		{
			m_nOrigin = cWorld::GetOrigin();
			m_nDirection = cWorld::GetDirection();
			m_oPieces[m_nDraggingPiece].SetPosition (PointAtZ(.5, m_nOrigin, m_nDirection)); 
		}
		cWorld::TraverseModel (m_oSquareRoot);

		cout << endl << "Collide? ";
		if (cWorld::Collide ())
		{
			cout << "YES!!!!!!" << endl;
			i = cWorld::GetCollisionIndex("square");
			m_nHiSq = i;
			m_oSquares[i].SetColor (HIGHLIGHT);
		}
		else
		{
			cout << "NOOOO :(" << endl;
		}


When I run the code Collide allways returns me false, since m_oHandlerQueue->get_num_entries() returns allways 0. I know I’m doing something wrong but I don’t know what could it be and it’s very frustrating :S

ahh the c++ makes my head hurt.

I don’t see anything obviously wrong; you could try turning on the collision debugging with:

notify-level-collide debug

or even

notify-level-collide spam

in your Config.prc. This may provide some more insight.

David

Thanks :slight_smile:

Hummm… with notify-level-collide debug I got nothing, but when I change it for spam it sais

:collide(spam):     Considering render/squareRoot
:collide(spam):     Comparing 0: bline, empty to bsphere, c (0 0 0), r 5.65685,
is_in = 0
:collide(spam):     render/squareRoot has 0 interested colliders ( )

However… I don’t know what this could mean… :frowning:

It means that the collision test didn’t get past the first node. It was rejected there, either because the bounding volume of your ray didn’t intersect the bounding volume of your scene, or because there were no collision bits in common between your ray’s from_collide_mask and the into_collide_mask of your scene.

I’m guessing, looking at your code, that it’s the latter case: you set your ray to from_collide_mask BitMask32::bit(1), but you don’t set that bit on any nodes in your scene. If there are no collision nodes in your scene, then, there’s nothing to pick.

David

Thanks again but nope… I’m doing it… just forgot to copy that piece of code…

// Load squared board
	for (i = 0; i < CHESS_SQUARES; i++)
	{
		m_oSquares[i].SetScale(1.0);
		m_oSquares[i].Init(this->GetWindowFramework(), this->GetPandaFramework());
		m_oSquares[i].LoadModel(SQUARE);
		m_oSquares[i].Reparent(this->GetModel());
		m_oSquares[i].SetPosition(SquarePos(i));
		m_oSquares[i].SetColor(SquareColor(i));
		sprintf(temp, "%d", i);
		m_oSquares[i].SetCollide("**/polygon", BitMask32::bit(1), "square", temp);
		
	}

And the SetCollide method does the following thing…

void cModel::SetCollide (const std::string &path, CollideMask mask, const std::string &key, const std::string &value)
{
	m_oModel.find(path).node()->set_into_collide_mask(mask);
	m_oModel.find(path).node()->set_tag(key, value);
}

… :S

I just realized in the output it says:

the bounding volume of your CollisionRay, normally a bounding line or bline, is empty. This means the CollisionRay is not properly set up. Something must be going wrong in the set_from_lens() call. In fact, now I see the problem here:

   m_oCollisionSolid = m_oCollisionRay->make_copy ();
   m_oCollisionNode  = new CollisionNode         ("pickRay");
   m_oCollisionNode->add_solid                   (m_oCollisionSolid);

Look: you are adding a copy of your ray to your collision node–not the ray itself. The copy is made at the time the ray is uninitialized, so the copy is also uninitialized. Later, you call ray->set_from_lens(), but of course this has no effect on the copy.

David

It’s great… it still returns me that no collision was made but after replacing the code

m_oCollisionSolid = m_oCollisionRay->make_copy ();
   m_oCollisionNode  = new CollisionNode         ("pickRay");
   m_oCollisionNode->add_solid                   (m_oCollisionSolid);

by

m_oCollisionNode  = new CollisionNode         ("pickRay");
   m_oCollisionNode->add_solid                   (m_oCollisionRay);

It returned me the following messages

:collide(spam):     Considering render/squareRoot
:collide(spam):     Comparing 0: bline, (0.357266 -12.8437 5.57738) - (0.693704
-11.9902 5.1794) to bsphere, c (0 0 0), r 5.65685, is_in = 1
:collide(spam):     render/squareRoot has 1 interested colliders ( 0. pickRay )
:collide(spam):       Considering render/squareRoot/square.egg
:collide(spam):       Comparing 0: bline, (0.357266 -12.8437 5.57738) - (0.69370
4 -11.9902 5.1794) to bsphere, c (-3.5 -3.5 0), r 0.707107, is_in = 0
:collide(spam):       render/squareRoot/square.egg has 0 interested colliders (
)

The collision function is as follow

bool cWorld::Collide (void)
{
	cout << "m_oHandlerQueue->get_num_entries() := " << m_oHandlerQueue->get_num_entries();
	if (m_oHandlerQueue->get_num_entries() > 0)
		return true;
	return false;
}

Unfortunatelly the get_num_entries() keeps returning me 0, however in the spam I saw it got 1… dunno if it’s good or bad but I guess It’s getting close, so it you could help me out a little more I guess I’ll have it :smiley:

No, in the spam it didn’t detect any collisions. It passed one bounding volume test, but then failed the next one. Short answer: your ray missed.

David

Argh… that’s bad… any idea on why did it failed now? I would really appreciate it… Thanks a lot!

Well, did it actually pass through any of your collision geometry? Or did it miss?

David

Humm, I found where could be the problem…
I’m using this function to set the lens…

void cWorld::SetCollisionRay (void)
{
	bool bColRay = false;
	LPoint3f normalized;

	normalized.set_x (GetMouseX());
	normalized.set_y (GetMouseY());
	//normalized.set_z (0);

	if (normalized.normalize())
	{
		bColRay = m_oCollisionRay->set_from_lens (m_oWindow->get_camera(0), normalized.get_x(), normalized.get_y());
	}
}

and if I use get_num_entries it returns 0, however, if I change it to this other thing

bColRay = m_oCollisionRay->set_from_lens (m_oWindow->get_camera(0), 0.001f, 0.001f);

Then it can collide with one of board’s pieces, but I would like to be able to drag a piece so I believe I should be able to use the mouse to set the array, however I can’t get it work :frowning:

Umm, why are you normalizing the vector? That’s just weird, and all it’s going to do is munge the X,Y pair to the wrong value.

Just pass the X, Y pair directly from the mouse coordinates, like this:

bColRay = m_oCollisionRay->set_from_lens (m_oWindow->get_camera(0), GetMouseX(), GetMouseY());

I’m assuming that your GetMouseX() and GetMouseY() functions are just wrappers around MouseWatcher::get_mouse_x() and get_mouse_y(), and so they should return values in the range -1 to 1 across the entire screen. This is the range that set_from_lens() expects to receive.

David

Hi and thanks :slight_smile:
Hummm, nope, I’m using

return m_oWindow->get_graphics_window()->get_pointer(0).get_x();

and that’s because I’m normalizing the point in SetCollisionRay… the problem is that if I use the MouseWatcher method they also returns the same (x = 0 and y = 0.4), thus, the collision is made only in the top mid four squares in the board… :S

Huh? I don’t understand your sentence. get_pointer(0)->get_x() returns basically the same thing as MouseWatcher::get_mouse_x(). The second interface is slightly preferred since it supports the data graph the way it is designed, but either gives the same result.

In any case, you want to take that raw mouse coordinate and pass it to set_from_lens() without normalizing it. Normalizing the vector make no sense whatsoever.

David

Hummm, nope… get_pointer(0)->get_x() returns a value between 0 and the window width and get_pointer(0)->get_y() returns a value between 0 and window height. SetFromLens needs parameters in the form of 0…1 values, thus I need to normalize those results to get that range… however it doesn’t work… :S

Oh, you’re right. But that’s a different meaning of “normalize”. In this case, you need to “normalize” your values to the range -1…1 by dividing by the width and height of your window, and then doubling the result and subtracting 1. That’s completely different from what Vec3.normalize() does.

Or, you can just use the MouseWatcher values, which are already in the right range.

David

Thanks a lot!!! Normalizing the points in the range -1…1 worked perfect!!!
I’ll add some chess rules to it and fix some things and try to post it to get a little example on how panda & Visual C++ can work.

Thanks a lot again :smiley: