Raycasting to the polygon level [solved]

Problems building or running the engine, queries about how to use features etc.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

funguine wrote:the following is copy-paste from our Mogre-port of your algorithm,
as displayed on the Wiki. Seems to work fine. This version also
reports the normal of the face that was hit.
((code snipped away))

Someone with access to wiki, please feel free ...
You have access to the Wiki, as do all members of the Ogre community.
Feel free to update the code - we appreciate it! :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
smernesto
Halfling
Posts: 78
Joined: Wed Jan 03, 2007 12:49 am
Location: Bogota, Colombia

Post by smernesto »

HI,

anyone has an example for using the code?

I need to know what entity the ray hit.

Thanks

Ernesto
funguine
Gnoblar
Posts: 10
Joined: Mon Jan 15, 2007 5:28 pm
Location: Oulu, Finland
x 1
Contact:

Post by funguine »

smernesto wrote:HI,
anyone has an example for using the code?
I need to know what entity the ray hit.
RaycastFromPoint as displayed on the wiki returns you true if an object
was hit by the ray specified by the parameters 'point' and 'normal' (latter
would be better called 'direction'). In the parameter list are also two (Mogre version) results, 'result' (better 'position') and 'resNormal', which in case true has been returned contain the coordinate where the ray intersects the mesh,
and the normal vector for the surface at that point. To get the name of
the entity that was hit you would need to add a third result parameter,
and set it thus:

Code: Select all

            if (ncf > -1)
            {
                closest_result = ray.GetPoint(closest_distance);
                // if you don't need the normal, comment this out; you'll save some CPU cycles.
                Vector3 v1 = vertices[indices[ncf]] - vertices[indices[ncf + 1]];
                Vector3 v2 = vertices[indices[ncf + 2]] - vertices[indices[ncf + 1]];
                vNormal = v1.CrossProduct(v2);
                name = pentity.Name; // THIS HERE NEW LINE
            }

My version does this and more, I could clean it up a bit and update the Wiki page. Soon :)

The general Raycast test using this function would be something like:

Code: Select all

                Vector3 point = Vector3.ZERO;
                Vector3 direction = -Vector3.UNIT_Z;
                Vector3 pos = Vector3.ZERO;
                Vector3 norm = Vector3.ZERO;
                String name = "";
                if (RaycastFromPoint(point, direction, ref pos, ref norm, ref name))
                { // an object was hit by the ray
                     Console.WriteLine(name + " was hit at " + pos.ToString() + ". Normal: " + norm.ToString());
                } else
                    Console.WriteLine("No object was hit by the ray");
               

User avatar
smernesto
Halfling
Posts: 78
Joined: Wed Jan 03, 2007 12:49 am
Location: Bogota, Colombia

Post by smernesto »

Will be great if you upload your version of this code to the wiki.

EDIT: thanks , i modified the code and now It works for me, it gives me the name of the entity.

There is any way I can use the code without the unsafe method?
Minimoi
Gnoblar
Posts: 3
Joined: Thu Jul 26, 2007 8:40 am
Location: FRANCE Brest

Post by Minimoi »

There is a little bug in the fonction, at line:

// get the mesh information
OgreVE::GetMeshInformation(pentity->getMesh(), vertex_count, vertices, index_count, indices,
pentity->getParentNode()->getWorldPosition(),
pentity->getParentNode()->getWorldOrientation(),
pentity->getParentNode()->getScale());


It 's not pentity->getParentNode()->getScale() but pentity->getParentNode()->_getDerivedScale().
User avatar
xabila
Goblin
Posts: 225
Joined: Mon Jun 05, 2006 9:40 am
Location: rennes [FR]

Post by xabila »

I found the same bug yesterday, you should update the wiki then ;)
User avatar
Pascal
Gnoblar
Posts: 4
Joined: Thu Apr 20, 2006 10:32 pm
Location: Poland, Chybie

Post by Pascal »

I am trying to raycast mesh created from ManualObject created with option OT_TRIANGLE_STRIP (or OT_TRIANGLE_FAN). I have error at line:

Code: Select all

std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, vertices[indices[i]], vertices[indices[i+1]], vertices[indices[i+2]], true, false);
It means that only OT_TRIANGLE_LIST is supported? Maybe, the reason is that index count is always 3 * n. Triangle fans or strips don't always have 3 * n indices.
KuRi
Goblin
Posts: 242
Joined: Wed Jul 05, 2006 4:19 pm

Post by KuRi »

Any sample code on using this with the mouse input to pickup polygons with the mouse?

Thanks!

PD: Self-Solved:

Code: Select all

Ray mouseray = mCamera->getCameraToViewportRay(MouseX,MouseY);
bool ok = RaycastFromPoint(mouseray.getPoint(0),mouseray.getDirection(),obj->Position);
User avatar
foxmulder900
Halfling
Posts: 48
Joined: Fri Mar 03, 2006 9:40 pm
Location: Portsmouth, OH

Post by foxmulder900 »

Sorry for just pasting a bunch of code especially since much of it was pasted above. But I am using this picking method, however, I modified it slightly for my needs. It works i would say 80% of the time however sometimes when It should be returning something it does not.

Code: Select all

		ray = camera->getCameraToMouseRay();
		query->setRay(ray);
		
		results = query->execute();

		float closest_distance = -1;
		Vector3 closest_result;
		Entity* closest_entity = NULL;

		for(unsigned int i=0; i<results.size(); i++)
		{
			
			if(closest_distance >= 0 && closest_distance < results[i].distance)
				break;
			if(results[i].movable && results[i].movable->getName().find("GrassPage") != string::npos)
				continue;
			if(results[i].movable && results[i].movable->getMovableType().compare("Entity") == 0)
			{
				Entity* ent = (Entity*)results[i].movable;
				Node* node = ent->getParentNode();

				// mesh data to retrieve         
				size_t vertex_count;
				size_t index_count;
				Vector3 *vertices;
				unsigned long *indices;

				GetMeshInformation(ent->getMesh(), vertex_count, vertices, index_count, indices, node->getWorldPosition(), node->getWorldOrientation(), node->getScale());
				bool new_closest_found = false;
				for(int j=0; j<(int)index_count; j+=3)
				{
					std::pair<bool, Real> hit = Math::intersects(ray, vertices[indices[j]], vertices[indices[j+1]], vertices[indices[j+2]], true, false);
					if(hit.first)
					{
						if(closest_distance < 0 || hit.second < closest_distance)
						{
							closest_distance = hit.second;
							new_closest_found = true;
						}
					}
				}	
				delete[] vertices;
				delete[] indices;
				
				if(new_closest_found)
				{
					closest_result = ray.getPoint(closest_distance);
					closest_entity = ent;
				}
			}

		}
		if(closest_entity)
			cout << closest_entity->getName() << endl;
		else
			cout << "null" << endl;
I am using my own camera class and I am sure that the getCameraToMouseRay() function is returning the correct ray.

As you can see I put a few cout statements at the end for testing and like I said about 80% of my clicks that should return an entity do, however a few of them are slipping through the code somehow! If anyone sees anything I am missing please let me know! (fyi I did not modify the GetMeshInformation function)
User avatar
RobG
Gnoblar
Posts: 14
Joined: Wed Oct 17, 2007 11:10 pm

Post by RobG »

I'm guessing that your method is relying on hitting the object's bounding box before starting to work out which polygons you are hitting. Switch your bounding box display on and check if your mesh is extending past it's edges. This is what was happening to me when I used an animated mesh.
User avatar
foxmulder900
Halfling
Posts: 48
Joined: Fri Mar 03, 2006 9:40 pm
Location: Portsmouth, OH

Post by foxmulder900 »

It was my understanding that the fastest way (if not the only way) was to check if a ray collides on the bounding box level - then continue on to check to the polygon level. Maybe I am misunderstanding your suggestion.
User avatar
RobG
Gnoblar
Posts: 14
Joined: Wed Oct 17, 2007 11:10 pm

Post by RobG »

Yes I agree entirely. I was simply stating that it is possible for a mesh to leave the bounding box. Therefore when you try cast a ray that doesn't go through the bounding box, but does go through the mesh, you will get a NULL result.

Just letting you know that this could potentially be your problem. And the way to fix it would be to make sure all the meshes are fully encompassed in their respective AABBs.
CurlyHairedGenius
Gnoblar
Posts: 1
Joined: Sun Nov 30, 2008 4:49 am

polygon level after animation transform...

Post by CurlyHairedGenius »

I know this is kind of an old thread...

I would like to use this method for some collision detection, but I noticed it does not take into account the animation transforms.

Is there a way to raycast to the polygon level at the current animation state.

I think there should be a way to redo the GetMeshInformation function but I'm not exactly sure on the particulars.


I was thinking it could be done at the entity level instead of the mesh level something along the lines of:

Ogre::VertexData * vData = ent->_getHardwareVertexAnimVertexData();

const Ogre::VertexElement* pElem = vData->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

Ogre::HardwareVertexBufferSharedPtr vBuf = vData->vertexBufferBinding->getBuffer(pElem->getSource());

unsigned char* vert = static_cast<unsigned char*>(vBuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));



I am pretty new to ogre and I feel like I know this is incomplete...

Does anyone have any ideas on how to raycast to the polygon level after the animation transforms?
User avatar
mcaden
Goblin
Posts: 206
Joined: Wed Jul 02, 2008 9:29 am
Location: Texas, USA
Contact:

Post by mcaden »

KuRi wrote:Any sample code on using this with the mouse input to pickup polygons with the mouse?

Thanks!

PD: Self-Solved:

Code: Select all

Ray mouseray = mCamera->getCameraToViewportRay(MouseX,MouseY);
bool ok = RaycastFromPoint(mouseray.getPoint(0),mouseray.getDirection(),obj->Position);
I don't see any problem with this code but I'm having trouble getting this to work. Would using an orthogonal projection skew that mouseray a bit?

Every raycast is returning false. I'm using the mogre code from the wiki and funguine's addition of returning the entity name with KuRi's method of mousePicking.

Code: Select all

if (e.state.ButtonDown(MouseButtonID.MB_Left))
{
    Mogre.Vector3 vec = Mogre.Vector3.ZERO;
    Mogre.Vector3 result = Mogre.Vector3.ZERO;
    String name = "";
    Ray mouseray = ExCamera.getOgreCamera.GetCameraToViewportRay( e.state.X.abs, e.state.Y.abs );

    if (RaycastFromPoint(mouseray.GetPoint(0), mouseray.Direction, ref result, ref vec, ref name))
    {
        LogManager.Singleton.LogMessage(name + " was hit at " + result.ToString() + ". Normal: " + vec.ToString());
    }
    else
    {
        LogManager.Singleton.LogMessage("shootin blanks");
    }
}
sms1986
Gnoblar
Posts: 24
Joined: Thu Jun 12, 2008 8:17 pm

Re: polygon level after animation transform...

Post by sms1986 »

CurlyHairedGenius wrote:I know this is kind of an old thread...

I would like to use this method for some collision detection, but I noticed it does not take into account the animation transforms.

Is there a way to raycast to the polygon level at the current animation state.

I think there should be a way to redo the GetMeshInformation function but I'm not exactly sure on the particulars.


I was thinking it could be done at the entity level instead of the mesh level something along the lines of:

Ogre::VertexData * vData = ent->_getHardwareVertexAnimVertexData();

const Ogre::VertexElement* pElem = vData->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

Ogre::HardwareVertexBufferSharedPtr vBuf = vData->vertexBufferBinding->getBuffer(pElem->getSource());

unsigned char* vert = static_cast<unsigned char*>(vBuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));



I am pretty new to ogre and I feel like I know this is incomplete...

Does anyone have any ideas on how to raycast to the polygon level after the animation transforms?
I have the same doubt...
Rumi
Halfling
Posts: 40
Joined: Mon Jan 08, 2007 9:31 pm
x 1

Re: Raycasting to the polygon level [solved]

Post by Rumi »

Same problem here... :?
I was using this function for a long time without any problem, until i start using it on animated entities. :(
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Re: Raycasting to the polygon level [solved]

Post by jacmoe »

Just update the animation.
Rebuild the information -> You must fetch the mesh information each frame.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
Rumi
Halfling
Posts: 40
Joined: Mon Jan 08, 2007 9:31 pm
x 1

Re: Raycasting to the polygon level [solved]

Post by Rumi »

That,s the point. I update my animation each frame, but then my rays keep colliding with the original mesh...
How do i fetch the mesh information every frame? I can't find anything about how to do this...
Doing this will make the rays collide with the animated state of the mesh?
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Re: Raycasting to the polygon level [solved]

Post by jacmoe »

Each frame you need to get vertex index mesh information from the animating mesh.
You need to update the animation and the model (IIRC) too, so that the transforms are applied before the frame is over.
It's a bit resource intensive, as you can imagine.

You will need to issue a software animation request, before you use it. Otherwise you won't be able to access the data.

Look here:
http://ogreconglo.svn.sourceforge.net/s ... code/trunk

Look at trunk\src\OgreMeshCollisionShape.cpp
And the demo.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
Rumi
Halfling
Posts: 40
Joined: Mon Jan 08, 2007 9:31 pm
x 1

Re: Raycasting to the polygon level [solved]

Post by Rumi »

Thanks jacmoe!
I looked the code, and the solution was to insert this code in my geometry checking function:

Code: Select all

...
bool useSoftwareBlendingVertices = entity->hasSkeleton();

for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
{
                Ogre::SubMesh* submesh = mesh->getSubMesh(i);

		//----------------------------------------------------------------
		// GET VERTEXDATA
		//----------------------------------------------------------------
		Ogre::VertexData* vertex_data;

                //THIS IS THE CODE THAT SOLVED THE ANIMATION PROBLEM:
		if(useSoftwareBlendingVertices)
#ifdef BUILD_AGAINST_AZATHOTH
			vertex_data = submesh->useSharedVertices ? entity->_getSharedBlendedVertexData() : entity->getSubEntity(i)->_getBlendedVertexData();
#else
			vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData();
#endif
		else
			vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
               //THIS IS THE CODE THAT SOLVED THE ANIMATION PROBLEM
                
                ...
Now i can raycast and pick an entity when it's animated! :D
Thanks again!
User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
x 16
Contact:

Re: Raycasting to the polygon level [solved]

Post by KungFooMasta »

Rumi, thanks for sharing your solution. Can you share the function as a whole? Did you adapt the function in the wiki (http://www.ogre3d.org/wiki/index.php/Ra ... ygon_level) or have your own subroutines?
Creator of QuickGUI!
Rumi
Halfling
Posts: 40
Joined: Mon Jan 08, 2007 9:31 pm
x 1

Re: Raycasting to the polygon level [solved]

Post by Rumi »

Yes, i adapted that function. Here is the whole function:

Code: Select all

void GetMeshInformation(const Entity *entity,
                                size_t &vertex_count,
                                Ogre::Vector3* &vertices,
                                size_t &index_count,
                                unsigned long* &indices,
                                const Ogre::Vector3 &position,
                                const Ogre::Quaternion &orient,
                                const Ogre::Vector3 &scale)
{
    bool added_shared = false;
    size_t current_offset = 0;
    size_t shared_offset = 0;
    size_t next_offset = 0;
    size_t index_offset = 0;
    vertex_count = index_count = 0;

	Ogre::MeshPtr mesh = entity->getMesh();


	bool useSoftwareBlendingVertices = entity->hasSkeleton();
	/*
	if (useSoftwareBlendingVertices)
	{
		entity->_updateAnimation();
	}
	*/


    // Calculate how many vertices and indices we're going to need
    for (unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
    {
        Ogre::SubMesh* submesh = mesh->getSubMesh( i );

        // We only need to add the shared vertices once
        if(submesh->useSharedVertices)
        {
            if( !added_shared )
            {
                vertex_count += mesh->sharedVertexData->vertexCount;
                added_shared = true;
            }
        }
        else
        {
            vertex_count += submesh->vertexData->vertexCount;
        }

        // Add the indices
        index_count += submesh->indexData->indexCount;
    }


    // Allocate space for the vertices and indices
    vertices = new Ogre::Vector3[vertex_count];
    indices = new unsigned long[index_count];

    added_shared = false;

    // Run through the submeshes again, adding the data into the arrays
    for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
    {
        Ogre::SubMesh* submesh = mesh->getSubMesh(i);

		//----------------------------------------------------------------
		// GET VERTEXDATA
		//----------------------------------------------------------------

        //Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
		Ogre::VertexData* vertex_data;

		//When there is animation:
		if(useSoftwareBlendingVertices)
#ifdef BUILD_AGAINST_AZATHOTH
			vertex_data = submesh->useSharedVertices ? entity->_getSharedBlendedVertexData() : entity->getSubEntity(i)->_getBlendedVertexData();
#else
			vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData();
#endif
		else
			vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;


        if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
        {
            if(submesh->useSharedVertices)
            {
                added_shared = true;
                shared_offset = current_offset;
            }

            const Ogre::VertexElement* posElem =
                vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

            Ogre::HardwareVertexBufferSharedPtr vbuf =
                vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());

            unsigned char* vertex =
                static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));

            // There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
            //  as second argument. So make it float, to avoid trouble when Ogre::Real will
            //  be comiled/typedefed as double:
            //      Ogre::Real* pReal;
            float* pReal;

            for( size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
            {
                posElem->baseVertexPointerToElement(vertex, &pReal);

                Ogre::Vector3 pt(pReal[0], pReal[1], pReal[2]);

                vertices[current_offset + j] = (orient * (pt * scale)) + position;
            }

            vbuf->unlock();
            next_offset += vertex_data->vertexCount;
        }


        Ogre::IndexData* index_data = submesh->indexData;
        size_t numTris = index_data->indexCount / 3;
        Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;

        bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);

        unsigned long*  pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
        unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong);


        size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset;

        if ( use32bitindexes )
        {
            for ( size_t k = 0; k < numTris*3; ++k)
            {
                indices[index_offset++] = pLong[k] + static_cast<unsigned long>(offset);
            }
        }
        else
        {
            for ( size_t k = 0; k < numTris*3; ++k)
            {
                indices[index_offset++] = static_cast<unsigned long>(pShort[k]) +
                    static_cast<unsigned long>(offset);
            }
        }

        ibuf->unlock();
        current_offset = next_offset;
    }
} 


User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
x 16
Contact:

Re: Raycasting to the polygon level [solved]

Post by KungFooMasta »

Awesome, thanks Rumi! :D Can I post your code on the wiki page?

On purpose?

Code: Select all

   /*
   if (useSoftwareBlendingVertices)
   {
      entity->_updateAnimation();
   }
   */
Creator of QuickGUI!
Rumi
Halfling
Posts: 40
Joined: Mon Jan 08, 2007 9:31 pm
x 1

Re: Raycasting to the polygon level [solved]

Post by Rumi »

Yes, you can post it. ;)

Ah... that commented lines was for performance issues in my app. But i forgot to uncomment them. In my app i always keep updated the animations, so i don't need to update them again, but i don't know if an animation is already updated then that line doesn't update it again? If so, then you can uncomment that line. ;)
zakgof
Gnoblar
Posts: 2
Joined: Wed Jan 27, 2010 10:21 am

Re: Raycasting to the polygon level [solved]

Post by zakgof »

Warning: Morge version fails for 32-bit indices.

Here ulong is used for indices:
ulong* pLong = (ulong*)ibuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);

However ulong is 64bit and you'll get buffer overrun.
Replace ulong by uint and the code will work fine.
Post Reply