Clickable Button Panel Primitive

Intro

Custom primitives are unexplored territory in Papervision. And they pack a big punch. To illustrate the point, consider the following custom clickable button panel primitive. From the image below you see 8 planes in two rows. Typically you rez planes on the stage one at a time and keep track of them individually as they rotate.

But with a custom primitive it’s all one primitive, which reduces the number of primitives you have to keep track of to one.

Click the image below to see a demo.

Clickable Custom Button Panel

Clickable Custom Button Panel

In the demo above, when you click on a plane a URL is brought up in the browser. Each plane calls a different URL.

Even cooler – you have total control over the number of rows, columns and spacing. The custom primitive is called

MultMatPlane

You place it in the primitives folder and rez it to the state the same way you would any other primitive and adjust its properties in the same way as well.

The primitive function look like this

MultMatPlane( materials:MaterialsList, myGap:Number=0,horizNum:int=1, vertiNum:int=1, width:Number=0, height:Number=0, segmentsW:Number=0, segmentsH:Number=0, initObject:Object=null )

The adjustable parameters are

  • materials
  • gap
  • column number
  • row number
  • image width
  • image height
  • width segments
  • height segments

Of course the important part in changing row and column parameter is to have the materials labeled correctly in the materials list. If the materials aren’t in the materials list – you won’t see them. That makes sense, but I’ve already made the mistake 100 times. And you should probably know about it. More on how this works below.

Note of Caution

One more thing – late last night this primitive didn’t like my Firefox pop up blocker (well at least not at 2 o’clock in the morning) – so keep an eye on that!

Any primitive as new as this one has to go through a few cycles of work to make sure it is fully functional for web use; this one has not been through those cycles yet!!! But it is being used successfully on a website – which is encouraging. The purpose of this tutorial is to demystify how such primitives are built – not beat all the bugs out. That comes with time and use.

Web Stuff

Demo: http://www.professionalpapervision.com/demos/web/buttonpanel/

Code: http://code.google.com/p/flex3cookbook1/downloads/list

(Download ButtonPanelPrimitive.zip from the list)

Youtube:

How it was made

Building this primitive was really easy. Here are the steps and stuff to look out for:

  1. Make a copy of the plane primitive and rename its class and constructor function to MultMatPlane (or whatever you want to call it)
  2. In the public class MultMatPlane, add the new properties that you need such as row number, column number, materials, … whatever you want.
  3. Then make sure those variables are carried over in your constructor function.
  4. Open up the cube primitive and see how the different sides were made. You will need to follow the same pattern, but generalize it for multisides and different geometries.
  5. After porting the cube code that you need, create and call the buildMultiPlane function – this is the heart of your class. It iterates through your column and row sizes placing them on the screen, shown below:

private function buildMultiPlane(myGap:Number, horizNum:Number, vertiNum:Number, width:Number, height:Number ):void
{

for(var n:int=0; n <vertiNum; n++){

for(var m:int=0; m <horizNum; m++){

var myMaterial:String = “myMat”+””+n+””+m;

trace(“myMat”+””+n+””+m);

buildPlane( myMaterial, myGap, m, n, horizNum, vertiNum, width, height) ;
trace(“makingPlane”+””+n+””+m);
}
}

5. Keep the code very similar to the cube code port, only replace all u variables with x variables and v variables with y variables.

6. And here is the big trick. Replace the plane vertices.push( new Vertex3D( x, y, 0 ) ) with the cube primitive technique for dealing with vertices as shown below. Make sure you put x in for u and y in for v.

var vertex:Vertex3D = new Vertex3D();

vertex[ “x” ] = x;
vertex[ “y” ] = y;
vertex[ “z” ] = 0;

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

And that is pretty much it! A little bit of work here, but now you are set up to do so much more. Like make single primitive carousels, images balls, and terrains. An all-in-one primitive. How cool is that?

Material List

So how do your get different images on the different planes in your custom primitive?

Use a materials list!

Here is an example of such a list from your main code:

var materialsList:MaterialsList = new MaterialsList();
materialsList.addMaterial(myBitmapFileMaterial00, “myMat00”);
materialsList.addMaterial(myBitmapFileMaterial01, “myMat01”);
materialsList.addMaterial(myBitmapFileMaterial02, “myMat02”);
materialsList.addMaterial(myBitmapFileMaterial03, “myMat03”);
materialsList.addMaterial(myBitmapFileMaterial10, “myMat10”);
materialsList.addMaterial(myBitmapFileMaterial11, “myMat11”);
materialsList.addMaterial(myBitmapFileMaterial12, “myMat12”);
materialsList.addMaterial(myBitmapFileMaterial13, “myMat13”);

The big thing was coming up with a naming conventions. For the cube it is top, right, left, top,…

But in this case I just numbered them by row and column, where myMatt00 is the lower left most button.

Interactivity

Making the plane interactive is not difficult. Give each of your materials a name and use a switch case to call that name when a plane is clicked on:

Name it like this!


myBitmapFileMaterial00.name = “myMat00”;

Call it like this!

switch(param1.face3d.material.name)
{
case “myMat00”:
{
navigateToURL(new URLRequest(“http://www.bible.com/&#8221;), “_blank”);
break;
}// end case …

That’s pretty much it! To check out the rest of the code by downloading it from the link above. And the MultMatPlane code is given below.

MultMatPlane Code

Get the MultMatPlane code under the more tab below. Note: The code is not fully documented. I’ll take care of that before the book comes out.

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;

//He that refuseth instruction despiseth his own soul: but he that heareth reproof getteth understanding.

/**
* The Plane 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 plane in the direction of the perspective or vanishing point, helps to reduce this problem. Perspective distortion dissapears when the plane is facing straignt to the camera, i.e. it is perpendicular with the vanishing point of the scene.
*/
public class MultMatPlane extends TriangleMesh3D
{
/**
* Number of segments horizontally. Defaults to 1.
*/

public var segmentsW :Number;

/**
* Number of segments vertically. Defaults to 1.
*/
public var segmentsH :Number;

public var myGap:Number;

public var horizNum:int;
public var vertiNum:int;

/**
* Default size of Plane if not texture is defined.
*/
static public var DEFAULT_SIZE :Number = 500;

/**
* Default size of Plane if not texture is defined.
*/
static public var DEFAULT_SCALE :Number = 1;

/**
* Default value of gridX if not defined. The default value of gridY is gridX.
*/
static public var DEFAULT_SEGMENTS :Number = 1;

// ___________________________________________________________________________________________________
// 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 Plane object.
* <p/>
* @param material A MaterialObject3D object that contains the material properties of the object.
* <p/>
* @param width [optional] – Desired width or scaling factor if there’s bitmap texture in material and no height is supplied.
* <p/>
* @param height [optional] – Desired height.
* <p/>
* @param segmentsW [optional] – Number of segments horizontally. Defaults to 1.
* <p/>
* @param segmentsH [optional] – Number of segments vertically. Defaults to segmentsW.
* <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 MultMatPlane( materials:MaterialsList, myGap:Number=0,horizNum:int=1, vertiNum:int=1, width:Number=0, height:Number=0, segmentsW:Number=0, segmentsH:Number=0, initObject:Object=null )
{
super( materials.getMaterialByName( “all” ), new Array(), new Array(), null, initObject );

this.materials = materials;

this.segmentsW = segmentsW || DEFAULT_SEGMENTS; // Defaults to 1
this.segmentsH = segmentsH || this.segmentsW; // Defaults to segmentsW

this.myGap=myGap;

this.horizNum=horizNum;
this.vertiNum=vertiNum;

var scale :Number = DEFAULT_SCALE;

if( ! height )
{
if( width )
scale = width;

if( material && material.bitmap )
{
width = material.bitmap.width * scale;
height = material.bitmap.height * scale;
}
else
{
width = DEFAULT_SIZE * scale;
height = DEFAULT_SIZE * scale;
}
}

buildMultiPlane(myGap,horizNum,vertiNum, width, height );

}

private function buildMultiPlane(myGap:Number, horizNum:Number, vertiNum:Number, width:Number, height:Number ):void
{

for(var n:int=0; n <vertiNum; n++){

for(var m:int=0; m <horizNum; m++){

var myMaterial:String = “myMat”+””+n+””+m;

trace(“myMat”+””+n+””+m);

buildPlane( myMaterial, myGap, m, n, horizNum, vertiNum, width, height) ;
trace(“makingPlane”+””+n+””+m);
}
}

mergeVertices();

this.geometry.ready = true;

}

private function buildPlane(myMaterial:String, myGap:Number, m:int, n:int, horizNum:int, vertiNum:int, width:Number, height:Number ):void
{

var matInstance:MaterialObject3D;
if( ! (matInstance= materials.getMaterialByName( myMaterial )))
{
if(!(matInstance=materials.getMaterialByName( “all” ))){
Papervision3D.log( “MultiPlane: Required material not found in given materials list. Supported materials are: front, back, right, left, top, bottom & all.” );
return;
}
}

var gridX :Number = this.segmentsW;
var gridY :Number = this.segmentsH;
var gridX1 :Number = gridX + 1;
var gridY1 :Number = gridY + 1;

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

var textureX :Number = width/2;
var textureY :Number = height/2;

var iW :Number = width / gridX;
var iH :Number = height / gridY;

// Vertices
for( var ix:int = 0; ix < gridX + 1; ix++ )
{
for( var iy:int = 0; iy < gridY1; iy++ )
{
var x :Number = ix * iW -(textureX+myGap/2)*(horizNum/2-m);
var y :Number = iy * iH -(textureY+myGap/2)*(vertiNum/2-n);

var vertex:Vertex3D = new Vertex3D();

vertex[ “x” ] = x;
vertex[ “y” ] = y;
vertex[ “z” ] = 0;

vertices.push( vertex );
planeVerts.push( vertex );
trace(“vertex land”);

}
}

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

for( ix = 0; ix < gridX; ix++ )
{
for( iy= 0; iy < gridY; iy++ )
{

trace(“UV Land”);
// Triangle A
var a:Vertex3D = planeVerts[ ix * gridY1 + iy ];
var c:Vertex3D = planeVerts[ ix * gridY1 + (iy+1) ];
var b:Vertex3D = planeVerts[ (ix+1) * gridY1 + iy ];

uvA = new NumberUV( ix / gridX, iy / gridY );
uvC = new NumberUV( ix / gridX, (iy+1) / gridY );
uvB = new NumberUV( (ix+1) / gridX, iy / gridY );

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

// Triangle B
a = planeVerts[ (ix+1) * gridY1 + (iy+1) ];
c = planeVerts[ (ix+1) * gridY1 + iy ];
b = planeVerts[ ix * gridY1 + (iy+1) ];

uvA = new NumberUV( (ix+1) / gridX, (iy+1) / gridY );
uvC = new NumberUV( (ix+1) / gridX, iy / gridY );
uvB = new NumberUV( ix / gridX, (iy+1) / gridY );

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

}
}
}

2 Responses to Clickable Button Panel Primitive

  1. Great tutorial again. We added your website to category 3D-Papervision.

  2. […] is essentially a combination of the button panel and panorama discussed in previous posts. But if you put them together they will not work. The […]

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: