Erain 3D
-->

Author: Max Pellizzaro
Date: March 16th 2008
version: 3.0.2

Learning how to use WOW Spring Constraint

Objective of the tutorial

With this tutorial I will introduce you to the use of Spring Constraint: WSpringConstraint. This constraint allows modeling lot of physic behaviors in nature; it’s just a matter how to use it in the best way!
As a first example I have decide to model two bouncing ball, these ball have a rope that hooks them on the ceiling of a room.
Be sure you have completed the previous two tutorials.

How to

Set up

The main class has been named Example032. And here you will find all you need

example032.rar

The AS Code

In this section we report the AS code as a reference, and it will be explained in the next paragraph.

package 
{
	import flash.utils.*;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.ui.*;
	import flash.media.Sound;

	//wow engine    
	import fr.seraf.wow.primitive.*;
	import fr.seraf.wow.constraint.*;
	import fr.seraf.wow.core.WOWEngine;
	import fr.seraf.wow.core.data.WVector;
	import fr.seraf.wow.math.*;

	// Sandy 
	import sandy.core.Scene3D;
	import sandy.core.data.*;
	import sandy.core.scenegraph.*;
	import sandy.materials.*;
	import sandy.materials.attributes.*;
	import sandy.primitive.*;
	import sandy.events.*;


	public class Example032 extends Sprite
	{
		private var wow:WOWEngine;
		private var sphere00:WSphere;
		private var sphere01:WSphere;
		private var sphere02:WSphere;
		private var plane01:WOWPlane;
		
		private var sphereS01:Sphere;
		private var sphereS02:Sphere;
		
		var myLine01:Line3D;
		var myLine02:Line3D;
		
		private var tg1:TransformGroup;
		private var tg2:TransformGroup;
		private var sound:Sound= new BallBounce();
		private var bounce:Sound3D;
		
		private var scene:Scene3D;
		private var camera:Camera3D;

		public function Example032()
		{
			//create an instance of the physic engine    
			wow=new WOWEngine();
			// SELECTIVE is better for dealing with lots of little particles colliding,    
			wow.collisionResponseMode = wow.SELECTIVE;
			// gravity -- particles of varying masses are affected the same    
			wow.addMasslessForce (new WVector(0,2,0));
			
			// creating the ceiling
			plane01 = new WOWPlane();
			plane01.setPosition (0,-150,0);
			plane01.setRotation (0,0,180);
			plane01.elasticity=0.8;
			plane01.friction=0.0;
			wow.addParticle (plane01);
			
			//create a sphere    
			sphere00 = new WSphere(0,0,0,20,true);
			sphere01 = new WSphere(0,0,0,20,false,10,0.5,0);
			sphere02 = new WSphere(0,0,0,20,false,10,0.5,0);
			sphere00.py=-100;
			sphere00.px=0;
			
			sphere01.py=-100;
			sphere01.px=-100;
			
			sphere02.py=0;
			sphere02.px=0;
			
			wow.addParticle (sphere00);
			wow.addParticle (sphere01);
			wow.addParticle (sphere02);

			var constraint01:WSpringConstraint=new WSpringConstraint(sphere00, sphere01,2);
			constraint01.restLength=100;
			var constraint02:WSpringConstraint=new WSpringConstraint(sphere00, sphere02,2);
			constraint02.restLength=100;
			
			wow.addConstraint(constraint01);
			wow.addConstraint(constraint02);
			
			
			// Sandy code
			camera = new Camera3D( 600, 600 );
			camera.z = -500;
			camera.y = 200;
			camera.lookAt (0,0,0);
			var root:Group = createScene();
			scene = new Scene3D( "scene", this, camera, root );
			scene.light.setDirection(100, -350, 0);
			
			addEventListener ( Event.ENTER_FRAME, enterFrameHandler );
		}
		
		private function createScene ():Group
		{
			// Create the root Group
			var g:Group = new Group();
			tg1 = new TransformGroup('myGroup1');
			tg2 = new TransformGroup('myGroup2');

			// Create the spheres, so we have something to show
			sphereS01 = new Sphere("sphere01",20,15,10);
			sphereS02 = new Sphere("sphere02",20,15,10);
			
			var a:PhongAttributes = new PhongAttributes(true, 							       0.2, 15);
            			a.diffuse = 0.5;
           			a.specular = 1;
            			a.gloss = 3;

			var m:ColorMaterial = new ColorMaterial (0xFF, 1, 
					    new MaterialAttributes(a));

			m.lightingEnable = true; 

			var m2:ColorMaterial = new ColorMaterial (0xff0000, 1, 					          new MaterialAttributes(a));
			m2.lightingEnable = true; 
			
			sphereS01.appearance = new Appearance (m);
			sphereS01.useSingleContainer = false;
			sphereS02.appearance = new Appearance (m2);
			sphereS02.useSingleContainer = false;
			
			sphereS01.enableEvents = true;
			sphereS02.enableEvents = true;
			
			sphereS01.addEventListener( MouseEvent.CLICK, 							   onClick );

			sphereS02.addEventListener( MouseEvent.CLICK, 							   onClick );
			
			
			myLine01 = new Line3D( "aLine01", new Vector(sphere00.px, -sphere00.py, sphere00.pz), new Vector(sphere01.px, -(sphere01.py-20), sphere01.pz));
			myLine02 = new Line3D( "aLine02", new Vector(sphere00.px, -sphere00.py, sphere00.pz), new Vector(sphere02.px, -( sphere02.py-20), sphere02.pz));
			
			myLine01.useSingleContainer = false;
			myLine02.useSingleContainer = false;
			
			bounce = new Sound3D("bounce", sound, 1,3,1500);
			bounce.type = Sound3D.NOISE;
			bounce.loops = 0;
			
			tg1.addChild(sphereS01);
			tg1.addChild(bounce);
			
			tg2.addChild(sphereS02);
			tg2.addChild(bounce);
			
			g.addChild ( tg1 );
			g.addChild ( tg2 );
			g.addChild ( myLine01 );
			g.addChild ( myLine02 );
		
			return g;
		}
		
		private function onClick( p_eEvent:Shape3DEvent ):void
        		{
            			var v:Vector = p_eEvent.polygon.normal.getVector().clone();
			this[p_eEvent.shape.name].velocity = new  WVector(-v.x*20,+v.y*20,-v.z*20);    
        		}
		
		
		private function enterFrameHandler ( event : Event ):void
		{
			//run the engine once  
			wow.step ();
			// spheres
			sphereS01.x = sphere01.px;
			sphereS01.y = -sphere01.py;
			sphereS01.z = sphere01.pz;
			sphereS02.x = sphere02.px;
			sphereS02.y = -sphere02.py;
			sphereS02.z = sphere02.pz;
			//lines
			myLine01.aPolygons[0].vertices[1].x=sphere01.px;
			myLine01.aPolygons[0].vertices[1].y=-(sphere01.py-20);
			myLine01.aPolygons[0].vertices[1].z=sphere01.pz;
			
			myLine02.aPolygons[0].vertices[1].x=sphere02.px;
			myLine02.aPolygons[0].vertices[1].y=-(sphere02.py-20);
			myLine02.aPolygons[0].vertices[1].z=sphere02.pz;
			
			// let's compute distance between the two balls
			var dist:Number = Math.sqrt((sphereS01.x-sphereS02.x)*(sphereS01.x-sphereS02.x)+
					(sphereS01.y-sphereS02.y)*(sphereS01.y-sphereS02.y)+
					(sphereS01.z-sphereS02.z)*(sphereS01.z-sphereS02.z));
			var velocity = WVectorMath.getNorm(sphere01.velocity) + WVectorMath.getNorm(sphere02.velocity) 
			if (dist < 42 && velocity > 0.5 ) 
			 bounce.play();
			 
			// now we check if the two balls hits the ceiling
			if (sphereS01.y > 100)
			 bounce.play();
			if (sphereS02.y > 100)
			 bounce.play(); 
			
			scene.render ();
		}
	}
}

Examining the code

Let’s examine the code.

Create the WOW engine

This task is pretty easy to do, is it the same one as the previuos tutorial

//create an instance of the physic engine    
wow=new WOWEngine();  
// SELECTIVE is better for dealing with lots of little particles colliding,    
wow.collisionResponseMode = wow.SELECTIVE;
Create the WOW objects

For this tutorial we need to model 6 WOW objects:

While the first two set of elements are pretty easy, let’s investigate the “hook” part and the constraint.
What we want to do is modeling a ball linked with a rope to the ceiling. To do so we will model a fixed WSphere on the ceiling (sphere00, the hook), and the distance between the hook and the normal ball will be model with a WSprinigContraint.

sphere00 = new WSphere(0,0,0,20,true);
sphere01 = new WSphere(0,0,0,20,false,10,0.5,0);
sphere02 = new WSphere(0,0,0,20,false,10,0.5,0);
…
var constraint01:WSpringConstraint=new WSpringConstraint(sphere00, sphere01,1);
constraint01.restLength=100;
var constraint02:WSpringConstraint=new WSpringConstraint(sphere00, sphere02,1);
constraint02.restLength=100;

If we look at the parameters of the WSpringConstraint we notice a last Number, this number is the stiffness/strength of the spring. Valid values are between 0 and 1. Lower values result in softer springs. Higher values result in stiffer, stronger springs

Create the Sandy engine

This is the easy part, since by now you know that far better than me…

camera = new Camera3D( 600, 600 );
camera.z = -400;
var root:Group = createScene();
scene = new Scene3D( "scene", this, camera, root );
Create the Sandy objects

Since in the previous tutorial I have warned you that modeling too many objects is resource consuming, here we will just model what we need:

Yes, for this tutorial we just need to model only these 4 elements, since the room is fixed and we are not going to rotate the room and move in and out our camera. Well since maybe two balls were not too much, I have decide to add also sound, so you need also

To link sound with the ball you must use a transformation group as I have explained in this tutorial.

How to link Sandy objects and WOW objects

It’s now time to link WOW objects to Sandy objects. Since we have modeled only two Sandy Objects, the two balls, we just need to link them:

sphereS01.x = sphere01.px;
sphereS01.y = -sphere01.py;
sphereS01.z = sphere01.pz;
sphereS02.x = sphere02.px;
sphereS02.y = -sphere02.py;
sphereS02.z = sphere02.pz;

We also need to link the Line3D with the balls, but this has nothing to do with WOW, since we will do them only with Sandy elemends:

myLine01.aPolygons[0].vertices[1].x=sphere01.px;
myLine01.aPolygons[0].vertices[1].y=-(sphere01.py-20);
myLine01.aPolygons[0].vertices[1].z=sphere01.pz;
 
myLine02.aPolygons[0].vertices[1].x=sphere02.px;
myLine02.aPolygons[0].vertices[1].y=-(sphere02.py-20);
myLine02.aPolygons[0].vertices[1].z=sphere02.pz;

Now the last thing we need to do is making the “hitting” alive. The best thing to do would be to catch the collision between the balls and the ball and the ceiling, but WOW still does not support it (but I have the feeling it will in the near future). Se we need to catch the hitting “by hand”:

// let's compute distance between the two balls
var dist:Number = Math.sqrt(
    (sphereS01.x-sphereS02.x)*(sphereS01.x-sphereS02.x)+
    (sphereS01.y-sphereS02.y)*(sphereS01.y-sphereS02.y)+
    (sphereS01.z-sphereS02.z)*(sphereS01.z-sphereS02.z));
 
var velocity = WVectorMath.getNorm(sphere01.velocity) + WVectorMath.getNorm(sphere02.velocity) 
if (dist < 42 && velocity > 0.5 ) 
   bounce.play();
 
// now we check if the two balls hits the ceiling
if (sphereS01.y > 100)
  bounce.play();
if (sphereS02.y > 100)
  bounce.play();

This is it! Let’s see our result now.

The output

Remember to have the speaker switch on your pc to hear the sound! Click on the two balls to punch them!!