Posts Tagged ‘as3’

Reflection in AS3

Source code + Flash Develop project

Inspired by this tutorial on reflections in AS3 at adobe devnet, I decided to play with reflections in Actionscript3. As usual I can never follow a tutorial or step by step process without constantly trying to tweak it.

In my process of tweaking I found a slight short coming in the example given. While it does a fantastic job of handling standard DisplayObjects, I found that things get a little messed up when you add filters to these objects. Notice how the edges of the first image are cut off and sharp, whereas the the second image that accounts for the filters’ impact on DisplayObjects’ dimensions looks smooth:

Reflections cut-off

Filters cut-off

Reflections cut-off

Filters complete

In my efforts to remedy this problem I came across a very useful function for BitmapData called generateFilterRect(). Given a Rectangle and a BitmapFilter this function will return the Rectangle that would actually encompass the DisplayObject along with its filter. With a simple bit of iteration we can traverse an object’s list of filters and determine what the max dimensions will be. Here’s the function I added to take care of it:

  private function _createReflectionBitmapData(obj:Sprite):BitmapData {
      var filterRect:Rectangle;
      var width:Number = obj.width;
      var height:Number = obj.height;
      var bmd:BitmapData = new BitmapData(width, height, true, 0xffffff);
      var matrix:Matrix = new Matrix();
 
      // filters can cause a display object to render outside of its rectangle
      for each (var filter:BitmapFilter in obj.filters) {
        filterRect = bmd.generateFilterRect(bmd.rect, filter);
        width = filterRect.width > width ? filterRect.width : width;
        height = filterRect.height > height ? filterRect.height : height;
      }
 
      // create, invert, and position relfection bitmapdata
      bmd = new BitmapData(width, height, true, 0xffffff);
      matrix.createBox(1, -1, 0, (width - obj.width)/2, height - (height - obj.height)/2);
      bmd.draw(obj, matrix);
 
      return bmd;
    }

The end result is BitmapData that contains our inverted DisplayObject, now properly sized and positioned. We can now pass this into a Bitmap object and continue on with the code in the tutorial mentioned at the beginning of this post. Or… you can use my stripped down and less functional version to get a simple view of how reflections are achieved in AS3:

package
{
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.DisplayObject;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.filters.BitmapFilter;
  import flash.filters.GlowFilter;
  import flash.geom.Matrix;
  import flash.geom.Rectangle;
 
  /**
   * ...
   * @author Tony Lukasavage - SavageLook.com
   */
  public class Main extends Sprite
  {
    public function Main():void
    {
      if (stage) init();
      else addEventListener(Event.ADDED_TO_STAGE, init);
    }
 
    private function init(e:Event = null):void
    {
      removeEventListener(Event.ADDED_TO_STAGE, init);
 
      // create main glow circle
      var obj:Sprite = new Sprite();
      var matrix:Matrix = new Matrix();
      var radius:Number = 100;
      matrix.createGradientBox(radius*2, radius*2, Math.PI / 2);
      obj.graphics.beginGradientFill("linear", [0x888888, 0xffffff], [1, 1], [0, 255], matrix);
      obj.graphics.drawCircle(radius, radius, radius);
      obj.graphics.endFill();
      obj.x = stage.stageWidth / 2 - radius;
      obj.y = stage.stageHeight / 2 - radius*1.5;
      obj.filters = [new GlowFilter(0xffffff, 1, 20, 20, 2, 1)];
      this.addChild(obj);
 
      // create reflection
      var bmd:BitmapData = _createReflectionBitmapData(obj);
      var bitmapReflect:Bitmap = new Bitmap(bmd);
      bitmapReflect.x = stage.stageWidth / 2 - bitmapReflect.width/2;
      bitmapReflect.y = obj.y + radius*2;
      this.addChild(bitmapReflect);
 
      // create gradient for reflection
      var grad:Sprite = new Sprite();
      matrix.createGradientBox(bitmapReflect.width, bitmapReflect.height / 2, Math.PI / 2, 0, 0);
      grad.graphics.beginGradientFill("linear", [0xffffff, 0xffffff], [1, 0], [0, 255], matrix);
      grad.graphics.drawRect(0, 0, bitmapReflect.width, bitmapReflect.height);
      grad.x = bitmapReflect.x;
      grad.y = bitmapReflect.y;
      grad.cacheAsBitmap = true;
      bitmapReflect.cacheAsBitmap = true;
      bitmapReflect.mask = grad;
      this.addChild(grad);
    }
 
    private function _createReflectionBitmapData(obj:Sprite):BitmapData {
      var filterRect:Rectangle;
      var width:Number = obj.width;
      var height:Number = obj.height;
      var bmd:BitmapData = new BitmapData(width, height, true, 0xffffff);
      var matrix:Matrix = new Matrix();
 
      // filters can cause a display object to render outside of its rectangle
      for each (var filter:BitmapFilter in obj.filters) {
        filterRect = bmd.generateFilterRect(bmd.rect, filter);
        width = filterRect.width > width ? filterRect.width : width;
        height = filterRect.height > height ? filterRect.height : height;
      }
 
      // create, invert, and position relfection bitmapdata
      bmd = new BitmapData(width, height, true, 0xffffff);
      matrix.createBox(1, -1, 0, (width - obj.width)/2, height - (height - obj.height)/2);
      bmd.draw(obj, matrix);
 
      return bmd;
    }
  }
}

And thats the basics. If you want an in depth description of each step and how they are performed, I will again refer you to the terrific tutorial by Ben Pritchard. In the meantime, enjoy your new found knowledge and start putting it to use. Everyone else is. Seriously. Like every image anymore has a reflection under it. This is really starting to irk my “swim against the current” side. Oh well, nothing a glass of Jack can’t cure.

Is everyone doing this in Away3D? No? Well you can add me to the list soon ;)

Adobe Alchemy

Adobe Labs has a prerelease project called Alchemy, which allows you to compile C/C++ code into SWC libraries that are usable in your AS3 code. For a former C/C++ coder like myself, this is music to my ears, but those without that background might be wondering why the hell you would even bother. Well, there’s 2 major points to consider:

  • You’ll be able to use the existing mountains of C/C++ libraries in your AS3 without having to create a port.
  • To quote the Adobe Alchemy page:  (Its) ideally suited for computation-intensive use cases, such as audio/video transcoding, data manipulation, XML parsing, cryptographic functions or physics simulation, performance can be considerably faster than ActionScript 3.0…

Now before you go getting all excited to compile your favorite C/C++ library into an SWC, there are some things to consider:

  • The more OS and other library dependencies your compiling target has, the less likely it is to work.
  • This is a prerelease labs project, so expect bugs and lots of visits to the Alchemy forums.  This should probably not be used for production code.
  • Adobe has not made it clear whether or not they plan to continue development of Alchemy, or whether it will ever be rolled into an official release.

If that hasn’t scared you off I’d highly suggest going to the Alchemy project page to get your necessary downloads and then heading immediately to the “Getting Started” page to setup up your development environment.  See if you can get their basic stringecho.c program working.  Once you have built your environment and compiled your first SWC for use in your AS3 code, it’s time to actually build your own Alchemy version of a C/C++ library.  Here’s a few examples of libraries that have been successfully ported to AS3 via Alchemy:

aalib Ascii Art in Alchemy

aalib Ascii Art in Alchemy

Box2D Physics in Alchemy

Box2D Physics in Alchemy

Now remember how I said you would inevitably run into bugs?  Yeah, that’s gonna happen, it wasn’t just a maybe.  Well, here’s a list of bugs I’ve run into so far (in attempting to port IJG’s JPEG library) and what I had to do to work around them.  And by “work around them” I mean “what people on the Alchemy forums did to work around them.”

  • adl.exe stuck

    checking whether we are cross compiling... \
    $FLEX_HOME/bin/adl.exe c:\\cygwin/tmp/t35f0.0/app.xml \
    2> /tmp/adl.trace & echo $!

    Cygwin must installed at C:\cygwin because Swfbridge, which loads AIR apps on the fly during configure scripts, is hardcoded to reference C:\cygwin.

    If you are working on a Linux system and get a similar error, make sure that you can execute your $FLEX_HOME/bin/adl file successfully. The executable for the standard Flex 3.2 SDK does not include a valid Linux ADL, only Windows and Mac. For a Linux version, download the AIR SDK for Linux and use its ADL.

  • Bad regex in achack/gcc

    Compiler] Error #1084: Syntax error: expecting identifier before \
    leftbrace.
        91011.achacks.as, Ln 1, Col 18:
        package cmodule. {

    Change line 274 of $ALCHEMY_HOME/achacks/gcc in the following way:

    #if($o =~ /([^\.]*)/)
    if($o =~ /([^\/\.]+)(\..*)*$/)

    or just replace $ALCHEMY_HOME/achacks/gcc with this fixed version.

  • Missing asmachine.abc

    [Compiler] Error #1063: Unable to open file: \
    $ALCHEMY_HOME/avm2-libc/lib/asmachine.abc.

    Don’t compile shared libraries, only static. You can usually set this in a configure script using “–enable-shared=no –enable-static=yes”. You can also pass the “-static” option to gcc directly when compiling.

That’s the list so far, but I’m sure there’ll be more. When all else fails, be sure to check the your /tmp directory for log files. I hope its saves anyone reading this a few hours as thats how long it took me to track these all down as an Alchemy noob. If you have the brains, guts, and patience to churn out an Alchemy port of a C/C++ library, leave a comment and let me know about it. Hopefully if enough of us do some real head turning work with it Adobe will actually put some serious effort into an actual supported release. Keep your fingers crossed.

Box2DFlashAS3 v2.1a HelloWorld

Box2D demo

Box2D demo

Click the image above for the demo.
Click here or right click on the demo to view the source code.

Inspired by this clip of Box2D running on Android, I decided to dive into this 2D physics engine I have heard so much about.  While the version of Box2D I used is the AS3 version called Box2DFlashAS3, the original version is written in C++.  Basically it very simply lets you apply 2D physics to your objects, or “bodies,” in AS3.

Most of the examples and tutorials I saw were lacking 2 things:

  1. A way to apply sprites to my “bodies” without the use of a flash project (FLA) file.
  2. Code that was compatible with the latest version of Box2DFlashAS3, v2.1a at the time of this post.

So to resolve that situation, or to account for my search engine deficiency, I present the HelloWorld example from the Box2DFlashAS3 2.1a distribution modified to be pure AS3:

package{
package {
  /*
   * Tony Lukasavage - SavageLook.com - 8/18/2010
   * Box2DFlashAS3 2.1a HelloWorld example, minus the need for an accompanying FLA
   *
   * This the basic Box2DFlashAS3 HelloWorld.as file from the source distribution
   * with some adjustments made so that you do not need an FLA file to compile and
   * run the code.  A simple bonus for us pure AS3 guys.  Also a few minor modifications
   * are made to account for changes between version 2.0 and 2.1, like adding a type
   * for body definitions.  Finally, I threw in an click handler to toggle between
   * normal and debug drawing.
   *
   */
 
  import Box2D.Collision.*;
  import Box2D.Collision.Shapes.*;
  import Box2D.Common.Math.*;
  import Box2D.Dynamics.*;
 
  import __AS3__.vec.Vector;
 
  import com.adobe.viewsource.ViewSource;
 
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.geom.Matrix;
  import flash.text.TextField;
  import flash.text.TextFormat;
 
  [SWF(width="800", height="600", frameRate="30")]
  public class box2d extends Sprite
  {
    private var _world:b2World;
    private var _velocityIterations:int = 10;
    private var _positionIterations:int = 10;
    private var _timeStep:Number = 1.0 / 30.0;
    private var _showDebug:Boolean = true;
    private var _debugSprite:Sprite;
    private var _bodySprites:Vector. = new Vector.();
 
    // Box2D uses meters for measurement, AS3 uses pixels.  1 meter = 30 pixels
    public var _worldRatio:int = 30;
 
    public function box2d()
    {
      // Add event for main loop
      addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
      stage.addEventListener(MouseEvent.CLICK, onClick );
 
      // add background gradient
      var bg:Sprite = new Sprite();
      var matrix:Matrix = new Matrix();
      matrix.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI/2, 0, 0);
      bg.graphics.beginGradientFill("linear", [0x9999ff, 0xffffff], [1, 1], [0, 255], matrix);
      bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
      bg.graphics.endFill();
      addChild(bg);
 
      // Define the gravity vector
      var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
 
      // Allow bodies to sleep
      var doSleep:Boolean = true;
 
      // Construct a world object
      _world = new b2World(gravity, doSleep);
 
      // set debug draw
      var debugDraw:b2DebugDraw = new b2DebugDraw();
      _debugSprite = new Sprite();
      addChild(_debugSprite);
      debugDraw.SetSprite(_debugSprite);
      debugDraw.SetDrawScale(_worldRatio);
      debugDraw.SetFillAlpha(0.5);
      debugDraw.SetLineThickness(2);
      debugDraw.SetAlpha(1);
      debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
      _world.SetDebugDraw(debugDraw);
 
      // Vars used to create bodies
      var body:b2Body;
      var bodyDef:b2BodyDef;
      var boxShape:b2PolygonShape;
      var circleShape:b2CircleShape;
 
      // Adding sprite variable for dynamically creating body userData
      var sprite:Sprite;
      var groundHeight:int = 60;
 
      sprite = new Sprite();
      sprite.graphics.lineStyle(1);
      sprite.graphics.beginFill(0x444444);
      sprite.graphics.drawRect(-stage.stageWidth/2, -groundHeight/2, stage.stageWidth, groundHeight);
      sprite.graphics.endFill();
 
      bodyDef = new b2BodyDef();
      bodyDef.type = b2Body.b2_staticBody;
      bodyDef.position.Set(stage.stageWidth / _worldRatio / 2, (stage.stageHeight - sprite.height/2) / _worldRatio);
      bodyDef.userData = sprite;
      addChild(bodyDef.userData);
 
      boxShape = new b2PolygonShape();
      boxShape.SetAsBox(sprite.width/_worldRatio/2, sprite.height/_worldRatio/2);
 
      var fixtureDef:b2FixtureDef = new b2FixtureDef();
      fixtureDef.shape = boxShape;
      fixtureDef.friction = 0.3;
      fixtureDef.density = 0; // static bodies require zero density
 
      body = _world.CreateBody(bodyDef);
      body.CreateFixture(fixtureDef);
 
      // Add some objects
      for (var i:int = 1; i < 20; i++) {
        // create generic body definition
        bodyDef = new b2BodyDef();
        bodyDef.type = b2Body.b2_dynamicBody;
        bodyDef.position.x = Math.random() * 15 + 5;
        bodyDef.position.y = Math.random() * 10;
        var rX:Number = Math.random() + 0.5;
        var rY:Number = Math.random() + 1;
        var spriteX:Number = rX * 30 * 2;
        var spriteY:Number = rY * 30 * 2;
 
        // Box
        if (Math.random() < 0.5) {
          sprite = new Sprite();
          sprite.graphics.lineStyle(1);
          sprite.graphics.beginFill(0xff6666);
          sprite.graphics.drawRect(-spriteX/2, -spriteY/2, spriteX, spriteY);
          sprite.graphics.endFill();
          bodyDef.userData = sprite;
 
          boxShape = new b2PolygonShape();
          boxShape.SetAsBox(rX, rY);
 
          fixtureDef.shape = boxShape;
          fixtureDef.density = 1.0;
          fixtureDef.friction = 0.5;
          fixtureDef.restitution = 0.2;
 
          body = _world.CreateBody(bodyDef);
          body.CreateFixture(fixtureDef);
        }
        // Circle
        else {
          sprite = new Sprite();
          sprite.graphics.lineStyle(1);
          sprite.graphics.beginFill(0x44ff44);
          sprite.graphics.drawCircle(0, 0, spriteX/2);
          sprite.graphics.endFill();
          bodyDef.userData = sprite;
 
          circleShape = new b2CircleShape(rX);
 
          fixtureDef.shape = circleShape;
          fixtureDef.density = 1.0;
          fixtureDef.friction = 0.5;
          fixtureDef.restitution = 0.2;
 
          body = _world.CreateBody(bodyDef);
          body.CreateFixture(fixtureDef);
        }
 
        _bodySprites.push(bodyDef.userData as Sprite);
        addChild(bodyDef.userData);
      }
 
      // enable view source
      ViewSource.addMenuItem(this, "srcview/index.html");
      var text:TextField = new TextField();
      text.text = "Right click to view source";
      text.setTextFormat(new TextFormat("arial", 14, 0, true));
      text.x = 20;
      text.y = 20;
      text.width = 200;
      addChild(text);
    }
 
    public function onClick(e:MouseEvent):void {
      _showDebug = !_showDebug;
      if (!_showDebug) {
        _debugSprite.graphics.clear();
      }
      for each (var sprite:Sprite in _bodySprites) {
        sprite.visible = !_showDebug;
      }
    }
 
    public function Update(e:Event):void{
      _world.Step(_timeStep, _velocityIterations, _positionIterations);
      if (_showDebug) {
        _world.DrawDebugData();
      }
 
      // Go through body list and update sprite positions/rotations
      for (var bb:b2Body = _world.GetBodyList(); bb; bb = bb.GetNext()){
        if (bb.GetUserData() is Sprite){
          var sprite:Sprite = bb.GetUserData() as Sprite;
          sprite.x = bb.GetPosition().x * 30;
          sprite.y = bb.GetPosition().y * 30;
          sprite.rotation = bb.GetAngle() * (180/Math.PI);
        }
      }
    }
  }
}

Very cool stuff that adds lots of possibilities to your Flash projects.  I can’t wait to start playing with the more complex aspects like joints, buoyancy and breakable bodies.  More intensely awesome demos sure to follow.

Webcam Video in Actionscript3

Webcam test in AS3

Click the above picture for the webcam demo (webcam required).
Right click on the demo, or click here for the source code.

This is a pretty simple demo compared to some of my other stuff, but it’s a key point for a lot of them.  Knowing how to use the webcam independently of an existing library can open up lots of options beside augmented reality.  For that reason, I present this small demo showing off how easy it is to do so in Actionscript3.

Here’s a slightly modified excerpt from the full source code that is the meat and potatoes of the demo:

var camera:Camera = Camera.getCamera();
camera.setQuality(0,100);
camera.setMode(800,600,60,true);
 
var video:Video = new Video(800,600);
video.attachCamera(camera);
addChild(video);

As you can see, its as simple as creating a Camera object, attaching it to a Video object and adding the Video object to the main sprite.  The demo will show you how to manipulate image quality, camera frame rate, and viewport size.  For more details on all the properties and methods of each, check out the ASDocs on Camera and Video.

What can you do with this besides making yet another video chat application?  I don’t know about you, but I’m planning an Away3DLite project that will involve multiple levels of “reflection” based on the images from the webcam.  It will be awesome.  Now I just need to figure out how I’m going to do it.  Details…

Adobe Swatch Exchange (ASE) Files in AS3

SwatchLoader.as in action (click image for demo)

Download the AS3 Class: SwatchLoader.as
Source Code: View Source

SwatchLoader.as is an AS3 class for FP10 that loads Adobe Swatch Exchange (ASE) files into an object.  From this object you can access all group and color information for the swatch.  It handles RGB, CMYK, and grayscale color modes.

This demo was inspired by the work of Jerome at wemakedotcoms.com (AKA, elguapoloco at the Away3D dev list) who recently posted an Away3D site that used the Kuler RSS feed.  Sites like Kuler and ColourLovers provide community supported collections of free color swatches.  Though the idea of creating a swatch of 5 colors is simple, it acts as creative inspiration.  And as such I was inspired to use these swatches as themes or skins for future Flash projects.

kuler.adobe.com

Not wanting to be at the mercy of the RSS (Jerome mentioned that he had some responsiveness issues with it) I decided to write a parser for ASE.  While the format is relatively simple and can be used by almost any Adobe CS 3+ product, it is proprietary.  Fortunately I was pointed to this site, where the binary format of many image types can be found.  Armed with this second hand specification and my trusty hex editor, I churned out the finalized code in just a few hours.  Now it can successfully handle ASE formatted swatches from Kuler and ColourLovers.  In theory it should work for any ASE, but I have not yet tested it on anything but swatches from those 2 sites.

With a little luck (and free time) you’ll probably see this included in some of my demos in the near future.  Send a comment if you find it useful or if you run into any problems.

One last note, its not error that the ColourLovers’ swatch name is always “unknown.”  The ASE files generated by ColourLovers do not include the name of the swatch.

Flash Builder 4: addElement() instead of addChild()

Here’s a quick tip for those of you who are also switching to Flex 4.x/Flash Builder 4 from Flex 3.x/Flex Builder 3.  In the past when you wanted to add a Flash DisplayObject to the main Application canvas you’d wrap the DisplayObject in a UIComponent, then add the UIComponent to the Application as a child, like this:

var sprite:Sprite = new Sprite();
var ui:UIComponent = new UIComponent();
ui.addChild(sprite);
this.addChild(ui);

Try that in Flash Builder 4 and you’ll encounter a fun error that looks like this:

Error: addChild() is not available in this class. Instead, use
addElement() or modify the skin, if you have one.

To resolve it, simply change the last line of the previous code to use addElement() instead (change in red):

var sprite:Sprite = new Sprite();
var ui:UIComponent = new UIComponent();
ui.addChild(sprite);
this.addElement(ui);