sydd
sydd

Reputation: 1946

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

i have lots of sprites arranged in 3D space, and their parent container has rotations applied. How do i reverse the sprites 3D rotation, that they always face the camera (Actionscript 3)?

heres a code to test it:

package{
import flash.display.Sprite;
import flash.events.Event;
public class test extends Sprite{

var canvas:Sprite = new Sprite();
var sprites:Array = []

public function test(){
    addChild(canvas)
    for (var i:int=0;i<20;i++){
        var sp:Sprite = new Sprite();
        canvas.addChild(sp);
        sp.graphics.beginFill(0xFF0000);
        sp.graphics.drawCircle(0,0,4);
        sp.x = Math.random()*400-200;
        sp.y = Math.random()*400-200;
        sp.z = Math.random()*400-200;
        sprites.push(sp);
    }
    addEventListener(Event.ENTER_FRAME,function():void{
        canvas.rotationX++;
        canvas.rotationY = canvas.rotationY+Math.random()*2;
        canvas.rotationZ++;
        for (var i:int=0;i<20;i++){
            //this is not working...
            sprites[i].rotationX = -canvas.rotationX
            sprites[i].rotationY = -canvas.rotationY
            sprites[i].rotationZ = -canvas.rotationZ
        }
    })
}
}
}

I am guessing i have to do some magic with the rotation3D matrices of the sprites... I've tried to implement this script: http://ughzoid.wordpress.com/2011/02/03/papervision3d-sprite3d/ , but had so success
Thanks for help.

Upvotes: 6

Views: 17128

Answers (4)

jackw11111
jackw11111

Reputation: 1547

enter image description here

As you can see in the picture above, the camera on the right is set to face the quad (on the left), so that their normals are exactly opposite. When the camera is rotated, for the quad to still have an opposite normal, the quad would have to be rotated in the opposite direction the same angle as the camera. So, I was able to produce the right effect simply by rotating the quad by -camera.Yawaround the y-axis.

Upvotes: 1

datenwolf
datenwolf

Reputation: 162194

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.

Upvotes: 27

sydd
sydd

Reputation: 1946

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;
    }
}
}

Upvotes: 2

NickHubben
NickHubben

Reputation: 81

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

Upvotes: 1

Related Questions