Dynamic text with phong shading using Stok3d

Tuesday, June 9th, 2009

Yesterday David DerSchmale Lenaerts released Stok3d into the wild. When he showed me the demos, I immediately felt (you guessed it) STOKED!

Naturally my first question was : "does it support transparency?!", and with a resounding yes, I just had to try this with text :D

I wanted to generate the texture maps dynamically. It's not quite there yet, but not bad for a quick experiment :)

This took three steps:

  1. Create the diffuse map by generating a text field, taking a bitmap data snapshot of it and adding some bevel to it.
  2. Create the normal map by repeating the previous step, only using Red and Blue colors, with slightly different bevel settings.
  3. Create the specular map by extracting "outlines" using convolution filter.

These three bitmaps look like this:

Here's the code for generating them:

Actionscript:
  1. var font_size:uint=140;
  2. var tf:TextFormat=new TextFormat("Arial",font_size,0xac8ea9,true);
  3. var diffuse:BitmapData=createTextLineBitmapDataFromTextFormat("STOK3D",tf);
  4. diffuse.applyFilter(diffuse,new Rectangle(0,0,diffuse.width,diffuse.height),new Point(),new BevelFilter(5,45,0xFFFFFF,1,0x00,1,10,10,3,3));
  5. _diffuse_map=new Bitmap(diffuse);
  6.  
  7. tf=new TextFormat("Arial",font_size,0xbf6cde,true);
  8. var normal:BitmapData=createTextLineBitmapDataFromTextFormat("STOK3D",tf);
  9. normal.applyFilter(normal,new Rectangle(0,0,normal.width,normal.height),new Point(),new BevelFilter(5,45,0x89a1f9,1,0xFF00FF,1,12,12,3,3));
  10. _normal_map=new Bitmap(normal);
  11.  
  12. tf=new TextFormat("Arial",font_size,0xFFFFFF,true);
  13. var specular:BitmapData=createTextLineBitmapDataFromTextFormat("STOK3D",tf);
  14. specular.applyFilter(specular,new Rectangle(0,0,normal.width,normal.height),new Point(),new BevelFilter(5,45,0x999999,1,0x00,1,0,0));
  15. specular.applyFilter(specular,new Rectangle(0,0,normal.width,normal.height),new Point(),
  16.                                             new ConvolutionFilter(3,3,new Array(0,20,0,20,-80,20,0,20,0),10));
  17. specular.applyFilter(specular,new Rectangle(0,0,normal.width,normal.height),new Point(),new BlurFilter(2,2,2));
  18. _specular_map=new Bitmap(specular);

Then I just grabbed Davids demo code and replaced his textures with mine... and voila:

click image for demo:


storm image from http://www.flickr.com/photos/digitaltool/2569963337/

For some reason the demo doesn't start everytime. Just refresh a few times and you should be STOKED! ;) Also, the specular lights go black sometimes. I'm convinced this is just due to my happy go lucky texture generation, but David claims he needs to look at his code.

check the demo

download the demo as project. This is just my demo file, you'll need to fetch the Stok3d code from google (see link above);

I have to say, Stok3d is pretty damn cool! Sweet work herr schmale :thumbsup: :pirate:

.

Verlet Text Effect

Thursday, June 4th, 2009

Here's a demo I made for my presentation at multi-mania. I played around with verlet integration before and have long wanted to try this technique on text. Once I got the vectorization going, it was just a matter of connecting all the points to "Verlet Sticks".

This turned out to be a little tougher than expected. The tricky thing about Verlet Sticks, is that if you connect the same Verlet Point to multiple Verlet Points, the calculations go berzerk, and your shape quickly vanishes from the canvas into the ether. So the challenge was to find a way to connect all the points of a vector shape, with a minimum number of "individual connections", yet in a way that maintains the integrity of the shape. Keyboard cat played me off multiple times in my attempts :D .

Finally, the solution I landed on, was to create a grid out of all the points. The idea is to create a boundary rectangle, which maintains it's shape via "cross sticks":

Then, create a grid using all the points of your vector shape(s), in such a way that each point connects to a subdivision of the outlines of that "boundary Rectangle":



In the image above you can see the "boundary rectangle" surrounding the vector shape. Then, each point is connected with a vertical and horizontal line. These verticals and horizontals are then connected to a "subdivided" boundary rectangle.



In the demo you can turn "render sticks" on and off to visualize the trick.

Strengths:

  • Can handle pretty much any set of shapes and subshapes

Weaknesses:

  • Due to the "Boundary Rectangle" the shapes "bouncing" is limited, a "T" for instance can't lie diagonally, it always ends up on one of the four sides of the rectangle.
  • The vertical and horizontal lines shouldn't have more than two subdivisions. To fix this, the code first checks for all points along the same x and y, then shifts any instances above 2 by .5 pixels each. It's practically invisible, but it's there.
  • Doesn't support transparency, I guess this could be done with a blendmode or so...
  • More complex shapes can still spazz out and disappear :(

Click to try out the demo here . Ignore the collision physics, it's CRAP! The effect works best with "pixel fonts". The controls are :

  • RENDER : renders the input text with the selected font
  • UPDATE : update one frame of animation (when stopped)
  • PLAY : updates the animation on an enter frame
  • BOUNCE : when the characters are just skidding the floor, spice things up by clicking this button

The code is DIRTY, but does the trick. I've uploaded the source for the brave (or desperate ;) ). It's the same project that I've been using for my last 5 or so blog posts, so there's a lot of redundant code in there. Download it here.

ext steps:

  • compose a clean "all purpose" class for generating such a grid
  • Render shapes with curves, using the drawing api
  • Use nicopteres vectorization code to do this with photos...
  • Improve the curves and optimization, again I think I can use some code from nicopetre

I just wanted to put this out there because, well, it's kind of nifty... More to come, enjoy!

.

3d Text by Extruding BitmapData Snapshots of Text

Tuesday, May 5th, 2009

Putting together a number of previous blog posts, here's the current state of my 3d text. In terms of speed and quality, it's a big improvement on the examples I was showing at FITC in Amsterdam, old example here.

So, the process is (links to blog posts with explanations and source code):

Here's some screen shots, click images for the demo :

The colors are random, use the widgets to choose a font, the text size and depth of the extrude.

On my machine the framerate is pretty steady in the 20's even with longer strings. The text box on the right displays the duration of the entire process described above.

Check out the demo

Download the source (I know, the project has the same name as the last 3, because it is, I've just added a bunch more code to it). Parts of it I'm quite happy with, others less so, but you know, there's always room for improvement ;)

My entry to Bit-101s 25 Lines competition

Wednesday, November 19th, 2008

I participated in the original one back at the were-here forums, and couldn't resist giving it a shot this time around as well :) If you don't know, this is what I'm talking about.

In the spirit of my upcoming FITC Talk, I naturally went for a text effect! Click the image to see it in action :



I started with a good 50 lines of code... I'm just getting my head around the tricks involved with using Graphics.drawTriangles(). Took a bit of effort to a) get it to work, b) get it down to 25 lines. Basically the code:

  • Creates a textfield
  • Grabs a bitmapData of it, this is the "material"
  • Creates a cylinder with a loop (vertices, triangles and uv data)
  • In an enterframe it:
    • Rotates a matrix in y and z
    • Transforms that rotation to the "mesh" and it to the perspective projection
    • Draws the triangles using the material as a bitmap fill

At the end, I got the line count down to the point where I could even add some glow and z rotation :o oh: The screenshot looks a bit "pixely", and it is, because the font is not embedded.

The 25lines website proclaims : "You probably don't want to share your code prematurely, but that's up to you", well.... Last time I was probably 60th out of 61 entries so I doubt this is gonna kill my chances :D I'll never match up to the math gurus out there, but hey, mine has a joke in it!

The code:

Actionscript:
  1. // 3 free lines! Alter the parameters of the following lines or remove them.
  2. // Do not substitute other code for the three lines in this section
  3. [SWF(width=800, height=380, backgroundColor=0x000044, frameRate=31)]
  4. stage.align = StageAlign.TOP_LEFT;
  5. stage.scaleMode = StageScaleMode.NO_SCALE;
  6. // 25 lines begins here!
  7.  
  8. var tf:TextField=new TextField();
  9. tf.defaultTextFormat=new TextFormat("Helvetica",120,0xFFFFFF,true);//Hopefully all machines have Helvetica...
  10. tf.text="Circular Logic Works Because ";
  11. tf.autoSize=TextFieldAutoSize.LEFT;
  12. tf.filters=[new GlowFilter(0x2222BB,.7,12,12,4,3)]
  13. var material:BitmapData=new BitmapData(tf.width+10,tf.height+10,false,0x000044);
  14. material.draw(tf,null,null,null,null,true);
  15. var vertices:Vector.<Number>=new Vector.<Number>(),t_vertices:Vector.<Number>=new Vector.<Number>(),points:Vector.<Number>=new Vector.<Number>(),triangles:Vector.<int>=new Vector.<int>(),uv:Vector.<Number>=new Vector.<Number>(),angle:Number=0,radius:Number=material.width/Math.PI/2,slices:uint=20,x_rot:Number=0,x_rot_speed:Number=1,y_rot:Number=-150,z_rot:Number=0,z_rot_dir:Number=-.1;
  16. for(var i:int=0;i<=slices;i++){
  17.     vertices.push(radius*Math.cos(angle*(Math.PI/180)),0,radius*Math.sin(angle*(Math.PI/180)),radius*Math.cos(angle*(Math.PI/180)),material.height,radius*Math.sin(angle*(Math.PI/180)));
  18.     uv.push(1-angle/360,0,0,1-angle/360,1,0);
  19.     angle+=(360/slices);
  20.     if(i>1)triangles.push(i*2-4, i*2-2, i*2-3,i*2-3, i*2-2, i*2-1);
  21. }
  22. triangles.push(i*2-4, i*2-2, i*2-3,i*2-3, i*2-2, i*2-1);
  23. addEventListener("enterFrame",function handleEnterFrame(e:Event=null) {
  24.     var transform_matrix:Matrix3D=new Matrix3D();
  25.     transform_matrix.prependTranslation(stage.stageWidth/2,stage.stageHeight/2-material.height/2,0);//maybe better to use a container?
  26.     transform_matrix.prependRotation(y_rot--, Vector3D.Y_AXIS);
  27.     transform_matrix.prependRotation((z_rot+=z_rot_dir), Vector3D.Z_AXIS);
  28.     if(z_rot>20 || z_rot<-20)z_rot_dir*=-1;
  29.     transform_matrix.transformVectors(vertices, t_vertices);
  30.     Utils3D.projectVectors(new Matrix3D(), t_vertices, points, uv);
  31.     graphics.clear();
  32.     graphics.beginBitmapFill(material);
  33.     graphics.drawTriangles(points, triangles, uv, TriangleCulling.POSITIVE);
  34. });
  35. // 25 lines ends here!

The source in an easier "cut and paste" format : circular_logic.txt

Again, the html page with the compiled swf in it : 25Lines.html

anyway, fingers crossed! Looking forwards to seeing the other entries :)