**Intro**

After creating a million custom primitives in Papervision3D, I have always wanted to create just one that did it all using parametric equations. But many of the algorithms used to generate the basic primitives in Papervision3D (Away3D is a little better) are so complicated that its hard to bring them altogether under one roof.

It’s something you need to do from the start. And with many thanks to Petri Leskinen (and a little recoding on my part) we can now do that as shown in the figure below.

Earth Map Courtesy of NASA/JPL-Caltech

Petri on his blog Pixelero (http://pixelero.wordpress.com/) created a globe example which uses the parametric eqautaions of a sphere.

// polar coordinate:

var vc:Vector3D = new Vector3D(

** Math.cos(iy)*Math.cos(ix),
Math.sin(iy),
Math.cos(iy)*Math.sin(ix)**

);

// In the case of a sphere the normal is so easy:

vertexNormals.push(vc);

// the coordinate, scaled by radius

vertices.push(radius*vc.x, 1.05*radius*vc.y, radius*vc.z);

With a little recoding I was able to integrate a switch case and timer which iterates through the parametric equations of a plane, cylinder, cone, sphere, and half torus.

switch (number) {

case 0:

//Plane

paraVec = new Vector3D(-.5*(2-ix), .5*(iy),0);

break;

case 1:

//Cylinder

paraVec = new Vector3D(

Math.cos(ix), iy, Math.sin(ix));

break;

case 2:

//Cone

paraVec = new Vector3D(

(iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix));

break;

case 3:

//Sphere

paraVec = new Vector3D(

Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math.cos(iy)*Math.sin(ix));

break;

case 4:

//Torus

paraVec = new Vector3D(

(1+.4*Math.cos(iy))*Math.cos(ix), .4*Math.sin(iy), (1+.4*Math.cos(iy))*Math.sin(ix));

break;

default:

trace ( ” no case tested true ” )

}

The heart of the code runs on the Matrix3D class which does all the perspective scaling for you automatically. This is all explained in the book and its accompanying videos.

To see the complete code, click the more button below.

**Generalized (Super) Primitive**

package {

import flash.display.*;

import flash.geom.*;

import flash.events.Event;

import flash.utils.Timer;

import flash.events.TimerEvent;

public class GeneralizedPrimitive extends Sprite {

//Embed your texture

[Embed(source=”worldmapCourtesyNASAJPL.jpg”)]

private var YourImage:Class;

//Primitive Declarations

private var primitive:Sprite;

private var myTimer:Timer;

private var myCount:int=0;

private var paraVec:Vector3D;

private var bitmapData:BitmapData;

private var rows:int=32;

private var cols:int=16;

private var ix:Number;

private var iy:Number;

//Elliptical Parameters

private var radx:Number=1;

private var rady:Number=1;

private var radz:Number=1;

//Vertice Data

private var verts:Vector.<Number>;

private var indices:Vector.<int>;

private var uvtData:Vector.<Number>;

private var uvtShadingData:Vector.<Number>;

private var projectedVerts:Vector.<Number>;

private var perspective:PerspectiveProjection;

private var projectionMatrix:Matrix3D;

private var myAngle:Number = 0;

public function GeneralizedPrimitive():void {

var myTimer = new Timer(1800);

myTimer.addEventListener(“timer”, timerHandler);

myTimer.start();

}

//Handle Timer Event

private function timerHandler(event:TimerEvent):void {

myCount=myCount%5;

init(myCount);

myCount++;

}

private function init(number:Number):void {

//Instantiate parameters

paraVec = new Vector3D();

verts = new Vector.<Number>();

indices= new Vector.<int>();

projectedVerts = new Vector.<Number>();

uvtData = new Vector.<Number>();

uvtShadingData = new Vector.<Number>();

bitmapData = new YourImage().bitmapData;

//remove primitive from stage before adding the next one

if(primitive){

removeChild(primitive);}

addChild(primitive= new Sprite());

//Set position on stage

primitive.x=stage.stageWidth/2;

primitive.y=stage.stageHeight/3;

//Set perspective

perspective= new PerspectiveProjection();

perspective.fieldOfView = 35.0; // camera angle, in degrees

//Calculate Vertices

for (var i:int = 0 ; i!=rows; i++) {

ix= i/(rows-1)*Math.PI*2.0;

for (var j:int =0 ; j!=cols; j++) {

iy= (j/(cols-1)-0.5)*Math.PI;

// Primitive Selection Switch Case

switch (number) {

case 0:

//Plane

paraVec = new Vector3D(-.5*(2-ix), .5*(iy),0);

break;

case 1:

//Cylinder

paraVec = new Vector3D(

Math.cos(ix), iy, Math.sin(ix));

break;

case 2:

//Cone

paraVec = new Vector3D(

(iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix));

break;

case 3:

//Sphere

paraVec = new Vector3D(

Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math.cos(iy)*Math.sin(ix));

break;

case 4:

//Torus

paraVec = new Vector3D(

(1+.4*Math.cos(iy))*Math.cos(ix), .4*Math.sin(iy), (1+.4*Math.cos(iy))*Math.sin(ix));

break;

default:

trace ( ” no case tested true ” )

}

//Collect vetices in the verts Vector array

verts.push(radx*paraVec.x,rady*paraVec.y,radz*paraVec.z);

//Load uvt data

uvtData.push( i/(rows-1),j/(cols-1), 0.0);

//Initialize projected vertices

projectedVerts.push(0.0,0.0);

}

}

//Create indices

var ii:int =0;

for (var ik:int=0 ; ik!=rows-1; ik++) {

for (var jk:int=0 ; jk!=cols-1; jk++) {

indices.push( ii,ii+cols+1,ii+1,

ii+cols,ii+cols+1,ii++);

}

ii++;

}

stage.addEventListener(Event.ENTER_FRAME, loop);

}

private function loop(event:Event =null):void {

// Set up a viewpoint:

projectionMatrix = perspective.toMatrix3D();

projectionMatrix.prependTranslation(0,0,0.35);

myAngle+=2;

//Rotate Primtive

projectionMatrix.prependRotation( myAngle ,new Vector3D(0,1,0.0));

//Update projection vectors

Utils3D.projectVectors(projectionMatrix, verts,

projectedVerts, uvtData);

//Draw primtive

with (primitive.graphics) {

clear();

beginBitmapFill(bitmapData,null, false, false);

drawTriangles(projectedVerts, indices, uvtData,TriangleCulling.NEGATIVE);

endFill();

}

}

}

}

Just to echo what I said on ML, looks quite similar to http://blog.wbsv.ru/?p=345.

On the practical side, what would be the benefit of this? Perhaps if we could mix or morph two primitives together…

Here’s where this is going – this program is a stepping stone to a Blender import algorithm I’m completing today. It allows you to import your Blender models into CS4. I’ve got the Blender into CS4 working, I just now need to generalize the algorithm to work with any Blender model. Almost there … when this is complete, I will create similar algorithms for 3DSMax.

Thanks for the comment.

Ah, I did not realize it was NOT for papervision, I guess I needed to actually read it :) Ha, it’s kinda x2 funny given that I just did that ASE parser for CS4, http://makc3d.wordpress.com/2009/01/22/ase-parser-for-flash-10 with very similar code in the demo.

p.s. I think it’s (iy)*Math.cos(ix), iy, (iy)*Math.sin(ix)); not (iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix)); for the cone.

[…] in the previous Super Prim post, Petri Leskinen’s great work on Pixelero http://pixelero.wordpress.com/ was key to this […]

Hey Mike,

Do you perhaps know how to tween rotation/translation in 3D with Tweener using the Matrix3D’s appendRotation/appendTranslation properties? If we could do that you could animate some of the more simpler 3D transformations in straight Flash/Flex without papervision (carousel galleries, etc.) without using all of Papervision. Thanks so much, this stuff is a little over my head :).

Stoked to check out your book!

Lance