Depth Sorting

Depth sorting, or z-sorting, is something weve already discussed in Chapter 15, when you were just applying perspective to movie clips. In that case, you took the negative of the z position and used that in swapDepths . Thus movie clips with a higher z (further away) were swapped to positions behind those with a lower z value (closer to the viewer).

At this point however, you are not dealing with multiple movie clips. All of the polygons are being drawn within the same movie clip (in this case the main timeline, or _root ). Thus, whenever a particular polygon is drawn, it will be drawn on top of any that have been drawn earlier. So, rather than swapping anythings depth, you just need to determine which polygon gets drawn when. Specifically, you want to draw the ones that are farthest away first; then you draw the rest, working your way forward, so that the closest polygons are drawn last, covering anything they might be in front of.

So how do you do that? Well, you have all the polygons in an array called triangles . And when you draw the shape, you loop through triangles , drawing each triangle from zero through to the end. What you need to do is sort this array so that the triangle that is furthest away is in element zero of the array, and the one closest to the viewer is in the very last element.

For this, you will use the Array.sort method. This method actually has many different options. Used without any parameters, sort will try to sort the array alphabetically and/or numerically . You can also specify various options, such as to sort ascending or descending, with or without case sensitivity, etc. Of course, this simple sorting is only useful if the elements are strings and/or numbers .

There is also an Array.sortOn method, which is a little more useful. Say you had an array called employees , which was full of objects. Each object had a number of properties, such as name and age . You could sort this array based on the name property like so:

 employees.sortOn("name"); 

Or on the age property like this:

 employees.sortOn("age"); 

Unfortunately even this doesnt help you. Each element of your triangles array contains an object with a , b , and c properties, each of which are indexes to an element in the points array. You need to use these indexes to get the three point objects that make up the triangle. When you have the three points, you can then analyze their z values to get the overall depth of that particular triangle. Thats what you eventually want to sort on. But thats quite a bit of logic. Obviously Flash doesnt come with a built-in method for such a thing! So how do you tell Flash to sort an array based on complex criteria like that?

Fortunately, there is one more way to use Array.sort . You can supply this method with the name of a sorting function, which you create. The function itself takes on a form like this:

 function sortFunction(elementA, elementB):Number {       // return 1 if elementA should go before element B       // return 1 if elementA should go after elementB       // return 0 if no change should be made } 

The sorting function can then perform any kind of logic you want it to in comparing elementA and elementB , as long as it returns a ˆ 1, 0, or 1. To use this function to sort an array, you would call it like this:

 myArray.sort(sortFunction); 

That part of it is pretty simple. You just need to supply the function that contains that comparing logic.

For the purposes of this example, youll make a method called triSort . It will look like this:

 function triSort(triA:Object, triB:Object):Number {       var zA:Number = Math.min(points[triA.a].z, points[triA.b].z);       zA = Math.min(zA, points[triA.c].z);       var zB:Number = Math.min(points[triB.a].z, points[triB.b].z);       zB = Math.min(zB, points[triB.c].z);       if(zA < zB)       {             return 1;       }       else       {             return -1;       } } 

Here, you are passed references to two triangles. For each triangle, you use Math.min to find the point with the lowest z value. In other words, the point that is closest to you. You store these in zA and zB . Then you compare these two values. If zA is less than zB , then triangleA is closer to you than triangleB , and should go later in the array, so it is drawn later. Thus you return 1. If the opposite is true, then you return ˆ 1. Theoretically, you could test to see if they are equal and if so return 0. But that is just an extra computation with no added benefit in this case.

So, when do you sort this array? Some time after you make all adjustments to the positions of the points, and just before you render the triangles. Ive put it in the onEnterFrame function like so:

 clear();  triangles.sort(triSort);  var numTriangles:Number = triangles.length; for(var i:Number =0;i<numTriangles;i++) {       renderTriangle(triangles[i]); } 

Again, just to keep things current, here is the final code for ch17_02.fla :

 var points:Array = new Array(); var triangles:Array = new Array(); var fl:Number = 250; var vpX:Number = Stage.width / 2; var vpY:Number = Stage.height / 2; var zOffset:Number = 400; init(); function init() {       points[0] =  {x: -50, y:-250, z:-50};       points[1] =  {x:  50, y:-250, z:-50};       points[2] =  {x: 200, y: 250, z:-50};       points[3] =  {x: 100, y: 250, z:-50};       points[4] =  {x: 50,  y: 100, z:-50};       points[5] =  {x: -50, y: 100, z:-50};       points[6] =  {x:-100, y: 250, z:-50};       points[7] =  {x:-200, y: 250, z:-50};       points[8] =  {x:   0, y:-150, z:-50};       points[9] =  {x:  50, y:   0, z:-50};       points[10] = {x: -50, y:   0, z:-50};       points[11] = {x: -50, y:-250, z: 50};       points[12] = {x:  50, y:-250, z: 50};       points[13] = {x: 200, y: 250, z: 50};       points[14] = {x: 100, y: 250, z: 50};       points[15] = {x:  50, y: 100, z: 50};       points[16] = {x: -50, y: 100, z: 50};       points[17] = {x:-100, y: 250, z: 50};       points[18] = {x:-200, y: 250, z: 50};       points[19] = {x:   0, y:-150, z: 50};       points[20] = {x:  50, y:   0, z: 50};       points[21] = {x: -50, y:   0, z: 50};       triangles[0] =  {a:0, b:1,  c:8,  col:0x6666cc};       triangles[1] =  {a:1, b:9,  c:8,  col:0x6666cc};       triangles[2] =  {a:1, b:2,  c:9,  col:0x6666cc};       triangles[3] =  {a:2, b:4,  c:9,  col:0x6666cc};       triangles[4] =  {a:2, b:3,  c:4,  col:0x6666cc};       triangles[5] =  {a:4, b:5,  c:9,  col:0x6666cc};       triangles[6] =  {a:9, b:5,  c:10, col:0x6666cc};       triangles[7] =  {a:5, b:6,  c:7,  col:0x6666cc};       triangles[8] =  {a:5, b:7,  c:10, col:0x6666cc};       triangles[9] =  {a:0, b:10, c:7,  col:0x6666cc};       triangles[10] = {a:0, b:8,  c:10, col:0x6666cc};       triangles[11] = {a:11, b:19,  c:12,  col:0xcc6666};       triangles[12] = {a:12, b:19,  c:20,  col:0xcc6666};       triangles[13] = {a:12, b:20,  c:13,  col:0xcc6666};       triangles[14] = {a:13, b:20,  c:15,  col:0xcc6666};       triangles[15] = {a:13, b:15,  c:14,  col:0xcc6666};       triangles[16] = {a:15, b:20,  c:16,  col:0xcc6666};       triangles[17] = {a:20, b:21,  c:16,  col:0xcc6666};       triangles[18] = {a:16, b:18,  c:17,  col:0xcc6666};       triangles[19] = {a:16, b:21,  c:18,  col:0xcc6666};       triangles[20] = {a:11, b:18,  c:21,  col:0xcc6666};       triangles[21] = {a:11, b:21,  c:19,  col:0xcc6666};       triangles[22] = {a:0,  b:11,  c:1,  col:0xcccc66};       triangles[23] = {a:11, b:12,  c:1,  col:0xcccc66};       triangles[24] = {a:1,  b:12,  c:2,  col:0xcccc66};       triangles[25] = {a:12, b:13,  c:2,  col:0xcccc66};       triangles[26] = {a:3,  b:2,   c:14, col:0xcccc66};       triangles[27] = {a:2,  b:13,  c:14, col:0xcccc66};       triangles[28] = {a:4,  b:3,   c:15, col:0xcccc66};       triangles[29] = {a:3,  b:14,  c:15, col:0xcccc66};       triangles[30] = {a:5,  b:4,   c:16, col:0xcccc66};       triangles[31] = {a:4,  b:15,  c:16, col:0xcccc66};       triangles[32] = {a:6,  b:5,   c:17, col:0xcccc66};       triangles[33] = {a:5,  b:16,  c:17, col:0xcccc66};       triangles[34] = {a:7,  b:6,   c:18, col:0xcccc66};       triangles[35] = {a:6,  b:17,  c:18, col:0xcccc66};       triangles[36] = {a:0,  b:7,   c:11, col:0xcccc66};       triangles[37] = {a:7,  b:18,  c:11, col:0xcccc66};       triangles[38] = {a:8,  b:9,   c:19, col:0xcccc66};       triangles[39] = {a:9,  b:20,  c:19, col:0xcccc66};       triangles[40] = {a:9,  b:10,  c:20, col:0xcccc66};       triangles[41] = {a:10, b:21,  c:20, col:0xcccc66};       triangles[42] = {a:10, b:8,   c:21, col:0xcccc66};       triangles[43] = {a:8,  b:19,  c:21, col:0xcccc66}; } function onEnterFrame():Void {       var numPoints:Number = points.length;       for (var i:Number=0;i<numPoints;i++) {             var point:MovieClip = points[i];             var angleY:Number = (_xmouse - vpX) * .001;             var cosY:Number = Math.cos(angleY);             var sinY:Number = Math.sin(angleY);             var angleX:Number = (_ymouse - vpY) * .001;             var cosX:Number = Math.cos(angleX);             var sinX:Number = Math.sin(angleX);             var x1:Number = point.x * cosY - point.z * sinY;             var z1:Number = point.z * cosY + point.x * sinY;             var y1:Number = point.y * cosX - z1 * sinX;             var z2:Number = z1 * cosX + point.y * sinX;             point.x = x1;             point.y = y1;             point.z = z2;             var scale:Number = fl / (fl + point.z + zOffset);             point.xPos = vpX + point.x * scale;             point.yPos = vpY + point.y * scale;       }       clear();  triangles.sort(triSort);  var numTriangles:Number = triangles.length;       for(var i:Number =0;i<numTriangles;i++)       {             renderTriangle(triangles[i]);       } } function renderTriangle(tri:Object):Void {       var pointA:Object = points[tri.a];       var pointB:Object = points[tri.b];       var pointC:Object = points[tri.c];       if(isBackFace(pointA, pointB, pointC))       {             return;       }       beginFill(tri.col, 100);       moveTo(pointA.xPos, pointA.yPos);       lineTo(pointB.xPos, pointB.yPos);       lineTo(pointC.xPos, pointC.yPos);       lineTo(pointA.xPos, pointA.yPos);       endFill(); } function isBackFace(pointA:Object, pointB:Object, pointC:Object):Boolean {       // see http://www.jurjans.lv/flash/shape.html       var cax:Number = pointC.xPos - pointA.xPos;       var cay:Number = pointC.yPos - pointA.yPos;       var bcx:Number = pointB.xPos - pointC.xPos;       var bcy:Number = pointB.yPos - pointC.yPos;       return cax * bcy > cay * bcx; }  function triSort(triA:Object, triB:Object):Number   {   var zA:Number = Math.min(points[triA.a].z, points[triA.b].z);   zA = Math.min(zA, points[triA.c].z);   var zB:Number = Math.min(points[triB.a].z, points[triB.b].z);   zB = Math.min(zB, points[triB.c].z);   if(zA < zB)   {   return 1;   }   else   {   return -1;   }   }  

When you run this, you should have a perfectly rendered solid. You are really getting someplace now. The next step will boost you right over the top in terms of coolness!

image from book
Figure 17-4: Sorting the depths puts it all right!


Foundation ActionScript. Animation. Making Things Move
Foundation Actionscript 3.0 Animation: Making Things Move!
ISBN: 1590597915
EAN: 2147483647
Year: 2005
Pages: 137
Authors: Keith Peters

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net