Contents:
Introduction
Every 3D engine needs to solve a very basic problem: what's under the mouse? In 3D graphics, this is commonly referred to as picking. Although it may sound as a simple and straight forward problem, it actually involves non-trivial mathematics, very efficient algorithms, and is definitely much more complex than its 2D counterpart. Detecting what object is beneath the cursor in a 3D scene can be in fact a programatically expensive task. But don't panic! Away3D offers a series of carefully crafted features in order to deal with such a problem, taking into account the Flash Player's capabilities and limitations, and giving the user plenty of flexibility to find the best balance between precision and performance for each particular implementation. In this article, we will explore the engine's picking feature set, get to know how easy it is to use them, and understand how to master the subtleties in order to achieve an efficient implementation in advanced and demanding projects.Hello Picking
So lets get right down to business. Have a look at the following example, and it's code, below:
anObject.mouseEnabled = true; anObject.addEventListener( MouseEvent3D.MOUSE_OVER, onObjectMouseOver ); private function onObjectMouseOver( event:MouseEvent3D ):void { // do stuff... }And its as simple as that. Of course, you can use all the other mouse event types, just like in 2D. The idea is that the interface is what you know and are already familiar with. Nothing new here. But it is 3D picking though, and things can get more complicated, as stated before, so lets have a look under the hood and see what's going on in the 3D engine.
Entity Properties
Picking in Away3D can be managed by a set of properties within 3D entities. Lets have a look at them.MouseEnabled
So, how is the engine doing the picking? Basically, the engine is casting a ray from the camera, through the mouse position on the screen, and straight into the scene. This ray could potentially hit several objects within the scene. One way to determine which objects are hit is using raw ray collision mathematics. This involves calculating ray-sphere collisions, ray-aabb collisions, ray-triangle collisions, etc. The most expensive part of this process is finding out if the ray hits the inside of a triangle in a mesh, which could potentially contain a very high poly count. Now, ray tracing is not the only method used for picking. Away3D in fact has 2 different methods and uses this one by default for reasons we will discuss further along in this tutorial. Figure 1 below shows the basics of the ray tracing approach.

mouseEnabled
property to true. This is pretty much the way it works on the regular Flash 2D display API, but in that case objects are mouse enabled by default because picking is so much cheaper in two dimensions.
Picking Colliders
You may have noticed on the previous listings that ( after rotating the camera to very specific angles ) picking around the sphere is not very accurate. In listing 1 or 2, if you hover the mouse close enough to the sphere, it will trigger a mouse over, even though the mouse is not actually over the object. Hmmm... not good.
BOUNDS_ONLY
by default.
BOUNDS_ONLY
by default. If you have a look at figure 1 again, what's happening is that the ray is being "stopped" at the mesh's bounding volume surface and collision calculations with its potentially hundreds or thousands or millions of triangles is avoided by default. If you do need more precision, you just need to tell the ray to keep going. You change this using the following property:
anObject.pickingCollider = PickingColliderType.BOUNDS_ONLY; // default //anObject.pickingCollider = PickingColliderType.AS3_FIRST_ENCOUNTERED; //anObject.pickingCollider = PickingColliderType.AS3_BEST_HIT; // etc...BOUNDS_ONLY is the cheapest picking collider that the engine offers and is accurate enough for more cases. If you want more precision, you just chose a different one:
-
PickingColliderType.BOUNDS_ONLY ( default value )
Calculates ray vs bounding volume collisions. -
PickingColliderType.AS3_FIRST_ENCOUNTERED
Calculates ray vs all mesh triangles collision. This costs considerably more than BOUNDS_ONLY (1) and is heavier as the number of triangles in the mesh increases. The collider stops all tests as soon as a collision with a triangle is found, despite the fact that the hit surface may not be the mesh's closest surface along the ray to the camera. -
PickingColliderType.AS3_BEST_HIT
As the previous collider, but more detailed, evaluating which of the triangles that collide with the ray is closest to the ray's origin. This is necessary if you want to accurately know the collision point on a mesh that may have more than one possible collision surface with the ray, such as a mesh representing a coffee cup, for example. AS3_FIRST_ENCOUNTERED (2) could incorrectly resolve a collision with the inner walls of the cup whereas this collider will not stop tests on the first hit, but will instead find all collisions and evaluate the best one overall. -
PickingColliderType.PB_FIRST_ENCOUNTERED
This is the same as AS3_FIRST_ENCOUNTERED (2) but uses pixel bender instead of pure Actionscript for ray collision calculations. This method proves to be faster for higher-poly meshes and slower for lower-poly meshes. Pixel bender takes advantage of multi-threading if it can, meaning that such a collider will tend to be faster on desktops, but may not be on simpler CPU's like those found on mobile devices. Furthermore, AIR for iOS simply does not support pixel bender at this time, which causes this collider to fail silently. -
PickingColliderType.PB_BEST_HIT
Pixel bender equivalent to AS3_BEST_HIT (3). -
PickingColliderType.AUTO_FIRST_ENCOUNTERED
As AS3_FIRST_ENCOUNTERED (2), but automatically decides wether to use the actionscript or pixel bender version of the collider depending on the poly count of the mesh. -
PickingColliderType.AUTO_BEST_HIT
Same as AUTO_FIRST_ENCOUNTERED (6) but with the behaviour of AS3_BEST_HIT (3).
sphere.pickingCollider = PickingColliderType.AS3_FIRST_ENCOUNTERED;Or, we could simply change the sphere's bounding volume to a BoundingSphere instead of an AxisAlignedBoundingBox (Away3D sets all bounding volumes to AxisAlignedBoundingBox by default). This would allow us to get the precision we want without the need to enter the volume and perform costly ray triangle collision calculations. Of course, this is a very specific, almost theoretical case, but its important to keep in mind that the core of this picking technique is bounding volumes, and different bounding volume shapes can be very helpful. In code, this is how you change a bounding volume:
sphere.bounds = new BoundingSphere();Either way we choose to resolve our precision problem, the result can be appreciated in listing 3:

View Properties
As there are per object properties, picking in Away3D also involves global properties. Lets study these now.Shader vs. Raycast Mouse Pickers
As mentioned before, Away3D offers a completely different approach for picking calculations. So far we have been dealing with the properties of individual objects, you can change the way picking works on a global scale too.view.mousePicker = PickingType.SHADER;You can choose from the following set of values:
-
PickingType.RAYCAST_FIRST_ENCOUNTERED ( default value )
Uses ray tracing as a picking method, stopping tests at the first successful renderable level collision. -
PickingType.RAYCAST_BEST_HIT
Uses ray tracing as a picking method, evaluating the best ( closest ) hit between all the colliding renderables. -
PickingType.SHADER
Uses a shader technique as a picking method, always evaluating the best hit.

anObject.shaderPickingDetails = true;This property simply asks the shader picker to evaluate not just whether the mouse events exist, but also the data involved in them such as position, normals, etc. The calculation of such information is not free as it is in the raycast approach, so it should only be requested when needed. If set to false, the mouse events will trigger, but some of the event properties will be null or invalid. The value is set to false by default, and in the case of listing 4, it needs to be enabled so that we can retrieve the location of the mouse event in the scene when using the shader picker. Hopefully, Adobe will someday remove the drawToBitmapData() bottleneck and make all our lives easier!
The Raycast Mouse Pickers
On the previous section, we discussed the difference between the raycast pickers and the shader picker. Its time to talk about the difference between the two available raycast mouse pickers, (1) RAYCAST_FIRST_ENCOUNTERED and (2) RAYCAST_BEST_HIT. These are actually the same picker, but with slightly different settings. The differentiation exists for the same reason "best hit" and "first encountered" options exist in an object's picking collider: for stopping the collision test as soon as it is needed in terms of precision. In this case, the test stops checking on the criteria of "renderables" instead of triangles.-
PickingType.RAYCAST_FIRST_ENCOUNTERED ( default value )
Uses ray tracing as a picking method, stopping tests at the first successful renderable level collision. -
PickingType.RAYCAST_BEST_HIT
Uses ray tracing as a picking method, evaluating the best ( closest ) hit between all the colliding renderables.

UV Painting
There is one last bit of the picking system that is worth visiting. One that is simple, but yet important: theMouseEvent3D
class. The scene position property of MouseEvent3D used in listings 4 and 5 allowed us to position a tracer mesh where the mouse ray collides with the object under it. MouseEvent3D also offers a series of other helpful properties:
-
scenePosition
The position of the event's collision in scene space. -
localPosition
Same as (1) but in the hit object's local space. -
sceneNormal
The object's normal at the point of collision, in scene space. -
localNormal
The object's normal at the point of collision, in object space. -
uv
The interpolated uv coordinates at the point of collision. Will not be available with raycast bounds collisions and shader collisions with details disabled. -
screenX and screenY
The position in screen space of the mouse event. -
material
The material of the colliding renderable. - etc...
