Second Life Tree in Papervision3D

Intro

A Second Life tree in Papervision3D is pretty easy to make. It’s made up of two planes intersecting at 90 degrees. As you rotate around the tree, it appears to be 3D since you see its sister plane. Of course it’s just an optical illusion, and as you get closer to the tree the planes become more apparent. But given the processing savings of creating a tree with just one primitive-it’s worth it! When you get to the Chapter on virtual tours you’ll use this trick to create an entire grove of trees on a Google Map.

Second Life Tree using Two Intersecting Planes

Second Life Tree using Two Intersecting Planes

Demo

Source

YouTube (Note: Discussion of Primitive does not occur until 1/4 of video has been  played)

Discussion

The trick to creating this illusion is to take a cube, which is made up of six planes, eliminate four of the sides and translate two of them into an intersection point.

From the Cube primitive available in Papervision3D, create a Tree primitive: as was done in the hourglass case (see the book). Then navigate to the plane building functions of the cube (now named tree) and comment out the four unneeded planes. In this case you’ll keep front and right planes.

if( ! (excludeFaces & FRONT) )
buildPlane( “front”, “x”, “y”, width, height, depth2, ! Boolean( insideFaces & FRONT ) );

//Remove if( ! (excludeFaces & BACK) )
//Remove buildPlane( “back”, “x”, “y”, width, height, -depth2, Boolean(
//Remove insideFaces & BACK ) );

if( ! (excludeFaces & RIGHT) )
buildPlane( “right”, “z”, “y”, depth, height, width2, Boolean( insideFaces & RIGHT ) );

//Remove if( ! (excludeFaces & LEFT) )
//Remove buildPlane( “left”, “z”, “y”, depth, height, -width2, !
//Remove Boolean( insideFaces & LEFT ) );

//Remove if( ! (excludeFaces & TOP) )
//Remove buildPlane( “top”, “x”, “z”, width, depth, height2, Boolean(
//Remove insideFaces & TOP ) );

//Remove if( ! (excludeFaces & BOTTOM) )
//Remove buildPlane( “bottom”, “x”, “z”, width, depth, -height2, !
//Remove Boolean( insideFaces & BOTTOM ) );

Then navigate down to the vertex generation equations. Removing the “rev” term from the vertex(u) equation below will bring the front and right planes together.

vertex[ u ] = (iu * incU – textureU)[Remove * rev];
vertex[ v ] = iv * incV – textureV;
vertex[ w ] = depth;

Your primitive is ready to run. But remember the cube uses the materials list. In this case, since you have only two planes, you’ll create a material list of only two “png” image files. You use “png” image files since your tree must have the area around its branches transparent and “png” files have a transparent channel.

//Create a BitmapFileMaterial for your first png
myBitmapFileMaterial = new BitmapFileMaterial(“assets/TreeBigFront.png”);
myBitmapFileMaterial.doubleSided = true;

//Create a BitmapFileMaterial for your first png
myBitmapFileMaterial2 = new BitmapFileMaterial(“assets/TreeBigSide.png”);
myBitmapFileMaterial2.doubleSided = true;

//Add a material list containing your two bitmaps
var materialsList:MaterialsList = new MaterialsList();
materialsList.addMaterial(myBitmapFileMaterial, “front” );
materialsList.addMaterial(myBitmapFileMaterial2, “right” );

//Instantiate your Tree and add it to the scene
mySLTree = new Tree(materialsList,400,400,400,4,4,4);
scene.addChild(mySLTree);
startRendering();

As mentioned above, you must use the material list to add your “png” images to the planes. In the next Chapter (see the book) on Dressing up Prims you’ll learn more about materials and how to apply them. But for now all you do is create two bitmap file materials, add your images to them, and then add them to your material list. Then the material list is added to your Second Life Tree.

Note: Second Life Trees actually have 3 planes. This example gives the lowest possible CPU hit for the biggest bang! It’s part of optimizing Papervision3D.

The rest is the same as before (see the book), just instantiate the tree primitive and add it to your scene using the addChild method: as demonstrated in the code above.

Click the more button below to see the entire Second Life Tree Primitive and BasicView Wrapper file.

Tree Primitive

package org.papervision3d.objects.primitives {
import org.papervision3d.Papervision3D;
import org.papervision3d.core.geom.*;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.math.NumberUV;
import org.papervision3d.core.proto.*;
import org.papervision3d.materials.utils.MaterialsList;

/**
* The Cube class lets you create and display flat rectangle objects.
* <p/>
* The rectangle can be divided in smaller segments. This is usually done to reduce linear mapping artifacts.
* <p/>
* Dividing the Cube in the direction of the perspective or vanishing point, helps to reduce this problem. Perspective distortion dissapears when the Cube is facing straignt to the camera, i.e. it is perpendicular with the vanishing point of the scene.
*/
public class Tree extends TriangleMesh3D
{
/**
* Number of segments per axis. Defaults to 1.
*/
public var segments :Number3D;

/**
* No faces selected.
*/
static public var NONE :int = 0x00;

/**
* Front face selection
*/
static public var FRONT :int = 0x01;

/**
* Back face selection
*/
static public var BACK :int = 0x02;

/**
* Right face selection
*/
static public var RIGHT :int = 0x04;

/**
* Left face selection
*/
static public var LEFT :int = 0x08;

/**
* Top face selection
*/
static public var TOP :int = 0x10;

/**
* Bottom face selection
*/
static public var BOTTOM :int = 0x20;

/**
* All faces selected.
*/
static public var ALL :int = FRONT + BACK + RIGHT + LEFT + TOP + BOTTOM;

// ___________________________________________________________________________________________________
// N E W
// NN NN EEEEEE WW WW
// NNN NN EE WW WW WW
// NNNNNN EEEE WWWWWWWW
// NN NNN EE WWW WWW
// NN NN EEEEEE WW WW

/**
* Create a new Cube object.
* <p/>
* @param materials A MaterialObject3D object that contains the material properties of the object.
*
* Supported materials are: front, back, right, left, top, bottom & all, for example:
*
* var materials:MaterialsList = new MaterialsList(
* {
* all: new MovieAssetMaterial( “Front”, true ), // This is the default material
* front: new MovieAssetMaterial( “Front”, true ),
* back: new MovieAssetMaterial( “Back”, true ),
* right: new MovieAssetMaterial( “Right”, true ),
* left: new MovieAssetMaterial( “Left”, true ),
* top: new MovieAssetMaterial( “Top”, true ),
* bottom: new MovieAssetMaterial( “Bottom”, true )
* } );
*
* <p/>
* @param width [optional] – Desired width.
* <p/>
* @param depth [optional] – Desired depth.
* <p/>
* @param height [optional] – Desired height.
* <p/>
* @param segmentsS [optional] – Number of segments sagitally (plane perpendicular to width). Defaults to 1.
* <p/>
* @param segmentsT [optional] – Number of segments transversally (plane perpendicular to depth). Defaults to segmentsS.
* <p/>
* @param segmentsH [optional] – Number of segments horizontally (plane perpendicular to height). Defaults to segmentsS.
* <p/>
* @param insideFaces [optional] – Faces that are visible from the inside. Defaults to Cube.NONE.
*
* You can add or sustract faces to your selection. For examples: Cube.FRONT+Cube.BACK or Cube.ALL-Cube.Top.
*
* <p/>
* @param excludeFaces [optional] – Faces that will not be created. Defaults to Cube.NONE.
*
* You can add or sustract faces to your selection. For examples: Cube.FRONT+Cube.BACK or Cube.ALL-Cube.Top.
*
* <p/>
* @param initObject [optional] – An object that contains user defined properties with which to populate the newly created GeometryObject3D.
* <p/>
* It includes x, y, z, rotationX, rotationY, rotationZ, scaleX, scaleY scaleZ and a user defined extra object.
* <p/>
* If extra is not an object, it is ignored. All properties of the extra field are copied into the new instance. The properties specified with extra are publicly available.
*/
public function Tree( materials:MaterialsList, width:Number=500, depth:Number=500, height:Number=500, segmentsS:int=1, segmentsT:int=1, segmentsH:int=1, insideFaces:int=0, excludeFaces:int=0, initObject:Object=null )
{
super( materials.getMaterialByName( “all” ), new Array(), new Array(), null);

this.materials = materials;

this.insideFaces = insideFaces;
this.excludeFaces = excludeFaces;

segments = new Number3D( segmentsS, segmentsT, segmentsH );

buildCube( width, height, depth );
}

private function buildCube( width:Number, height:Number, depth:Number ):void
{
var width2 :Number = width /2;
var height2 :Number = height /2;
var depth2 :Number = depth /2;

if( ! (excludeFaces & FRONT) )
buildPlane( “front”, “x”, “y”, width, height, depth2, ! Boolean( insideFaces & FRONT ) );

/* if( ! (excludeFaces & BACK) )
buildPlane( “back”, “x”, “y”, width, height, -depth2, Boolean( insideFaces & BACK ) );
*/
if( ! (excludeFaces & RIGHT) )
buildPlane( “right”, “z”, “y”, depth, height, width2, Boolean( insideFaces & RIGHT ) );

/* if( ! (excludeFaces & LEFT) )
buildPlane( “left”, “z”, “y”, depth, height, -width2, ! Boolean( insideFaces & LEFT ) );

if( ! (excludeFaces & TOP) )
buildPlane( “top”, “x”, “z”, width, depth, height2, Boolean( insideFaces & TOP ) );

if( ! (excludeFaces & BOTTOM) )
buildPlane( “bottom”, “x”, “z”, width, depth, -height2, ! Boolean( insideFaces & BOTTOM ) ); */

mergeVertices();

this.geometry.ready = true;

if(Papervision3D.useRIGHTHANDED)
this.geometry.flipFaces();
}

private function buildPlane( mat:String, u:String, v:String, width:Number, height:Number, depth:Number, reverse:Boolean=false ):void
{
var matInstance:MaterialObject3D;
if( ! (matInstance= materials.getMaterialByName( mat )))
{
if(!(matInstance=materials.getMaterialByName( “all” ))){
//Papervision3D.log( “Cube: Required material not found in given materials list. Supported materials are: front, back, right, left, top, bottom & all.” );
return;
}
}

// Find w depth axis
var w :String;
if( (u==”x” && v==”y”) || (u==”y” && v==”x”) ) w = “z”;
else if( (u==”x” && v==”z”) || (u==”z” && v==”x”) ) w = “y”;
else if( (u==”z” && v==”y”) || (u==”y” && v==”z”) ) w = “x”;

// Mirror
var rev :Number = reverse? -1 : 1;

// Build plane
var gridU :Number = this.segments[ u ];
var gridV :Number = this.segments[ v ];
var gridU1 :Number = gridU + 1;
var gridV1 :Number = gridV + 1;

var vertices :Array = this.geometry.vertices;
var faces :Array = this.geometry.faces;
var planeVerts :Array = new Array();

var textureU :Number = width /2;
var textureV :Number = height /2;

var incU :Number = width / gridU;
var incV :Number = height / gridV;

// Vertices
for( var iu:int = 0; iu < gridU1; iu++ )
{
for( var iv:int = 0; iv < gridV1; iv++ )
{
var vertex:Vertex3D = new Vertex3D();

//var myUin:Number=20*(1-iu*iu/((gridU1-1)*(gridU1-1)));
//var myVin:Number=20*(1-iv*iv/((gridV1-1)*(gridV1-1)));

vertex[ u ] = (iu * incU – textureU) ;
vertex[ v ] = iv * incV – textureV;
vertex[ w ] = 0;

vertices.push( vertex );
planeVerts.push( vertex );
}
}

// Faces
var uvA :NumberUV;
var uvC :NumberUV;
var uvB :NumberUV;

for( iu = 0; iu < gridU; iu++ )
{
for( iv= 0; iv < gridV; iv++ )
{
// Triangle A
var a:Vertex3D = planeVerts[ iu * gridV1 + iv ];
var c:Vertex3D = planeVerts[ iu * gridV1 + (iv+1) ];
var b:Vertex3D = planeVerts[ (iu+1) * gridV1 + iv ];

uvA = new NumberUV( iu / gridU, iv / gridV );
uvC = new NumberUV( iu / gridU, (iv+1) / gridV );
uvB = new NumberUV( (iu+1) / gridU, iv / gridV );

faces.push(new Triangle3D(this, [ a, b, c ], matInstance, [ uvA, uvB, uvC ] ) );

// Triangle B
a = planeVerts[ (iu+1) * gridV1 + (iv+1) ];
c = planeVerts[ (iu+1) * gridV1 + iv ];
b = planeVerts[ iu * gridV1 + (iv+1) ];

uvA = new NumberUV( (iu+1) / gridU, (iv+1) / gridV );
uvC = new NumberUV( (iu+1) / gridU, iv / gridV );
uvB = new NumberUV( iu / gridU, (iv+1) / gridV );

faces.push(new Triangle3D(this, [ c, a, b ], matInstance, [ uvC, uvA, uvB ] ) );
}
}
}
private var insideFaces :int;
private var excludeFaces :int;

}
}

BasicView Wrapper File

package
{
//Flash Imports
import flash.display.BitmapData;
import flash.events.Event;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
//Papervision Imports
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.materials.BitmapFileMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Tree;
import org.papervision3d.view.BasicView;

//Declare the Hello Plane class that extends BasicView
public class SecondLifeTree extends BasicView
{
//Declare the plane, bitmapdata, and material variables
private var mySLTree:Tree;
private var myBitmapFileMaterial: BitmapFileMaterial;
private var myBitmapFileMaterial2: BitmapFileMaterial;

protected var planeBitmapData:BitmapData;
protected var planeMaterial:BitmapMaterial;

//Create the Hello Plane Constructor function
public function SecondLifeTree()
{
super(1, 1, true, false);
initPV3D();
}
//Set up stage and plane
private function initPV3D():void
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;

opaqueBackground = 0x8c8b8b;

myBitmapFileMaterial = new BitmapFileMaterial(“assets/TreeBigFront.png”);
myBitmapFileMaterial.doubleSided = true;

myBitmapFileMaterial2 = new BitmapFileMaterial(“assets/TreeBigSide.png”);
myBitmapFileMaterial2.doubleSided = true;

//Add a material list
var materialsList:MaterialsList = new MaterialsList();
materialsList.addMaterial(myBitmapFileMaterial, “front” );
materialsList.addMaterial(myBitmapFileMaterial2, “right” );

//plane = new Plane(material applied to object, width, height, wSegments, hSegments);
mySLTree = new Tree(materialsList,400,400,400,4,4,4);
scene.addChild(mySLTree);
startRendering();

}
//Override protected function
override protected function onRenderTick(event:Event=null):void
{
//Rotate the hello plane primitive.

mySLTree.rotationY+=2;
//mySLTree.rotationX+=2;
super.onRenderTick(event);
}
}
}

One Response to Second Life Tree in Papervision3D

  1. […] Create a Tree Primitive (see the Post on Second Life Tree) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: