Sunday, August 30, 2009

3D Vector Drawing and Text with Papervision3D

The following article is an excerpt from Papervision3D Essentials and assumes that you have Papervision3D up and running on your computer. The first chapter of the book is a step-by-step introduction on how to download Papervision3D and how to configure Flash CS3, Flash CS4, Flex Builder or Flash Builder for creating Papervision3D projects.

The main part of this article is dedicated to a library called VectorVision that was incorporated into Papervision3D. After discussing the classes of this library, we will take a look at the Lines3D class in the next part that also enables you to draw 3D lines. This class was already a part of Papervision3D before VectorVision was incorporated.

VectorVision: 3D vector text and drawing

VectorVision is a library written in ActionScript that allows you to render vector graphics in Papervision3D and add a 3D perspective to them. The project started as a separate library that you could download and use as an add-on. However, it was fully integrated in Papervision3D in June 2008.

Being able to use vector shapes and text theoretically means that you could draw any kind of vector graphic and give it a 3D perspective. This article will focus on the features that are implemented in Papervision3D:

  • Creating 3D vector text
  • Drawing 3D vector shapes such as lines, circles, and rectangles

Keep in mind that 3D letters can be seen as vector shapes too, just like lines, circles, and rectangles. The above distinction is made based on how VectorVision is implemented in Papervision3D. Some classes specifically deal with creating 3D text, whereas others enable you to create vector shapes.

Creating a template class for the 3D text examples

Because the 3D text examples we are about to see have a lot in common, we will use a template class that looks as follows:

package
{
import flash.events.Event;
import org.papervision3d.materials.special.Letter3DMaterial;
import org.papervision3d.typography.Font3D;
import org.papervision3d.typography.Text3D;
import org.papervision3d.typography.fonts.HelveticaBold;
import org.papervision3d.view.BasicView;
public class Text3DTemplate extends BasicView
{
private var material:Letter3DMaterial;
private var font3D:Font3D;
private var text3D:Text3D;
private var easeOut:Number = 0.6;
private var reachX:Number = 0.5
private var reachY:Number = 0.5
private var reachZ:Number = 0.5;
public function Text3DTemplate()
{
stage.frameRate = 40;
init();
startRendering();
}
private function init():void
{
//code to be added
}
override protected function onRenderTick(event:Event = null):void
{
var xDist:Number = mouseX - stage.stageWidth * 0.5;
var yDist:Number = mouseY - stage.stageHeight * 0.5;
camera.x += (xDist - camera.x * reachX) * easeOut;
camera.y += (yDist - camera.y * reachY) * easeOut;
camera.z += (-mouseY * 2 - camera.z ) * reachZ;
super.onRenderTick();
}
}
}

We added some class properties that are used in the render method, where we added code to move the camera when the mouse moves. Also, we imported four classes and added three class properties that will enable us to create 3D text.

How to create and add 3D text

Let's see how we can create 3D vector text that looks crisp and clear. The general process of creating and displaying 3D text looks as follows:

  1. Create material with Letter3DMaterial.
  2. Create a Font3D instance.
  3. Create a Text3D instance, passing the text, font, and material to it, and add it to the scene or to another do3D.

We will create an example that demonstrates several features of Text3D:

  • Multiline
  • Alignment
  • Outlines

All the following code should be added inside the init() method. Before we instantiate the classes that we need in order to display 3D text, we assign a text string to a local variable.

var text:String = "Multiline 3D textnwith letter spacing,nline spacing,nand alignment ;-)";

Now, let's create a text material, font, and text. First we instantiate Letter3DMaterial, which resides in the org.papervision3d.materials.special package:

material = new Letter3DMaterial(0x000000);

The constructor of this class takes two optional parameters:


 

Parameter

Data type

Default value

Description

1

fillColor

uint

0xFF00FF

Defines the material color with a 24-bit hexadecimal value, which in turn defines the color of the text.

2

fillAlpha

Number

1

Sets the transparency of the material.


In our example, we created black text material with no transparency.

Next, we choose the font of our liking by instantiating one of the font classes. Papervision3D has four classes that represent the following fonts:

  • HelveticaBold
  • HelveticaLight
  • HelveticaMedium
  • HelveticaRoman

The classes have the same name as the font and are subclasses of Font3D. Later we will look at how we can use fonts other than these four, but for now we will pickHelveticaBold. The Font3D constructor does not have any parameters.

font3D = new HelveticaBold();

We then create a Text3D instance:

text3D = new Text3D(text,font3D,material);

Text3D has four parameters available, of which only the last one is optional.


 

Parameter

Data type

Default value

Description

1

text

String

Defines the text you want to display.

2

font

Font3D

Sets the font of the text.

3

material

Letter3DMaterial

Sets the text material.

4

name

String

null

An optional name for the Text3D instance.


Text3D is inherited from DisplayObject3D, so we can position, rotate, and scale the instance.

text3D.x = 800;
text3D.y = 400;
text3D.localRotationY = -30;
text3D.scale = 2;

But Text3D has more to offer, as it has the following properties that format the text:

  • align
  • letterSpacing
  • lineSpacing

The align property aligns the text to the left (default), right, or center, and takes a string:

  • "left"
  • "right"
  • "center"

The following code aligns the text to the right:

text3D.align = "right";

To set the amount of space that is distributed between all characters, you can setletterSpacing.

text3D.letterSpacing = -3;

Notice that we didn't import any classes from the flash.text package. Although these properties are named after Flash TextField properties, they are actually created and set in Text3D.

The lineSpacing property is the equivalent of leading in Flash's TextFormat class and defines the amount of vertical space between lines:

text3D.lineSpacing = -30;

Creating multiline text requires only a regular line break—n—to go to a new line.

You can also add an outline to the text. The outline is defined by three properties of the material such as line thickness, line alpha, and line color:

text3D.material.lineThickness = 2;
text3D.material.lineAlpha = 1;
text3D.material.lineColor = 0xFF0000;

Finally, we add the text3D instance to the scene:

scene.addChild(text3D);

Publishing this example should show you the text that we have passed, multilined, aligned to the right, and last but not least, in 3D:

The classes that we have described make it quite simple to get 3D text onto your screen. But what if we do not want to limit ourselves to the four font types that Papervision3D has incorporated?

Font creation

In order to use other fonts in Papervision3D, we need to create our own custom Font3D classes. Although this may sound pretty daunting, it is not hard at all, thanks to a tool by Mathieu Badimon, the developer of Five3D.

Five3D is a 3D engine written in ActionScript 3.0 that lets you create interactive vector-based 3D animations. See http://five3d.mathieu-badimon.com

In short, downloading the tool and placing in it the correct folder will create a new panel inside the Flash IDE. When the panel is opened, you can choose a font and the tool will generate an ActionScript class file that contains vector data about the font. The following screenshot shows the panel:

Go to http://code.google.com/p/five3d/downloads/list and download the latest release, which was FIVe3D_make_typography_v2.0.zip at the time of writing.

In side the ZIP file you will find a SWF and a PDF file. The PDF contains instructions on where to place the SWF and how to create a font file. Read these instructions carefully and create a file with the font of your choice.

In the instructions, a WindowSWF folder is mentioned. The path to this folder is as follows:
On Windows:
C:Program FilesAdobeAdobe Flash CS4enFirst RunWindowSWF
On Mac OS X:
UsersUsernameLibraryApplication SupportAdobeFlashCS4enConfigurationWindowSWF
Where it says FlashCS4 in the path, it can also be FlashCS3, depending on the version you use. Note that on Mac OS X the generated font file does not end up in the folder where you saved your FLA, but in the root drive Macintosh HD

When you have created a font file, which contains an ActionScript class, there are a few things to be done before we can use it. Let's assume we have exported a font file that contains data of the Courier font and thus is called Courier.as:

  • Create a folder named five3D inside your src folder. Inside the five3D folder, create a folder named typography. Save the font file in thetypography folder.
  • Open the font file and add the following import to the first line of the class it contains:
    • import org.papervision3d.typography.Font3D;
  • Extend the font class with the Font3D class, so that the first line of the class definition looks like this:
    • public class Courier extends Font3D{
  • Add the following three methods to the class definition:
    • {
      if(!__initialized)initialize();
      return __motifs;
      }
      override public function get widths():Object
      {
      if(!__initialized)initialize();
      return __widths;
      }
      override public function get height():Number
      {
      if(!__initialized)initialize();
      return __heights;
      }

The class is now compatible with Papervision3D. Let's return to our previous example where we instantiated the HelveticaBold font and see how we can get this new font working. First we import the font class:

import five3D.typography.Courier;

Then, all you have to do is alter the line that instantiates the font:

font3D = new Courier();

Publishing the file should now show you the font that you exported with the tool, in our example Courier.

For further reading about font creation, VectorVision, and Papervision3D, visit http://code.google.com/p/vectorvision/wiki/FontCreation

Text3DExample



Papervision3D Essentials
 
Papervision3D Essentials Create interactive Papervision 3D applications with stunning effects and powerful animations
  • Build stunning, interactive Papervision3D applications from scratch
  • Export and import 3D models from Autodesk 3ds Max, SketchUp and Blender to Papervision3D
  • In-depth coverage of important 3D concepts with demo applications, screenshots and example code.
  • Step-by-step guide for beginners and professionals with tips and tricks based on the authors’ practical experience
http://www.packtpub.com/papervision3d-essentials/book


Adding interactivity to 3D vector text and shapes

Adding interactivity to 3D vector objects basically works the same as adding interactivity to any other 3D object. You can use the InteractiveScene3DEvent. However, to increase the accuracy you should import VectorShapeHitTest, which is located in the org.papervision3d.core.render.command package, and add the following line at the top of your init() method:

VectorShapeHitTest.instance.assignViewport(viewport);

Another aspect worth taking a look at, is adding interactivity to 3D text. If you would try to add an event listener to a Text3D instance directly, you are out of luck. Adding interactivity is done by adding listeners to the letters of the text. Let's see how that works.

Adding interactivity to 3D text

Aga in, we will take the Text3DTemplate as our starting point. We will create a simple example with some 3D text and make it interactive. When the mouse hovers a letter, it will turn red and the color of all the other letters will change randomly. When the mouse leaves the letter, then the color of all letters will change randomly. First, we create some 3D text in the init() method.

var text:String = "Interactive Text3D";
material = new Letter3DMaterial(0xFFFFFF);
material.interactive = true;
font3D = new HelveticaBold();
text3D = new Text3D(text,font3D,material);
text3D.scale = 2;
scene.addChild(text3D);

Set the interactive property of the material to true to enable interactivity. Also, let the BasicView class know that you want to make the viewport interactive, by adding the super() call in the constructor and setting the fourth parameter to true.

super(stage.stageWidth,stage.stageHeight,true,true);

Before we add listeners and their associated methods, add the line we discussed in the previous section at the top of the init() method for more precise interactivity:

VectorShapeHitTest.instance.assignViewport(viewport);

Now, let's add a listener to each letter of the Text3D instance:

for each(var letter:VectorLetter3D in text3D.letters)
{
letter.addEventListener(InteractiveScene3DEvent.
OBJECT_OVER,overLetterListener);
letter.addEventListener(InteractiveScene3DEvent.
OBJECT_OUT,outLetterListener);
}

We use a for each loop to add an event listener to each letter. Letters in a Text3D instance are of the VectorLetter3D type, so you need to import this class, which can be found in the org.papervision3d.typography package. Text3D has a letters property, which is an array that holds the letters of the text.

The event handler that is associated with the OBJECT_OVER event looks like this:

private function overLetterListener(e:InteractiveScene3DEvent):void
{
for each(var letter:VectorLetter3D in text3D.letters)
{
if(letter != e.target)
{
letter.material = new Letter3DMaterial(Math.random()
* 0xFFFFFF);
letter.material.interactive = true;
}
else
{
e.target.material.fillColor = 0xFF0000;
}
}
}

Again we use a for each loop. Inside the loop, an if-else statement evaluates for each letter whether it is the one that has been hovered. If it is not, we assign a new material to the letter with a random color. If it is the letter that has been hovered over, then we change the fillColor property of its material to red. Notice that we also set the interactive property to true when we apply new material to the letters that haven't been hovered over.

If you only add target.material.fillColor = 0xFF0000; in the handler, the whole text would turn red on hovering a letter. This makes sense because all letters have the same material. That is why we applied a new material to the letters that haven't been hovered.

In the handler method that accompanies the OBJECT_OUT event, we give each letter a random color:

private function outLetterListener(e:InteractiveScene3DEvent):void
{
for each(var letter:VectorLetter3D in text3D.letters)
{
letter.material = new Letter3DMaterial(Math.random() * 0xFFFFFF);
letter.material.interactive = true;
}
}

The following screenshot shows what you should see when publishing this example. The letter the mouse is over should be red, the other letters should be randomly colored.

InteractiveText3DExample

Drawing vector shapes—lines, circles, and rectangles

The integrated VectorVision library not only lets you create 3D text, it also provides a VectorShape3D class that allows drawing basic vector shapes such as lines, circles, and rectangles. The shapes are initially drawn in 2D and then projected in 3D space.

Working with vector shapes requires two classes, VectorShapeMaterial—located in the org.papervision3d.materials.special package—and VectorShape3D - located in the org.papervision3d.objects.special package. To create a vector shape material, use the VectorShapeMaterial class.

var material:VectorShapeMaterial = new VectorShapeMaterial();

The constructor of this class does not take any parameters.

When you want to draw a shape, you must instantiate VectorShape3D. The following code draws a line:

var line:VectorShape3D = new VectorShape3D(material);
line.graphics.lineStyle(2,0x00CCFF);
line.graphics.beginFill(0x666699)
line.graphics.moveTo(-300,-300);
line.graphics.lineTo(300,-300);
scene.addChild(line);

The first line of code instantiates the VectorShape3D class, passing the material we just created. The code that then follows illustrates that vector drawing in Papervision3D is very similar to 2D drawing in Flash. The last line adds the vector shape to the scene.

In the VectorShape3D class, an instance of the Graphics3D class is created, which is the 3D equivalent of the Flash Graphics class. We can call the methods in theGraphics3D class the same way we call methods in the Graphics class. When you create an instance of VectorShape3D, you can draw lines, circles, ellipses, and rectangles using methods such as drawRect() and lineTo(). Compared to theGraphics class, Graphics3D is a little less elaborate because it does not provide methods to draw gradient lines or fills.

This is how you would draw a rounded rectangle:

var roundedRect:VectorShape3D = new VectorShape3D(material);
scene.addChild(roundedRect);
roundedRect.graphics.lineStyle(3,0xff0000);
roundedRect.graphics.beginFill(0x666699)
roundedRect.graphics.drawRoundRect(-100,-100,200,200,20,20);

You can also draw curved lines.

var curvedLine:VectorShape3D = new VectorShape3D(material);
scene.addChild(curvedLine);
curvedLine.graphics.lineStyle(2,0x00CCFF);
curvedLine.graphics.moveTo(-300,-300);
curvedLine.graphics.curveTo(0,-600,300,-300);

The following screenshots show a circle, a filled rounded rectangle, a line, and a curved line. The screenshot at the left shows a frontal view. In the screenshot at the right, the objects are rotated, offering a 3D perspective.

VectorShape3DExample


Summary

Papervision3D offers a set of easy-to-use classes to draw 3D vector shapes such as simple graphics and text. The classes were originally part of VectorVision, a separate project that was developed to create 3D vector text, but was integrated into Papervision3D.

3D vector graphics can be created similar to how the Flash drawing API works, with methods such as drawCircle() and lineTo(). The graphics, such as lines, circles, rectangles, and ellipses are drawn in 2D and can then be rotated to create a 3D illusion.

The 3D text classes allow you to create crisp looking multiline text with alignment, letter spacing, and line spacing. Although Papervision3D has only four built-in fonts, it is possible to create text with other fonts. An external tool has been discussed, which generates font classes, containing vector information about the font you want to use. These classes can easily be incorporated into your Papervision3D project.

You add interactivity to 3D text as well as other 3D vector shapes, similar to adding interactivity to any other 3D object.

In the next part, we will see how to draw lines with Lines3D and add interactivity to Lines3D lines.



Papervision3D Essentials
 
Papervision3D Essentials Create interactive Papervision 3D applications with stunning effects and powerful animations
  • Build stunning, interactive Papervision3D applications from scratch
  • Export and import 3D models from Autodesk 3ds Max, SketchUp and Blender to Papervision3D
  • In-depth coverage of important 3D concepts with demo applications, screenshots and example code.
  • Step-by-step guide for beginners and professionals with tips and tricks based on the authors’ practical experience
http://www.packtpub.com/papervision3d-essentials/book



8 comments:

  1. Hi!!
    very Thanks

    Make a new typography file.swf

    C:Program FilesAdobeAdobe Flash CS4enFirst RunWindowSWF
    -------------------------------------------
    C:\Documents and Settings\[user]\Local Settings\Application Data\Adobe\Flash CS4\en\Configuration\WindowSWF

    ReplyDelete
  2. Dear Jeff,

    I have your book and am trying to draw a scene which contains a sphere that is texture mapped with a BitmapFileMaterial of woodgrain and a red 2D rectangle.

    But when I add both objects to the scene, both end up painted red. When I add just the sphere, it is painted with the correct woodgrain texture.

    Is this a bug or am I overlooking something? I really need to be able to draw a mixed scene. Thanks!

    ReplyDelete
  3. Actually, they both end up red when the 2D rectangle's line color is set to red not its fill color.

    Actually, whenever I apply a linestyle to the 2D vector, it adds the same line to everything else.

    ReplyDelete
  4. Okay, I figured it out. If you don't want the linestyle to be applied to all subsequent triangles, including ones with bitmap materials, you have to set the linestyle back to -1 after you draw your curved line or ellipse or rectangle etc.

    ReplyDelete
  5. Hi,
    The code should look like:

    package five3D.typography {
    import org.papervision3d.typography.Font3D;
    public class MonotypeCorsiva extends Font3D {
    ....
    static public function initialize():void {
    ....
    }
    override public function get motifs():Object {
    if (!__initialized) initialize();
    return __motifs;
    }
    override public function get widths():Object {
    if (!__initialized) initialize();
    return __widths;
    }
    override public function get height():Number {
    if (!__initialized) initialize();
    return __heights;
    }
    ...

    ReplyDelete
  6. I thought it would be difficult to understand through this article but I got good results for that reason I recommended you this.

    ReplyDelete
  7. I was looking for something really different and I'm so happy because finally I could get what I wanted thanks so much for sharing this.

    ReplyDelete