Read more about this book |

*(For more resources on this subject, see here.)*

# Introduction

In this article, you are going to learn how to check intersection (collision) between 3D objects.

# Detecting collisions between objects in Away3D

This recipe will teach you the fundamentals of collision detection between objects in 3D space. We are going to learn how to perform a few types of intersection tests. These tests can hardly be called collision detection in their physical meaning, as we are not going to deal here with any simulation of collision reaction between two bodies. Instead, the goal of the recipe is to understand the collision tests from a mathematical point of view. Once you are familiar with intersection test techniques,the road to creating of physical collision simulations is much shorter. There are many types of intersection tests in mathematics. These include some simple tests such as **AABB** (**axially aligned bounding box**), Sphere - Sphere, or more complex such as Triangle - Triangle, Ray - Plane, Line - Plane, and more. Here, we will cover only those which we can achieve using built-in Away3D functionality. These are AABB and AABS (axially aligned bounding sphere) intersections, as well as Ray-AABS and the more complex Ray- Triangle. The rest of the methods are outside of the scope of this article and you can learn about applying them from various 3D math resources.

## Getting ready

Setup an Away3D scene in a new file extending *AwayTemplate*. Give the class a name *CollisionDemo*.

## How to do it...

In the following example, we perform an intersection test between two spheres based on their bounding boxes volumes. You can move one of the spheres along X and Y with arrow keys onto the second sphere. On the objects overlapping, the intersected (static) sphere glows with a red color.

### AABB test:

CollisionDemo.as

package

{

public class CollisionDemo extends AwayTemplate

{

private var _objA:Sphere;

private var _objB:Sphere;

private var _matA:ColorMaterial;

private var _matB:ColorMaterial;

private var _gFilter:GlowFilter=new GlowFilter();

public function CollisionDemo()

{

super();

_cam.z=-500;

}

override protected function initMaterials() : void{

_matA=new ColorMaterial(0xFF1255);

_matB=new ColorMaterial(0x00FF11);

}

override protected function initGeometry() : void{

_objA=new Sphere({radius:30,material:_matA});

_objB=new Sphere({radius:30,material:_matB});

_view.scene.addChild(_objA);

_view.scene.addChild(_objB);

_objB.ownCanvas=true;

_objA.debugbb=true;

_objB.debugbb=true;

_objA.transform.position=new Vector3D(-80,0,400);

_objB.transform.position=new Vector3D(80,0,400);

}

override protected function initListeners() : void{

super.initListeners();

stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);

}

override protected function onEnterFrame(e:Event) : void{

super.onEnterFrame(e);

if(AABBTest()){

_objB.filters=[_gFilter];

}else{

_objB.filters=[];

}

}

private function AABBTest():Boolean{

if(_objA.parentMinX>_objB.parentMaxX||_objB.parentMinX>_objA.

parentMaxX){

return false;

}

if(_objA.parentMinY>_objB.parentMaxY||_objB.parentMinY>_objA.

parentMaxY){

return false;

}

if(_objA.parentMinZ>_objB.parentMaxZ||_objB.parentMinZ>_objA.

parentMaxZ){

return false;

}

return true;

}

private function onKeyDown(e:KeyboardEvent):void{

switch(e.keyCode){

case 38:_objA.moveUp(5); break;

case 40:_objA.moveDown(5); break;

case 37:_objA.moveLeft(5); break;

case 39:_objA.moveRight(5); break;

case 65:_objA.rotationZ-=3; break;

case 83:_objA.rotationZ+=3; break;

default:

}

}

}

}

In this screenshot, the green sphere bounding box has a red glow while it is being intersected by the red sphere's bounding box:

## How it works...

Testing intersections between two AABBs is really simple. First, we need to acquire the boundaries of the object for each axis. The box boundaries for each axis of any Object3D are defined by a minimum value for that axis and maximum value. So let's look at the *AABBTest()* method. Axis boundaries are defined by *parentMin* and *parentMax* for each axis, which are accessible for each object extending Object3D.

*You can see that Object3D also has minX,minY,minZ and maxX,maxY,maxZ. These properties define the bounding box boundaries too, but in objects space and therefore aren't helpful in AABB tests between two objects.*

So in order for a given bounding box to intersect a bounding box of other objects, three conditions have to be met for each of them:

- Minimal X coordinate for each of the objects should be less than maximum X of another.
- Minimal Y coordinate for each of the objects should be less than maximum Y of another.
- Minimal Z coordinate for each of the objects should be less than maximum Z of another.

If one of the conditions is not met for any of the two AABBs, there is no intersection. The preceding algorithm is expressed in the *AABBTest()* function:

private function AABBTest():Boolean{

if(_objA.parentMinX>_objB.parentMaxX||_objB.parentMinX>_objA.

parentMaxX){

return false;

}

if(_objA.parentMinY>_objB.parentMaxY||_objB.parentMinY>_objA.

parentMaxY){

return false;

}

if(_objA.parentMinZ>_objB.parentMaxZ||_objB.parentMinZ>_objA.

parentMaxZ){

return false;

}

return true;

}

As you can see, if all of the conditions we listed previously are met, the execution will skip all the return false blocks and the function will return true, which means the intersection has occurred.

## There's more...

Now let's take a look at the rest of the methods for collision detection, which are AABS-AABS, Ray-AABS, and Ray-Triangle.

### AABS test

The intersection test between two bounding spheres is even simpler to perform than AABBs. The algorithm works as follows.

If the distance between the centers of two spheres is less than the sum of their radius, then the objects intersect. Piece of cake! Isn't it? Let's implement it within the code.

*The AABS collision algorithm gives us the best performance. While there are many other even more sophisticated approaches, try to use this test if you are not after extreme precision. (Most of the casual games can live with this approximation).*

First, let's switch the debugging mode of *_objA* and *_objB* to bounding spheres. In the last application we built, go to the *initGeometry()* function and change:

_objA.debugbb=true;

_objB.debugbb=true;

To:

_objA.debugbs=true;

_objB.debugbs=true;

Next, we add the function to the class which implements the algorithm we described previously:

private function AABSTest():Boolean{

var dist:Number=Vector3D.distance(_objA.position,_objB.

position);

if(dist<=(_objA.radius+_objB.radius)){

return true;

}

return false;

}

Finally, we add the call to the method inside *onEnterFrame()*:

if(AABSTest()){

_objB.filters=[_gFilter];

}else{

_objB.filters=[];

}

Each time *AABSTest* returns true, the intersected sphere is highlighted with a red glow:

Read more about this book |

*(For more resources on Away3D, see here.)*

### Ray – AABS intersection test

Away3D contains a Ray utility which is used in 3D math to test for intersection. In most general terms, a Ray is a line which has an origin, direction vectors, and length. Ray is used rapidly in game development when there is a need to detect the collision of firing weapons hitting their targets, driving simulators, convert screen points into 3D world coordinates, and much more.

Ray intersection calculations are a relatively complex calculation for one who is not deep into 3D math. Away3D *Ray* class supplies us with a couple of predefined test methods which save us most of the math intensive calculations and which you are going to learn how to use. In this part, we are going to use the Ray utility to check an intersection with the bounding sphere of a 3D object using the Ray. *intersectBoundingRadius()* method.

So we will continue with the class we have built in the examples. First put the following global variables above the class constructor:

private var _lineSeg:LineSegment;

private var _wireMat:WireColorMaterial;

private var _startVertex:Vertex=new Vertex(0,0,0);

private var _endtVertex:Vertex=new Vertex(0,0,0);

*LineSegment* will serve us as a debug-tracer for the ray. As the ray is actually an invisible line, it would be more convenient for us to get its graphical representations. *_startVertex* and *_endVertex* stand for start and end point of the segment line which will receive origin and direction values from the ray as you will see shortly. Now insert the following function into the class:

private function RayAABSTest():Boolean{

var ray:Ray=new Ray();

ray.orig=_objA.position;

var d:Vector3D=new Vector3D(0,200,0);

var dir:Vector3D=_objA.transform.transformVector(d);

ray.dir=dir;

_startVertex.x=ray.orig.x;

_startVertex.y=ray.orig.y

_startVertex.z=ray.orig.z

_lineSeg.start=_startVertex;

_endtVertex.x=ray.dir.x;

_endtVertex.y=ray.dir.y;

_endtVertex.z=ray.dir.z;

_lineSeg.end=_endtVertex;

_view.scene.addChild(_lineSeg);

_lineSeg.material=_wireMat;

ray.orig.normalize();

ray.dir.normalize();

return ray.intersectBoundingRadius( _objB.position,_objB.

boundingRadius);

}

Inside *onEnterFrame* method change existing *If .. else...* condition to be like this:

if(RayAABSTest()){

_objB.filters=[_gFilter];

}else{

_objB.filters=[];

}

*There is one important thing you should know regarding this test in Away3D. The method ray.intersectBoundingRadius() returns an intersection only if the ray direction (end point) vector is inside the bounding sphere of the object. That is, if the ray is intersecting the sphere, but its direction vector endpoint doesn't lay inside the bounding radius, the intersection test returns false.*

Let's see in detail what is going on inside *RayAABSTest()*. We have to define the origin for the ray. We set it to be the position of the *_objA* sphere, which we are able to move around the scene:

ray.orig=_objA.position;

Now we have to define the ray direction which is also a vector. We wish that the direction vector of the ray will face the same direction as the *_objA* sphere local y-axis (upwards). These lines help us to achieve these tasks by transforming the direction vector we defined into the *_objA* space:

var d:Vector3D=new Vector3D(0,200,0);

var dir:Vector3D=_objA.transform.transformVector(d);

ray.dir=dir;

Next, we draw a segment line from the ray origin to its direction end vector to get a visual trace of it:

_startVertex.x=ray.orig.x;

_startVertex.y=ray.orig.y

_startVertex.z=ray.orig.z

_lineSeg.start=_startVertex;

_endtVertex.x=ray.dir.x;

_endtVertex.y=ray.dir.y;

_endtVertex.z=ray.dir.z;

_lineSeg.end=_endtVertex;

Then we must normalize the ray's origin and direction vectors, otherwise the equation which performs the intersection calculation will return a huge positive value which can never reach a negative number and therefore you will always receive intersection indication, even if there is actually none. Last we call *ray.intersectBoundingRadius()*; to which we pass the target object position and its bounding radius. It's return type is Boolean, so that if the ray hits the volume of the bounding sphere, it returns *true*.

As is seen from the following screenshot, the ray cast from the red sphere is intersecting the textured sphere's bounding volume, causing it to glow with a red outline:

### Ray Triangle test

The following intersection is going to be fun and actually it has quite a lot of practical use. We are going to test ray intersection with mesh triangles. Working on a shooter game, this is one of the handiest features you are desperately going to need in order to trace mesh hit points to apply bullet decay textures, or may be add a damage to a mesh by removing triangles from its surface which received ray hit. In the following demo, I am going to show you how to do the second thing. We are going to strip the sphere primitive from its triangles by eliminating them when they receive a ray hit. Let's have some fun!

Copy the following method inside the class we have used previously:

private function RayFaceTest():Vector3D{

var intersectVector:Vector3D;

var ray:Ray=new Ray();

for(var i:int=0;i<_objB.faces.length;i++){

var p0:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[0].position);

var p1:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[1].position);

var p2:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[2].position);

ray.orig=_objA.position;

var dd:Vector3D=new Vector3D(0,400,0);

var dird:Vector3D=_objA.transform.transformVector(dd);

ray.dir=dird;

_startVertex.x=ray.orig.x;

_startVertex.y=ray.orig.y

_startVertex.z=ray.orig.z

_lineSeg.start=_startVertex;

_endtVertex.x=ray.dir.x;

_endtVertex.y=ray.dir.y;

_endtVertex.z=ray.dir.z;

_lineSeg.end=_endtVertex;

intersectVector=ray.getIntersect(ray.orig,ray.dir,p0,p1,

p2);

if(intersectVector){

trace(intersectVector);

var fc:Face=_objB.faces[i];

_objB.removeFace(fc);

break;

}

}

return intersectVector;

}

Inside *onEnterFrame()*, comment out the *if...else* condition block and add a single call to the function we have just inserted. Add the following line to rotate the target sphere:

_objB.rotate(Vector3D.X_AXIS,5);

Run the application. Move the ray onto the region of the static sphere. You should see that the triangles which are intersecting with the ray are being deleted. Move the ray so that it hits the target from different angles, and in a few moments, you have just the bounding sphere left. All the triangles are gone!

Now it is time to dig into the code behind this demo. Let's see how it works inside *RayFaceTest()*. In order to find the intersection point coordinates of the ray on a particular triangle, you have to pass, besides the ray origin and direction, three vector3D values which represent three vertices of each triangle in a mesh into the *ray.getIntersect()* method. The common mistake many inexperienced developers make in this step is to forget converting vertex coordinates from the object's local space to the world (scene) space. This step is critical as the ray is cast in the scene space and not in the objects. The following block transforms the vertices from object's local to scene space:

for(var i:int=0;i<_objB.faces.length;i++){

var p0:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[0].position);

var p1:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[1].position);

var p2:Vector3D=_objB.sceneTransform.transformVector(_objB.

faces[i].vertices[2].position);

Notice that this block, as well as the rest of the function, is wrapped with a *for* loop. We need to iterate through each triangle of the mesh till we detect the ray intersected triangle.

Now when we have the vertices coordinates transformed, we can start to define a ray's origin and direction vectors. This step is similar to the previous example on the Ray-AABS test:

ray.orig=_objA.position;

var dd:Vector3D=new Vector3D(0,400,0);

var dird:Vector3D=_objA.transform.transformVector(dd);

ray.dir=dird;

_startVertex.x=ray.orig.x;

_startVertex.y=ray.orig.y

_startVertex.z=ray.orig.z

_lineSeg.start=_startVertex;

_endtVertex.x=ray.dir.x;

_endtVertex.y=ray.dir.y;

_endtVertex.z=ray.dir.z;

_lineSeg.end=_endtVertex;

Next we cast the ray with the origin on the *_objA* sphere and direction defined towards the sphere's local positive Y (UP). The three other arguments are three vertex position vectors of the currently tested triangle:

intersectVector=ray.getIntersect(ray.orig,ray.dir,p0,p1,p2);

If the ray hits the triangle, *intersectVector* returns the intersection world coordinates, otherwise it returns null. This way, we can detect which triangle got hit and further isolate it for our needs. The next block does just that:

if(intersectVector){

var fc:Face=_objB.faces[i];

_objB.removeFace(fc);

break;

}

The preceding condition block works like this.

If the currently tested triangle is hit, then *intersectVector* inside *if()* becomes true. In such a case, we get the reference to that very triangle and remove it from the mesh.

Afterwards, we exit the wrapping *for* loop immediately, because inside the current iteration, there is no reason to continue the test as the intersecting triangle has already been detected and we want to pass the return value for the function which is the intersection Vector3D.

Then we start over the loop routine and iterate through the array of remaining faces again.

*If you want to boost the frame rate of the previous examples, comment out these lines inside the initGeometry() method:*

*
*

// _objA.debugbs=true;

//_objB.debugbs=true;

*Redrawing bounding spheres are memory intensive.*

# Summary

In this article we saw how to detect collisions between objects in Away3D.

**Further resources on this subject:**

- Tips and Tricks on Away3D 3.6 [article]
- Importing 3D Formats into Away3D [article]
- Models and Animations with Away3D 3.6 [article]
- Creating and Warping 3D Text with Away3D 3.6 [article]