notice: Flash editor updated! Join the development! Thanks to MiniBuilder


embed

FAVORITE BY
:
:
3d
:
:
:
3d
:
3D
:
ambient
:
GlobalIlluminationRender3DAmbient Occlusionでリアルタイム大域照明ww Flashでかいw
:
3D
:
3D
:
occlusionshadercoool, nice work
:
やばすぎる…
:
Ambient Occlusion
:
blimey. 3d sphere things in flash.
:
3dびびる pv3d使ってない
:
相変わらずスゴイなぁ
FORKED
  1. // forked from keim_at_Si's Real time ambient occlusion
  2. // gouraud shading based ambient occlusion (PoC)
  3. //   mouse move to move camera, mouse click to stop motion
  4. //   press z key to switch ambient occlusion
  5. //----------------------------------------------------------------------
  6. package {
  7.     import flash.display.*;
  8.     import flash.events.*;
  9.     import flash.geom.*;
  10.     import flash.utils.*;
  11.     [SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
  12.     public class main extends Sprite {
  13.         // settings
  14.         private const aoBlendRatio:Number = 0.6// blending ratio of a.o.
  15.         private const aoRadius:Number = 8;       // pseudo radius for a.o. calculation
  16.         private const divPlane:int = 16;         // partition number of plane
  17.         private const divSphere:int = 24;        // partition number of sphere
  18.         private const hcResolution:int = 3;      // log2 based hemi-cube resolusion 
  19.         
  20.         // 3D renders
  21.         private var _materials:Vector.<Material> = new Vector.<Material>();
  22.         private var _light:Light = new Light(1,1,1);
  23.         private var _meshSphere:Mesh = new Mesh(_materials);
  24.         private var _meshPlane :Mesh = new Mesh(_materials);
  25.         private var _screen:BitmapData = new BitmapData(465465false0);
  26.         private var _matscr:Matrix = new Matrix(1001232.5232.5);
  27.         private var gl:Render3D = new Render3D(250);
  28.         // utils
  29.         private var _timer:timer = new timer(10"Total: ##[ms/frame]""AO calc: ##[ms/frame]",  "Rendering: ##[ms/frame]");
  30.         
  31.         // objects
  32.         private var camera:Vector3D;
  33.         private var plane:AOPrimitive;
  34.         private var spheres:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  35.         private var aoShade:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  36.         private var spheresProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  37.         private var aoShadeProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  38.         
  39.         // motions
  40.         private var motionRadius:Vector.<Number> = Vector.<Number>([  10,  -20,   32]);
  41.         private var motionFreq  :Vector.<Number> = Vector.<Number>([0.200.07,-0.12]);
  42.         private var motionFreqY :Vector.<Number> = Vector.<Number>([0.120.250.05]);
  43.         private var vertexCount:int;
  44.         private var frame:int = 0;
  45.         private var frameStep:int = 1;
  46.         private var aoSwitch:Boolean = true;
  47.         
  48.         // entry point
  49.         function main() {
  50.             var sphereVerticesCount:int = ((divSphere>>1)-1)*divSphere+2,
  51.                 planeVerticesCount:int  = (divPlane+1)*(divPlane+1);
  52.             vertexCount = planeVerticesCount + sphereVerticesCount*3;
  53.             _timer.title = "Vertices: " + String(vertexCount) + "\n";
  54.             
  55.             camera = new Vector3D(0, -5, -50);
  56.             plane  = new AOPrimitive(0000, planeVerticesCount);
  57.             spheresProjected[0] = spheres[0] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  58.             spheresProjected[1] = spheres[1] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  59.             spheresProjected[2] = spheres[2] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  60.             aoShadeProjected[0] = aoShade[0] = new AOPrimitive(0,-60, aoRadius);
  61.             aoShadeProjected[1] = aoShade[1] = new AOPrimitive(0,-60, aoRadius);
  62.             aoShadeProjected[2] = aoShade[2] = new AOPrimitive(0,-60, aoRadius);
  63.             plane.aoPrimitives.push(spheres[0], spheres[1], spheres[2]);
  64.             spheres[0].aoPrimitives.push(aoShade[0], spheres[1], spheres[2]);
  65.             spheres[1].aoPrimitives.push(spheres[0], aoShade[1], spheres[2]);
  66.             spheres[2].aoPrimitives.push(spheres[0], spheres[1], aoShade[2]);
  67.             _materials.push((new Material()).setColor(0xffffff, 01283280));
  68.             _createSphere(_meshSphere, 5, divSphere>>1, divSphere);
  69.             _createPlane(_meshPlane, 8080, divPlane, divPlane);
  70.             
  71.             addChild(gl).visible = false;
  72.             addChild(new Bitmap(_screen));
  73.             addChild(_timer);
  74.             addEventListener("enterFrame", _onEnterFrame);
  75.             stage.addEventListener("click", _onClick);
  76.             stage.addEventListener("keyUp", _onKeyUp);
  77.         }
  78.         private function _onEnterFrame(e:Event) : void {
  79.             // motion
  80.             for (var i:int=0; i<3; i++) {
  81.                 spheres[i].x = Math.cos(frame*motionFreq[i]) * motionRadius[i];
  82.                 spheres[i].y = Math.sin(frame*motionFreqY[i]) * 5 + 10;
  83.                 spheres[i].z = Math.sin(frame*motionFreq[i]) * motionRadius[i];
  84.                 aoShade[i].x = spheres[i].x;
  85.                 aoShade[i].y = -5;
  86.                 aoShade[i].z = spheres[i].z;
  87.             }
  88.             
  89.             _timer.start(0);
  90.             // clear screen
  91.             _screen.fillRect(_screen.rect, 0);
  92.             // camera
  93.             gl.id().tv(camera).rx((400-mouseY)*0.15).ry((232-mouseX)*0.5);
  94.             _light.transformBy(gl.matrix);
  95.             // project all primitives
  96.             gl.projectPoint3D(spheresProjected);
  97.             gl.projectPoint3D(aoShadeProjected);
  98.             spheresProjected.sort(function(v1:Point3D, v2:Point3D) : Number { return v1.world.z - v2.world.z; });
  99.             // plane
  100.             gl.push().tv(plane).project(_meshPlane);
  101.             calculateVertexNormal(_meshPlane);
  102.             if (frameStep) calculateAmbientOcclusion(_meshPlane, plane.aoPrimitives, plane.ao);
  103.             calculateTexCoordByVertexNormal(_meshPlane, _light, plane.ao);
  104.             _timer.start(2); 
  105.             _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  106.             _timer.pause(2);
  107.             gl.pop();
  108.             // spheres
  109.             for each (var v:Point3D in spheresProjected) {
  110.                 var p:AOPrimitive = AOPrimitive(v);
  111.                 gl.push().tv(p).project(_meshSphere);
  112.                 calculateVertexNormal(_meshSphere);
  113.                 if (frameStep) calculateAmbientOcclusion(_meshSphere, p.aoPrimitives, p.ao);
  114.                 calculateTexCoordByVertexNormal(_meshSphere, _light, p.ao);
  115.                 _timer.start(2);
  116.                 _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  117.                 _timer.pause(2);
  118.                 gl.pop();
  119.             }
  120.             _timer.pause(0);
  121.             
  122.             frame += frameStep;
  123.         }
  124.         
  125.         private function _onClick(e:Event) : void {
  126.             frameStep = 1 - frameStep;
  127.         }
  128.         
  129.         private function _onKeyUp(e:KeyboardEvent) : void {
  130.             var inkey:String = String.fromCharCode(e.keyCode);
  131.             if (inkey == "Z") aoSwitch = !aoSwitch;
  132.         }
  133.         
  134.         private function _createSphere(mesh:Mesh, radius:Number, dlat:int, dlong:int) : Mesh {
  135.             var ilat:int, ilong:int, lat:Number, long:Number, r:Number, vidx:int
  136.             slat:Number = 3.141592653589793/dlat, slong:Number  = 6.283185307179586/dlong;
  137.             mesh.vertices.push(0, radius, 0);
  138.             mesh.texCoord.push(000);
  139.             for (ilat=1, lat=slat; ilat<dlat; ilat++, lat+=slat) {
  140.                 r = Math.sin(lat) * radius;
  141.                 for (ilong=0, long=-ilat*slong*0.5; ilong<dlong; ilong++, long+=slong) {
  142.                     mesh.vertices.push(Math.cos(long) * r, Math.cos(lat) * radius, Math.sin(long) * r);
  143.                     mesh.texCoord.push(000);
  144.                 }
  145.             }
  146.             mesh.vertices.push(0, -radius, 0);
  147.             mesh.texCoord.push(000);
  148.             for (ilat=0; ilat<dlat-2; ilat++) {
  149.                 vidx = ilat * dlong + 1;
  150.                 for (ilong=1; ilong<dlong; ilong++, vidx++) {
  151.                     mesh.qface(vidx+1, vidx, vidx+dlong+1, vidx+dlong, 0true);
  152.                 }
  153.                 mesh.qface(vidx-dlong+1, vidx, vidx+1, vidx+dlong, 0true);
  154.             }
  155.             vidx = dlong * (dlat-1) + 1;
  156.             for (ilong=0; ilong<dlong-1; ilong++) {
  157.                 mesh.face(0, ilong+1, ilong+20);
  158.                 mesh.face(vidx, vidx-(ilong+1), vidx-(ilong+2), 0);
  159.             }
  160.             mesh.face(0, ilong+110);
  161.             mesh.face(vidx, vidx-(ilong+1), vidx-10);
  162.             mesh.updateFaces();
  163.             
  164.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  165.             for (var i:int=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  166.             return mesh;
  167.         }
  168.         
  169.         private function _createPlane(mesh:Mesh, w:Number, h:Number, divx:int, divy:int) : Mesh {
  170.             var x:Number, y:Number, ix:int, iy:int, i:int, j:int
  171.                 hw:Number=w*0.5, hh:Number=h*0.5, dx:Number=1/divx, dy:Number=1/divy;
  172.             for (y=0, iy=0; iy<=divy; y+=dy, iy++) {
  173.                 for (x=0, ix=0; ix<=divx; x+=dx, ix++) {
  174.                     mesh.vertices.push(x*w-hw, 0, y*h-hh);
  175.                     mesh.texCoord.push(x, y, 0);
  176.                 }
  177.             }
  178.             for (iy=0; iy<divy; iy++) {
  179.                 for (ix=0; ix<divx; ix++) {
  180.                     i = iy*(divy+1)+ix;
  181.                     j = i + divy+1;
  182.                     mesh.qface(i, i+1, j, j+10true);
  183.                 }
  184.             }
  185.             mesh.updateFaces();
  186.             
  187.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  188.             for (i=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  189.             return mesh;
  190.         }
  191.         
  192.         public function calculateVertexNormal(mesh:Mesh) : void {
  193.             var i:int, v:Vector3D, fn:Vector3D, n:Number, vnormals:Vector.<Vector3D>=mesh.vnormals;
  194.             for each (v in vnormals) {
  195.                 v.x = v.y = v.z = v.w = 0;
  196.             }
  197.             for each (var face:Face in mesh.faces) {
  198.                 fn = face.normal.world;
  199.                 v = vnormals[face.i0]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  200.                 v = vnormals[face.i1]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  201.                 v = vnormals[face.i2]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  202.             }
  203.             for each (v in vnormals) {
  204.                 if (v.w == 0continue;
  205.                 n = 1/v.w;
  206.                 v.x *= n;
  207.                 v.y *= n;
  208.                 v.z *= n;
  209.             }
  210.             mesh.vnormals = vnormals;
  211.         }
  212.         
  213.         private const bmdw:int = 1<<hcResolution;
  214.         private var v0:Vector3D = new Vector3D(), v1:Vector3D = new Vector3D(), v:Vector3D = new Vector3D();
  215.         private var hc0:BitmapData = new BitmapData(bmdw,bmdw,false);
  216.         private var hcx:Vector.<BitmapData> = Vector.<BitmapData>([
  217.             new BitmapData(bmdw, bmdw>>1false), 
  218.             new BitmapData(bmdw, bmdw>>1false)
  219.         ]);
  220.         private var hcy:Vector.<BitmapData> = Vector.<BitmapData>([
  221.             new BitmapData(bmdw, bmdw>>1false), 
  222.             new BitmapData(bmdw, bmdw>>1false)
  223.         ]);
  224.         private var rect:Rectangle = new Rectangle();
  225.         public function calculateAmbientOcclusion(mesh:Mesh, prims:Vector.<AOPrimitive>, aoBuffer:Vector.<Number>) : void {
  226.             var x:Number, y:Number, z:Number, w:Number, t:Number, v2:Vector3D, p:AOPrimitive, 
  227.                 i:int=0, j:int=0, k:int, hci:int, ao:int, ix:int, iy:int
  228.                 hhc:int = bmdw>>1, hc0s:int = bmdw*bmdw, hcxs:int = hc0s>>1, hcxf:int = bmdw-1
  229.                 vnormals:Vector.<Vector3D> = mesh.vnormals, 
  230.                 vertices:Vector.<Number> = mesh.verticesOnWorld;
  231.             _timer.start(1);
  232.             for each (v2 in vnormals) {
  233.                 v1.x = 0.0;
  234.                 v1.y = 0.0;
  235.                 v1.z = 0.0;
  236.                      if ((v2.y < 0.6) && (v2.y > -0.6)) v1.y = -1.0;
  237.                 else if ((v2.z < 0.6) && (v2.z > -0.6)) v1.z = 1.0;
  238.                 else v1.x = 1.0;
  239.                 v0.x = v1.y * v2.z - v1.z * v2.y;
  240.                 v0.y = v1.z * v2.x - v1.x * v2.z;
  241.                 v0.z = v1.x * v2.y - v1.y * v2.x;
  242.                 v0.normalize();
  243.                 v1.x = v2.y * v0.z - v2.z * v0.y;
  244.                 v1.y = v2.z * v0.x - v2.x * v0.z;
  245.                 v1.z = v2.x * v0.y - v2.y * v0.x;
  246.                 v1.normalize();
  247.                 // render on hemi-cubes
  248.                 hc0.fillRect(hc0.rect, 1);
  249.                 hcx[0].fillRect(hcx[0].rect, 1);
  250.                 hcx[1].fillRect(hcx[1].rect, 1);
  251.                 hcy[0].fillRect(hcy[0].rect, 1);
  252.                 hcy[1].fillRect(hcy[1].rect, 1);
  253.                 for each (p in prims) {
  254.                     x = p.world.x - vertices[i];
  255.                     y = p.world.y - vertices[i+1];
  256.                     z = p.world.z - vertices[i+2];
  257.                     v.x = x * v0.x + y * v0.y + z * v0.z;
  258.                     v.y = x * v1.x + y * v1.y + z * v1.z;
  259.                     v.z = x * v2.x + y * v2.y + z * v2.z;
  260.                     // hemi-cube front
  261.                     if (v.z > 0) {
  262.                         t = hhc/v.z;
  263.                         w = p.aoRadius * 0.8 * t;
  264.                         rect.x = v.x * t - w + hhc;
  265.                         rect.y = v.y * t - w + hhc;
  266.                         rect.width = w + w;
  267.                         rect.height = w + w;
  268.                         hc0.fillRect(rect, 0);
  269.                     }
  270.                     // hemi-cube x direction
  271.                     if (v.x != 0) {
  272.                         if (v.x > 0) { hci = 0; t =  hhc/v.x; }
  273.                         else         { hci = 1; t = -hhc/v.x; }
  274.                         w = p.aoRadius * 0.65 * t;
  275.                         rect.x = v.y * t - w + hhc;
  276.                         rect.y = v.z * t - w;
  277.                         rect.width = w + w;
  278.                         rect.height = w + w;
  279.                         hcx[hci].fillRect(rect, 0);
  280.                     }
  281.                     // hemi-cube y direction
  282.                     if (v.y != 0) {
  283.                         if (v.y > 0) { hci = 0; t =  hhc/v.y; }
  284.                         else         { hci = 1; t = -hhc/v.y; }
  285.                         w = p.aoRadius * 0.65 * t;
  286.                         rect.x = v.x * t - w + hhc;
  287.                         rect.y = v.z * t - w;
  288.                         rect.width = w + w;
  289.                         rect.height = w + w;
  290.                         hcy[hci].fillRect(rect, 0);
  291.                     }
  292.                 }
  293.                 ao = 0;
  294.                 for (k=0; k<hc0s; k++) {
  295.                     ao += hc0.getPixel(k&hcxf, k>>hcResolution);
  296.                 }
  297.                 for (k=0; k<hcxs; k++) {
  298.                     ix = k & hcxf;
  299.                     iy = k >> hcResolution;
  300.                     ao += hcx[0].getPixel(ix, iy) + hcx[1].getPixel(ix, iy) +
  301.                           hcy[0].getPixel(ix, iy) + hcy[1].getPixel(ix, iy);
  302.                 }
  303.                 aoBuffer[j] = ao * 0.005208333333333333;
  304.                 i+=3;
  305.                 j++;
  306.             }
  307.             _timer.pause(1);
  308.         }
  309.         
  310.         public function calculateTexCoordByVertexNormal(mesh:Mesh, light:Light, aoBuffer:Vector.<Number>) : void {
  311.             var x:Number, y:Number, v:Vector3D, i:int=0, j:int=0, vnormals:Vector.<Vector3D>=mesh.vnormals,
  312.                 dir:Vector3D=light, hv:Vector3D=light.halfVector, 
  313.                 t:Number = aoBlendRatio, it:Number = 1-aoBlendRatio, offset:Number = 0;
  314.             if (!aoSwitch) {
  315.                 t = 0;
  316.                 offset = aoBlendRatio*0.8;
  317.                 it = 1-offset;
  318.             }
  319.             for each (v in vnormals) {
  320.                 x = dir.x * v.x + dir.y * v.y + dir.z * v.z;
  321.                 y = hv.x  * v.x + hv.y  * v.y + hv.z  * v.z;
  322.                 if (x<0) x = 0;
  323.                 if (y<0) y = 0;
  324.                 mesh.texCoord[i] = x*it + aoBuffer[j]*t + offset; i++; j++;
  325.                 mesh.texCoord[i] = y; i+=2;
  326.             }
  327.         }
  328.     }
  329. }
  330. import flash.display.*;
  331. import flash.text.*;
  332. import flash.geom.*;
  333. import flash.events.*;
  334. import flash.utils.getTimer;
  335. class timer extends TextField {
  336.     public var title:String = "";
  337.     private var _time:Vector.<int>;
  338.     private var _sum :Vector.<int>;
  339.     private var _stat:Vector.<String>;
  340.     private var _cnt :int;
  341.     private var _avc:int;
  342.     function timer(averagingCount:int, ...stat) : void {
  343.         _avc  = averagingCount;
  344.         _stat = Vector.<String>(stat);
  345.         _time = new Vector.<int>(stat.length, true);
  346.         _sum  = new Vector.<int>(stat.length, true);
  347.         _cnt  = new Vector.<int>(stat.length, true);
  348.         background = true;
  349.         backgroundColor = 0x00ff00;
  350.         autoSize = "left";
  351.         multiline = true;
  352.         addEventListener("enterFrame", _onEnterFrame);
  353.     }
  354.     public function start(slot:int=0) : void { _time[slot] = getTimer(); }
  355.     public function pause(slot:int=0) : void { _sum[slot] += getTimer() - _time[slot]; }
  356.     public function _onEnterFrame(e:Event) : void {
  357.         if (++_cnt == _avc) {
  358.             _cnt = 0;
  359.             var str:String = "", line:String;
  360.             for (var slot:int = 0; slot<_sum.length; slot++) {
  361.                 line = _stat[slot].replace("##"String(_sum[slot] / _avc));
  362.                 str += line + "\n";
  363.                 _sum[slot] = 0;
  364.             }
  365.             text = title + str;
  366.         }
  367.     }
  368. }
  369. // 3D Engine
  370. //----------------------------------------------------------------------------------------------------
  371. /** Core */
  372. class Render3D extends Shape {
  373.     /** model view matrix */
  374.     public var matrix:Matrix3D;
  375.     private var _meshProjected:Mesh = null;                              // projecting mesh
  376.     private var _facesProjected:Vector.<Face> = new Vector.<Face>();     // projecting face
  377.     private var _projectionMatrix:Matrix3D;                              // projection matrix
  378.     private var _matrixStac:Vector.<Matrix3D> = new Vector.<Matrix3D>(); // matrix stac
  379.     private var _cmdTriangle:Vector.<int> = Vector.<int>([1,2,2]);       // commands to draw triangle
  380.     private var _cmdQuadrangle:Vector.<int> = Vector.<int>([1,2,2,2]);   // commands to draw quadrangle
  381.     private var _data:Vector.<Number> = new Vector.<Number>(8true);    // data to draw shape
  382.     private var _clippingZ:Number;                                       // z value of clipping plane
  383.     
  384.     /** constructor */
  385.     function Render3D(focus:Number=300, clippingZ:Number=0) {
  386.         var projector:PerspectiveProjection = new PerspectiveProjection()
  387.         projector.focalLength = focus;
  388.         _projectionMatrix = projector.toMatrix3D();
  389.         _clippingZ = -clippingZ;
  390.         matrix = new Matrix3D();
  391.         _matrixStac.length = 1;
  392.         _matrixStac[0] = matrix;
  393.     }
  394.     // control matrix
  395.     //--------------------------------------------------
  396.     public function clear() : Render3D { matrix = _matrixStac[0]; _matrixStac.length = 1return this; }
  397.     public function push() : Render3D { _matrixStac.push(matrix.clone()); return this; }
  398.     public function pop() : Render3D { matrix = (_matrixStac.length == 1) ? matrix : _matrixStac.pop(); return this; }
  399.     public function id() : Render3D { matrix.identity(); return this; }
  400.     public function t(x:Number, y:Number, z:Number) : Render3D { matrix.prependTranslation(x, y, z); return this; }
  401.     public function tv(v:Vector3D) : Render3D { matrix.prependTranslation(v.x, v.y, v.z); return this; }
  402.     public function s(x:Number, y:Number, z:Number) : Render3D { matrix.prependScale(x, y, z); return this; }
  403.     public function sv(v:Vector3D) : Render3D { matrix.prependScale(v.x, v.y, v.z); return this; }
  404.     public function r(angle:Number, axis:Vector3D) : Render3D { matrix.prependRotation(angle, axis); return this; }
  405.     public function rv(v:Vector3D) : Render3D { matrix.prependRotation(v.w, v); return this; }
  406.     public function rx(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.X_AXIS); return this; }
  407.     public function ry(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Y_AXIS); return this; }
  408.     public function rz(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Z_AXIS); return this; }
  409.     
  410.     // projections
  411.     //--------------------------------------------------
  412.     /** project */
  413.     public function project(mesh:Mesh) : Render3D {
  414.         matrix.transformVectors(mesh.vertices, mesh.verticesOnWorld);
  415.         var fn:Point3D, vs:Vector.<Number> = mesh.verticesOnWorld;
  416.         var m:Vector.<Number> = matrix.rawData, 
  417.             m00:Number = m[0], m01:Number = m[1], m02:Number = m[2], 
  418.             m10:Number = m[4], m11:Number = m[5], m12:Number = m[6], 
  419.             m20:Number = m[8], m21:Number = m[9], m22:Number = m[10];
  420.         _facesProjected.length = 0;
  421.         for each (var f:Face in mesh.faces) {
  422.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2,
  423.                 x0:Number=vs[i0++], x1:Number=vs[i1++], x2:Number=vs[i2++],
  424.                 y0:Number=vs[i0++], y1:Number=vs[i1++], y2:Number=vs[i2++],
  425.                 z0:Number=vs[i0],   z1:Number=vs[i1],   z2:Number=vs[i2];
  426.             if (z0<_clippingZ && z1<_clippingZ && z2<_clippingZ) {
  427.                 fn = f.normal;
  428.                 fn.world.x = fn.x * m00 + fn.y * m10 + fn.z * m20;
  429.                 fn.world.y = fn.x * m01 + fn.y * m11 + fn.z * m21;
  430.                 fn.world.z = fn.x * m02 + fn.y * m12 + fn.z * m22;
  431.                 if (vs[f.pvi-2]*fn.world.x + vs[f.pvi-1]*fn.world.y + vs[f.pvi]*fn.world.z <= 0) {
  432.                     _facesProjected.push(f);
  433.                 }
  434.             }
  435.         }
  436.         _facesProjected.sort(function(f1:Face, f2:Face):Numberreturn vs[f2.pvi] - vs[f1.pvi]; });
  437.         _meshProjected = mesh;
  438.         return this;
  439.     }
  440.     
  441.     /** project slower than transformVectors() but Vector3D.w considerable. */
  442.     public function projectPoint3D(points:Vector.<Point3D>) : Render3D {
  443.         var m:Vector.<Number> = matrix.rawData, p:Point3D, 
  444.             m00:Number = m[0],  m01:Number = m[1],  m02:Number = m[2], 
  445.             m10:Number = m[4],  m11:Number = m[5],  m12:Number = m[6], 
  446.             m20:Number = m[8],  m21:Number = m[9],  m22:Number = m[10], 
  447.             m30:Number = m[12], m31:Number = m[13], m32:Number = m[14];
  448.         for each (p in points) {
  449.             p.world.x = p.x * m00 + p.y * m10 + p.z * m20 + p.w * m30;
  450.             p.world.y = p.x * m01 + p.y * m11 + p.z * m21 + p.w * m31;
  451.             p.world.z = p.x * m02 + p.y * m12 + p.z * m22 + p.w * m32;
  452.         }
  453.         return this;
  454.     }
  455.     // rendering
  456.     //--------------------------------------------------
  457.     /** render solid */
  458.     public function renderSolid(light:Light) : Render3D {
  459.         var idx:int, mat:Material, materials:Vector.<Material> = _meshProjected.materials,
  460.             vout:Vector.<Number> = _meshProjected.verticesOnScreen;
  461.         Utils3D.projectVectors(_projectionMatrix, _meshProjected.verticesOnWorld, vout, _meshProjected.texCoord);
  462.         graphics.clear();
  463.         for each (var face:Face in _facesProjected) {
  464.             mat = materials[face.mat];
  465.             graphics.beginFill(mat.getColor(light, face.normal.world), mat.alpha);
  466.             idx = face.i0<<1;
  467.             _data[0] = vout[idx]; idx++;
  468.             _data[1] = vout[idx];
  469.             idx = face.i1<<1;
  470.             _data[2] = vout[idx]; idx++;
  471.             _data[3] = vout[idx];
  472.             idx = face.i2<<1;
  473.             _data[4] = vout[idx]; idx++;
  474.             _data[5] = vout[idx];
  475.             if (face.i3 == -1) {
  476.                 graphics.drawPath(_cmdTriangle, _data);
  477.             } else {
  478.                 idx = face.i3<<1;
  479.                 _data[6] = vout[idx]; idx++;
  480.                 _data[7] = vout[idx];
  481.                 graphics.drawPath(_cmdQuadrangle, _data);
  482.             }
  483.             graphics.endFill();
  484.         }
  485.         return this;
  486.     }
  487.     
  488.     /** render with texture */
  489.     private var _indices:Vector.<int> = new Vector.<int>(); // temporary index list
  490.     public function renderTexture(texture:BitmapData) : Render3D {
  491.         var idx:int, mat:Material, indices:Vector.<int> = _indices;
  492.         indices.length = 0;
  493.         for each (var face:Face in _facesProjected) { indices.push(face.i0, face.i1, face.i2); }
  494.         Utils3D.projectVectors(_projectionMatrix, 
  495.                                _meshProjected.verticesOnWorld, 
  496.                                _meshProjected.verticesOnScreen, 
  497.                                _meshProjected.texCoord);
  498.         graphics.clear();
  499.         graphics.beginBitmapFill(texture, nullfalsetrue);
  500.         graphics.drawTriangles(_meshProjected.verticesOnScreen, indices, _meshProjected.texCoord);
  501.         graphics.endFill();
  502.         return this;
  503.     }
  504. }
  505. /** Point3D */
  506. class Point3D extends Vector3D {
  507.     public var world:Vector3D;
  508.     function Point3D(x:Number=0, y:Number=0, z:Number=0, w:Number=1) { super(x,y,z,w); world=clone(); }
  509. }
  510. /** Face */
  511. class Face {
  512.     public var i0:int, i1:int, i2:int, i3:int, pvi:int, mat:int, normal:Point3D;
  513.     static private var _freeList:Vector.<Face> = new Vector.<Face>();
  514.     static public function free(face:Face) : void { _freeList.push(face); }
  515.     static public function alloc(i0:int, i1:int, i2:int, i3:int, mat:int) : Face { 
  516.         var f:Face = _freeList.pop() || new Face();
  517.         f.i0=i0; f.i1=i1; f.i2=i2; f.i3=i3; f.pvi=0; f.mat=mat;
  518.         return f;
  519.     }
  520. }
  521. /** Mesh */
  522. class Mesh {
  523.     public var materials:Vector.<Material>;                 // material list
  524.     public var verticesCount:int;
  525.     public var vertices:Vector.<Number>;                    // vertex
  526.     public var verticesOnWorld:Vector.<Number>;             // vertex on camera coordinate
  527.     public var verticesOnScreen:Vector.<Number>;            // vertex on screen
  528.     public var texCoord:Vector.<Number>;                    // texture coordinate
  529.     public var faces:Vector.<Face> = new Vector.<Face>();   // face list
  530.     public var vnormals:Vector.<Vector3D>;                  // vertex normal
  531.     
  532.     /** constructor */
  533.     function Mesh(materials:Vector.<Material>) {
  534.         this.materials = materials;
  535.         this.vertices = new Vector.<Number>();
  536.         this.texCoord = new Vector.<Number>();
  537.         this.verticesOnWorld = new Vector.<Number>();
  538.         this.verticesOnScreen = new Vector.<Number>();
  539.         this.vnormals = null;
  540.         this.verticesCount = 0;
  541.     }
  542.     
  543.     /** clear all faces */
  544.     public function clear() : Mesh {
  545.         for each (var face:Face in faces) Face.free(face);
  546.         faces.length = 0;
  547.         return this;
  548.     }
  549.     
  550.     /** register face */
  551.     public function face(i0:int, i1:int, i2:int, mat:int=0) : Mesh {
  552.         faces.push(Face.alloc(i0, i1, i2, -1, mat));
  553.         return this;
  554.     }
  555.     
  556.     /** register quadrangle face. set div=true to divide into 2 triangles. */
  557.     public function qface(i0:int, i1:int, i2:int, i3:int, mat:int=0, div:Boolean=true) : Mesh {
  558.         if (div) faces.push(Face.alloc(i0, i1, i2, -1, mat), Face.alloc(i3, i2, i1, -1, mat));
  559.         else     faces.push(Face.alloc(i0, i1, i3, i2, mat));
  560.         return this;
  561.     }
  562.     
  563.     /** update face gravity point and normal */
  564.     public function updateFaces() : Mesh {
  565.         verticesCount = vertices.length/3;
  566.         var vs:Vector.<Number> = vertices;
  567.         for each (var f:Face in faces) {
  568.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2;
  569.             var x01:Number=vs[i1]-vs[i0], x02:Number=vs[i2]-vs[i0];
  570.             f.pvi = vs.length+2;
  571.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  572.             var y01:Number=vs[i1]-vs[i0], y02:Number=vs[i2]-vs[i0];
  573.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  574.             var z01:Number=vs[i1]-vs[i0], z02:Number=vs[i2]-vs[i0];
  575.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  576.             f.normal = new Point3D(y02*z01-y01*z02, z02*x01-z01*x02, x02*y01-x01*y02, 0);
  577.             f.normal.normalize();
  578.             if (f.i3 != -1) {
  579.                 var i3:int = (f.i3<<1)+f.i3;
  580.                 vs[f.pvi-2] = vs[f.pvi-2]*0.75 + vs[i3++]*0.25;
  581.                 vs[f.pvi-1] = vs[f.pvi-1]*0.75 + vs[i3++]*0.25;
  582.                 vs[f.pvi]   = vs[f.pvi]  *0.75 + vs[i3]  *0.25;
  583.             }
  584.         }
  585.         return this;
  586.     }
  587. }
  588. /** Light */
  589. class Light extends Point3D {
  590.     public var halfVector:Vector3D = new Vector3D();
  591.     
  592.     /** constructor (set position) */
  593.     function Light(x:Number=1, y:Number=1, z:Number=1) {
  594.         super(x, y, z, 0);
  595.         normalize();
  596.     }
  597.     /** projection */
  598.     public function transformBy(matrix:Matrix3D) : void {
  599.         world = matrix.deltaTransformVector(this);
  600.         halfVector.x = world.x;
  601.         halfVector.y = world.y;
  602.         halfVector.z = world.z + 1
  603.         halfVector.normalize();
  604.     }
  605. }
  606. /** Material */
  607. class Material extends BitmapData {
  608.     public var alpha:Number = 1;    // The alpha value is available for renderSolid()
  609.     public var doubleSided:int = 0// set doubleSided=-1 if double sided material
  610.     
  611.     /** constructor */
  612.     function Material(dif:int=128, spc:int=128) { super(dif, spc, false); }
  613.     
  614.     /** set color. */
  615.     public function setColor(col:uint, amb:int=64, dif:int=192, spc:int=0,  pow:Number=8) : Material {
  616.         fillRect(rect, col);
  617.         var lmap:LightMap = new LightMap(width, height);
  618.         draw(lmap.diffusion(amb, dif), nullnull"hardlight");
  619.         draw(lmap.specular (spc, pow), nullnull"add");
  620.         lmap.dispose();
  621.         return this;
  622.     }
  623.     
  624.     /** calculate color by light and normal vector. */
  625.     public function getColor(l:Light, n:Vector3D) : uint {
  626.         var dir:Vector3D = l.world, hv:Vector3D = l.halfVector;
  627.         var ln:int = int((dir.x * n.x + dir.y * n.y + dir.z * n.z) * (width-1)),
  628.             hn:int = int((hv.x  * n.x + hv.y  * n.y + hv.z  * n.z) * (height-1));
  629.         if (ln<0) ln = (-ln) & doubleSided;
  630.         if (hn<0) hn = (-hn) & doubleSided;
  631.         return getPixel(ln, hn);
  632.     }
  633. }
  634. class LightMap extends BitmapData {
  635.     function LightMap(dif:int, spc:int) { super(dif, spc, false); }
  636.     
  637.     public function diffusion(amb:int, dif:int) : BitmapData {
  638.         var col:int, rc:Rectangle = new Rectangle(001, height), ipk:Number = 1 / width;
  639.         for (rc.x=0; rc.x<width; rc.x+=1) {
  640.             col = ((rc.x * (dif - amb)) * ipk) + amb;
  641.             fillRect(rc, (col<<16)|(col<<8)|col);
  642.         }
  643.         return this;
  644.     }
  645.     
  646.     public function specular(spc:int, pow:Number) : BitmapData {
  647.         var col:int, rc:Rectangle = new Rectangle(00, width, 1),
  648.             mpk:Number = (pow + 2) * 0.15915494309189534, ipk:Number = 1 / height;
  649.         for (rc.y=0; rc.y<height; rc.y+=1) {
  650.             col = Math.pow(rc.y * ipk, pow) * spc * mpk;
  651.             if (col > 255) col = 255;
  652.             fillRect(rc, (col<<16)|(col<<8)|col);
  653.         }
  654.         return this;
  655.     }
  656. }
  657. // Ambient Occlusion Primitive
  658. class AOPrimitive extends Point3D {
  659.     public var aoPrimitives:Vector.<AOPrimitive> = new Vector.<AOPrimitive>();
  660.     public var aoRadius:Number, ao:Vector.<Number>;
  661.     function AOPrimitive(x:Number=0, y:Number=0, z:Number=0, r:Number=0, vc:int=0) {
  662.         super(x, y, z, 1);
  663.         aoRadius = r;
  664.         ao = new Vector.<Number>(vc, true);
  665.     }
  666. }
noswf
  1. // forked from keim_at_Si's Real time ambient occlusion
  2. // gouraud shading based ambient occlusion (PoC)
  3. //   mouse move to move camera, mouse click to stop motion
  4. //   press z key to switch ambient occlusion
  5. //----------------------------------------------------------------------
  6. package {
  7.     import flash.display.*;
  8.     import flash.events.*;
  9.     import flash.geom.*;
  10.     import flash.utils.*;
  11.     [SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
  12.     public class main extends Sprite {
  13.         // settings
  14.         private const aoBlendRatio:Number = 0.6// blending ratio of a.o.
  15.         private const aoRadius:Number = 8;       // pseudo radius for a.o. calculation
  16.         private const divPlane:int = 16;         // partition number of plane
  17.         private const divSphere:int = 24;        // partition number of sphere
  18.         private const hcResolution:int = 3;      // log2 based hemi-cube resolusion 
  19.         
  20.         // 3D renders
  21.         private var _materials:Vector.<Material> = new Vector.<Material>();
  22.         private var _light:Light = new Light(1,1,1);
  23.         private var _meshSphere:Mesh = new Mesh(_materials);
  24.         private var _meshPlane :Mesh = new Mesh(_materials);
  25.         private var _screen:BitmapData = new BitmapData(465465false0);
  26.         private var _matscr:Matrix = new Matrix(1001232.5232.5);
  27.         private var gl:Render3D = new Render3D(250);
  28.         // utils
  29.         private var _timer:timer = new timer(10"Total: ##[ms/frame]""AO calc: ##[ms/frame]",  "Rendering: ##[ms/frame]");
  30.         
  31.         // objects
  32.         private var camera:Vector3D;
  33.         private var plane:AOPrimitive;
  34.         private var spheres:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  35.         private var aoShade:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  36.         private var spheresProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  37.         private var aoShadeProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  38.         
  39.         // motions
  40.         private var motionRadius:Vector.<Number> = Vector.<Number>([  10,  -20,   32]);
  41.         private var motionFreq  :Vector.<Number> = Vector.<Number>([0.200.07,-0.12]);
  42.         private var motionFreqY :Vector.<Number> = Vector.<Number>([0.120.250.05]);
  43.         private var vertexCount:int;
  44.         private var frame:int = 0;
  45.         private var frameStep:int = 1;
  46.         private var aoSwitch:Boolean = true;
  47.         
  48.         // entry point
  49.         function main() {
  50.             var sphereVerticesCount:int = ((divSphere>>1)-1)*divSphere+2,
  51.                 planeVerticesCount:int  = (divPlane+1)*(divPlane+1);
  52.             vertexCount = planeVerticesCount + sphereVerticesCount*3;
  53.             _timer.title = "Vertices: " + String(vertexCount) + "\n";
  54.             
  55.             camera = new Vector3D(0, -5, -50);
  56.             plane  = new AOPrimitive(0000, planeVerticesCount);
  57.             spheresProjected[0] = spheres[0] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  58.             spheresProjected[1] = spheres[1] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  59.             spheresProjected[2] = spheres[2] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  60.             aoShadeProjected[0] = aoShade[0] = new AOPrimitive(0,-60, aoRadius);
  61.             aoShadeProjected[1] = aoShade[1] = new AOPrimitive(0,-60, aoRadius);
  62.             aoShadeProjected[2] = aoShade[2] = new AOPrimitive(0,-60, aoRadius);
  63.             plane.aoPrimitives.push(spheres[0], spheres[1], spheres[2]);
  64.             spheres[0].aoPrimitives.push(aoShade[0], spheres[1], spheres[2]);
  65.             spheres[1].aoPrimitives.push(spheres[0], aoShade[1], spheres[2]);
  66.             spheres[2].aoPrimitives.push(spheres[0], spheres[1], aoShade[2]);
  67.             _materials.push((new Material()).setColor(0xffffff, 01283280));
  68.             _createSphere(_meshSphere, 5, divSphere>>1, divSphere);
  69.             _createPlane(_meshPlane, 8080, divPlane, divPlane);
  70.             
  71.             addChild(gl).visible = false;
  72.             addChild(new Bitmap(_screen));
  73.             addChild(_timer);
  74.             addEventListener("enterFrame", _onEnterFrame);
  75.             stage.addEventListener("click", _onClick);
  76.             stage.addEventListener("keyUp", _onKeyUp);
  77.         }
  78.         private function _onEnterFrame(e:Event) : void {
  79.             // motion
  80.             for (var i:int=0; i<3; i++) {
  81.                 spheres[i].x = Math.cos(frame*motionFreq[i]) * motionRadius[i];
  82.                 spheres[i].y = Math.sin(frame*motionFreqY[i]) * 5 + 10;
  83.                 spheres[i].z = Math.sin(frame*motionFreq[i]) * motionRadius[i];
  84.                 aoShade[i].x = spheres[i].x;
  85.                 aoShade[i].y = -5;
  86.                 aoShade[i].z = spheres[i].z;
  87.             }
  88.             
  89.             _timer.start(0);
  90.             // clear screen
  91.             _screen.fillRect(_screen.rect, 0);
  92.             // camera
  93.             gl.id().tv(camera).rx((400-mouseY)*0.15).ry((232-mouseX)*0.5);
  94.             _light.transformBy(gl.matrix);
  95.             // project all primitives
  96.             gl.projectPoint3D(spheresProjected);
  97.             gl.projectPoint3D(aoShadeProjected);
  98.             spheresProjected.sort(function(v1:Point3D, v2:Point3D) : Number { return v1.world.z - v2.world.z; });
  99.             // plane
  100.             gl.push().tv(plane).project(_meshPlane);
  101.             calculateVertexNormal(_meshPlane);
  102.             if (frameStep) calculateAmbientOcclusion(_meshPlane, plane.aoPrimitives, plane.ao);
  103.             calculateTexCoordByVertexNormal(_meshPlane, _light, plane.ao);
  104.             _timer.start(2); 
  105.             _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  106.             _timer.pause(2);
  107.             gl.pop();
  108.             // spheres
  109.             for each (var v:Point3D in spheresProjected) {
  110.                 var p:AOPrimitive = AOPrimitive(v);
  111.                 gl.push().tv(p).project(_meshSphere);
  112.                 calculateVertexNormal(_meshSphere);
  113.                 if (frameStep) calculateAmbientOcclusion(_meshSphere, p.aoPrimitives, p.ao);
  114.                 calculateTexCoordByVertexNormal(_meshSphere, _light, p.ao);
  115.                 _timer.start(2);
  116.                 _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  117.                 _timer.pause(2);
  118.                 gl.pop();
  119.             }
  120.             _timer.pause(0);
  121.             
  122.             frame += frameStep;
  123.         }
  124.         
  125.         private function _onClick(e:Event) : void {
  126.             frameStep = 1 - frameStep;
  127.         }
  128.         
  129.         private function _onKeyUp(e:KeyboardEvent) : void {
  130.             var inkey:String = String.fromCharCode(e.keyCode);
  131.             if (inkey == "Z") aoSwitch = !aoSwitch;
  132.         }
  133.         
  134.         private function _createSphere(mesh:Mesh, radius:Number, dlat:int, dlong:int) : Mesh {
  135.             var ilat:int, ilong:int, lat:Number, long:Number, r:Number, vidx:int
  136.             slat:Number = 3.141592653589793/dlat, slong:Number  = 6.283185307179586/dlong;
  137.             mesh.vertices.push(0, radius, 0);
  138.             mesh.texCoord.push(000);
  139.             for (ilat=1, lat=slat; ilat<dlat; ilat++, lat+=slat) {
  140.                 r = Math.sin(lat) * radius;
  141.                 for (ilong=0, long=-ilat*slong*0.5; ilong<dlong; ilong++, long+=slong) {
  142.                     mesh.vertices.push(Math.cos(long) * r, Math.cos(lat) * radius, Math.sin(long) * r);
  143.                     mesh.texCoord.push(000);
  144.                 }
  145.             }
  146.             mesh.vertices.push(0, -radius, 0);
  147.             mesh.texCoord.push(000);
  148.             for (ilat=0; ilat<dlat-2; ilat++) {
  149.                 vidx = ilat * dlong + 1;
  150.                 for (ilong=1; ilong<dlong; ilong++, vidx++) {
  151.                     mesh.qface(vidx+1, vidx, vidx+dlong+1, vidx+dlong, 0true);
  152.                 }
  153.                 mesh.qface(vidx-dlong+1, vidx, vidx+1, vidx+dlong, 0true);
  154.             }
  155.             vidx = dlong * (dlat-1) + 1;
  156.             for (ilong=0; ilong<dlong-1; ilong++) {
  157.                 mesh.face(0, ilong+1, ilong+20);
  158.                 mesh.face(vidx, vidx-(ilong+1), vidx-(ilong+2), 0);
  159.             }
  160.             mesh.face(0, ilong+110);
  161.             mesh.face(vidx, vidx-(ilong+1), vidx-10);
  162.             mesh.updateFaces();
  163.             
  164.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  165.             for (var i:int=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  166.             return mesh;
  167.         }
  168.         
  169.         private function _createPlane(mesh:Mesh, w:Number, h:Number, divx:int, divy:int) : Mesh {
  170.             var x:Number, y:Number, ix:int, iy:int, i:int, j:int
  171.                 hw:Number=w*0.5, hh:Number=h*0.5, dx:Number=1/divx, dy:Number=1/divy;
  172.             for (y=0, iy=0; iy<=divy; y+=dy, iy++) {
  173.                 for (x=0, ix=0; ix<=divx; x+=dx, ix++) {
  174.                     mesh.vertices.push(x*w-hw, 0, y*h-hh);
  175.                     mesh.texCoord.push(x, y, 0);
  176.                 }
  177.             }
  178.             for (iy=0; iy<divy; iy++) {
  179.                 for (ix=0; ix<divx; ix++) {
  180.                     i = iy*(divy+1)+ix;
  181.                     j = i + divy+1;
  182.                     mesh.qface(i, i+1, j, j+10true);
  183.                 }
  184.             }
  185.             mesh.updateFaces();
  186.             
  187.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  188.             for (i=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  189.             return mesh;
  190.         }
  191.         
  192.         public function calculateVertexNormal(mesh:Mesh) : void {
  193.             var i:int, v:Vector3D, fn:Vector3D, n:Number, vnormals:Vector.<Vector3D>=mesh.vnormals;
  194.             for each (v in vnormals) {
  195.                 v.x = v.y = v.z = v.w = 0;
  196.             }
  197.             for each (var face:Face in mesh.faces) {
  198.                 fn = face.normal.world;
  199.                 v = vnormals[face.i0]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  200.                 v = vnormals[face.i1]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  201.                 v = vnormals[face.i2]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  202.             }
  203.             for each (v in vnormals) {
  204.                 if (v.w == 0continue;
  205.                 n = 1/v.w;
  206.                 v.x *= n;
  207.                 v.y *= n;
  208.                 v.z *= n;
  209.             }
  210.             mesh.vnormals = vnormals;
  211.         }
  212.         
  213.         private const bmdw:int = 1<<hcResolution;
  214.         private var v0:Vector3D = new Vector3D(), v1:Vector3D = new Vector3D(), v:Vector3D = new Vector3D();
  215.         private var hc0:BitmapData = new BitmapData(bmdw,bmdw,false);
  216.         private var hcx:Vector.<BitmapData> = Vector.<BitmapData>([
  217.             new BitmapData(bmdw, bmdw>>1false), 
  218.             new BitmapData(bmdw, bmdw>>1false)
  219.         ]);
  220.         private var hcy:Vector.<BitmapData> = Vector.<BitmapData>([
  221.             new BitmapData(bmdw, bmdw>>1false), 
  222.             new BitmapData(bmdw, bmdw>>1false)
  223.         ]);
  224.         private var rect:Rectangle = new Rectangle();
  225.         public function calculateAmbientOcclusion(mesh:Mesh, prims:Vector.<AOPrimitive>, aoBuffer:Vector.<Number>) : void {
  226.             var x:Number, y:Number, z:Number, w:Number, t:Number, v2:Vector3D, p:AOPrimitive, 
  227.                 i:int=0, j:int=0, k:int, hci:int, ao:int, ix:int, iy:int
  228.                 hhc:int = bmdw>>1, hc0s:int = bmdw*bmdw, hcxs:int = hc0s>>1, hcxf:int = bmdw-1
  229.                 vnormals:Vector.<Vector3D> = mesh.vnormals, 
  230.                 vertices:Vector.<Number> = mesh.verticesOnWorld;
  231.             _timer.start(1);
  232.             for each (v2 in vnormals) {
  233.                 v1.x = 0.0;
  234.                 v1.y = 0.0;
  235.                 v1.z = 0.0;
  236.                      if ((v2.y < 0.6) && (v2.y > -0.6)) v1.y = -1.0;
  237.                 else if ((v2.z < 0.6) && (v2.z > -0.6)) v1.z = 1.0;
  238.                 else v1.x = 1.0;
  239.                 v0.x = v1.y * v2.z - v1.z * v2.y;
  240.                 v0.y = v1.z * v2.x - v1.x * v2.z;
  241.                 v0.z = v1.x * v2.y - v1.y * v2.x;
  242.                 v0.normalize();
  243.                 v1.x = v2.y * v0.z - v2.z * v0.y;
  244.                 v1.y = v2.z * v0.x - v2.x * v0.z;
  245.                 v1.z = v2.x * v0.y - v2.y * v0.x;
  246.                 v1.normalize();
  247.                 // render on hemi-cubes
  248.                 hc0.fillRect(hc0.rect, 1);
  249.                 hcx[0].fillRect(hcx[0].rect, 1);
  250.                 hcx[1].fillRect(hcx[1].rect, 1);
  251.                 hcy[0].fillRect(hcy[0].rect, 1);
  252.                 hcy[1].fillRect(hcy[1].rect, 1);
  253.                 for each (p in prims) {
  254.                     x = p.world.x - vertices[i];
  255.                     y = p.world.y - vertices[i+1];
  256.                     z = p.world.z - vertices[i+2];
  257.                     v.x = x * v0.x + y * v0.y + z * v0.z;
  258.                     v.y = x * v1.x + y * v1.y + z * v1.z;
  259.                     v.z = x * v2.x + y * v2.y + z * v2.z;
  260.                     // hemi-cube front
  261.                     if (v.z > 0) {
  262.                         t = hhc/v.z;
  263.                         w = p.aoRadius * 0.8 * t;
  264.                         rect.x = v.x * t - w + hhc;
  265.                         rect.y = v.y * t - w + hhc;
  266.                         rect.width = w + w;
  267.                         rect.height = w + w;
  268.                         hc0.fillRect(rect, 0);
  269.                     }
  270.                     // hemi-cube x direction
  271.                     if (v.x != 0) {
  272.                         if (v.x > 0) { hci = 0; t =  hhc/v.x; }
  273.                         else         { hci = 1; t = -hhc/v.x; }
  274.                         w = p.aoRadius * 0.65 * t;
  275.                         rect.x = v.y * t - w + hhc;
  276.                         rect.y = v.z * t - w;
  277.                         rect.width = w + w;
  278.                         rect.height = w + w;
  279.                         hcx[hci].fillRect(rect, 0);
  280.                     }
  281.                     // hemi-cube y direction
  282.                     if (v.y != 0) {
  283.                         if (v.y > 0) { hci = 0; t =  hhc/v.y; }
  284.                         else         { hci = 1; t = -hhc/v.y; }
  285.                         w = p.aoRadius * 0.65 * t;
  286.                         rect.x = v.x * t - w + hhc;
  287.                         rect.y = v.z * t - w;
  288.                         rect.width = w + w;
  289.                         rect.height = w + w;
  290.                         hcy[hci].fillRect(rect, 0);
  291.                     }
  292.                 }
  293.                 ao = 0;
  294.                 for (k=0; k<hc0s; k++) {
  295.                     ao += hc0.getPixel(k&hcxf, k>>hcResolution);
  296.                 }
  297.                 for (k=0; k<hcxs; k++) {
  298.                     ix = k & hcxf;
  299.                     iy = k >> hcResolution;
  300.                     ao += hcx[0].getPixel(ix, iy) + hcx[1].getPixel(ix, iy) +
  301.                           hcy[0].getPixel(ix, iy) + hcy[1].getPixel(ix, iy);
  302.                 }
  303.                 aoBuffer[j] = ao * 0.005208333333333333;
  304.                 i+=3;
  305.                 j++;
  306.             }
  307.             _timer.pause(1);
  308.         }
  309.         
  310.         public function calculateTexCoordByVertexNormal(mesh:Mesh, light:Light, aoBuffer:Vector.<Number>) : void {
  311.             var x:Number, y:Number, v:Vector3D, i:int=0, j:int=0, vnormals:Vector.<Vector3D>=mesh.vnormals,
  312.                 dir:Vector3D=light, hv:Vector3D=light.halfVector, 
  313.                 t:Number = aoBlendRatio, it:Number = 1-aoBlendRatio, offset:Number = 0;
  314.             if (!aoSwitch) {
  315.                 t = 0;
  316.                 offset = aoBlendRatio*0.8;
  317.                 it = 1-offset;
  318.             }
  319.             for each (v in vnormals) {
  320.                 x = dir.x * v.x + dir.y * v.y + dir.z * v.z;
  321.                 y = hv.x  * v.x + hv.y  * v.y + hv.z  * v.z;
  322.                 if (x<0) x = 0;
  323.                 if (y<0) y = 0;
  324.                 mesh.texCoord[i] = x*it + aoBuffer[j]*t + offset; i++; j++;
  325.                 mesh.texCoord[i] = y; i+=2;
  326.             }
  327.         }
  328.     }
  329. }
  330. import flash.display.*;
  331. import flash.text.*;
  332. import flash.geom.*;
  333. import flash.events.*;
  334. import flash.utils.getTimer;
  335. class timer extends TextField {
  336.     public var title:String = "";
  337.     private var _time:Vector.<int>;
  338.     private var _sum :Vector.<int>;
  339.     private var _stat:Vector.<String>;
  340.     private var _cnt :int;
  341.     private var _avc:int;
  342.     function timer(averagingCount:int, ...stat) : void {
  343.         _avc  = averagingCount;
  344.         _stat = Vector.<String>(stat);
  345.         _time = new Vector.<int>(stat.length, true);
  346.         _sum  = new Vector.<int>(stat.length, true);
  347.         _cnt  = new Vector.<int>(stat.length, true);
  348.         background = true;
  349.         backgroundColor = 0x00ff00;
  350.         autoSize = "left";
  351.         multiline = true;
  352.         addEventListener("enterFrame", _onEnterFrame);
  353.     }
  354.     public function start(slot:int=0) : void { _time[slot] = getTimer(); }
  355.     public function pause(slot:int=0) : void { _sum[slot] += getTimer() - _time[slot]; }
  356.     public function _onEnterFrame(e:Event) : void {
  357.         if (++_cnt == _avc) {
  358.             _cnt = 0;
  359.             var str:String = "", line:String;
  360.             for (var slot:int = 0; slot<_sum.length; slot++) {
  361.                 line = _stat[slot].replace("##"String(_sum[slot] / _avc));
  362.                 str += line + "\n";
  363.                 _sum[slot] = 0;
  364.             }
  365.             text = title + str;
  366.         }
  367.     }
  368. }
  369. // 3D Engine
  370. //----------------------------------------------------------------------------------------------------
  371. /** Core */
  372. class Render3D extends Shape {
  373.     /** model view matrix */
  374.     public var matrix:Matrix3D;
  375.     private var _meshProjected:Mesh = null;                              // projecting mesh
  376.     private var _facesProjected:Vector.<Face> = new Vector.<Face>();     // projecting face
  377.     private var _projectionMatrix:Matrix3D;                              // projection matrix
  378.     private var _matrixStac:Vector.<Matrix3D> = new Vector.<Matrix3D>(); // matrix stac
  379.     private var _cmdTriangle:Vector.<int> = Vector.<int>([1,2,2]);       // commands to draw triangle
  380.     private var _cmdQuadrangle:Vector.<int> = Vector.<int>([1,2,2,2]);   // commands to draw quadrangle
  381.     private var _data:Vector.<Number> = new Vector.<Number>(8true);    // data to draw shape
  382.     private var _clippingZ:Number;                                       // z value of clipping plane
  383.     
  384.     /** constructor */
  385.     function Render3D(focus:Number=300, clippingZ:Number=0) {
  386.         var projector:PerspectiveProjection = new PerspectiveProjection()
  387.         projector.focalLength = focus;
  388.         _projectionMatrix = projector.toMatrix3D();
  389.         _clippingZ = -clippingZ;
  390.         matrix = new Matrix3D();
  391.         _matrixStac.length = 1;
  392.         _matrixStac[0] = matrix;
  393.     }
  394.     // control matrix
  395.     //--------------------------------------------------
  396.     public function clear() : Render3D { matrix = _matrixStac[0]; _matrixStac.length = 1return this; }
  397.     public function push() : Render3D { _matrixStac.push(matrix.clone()); return this; }
  398.     public function pop() : Render3D { matrix = (_matrixStac.length == 1) ? matrix : _matrixStac.pop(); return this; }
  399.     public function id() : Render3D { matrix.identity(); return this; }
  400.     public function t(x:Number, y:Number, z:Number) : Render3D { matrix.prependTranslation(x, y, z); return this; }
  401.     public function tv(v:Vector3D) : Render3D { matrix.prependTranslation(v.x, v.y, v.z); return this; }
  402.     public function s(x:Number, y:Number, z:Number) : Render3D { matrix.prependScale(x, y, z); return this; }
  403.     public function sv(v:Vector3D) : Render3D { matrix.prependScale(v.x, v.y, v.z); return this; }
  404.     public function r(angle:Number, axis:Vector3D) : Render3D { matrix.prependRotation(angle, axis); return this; }
  405.     public function rv(v:Vector3D) : Render3D { matrix.prependRotation(v.w, v); return this; }
  406.     public function rx(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.X_AXIS); return this; }
  407.     public function ry(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Y_AXIS); return this; }
  408.     public function rz(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Z_AXIS); return this; }
  409.     
  410.     // projections
  411.     //--------------------------------------------------
  412.     /** project */
  413.     public function project(mesh:Mesh) : Render3D {
  414.         matrix.transformVectors(mesh.vertices, mesh.verticesOnWorld);
  415.         var fn:Point3D, vs:Vector.<Number> = mesh.verticesOnWorld;
  416.         var m:Vector.<Number> = matrix.rawData, 
  417.             m00:Number = m[0], m01:Number = m[1], m02:Number = m[2], 
  418.             m10:Number = m[4], m11:Number = m[5], m12:Number = m[6], 
  419.             m20:Number = m[8], m21:Number = m[9], m22:Number = m[10];
  420.         _facesProjected.length = 0;
  421.         for each (var f:Face in mesh.faces) {
  422.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2,
  423.                 x0:Number=vs[i0++], x1:Number=vs[i1++], x2:Number=vs[i2++],
  424.                 y0:Number=vs[i0++], y1:Number=vs[i1++], y2:Number=vs[i2++],
  425.                 z0:Number=vs[i0],   z1:Number=vs[i1],   z2:Number=vs[i2];
  426.             if (z0<_clippingZ && z1<_clippingZ && z2<_clippingZ) {
  427.                 fn = f.normal;
  428.                 fn.world.x = fn.x * m00 + fn.y * m10 + fn.z * m20;
  429.                 fn.world.y = fn.x * m01 + fn.y * m11 + fn.z * m21;
  430.                 fn.world.z = fn.x * m02 + fn.y * m12 + fn.z * m22;
  431.                 if (vs[f.pvi-2]*fn.world.x + vs[f.pvi-1]*fn.world.y + vs[f.pvi]*fn.world.z <= 0) {
  432.                     _facesProjected.push(f);
  433.                 }
  434.             }
  435.         }
  436.         _facesProjected.sort(function(f1:Face, f2:Face):Numberreturn vs[f2.pvi] - vs[f1.pvi]; });
  437.         _meshProjected = mesh;
  438.         return this;
  439.     }
  440.     
  441.     /** project slower than transformVectors() but Vector3D.w considerable. */
  442.     public function projectPoint3D(points:Vector.<Point3D>) : Render3D {
  443.         var m:Vector.<Number> = matrix.rawData, p:Point3D, 
  444.             m00:Number = m[0],  m01:Number = m[1],  m02:Number = m[2], 
  445.             m10:Number = m[4],  m11:Number = m[5],  m12:Number = m[6], 
  446.             m20:Number = m[8],  m21:Number = m[9],  m22:Number = m[10], 
  447.             m30:Number = m[12], m31:Number = m[13], m32:Number = m[14];
  448.         for each (p in points) {
  449.             p.world.x = p.x * m00 + p.y * m10 + p.z * m20 + p.w * m30;
  450.             p.world.y = p.x * m01 + p.y * m11 + p.z * m21 + p.w * m31;
  451.             p.world.z = p.x * m02 + p.y * m12 + p.z * m22 + p.w * m32;
  452.         }
  453.         return this;
  454.     }
  455.     // rendering
  456.     //--------------------------------------------------
  457.     /** render solid */
  458.     public function renderSolid(light:Light) : Render3D {
  459.         var idx:int, mat:Material, materials:Vector.<Material> = _meshProjected.materials,
  460.             vout:Vector.<Number> = _meshProjected.verticesOnScreen;
  461.         Utils3D.projectVectors(_projectionMatrix, _meshProjected.verticesOnWorld, vout, _meshProjected.texCoord);
  462.         graphics.clear();
  463.         for each (var face:Face in _facesProjected) {
  464.             mat = materials[face.mat];
  465.             graphics.beginFill(mat.getColor(light, face.normal.world), mat.alpha);
  466.             idx = face.i0<<1;
  467.             _data[0] = vout[idx]; idx++;
  468.             _data[1] = vout[idx];
  469.             idx = face.i1<<1;
  470.             _data[2] = vout[idx]; idx++;
  471.             _data[3] = vout[idx];
  472.             idx = face.i2<<1;
  473.             _data[4] = vout[idx]; idx++;
  474.             _data[5] = vout[idx];
  475.             if (face.i3 == -1) {
  476.                 graphics.drawPath(_cmdTriangle, _data);
  477.             } else {
  478.                 idx = face.i3<<1;
  479.                 _data[6] = vout[idx]; idx++;
  480.                 _data[7] = vout[idx];
  481.                 graphics.drawPath(_cmdQuadrangle, _data);
  482.             }
  483.             graphics.endFill();
  484.         }
  485.         return this;
  486.     }
  487.     
  488.     /** render with texture */
  489.     private var _indices:Vector.<int> = new Vector.<int>(); // temporary index list
  490.     public function renderTexture(texture:BitmapData) : Render3D {
  491.         var idx:int, mat:Material, indices:Vector.<int> = _indices;
  492.         indices.length = 0;
  493.         for each (var face:Face in _facesProjected) { indices.push(face.i0, face.i1, face.i2); }
  494.         Utils3D.projectVectors(_projectionMatrix, 
  495.                                _meshProjected.verticesOnWorld, 
  496.                                _meshProjected.verticesOnScreen, 
  497.                                _meshProjected.texCoord);
  498.         graphics.clear();
  499.         graphics.beginBitmapFill(texture, nullfalsetrue);
  500.         graphics.drawTriangles(_meshProjected.verticesOnScreen, indices, _meshProjected.texCoord);
  501.         graphics.endFill();
  502.         return this;
  503.     }
  504. }
  505. /** Point3D */
  506. class Point3D extends Vector3D {
  507.     public var world:Vector3D;
  508.     function Point3D(x:Number=0, y:Number=0, z:Number=0, w:Number=1) { super(x,y,z,w); world=clone(); }
  509. }
  510. /** Face */
  511. class Face {
  512.     public var i0:int, i1:int, i2:int, i3:int, pvi:int, mat:int, normal:Point3D;
  513.     static private var _freeList:Vector.<Face> = new Vector.<Face>();
  514.     static public function free(face:Face) : void { _freeList.push(face); }
  515.     static public function alloc(i0:int, i1:int, i2:int, i3:int, mat:int) : Face { 
  516.         var f:Face = _freeList.pop() || new Face();
  517.         f.i0=i0; f.i1=i1; f.i2=i2; f.i3=i3; f.pvi=0; f.mat=mat;
  518.         return f;
  519.     }
  520. }
  521. /** Mesh */
  522. class Mesh {
  523.     public var materials:Vector.<Material>;                 // material list
  524.     public var verticesCount:int;
  525.     public var vertices:Vector.<Number>;                    // vertex
  526.     public var verticesOnWorld:Vector.<Number>;             // vertex on camera coordinate
  527.     public var verticesOnScreen:Vector.<Number>;            // vertex on screen
  528.     public var texCoord:Vector.<Number>;                    // texture coordinate
  529.     public var faces:Vector.<Face> = new Vector.<Face>();   // face list
  530.     public var vnormals:Vector.<Vector3D>;                  // vertex normal
  531.     
  532.     /** constructor */
  533.     function Mesh(materials:Vector.<Material>) {
  534.         this.materials = materials;
  535.         this.vertices = new Vector.<Number>();
  536.         this.texCoord = new Vector.<Number>();
  537.         this.verticesOnWorld = new Vector.<Number>();
  538.         this.verticesOnScreen = new Vector.<Number>();
  539.         this.vnormals = null;
  540.         this.verticesCount = 0;
  541.     }
  542.     
  543.     /** clear all faces */
  544.     public function clear() : Mesh {
  545.         for each (var face:Face in faces) Face.free(face);
  546.         faces.length = 0;
  547.         return this;
  548.     }
  549.     
  550.     /** register face */
  551.     public function face(i0:int, i1:int, i2:int, mat:int=0) : Mesh {
  552.         faces.push(Face.alloc(i0, i1, i2, -1, mat));
  553.         return this;
  554.     }
  555.     
  556.     /** register quadrangle face. set div=true to divide into 2 triangles. */
  557.     public function qface(i0:int, i1:int, i2:int, i3:int, mat:int=0, div:Boolean=true) : Mesh {
  558.         if (div) faces.push(Face.alloc(i0, i1, i2, -1, mat), Face.alloc(i3, i2, i1, -1, mat));
  559.         else     faces.push(Face.alloc(i0, i1, i3, i2, mat));
  560.         return this;
  561.     }
  562.     
  563.     /** update face gravity point and normal */
  564.     public function updateFaces() : Mesh {
  565.         verticesCount = vertices.length/3;
  566.         var vs:Vector.<Number> = vertices;
  567.         for each (var f:Face in faces) {
  568.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2;
  569.             var x01:Number=vs[i1]-vs[i0], x02:Number=vs[i2]-vs[i0];
  570.             f.pvi = vs.length+2;
  571.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  572.             var y01:Number=vs[i1]-vs[i0], y02:Number=vs[i2]-vs[i0];
  573.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  574.             var z01:Number=vs[i1]-vs[i0], z02:Number=vs[i2]-vs[i0];
  575.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  576.             f.normal = new Point3D(y02*z01-y01*z02, z02*x01-z01*x02, x02*y01-x01*y02, 0);
  577.             f.normal.normalize();
  578.             if (f.i3 != -1) {
  579.                 var i3:int = (f.i3<<1)+f.i3;
  580.                 vs[f.pvi-2] = vs[f.pvi-2]*0.75 + vs[i3++]*0.25;
  581.                 vs[f.pvi-1] = vs[f.pvi-1]*0.75 + vs[i3++]*0.25;
  582.                 vs[f.pvi]   = vs[f.pvi]  *0.75 + vs[i3]  *0.25;
  583.             }
  584.         }
  585.         return this;
  586.     }
  587. }
  588. /** Light */
  589. class Light extends Point3D {
  590.     public var halfVector:Vector3D = new Vector3D();
  591.     
  592.     /** constructor (set position) */
  593.     function Light(x:Number=1, y:Number=1, z:Number=1) {
  594.         super(x, y, z, 0);
  595.         normalize();
  596.     }
  597.     /** projection */
  598.     public function transformBy(matrix:Matrix3D) : void {
  599.         world = matrix.deltaTransformVector(this);
  600.         halfVector.x = world.x;
  601.         halfVector.y = world.y;
  602.         halfVector.z = world.z + 1
  603.         halfVector.normalize();
  604.     }
  605. }
  606. /** Material */
  607. class Material extends BitmapData {
  608.     public var alpha:Number = 1;    // The alpha value is available for renderSolid()
  609.     public var doubleSided:int = 0// set doubleSided=-1 if double sided material
  610.     
  611.     /** constructor */
  612.     function Material(dif:int=128, spc:int=128) { super(dif, spc, false); }
  613.     
  614.     /** set color. */
  615.     public function setColor(col:uint, amb:int=64, dif:int=192, spc:int=0,  pow:Number=8) : Material {
  616.         fillRect(rect, col);
  617.         var lmap:LightMap = new LightMap(width, height);
  618.         draw(lmap.diffusion(amb, dif), nullnull"hardlight");
  619.         draw(lmap.specular (spc, pow), nullnull"add");
  620.         lmap.dispose();
  621.         return this;
  622.     }
  623.     
  624.     /** calculate color by light and normal vector. */
  625.     public function getColor(l:Light, n:Vector3D) : uint {
  626.         var dir:Vector3D = l.world, hv:Vector3D = l.halfVector;
  627.         var ln:int = int((dir.x * n.x + dir.y * n.y + dir.z * n.z) * (width-1)),
  628.             hn:int = int((hv.x  * n.x + hv.y  * n.y + hv.z  * n.z) * (height-1));
  629.         if (ln<0) ln = (-ln) & doubleSided;
  630.         if (hn<0) hn = (-hn) & doubleSided;
  631.         return getPixel(ln, hn);
  632.     }
  633. }
  634. class LightMap extends BitmapData {
  635.     function LightMap(dif:int, spc:int) { super(dif, spc, false); }
  636.     
  637.     public function diffusion(amb:int, dif:int) : BitmapData {
  638.         var col:int, rc:Rectangle = new Rectangle(001, height), ipk:Number = 1 / width;
  639.         for (rc.x=0; rc.x<width; rc.x+=1) {
  640.             col = ((rc.x * (dif - amb)) * ipk) + amb;
  641.             fillRect(rc, (col<<16)|(col<<8)|col);
  642.         }
  643.         return this;
  644.     }
  645.     
  646.     public function specular(spc:int, pow:Number) : BitmapData {
  647.         var col:int, rc:Rectangle = new Rectangle(00, width, 1),
  648.             mpk:Number = (pow + 2) * 0.15915494309189534, ipk:Number = 1 / height;
  649.         for (rc.y=0; rc.y<height; rc.y+=1) {
  650.             col = Math.pow(rc.y * ipk, pow) * spc * mpk;
  651.             if (col > 255) col = 255;
  652.             fillRect(rc, (col<<16)|(col<<8)|col);
  653.         }
  654.         return this;
  655.     }
  656. }
  657. // Ambient Occlusion Primitive
  658. class AOPrimitive extends Point3D {
  659.     public var aoPrimitives:Vector.<AOPrimitive> = new Vector.<AOPrimitive>();
  660.     public var aoRadius:Number, ao:Vector.<Number>;
  661.     function AOPrimitive(x:Number=0, y:Number=0, z:Number=0, r:Number=0, vc:int=0) {
  662.         super(x, y, z, 1);
  663.         aoRadius = r;
  664.         ao = new Vector.<Number>(vc, true);
  665.     }
  666. }
noswf
  1. // forked from keim_at_Si's Real time ambient occlusion
  2. // gouraud shading based ambient occlusion (PoC)
  3. //   mouse move to move camera, mouse click to stop motion
  4. //   press z key to switch ambient occlusion
  5. //----------------------------------------------------------------------
  6. package {
  7.     import flash.display.*;
  8.     import flash.events.*;
  9.     import flash.geom.*;
  10.     import flash.utils.*;
  11.     [SWF(width='465', height='465', backgroundColor='#000000', frameRate='30')]
  12.     public class main extends Sprite {
  13.         // settings
  14.         private const aoBlendRatio:Number = 0.4// blending ratio of a.o.
  15.         private const aoRadius:Number = 12;       // pseudo radius for a.o. calculation
  16.         private const divPlane:int = 16;         // partition number of plane
  17.         private const divSphere:int = 6;        // partition number of sphere
  18.         private const hcResolution:int = 2;      // log2 based hemi-cube resolusion 
  19.         
  20.         // 3D renders
  21.         private var _materials:Vector.<Material> = new Vector.<Material>();
  22.         private var _light:Light = new Light(1,1,1);
  23.         private var _meshSphere:Mesh = new Mesh(_materials);
  24.         private var _meshPlane :Mesh = new Mesh(_materials);
  25.         private var _screen:BitmapData = new BitmapData(465465false0);
  26.         private var _matscr:Matrix = new Matrix(1001232.5232.5);
  27.         private var gl:Render3D = new Render3D(250);
  28.         // utils
  29.         private var _timer:timer = new timer(10"Total: ##[ms/frame]""AO calc: ##[ms/frame]",  "Rendering: ##[ms/frame]");
  30.         
  31.         // objects
  32.         private var camera:Vector3D;
  33.         private var plane:AOPrimitive;
  34.         private var spheres:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  35.         private var aoShade:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  36.         private var spheresProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  37.         private var aoShadeProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  38.         
  39.         // motions
  40.         private var motionRadius:Vector.<Number> = Vector.<Number>([  10,  -20,   32]);
  41.         private var motionFreq  :Vector.<Number> = Vector.<Number>([0.200.07,-0.12]);
  42.         private var motionFreqY :Vector.<Number> = Vector.<Number>([0.120.250.05]);
  43.         private var vertexCount:int;
  44.         private var frame:int = 0;
  45.         private var frameStep:int = 1;
  46.         private var aoSwitch:Boolean = true;
  47.         
  48.         // entry point
  49.         function main() {
  50.             var sphereVerticesCount:int = ((divSphere>>1)-1)*divSphere+2,
  51.                 planeVerticesCount:int  = (divPlane+1)*(divPlane+1);
  52.             vertexCount = planeVerticesCount + sphereVerticesCount*3;
  53.             _timer.title = "Vertices: " + String(vertexCount) + "\n";
  54.             
  55.             camera = new Vector3D(0, -5, -50);
  56.             plane  = new AOPrimitive(0000, planeVerticesCount);
  57.             spheresProjected[0] = spheres[0] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  58.             spheresProjected[1] = spheres[1] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  59.             spheresProjected[2] = spheres[2] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  60.             aoShadeProjected[0] = aoShade[0] = new AOPrimitive(0,-60, aoRadius);
  61.             aoShadeProjected[1] = aoShade[1] = new AOPrimitive(0,-60, aoRadius);
  62.             aoShadeProjected[2] = aoShade[2] = new AOPrimitive(0,-60, aoRadius);
  63.             plane.aoPrimitives.push(spheres[0], spheres[1], spheres[2]);
  64.             spheres[0].aoPrimitives.push(aoShade[0], spheres[1], spheres[2]);
  65.             spheres[1].aoPrimitives.push(spheres[0], aoShade[1], spheres[2]);
  66.             spheres[2].aoPrimitives.push(spheres[0], spheres[1], aoShade[2]);
  67.             _materials.push((new Material()).setColor(0xffffff, 01283280));
  68.             _createSphere(_meshSphere, 5, divSphere>>1, divSphere);
  69.             _createPlane(_meshPlane, 8080, divPlane, divPlane);
  70.             
  71.             addChild(gl).visible = false;
  72.             addChild(new Bitmap(_screen));
  73.             addChild(_timer);
  74.             addEventListener("enterFrame", _onEnterFrame);
  75.             stage.addEventListener("click", _onClick);
  76.             stage.addEventListener("keyUp", _onKeyUp);
  77.         }
  78.         private function _onEnterFrame(e:Event) : void {
  79.             // motion
  80.             for (var i:int=0; i<3; i++) {
  81.                 spheres[i].x = Math.cos(frame*motionFreq[i]) * motionRadius[i];
  82.                 spheres[i].y = Math.sin(frame*motionFreqY[i]) * 5 + 10;
  83.                 spheres[i].z = Math.sin(frame*motionFreq[i]) * motionRadius[i];
  84.                 aoShade[i].x = spheres[i].x;
  85.                 aoShade[i].y = -5;
  86.                 aoShade[i].z = spheres[i].z;
  87.             }
  88.             
  89.             _timer.start(0);
  90.             // clear screen
  91.             _screen.fillRect(_screen.rect, 0);
  92.             // camera
  93.             gl.id().tv(camera).rx((400-mouseY)*0.15).ry((232-mouseX)*0.5);
  94.             _light.transformBy(gl.matrix);
  95.             // project all primitives
  96.             gl.projectPoint3D(spheresProjected);
  97.             gl.projectPoint3D(aoShadeProjected);
  98.             spheresProjected.sort(function(v1:Point3D, v2:Point3D) : Number { return v1.world.z - v2.world.z; });
  99.             // plane
  100.             gl.push().tv(plane).project(_meshPlane);
  101.             calculateVertexNormal(_meshPlane);
  102.             if (frameStep) calculateAmbientOcclusion(_meshPlane, plane.aoPrimitives, plane.ao);
  103.             calculateTexCoordByVertexNormal(_meshPlane, _light, plane.ao);
  104.             _timer.start(2); 
  105.             _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  106.             _timer.pause(2);
  107.             gl.pop();
  108.             // spheres
  109.             for each (var v:Point3D in spheresProjected) {
  110.                 var p:AOPrimitive = AOPrimitive(v);
  111.                 gl.push().tv(p).project(_meshSphere);
  112.                 calculateVertexNormal(_meshSphere);
  113.                 if (frameStep) calculateAmbientOcclusion(_meshSphere, p.aoPrimitives, p.ao);
  114.                 calculateTexCoordByVertexNormal(_meshSphere, _light, p.ao);
  115.                 _timer.start(2);
  116.                 _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  117.                 _timer.pause(2);
  118.                 gl.pop();
  119.             }
  120.             _timer.pause(0);
  121.             
  122.             frame += frameStep;
  123.         }
  124.         
  125.         private function _onClick(e:Event) : void {
  126.             frameStep = 1 - frameStep;
  127.         }
  128.         
  129.         private function _onKeyUp(e:KeyboardEvent) : void {
  130.             var inkey:String = String.fromCharCode(e.keyCode);
  131.             if (inkey == "Z") aoSwitch = !aoSwitch;
  132.         }
  133.         
  134.         private function _createSphere(mesh:Mesh, radius:Number, dlat:int, dlong:int) : Mesh {
  135.             var ilat:int, ilong:int, lat:Number, long:Number, r:Number, vidx:int
  136.             slat:Number = 3.141592653589793/dlat, slong:Number  = 6.283185307179586/dlong;
  137.             mesh.vertices.push(0, radius, 0);
  138.             mesh.texCoord.push(000);
  139.             for (ilat=1, lat=slat; ilat<dlat; ilat++, lat+=slat) {
  140.                 r = Math.sin(lat) * radius;
  141.                 for (ilong=0, long=-ilat*slong*0.5; ilong<dlong; ilong++, long+=slong) {
  142.                     mesh.vertices.push(Math.cos(long) * r, Math.cos(lat) * radius, Math.sin(long) * r);
  143.                     mesh.texCoord.push(000);
  144.                 }
  145.             }
  146.             mesh.vertices.push(0, -radius, 0);
  147.             mesh.texCoord.push(000);
  148.             for (ilat=0; ilat<dlat-2; ilat++) {
  149.                 vidx = ilat * dlong + 1;
  150.                 for (ilong=1; ilong<dlong; ilong++, vidx++) {
  151.                     mesh.qface(vidx+1, vidx, vidx+dlong+1, vidx+dlong, 0true);
  152.                 }
  153.                 mesh.qface(vidx-dlong+1, vidx, vidx+1, vidx+dlong, 0true);
  154.             }
  155.             vidx = dlong * (dlat-1) + 1;
  156.             for (ilong=0; ilong<dlong-1; ilong++) {
  157.                 mesh.face(0, ilong+1, ilong+20);
  158.                 mesh.face(vidx, vidx-(ilong+1), vidx-(ilong+2), 0);
  159.             }
  160.             mesh.face(0, ilong+110);
  161.             mesh.face(vidx, vidx-(ilong+1), vidx-10);
  162.             mesh.updateFaces();
  163.             
  164.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  165.             for (var i:int=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  166.             return mesh;
  167.         }
  168.         
  169.         private function _createPlane(mesh:Mesh, w:Number, h:Number, divx:int, divy:int) : Mesh {
  170.             var x:Number, y:Number, ix:int, iy:int, i:int, j:int
  171.                 hw:Number=w*0.5, hh:Number=h*0.5, dx:Number=1/divx, dy:Number=1/divy;
  172.             for (y=0, iy=0; iy<=divy; y+=dy, iy++) {
  173.                 for (x=0, ix=0; ix<=divx; x+=dx, ix++) {
  174.                     mesh.vertices.push(x*w-hw, 0, y*h-hh);
  175.                     mesh.texCoord.push(x, y, 0);
  176.                 }
  177.             }
  178.             for (iy=0; iy<divy; iy++) {
  179.                 for (ix=0; ix<divx; ix++) {
  180.                     i = iy*(divy+1)+ix;
  181.                     j = i + divy+1;
  182.                     mesh.qface(i, i+1, j, j+10true);
  183.                 }
  184.             }
  185.             mesh.updateFaces();
  186.             
  187.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  188.             for (i=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  189.             return mesh;
  190.         }
  191.         
  192.         public function calculateVertexNormal(mesh:Mesh) : void {
  193.             var i:int, v:Vector3D, fn:Vector3D, n:Number, vnormals:Vector.<Vector3D>=mesh.vnormals;
  194.             for each (v in vnormals) {
  195.                 v.x = v.y = v.z = v.w = 0;
  196.             }
  197.             for each (var face:Face in mesh.faces) {
  198.                 fn = face.normal.world;
  199.                 v = vnormals[face.i0]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  200.                 v = vnormals[face.i1]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  201.                 v = vnormals[face.i2]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  202.             }
  203.             for each (v in vnormals) {
  204.                 if (v.w == 0continue;
  205.                 n = 1/v.w;
  206.                 v.x *= n;
  207.                 v.y *= n;
  208.                 v.z *= n;
  209.             }
  210.             mesh.vnormals = vnormals;
  211.         }
  212.         
  213.         private const bmdw:int = 1<<hcResolution;
  214.         private var v0:Vector3D = new Vector3D(), v1:Vector3D = new Vector3D(), v:Vector3D = new Vector3D();
  215.         private var hc0:BitmapData = new BitmapData(bmdw,bmdw,false);
  216.         private var hcx:Vector.<BitmapData> = Vector.<BitmapData>([
  217.             new BitmapData(bmdw, bmdw>>1false), 
  218.             new BitmapData(bmdw, bmdw>>1false)
  219.         ]);
  220.         private var hcy:Vector.<BitmapData> = Vector.<BitmapData>([
  221.             new BitmapData(bmdw, bmdw>>1false), 
  222.             new BitmapData(bmdw, bmdw>>1false)
  223.         ]);
  224.         private var rect:Rectangle = new Rectangle();
  225.         public function calculateAmbientOcclusion(mesh:Mesh, prims:Vector.<AOPrimitive>, aoBuffer:Vector.<Number>) : void {
  226.             var x:Number, y:Number, z:Number, w:Number, t:Number, v2:Vector3D, p:AOPrimitive, 
  227.                 i:int=0, j:int=0, k:int, hci:int, ao:int, ix:int, iy:int
  228.                 hhc:int = bmdw>>1, hc0s:int = bmdw*bmdw, hcxs:int = hc0s>>1, hcxf:int = bmdw-1
  229.                 vnormals:Vector.<Vector3D> = mesh.vnormals, 
  230.                 vertices:Vector.<Number> = mesh.verticesOnWorld;
  231.             _timer.start(1);
  232.             for each (v2 in vnormals) {
  233.                 v1.x = 0.0;
  234.                 v1.y = 0.0;
  235.                 v1.z = 0.0;
  236.                      if ((v2.y < 0.6) && (v2.y > -0.6)) v1.y = -1.0;
  237.                 else if ((v2.z < 0.6) && (v2.z > -0.6)) v1.z = 1.0;
  238.                 else v1.x = 1.0;
  239.                 v0.x = v1.y * v2.z - v1.z * v2.y;
  240.                 v0.y = v1.z * v2.x - v1.x * v2.z;
  241.                 v0.z = v1.x * v2.y - v1.y * v2.x;
  242.                 v0.normalize();
  243.                 v1.x = v2.y * v0.z - v2.z * v0.y;
  244.                 v1.y = v2.z * v0.x - v2.x * v0.z;
  245.                 v1.z = v2.x * v0.y - v2.y * v0.x;
  246.                 v1.normalize();
  247.                 // render on hemi-cubes
  248.                 hc0.fillRect(hc0.rect, 1);
  249.                 hcx[0].fillRect(hcx[0].rect, 1);
  250.                 hcx[1].fillRect(hcx[1].rect, 1);
  251.                 hcy[0].fillRect(hcy[0].rect, 1);
  252.                 hcy[1].fillRect(hcy[1].rect, 1);
  253.                 for each (p in prims) {
  254.                     x = p.world.x - vertices[i];
  255.                     y = p.world.y - vertices[i+1];
  256.                     z = p.world.z - vertices[i+2];
  257.                     v.x = x * v0.x + y * v0.y + z * v0.z;
  258.                     v.y = x * v1.x + y * v1.y + z * v1.z;
  259.                     v.z = x * v2.x + y * v2.y + z * v2.z;
  260.                     // hemi-cube front
  261.                     if (v.z > 0) {
  262.                         t = hhc/v.z;
  263.                         w = p.aoRadius * 0.8 * t;
  264.                         rect.x = v.x * t - w + hhc;
  265.                         rect.y = v.y * t - w + hhc;
  266.                         rect.width = w + w;
  267.                         rect.height = w + w;
  268.                         hc0.fillRect(rect, 0);
  269.                     }
  270.                     // hemi-cube x direction
  271.                     if (v.x != 0) {
  272.                         if (v.x > 0) { hci = 0; t =  hhc/v.x; }
  273.                         else         { hci = 1; t = -hhc/v.x; }
  274.                         w = p.aoRadius * 0.65 * t;
  275.                         rect.x = v.y * t - w + hhc;
  276.                         rect.y = v.z * t - w;
  277.                         rect.width = w + w;
  278.                         rect.height = w + w;
  279.                         hcx[hci].fillRect(rect, 0);
  280.                     }
  281.                     // hemi-cube y direction
  282.                     if (v.y != 0) {
  283.                         if (v.y > 0) { hci = 0; t =  hhc/v.y; }
  284.                         else         { hci = 1; t = -hhc/v.y; }
  285.                         w = p.aoRadius * 0.65 * t;
  286.                         rect.x = v.x * t - w + hhc;
  287.                         rect.y = v.z * t - w;
  288.                         rect.width = w + w;
  289.                         rect.height = w + w;
  290.                         hcy[hci].fillRect(rect, 0);
  291.                     }
  292.                 }
  293.                 ao = 0;
  294.                 for (k=0; k<hc0s; k++) {
  295.                     ao += hc0.getPixel(k&hcxf, k>>hcResolution);
  296.                 }
  297.                 for (k=0; k<hcxs; k++) {
  298.                     ix = k & hcxf;
  299.                     iy = k >> hcResolution;
  300.                     ao += hcx[0].getPixel(ix, iy) + hcx[1].getPixel(ix, iy) +
  301.                           hcy[0].getPixel(ix, iy) + hcy[1].getPixel(ix, iy);
  302.                 }
  303.                 aoBuffer[j] = ao * 0.005208333333333333;
  304.                 i+=3;
  305.                 j++;
  306.             }
  307.             _timer.pause(1);
  308.         }
  309.         
  310.         public function calculateTexCoordByVertexNormal(mesh:Mesh, light:Light, aoBuffer:Vector.<Number>) : void {
  311.             var x:Number, y:Number, v:Vector3D, i:int=0, j:int=0, vnormals:Vector.<Vector3D>=mesh.vnormals,
  312.                 dir:Vector3D=light, hv:Vector3D=light.halfVector, 
  313.                 t:Number = aoBlendRatio, it:Number = 1-aoBlendRatio, offset:Number = 0;
  314.             if (!aoSwitch) {
  315.                 t = 0;
  316.                 offset = aoBlendRatio*0.8;
  317.                 it = 1-offset;
  318.             }
  319.             for each (v in vnormals) {
  320.                 x = dir.x * v.x + dir.y * v.y + dir.z * v.z;
  321.                 y = hv.x  * v.x + hv.y  * v.y + hv.z  * v.z;
  322.                 if (x<0) x = 0;
  323.                 if (y<0) y = 0;
  324.                 mesh.texCoord[i] = x*it + aoBuffer[j]*t + offset; i++; j++;
  325.                 mesh.texCoord[i] = y; i+=2;
  326.             }
  327.         }
  328.     }
  329. }
  330. import flash.display.*;
  331. import flash.text.*;
  332. import flash.geom.*;
  333. import flash.events.*;
  334. import flash.utils.getTimer;
  335. class timer extends TextField {
  336.     public var title:String = "";
  337.     private var _time:Vector.<int>;
  338.     private var _sum :Vector.<int>;
  339.     private var _stat:Vector.<String>;
  340.     private var _cnt :int;
  341.     private var _avc:int;
  342.     function timer(averagingCount:int, ...stat) : void {
  343.         _avc  = averagingCount;
  344.         _stat = Vector.<String>(stat);
  345.         _time = new Vector.<int>(stat.length, true);
  346.         _sum  = new Vector.<int>(stat.length, true);
  347.         _cnt  = new Vector.<int>(stat.length, true);
  348.         background = true;
  349.         backgroundColor = 0x00ff00;
  350.         autoSize = "left";
  351.         multiline = true;
  352.         addEventListener("enterFrame", _onEnterFrame);
  353.     }
  354.     public function start(slot:int=0) : void { _time[slot] = getTimer(); }
  355.     public function pause(slot:int=0) : void { _sum[slot] += getTimer() - _time[slot]; }
  356.     public function _onEnterFrame(e:Event) : void {
  357.         if (++_cnt == _avc) {
  358.             _cnt = 0;
  359.             var str:String = "", line:String;
  360.             for (var slot:int = 0; slot<_sum.length; slot++) {
  361.                 line = _stat[slot].replace("##"String(_sum[slot] / _avc));
  362.                 str += line + "\n";
  363.                 _sum[slot] = 0;
  364.             }
  365.             text = title + str;
  366.         }
  367.     }
  368. }
  369. // 3D Engine
  370. //----------------------------------------------------------------------------------------------------
  371. /** Core */
  372. class Render3D extends Shape {
  373.     /** model view matrix */
  374.     public var matrix:Matrix3D;
  375.     private var _meshProjected:Mesh = null;                              // projecting mesh
  376.     private var _facesProjected:Vector.<Face> = new Vector.<Face>();     // projecting face
  377.     private var _projectionMatrix:Matrix3D;                              // projection matrix
  378.     private var _matrixStac:Vector.<Matrix3D> = new Vector.<Matrix3D>(); // matrix stac
  379.     private var _cmdTriangle:Vector.<int> = Vector.<int>([1,2,2]);       // commands to draw triangle
  380.     private var _cmdQuadrangle:Vector.<int> = Vector.<int>([1,2,2,2]);   // commands to draw quadrangle
  381.     private var _data:Vector.<Number> = new Vector.<Number>(8true);    // data to draw shape
  382.     private var _clippingZ:Number;                                       // z value of clipping plane
  383.     
  384.     /** constructor */
  385.     function Render3D(focus:Number=300, clippingZ:Number=0) {
  386.         var projector:PerspectiveProjection = new PerspectiveProjection()
  387.         projector.focalLength = focus;
  388.         _projectionMatrix = projector.toMatrix3D();
  389.         _clippingZ = -clippingZ;
  390.         matrix = new Matrix3D();
  391.         _matrixStac.length = 1;
  392.         _matrixStac[0] = matrix;
  393.     }
  394.     // control matrix
  395.     //--------------------------------------------------
  396.     public function clear() : Render3D { matrix = _matrixStac[0]; _matrixStac.length = 1return this; }
  397.     public function push() : Render3D { _matrixStac.push(matrix.clone()); return this; }
  398.     public function pop() : Render3D { matrix = (_matrixStac.length == 1) ? matrix : _matrixStac.pop(); return this; }
  399.     public function id() : Render3D { matrix.identity(); return this; }
  400.     public function t(x:Number, y:Number, z:Number) : Render3D { matrix.prependTranslation(x, y, z); return this; }
  401.     public function tv(v:Vector3D) : Render3D { matrix.prependTranslation(v.x, v.y, v.z); return this; }
  402.     public function s(x:Number, y:Number, z:Number) : Render3D { matrix.prependScale(x, y, z); return this; }
  403.     public function sv(v:Vector3D) : Render3D { matrix.prependScale(v.x, v.y, v.z); return this; }
  404.     public function r(angle:Number, axis:Vector3D) : Render3D { matrix.prependRotation(angle, axis); return this; }
  405.     public function rv(v:Vector3D) : Render3D { matrix.prependRotation(v.w, v); return this; }
  406.     public function rx(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.X_AXIS); return this; }
  407.     public function ry(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Y_AXIS); return this; }
  408.     public function rz(angle:Number) : Render3D { matrix.prependRotation(angle, Vector3D.Z_AXIS); return this; }
  409.     
  410.     // projections
  411.     //--------------------------------------------------
  412.     /** project */
  413.     public function project(mesh:Mesh) : Render3D {
  414.         matrix.transformVectors(mesh.vertices, mesh.verticesOnWorld);
  415.         var fn:Point3D, vs:Vector.<Number> = mesh.verticesOnWorld;
  416.         var m:Vector.<Number> = matrix.rawData, 
  417.             m00:Number = m[0], m01:Number = m[1], m02:Number = m[2], 
  418.             m10:Number = m[4], m11:Number = m[5], m12:Number = m[6], 
  419.             m20:Number = m[8], m21:Number = m[9], m22:Number = m[10];
  420.         _facesProjected.length = 0;
  421.         for each (var f:Face in mesh.faces) {
  422.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2,
  423.                 x0:Number=vs[i0++], x1:Number=vs[i1++], x2:Number=vs[i2++],
  424.                 y0:Number=vs[i0++], y1:Number=vs[i1++], y2:Number=vs[i2++],
  425.                 z0:Number=vs[i0],   z1:Number=vs[i1],   z2:Number=vs[i2];
  426.             if (z0<_clippingZ && z1<_clippingZ && z2<_clippingZ) {
  427.                 fn = f.normal;
  428.                 fn.world.x = fn.x * m00 + fn.y * m10 + fn.z * m20;
  429.                 fn.world.y = fn.x * m01 + fn.y * m11 + fn.z * m21;
  430.                 fn.world.z = fn.x * m02 + fn.y * m12 + fn.z * m22;
  431.                 if (vs[f.pvi-2]*fn.world.x + vs[f.pvi-1]*fn.world.y + vs[f.pvi]*fn.world.z <= 0) {
  432.                     _facesProjected.push(f);
  433.                 }
  434.             }
  435.         }
  436.         _facesProjected.sort(function(f1:Face, f2:Face):Numberreturn vs[f2.pvi] - vs[f1.pvi]; });
  437.         _meshProjected = mesh;
  438.         return this;
  439.     }
  440.     
  441.     /** project slower than transformVectors() but Vector3D.w considerable. */
  442.     public function projectPoint3D(points:Vector.<Point3D>) : Render3D {
  443.         var m:Vector.<Number> = matrix.rawData, p:Point3D, 
  444.             m00:Number = m[0],  m01:Number = m[1],  m02:Number = m[2], 
  445.             m10:Number = m[4],  m11:Number = m[5],  m12:Number = m[6], 
  446.             m20:Number = m[8],  m21:Number = m[9],  m22:Number = m[10], 
  447.             m30:Number = m[12], m31:Number = m[13], m32:Number = m[14];
  448.         for each (p in points) {
  449.             p.world.x = p.x * m00 + p.y * m10 + p.z * m20 + p.w * m30;
  450.             p.world.y = p.x * m01 + p.y * m11 + p.z * m21 + p.w * m31;
  451.             p.world.z = p.x * m02 + p.y * m12 + p.z * m22 + p.w * m32;
  452.         }
  453.         return this;
  454.     }
  455.     // rendering
  456.     //--------------------------------------------------
  457.     /** render solid */
  458.     public function renderSolid(light:Light) : Render3D {
  459.         var idx:int, mat:Material, materials:Vector.<Material> = _meshProjected.materials,
  460.             vout:Vector.<Number> = _meshProjected.verticesOnScreen;
  461.         Utils3D.projectVectors(_projectionMatrix, _meshProjected.verticesOnWorld, vout, _meshProjected.texCoord);
  462.         graphics.clear();
  463.         for each (var face:Face in _facesProjected) {
  464.             mat = materials[face.mat];
  465.             graphics.beginFill(mat.getColor(light, face.normal.world), mat.alpha);
  466.             idx = face.i0<<1;
  467.             _data[0] = vout[idx]; idx++;
  468.             _data[1] = vout[idx];
  469.             idx = face.i1<<1;
  470.             _data[2] = vout[idx]; idx++;
  471.             _data[3] = vout[idx];
  472.             idx = face.i2<<1;
  473.             _data[4] = vout[idx]; idx++;
  474.             _data[5] = vout[idx];
  475.             if (face.i3 == -1) {
  476.                 graphics.drawPath(_cmdTriangle, _data);
  477.             } else {
  478.                 idx = face.i3<<1;
  479.                 _data[6] = vout[idx]; idx++;
  480.                 _data[7] = vout[idx];
  481.                 graphics.drawPath(_cmdQuadrangle, _data);
  482.             }
  483.             graphics.endFill();
  484.         }
  485.         return this;
  486.     }
  487.     
  488.     /** render with texture */
  489.     private var _indices:Vector.<int> = new Vector.<int>(); // temporary index list
  490.     public function renderTexture(texture:BitmapData) : Render3D {
  491.         var idx:int, mat:Material, indices:Vector.<int> = _indices;
  492.         indices.length = 0;
  493.         for each (var face:Face in _facesProjected) { indices.push(face.i0, face.i1, face.i2); }
  494.         Utils3D.projectVectors(_projectionMatrix, 
  495.                                _meshProjected.verticesOnWorld, 
  496.                                _meshProjected.verticesOnScreen, 
  497.                                _meshProjected.texCoord);
  498.         graphics.clear();
  499.         graphics.beginBitmapFill(texture, nullfalsetrue);
  500.         graphics.drawTriangles(_meshProjected.verticesOnScreen, indices, _meshProjected.texCoord);
  501.         graphics.endFill();
  502.         return this;
  503.     }
  504. }
  505. /** Point3D */
  506. class Point3D extends Vector3D {
  507.     public var world:Vector3D;
  508.     function Point3D(x:Number=0, y:Number=0, z:Number=0, w:Number=1) { super(x,y,z,w); world=clone(); }
  509. }
  510. /** Face */
  511. class Face {
  512.     public var i0:int, i1:int, i2:int, i3:int, pvi:int, mat:int, normal:Point3D;
  513.     static private var _freeList:Vector.<Face> = new Vector.<Face>();
  514.     static public function free(face:Face) : void { _freeList.push(face); }
  515.     static public function alloc(i0:int, i1:int, i2:int, i3:int, mat:int) : Face { 
  516.         var f:Face = _freeList.pop() || new Face();
  517.         f.i0=i0; f.i1=i1; f.i2=i2; f.i3=i3; f.pvi=0; f.mat=mat;
  518.         return f;
  519.     }
  520. }
  521. /** Mesh */
  522. class Mesh {
  523.     public var materials:Vector.<Material>;                 // material list
  524.     public var verticesCount:int;
  525.     public var vertices:Vector.<Number>;                    // vertex
  526.     public var verticesOnWorld:Vector.<Number>;             // vertex on camera coordinate
  527.     public var verticesOnScreen:Vector.<Number>;            // vertex on screen
  528.     public var texCoord:Vector.<Number>;                    // texture coordinate
  529.     public var faces:Vector.<Face> = new Vector.<Face>();   // face list
  530.     public var vnormals:Vector.<Vector3D>;                  // vertex normal
  531.     
  532.     /** constructor */
  533.     function Mesh(materials:Vector.<Material>) {
  534.         this.materials = materials;
  535.         this.vertices = new Vector.<Number>();
  536.         this.texCoord = new Vector.<Number>();
  537.         this.verticesOnWorld = new Vector.<Number>();
  538.         this.verticesOnScreen = new Vector.<Number>();
  539.         this.vnormals = null;
  540.         this.verticesCount = 0;
  541.     }
  542.     
  543.     /** clear all faces */
  544.     public function clear() : Mesh {
  545.         for each (var face:Face in faces) Face.free(face);
  546.         faces.length = 0;
  547.         return this;
  548.     }
  549.     
  550.     /** register face */
  551.     public function face(i0:int, i1:int, i2:int, mat:int=0) : Mesh {
  552.         faces.push(Face.alloc(i0, i1, i2, -1, mat));
  553.         return this;
  554.     }
  555.     
  556.     /** register quadrangle face. set div=true to divide into 2 triangles. */
  557.     public function qface(i0:int, i1:int, i2:int, i3:int, mat:int=0, div:Boolean=true) : Mesh {
  558.         if (div) faces.push(Face.alloc(i0, i1, i2, -1, mat), Face.alloc(i3, i2, i1, -1, mat));
  559.         else     faces.push(Face.alloc(i0, i1, i3, i2, mat));
  560.         return this;
  561.     }
  562.     
  563.     /** update face gravity point and normal */
  564.     public function updateFaces() : Mesh {
  565.         verticesCount = vertices.length/3;
  566.         var vs:Vector.<Number> = vertices;
  567.         for each (var f:Face in faces) {
  568.             var i0:int=(f.i0<<1)+f.i0, i1:int=(f.i1<<1)+f.i1, i2:int=(f.i2<<1)+f.i2;
  569.             var x01:Number=vs[i1]-vs[i0], x02:Number=vs[i2]-vs[i0];
  570.             f.pvi = vs.length+2;
  571.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  572.             var y01:Number=vs[i1]-vs[i0], y02:Number=vs[i2]-vs[i0];
  573.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  574.             var z01:Number=vs[i1]-vs[i0], z02:Number=vs[i2]-vs[i0];
  575.             vs.push((vs[i0++] + vs[i1++] + vs[i2++]) * 0.333333333333);
  576.             f.normal = new Point3D(y02*z01-y01*z02, z02*x01-z01*x02, x02*y01-x01*y02, 0);
  577.             f.normal.normalize();
  578.             if (f.i3 != -1) {
  579.                 var i3:int = (f.i3<<1)+f.i3;
  580.                 vs[f.pvi-2] = vs[f.pvi-2]*0.75 + vs[i3++]*0.25;
  581.                 vs[f.pvi-1] = vs[f.pvi-1]*0.75 + vs[i3++]*0.25;
  582.                 vs[f.pvi]   = vs[f.pvi]  *0.75 + vs[i3]  *0.25;
  583.             }
  584.         }
  585.         return this;
  586.     }
  587. }
  588. /** Light */
  589. class Light extends Point3D {
  590.     public var halfVector:Vector3D = new Vector3D();
  591.     
  592.     /** constructor (set position) */
  593.     function Light(x:Number=1, y:Number=1, z:Number=1) {
  594.         super(x, y, z, 0);
  595.         normalize();
  596.     }
  597.     /** projection */
  598.     public function transformBy(matrix:Matrix3D) : void {
  599.         world = matrix.deltaTransformVector(this);
  600.         halfVector.x = world.x;
  601.         halfVector.y = world.y;
  602.         halfVector.z = world.z + 1
  603.         halfVector.normalize();
  604.     }
  605. }
  606. /** Material */
  607. class Material extends BitmapData {
  608.     public var alpha:Number = 1;    // The alpha value is available for renderSolid()
  609.     public var doubleSided:int = 0// set doubleSided=-1 if double sided material
  610.     
  611.     /** constructor */
  612.     function Material(dif:int=128, spc:int=128) { super(dif, spc, false); }
  613.     
  614.     /** set color. */
  615.     public function setColor(col:uint, amb:int=64, dif:int=192, spc:int=0,  pow:Number=8) : Material {
  616.         fillRect(rect, col);
  617.         var lmap:LightMap = new LightMap(width, height);
  618.         draw(lmap.diffusion(amb, dif), nullnull"hardlight");
  619.         draw(lmap.specular (spc, pow), nullnull"add");
  620.         lmap.dispose();
  621.         return this;
  622.     }
  623.     
  624.     /** calculate color by light and normal vector. */
  625.     public function getColor(l:Light, n:Vector3D) : uint {
  626.         var dir:Vector3D = l.world, hv:Vector3D = l.halfVector;
  627.         var ln:int = int((dir.x * n.x + dir.y * n.y + dir.z * n.z) * (width-1)),
  628.             hn:int = int((hv.x  * n.x + hv.y  * n.y + hv.z  * n.z) * (height-1));
  629.         if (ln<0) ln = (-ln) & doubleSided;
  630.         if (hn<0) hn = (-hn) & doubleSided;
  631.         return getPixel(ln, hn);
  632.     }
  633. }
  634. class LightMap extends BitmapData {
  635.     function LightMap(dif:int, spc:int) { super(dif, spc, false); }
  636.     
  637.     public function diffusion(amb:int, dif:int) : BitmapData {
  638.         var col:int, rc:Rectangle = new Rectangle(001, height), ipk:Number = 1 / width;
  639.         for (rc.x=0; rc.x<width; rc.x+=1) {
  640.             col = ((rc.x * (dif - amb)) * ipk) + amb;
  641.             fillRect(rc, (col<<16)|(col<<8)|col);
  642.         }
  643.         return this;
  644.     }
  645.     
  646.     public function specular(spc:int, pow:Number) : BitmapData {
  647.         var col:int, rc:Rectangle = new Rectangle(00, width, 1),
  648.             mpk:Number = (pow + 2) * 0.15915494309189534, ipk:Number = 1 / height;
  649.         for (rc.y=0; rc.y<height; rc.y+=1) {
  650.             col = Math.pow(rc.y * ipk, pow) * spc * mpk;
  651.             if (col > 255) col = 255;
  652.             fillRect(rc, (col<<16)|(col<<8)|col);
  653.         }
  654.         return this;
  655.     }
  656. }
  657. // Ambient Occlusion Primitive
  658. class AOPrimitive extends Point3D {
  659.     public var aoPrimitives:Vector.<AOPrimitive> = new Vector.<AOPrimitive>();
  660.     public var aoRadius:Number, ao:Vector.<Number>;
  661.     function AOPrimitive(x:Number=0, y:Number=0, z:Number=0, r:Number=0, vc:int=0) {
  662.         super(x, y, z, 1);
  663.         aoRadius = r;
  664.         ao = new Vector.<Number>(vc, true);
  665.     }
  666. }
noswf
  1. // forked from keim_at_Si's Real time ambient occlusion
  2. // gouraud shading based ambient occlusion (PoC)
  3. //   mouse move to move camera, mouse click to stop motion
  4. //   press z key to switch ambient occlusion
  5. //----------------------------------------------------------------------
  6. package {
  7.     import flash.display.*;
  8.     import flash.events.*;
  9.     import flash.geom.*;
  10.     import flash.utils.*;
  11.     [SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
  12.     public class main extends Sprite {
  13.         // settings
  14.         private const aoBlendRatio:Number = 0.6// blending ratio of a.o.
  15.         private const aoRadius:Number = 8;       // pseudo radius for a.o. calculation
  16.         private const divPlane:int = 16;         // partition number of plane
  17.         private const divSphere:int = 24;        // partition number of sphere
  18.         private const hcResolution:int = 3;      // log2 based hemi-cube resolusion 
  19.         
  20.         // 3D renders
  21.         private var _materials:Vector.<Material> = new Vector.<Material>();
  22.         private var _light:Light = new Light(1,1,1);
  23.         private var _meshSphere:Mesh = new Mesh(_materials);
  24.         private var _meshPlane :Mesh = new Mesh(_materials);
  25.         private var _screen:BitmapData = new BitmapData(465465false0);
  26.         private var _matscr:Matrix = new Matrix(1001232.5232.5);
  27.         private var gl:Render3D = new Render3D(250);
  28.         // utils
  29.         private var _timer:timer = new timer(10"Total: ##[ms/frame]""AO calc: ##[ms/frame]",  "Rendering: ##[ms/frame]");
  30.         
  31.         // objects
  32.         private var camera:Vector3D;
  33.         private var plane:AOPrimitive;
  34.         private var spheres:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  35.         private var aoShade:Vector.<AOPrimitive> = new Vector.<AOPrimitive>(3);
  36.         private var spheresProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  37.         private var aoShadeProjected:Vector.<Point3D> = new Vector.<Point3D>(3);
  38.         
  39.         // motions
  40.         private var motionRadius:Vector.<Number> = Vector.<Number>([  10,  -20,   32]);
  41.         private var motionFreq  :Vector.<Number> = Vector.<Number>([0.200.07,-0.12]);
  42.         private var motionFreqY :Vector.<Number> = Vector.<Number>([0.120.250.05]);
  43.         private var vertexCount:int;
  44.         private var frame:int = 0;
  45.         private var frameStep:int = 1;
  46.         private var aoSwitch:Boolean = true;
  47.         
  48.         // entry point
  49.         function main() {
  50.             var sphereVerticesCount:int = ((divSphere>>1)-1)*divSphere+2,
  51.                 planeVerticesCount:int  = (divPlane+1)*(divPlane+1);
  52.             vertexCount = planeVerticesCount + sphereVerticesCount*3;
  53.             _timer.title = "Vertices: " + String(vertexCount) + "\n";
  54.             
  55.             camera = new Vector3D(0, -5, -50);
  56.             plane  = new AOPrimitive(0000, planeVerticesCount);
  57.             spheresProjected[0] = spheres[0] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  58.             spheresProjected[1] = spheres[1] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  59.             spheresProjected[2] = spheres[2] = new AOPrimitive(050, aoRadius, sphereVerticesCount);
  60.             aoShadeProjected[0] = aoShade[0] = new AOPrimitive(0,-60, aoRadius);
  61.             aoShadeProjected[1] = aoShade[1] = new AOPrimitive(0,-60, aoRadius);
  62.             aoShadeProjected[2] = aoShade[2] = new AOPrimitive(0,-60, aoRadius);
  63.             plane.aoPrimitives.push(spheres[0], spheres[1], spheres[2]);
  64.             spheres[0].aoPrimitives.push(aoShade[0], spheres[1], spheres[2]);
  65.             spheres[1].aoPrimitives.push(spheres[0], aoShade[1], spheres[2]);
  66.             spheres[2].aoPrimitives.push(spheres[0], spheres[1], aoShade[2]);
  67.             _materials.push((new Material()).setColor(0xffffff, 01283280));
  68.             _createSphere(_meshSphere, 5, divSphere>>1, divSphere);
  69.             _createPlane(_meshPlane, 8080, divPlane, divPlane);
  70.             
  71.             addChild(gl).visible = false;
  72.             addChild(new Bitmap(_screen));
  73.             addChild(_timer);
  74.             addEventListener("enterFrame", _onEnterFrame);
  75.             stage.addEventListener("click", _onClick);
  76.             stage.addEventListener("keyUp", _onKeyUp);
  77.         }
  78.         private function _onEnterFrame(e:Event) : void {
  79.             // motion
  80.             for (var i:int=0; i<3; i++) {
  81.                 spheres[i].x = Math.cos(frame*motionFreq[i]) * motionRadius[i];
  82.                 spheres[i].y = Math.sin(frame*motionFreqY[i]) * 5 + 10;
  83.                 spheres[i].z = Math.sin(frame*motionFreq[i]) * motionRadius[i];
  84.                 aoShade[i].x = spheres[i].x;
  85.                 aoShade[i].y = -5;
  86.                 aoShade[i].z = spheres[i].z;
  87.             }
  88.             
  89.             _timer.start(0);
  90.             // clear screen
  91.             _screen.fillRect(_screen.rect, 0);
  92.             // camera
  93.             gl.id().tv(camera).rx((400-mouseY)*0.15).ry((232-mouseX)*0.5);
  94.             _light.transformBy(gl.matrix);
  95.             // project all primitives
  96.             gl.projectPoint3D(spheresProjected);
  97.             gl.projectPoint3D(aoShadeProjected);
  98.             spheresProjected.sort(function(v1:Point3D, v2:Point3D) : Number { return v1.world.z - v2.world.z; });
  99.             // plane
  100.             gl.push().tv(plane).project(_meshPlane);
  101.             calculateVertexNormal(_meshPlane);
  102.             if (frameStep) calculateAmbientOcclusion(_meshPlane, plane.aoPrimitives, plane.ao);
  103.             calculateTexCoordByVertexNormal(_meshPlane, _light, plane.ao);
  104.             _timer.start(2); 
  105.             _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  106.             _timer.pause(2);
  107.             gl.pop();
  108.             // spheres
  109.             for each (var v:Point3D in spheresProjected) {
  110.                 var p:AOPrimitive = AOPrimitive(v);
  111.                 gl.push().tv(p).project(_meshSphere);
  112.                 calculateVertexNormal(_meshSphere);
  113.                 if (frameStep) calculateAmbientOcclusion(_meshSphere, p.aoPrimitives, p.ao);
  114.                 calculateTexCoordByVertexNormal(_meshSphere, _light, p.ao);
  115.                 _timer.start(2);
  116.                 _screen.draw(gl.renderTexture(_materials[0]), _matscr);
  117.                 _timer.pause(2);
  118.                 gl.pop();
  119.             }
  120.             _timer.pause(0);
  121.             
  122.             frame += frameStep;
  123.         }
  124.         
  125.         private function _onClick(e:Event) : void {
  126.             frameStep = 1 - frameStep;
  127.         }
  128.         
  129.         private function _onKeyUp(e:KeyboardEvent) : void {
  130.             var inkey:String = String.fromCharCode(e.keyCode);
  131.             if (inkey == "Z") aoSwitch = !aoSwitch;
  132.         }
  133.         
  134.         private function _createSphere(mesh:Mesh, radius:Number, dlat:int, dlong:int) : Mesh {
  135.             var ilat:int, ilong:int, lat:Number, long:Number, r:Number, vidx:int
  136.             slat:Number = 3.141592653589793/dlat, slong:Number  = 6.283185307179586/dlong;
  137.             mesh.vertices.push(0, radius, 0);
  138.             mesh.texCoord.push(000);
  139.             for (ilat=1, lat=slat; ilat<dlat; ilat++, lat+=slat) {
  140.                 r = Math.sin(lat) * radius;
  141.                 for (ilong=0, long=-ilat*slong*0.5; ilong<dlong; ilong++, long+=slong) {
  142.                     mesh.vertices.push(Math.cos(long) * r, Math.cos(lat) * radius, Math.sin(long) * r);
  143.                     mesh.texCoord.push(000);
  144.                 }
  145.             }
  146.             mesh.vertices.push(0, -radius, 0);
  147.             mesh.texCoord.push(000);
  148.             for (ilat=0; ilat<dlat-2; ilat++) {
  149.                 vidx = ilat * dlong + 1;
  150.                 for (ilong=1; ilong<dlong; ilong++, vidx++) {
  151.                     mesh.qface(vidx+1, vidx, vidx+dlong+1, vidx+dlong, 0true);
  152.                 }
  153.                 mesh.qface(vidx-dlong+1, vidx, vidx+1, vidx+dlong, 0true);
  154.             }
  155.             vidx = dlong * (dlat-1) + 1;
  156.             for (ilong=0; ilong<dlong-1; ilong++) {
  157.                 mesh.face(0, ilong+1, ilong+20);
  158.                 mesh.face(vidx, vidx-(ilong+1), vidx-(ilong+2), 0);
  159.             }
  160.             mesh.face(0, ilong+110);
  161.             mesh.face(vidx, vidx-(ilong+1), vidx-10);
  162.             mesh.updateFaces();
  163.             
  164.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  165.             for (var i:int=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  166.             return mesh;
  167.         }
  168.         
  169.         private function _createPlane(mesh:Mesh, w:Number, h:Number, divx:int, divy:int) : Mesh {
  170.             var x:Number, y:Number, ix:int, iy:int, i:int, j:int
  171.                 hw:Number=w*0.5, hh:Number=h*0.5, dx:Number=1/divx, dy:Number=1/divy;
  172.             for (y=0, iy=0; iy<=divy; y+=dy, iy++) {
  173.                 for (x=0, ix=0; ix<=divx; x+=dx, ix++) {
  174.                     mesh.vertices.push(x*w-hw, 0, y*h-hh);
  175.                     mesh.texCoord.push(x, y, 0);
  176.                 }
  177.             }
  178.             for (iy=0; iy<divy; iy++) {
  179.                 for (ix=0; ix<divx; ix++) {
  180.                     i = iy*(divy+1)+ix;
  181.                     j = i + divy+1;
  182.                     mesh.qface(i, i+1, j, j+10true);
  183.                 }
  184.             }
  185.             mesh.updateFaces();
  186.             
  187.             mesh.vnormals = new Vector.<Vector3D>(mesh.verticesCount, true);
  188.             for (i=0; i<mesh.verticesCount; i++) mesh.vnormals[i] = new Vector3D();
  189.             return mesh;
  190.         }
  191.         
  192.         public function calculateVertexNormal(mesh:Mesh) : void {
  193.             var i:int, v:Vector3D, fn:Vector3D, n:Number, vnormals:Vector.<Vector3D>=mesh.vnormals;
  194.             for each (v in vnormals) {
  195.                 v.x = v.y = v.z = v.w = 0;
  196.             }
  197.             for each (var face:Face in mesh.faces) {
  198.                 fn = face.normal.world;
  199.                 v = vnormals[face.i0]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  200.                 v = vnormals[face.i1]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  201.                 v = vnormals[face.i2]; v.x += fn.x; v.y += fn.y; v.z += fn.z; v.w += 1;
  202.             }
  203.             for each (v in vnormals) {
  204.                 if (v.w == 0continue;
  205.                 n = 1/v.w;
  206.                 v.x *= n;
  207.                 v.y *= n;
  208.                 v.z *= n;
  209.             }
  210.             mesh.vnormals = vnormals;
  211.         }
  212.         
  213.         private const bmdw:int = 1<<hcResolution;
  214.         private var v0:Vector3D = new Vector3D(), v1:Vector3D = new Vector3D(), v:Vector3D = new Vector3D();
  215.         private var hc0:BitmapData = new BitmapData(bmdw,bmdw,false);
  216.         private var hcx:Vector.<BitmapData> = Vector.<BitmapData>([
  217.             new BitmapData(bmdw, bmdw>>1false), 
  218.             new BitmapData(bmdw, bmdw>>1false)
  219.         ]);
  220.         private var hcy:Vector.<BitmapData> = Vector.<BitmapData>([
  221.             new BitmapData(bmdw, bmdw>>1false), 
  222.             new BitmapData(bmdw, bmdw>>1false)
  223.         ]);
  224.         private var rect:Rectangle = new Rectangle();
  225.         public function calculateAmbientOcclusion(mesh:Mesh, prims:Vector.<AOPrimitive>, aoBuffer:Vector.<Number>) : void {
  226.             var x:Number, y:Number, z:Number, w:Number, t:Number, v2:Vector3D, p:AOPrimitive, 
  227.                 i:int=0, j:int=0, k:int, hci:int, ao:int, ix:int, iy:int
  228.                 hhc:int = bmdw>>1, hc0s:int = bmdw*bmdw, hcxs:int = hc0s>>1, hcxf:int = bmdw-1
  229.                 vnormals:Vector.<Vector3D> = mesh.vnormals, 
  230.                 vertices:Vector.<Number> = mesh.verticesOnWorld;
  231.             _timer.start(1);
  232.             for each (v2 in vnormals) {
  233.                 v1.x = 0.0;
  234.                 v1.y = 0.0;
  235.                 v1.z = 0.0;
  236.                      if ((v2.y < 0.6) && (v2.y > -0.6)) v1.y = -1.0;
  237.                 else if ((v2.z < 0.6) && (v2.z > -0.6)) v1.z = 1.0;
  238.                 else v1.x = 1.0;
  239.                 v0.x = v1.y * v2.z - v1.z * v2.y;
  240.                 v0.y = v1.z * v2.x - v1.x * v2.z;
  241.                 v0.z = v1.x * v2.y - v1.y * v2.x;
  242.                 v0.normalize();
  243.                 v1.x = v2.y * v0.z - v2.z * v0.y;
  244.                 v1.y = v2.z * v0.x - v2.x * v0.z;
  245.                 v1.z = v2.x * v0.y - v2.y * v0.x;
  246.                 v1.normalize();
  247.                 // render on hemi-cubes
  248.                 hc0.fillRect(hc0.rect, 1);
  249.                 hcx[0].fillRect(hcx[0].rect, 1);
  250.                 hcx[1].fillRect(hcx[1].rect, 1);
  251.                 hcy[0].fillRect(hcy[0].rect, 1);
  252.                 hcy[1].fillRect(hcy[1].rect, 1);
  253.                 for each (p in prims) {
  254.                     x = p.world.x - vertices[i];
  255.                     y = p.world.y - vertices[i+1];
  256.                     z = p.world.z - vertices[i+2];
  257.                     v.x = x * v0.x + y * v0.y + z * v0.z;
  258.                     v.y = x * v1.x + y * v1.y + z * v1.z;
  259.                     v.z = x * v2.x + y * v2.y + z * v2.z;
  260.                     // hemi-cube front
  261.                     if (v.z > 0) {
  262.                         t = hhc/v.z;
  263.                         w = p.aoRadius * 0.8 * t;
  264.                         rect.x = v.x * t - w + hhc;
  265.                         rect.y = v.y * t - w + hhc;
  266.                         rect.width = w + w;
  267.                         rect.height = w + w;
  268.                         hc0.fillRect(rect, 0);
  269.                     }
  270.                     // hemi-cube x direction
  271.                     if (v.x != 0) {
  272.                         if (v.x > 0) { hci = 0; t =  hhc/v.x; }
  273.                         else         { hci = 1; t = -hhc/v.x; }
  274.                         w = p.aoRadius * 0.65 * t;
  275.                         rect.x = v.y * t - w + hhc;
  276.                         rect.y = v.z * t - w;
  277.                         rect.width = w + w;
  278.                         rect.height = w + w;
  279.                         hcx[hci].fillRect(rect, 0);
  280.                     }
  281.                     // hemi-cube y direction
  282.                     if (v.y != 0) {
  283.                         if (v.y > 0) { hci = 0; t =  hhc/v.y; }
  284.                         else         { hci = 1; t = -hhc/v.y; }
  285.                         w = p.aoRadius * 0.65 * t;
  286.                         rect.x = v.x * t - w + hhc;
  287.                         rect.y = v.z * t - w;
  288.                         rect.width = w + w;
  289.                         rect.height = w + w;
  290.                         hcy[hci].fillRect(rect, 0);
  291.                     }
  292.                 }
  293.                 ao = 0;
  294.                 for (k=0; k<hc0s; k++) {
  295.                     ao += hc0.getPixel(k&hcxf, k>>hcResolution);
  296.                 }
  297.                 for (k=0; k<hcxs; k++) {
  298.                     ix = k & hcxf;
  299.                     iy = k >> hcResolution;
  300.                     ao += hcx[0].getPixel(ix, iy) + hcx[1].getPixel(ix, iy) +
  301.                           hcy[0].getPixel(ix, iy) + hcy[1].getPixel(ix, iy);
  302.                 }
  303.                 aoBuffer[j] = ao * 0.005208333333333333;
  304.                 i+=3;
  305.                 j++;
  306.             }
  307.             _timer.pause(1);
  308.         }
  309.         
  310.         public function calculateTexCoordByVertexNormal(mesh:Mesh, light:Light, aoBuffer:Vector.<Number>) : void {
  311.             var x:Number, y:Number, v:Vector3D, i:int=0, j:int=0, vnormals:Vector.<Vector3D>=mesh.vnormals,
  312.                 dir:Vector3D=light, hv:Vector3D=light.halfVector, 
  313.                 t:Number = aoBlendRatio, it:Number = 1-aoBlendRatio, offset:Number = 0;
  314.             if (!aoSwitch) {
  315.                 t = 0;
  316.                 offset = aoBlendRatio*0.8;
  317.                 it = 1-offset;
  318.             }
  319.             for each (v in vnormals) {
  320.                 x = dir.x * v.x + dir.y * v.y + dir.z * v.z;
  321.                 y = hv.x  * v.x + hv.y  * v.y + hv.z  * v.z;
  322.                 if (x<0) x = 0;
  323.                 if (y<0) y = 0;
  324.                 mesh.texCoord[i] = x*it + aoBuffer[j]*t + offset; i++; j++;
  325.                 mesh.texCoord[i] = y; i+=2;
  326.             }
  327.         }
  328.     }
  329. }
  330. import flash.display.*;
  331. import flash.text.*;
  332. import flash.geom.*;
  333. import flash.events.*;
  334. import flash.utils.getTimer;
  335. class timer extends TextField {
  336.     public var title:String = "";
  337.     private var _time:Vector.<int>;
  338.     private var _sum :Vector.<int>;
  339.     private var _stat:Vector.<String>;
  340.     private var _cnt :int;
  341.     private var _avc:int;
  342.     function timer(averagingCount:int, ...stat) : void {
  343.         _avc  = averagingCount;
  344.         _stat = Vector.<String>(stat);
  345.         _time = new Vector.<int>(stat.length, true);
  346.         _sum  = new Vector.<int>(stat.length, true);
  347.         _cnt  = new Vector.<int>(stat.length, true);
  348.         background = true;
  349.         backgroundColor = 0x00ff00;
  350.         autoSize = "left";
  351.         multiline = true;
  352.         addEventListener("enterFrame", _onEnterFrame);
  353.     }
  354.     public function start(slot:int=0) : void { _time[slot] = getTimer(); }
  355.     public function pause(slot:int=0) : void { _sum[slot] += getTimer() - _time[slot]; }
  356.     public function _onEnterFrame(e:Event) : void {
  357.         if (++_cnt == _avc) {
  358.             _cnt = 0;
  359.             var str:String = "", line:String;
  360.             for (var slot:int = 0; slot<_sum.length; slot++) {
  361.                 line = _stat[slot].replace("##"String(_sum[slot] / _avc));
  362.                 str += line + "\n";
  363.                 _sum[slot] = 0;
  364.             }
  365.             text = title + str;
  366.         }
  367.     }
  368. }
  369. // 3D Engine
  370. //----------------------------------------------------------------------------------------------------
  371. /** Core */
  372. class Render3D extends Shape {
  373.     /** model view matrix */
  374.     public var matrix:Matrix3D;
  375.     private var _meshProjected:Mesh = null;      &