Game Development Community

Cone container search

by Lukas Joergensen · in Torque 3D Professional · 01/25/2013 (10:54 am) · 13 replies

I was looking at writing a cone container search and got kindda stuck, I have only found like 3 other threads about this subject and none of them were to much help ^^

This is what I got so far, which should create a boxSearch between two point with given radius, therefore the box around the cone. Any ideas on how to test if any of the objects inside that box is inside a cone?

void SceneContainer::initConeSearch(
	const Point3F Start, 
	const Point3F End, 
	const F32 Radius, 
	const U32 TypeMask)
{
	cleanupSearchVectors();

	Box3F queryBox;
	Point3F center = ( End - Start ) / 2;
	Point3F perp1 = mPerp(End - Start);
	Point3F perp2 = IPSCore::perpendicularVector(perp1, End - Start);
	perp1.normalize();
	perp2.normalize();
	VectorF perp = perp1 + perp2;
	perp *= Radius;
	AssertFatal( mIsZero( mDot( perp1, perp2 ) ), "ConeSearch, failed to generate perpendicular vector perp1 , perp2" );
	AssertFatal( mIsZero( mDot( center, perp2 ) ), "ConeSearch, failed to generate perpendicular vector center , perp2" );
	AssertFatal( mIsZero( mDot( perp1, center ) ), "ConeSearch, failed to generate perpendicular vector perp1, center" );
	queryBox.minExtents -= Point3F((Start - center).x - perp.x, (Start - center).y - perp.y, (Start - center).z -perp.z);
	queryBox.maxExtents += Point3F((End - center).x + perp.x, (End - center).y + perp.y, (End - center).z + perp.z);

	SimpleQueryList queryList;
	gServerContainer.findObjects(queryBox, TypeMask, SimpleQueryList::insertionCallback, &queryList);

	const F32* pPoint = &center.x;
	for (U32 i = 0; i < queryList.mList.size(); i++)
	{
		// Find objects inside the cone
	}
	if (mSearchList.size() != 0)
	{
		sgSortReferencePoint = mSearchReferencePoint;
		dQsort(mSearchList.address(), mSearchList.size(),
			sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
	}
}

#1
01/25/2013 (11:06 am)
Also, the list returned is a SceneObject list. Is it possible to get the geometry based on the SceneObject? (Preferably without Dynamic Casting it to this and that) or would you recommend to do it on the world box?
#2
01/25/2013 (11:17 am)
Once you have your list of objects contained within the box search, it should be possible to check if each one is within a certain field of view (the angle of which would be defined by your cone). The GuiShapeNameHud performs a cone/fov check (starting at the player's camera position and extending outward) to determine if it should render the name of an object.
#3
01/25/2013 (11:49 am)
My biggest issue with that is getting the closest point on the world box to check with.. Guess I have to do it the hard way and check each plane in the box individually.
#4
01/25/2013 (12:30 pm)
Did you look for the frustrum culling code? You could probably leverage that.
#5
01/25/2013 (12:37 pm)
The frustum culling code is using a PolyhedronImpl (no idea what the impl is for lol) to test the intersection:
OverlapTestResult testPotentialIntersection( const Box3F& aabb ) const
      {
         return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).testPotentialIntersection( aabb );
      }
Perhaps I can figure something out based on that, but this would mean that I would have to define the geometry for the cone, will see what I can figure out.
#6
01/25/2013 (1:00 pm)
Okay this is what I came up with:
void SceneContainer::initConeSearch(
	const Point3F Start, 
	const Point3F End, 
	const F32 Radius, 
	const U32 TypeMask)
{
	cleanupSearchVectors();

	Point3F center = ( End - Start ) / 2;
	mSearchReferencePoint = center;

	Box3F queryBox;
	Point3F perp1 = mPerp(End - Start);
	Point3F perp2 = IPSCore::perpendicularVector(perp1, End - Start);
	perp1.normalize();
	perp2.normalize();
	perp1 *= Radius;
	perp2 *= Radius;
	VectorF perp = perp1 + perp2;

	AssertFatal( mIsZero( mDot( perp1, perp2 ) ), "ConeSearch, failed to generate perpendicular vector perp1 , perp2" );
	AssertFatal( mIsZero( mDot( center, perp2 ) ), "ConeSearch, failed to generate perpendicular vector center , perp2" );
	AssertFatal( mIsZero( mDot( perp1, center ) ), "ConeSearch, failed to generate perpendicular vector perp1, center" );
	queryBox.minExtents -= Point3F((Start - center).x - perp.x, (Start - center).y - perp.y, (Start - center).z -perp.z);
	queryBox.maxExtents += Point3F((End - center).x + perp.x, (End - center).y + perp.y, (End - center).z + perp.z);

	SimpleQueryList queryList;
	gServerContainer.findObjects(queryBox, TypeMask, SimpleQueryList::insertionCallback, &queryList);

	for (U32 i = 0; i < queryList.mList.size(); i++)
	{
		Vector<PlaneF> planes;
		U8 resolution = 8;
		for(int i = 0; i < resolution; i++)
		{
			Point3F p1,p2;
			p1 = End+((perp1 * cos((360/resolution*i)*M_PI/180)) + (perp2 * cos((360/resolution*i)*M_PI/180)));
			p2 = End+((perp1 * cos((360/resolution*(i+1))*M_PI/180)) + (perp2 * cos((360/resolution*(i+1))*M_PI/180)));
			PlaneF plane(Start, p1, p2);
			planes.push_back(plane);
		}
		
		PlaneSetF planeSet(planes.address(),6);
		if(planeSet.testPotentialIntersection(queryList.mList[0]->getWorldBox()))
		{
			mSearchList.push_back(new SimObjectPtr<SceneObject>);
			*(mSearchList.last()) = queryList.mList[i];
		}
	}
	if (mSearchList.size() != 0)
	{
		sgSortReferencePoint = mSearchReferencePoint;
		dQsort(mSearchList.address(), mSearchList.size(),
			sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
	}
}
Does it make sense to you?
What I do is construct 8 planes for the cone, each plane is based on 3 points, the start of the cone and two points which is the end of the cone with the 2 perpendicular vectors multiplied by a cosinus/sinus function added to it.
(Have no idea how to test it)
#7
01/25/2013 (4:29 pm)
<shrug> looks like it should work.... have to wind it up and watch it go....
#8
01/27/2013 (6:49 pm)
I found this. If this kind of what you were looking for?

//Uses the Direction of the bipCOM 
bool RPGPlayer::checkAttackingFOV(Point3F targetPOS,Point3F sourcePOS, F32 FOV)
{
	//by Chris Haigler &Acirc;&middot; 04/24/2011 (9:26 pm)	 &Acirc;&middot; 4 comments
	//Modified by KEvin Mitchell
	//Comments un modified by orginal poster 

	// So we need to move to the destination. Let's check to see if we're (roughly) facing the destination.  
    // If not, we'll skip the movement stage to give ourselves a chance to turn toward the target.   
    // This prevents AIPlayers from moving toward a target in reverse and turning at the same time (which looks wrong).  
    // Note: This is a simple (and fast) 'field of view' check, a ray cast could potentially be more accurate.  
    Point3F targetVec;  
	Point3F forwardVector;
	forwardVector.set(AttackingX,AttackingY,0);
    targetVec = targetPOS - sourcePOS;  
    targetVec.normalize(); // need to normalize the vector so it's a... normal vector.  
  
    F32 dot = mDot(targetVec, forwardVector);//getTransform().getForwardVector());  
  
    F32 angle = mDegToRad(FOV);  
    angle = mCos(angle);  
  
    // we now have the view cone going from 90 degrees left of center and 90 degress right of center  
    // if our target destination is within this viewport move to it, else ignore movement for now  
    if(dot < angle) //target not in view  
    {  
        return false;  
    }else{
		return true;
	}
}

bool RPGPlayer::checkCameraFOV(Point3F targetPOS,Point3F sourcePOS, F32 FOV)
{
	//by Chris Haigler &Acirc;&middot; 04/24/2011 (9:26 pm)	 &Acirc;&middot; 4 comments
	//Modified by KEvin Mitchell
	//Comments un modified by orginal poster 

	// So we need to move to the destination. Let's check to see if we're (roughly) facing the destination.  
    // If not, we'll skip the movement stage to give ourselves a chance to turn toward the target.   
    // This prevents AIPlayers from moving toward a target in reverse and turning at the same time (which looks wrong).  
    // Note: This is a simple (and fast) 'field of view' check, a ray cast could potentially be more accurate.  
    Point3F targetVec;  
    targetVec = targetPOS - sourcePOS;  
    targetVec.normalize(); // need to normalize the vector so it's a... normal vector.  
  
    F32 dot = mDot(targetVec, getTransform().getForwardVector());  
  
    F32 angle = mDegToRad(FOV);  
    angle = mCos(angle);  
  
    // we now have the view cone going from 90 degrees left of center and 90 degress right of center  
    // if our target destination is within this viewport move to it, else ignore movement for now  
    if(dot < angle) //target not in view  
    {  
        return false;  
    }else{
		return true;
	}
}
#9
01/27/2013 (6:51 pm)
Mind you I'm using this in junction with the default container code. Does not seem to take too long to execute:

%playerObject = containerFindFirst( $Player::Obstacles, %this.getTransform() ,%this.dataBlock.AttackDistance,%this.dataBlock.AttackDistance,%this.dataBlock.AttackDistance);
               while(%playerObject){
                  %name = %playerObject.GlossaryName;      
                  switch$(%name)
                  {
                     case "PlayableCharacter" :
                        if(%this.GlossaryName$="Mob"){
                           if(%this.checkCameraFOV(%playerObject.getTransform(),%this.getTransform(),90)){
                              %this.applyMeleeDammage(%this,%playerObject);
                           }
                        }
                        if(%this.GlossaryName$="MISSION_MOB"){
                           if(%this.checkCameraFOV(%playerObject.getTransform(),%this.getTransform(),90)){
                              %this.applyMeleeDammage(%this,%playerObject);
                           }
                        }
                     case "MISSION_MOB" :
                        if(%this.GlossaryName$="PlayableCharacter"){
                           %wasHit=1;
                           if(%this.checkCameraFOV(%playerObject.getTransform(),%this.getTransform(),90)){
                              %this.applyMeleeDammageAIPT(%this,%playerObject);
                           }
                        }
                     case "Mob" :
                        if(%this.GlossaryName$="PlayableCharacter"){
                           %wasHit=1;
                           if(%this.checkCameraFOV(%playerObject.getTransform(),%this.getTransform(),90)){
                              %this.applyMeleeDammageAIPT(%this,%playerObject);
                           }
                        }
                     default :
                  }
                  %playerObject =  containerFindNext();
               }
#10
01/28/2013 (2:57 am)
@Kevin I have yet to test whether the source I came up with would work (I know, should probably have set some time off to test it before that lol).
But seems like your method would work aswell, atleast in a 2d plane (seems to only check the horizontal dimension and not the vertical of the cone) which tbh would work in almost all cases aswell.
I will test the code I came up with and say if it works, I believe it would be slightly more efficient.
However a combination of them would probably be the most efficient, will see if I can make a version with the FOV check and the box search :)
#11
01/28/2013 (4:07 am)
This actually is a 3D sphere scan with a 90 degree slice in it. But I can detect enemies in a 3D area. Unless I'm misunderstanding.

Are you trying to get a square 3D cone container?

The green is what I'm doing are you trying to get the other effect?


To test you just need to create 3D Sphere from a primitive object. I did a 90x90x90 area and created a primitive every 10 meters. Your floor must be transparent so you can walk into the middle of the Test area. If you schedule a 3d container scan you can make the detected objects visible.

if you walk dead center of the space you can look around and see if it is working. Using CTRL+C you can fly around and see the container shape you are looking for.

The test area will look like this: (But not as many nodes :) )

i26.photobucket.com/albums/c118/shoiko/3DCube_zpsb42f7a69.png
#12
01/28/2013 (5:18 am)
Ah but I'm trying to do a cone:
www.webdesignhelper.co.uk/photoshop_tutorials/photoshop_tutorials/photoshop_tutorial33/cone.jpg

What I'm trying to do is do a box search and then check if the objects found by that box search is inside the cone.
It's pretty much like you are doing however instead of doing a search on everything inside a radius of a point, I just find everything within a box and filter the found objects from there :)
#13
01/29/2013 (12:56 am)
I think the difference in your approaches is that Mich's code only considers the objects' centre points, not their bounding boxes.

Mich: the cameraFOV code you have above actually does a cone. The attackFOV code does a true cone as well, though one that always points horizontally.

Lukas: checking each vertex of the object's world box could work. Just be sure to test for cases where the object box is bigger than the search cone, so none of the vertices are actually inside it. If you want to go the full-on collision route I could suggest some ideas... I'm not sure how it's supposed to be done but I think I could suggest a way that works.

EDIT: Not sure if you figured it out, but SceneObject::buildPolyList lets you grab polygon geometry from objects. It's used fairly frequently to do just that.