Projecting the mouse position into the scene

As we have seen, responding to mouse events from 3D objects in Away3D is very easy. We simply specify that a function be called in response to the various events defined in the Mouse3DEvent class, using the same addEventListener() function that is used in regular 2D Flash applications.

In addition to responding to events in this way, Away3D also allows you to get the position of the mouse cursor within the scene. This position can then be used to construct a ray that extends into the scene, and then intersects a plane. The following image shows how this ray / plane intersection works:

Projecting the mouse position into the scene

In the following InteractivityDemo application we use this ray to find a point on a plane, which is then used to reposition a sphere as if it were being dragged around in the scene. We will also respond to the MouseEvent3D.MOUSE_OVER, MouseEvent3D.MOUSE_OUT, and MouseEvent3D.MOUSE_DOWN events.

package
{
  import away3d.cameras.lenses.PerspectiveLens;
  import away3d.core.base.Object3D;
  import away3d.core.geom.Plane3D;
  import away3d.core.render.BasicRenderer;
  import away3d.core.utils.Cast;
  import away3d.events.MouseEvent3D;
  import away3d.materials.BitmapMaterial;
  import away3d.primitives.Plane;
  import away3d.primitives.Sphere;
  import flash.geom.Vector3D;

  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.filters.GlowFilter;	

  public class InteractivityDemo extends Away3DTemplate
  {

The checkerboard.jpg texture is embedded. This will be used for the ground plane 3D object.

    [Embed(source = "checkerboard.jpg")] 
    protected var CheckerBoardTexture:Class;

The selectedObject property will reference the sphere 3D object that is to be moved.

    protected var selectedObject:Object3D;

The sphere1 and sphere2 properties will reference the two sphere 3D objects that will be added to the scene.

    protected var sphere1:Sphere;
    protected var sphere2:Sphere;

The groundPlane property will reference the plane 3D object that will represent the ground.

    protected var groundPlane:Plane3D;

The throughScreenVector and groundPosition properties will reference the vectors and positions used later in the class while determining the position of the mouse cursor on the ground plane 3D object.

    protected var throughScreenVector:Vector3D;
    protected var groundPosition:Vector3D;

    public function InteractivityDemo()
    {
      super();
    }

    protected override function initEngine():void
    {
      super.initEngine();

The functions used to project the position of the mouse cursor into the scene and onto a plane only work if the camera is using the perspective lens, which was covered in Chapter 7, Cameras. If you use the default lens, which is provided by the ZoomFocusLens class, you will find that the mouse cursor's calculated position within the scene doesn't quite line up with the actual position of the mouse cursor.

      camera.lens = new PerspectiveLens();

The scene will contain two spheres, each with ownCanvas set to true. This is required to apply filters to the individual 3D objects. Away3D has a feature called triangle caching (which is explained in more detail in Chapter 13, Performance Tips) which can cause those 3D objects with ownCanvas set to true to not be sorted correctly as their relative distances to the camera changes. Setting the forceUpdate property on the view to true disables triangle caching, and fixes these sorting issues.

Tip

Be aware that disabling triangle caching can have a negative effect on performance.

To see the effect that triangle caching can have on the sorting of two or more 3D objects with their ownCanvas properties set to true, comment out the following line of code. You will notice that one sphere is then always drawn in front of the other, regardless of the position of the spheres within the scene.

      view.forceUpdate = true;
    }

    protected override function initScene():void
    {
      super.initScene();

The camera is placed 100 units up along the Y-axis, and then tilted down slightly to get a nice view of the scene.

      camera.position = new Vector3D(0, 100, 0);
      camera.tilt(20);

A bitmap material is created and then assigned to a plane primitive, which is added to the scene to represent the ground.

      var planeMaterial:BitmapMaterial = 
        new BitmapMaterial(
          Cast.bitmap(CheckerBoardTexture)
        );
      var plane:Plane = new Plane(
        {
          material: planeMaterial,
          segments: 10,
          width: 1000,
          height: 1000,
          y: -15,
          z: 250
        }
      );

By setting the screenZOffset to 1000, we are forcing the plane to be drawn beneath the two spheres that will be added next. Chapter 4, Z-Sorting, covers the screenZOffset property in more detail, as well as a number of additional methods that can be used to adjust the sorting order of 3D objects within the scene.

      plane.screenZOffset = 1000;
      scene.addChild(plane);

Now we add two sphere primitives to the scene. The ownCanvas init object parameter is set to true, which will allow us to use the glow filter to highlight the spheres when they are under the mouse cursor. Chapter 12, Filters and Postprocessing Effects, shows you how to use filters in more detail.

      sphere1 = new Sphere(
        {
          x: -50,
          z: 250,
          radius: 10,
          ownCanvas: true
        }
      );
      sphere1.ownCanvas = true
      scene.addChild(sphere1);

      sphere2 = new Sphere(
        {
          x: 50,
          z: 250,
          radius: 10,
          ownCanvas: true
        }
      );
      scene.addChild(sphere2);

The Plane3D class represents an infinite plane. A Plane3D object is not a visible object, and should not to be confused with the Plane class, which creates a primitive 3D object.

      groundPlane = new Plane3D();

The Plane3D class is initialized from a normal vector (pointing straight up along the Y-axis) and a position that exists anywhere on the plane (the origin, in this case). This creates a plane that lays flat on the X / Z plane.

      groundPlane.fromNormalAndPoint(
        new Vector3D(0, 1, 0), 
        new Vector3D()
      );
    }

    protected override function initListeners():void
    {
      super.initListeners();

The onMouseUp() function is registered against the MouseEvent.MOUSE_UP event dispatched by the stage. Note that this is a standard Flash mouse event, and is not dispatched by any Away3D classes.

      stage.addEventListener(
        MouseEvent.MOUSE_UP, 
        onMouseUp
      );

The three functions onMouseOver(), onMouseOut(), and onMouseDown() are registered with both spheres against the MouseEvent3D.MOUSE_OVER, MouseEvent3D.MOUSE_OUT, and MouseEvent3D.MOUSE_DOWN events. Unlike the event dispatched by the stage above, these three events do represent mouse events within the 3D scene.

      sphere1.addEventListener(
        MouseEvent3D.MOUSE_OVER, 
        onMouseOver
      );
      sphere1.addEventListener(
        MouseEvent3D.MOUSE_OUT, 
        onMouseOut
      );
      sphere1.addEventListener(
        MouseEvent3D.MOUSE_DOWN, 
        onMouseDown
      );

      sphere2.addEventListener(
        MouseEvent3D.MOUSE_OVER, 
        onMouseOver
      );
      sphere2.addEventListener(
        MouseEvent3D.MOUSE_OUT, 
        onMouseOut
      );
      sphere2.addEventListener(
        MouseEvent3D.MOUSE_DOWN, 
        onMouseDown
      );
    }

Tip

The addOnMouseMove(), addOnMouseDown(), addOnMouseUp(), addOnMouseOver(), addOnMouseOut(), addOnRollOver(), and addOnRollOut() functions can be used as a shorthand way of linking functions to events, like:

sphere1.addOnMouseDown(onMouseDown);

Likewise, the removeOnMouseMove(), removeOnMouseDown(), removeOnMouseUp(), removeOnMouseOver(), removeOnMouseOut(), removeOnRollOver(), and removeOnRollOut() functions can be used to stop a function from responding to an event.

When the mouse has moved over a sphere, we will highlight it by applying the glow filter. This is done by adding an instance of the GlowFilter class to an array, which is then assigned to the filters property defined by the Object3D class. Chapter 12, Filters and Postprocessing Effects, goes into these filters in more detail.

    protected function onMouseOver(event:MouseEvent3D):void
    {
      event.object.filters = [new GlowFilter()];
    }

When the mouse has moved off a sphere the filters are cleared, reverting the sphere back to its default appearance.

    protected function onMouseOut(event:MouseEvent3D):void
    {
      event.object.filters = [];
    }

When the mouse is clicked on a sphere it is assigned to the selectedObject property, which effectively selects that sphere as the one to be moved.

    protected function onMouseDown(event:MouseEvent3D):void
    {
      selectedObject = event.object;
    }

When the mouse button is released, the selectedObject property is set to null, which means that neither of the spheres is selected.

Tip

The spheres are deselected in response to the MouseEvent.MOUSE_UP event dispatched by the stage, and not by the Mouse3DEvent.MOUSE_UP event dispatched by a 3D object. This was done because releasing the mouse button should deselect the spheres regardless of which Away3D object was under the cursor when the button was released, if any. It is possible that the selected sphere was not under the mouse cursor when the mouse button was released, so if we had registered the onMouseUp() function to be called in response to the MouseEvent3D.MOUSE_UP event dispatched by the sphere primitives there is a possibility that the sphere would not be deselected as expected.

    protected function onMouseUp(event:MouseEvent):void
    {
      selectedObject = null;
    }

In the onEnterFrame() function we will move the selected sphere to a position under the mouse cursor.

    protected override function onEnterFrame(event:Event):void
    {
      super.onEnterFrame(event);

If the selectedObject variable is not null, indicating that one of the spheres has been selected, we will then move the sphere it references so that it is under the position of the mouse cursor.

      if (selectedObject != null)
      {

The Camera3D unproject() function takes a 2D position on the screen and returns a vector that points from the camera, through the supplied screen position, and out into the scene.

The position of the mouse needs to be supplied relative to the position of the view on the stage. Since the view is situated in the middle of the stage, we have to adjust the mouse coordinates, which are relative to the top-left corner of the stage.

        throughScreenVector = camera.unproject(
          stage.mouseX - stage.stageWidth / 2, 
          stage.mouseY - stage.stageHeight / 2
        );

Tip

Don't get caught out with the difference between the stage width/height and stageWidth/stageHeight properties. The width and height properties define the area taken up by the children of the stage, while the stageWidth and stageHeight properties define the actual dimensions of the stage itself. Although it makes little difference when the children of the stage (the View3D object in our case) take up all the available space on the stage, it is worth knowing the difference between the two sets of measurements.

To turn this direction vector into a position within the scene we add to it the position of the camera.

        throughScreenVector = 
          throughScreenVector.add(camera.position);

The Plane3D getIntersectionLineNumbers() function takes two points within the scene, and returns the point on the plane where the line defined by these two points intersects it. The position of the camera, and the position of the camera plus the vector of the mouse cursor coordinates projected out into the scene are supplied as the two points to define the line.

        groundPosition = 
          groundPlane.getIntersectionLineNumbers(
            camera.position, 
            throughScreenVector
          );

The selected sphere is then moved to the intersection point.

        selectedObject.position = groundPosition;
      }
    }
  }
}

When the application is run, you will see that the sphere under the mouse cursor is highlighted using the glow filter. You can then click and drag the sphere around within the scene. Although a mouse can only be moved in two dimensions, by positioning the spheres on a plane we can provide an intuitive way to move the spheres within a 3D scene.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.151.126