Away3D: Detecting Collisions

Exclusive offer: get 50% off this eBook here
Away3D 3.6 Cookbook

Away3D 3.6 Cookbook — Save 50%

Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine

$32.99    $16.50
by Michael Ivanov | June 2011 | Open Source Web Graphics & Video

Three dimensions are better than two — and it's not a secret anymore that 3D is here to stay. Gone are the days when Flash was just used for 2D animations. In the last few years, online Flash content has undergone a revolution with the introduction of real-time 3D engines for Flash. Away3D is the big daddy of them all—which makes it the ultimate resource for top-rated 3D content development and for powering today's coolest games and Flash sites.

In this article by Michael Ivanov, author of Away3D 3.6 Cookbook, we will see how to detect collisions between objects in Away3D.

 

Away3D 3.6 Cookbook

Away3D 3.6 Cookbook

Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine

        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:

Away3D: Detecting Collisions

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:

Away3D: Detecting Collisions

Away3D 3.6 Cookbook Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine
Published: May 2011
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:
        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:

Away3D: Detecting Collisions

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!

Away3D: Detecting Collisions

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:


Away3D 3.6 Cookbook Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine
Published: May 2011
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:

About the Author :


Michael Ivanov

Although initially dreaming of becoming a historian, in the middle of his undergraduate studies in the Hebrew University of Jerusalem Michael already understood that his real passion is computers. Today he has been a professional Flash developer working in the field of web and game development for more than 5 years .Being a researcher by nature with a strong hunger for technological exploration he constantly broadens his professional knowledge by exploring a wide range of technologies from different fields of computer science where real-time 3D engines are of primary interest. For the past two years Michael has been working for Neurotech Solutions Ltd, which is a rapidly growing Israeli startup bringing revolutionizing solutions in the field of research and treatment of ADHD, as lead programmer. Michael led the development of unique ADHD training game programs, which are based on the Adobe Flash platform powered by open source 3D libraries such as PV3D and Away3D. Although in everyday life he works mostly on RIA and desktop application development, his true passion is 3D graphics and game programming. In his little spare time Michael works with such technologies as Java3D Epic's UDK, Unity3D, and wide range of Flash open source 3D libraries from which Away3D is his favorite .Michael is a zealous promoter of cutting-edge 3D technologies, especially those that are open source, and misses no opportunity to speak on these subjects at local game industry events and conferences. Born in Russia, today Michael has lived and worked in Israel for more than 12 years. When he is not writing a code he enjoys playing around with his three-year old son David as well as reading history book and running.

You can find him in the Web on his personal blog http://blog.alladvanced.net, or as an active member of the Away3D developers community group at: http://groups.google.com/group/away3d-dev?pli=1

Books From Packt


Away3D 3.6 Essentialsx
Away3D 3.6 Essentials

Inkscape 0.48 Illustrator's Cookbook
Inkscape 0.48 Illustrator's Cookbook

OpenSceneGraph 3.0: Beginner's Guide
OpenSceneGraph 3.0: Beginner's Guide

Python 2.6 Graphics Cookbook
Python 2.6 Graphics Cookbook

Inkscape 0.48 Essentials for Web Designers
Inkscape 0.48 Essentials for Web Designers

OGRE 3D 1.7 Beginner's Guide
OGRE 3D 1.7 Beginner's Guide

Blender 2.5 HOTSHOT
Blender 2.5 HOTSHOT

Blender 2.5 Materials and Textures Cookbook
Blender 2.5 Materials and Textures Cookbook


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software