Cheata 3D
19
Jul 09
Problems and Solutions
I have had a look at the away3d render code and read a few articles about the drawTriangles(). It seems that this method is not necessarily the fastest solution, due to a missing z-buffer implementation. Papervision is in Flash 9 sometimes even faster than in Flash 10!
Well, what does this mean? I think the core issue is that the Flash Drawing API is unfortunately not fast enough to handle more than 20′000 triangles. I have decided to try-out Alchemy… I will first have to setup my windows machine, since my Mac is still running on Tiger.
11
Jul 09
UV Mapping
I have tried to wrap a bitmap around the cube, but it hasn’t worked so far. The problem is, there are 8 points to and 3 x 12 indices to render a cube. To map the bitmap each vertex needs its own uv map position.
But if you look at uv map that starts top left at 0,0 and ends at bottom right 1,1, you will notice that the point at the vertext at 0,0 should also be at 1,0. There is the problem. Analising a .obj file exported from Modo shows that a cube needs more points than eight to be properly uv mappped.
I think, I will have to run more than 8 points per cube. Points 3 and 1 need to be cloned 2 times so that the different uv coordinates can be applied. Points 7 and 5 will be cloned once. The number of points increases by 6 to total 14 points per cube. This seems to be a lot more points. I will show the results in the next article.
Here is the current code:
package core { import __AS3__.vec.Vector; import flash.display.Bitmap; import flash.display.Sprite; import flash.display.TriangleCulling; import flash.events.Event; import flash.utils.getTimer; import mx.core.Application; public class GameEngine extends Sprite { private var _lastTime:int; private const third:Number = 1/3; private var _vertices:Vector. = new Vector.(); private var _render:Vector. = new Vector.(); private var _indices:Vector. = new Vector.(); private var _uvtData:Vector. = new Vector.(); private var _cubes:Vector. = new Vector.(); // Rubiks Cube properties private var _type:uint = 3; // 3 x 3 cube private var _size:Number = 100; private var _gap:Number = 20; private var _halfSize:Number = _size / 2; private var _offset:Number = _size + _gap; private var _focalLength:Number = 1000; private var _radius:Number = 100; private var _rHalf:Number = _radius / 2; private var a:Number = Math.PI / 360; private var cos:Number = Math.cos(a); private var sin:Number = Math.sin(a); private var _sp:Sprite = new Sprite(); [Embed(source='assets/bmp2.png')] private var Chee:Class; private var _img:Bitmap = new Chee as Bitmap; public var xRot:Boolean; public var yRot:Boolean; public var zRot:Boolean; public function GameEngine() { super(); init(); } private function onAdded(e:Event):void { _sp.x = Application.application.width / 2; _sp.y = Application.application.height / 2; if(this.hasEventListener(Event.ADDED_TO_STAGE)) this.removeEventListener(Event.ADDED_TO_STAGE, onAdded); if(!this.hasEventListener(Event.RESIZE)) this.addEventListener(Event.RESIZE, onAdded); } private function init():void { // Set World this.addEventListener(Event.ADDED_TO_STAGE, onAdded); addChild(_sp); var i:uint = 0; for(var r:uint = 0; r < _type; r++) { for(var c:uint = 0; c < _type; c++) { for(var d:uint = 0; d < _type; d++) { var s:DepthSorter= new DepthSorter(); s.name = i.toString(); s.zIndex = i; _sp.addChild(s); _cubes.push(s); makeCube(i,r,c,d); i++ } } } this.addEventListener(Event.ENTER_FRAME, update); //update(null); } private function makeCube(index:uint, r:uint, c:uint, d:uint):void { var v0:uint = 0 + 8 * index; var v1:uint = 1 + 8 * index; var v2:uint = 2 + 8 * index; var v3:uint = 3 + 8 * index; var v4:uint = 4 + 8 * index; var v5:uint = 5 + 8 * index; var v6:uint = 6 + 8 * index; var v7:uint = 7 + 8 * index; if(index == 0) { _uvtData.push(1,third, 0.75,third, 1,0, 0.25,0, 0.25,third, 1,1, 1,0, 0,0); _indices.push(v2,v6,v0); _indices.push(v6,v4,v0); _indices.push(v6,v7,v4); _indices.push(v7,v5,v4); _indices.push(v7,v3,v5); _indices.push(v3,v1,v5); _indices.push(v3,v2,v1); _indices.push(v2,v0,v1); _indices.push(v3,v7,v2); _indices.push(v7,v6,v2); _indices.push(v0,v4,v1); _indices.push(v4,v5,v1); } // Create 8 3D-Points for(var i:uint = 0; i < 2; i++) { for(var j:uint = 0; j < 2; j++) { for(var p:uint = 0; p < 2; p++) { var xPos:Number = (i * _size - _halfSize) + c * (_size + _gap) - _offset; var yPos:Number = (j * _size - _halfSize) + r * (_size + _gap)- _offset; var zPos:Number = (p * _size - _halfSize) + d * (_size + _gap)- _offset; _vertices.push(xPos, yPos, zPos); } } } } private function compare(a:DepthSorter, b:DepthSorter):Number { var aZ:uint = a.name as uint; if(a.zIndex > b.zIndex) return -1; else return 1; } private function update(e:Event):void { var thisTime:int = getTimer(); var fps:Number = thisTime - _lastTime; _lastTime = thisTime; //trace(fps); // Update /** * RENDER LOOP, converts 3d to screen and applies matrices * */ _render = new Vector.(); var j:uint = 0; for(var i:uint = 0; i < 216; i++) { var xPos:Number = _vertices[i*3]; var yPos:Number = _vertices[i*3+1]; var zPos:Number = _vertices[i*3+2]; var x1:Number; var y1:Number; var z1:Number; if(xRot) // X Rotation { x1 = xPos; y1 = cos * yPos - sin * zPos; z1 = cos * zPos + sin * yPos; } if(yRot) // Y Rotation { if(true) { x1 = cos * xPos - sin * zPos; y1 = yPos; z1 = cos * zPos + sin * xPos; }else { x1 = xPos; y1 = yPos; z1 = zPos; } } if(zRot) // Z Rotation { x1 = cos * xPos - sin * yPos; y1 = cos * yPos + sin * xPos; z1 = zPos; } if(!zRot && !xRot && !yRot) { x1 = xPos y1 = yPos z1 = zPos; } _vertices[i*3] = x1; _vertices[i*3+1] = y1; _vertices[i*3+2] = z1; if(i % 8 == 0) { _cubes[j].zIndex = z1; j++; } // Render var scale:Number = _focalLength / (_focalLength + _vertices[i*3+2]); _render.push(_vertices[i*3] * scale, _vertices[i*3+1] * scale); } // Draw for(var t:uint = 0; t < 27; t++) { with(_cubes[t].graphics) { clear(); lineStyle(1,0); //drawCircle(300,300,t *10); beginBitmapFill(_img.bitmapData); //beginFill(0xFF9900); var f:uint = t*16; drawTriangles(_render.slice(f,f+16), _indices, _uvtData, TriangleCulling.POSITIVE); endFill(); } } // sort _cubes.sort(compare); for(var q:uint = 0; q < 27; q++) { _sp.addChild(_cubes[q]); } } } }
11
Jul 09
Rubiks cube with drawTriangles()
The Flash plugin is required to view this object.
This is my first attempt to render a 3×3 rubiks cube. The beef is in the drawTriangles() method of the Graphics class.
To calculate each frame, matrices are applied to the 3d Points and rendered down to 2d vertices. The Array of 2d vertices is then passed to the drawTriangles() meathod along with the indices. (AdvancED Actionscript Animation)
The engine draws each cube into it’s own Shape. After the cubes have been drawn, the Shapes will be sorted. The Shapes are actually DepthSort objects that inherit from Shape and extend it with an additional zIndex property.
Right click swf to view source.
10
Jul 09
Rubiks Cube with Flash Cheata 3D

Cheetah
After reading AdvancED Actionscript 3.0 Animation, I have decided to give a self-made Flash 3D Engine a shot. The idea is to create a small and fast 3d framework that out-powers the 3 major engines (Away, Papervision, Alternative). through incorporation of Pixel Bender, minimising function calls and if possible passing byte-arrays to OpenGL via “Alchemy”. The Framework shall provide 3D Vertice management on byte-level with Depth-Sorting, rotation matrices and UV mapping , a Camera Object, a Render Engine, and eventually an Obj parser.
The Books I am using are:
- AdvancED Actionscript 3.0 Animation – Keith Peters – friedsofed – ISBN: 978 – 1 – 4302 – 1608 – 7
- Actionscript 3.0 Animation “Making things move” – Keith Peters – friendsofed – ISBN: 1 – 59059 – 791 – 5
- 3D Math Primer – Fletcher Dunn and Ian Parberry – Wordware – ISBN: 1 – 55622 – 911 – 9
To instantiate the engine, the code should look similar to this:
var engine:Cheata = new Cheata();
addChild(engine);
engine.data = _initVertices:Vector<Number>;
engine.startRendering();
engine.startRotatingX(anglePerSeconds:Number);
I hope this experiment will show the differences among FP 10 3D-Engines.