Erain 3D
-->

Author: Max Pellizzaro
Date: May 11th 2008
version: 3.0.2

How to model a net/blanket using WOW

Objective of the tutorial

With tutorial will teach you on how to model a balnket (or a net) using WOW. I have seen on the WOW's official web site three nice demos on using a cloth, and I just want to do it with Sandy.
In this tutorial we will model a squared net hanging from the four vertex of the net, as you can see from the picture below.

How to

Set up

The main class has been named Example033.

example033.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 Example033 extends Sprite
	{
		private var wow:WOWEngine;
		private var net:Plane3D;
		private var arrayWSphere:Array = new Array();
		private var arrayWConstraint:Array = new Array();
		
		private var scene:Scene3D;
		private var camera:Camera3D;

		public function Example033()
		{
			//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,1,0));
			
			var plane01:WOWPlane = new WOWPlane();
			plane01.setPosition (0,60,0);
			plane01.elasticity=0.1;
			plane01.friction=0.0;
			//wow.addParticle (plane01);
			
			// Sandy code
			camera = new Camera3D( 600, 600 );
			camera.z = -200;
			camera.y = 50;
			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 );
			 stage.addEventListener (MouseEvent.MOUSE_MOVE, mouseMovedHandler);
		}
		
		private function createScene ():Group
		{
			// Create the root Group
			var g:Group = new Group();
			net = new Plane3D( "theNet", 100, 100, 9, 9, Plane3D.ZX_ALIGNED, "quad" );
			net.y = 10;
			net.enableBackFaceCulling = false;
			
			var materialAttr01:MaterialAttributes = new MaterialAttributes( 
				new LightAttributes( true, 0.01),
				new LineAttributes(1, 0xFFFFFF, 1)
				);
		    var material01:Material = new ColorMaterial( 0x990000, 1, materialAttr01 );
            material01.lightingEnable = false;
		    var app01:Appearance = new Appearance( material01 );
		    net.appearance = app01;
			
			// vertex of the pshere
			for (var i=0; i<net.geometry.aVertex.length; i++){
				var sphere:WSphere = null;
				if(i==0 || i==9 || i==90 || i==99)
				 sphere = new WSphere(net.geometry.aVertex[i].x,
									  -net.geometry.aVertex[i].y,
									  net.geometry.aVertex[i].z,
									  1,true);
				else
				 sphere = new WSphere(net.geometry.aVertex[i].x,
									  -net.geometry.aVertex[i].y,
									  net.geometry.aVertex[i].z,
									  1,false,0.01,0.1,0);
				arrayWSphere[i] = sphere;
				wow.addParticle (arrayWSphere[i]);
			}
			
			
			for (var j=0; j<net.geometry.aEdges.length; j++){
				arrayWConstraint[j] =new WSpringConstraint(
								arrayWSphere[net.geometry.aEdges[j].vertexId1], 
								arrayWSphere[net.geometry.aEdges[j].vertexId2],
								1);
				wow.addConstraint(arrayWConstraint[j]);
				
			}
			
			/*
			
			// let's add constraints to the net
			var k = 0;
		    for (var k1=0; k1<=90; k1=k1+10){
				for (var j1=0; j1<4; j1++){ 
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[k1+j1], 
								arrayWSphere[k1+j1+1],
								1);
				      wow.addConstraint(arrayWConstraint[k]);
				  k++;
			 }
			}
			
			for (var k2=0; k2<=9; k2++){
				for (var j2=0; j2<=40; j2=j2+10){ 
			      arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[k2+j2], 
								arrayWSphere[k2+j2+10],
								1);
			      wow.addConstraint(arrayWConstraint[k]);
				  k++;
			 }
			}
			
			 for (var k3=9; k3<=99; k3=k3+10){
				for (var j3=0; j3<4; j3++){ 
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[k3-j3], 
								arrayWSphere[k3-j3-1],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			 }
			}
			
			for (var k4=90; k4<=99; k4++){
				for (var j4=0; j4<=30; j4=j4+10){ 
			      arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[k4-j4], 
								arrayWSphere[k4-j4-10],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			 }
			}
			
			for (var k5=0; k5<=90; k5=k5+10){
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[4+k5], 
								arrayWSphere[5+k5],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			}
			for (var k7=0; k7<=90; k7=k7+10){
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[5+k7], 
								arrayWSphere[4+k7],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			}
			
			for (var k6=0; k6<=9; k6++){
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[40+k6], 
								arrayWSphere[50+k6],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			}
			for (var k8=0; k8<=9; k8++){
				  arrayWConstraint[k] =new WSpringConstraint(
								arrayWSphere[50+k8], 
								arrayWSphere[40+k8],
								1);
				  wow.addConstraint(arrayWConstraint[k]);
				  k++;
			}
			*/
			
			g.addChild ( net );
			
			return g;
		}
		
	  private function mouseMovedHandler (event:MouseEvent):void{
		 net.pan=-(event.stageX-600/2)/10;
		 net.tilt=-(event.stageY-600/2)/10; 
	   }
	  
		private function enterFrameHandler ( event : Event ):void
		{
			//run the engine once  
			wow.step ();
			for (var i=0; i<net.geometry.aVertex.length; i++){
				net.geometry.aVertex[i].x = arrayWSphere[i].px;
				net.geometry.aVertex[i].y = -arrayWSphere[i].py;
				net.geometry.aVertex[i].z = arrayWSphere[i].pz;
			}
			scene.render ();
		}
	}
}

Examining the code

Before examining the code we need to explain what we need to model and how to do it.
The idea behind the model of the net is to model each intersection of the net as a single sphere, and to use springs for every edges linking each intersection of the net. So now 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 a lot of WOW objects:

let's model the intersection of the net (and for of them are fixed):

for (var i=0; i<net.geometry.aVertex.length; i++){
   var sphere:WSphere = null;
   if(i==0 || i==9 || i==90 || i==99)
     sphere = new WSphere(net.geometry.aVertex[i].x,
				  -net.geometry.aVertex[i].y,
	     			  net.geometry.aVertex[i].z,
				  1,true);
  else
    sphere = new WSphere(net.geometry.aVertex[i].x,
				  -net.geometry.aVertex[i].y,
				  net.geometry.aVertex[i].z,
				  1,false,0.01,0.1,0);
 arrayWSphere[i] = sphere;
 wow.addParticle (arrayWSphere[i]);
}

Let's now mode the Springs, one for each edge:

for (var j=0; j<net.geometry.aEdges.length; j++){
   arrayWConstraint[j] =new WSpringConstraint(
	arrayWSphere[net.geometry.aEdges[j].vertexId1], 
	arrayWSphere[net.geometry.aEdges[j].vertexId2],
								1);
  wow.addConstraint(arrayWConstraint[j]);			
}
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 = -200;
camera.y = 50;
camera.lookAt (0,0,0);
var root:Group = createScene();
scene = new Scene3D( "scene", this, camera, root );
scene.light.setDirection(100, -350, 0);
Create the Sandy objects

We just need one Sandy object: a plabe3D, tha in fact we have already used when creating the WOW object since we have looped on the Sandy object vertex and edges in order to model WOW spheres and WOW springs.

net = new Plane3D( "theNet", 100, 100, 9, 9, Plane3D.ZX_ALIGNED, "quad" );
net.y = 10;
net.enableBackFaceCulling = false;
How to link Sandy objects and WOW objects

This task is prety easy too… we just need to loop between Sandy vertex and WOW spheres.

for (var i=0; i<net.geometry.aVertex.length; i++){
	net.geometry.aVertex[i].x = arrayWSphere[i].px;
	net.geometry.aVertex[i].y = -arrayWSphere[i].py;
	net.geometry.aVertex[i].z = arrayWSphere[i].pz;
  }

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

The output (v1)

Let's see our first output.

Looking at this result I was not too happy, because I wanted to see a simmetric net! So I have digged to see how to make it simmetric and I have found out that the order of the spring was impostant. When you define a Sping you need to specify the two edges of the sping. If you have a net with 6 edges, you should specify 3 spring in one direction and 3 spring in the other direction. I have places this “simmetric” code in comment in the above code example, and here is the result !!