Introduction
A skybox is a simple device. Just a large low polygon cube that you put six panoramic images on-one for each side of the cube. Putting particles in the sky box adds a new dimension of fun and interactivity. But Papervision’s ParticleMaterial class only has two particles-a circle and square. So I added some of my own;
Stars – bursts – wedges – gears – polys
Below is a video on how I created this, demo site, source code download, background on its creation and source code swicth case that I added to Papervision’s ParticleMaterial class.
VIdeo Demo Code
YouTube:
Demo:
http://www.professionalpapervision.com/demos/web/skyboxstars/
Source: (SkyBoxandStars)
http://code.google.com/p/flex3cookbook1/downloads/list
Background
Recently, Seb Lee-Delisle did a skybox example in his presentation on Papervision3D Simplified-great presentation!
I downloaded his source code for the skybox and it didn’t work (probably because I was using a newer version of Papervision). So I rewrote it and added a few new features;
1. The planets now return when the cube gets too far out.
2. I added to the particle class. It now includes more than just square and circle particles. It has,
Stars – bursts – wedges – gears – polys
3. I got rid of the else if statements and added a switch case.
4. And I added multiple type planets to the skybox – starts – circles – burst. You can add more if you want.
You can catch Seb’s video and source code from his blog at
http://www.sebleedelisle.com/?p=263
I also encourage you to donate to Seb’s cancer run if you use his (or this post) material. I did!
http://www.sebleedelisle.com/?p=274
In my download, I included all the source code even the Papervision classes needed for the project to run.
ParticalMaterial Switch Case
The core replacement switch case that I wrote for Papervision’s ParticleMaterial class is given below;
switch(shape)
{
case SHAPE_SQUARE:
graphics.drawRect(renderrect.x, renderrect.y, renderrect.width, renderrect.height);
break;
case SHAPE_CIRCLE:
graphics.drawCircle(renderrect.x+renderrect.width/2, renderrect.y+renderrect.width/2, renderrect.width/2);
break;
case SHAPE_STAR:
var points:int=6;
var innerRadius:Number=renderrect.width/10;
var outerRadius:Number=renderrect.width*2;
var count:int = Math.abs(points);
if (count>=2)
{
// calculate distance between points
var step:Number = (Math.PI*2)/points;
var halfStep:Number = step/2;
// calculate starting angle in radians
var start:Number = (20/180)*Math.PI;
graphics.moveTo(renderrect.x+(Math.cos(start)*outerRadius), renderrect.y-(Math.sin(start)*outerRadius));
// draw lines
for (var i:int=1; i<=count; i++)
{
graphics.lineTo(renderrect.x+Math.cos(start+(step*i)-halfStep)*innerRadius,
renderrect.y-Math.sin(start+(step*i)-halfStep)*innerRadius);
graphics.lineTo(renderrect.x+Math.cos(start+(step*i))*outerRadius,
renderrect.y-Math.sin(start+(step*i))*outerRadius);
}
}
break;
case SHAPE_GEAR:
var gearPoints:int=5;
var ginnerRadius:Number=renderrect.width/2;
var gouterRadius:Number=renderrect.width;
var holeSides:int=4;
var holeRadius:Number=renderrect.width/4;
if (gearPoints>=2)
{
// calculate length of sides
var gearStep:Number = (Math.PI*2)/gearPoints;
var gearQtrStep:Number = gearStep/4;
// calculate starting angle in radians
var gearStart:Number = (20/180)*Math.PI;
graphics.moveTo(renderrect.x+(Math.cos(gearStart)*gouterRadius), renderrect.y-(Math.sin(gearStart)*gouterRadius));
// draw lines
for (var j:int=1; j<=gearPoints; j++)
{
graphics.lineTo(renderrect.x+Math.cos(gearStart+(gearStep*j)-(gearQtrStep*3))*ginnerRadius,
renderrect.y-Math.sin(gearStart+(gearStep*j)-(gearQtrStep*3))*ginnerRadius);
graphics.lineTo(renderrect.x+Math.cos(gearStart+(gearStep*j)-(gearQtrStep*2))*ginnerRadius,
renderrect.y-Math.sin(gearStart+(gearStep*j)-(gearQtrStep*2))*ginnerRadius);
graphics.lineTo(renderrect.x+Math.cos(gearStart+(gearStep*j)-gearQtrStep)*gouterRadius,
renderrect.y-Math.sin(gearStart+(gearStep*j)-gearQtrStep)*gouterRadius);
graphics.lineTo(renderrect.x+Math.cos(gearStart+(gearStep*j))*gouterRadius,
renderrect.y-Math.sin(gearStart+(gearStep*j))*gouterRadius);
}
if (holeSides>=2)
{
if(holeRadius == 0)
{
holeRadius = ginnerRadius/3;
}
gearStep = (Math.PI*2)/holeSides;
graphics.moveTo(renderrect.x+(Math.cos(gearStart)*holeRadius), renderrect.y-(Math.sin(gearStart)*holeRadius));
for (var k:int=1; k<=holeSides; k++)
{
graphics.lineTo(renderrect.x+Math.cos(gearStart+(gearStep*k))*holeRadius,
renderrect.y-Math.sin(gearStart+(gearStep*k))*holeRadius);
}
}
}
break;
case SHAPE_WEDGE:
// move into position
var arc:Number=40;
var radius:Number= renderrect.width*2;
var yRadius:Number= renderrect.width;
var wedgeStartAngle:Number=20;
graphics.moveTo(renderrect.x, renderrect.y);
// limit sweep to reasonable numbers
if (Math.abs(arc)>360)
{
arc = 360;
}
// Draw in a maximum of 45 degree segments. First we calculate how
// many segments are needed for our arc.
var segs:Number = Math.ceil(Math.abs(arc)/45);
// Now calculate the sweep of each segment.
var segAngle:Number = arc/segs;
// The math requires radians rather than degrees. To convert from degrees
// use the formula (degrees/180)*Math.PI to get radians.
var theta:Number =-(segAngle/180)*Math.PI;
// convert angle startAngle to radians
var angle:Number =-(wedgeStartAngle/180)*Math.PI;
// draw the curve in segments no larger than 45 degrees.
if (segs>0)
{
// draw a line from the center to the start of the curve
graphics.lineTo(renderrect.x+Math.cos(wedgeStartAngle/180*Math.PI)*radius,
renderrect.y+Math.sin(-wedgeStartAngle/180*Math.PI)*yRadius);
//draw curve segments
for (var m:int = 0; m<segs; m++)
{
angle += theta;
var angleMid:Number = angle-(theta/2);
graphics.curveTo(renderrect.x+Math.cos(angleMid)*(radius/Math.cos(theta/2)),
renderrect.y+Math.sin(angleMid)*(yRadius/Math.cos(theta/2)),
renderrect.x+Math.cos(angle)*radius, renderrect.y+Math.sin(angle)*yRadius);
}
//close the wedge by drawing a line to the center
graphics.lineTo(renderrect.x, renderrect.y);
}
break;
case SHAPE_POLY:
var polyPoints:int=7;
var myCount:int = Math.abs(polyPoints);
var polyAngle:Number=20;
var polyRadius:Number=renderrect.width;
if (myCount>=2)
{
// calculate span of sides
var polyStep:Number = (Math.PI*2)/polyPoints;
// calculate starting angle in radians
var polyStart:Number = (polyAngle/180)*Math.PI;
graphics.moveTo(renderrect.x+(Math.cos(polyStart)*polyRadius), renderrect.y-(Math.sin(polyStart)*polyRadius));
// draw the polygon
for (var n:int=1; n<=myCount; n++)
{
graphics.lineTo(renderrect.x+Math.cos(polyStart+(polyStep*n))*polyRadius,
renderrect.y-Math.sin(polyStart+(polyStep*n))*polyRadius);
}
}
break;
case SHAPE_BURST:
var binnerRadius:Number=renderrect.width/2;
var bouterRadius:Number=renderrect.width;
var bAngle:int=20;
var bPoints:int=7
if (bPoints >=2)
{
// calculate length of sides
var bStep:Number = (Math.PI*2)/bPoints;
var bHalfStep:Number = bStep/2;
var bQtrStep:Number = bStep/4;
// calculate starting angle in radians
var bStart:Number = (bAngle/180)*Math.PI;
graphics.moveTo(renderrect.x+(Math.cos(bStart)*bouterRadius), renderrect.y-(Math.sin(bStart)*bouterRadius));
// draw curves
for (var p:int=1; p<=bPoints; p++)
{
graphics.curveTo(renderrect.x+Math.cos(bStart+(bStep*p)-(bQtrStep*3))*(binnerRadius/Math.cos(bQtrStep)),
renderrect.y-Math.sin(bStart+(bStep*p)-(bQtrStep*3))*(binnerRadius/Math.cos(bQtrStep)),
renderrect.x+Math.cos(bStart+(bStep*p)-bHalfStep)*binnerRadius,
renderrect.y-Math.sin(bStart+(bStep*p)-bHalfStep)*binnerRadius);
graphics.curveTo(renderrect.x+Math.cos(bStart+(bStep*p)-bQtrStep)*(binnerRadius/Math.cos(bQtrStep)),
renderrect.y-Math.sin(bStart+(bStep*p)-bQtrStep)*(binnerRadius/Math.cos(bQtrStep)),
renderrect.x+Math.cos(bStart+(bStep*p))*bouterRadius,
renderrect.y-Math.sin(bStart+(bStep*p))*bouterRadius);
}
}
break;
default:
trace(“warning – Particle material has no valid shape.”);
break;
}
Hi Mike,
I was wondering if you could briefly explain how you made the skybox images, i.e. what softwares you used, what strategies you used to line up the images right, and how others could make them. That would be really helpful.
Thanks so much, I am very excited about your new book!
Best,
Lance
Thanks so much for your comment. This is such an important area that we are doing an entire video series on it coming out after winter break.
Essentially you have to use a stitching program.
Take some photos and then stitch them together and then you need to separate the image out into the different faces (if you are using a cube for example). We use PTGUI (for stitching) and Pano2QTVR (for separating out the images and vice versa).
Hope this helps,
Best,
Mike
Awesome, that sounds great. I’m totally new to this so I’ll have to look into what all that means, but I’m looking forward to your video series!
Thanks for such a quick response!
Best,
Lance
Hi again :)
I know this isn’t probably the best place to post this question, but I think you’re one of the only ones that can answer it right now…
I have been working on trying to make a Papervision Flex UIComponent so you can have interactive 3D Flex Components anywhere in your app. Here is the example with source:
http://www.systemsofseven.com/papervision/pv3d_flex/DocumentBased3D.html
The issue I’m running into is, after the component is loaded from the main stage to the MovieMaterial, I can’t get it back. Any suggestions on how that could be done? Everything I’ve done is in the source.
If you included something about creating Papervision Flex UIComponents in your book that would be amazing! There’s nothing out there on the internet like that yet!
Again, sorry for posting this on your comment wall, I couldn’t find your email on your site.
Thanks so much for all of your tutorials!
Best,
Lance
Hi Lance,
I totally love Flex so thanks for the question. What I have done in building Flex components is to include all the import statements in the Flex component – that way I can move them where ever I want at any time. I know this is a really simple unsophisticated approach – but that is typically how I do things.
However, panosalado at http://code.google.com/p/panosalado/ has built a FlexUI and it is open source – I’ve only glanced at it – but you might want to take look as well. As I get to the Flex part of the book, I will look at this more closely.
I am not sure if this answers your question – if not – please ask again.
Flex doesn’t have the same component set as Flash and as a result you need to use pv3Canvas.rawChildren.addChild(viewport), but it looks like you have already hopped over this barrier.
Best,
Mike
[…] find a whole lot of examples/tutorials about skyboxes in Papervision3D, I managed to find a skybox tutorial source learned how to do it. The tutorial I got the initial source from is actually about adding stars […]
Hello Mike,
Thanks for posting this excellent example, I’ve learned some of the basics from this example (skybox and particle fields).
I took the liberty of using your skybox approach and making a nice Skybox class out of it. I’m sharing this class in my blogpost (Easy Papervision3D skybox tutorial and source). Hope you don’t mind…