Inverting rotation in 3D, to make an object always face the camera?

Solution 1:

The easiest way to do this is "clearing" the rotational part of the transform matrix. Your typical homogenous transformation looks like this

| xx xy xz xw |
| yx yy yz yw |
| zx zy zz zw |
| wx wy wz ww | 

with wx = wy = wz = 0, ww = 1. If you take a closer look you'll see that in fact this matrix is composed of a 3x3 submatrix defining the rotation, a 3 subvector for the translation and a homogenous row 0 0 0 1

| R       T |
| (0,0,0) 1 |

For a billboard/sprite you want to keep the translation, but get rid of the rotation, i.e. R = I. In case some scaleing was applied the identity needs to be scaled as well.

This gives the following recipie:

d = sqrt( xx² + yx² + zx² )

| d 0 0 T.x |
| 0 d 0 T.y |
| 0 0 d T.z |
| 0 0 0   1 |

Loading this matrix allows you to draw camera aligned sprites.

Solution 2:

Apologies in advance for this being a suggestion, not a solution:

If your intent is simulate 3D spherical rotations, your easiest route would be to forego the 2.5D API and just use simple scaling and sin/cos calculations for the positioning.

A fun place to start: http://www.reflektions.com/miniml/template_permalink.asp?id=329

Solution 3:

I've solved it using Wikipedia, matrices and black magic. I choose to implement custom rotation instead of inverting the rotation of all objects. Heres the code if anyones interested:

package{
import flash.display.Sprite;
import flash.events.Event;

public class test extends Sprite{

private var canvas:Sprite = new Sprite();
private var sprites:Array = []
private var rotx:Number=0,roty:Number=0,rotz:Number=0;
private var mm:Matrix3 = new Matrix3();
public function test(){
    addChild(canvas);
    canvas.x = canvas.y = 230
    for (var i:int=0;i<30;i++){
        var sp:Sprite = new Sprite();
        canvas.addChild(sp);
        sp.graphics.beginFill(0xFF0000);
        sp.graphics.drawCircle(0,0,2);
        sp.x = Math.random()*200-100;
        sp.y = Math.random()*200-100;
        sp.z = Math.random()*200-100;
        sprites.push(sp);
        rotx=0.06; //from top to bottom
        //roty=0.1; //from right to left
        rotz=0.1; //clockwise
        mm.make3DTransformMatrix(rotx,roty,rotz);
    }
    addEventListener(Event.ENTER_FRAME,function():void{
        for (var i:int=0;i<sprites.length;i++){
            var s:Sprite = sprites[i];
            mm.rotateByAngles(s);
        }
    })
}
}
}

and the matrix3 class:

public class Matrix3{

    private var da:Vector.<Number>; // rows

    public function make3DTransformMatrix(rotx:Number,roty:Number,rotz:Number):void{
        var cosx:Number = Math.cos(rotx);
        var cosy:Number = Math.cos(roty);
        var cosz:Number = Math.cos(rotz);
        var sinx:Number = Math.sin(rotx);
        var siny:Number = Math.sin(roty);
        var sinz:Number = Math.sin(rotz);
        da =    new <Number>[
            cosy*cosz, -cosx*sinz+sinx*siny*cosz,  sinx*sinz+cosx*siny*cosz,
            cosy*sinz, cosx*cosz+sinx*siny*sinz , -sinx*cosz+cosx*siny*sinz,
            -siny    ,         sinx*cosy        ,        cosx*cosy         ];
    }

    public function rotateByAngles(d:DisplayObject):void{
        var dx:Number,dy:Number,dz:Number;
        dx = da[0]*d.x+da[1]*d.y+da[2]*d.z;
        dy = da[3]*d.x+da[4]*d.y+da[5]*d.z;
        dz = da[6]*d.x+da[7]*d.y+da[8]*d.z;
        d.x = dx;
        d.y = dy;
        d.z = dz;
    }
}
}