Sandy3D: adding interactivity to 3D objects

Posted by Dennis on Sep 25, 2007 in 3D, ActionScript, Flash, Flex14 comments

In this small demo you can see how you can add interactivity to Shape3D objects. Every Shape3D object has a property called container which is a native Flash Sprite. This means you can add event listeners and apply filters the same way as you’re used to with Sprites. I added event listeners for the mouse over and mouse click events. This demo also shows how to create a Scene3D with Sandy. This is a new feature and is an alternative to World3D. You can download all files that are used here. You can find the main file explained below. For this demo I used Dreammania’s AS3 Geom Class Exporter and Tweener. Enjoy!

PS: the mouse over behavior may seem erratic when you go over the holes. I actually did that on purpose so you can see that it only affects the mesh!

Here are all the classes that are used:

Here’s where all the stuff happens. The comments should explain everything.


package {
	import caurina.transitions.Tweener;

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

	import sandy.core.Scene3D;
	import sandy.core.scenegraph.Camera3D;
	import sandy.core.scenegraph.Group;
	import sandy.core.scenegraph.Shape3D;
	import sandy.materials.Appearance;
	import sandy.materials.ColorMaterial;
	import sandy.materials.attributes.LightAttributes;
	import sandy.materials.attributes.MaterialAttributes;

	[SWF(width="400", height="200", backgroundColor='#000000', frameRate='24', quality='high')]

	/**
	 * SandyInteractivity.as
	 * 24 September 2007
	 * @author Dennis Ippel - http://www.rozengain.com
	 *
	 */
	public class SandyInteractivity extends Sprite
	{
		private var scene:Scene3D;
		private var selectedMenuItem:Shape3D;
		private var menuItem1:MenuItem1;
		private var menuItem2:MenuItem2;
		private var menuItem3:MenuItem3;	

		public function SandyInteractivity()
		{
			init();
			addEventListener( Event.ENTER_FRAME, enterFrameHandler );
		}

		private function init():void
		{
			// -- configure the camera, move it upwards a little
			var camera:Camera3D = new Camera3D( 400, 200 );
			camera.y = 25;
			camera.z = -75;
			camera.lookAt( 0, 0, 0 );

			// -- creat the root group and the scene
			var root:Group = new Group( "root" );
			scene = new Scene3D( "scene", this, camera, root );

			// -- menu item 1
			menuItem1 = new MenuItem1( "menuItem1" );
			configureMenuItem( menuItem1, 0xff0000, -50, -25 );

			// -- menu item 2
			menuItem2 = new MenuItem2( "menuItem2" );
			configureMenuItem( menuItem2, 0xffff00, 0, -25 );

			// -- menu item 3
			menuItem3 = new MenuItem3( "menuItem3" );
			configureMenuItem( menuItem3, 0x00ff00, 50, -25 );
		}

		/**
		 * The handler for the mouse over event
		 * @param event
		 *
		 */
		private function mouseOverHandler( event:MouseEvent ):void
		{
			// -- get the target container. The event handler
			//    was attached to the Shape3D's container. Here
			//    we use it again to apply a filter
			var container:Sprite = event.target as Sprite;
			// -- apply a glow filter to the container
			container.filters = [
				new GlowFilter( 0xffffff, 1, 12, 12 )
			];

			// -- get the Shape3D this container is in
			var currentShape:Shape3D = getShape3DByContainer( container );

			// -- create a tween
			Tweener.addTween( currentShape, {
				rotateX: 90,
				time : 1,
				transition : "easeOutBounce"
			});
		}

		/**
		 * The handler for the mouse out event
		 * @param event
		 *
		 */
		private function mouseOutHandler( event:MouseEvent ):void
		{
			// -- get the target container. The event handler
			//    was attached to the Shape3D's container. Here
			//    we use it again to remove a filter
			var container:Sprite = event.target as Sprite;
			container.filters = [];

			// -- get the Shape3D this container is in
			var currentShape:Shape3D = getShape3DByContainer( container );

			// -- create a tween
			Tweener.addTween( currentShape, {
				rotateX: 0,
				time : 0.5,
				transition : "easeOutBounce"
			});
		}

		/**
		 * The handler for the mouse click event
		 * @param event
		 *
		 */
		private function clickHandler( event:MouseEvent ):void
		{
			// -- get the target container.
			var container:Sprite = event.target as Sprite;

			// -- get the Shape3D this container is in
			var currentShape:Shape3D = getShape3DByContainer( container );

			// -- create a tween
			Tweener.addTween( currentShape, {
				z: 50,
				time : 0.5,
				transition : "easeOutSine",
				onComplete : function():void
				{
					// -- tween back to the original position
					Tweener.addTween( currentShape,
					{
						z: 0,
						time : 1,
						transition : "easeOutSine"
					});
				}
			});
		}

		/**
		 * Compares the given container Sprite to all the container Sprites
		 * in the scene.
		 * @param container
		 * @return
		 *
		 */
		private function getShape3DByContainer( container:Sprite ):Shape3D
		{
			for each( var shape:Object in scene.root.children )
				if( shape is Shape3D && shape.container == container )
					return shape as Shape3D;

			return null;
		}

		/**
		 * Adds standard properties to the items
		 * @param item
		 * @param color
		 * @param x
		 * @param y
		 *
		 */
		private function configureMenuItem( item:Shape3D, color:uint, x:int, y:int ):void
		{
			// -- appearance / material properties
			var mattAttr:MaterialAttributes = new MaterialAttributes(
				new LightAttributes( false, 0.3 )
			);

			item.appearance = new Appearance(
				new ColorMaterial( color, 100, mattAttr )
			);
			item.appearance.frontMaterial.lightingEnable = true;

			// -- position
			item.x = x;
			item.y = y;

			// -- add event listeners to this Shape3D's container. The
			//    container is a Sprite, native to Flash
			item.container.addEventListener( MouseEvent.MOUSE_OVER, mouseOverHandler );
			item.container.addEventListener( MouseEvent.MOUSE_OUT, mouseOutHandler );
			item.container.addEventListener( MouseEvent.CLICK, clickHandler );
			item.container.useHandCursor = true;
			item.container.buttonMode = true;

			// -- add this Shape3D to the scene
			scene.root.addChild( item );
		}

		private function enterFrameHandler( event:Event ):void
		{
			scene.render();
		}
	}
}


Tags: , , , ,


14 comments

» Comments RSS Feed
  1. Mmm.. like that :)
    With the Sandy 3.0 release, there will be a lot of fun coming up.

  2. I have been scouring the web for almost three months because:

    a. I am new to Flash, and
    b. Have no experience with any of the wide range of 3D tools that are available.

    Nonetheless, because I have a lot of experience with a lot of programming environments extending over a very long period of time, I have been able to work my way through a variety of ‘test programs’ exhibiting different levels of 3D rendering technologies and different levels of Actionscript coding. Where I have not been particularly lucky or productive has been in attempting to work through the many, quickly-emerging examples of interesting techniques using Papervision3D, Away3D, or Sandy.

    The reason for this long introduction is that I want to complement y0u on the nice example you have provided here. Although it required diverting into yet another download of yet another plug-in, at the end of the day I am able to create and understand some very compelling 3D behaviors from your example. I stumbled upon your example through the Sandy path, but I will go forward and see if I can finally understand how to do something productive with Papervision3D and Alway3D. I expect that as others come across your example, they will, too, be pleased to find something that works just about as advertised.

    So, that’s the final point — there is an aspect of behavior that needs to be fixed, but I am too much of a newbie to even know where the problem lies. Your MouseOver behavior only results in a smooth transition from ‘prone’ to ‘upright’ if the user’s mouse manages to continuously lie over a portion of the complex polygon you have drawn. In most cases. as the targeted object moves, the mouse ends up being over a portion of one of the ‘holes’ in the targeted object. It would be very helpful to know how you would fix that so that the expected behavior would occur.

    Whether you get around to fixing/documenting the solution to that problem or not, thank you for a well-thought-out, well-delivered tutorial.

  3. Hi Terry,
    Thanks for your kind words!
    Yes, I know the behavior looks a little strange. I actually left that in on purpose for people to see that the mouse actions only work on the mesh itself. The problem could be solved by using the object’s bounding sphere. Which makes actually makes a good subject for a next demo. So keep an eye on my blog :-)
    Dennis

  4. 01. I will look forward to instructions on how to make the mouse events respond to the object’s bounding sphere, as I think that would likely provide the desired user experience for most sorts of ‘interesting’ widgets in a UI.

    02. In the meantime, I changed the simple torus shape I had used when reproducing the steps of your tutorial in my own Flash CS3 environment to a not quite so simple ‘Gordian Knot’. As designed, the change from one 3dsmax object to another is quite simple. When, however, nothing seemed to be happening, it took a couple of iterations before I located the #1502 timeout that was occurring. So, with a quick Google and look at the AS3 docs, I saw that I could catch the Error.

    Of course, I did not know exactly what to expect even if I did manage to catch the Error, but the result was — to me — interesting, and might be something else you would like to add to any augmented tutorial. First, I put a trace inside the catch block. Second, when I re-ran, I only got one trace message, meaning that the rendering of my particular shape was just a little too much, not much, much too much. Third, thereafter, the tween animations, ran just fine.

    So, that says something about the ‘black-box mystery’ of what is going on in the Sandy code. Once an object is ‘generated’ — a very compute sensitive operation — it may be ‘manipulated’ — with considerably less compute work. If something like that is true, your tutorial could elegantly demonstrate those characteristics of the facilities you have documented for us.

  5. Hi,
    your example shows the use of some interesting features and will be of a great help to start.
    thanks a lot !

  6. OK, I’m missing some key point and I’m stumped.

    If, instead of using a class created via the DreamMania export technique, I simply try to use a Sandy primitive object — Sphere — nothing is rendered.
    The DreamMania-generated classes extend Shape3D and implement the Primitive3D interface. Sphere extends Shape3D and implements the Primitive3D interface, i.e., there is a generate method to return the valid Geometry3D.

    If I really understood your example, I ought to be able to make this small change and debug the problem, but so far, no luck.

  7. OK, the tutorial did its job marvelously well. It forced me to try to understand the trigonometry of the relationships between camera position and object position [which, I appreciate was NOT the original purpose of this blog entry]. My Sphere’s radius [actually the default 100] caused, I guess all visible vertex surfaces to be behind the camera.

    So, to the extent that you are helping idiots like me move gently from 2.5D to 3D, some explanation of ’stage layout’ in the Sandy environment [in addition, of course, to these pretty demos you are providing], would be fantastic.

    Thanks again.

  8. This is really like a quick tutorial thank you very much. I will try to adopt 3d interactivity to my services. This is a start point.

  9. This site is interesting and very informative, nicely interface. Enjoyed browsing through the site.

  10. Well, interesting to play with Sandy 3.0..

    btw nice tutorial

  11. […] Unfortunately, this isn’t the first time I’ve had this problem. Enter Six Apart. When signed up and tested, I couldn’t leave comments on my own brand new TypePad blog (tdavid.typepad.com). This was around Christmas time two years ago and it took a week or so to find out that the reason my comments were being blocked was because makeyougohmm.com was listed on an old spam service. […]

  12. Yeah, the example shows the use of some interesting features and will be of a great help to start.

  13. Hm, this is something new. I worked on 3D models on CAD programs but not on Sandy. Will have to check it out. Thanks.

  14. Hm, this is something new. I worked on 3D models on CAD programs but not on Sandy. Will have to check it out. Thanks.

Leave a comment