Reputation: 1946
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
Reputation: 1547
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.Yaw
around the y-axis.
Upvotes: 1
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
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
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