Actionscript 3: translating coordinates from object's 3D space to another's? - actionscript-3

I feel like this has probably been asked/answered here, and if so, I apologize for the bandwidth, but I don't see any explanation.
How does one translate from one object's coordinate space to another in Flash AS3? I can take a point in an object and translate it to global coordinates using local3DToGlobal() and then to another object's local using globalToLocal3D() -- but is there a direct way?
Thus, if I wanted one object to be able to say to another: 'move your top left corner to my top left corner', even through the two objects are in different z-spaces, rotated 3-dimensionally, etc.
I assume it is in the matrix3D matrix manipulations —
Matrix multiplication? TransformVector()? deltaTransformVector()?
I have been poring over the API but would really appreciate a concrete example.
Thanks!

One approach would be getRelativeMatrix3D(), called from the transform property of a display object, as in: transform.getRelativeMatrix3d(root).position.
Returns a Matrix3D object, which can transform the space of a
specified display object in relation to the current display object's
space. You can use the getRelativeMatrix3D() method to move one
three-dimensional display object relative to another three-dimensional
display object.
From Adobe's Performing complex 3D transformations, there is an example using Matrix3D objects for reordering display, in which faces of a box are reordered to ensure that layering of 3D display objects corresponds to the relative depths after rotations have been applied:
var faces:Array;
for (var i:uint = 0; i < 6; i++)
{
faces[i].z = faces[i].child.transform.getRelativeMatrix3D(root).position.z;
this.removeChild(faces[i].child);
}
faces.sortOn("z", Array.NUMERIC | Array.DESCENDING);
for (i = 0; i < 6; i++)
{
this.addChild(faces[i].child);
}

Related

ActionScript 3: Zoom into movieclip while not scaling its childrens

I've included a zoom functionality similar to the one explained at this website:
http://www.flashandmath.com/howtos/zoom/
This works perfectly on my background image(a map, that is), but I want to keep the symbols on my map the same size while zooming in.
I probably could work this out by changing all the children's size when calling the zoom-function, but I am hoping there is some kind of easy code adapt in my children class to make the size of the instances unchangable. Is there?
Thanks!
One crude way, so you don't have to calculate the symbols scale, would be to remove the symbols from the mapDisplayObject so they're no longer a child and instead put symbol placeholders. Then match each symbol's x and y to each place holder, using localToGlobal...
If your children are not scaled or skewed or rotated you can iterate all of them and set transformation matrix to 1/parentScale. Something like:
for each (var child:DisplayObject in parent) {
var matrix:Matrix = child.transform.matrix;
matrix.a = 1/parentScale;
matrix.d = 1/parentScale;
child.transform.matrix = marix;
}

Masking Many Objects With 1 Mask Using `getChildAt(i).mask` Not Working

I am attempting to apply a mask to all objects on the stage except for a couple. There are a lot of different objects, and the amount of them will change in the future, so I want the masking to be done dynamically.
I wrote this code:
var i;
for (i = 0; i < this.numChildren; i++) {
if (this.getChildAt(i).name!="stage_kelp_bg" && this.getChildAt(i).name!="magnifier_mask") {
this.getChildAt(i).mask = this.magnifier_mask;
}
}
The above code is inside the document class's constructor method. Simply stating something like:
this.stage_kelp.mask = this.magnifier_mask;
works flawlessly, but only for that one object. Any idea what's wrong?
No errors are thrown, the objects just simply don't get masked.
Further research shows me that I cannot apply 1 mask to multiple objects. I have to have a mask for each object, or put all the objects into one container and mask that container.
Apparently you can use a layer to mask multiple objects on the timeline, but you can't do it programmatically without adding all the objects to one container. Unfortunately I can't do this without re-coding the entire application, so I will be using the timeline to mask things.
I would suggest you to better move all the movieclips to be masked in a single movieclip. This would be easier, if it's feasible in your case.
How about for each
for (var mc:movieClicp in this){
mc.mask=mask_}

localToGlobal/globalToLocal AS3 confusion

I want to move a display object from one container to another, but have it appear in the same place on screen.
I thought I'd understood this years ago, but the following does not work:
function moveToNewContainer(obj:DisplayObject, newParent:DisplayObjectContainer):void {
var pos:Point = new Point(obj.x, obj.y);
var currentParent:DisplayObjectContainer = obj.parent;
pos = currentParent.localToGlobal(pos);
currentParent.removeChild(obj);
newParent.addChild(obj);
pos = newParent.globalToLocal(pos);
obj.x = pos.x;
obj.y = pos.y;
}
This doesn't position the object in the same place as I would have expected.
Does anyone know what I am doing wrong, please?
Thanks,
James
Using localToGlobal/globalToLocal and setting the x and y properties like you showed calculates the correct position for the object in its new parent, but does not adjust for other aspects of the transformation such as scaling or rotation. In other words, the object's registration point will indeed remain in the same place, but the object may be rotated, scaled, or sheared differently.
The solution to your problem will need to take into account the transform.concatenatedMatrix properties of the old and new parents--you'll need to multiply the object's transformation matrix by one and then by the inverse of the other, or something along those lines. Leave a comment if you need help working out the math.
There is nothing wrong with your code, provided that both containers have no transformations applied. If your clips are scaled, rotated, etc.. you need to handle that in addition to the coordinate space transformations that localToGlobal and globalToLocal do.
You have to check if your containers are actually placed on stage. If your new container isn't added as a child to stage, function globalToLocal fails, just because it doesnt know how to correctly calculate that data.

AS3 - Using Matrix3D objects for reordering display

I'm working with about 20 objects that are moving around in 3D space. Adobe recommends "Using Matrix3D objects for reordering display":
Use the getRelativeMatrix3D() method of the Transform object to get the relative z-axes of the child 3D display objects.
Use the removeChild() method to remove the objects from the display list.
Sort the display objects based on their relative z-axis values.
Use the addChild() method to add the children back to the display list in reverse order.
Great. That's fine if the objects aren't moving. But what if there are animations happening and one object comes in front of another in z-space? The objects are displayed accoring to the position in the display list, not according to their z-order. How can you make objects respect z-order while animating (make object A appear in front of object B if object A's z-value becomes smaller than object B)? Obviously you can't clear the display list during an animation.
It's practically the same as adobe's docs says... and it works perfectly for moving objects.
The simplest way, so you have some code reference, and given you have all of your 3d objects in an array, would go something like this:
function zSort(objects:Array) {
objects.sortOn("z", Array.DESCENDING | Array.NUMERIC); // sort the array on the "z" property
for each(var clip:DisplayObject in objects) { //loop the array and add the childs in the corrected order...
addChild(clip);
}
}

Displaying 100 Floating Cubes Using DirectX OR OpenGL

I'd like to display 100 floating cubes using DirectX or OpenGL.
I'm looking for either some sample source code, or a description of the technique. I have trouble getting more one cube to display correctly.
I've combed the net for a good series of tutorials and although they talk about how to do 3D primitives, what I can't find is information on how to do large numbers of 3D primitives - cubes, spheres, pyramids, and so forth.
You say you have enough trouble getting one cube to display... so I am not sure if you have got one to display or not.
Basically... put your code for writing a cube in one function, then just call that function 100 times.
void DrawCube()
{
//code to draw the cube
}
void DisplayCubes()
{
for(int i = 0; i < 10; ++i)
{
for(int j = 0; j < 10; ++j)
{
glPushMatrix();
//alter these values depending on the size of your cubes.
//This call makes sure that your cubes aren't drawn overtop of each other
glTranslatef(i*5.0, j*5.0, 0);
DrawCube();
glPopMatrix();
}
}
}
That is the basic outline for how you could go about doing this. If you want something more efficient take a look into Display Lists sometime once you have the basics figured out :)
Just use glTranslatef (or the DirectX equivalent) to draw a cube using the same code, but moving the relative point where you draw it. Maybe there's a better way to do it though, I'm fairly new to OpenGL. Be sure to set your viewpoint so you can see them all.
Yeah, if you were being efficient you'd throw everything into the same vertex buffer, but I don't think drawing 100 cubes will push any GPU produced in the past 5 years, so you should be fine following the suggestions above.
Write a basic pass through vertex shader, shade however you desire in the pixel shader. Either pass in a world matrix and do the translation in the vertex shader, or just compute the world space vertex positions on the CPU side (do this if your cubes are going to stay fixed).
You could get fancy and do geometry instancing etc, but just get the basics going first.
This answer isn't just for OP's question. It also answers a more general question - displaying many cubes in general.
Drawing many cube meshes
This is probably the most naive way of doing things. We draw the same cube mesh with many different transformation matrices:
prepare();
for (int i = 0; i < numCubes; i++) {
setTransformation(matrices[i]);
drawCube();
}
/* and so on... */
The nice thing is that this is SUPER easy to implement, and it's not too slow (at least for 100 cubes). I'd recommend this as a starter.
The problem
Ok, but let's say you want to make a Minecraft clone, or at least some sort of project that requires thousands, if not tens of thousands of cubes to be rendered. That's where the performance starts to go down. The problem is that each drawCube() sends a draw call to the GPU, and the time in each draw call adds up, so that eventually, it's unbearable.
However, we can fix this. The solution is batching, a way to do only one draw call for all of the cubes.
Batching
We join all the (transformed) cubes into one single mesh. This means that we will have to deal with only one draw call, instead of thousands. Here is some pseudocode for doing so:
vector<float> transformedVerts;
for (int i = 0; i < numCubes; i++) {
cubeData = cubes[i];
for (int j = 0; j < numVertsPerCube; j++) {
vert = verts[j];
/* We transform the position by the transformation matrix. */
vec3 vposition = matrices[i] * verts.position;
transformedVerts.push(vposition);
/* We don't need to transform the colors, so we just directly push them. */
transformedVerts.push(vert.color);
}
}
...
sendDataToBuffer(transformedVerts);
If the cubes are moving, or one of the cubes is added or deleted, you'll have to recalculate transformedVerts and then resend it to the buffer - but this is minor.
Then at the end we draw the entire lumped-together mesh in one draw call, instead of many.