Ray casting and tiles

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Ray casting and tiles

Post by tBane »

Hi. I coding Editor Map. I need info which tile is clicked. I wrote prototype, but not working this. I need you help.

Image

code of tile

Code: Select all

class HexTile : public scene::ISceneNode
{

	aabbox3d<f32> Box;
	S3DVertex Vertices[8];
	SMaterial Material;
	

public:
	terrainType ttype;

	HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id);
	virtual void OnRegisterSceneNode();
	virtual void render();
	virtual const aabbox3d<f32>& getBoundingBox() const;
	virtual u32 getMaterialCount() const;
	virtual SMaterial& getMaterial(u32 i);
	void setTexture(ITexture*);
	void setTerrainType(terrainType);
};

HexTile::HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: ISceneNode(parent, smgr, id)
{
	ttype = terrainType::grass;

	/////////////////////////////////////////////////
	Material.Wireframe = false;
	Material.Lighting = false;
	Material.setTexture(0, getTexture(ttype));
	
	SColor color =  SColor(255, 255, 255, 255);

	// vertices of hexagons
	for (int i = 0; i < 7; i++)
		Vertices[i+1] = S3DVertex(hexVertices[2*i], 0.0f, hexVertices[2 * i + 1], 0, 0, 0, color, texture_uv[2 * i], texture_uv[2 * i + 1]);

	Box.reset(Vertices[0].Pos);
	for (s32 i = 1; i < 7; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
}

void HexTile::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this);

	ISceneNode::OnRegisterSceneNode();
}

void HexTile::render()
{
	u16 indices[] = { 0, 1, 2, /**/ 0, 2, 3, /**/ 0, 3, 4, /**/ 0, 4, 5, /**/ 0, 5, 6, /**/ 0, 6, 7 };
	IVideoDriver* driver = smgr->getVideoDriver();
	driver->setMaterial(Material);
	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
	driver->drawVertexPrimitiveList(&Vertices[0], 3, &indices[0], 6, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);

}

const aabbox3d<f32>& HexTile::getBoundingBox() const
{
	return Box;
}

u32 HexTile::getMaterialCount() const
{
	return 1;
}

SMaterial& HexTile::getMaterial(u32 i)
{
	return Material;
}

void HexTile::setTexture(ITexture* tex)
{
	Material.setTexture(0, tex);
}

void HexTile::setTerrainType(terrainType ttype)
{
	this->ttype = ttype;
	setTexture(getTexture(ttype));
}
code of raycasting

Code: Select all

position2d < s32 > mousePos = device->getCursorControl()->getPosition();
cout << "mouse position: " << mousePos.X << ", " << mousePos.Y<<" - ";

core::line3d<f32> ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(mousePos, cam);
core::vector3df intersection;
core::triangle3df hitTriangle;
scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();

if (collMan != nullptr)
	cout << "collman ok - ";

scene::ISceneNode* selectedSceneNode =
	collMan->getSceneNodeAndCollisionPointFromRay(
		ray,
		intersection,	// This will be the position of the collision
		hitTriangle);// ,	// This will be the triangle hit in the collision
		//0,				// This ensures that only nodes that we have set up to be pickable are considered
		//0);				// Check the entire scene (this is actually the implicit default)

if (selectedSceneNode != nullptr)
	cout << " | " << selectedSceneNode->getPosition().X << ", " << selectedSceneNode->getPosition().Y << endl;
else
	cout << " node is null\n";
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

Your node needs to have a triangle selector for the collision manager to work. It's basically geometry data so the line can collide against something.

With a custom node you have 2 options:
First: Instead of using just Vertices you create a real mesh inside your node. Then the ISceneManager::createTriangleSelector function can be used to create one for you. I'd recommend that - having a mesh doesn't cost much and has more advantages (for example if you ever port to WebGL then you need that anyway).
Second: You can also create a new ITriangeSelector which implements that interface specific for your node. I think the terrain node in Irrlicht does that for example. Has the advantage that you can sometimes save a bit memory. And you can pass additional information (as it's your own triangle selector and you can put it in whatever you want).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

how create a mesh inside node ?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

Mesh with: new irr::scene::SMesh()
Then add a meshbuffer - in your case an SMeshBuffer will probably do.
So: new irr::scene::SMeshBuffer()
Then add the meshbuffer to the mesh.
That meshbuffer can contain your vertices like you did above.

That's basically it. It adds a few dynamic allocations, so not free and if the map is really huge you might want to approach it a bit different. But if it's just about a few thousand tiles that's fine.

edit: Btw, if you have created your own mesh you don't need your own SceneNode anymore (unless you want it). You can then also call ISceneManager::addMeshSceneNode and use the one from Irrlicht.
Your solution is also useful sometimes (for certain optimizations). But then you have to write your own triangle selector (which is also not too complicated). Always more than one option to do things ;-)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

Code: Select all

HexTile::HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: ISceneNode(parent, smgr, id)
{
	ttype = terrainType::grass;

	/////////////////////////////////////////////////
	Material.Wireframe = false;
	Material.Lighting = false;
	Material.setTexture(0, getTexture(ttype));
	
	SColor color =  SColor(255, 255, 255, 255);

	// vertices of hexagons
	for (int i = 0; i < 7; i++)
		Vertices[i + 1] = S3DVertex(hexVertices[2 * i] * 0.8f, 0.0f, hexVertices[2 * i + 1] * 0.8f, 0, 0, 0, color, texture_uv[2 * i], texture_uv[2 * i + 1]);

	mesh = new SMesh();
	meshBuffer = new SMeshBuffer();
	mesh->addMeshBuffer(meshBuffer);
	//meshBuffer->append(Vertices, 8) ???  
	
	Box.reset(Vertices[0].Pos);
	for (s32 i = 1; i < 7; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
}
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

Check source/Irrlicht/CGeometryCreator.cpp - it has several functions which work with SMeshBuffer. Or example 23 SMeshHandling.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

Code: Select all

HexTile::HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: ISceneNode(parent, smgr, id)
{
	ttype = terrainType::grass;

	/////////////////////////////////////////////////
	Material.Wireframe = false;
	Material.Lighting = false;
	Material.setTexture(0, getTexture(ttype));

	mesh = new SMesh();
	meshBuffer = new SMeshBuffer();
	

	SColor color = SColor(255, 255, 255, 255);
	const u16 u[18] = { 0,1,2,   0,2,3,   0,3,4, 0,4,5,   0,5,6,   0,6,7 };

	meshBuffer->Indices.set_used(18);

	for (u32 i = 0; i < 18; ++i)
		meshBuffer->Indices[i] = u[i];

	meshBuffer->Vertices.push_back(S3DVertex(0, 0, 0, 0, 0, 0, color, 0, 0 ));
	for (int i = 0; i < 7; i++)
		meshBuffer->Vertices.push_back(S3DVertex(hexVertices[2*i], 0.0f, hexVertices[2*i+1], 0, 0, 0, color, texture_uv[2 * i], texture_uv[2 * i + 1]));

	mesh->addMeshBuffer(meshBuffer);

	Box.reset(Vertices[0].Pos);
	for (s32 i = 1; i < 7; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
}
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

Looks good. Now you can create a triangle selector for it and set it for the node. Then your first code for collision should work.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

now not render map :-/

Code: Select all

class HexTile : public scene::ISceneNode
{
	aabbox3d<f32> Box;
	SMaterial Material;

	S3DVertex Vertices[8]; 

	SMesh* mesh;
	SMeshBuffer* meshBuffer;

public:
	terrainType ttype;

	HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id);
	virtual void OnRegisterSceneNode();
	virtual void render();
	virtual const aabbox3d<f32>& getBoundingBox() const;
	virtual u32 getMaterialCount() const;
	virtual SMaterial& getMaterial(u32 i);
	void setTexture(ITexture*);
	void setTerrainType(terrainType);
};

HexTile::HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: ISceneNode(parent, smgr, id)
{
	ttype = terrainType::grass;

	meshBuffer = new SMeshBuffer();
	
	// set the indices
	const u16 u[18] = { 0,1,2,	0,2,3,	0,3,4,	0,4,5,	0,5,6,	0,6,7 };

	meshBuffer->Indices.set_used(18);
	for (u32 i = 0; i < 18; ++i)
		meshBuffer->Indices[i] = u[i];

	SColor color = SColor(255, 255, 255, 255);

	// set the vertices
	meshBuffer->Vertices.push_back(S3DVertex(0, 0, 0, 0, 0, 0, color, 0, 0 ));
	for (int i = 0; i < 7; i++)
		meshBuffer->Vertices.push_back(S3DVertex(hexVertices[2*i], 0.0f, hexVertices[2*i+1], 0, 0, 0, color, texture_uv[2 * i], texture_uv[2 * i + 1]));

	// set the bounding box
	meshBuffer->BoundingBox.reset(Vertices[0].Pos);
	for (s32 i = 1; i < 7; ++i)
		Box.addInternalPoint(Vertices[i].Pos);

	meshBuffer->recalculateBoundingBox();

	// set the material
	meshBuffer->Material.Wireframe = false;
	meshBuffer->Material.Lighting = false;
	meshBuffer->Material.setTexture(0, getTexture(ttype));

	mesh = new SMesh();
	mesh->addMeshBuffer(meshBuffer);
}
Last edited by tBane on Wed Feb 21, 2024 9:12 pm, edited 2 times in total.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

Maybe you have to update your draw code. You can directly use driver->drawMeshbuffer now. Otherwise set breakpoint (or printf) in draw code and check if it reaches it.

edit: Also you can use recalculateBoundingBox () for your mesh. And then set your node Box to the result.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

now i have this code. I need add to this code collision with mouse cursor.
Image

Code: Select all

class HexTile : public scene::ISceneNode
{
	SMesh* mesh;
	SMeshBuffer* meshBuffer;

public:
	terrainType ttype;

	HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id);
	virtual void OnRegisterSceneNode();
	virtual void render();
	virtual const aabbox3d<f32>& getBoundingBox() const;
	virtual u32 getMaterialCount() const;
	virtual SMaterial& getMaterial(u32 i);
	void setTexture(ITexture*);
	void setTerrainType(terrainType);
};

HexTile::HexTile(ISceneNode* parent, ISceneManager* smgr, s32 id)
	: ISceneNode(parent, smgr, id)
{
	ttype = terrainType::grass;

	meshBuffer = new SMeshBuffer();
	
	// SET THE INDICES

	meshBuffer->Indices.set_used(18);
	for (u32 i = 0; i < 18; ++i)
		meshBuffer->Indices[i] = hexIndices[i];

	// SET THE VERTICES
	SColor color = SColor(255, 255, 255, 255);
	
	meshBuffer->Vertices.push_back(S3DVertex(0, 0, 0, 0, 0, 0, color, 0, 0 ));
	for (int i = 0; i < 7; i++)
		meshBuffer->Vertices.push_back(S3DVertex(hexVertices[2*i], 0.0f, hexVertices[2*i+1], 0, 0, 0, color, texture_uv[2 * i], texture_uv[2 * i + 1]));


	// SET THE BOUNDING BOX
	meshBuffer->BoundingBox.reset(vector3df(hexVertices[0], 0, hexVertices[1]));
	for (s32 i = 1; i < 7; ++i)
		meshBuffer->BoundingBox.addInternalPoint(vector3df(hexVertices[2 * i], 0, hexVertices[2 * i + 1]));


	// SET THE MATERIAL
	meshBuffer->Material.Wireframe = false;
	meshBuffer->Material.Lighting = false;
	meshBuffer->Material.setTexture(0, getTexture(ttype));

	mesh = new SMesh();
	mesh->addMeshBuffer(meshBuffer);
	mesh->recalculateBoundingBox();
}

void HexTile::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this);

	ISceneNode::OnRegisterSceneNode();
}

void HexTile::render()
{
	IVideoDriver* driver = smgr->getVideoDriver();
	driver->setMaterial(meshBuffer->Material);
	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
	driver->drawMeshBuffer(meshBuffer);

}

const aabbox3d<f32>& HexTile::getBoundingBox() const
{
	return meshBuffer->BoundingBox;
}

u32 HexTile::getMaterialCount() const
{
	return 1;
}

SMaterial& HexTile::getMaterial(u32 i)
{
	return meshBuffer->Material;
}

void HexTile::setTexture(ITexture* tex)
{
	meshBuffer->Material.setTexture(0, tex);
}

void HexTile::setTerrainType(terrainType ttype)
{
	this->ttype = ttype;
	setTexture(getTexture(ttype));
}
Last edited by tBane on Wed Feb 21, 2024 9:41 pm, edited 1 time in total.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

ISceneManager::createTriangleSelector with the mesh and node (you have to pass both as often same mesh is used by several nodes, although not in your case). Then ISceneNode::setTriangleSelector. And hopefully then your original collMan->getSceneNodeAndCollisionPointFromRay code will work.

Side-note - you don't need the indices in render anymore
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

Can you show me how to program it?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Ray casting and tiles

Post by CuteAlien »

It's just those 2 lines I mentioned. But I messed up post originally - createTriangleSelector is in ISceneManager, not ISceneNode.
And you need some function in your node to return the mesh so you can pass it to createTriangleSelector.
So for each HexTile node, something like

Code: Select all

 ITriangleSelector * selector = smgr->createTriangleSelector (hexTile->getMesh(), hexTile);
hexTile->setTriangleSelector(selector);
selector->drop(); // node has it now, so can drop
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
tBane
Posts: 33
Joined: Wed Feb 21, 2024 2:25 pm
Location: Poland

Re: Ray casting and tiles

Post by tBane »

I not have idea where use it ..

part of main.cpp

Code: Select all

			// UPDATE
			position2d < s32 > mousePos = device->getCursorControl()->getPosition();
			cout << "mouse position: " << mousePos.X << ", " << mousePos.Y<<endl;
			
			core::line3d<f32> ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(mousePos, cam);
			cout << "ray start: " << ray.start.X << ", " << ray.start.Y << ", " << ray.start.Z << endl;
			ray.end.X = 0;
			ray.end.Y = -10;
			ray.end.Z = 0;
			cout << "ray end: " << ray.end.X << ", " << ray.end.Y << ", " << ray.end.Z << endl;
			

			core::vector3df intersection;
			core::triangle3df hitTriangle;
			scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
			
			if (collMan != nullptr)
				cout << "collMan ok "<<endl;
			else	
				cout << "collMaan is nullptr "<<endl;


			scene::ISceneNode* selectedSceneNode = collMan->getSceneNodeAndCollisionPointFromRay( ray, intersection, hitTriangle);
			
			ITriangleSelector* selector;
			for (auto& tile : hexMap->tiles)
			{
				selector = smgr->createTriangleSelector(tile->getMesh(), tile);
				tile->setTriangleSelector(selector);
			}

			if (selectedSceneNode != nullptr)
				cout << "collision node position: " << selectedSceneNode->getPosition().X << ", " << selectedSceneNode->getPosition().Y << endl;
			else
				cout << "collision node is null\n";

			cout << endl;
Post Reply