RAYCASTING & WORLD QUERYING

27
Jun
2012

RAYCASTING

Ray casting is often used to find out what objects are in a certain part of the world. A ray is just a straight line, and we can use a function provided by Box2D to check if the line crosses a fixture. We can also find out what the normal is at the point the line hits the fixture.

Here is the function I'm talking about, which returns true if the ray hits the fixture. Notice it's a member of the b2Fixture class, which means we'll first need to have one of those to cast a ray against.
var input = new Box2D.Collision.b2RayCastInput;
var output = new Box2D.Collision.b2RayCastOutput;
var result = fixDef.RayCast(output, input); //Will return true or false
There are THREE input parameters: p1, p2 and maxFraction.
var p1 = new b2Vec2(0, 0); //Starting point
var p2 = new b2Vec2(4, 6); //End point
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;



There are TWO output parameters: normal & fraction.
var fraction;
var intersectionNormal = new b2Vec2();
fraction = output.fraction;
intersectionNormal = output.normal;



NOTE: A maxFraction of 1 means the line of sight will be from p1 to p2. A maxFraction of 2 means the line of sight will be in the direction of line joining p1 & p2 and with a length equal to double of the length between p1 & p2.

In the above figure maxFraction was set to 1. So even if the body is in line of sight, Raycast function will return FALSE until and unless maxFraction is set to 2 or higher than 1.7.

Example:
		var world = new b2World(new b2Vec2(0,10), true);
        
        var canvas = $('#canvas');
		var context = canvas.get(0).getContext('2d');
        
		//box		
		var bodyDef = new b2BodyDef;
		bodyDef.type = b2Body.b2_dynamicBody;
		bodyDef.position.Set(9,7);
		bodyDef.userData = 'box';		
		var fixDef = new b2FixtureDef;		
		fixDef.density = 10.0;
		fixDef.friction = 0.5;
		fixDef.restitution = .5;		
		fixDef.shape = new b2PolygonShape;
		fixDef.shape.SetAsBox(1,5);		
		var box1 = world.CreateBody(bodyDef);
		box1.CreateFixture(fixDef);
		
		//circle
		bodyDef.position.Set(4,8);
		bodyDef.userData = 'obj';
		fixDef.restitution = .2; 
		fixDef.shape = new b2CircleShape(1);		  
        var wheel = world.CreateBody(bodyDef);
		wheel.CreateFixture(fixDef);
		
		//create a ground
        bodyDef.type = b2Body.b2_staticBody;
		bodyDef.userData = "ground";
        bodyDef.position.Set(10, 14);		 
		fixDef.shape = new b2PolygonShape;
		fixDef.shape.SetAsBox(10,1);		  
        var ground = world.CreateBody(bodyDef);
        ground.CreateFixture(fixDef);
        
        //at global scope
 		var currentRayAngle = 0;
		var input = new b2RayCastInput();
		var output = new b2RayCastOutput();
		var b = new b2BodyDef();
		var f = new b2FixtureDef();
		var closestFraction = 1;
		var intersectionNormal = new b2Vec2(0,0);
		var intersectionPoint = new b2Vec2();
		rayLength = 25; //long enough to hit the walls
  		var p1 = new b2Vec2( 11, 7 ); //center of scene
		var p2 = new b2Vec2();
		var normalEnd = new b2Vec2();
        
        function ray()	{
			
			var k = 360/20;
			var t = k/60;
			var DEGTORAD = Math.PI/180;
  			currentRayAngle += t * DEGTORAD; //one revolution every 20 seconds
  
            //calculate points of ray
  			p2.x = p1.x + rayLength * Math.sin(currentRayAngle);
			p2.y = p1.y + rayLength * Math.cos(currentRayAngle);
			
			input.p1 = p1;
			input.p2 = p2;
			input.maxFraction = 1;
			closestFraction = 1;
		
			var b = new b2BodyDef();
			var f = new b2FixtureDef();
            
			for(b = world.GetBodyList(); b; b = b.GetNext())	{			
				for(f = b.GetFixtureList(); f; f = f.GetNext())	{
					if(!f.RayCast(output, input))
						continue;
					else if(output.fraction < closestFraction)	{
						closestFraction = output.fraction;
                  					intersectionNormal = output.normal;
					}
				}
			
			}
			intersectionPoint.x = p1.x + closestFraction * (p2.x - p1.x);
			intersectionPoint.y = p1.y + closestFraction * (p2.y - p1.y);
			
			normalEnd.x = intersectionPoint.x + intersectionNormal.x;
			normalEnd.y = intersectionPoint.y + intersectionNormal.y;
			
			context.strokeStyle = "rgb(255, 255, 255)";
			
			context.beginPath(); // Start the path
			context.moveTo(p1.x*30,p1.y*30); // Set the path origin
			context.lineTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path destination
			context.closePath(); // Close the path
			context.stroke();
			
			context.beginPath(); // Start the path
			context.moveTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path origin
			context.lineTo(normalEnd.x*30, normalEnd.y*30); // Set the path destination
			context.closePath(); // Close the path
			context.stroke(); // Outline the path
		}

Now in update() call ray().
			function update() {
                world.Step(1 / 60, 10, 10);
                world.DrawDebugData();
                world.ClearForces();
                ray();
            }

End Result:


(Play with Mouse)

AREA QUERYING

If we need to find out all the FIXTURES within a specficied RECTANGULAR region, we use Area Querying.

The function for this is called QueryAABB and it gets the name because the area must be specified as an 'axis-aligned bounding box'. This just means that the area is rectangular and can't be rotated at an odd angle. It also means that instead of testing whether fixtures are within the area, it tests if the AABBs of fixtures are within the area. Here is an example of the AABBs (Green Boundary surrounding all the fixtures) of some fixtures.


function areaquery()	{
	var aabb = new b2AABB();
	aabb.lowerBound.Set(10, 0); // Top left point of the area
	aabb.upperBound.Set(20, 7); // Bottom Right point of the area
	// Query the world for overlapping shapes.
	world.QueryAABB(getBodyCB, aabb); // getBodyCB is the callback function
}
function getBodyCB(fixture) {
	// Do Something with the fixture
    return true;
}
We need to call areaquery(). We will call areaquery() from the update().
		function update() {			
			world.Step(1 / 60, 10, 10);
			world.DrawDebugData();
			world.ClearForces();

			areaquery();			
		}
EXAMPLE:

By administrator at 12:46:13 AM 0 Comment(s)

Comments

There are no comments yet. Be the first one to add a comment!

Add a Comment

Please enter the email address.Invalid format. (Won't be Displayed)
Notify me of followup comments
Enter the displayed Code: captcha