AS3: localToGlobal with perspectiveProjection - actionscript-3

It seems that if a display object has a perspective projection applied to it, calling localToGlobal gives you the wrong coordinates. In the following code, I draw a rectangle, rotate it slightly about its X axis, then draw an oval inside it using coordinates derived from localToGlobal. It works fine, until I try to apply a perspective projection, then the coordinates are all wrong. Anyone know how to get around this problem?
var w:uint = 300, h:uint = 150;
var s:Sprite = new Sprite();
s.graphics.beginFill(0x000000);
s.graphics.drawRect(-w/2,0,w,h);
s.graphics.endFill();
s.x = 275; s.y = 200; s.z = 600;
s.rotationX = -45;
addChild(s);
var point00:Point = new Point(0,0);
var point0h:Point = new Point(0,h);
var midL:Point = new Point(-w/2,h/2);
var midR:Point = new Point(w/2,h/2);
/*var VP:PerspectiveProjection = new PerspectiveProjection();
VP.fieldOfView = 55;
var p:Point = new Point(275,100);
VP.projectionCenter = p;
s.transform.perspectiveProjection = VP;*/
var o:Shape = new Shape();
o.graphics.beginFill(0x00ff00);
o.graphics.drawEllipse(
s.localToGlobal(midL).x,
s.localToGlobal(point00).y,
s.localToGlobal(midR).x - s.localToGlobal(midL).x,
s.localToGlobal(point0h).y - s.localToGlobal(point00).y
);
addChild(o);

It seems that setting perspectiveProjection does not force the player to redraw the object, and localToGlobal depends on redraw for correct results. You can wait 1 tick like you found (setTimeout for 0ms or enterFrame for 1 frame will do it) or you can force the player to redraw using BitmapData/draw():
// ...
s.transform.perspectiveProjection = VP;
new BitmapData(1, 1).draw(s); // forces player to redraw the sprite
var o:Shape = new Shape();
o.graphics.beginFill(0x00ff00);
o.graphics.drawEllipse(
s.localToGlobal(midL).x,
s.localToGlobal(point00).y,
s.localToGlobal(midR).x - s.localToGlobal(midL).x,
s.localToGlobal(point0h).y - s.localToGlobal(point00).y
);
addChild(o);

I found a workaround that isn't terrible. All you have to do is put a slight delay before the localToGlobal calls and then they will return the right coordinates. That also fixes local3DToGlobal.

Related

Using text as a mask to add a gradient to the text in AS3

I want to apply a gradient to my text in Flash Develop and read that using a mask is the easiest way. Problem is that I can not get it to work. I tried to create a mask with just the textField and by adding it to a sprite, neither work.
Another strange thing is that the textmask will function as a mask for start title if I reverse them like:
starttitle.mask =textmask
When I try the code below nothing displays.
var format2:TextFormat = new TextFormat();
format2.size = 40;
format2.font = "Times New Roman";
format2.bold = true
starttitle = new TextField;
starttitle.defaultTextFormat = format2;
starttitle.text = "Memory Circles";
starttitle.textColor = 0xFF0000
starttitle.autoSize = "center";
starttitle.background = false;
starttitle.backgroundColor = 0xFF4242;
starttitle.x = (300 - starttitle.width * .5);
starttitle.y = (150 - starttitle.height * .5);
starttitle.mouseEnabled = false;
addChild(starttitle)
var textmask:Shape = new Shape
var fillType:String = GradientType.LINEAR;
var colors:Array = [0xFF0000, 0];
var alphas:Array = [1, 1];
var ratios:Array = [0, 255];
var gradientBoxMatrix:Matrix = new Matrix();
gradientBoxMatrix.createGradientBox(50,100,45,0,0);
var spreadMethod:String = SpreadMethod.REPEAT;
textmask.graphics.beginGradientFill(fillType, colors, alphas, ratios,
gradientBoxMatrix, spreadMethod);
textmask.graphics.drawRect(200,100,300,100)
addChild(textmask)
textmask.mask =starttitle
I read something about the font needing to be embedded but I'm using Times New Roman and thought this would be embedded. Also other fonts don't work.
Try to use bitmap caching on starttitle and textmask:
starttitle.cacheAsBitmap=true;
textmask.cacheAsBitmap=true;

Issues with BitmapData of a MovieClip with rotationY applied to it

So I have a simple file right now that eventually will take a movieclip and save it as an image. I am currently rotating the clip's y by 45 to give it kind of a 3D look and adding it back to the stage. I've tried googling and a couple different things, but I can't quite get it to work. The first thing I tried was getting the BitmapData of the movieclip I rotated. That flattened the clip:
test_mc.response_mc.rotationY = 45;
var mc:MovieClip = test_mc.response_mc;
var bmp:BitmapData = new BitmapData(mc.width, mc.height);
bmp.draw(mc);
var output:Bitmap = new Bitmap(bmp);
output.x = 270;
output.y = 191;
addChild(output);
The next thing I tried was getting the parent clip's BD. That gave it an angle... just the wrong one:
(same code as above with this line change)
var mc:MovieClip = test_mc;
Any thoughts here would be nice. I tried doing stuff with matrices, but had no luck. I also have images... just can't post them yet :(
Something else I've tried with no luck:
var target:DisplayObject = test_mc as DisplayObject;
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
var output:Bitmap = new Bitmap(cloneData);
**Another update**
So hopefully this will help a little. I was able to recreate what I was doing using Matrix3D.
test_mc.response_mc.rotationY = 45;
var matrix:Matrix3D = new Matrix3D();
matrix.prependRotation(45, Vector3D.Y_AXIS);
test2_mc.response_mc.transform.matrix3D = matrix;
test2_mc.response_mc.transform.matrix3D.appendTranslation(0, 0, 0);
And I've come closer to getting the bitmap to look correct (thank to a Mike Chambers blog).
var mcOffset:Matrix3D = test2_mc.response_mc.transform.matrix3D;
var rawMatrixData:Vector.<Number> = mcOffset.rawData;
var globalBounds:Rectangle = test2_mc.response_mc.getBounds(test2_mc);
var matrix:Matrix = new Matrix();
matrix.a = rawMatrixData[0];
matrix.c = rawMatrixData[1];
matrix.tx = test2_mc.response_mc.x - globalBounds.x;
matrix.b = rawMatrixData[4];
matrix.d = rawMatrixData[5];
matrix.ty = test2_mc.response_mc.y - globalBounds.y;
var bmp:BitmapData = new BitmapData(150, test2_mc.height);
test2_mc.response_mc.transform.matrix3D = null;
bmp.draw(test2_mc.response_mc, matrix);
test2_mc.response_mc.transform.matrix3D = mcOffset;
var output:Bitmap = new Bitmap(bmp);
Well I took it a step further back and went to the stage. For whatever reason trying to draw the parent (test_mc) of the rotated movie clip (response_mc) WON'T work, however drawing the stage and setting the bounds to the area of the parent DOES work. Here's my code (which is sadly very simple):
var bmp:BitmapData = new BitmapData(test_mc.width, test_mc.height);
bmp.draw(stage.getChildAt(0), new Matrix(1, 0, 0, 1, -test_mc.x, -test_mc.y));
var output:Bitmap = new Bitmap(bmp);

Why Box2D body doesn't collide?

I does some tests with Box2D and stuck with it.
Here is my body-construct code:
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.fixedRotation = true;
var center:Number = Consts.stageToB2(Consts.worldSize / 2);
bodyDef.position.Set(center, center);
var body:b2Body = physicWorld.CreateBody(bodyDef);
var shape:b2CircleShape = new b2CircleShape(Consts.stageToB2(w) * 0.5); // our monster is in circle shape.
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
body.CreateFixture(fixtureDef);
I created such two bodies, but they doesn't collide! the debugDraw also doesn't light up the bodies. but when I add an angular velocitiy for one of them:
body.SetAngularVelocity(Math.PI / 89);
They'll start collide. Could you explain What happens here?
I have a feeling that Box2D won't check for collisions between objects that haven't had any forces applied to them. It seems logical because why would the engine waste resources to check those objects if they're not doing anything.
If you're placing the objects atop each other and then expecting something to happen without a gravity set or applying any forces to the boxes, that could be why.
What about gravity? If they are not moving at all , even not falling , it seems like you don't have a gravity in your world..
private var gravity:b2Vec2 = new b2Vec2(0, 9.8);
...
private var your_world:b2World = new b2World(gravity, true)

Drawing a bitmap based on a transformed movieclip

I'm drawing bitmaps of movieclips which I then feed into my hittest function to test for collisions. However, I'm not quite sure how i would add to the code below to take into account and draw bitmaps for movieclips which have been scaled and/or rotated. The below code obviously only works for non-transformed movieclips. I've included in comments code which i've already tried but to no success.
When adding the drawn bitmap to the stage, no matter whether the movieclip in question is transformed or not, the drawn bitmap is "cut off" and incorrectly drawn - it appears to only draw a section of it. However, this does not particularly affect the collision testing for the non-transformed movieclips, but has an adverse effect on transformed movieclips.
All of the movieclips I want to be drawn have been created through the graphics property.
//for example:
var mymc:MovieClip = new MovieClip();
var g:Graphics = mymc.graphics;
g.moveTo(0,0);
g.lineTo(17.5,0);
g.lineTo(8.75,17.5);
g.lineTo(-8.75,17.5);
g.lineTo(0,0);
main code:
for each(var mc:MovieClip in impassable) {
//var bMatrix:Matrix = new Matrix();
//bMatrix.scale(mc.scaleX, mc.scaleY);
//bMatrix.rotate(mc.rotation * (Math.PI/180));
var bData:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
//bData.draw(mc, bMatrix);
bData.draw(mc);
var bitmap:Bitmap = new Bitmap(bData);
bitmap.x = mc.x;
bitmap.y = mc.y;
var HitTest:Number = newCollision(bitmap, centerX, centerY, 13.7);
Any thoughts? thanks
This function will create a BitmapData clone of a DisplayObject, taking into account its transform matrix, though it doesn't take into account bitmap filters. (Based on this answer.)
function createBitmapClone(target:DisplayObject):BitmapData {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
return cloneData;
}
When you call successive transforms on a Matrix, the ordering is very important and can really mess things up.
Luckily there is a helper method that allows you to specify translation, rotation and scaling in one go and avoid those issues - createBox
For your case, something like this:
var matrix:Matrix = new Matrix();
matrix.createBox(mc.scaleX, mc.scaleY, mc.rotation*Math.PI/180, 0, 0);
(the two zeros are for x and y translation)

Positioning movieclip with x and y coordinates

I have a strange problem. I have a two sprites that are added to other Sprite and I, with localToGlobal(), get their global x,y coordinates, but when I add another movieclip and try to move them to "global" coordinates on ENTER_FRAME event, they are locked on 0,0 of the stage??
I have traced coords and they are correct but no positioning... Why?
var point:Point = new Point();
addChild(pl_name);
pl_name.addEventListener(Event.ENTER_FRAME, mov);
function mov(e:Event):void
{
for (var i in players)
{
var gglobal:Point = players[i].localToGlobal(point);
pl_name.x = gglobal.x;
pl_name.y = gglobal.y;
trace ("nameX "+pl_name.x+" nameY "+pl_name.y);
}
}
The point you are giving it isnt set to anything before it is used. Try this.
point.x = players[i].x;
point.y = players[i].y;
var gglobal:Point = players[i].localToGlobal(point);