📄 engine3d.as
字号:
/**
* project3D Engine
* @author John Sword
* @version 2 - AS3
*
* using Left-handed Coordinate System
*
* Z
* /
* /__ X
* |
* |
* Y
* based loosely on work by BrandonWilliams
* and Carlos Ulloa Matesanz (Papervision)
*
* Copyright 2007 (c) John Sword
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package engine
{
import engine.camera.Camera3D;
import engine.geom.Face;
import engine.geom.Vector;
import engine.geom.convexHull2D;
import engine.objects.Object3D;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Rectangle;
import flash.utils.Timer;
public class Engine3D
{
/* public */
public var zClipping:Number = 0;
public var renderSpeed:Number = 45;
public var minHullSize:int = 2000;
public var maxHullSize:int = 600000;
public var occlusion:Boolean = false;
/* private */
private var visiblePolys:Number = 0;
private var SCREEN_WIDTH:Number;
private var SCREEN_HEIGHT:Number;
private var WorldObjects:Array;
private var WorldBlockers:Array;
private var WorldMC:Sprite;
private var WorldCam:Camera3D;
private var totalObjects:int = 0;
private var renderTimer:Timer;
private var mouseOverFlag:Boolean = false;
/* static */
public static const QUALITY_LOW:String = "LOW";
public static const QUALITY_AUTOLOW:String = "AUTOLOW";
public static const QUALITY_MEDIUM:String = "MEDIUM";
public static const QUALITY_HIGH:String = "HIGH";
public static const QUALITY_AUTOHIGH:String = "AUTOHIGH";
//private static var sin:Function = Math.sin;
//private static var cos:Function = Math.cos;
//private const sin:Function = Math.sin;
//private const cos:Function = Math.cos;
private static var mouseOverObj:Object3D;
private static var mouseOutObj:Object3D;
private static var g:Graphics;
private static const toDEGREES:Number = 180 / Math.PI;
private static const toRADIANS:Number = Math.PI / 180;
private static const polyBuffer:Array = new Array();
/**
* Constructor
* @param x the X position of the 3D screen
* @param y the Y position of the 3D screen
* @param w the Width of the 3D screen
* @param h the Height of the 3D screen
*/
public function Engine3D ( screen:Sprite, x:int=0, y:int=0, w:int=640, h:int=480 )
{
// set 3d engine screen size
WorldMC = new Sprite();
WorldMC.graphics.moveTo(x,y);
WorldMC.graphics.lineTo(w,y);
WorldMC.graphics.lineTo(w,h);
WorldMC.graphics.lineTo(x,h);
WorldMC.cacheAsBitmap = true;
SCREEN_WIDTH = WorldMC.width;
SCREEN_HEIGHT = WorldMC.height;
WorldObjects = new Array();
WorldBlockers = new Array();
g = WorldMC.graphics;
//polyBuffer = new Array();
//polyBuffer;
// create a default camera
setCamera( new Camera3D ( WorldMC ) );
// clip screen
WorldMC.scrollRect = new Rectangle(x,y,SCREEN_WIDTH,SCREEN_HEIGHT);
// ini render timer with default speed
renderTimer = new Timer( renderSpeed );
// listener for object mouse events
WorldMC.addEventListener (MouseEvent.CLICK, MouseEventCLICK, false, 0, true);
WorldMC.addEventListener (MouseEvent.MOUSE_DOWN, MouseEventMouseDown, false, 0, true);
WorldMC.addEventListener (MouseEvent.MOUSE_UP, MouseEventMouseUp, false, 0, true);
// show 3d world
screen.addChild( WorldMC );
trace("Engine 3D init");
}
public function getWorld () : Sprite
{
return WorldMC;
}
public function applyFilters ( filters:Array ) : void
{
WorldMC.filters = filters;
}
public function setQuality ( setting:String ) : void
{
WorldMC.stage.quality = setting;
}
public function setCamera ( c:Camera3D ) : void
{
WorldCam = c;
}
public function getCamera () : Camera3D
{
return WorldCam;
}
public function getWidth () : Number
{
return SCREEN_WIDTH;
}
public function getHeight () : Number
{
return SCREEN_HEIGHT;
}
public function getVisiblePolys () : Number
{
return visiblePolys;
}
public function addObject ( o:Object3D, n:String = "obj" ) : void
{
// TODO: check for duplicate names?
WorldObjects.push (o);
totalObjects = WorldObjects.length;
if( n == "obj" ) n = n + totalObjects.toString();
o.objID = totalObjects;
o.objName = n;
}
public function render() : void
{
if ( !renderTimer.running )
{
renderTimer = new Timer( renderSpeed );
renderTimer.addEventListener(TimerEvent.TIMER, _onEnterFrame);
renderTimer.start();
}
}
public function stopRender() : void
{
renderTimer.stop();
}
private function _onEnterFrame( event:TimerEvent ) : void
{
WorldMC.visible = false;
WorldMC.buttonMode = false;
WorldMC.useHandCursor = false;
/*
if ( WorldMC.numChildren > 0 )
{
var bmp:Bitmap = WorldMC.getChildAt( 0 ) as Bitmap;
bmp.bitmapData.fillRect( new Rectangle ( 0,0,SCREEN_WIDTH,SCREEN_HEIGHT ), 0x00 );
}
*/
mouseOverObj = null;
// clear faces from buffer array
//polyBuffer = new Array();
polyBuffer.splice(0);
// update camera rotation if need be
if ( WorldCam.renderMe ) WorldCam.update_rotation_matrix ();
// clear screen
g.clear();
// render world objects
var w:Array = WorldObjects;
var i:int = totalObjects;
if ( occlusion ) {
WorldBlockers.splice(0);
w.sortOn ( 'screenZ', Array.DESCENDING | Array.NUMERIC );
}
var o:Object3D;
while (o = w[--i]) {
if ( o.isVisible )
{
worldToCam ( o ); // transform object
// dont render if object faces are'nt visible
if ( o.facesVisible ) {
if ( occlusion ) {
//trace( o.objName + " " + o.getObjectArea() );
var area:int = o.getObjectArea()
if ( area > minHullSize && area < maxHullSize )
buildHull( o );
if ( !testHulls( o.screenZ,o.OBB ) ) continue;
//trace( o.objName + " " + o.screenZ );
}
testFaces( o );
}
}
}
// render object's faces (polys)
renderPolys ();
// show world
WorldMC.cacheAsBitmap = true;
WorldMC.visible = true;
WorldCam.renderMe = false;
}
private function worldToCam ( obj:Object3D ) : void
{
var o:Object3D = obj; // local object reference
// invisible objects dont project
if (o.isVisible)
{
// transform object relative to world cam
o.project ( WorldCam, occlusion );
}
}
private function buildHull ( obj:Object3D ) : void
{
var o:Object3D = obj; // local object reference
var hull:convexHull2D = new convexHull2D();
var n:int = hull.buildApproximate( o.tVertices );
if ( n <= 3 ) return;
hull.screenZ = o.screenZ;
WorldBlockers.push( hull );
o.hull = hull;
//hull.render( g );
/*
var bb:Object = o.OBB;
g.lineStyle( 5, 0xFF0000, 1);
g.moveTo( bb.min.x,bb.min.y );
g.lineTo( bb.max.x,bb.min.y );
g.lineTo( bb.max.x,bb.max.y );
g.lineTo( bb.min.x,bb.max.y );
g.lineTo( bb.min.x,bb.min.y );
g.lineStyle();
*/
//if ( hull.contains( WorldMC.mouseX, WorldMC.mouseY ) )
// trace( "contact at x: " + WorldMC.mouseX + " y: " + WorldMC.mouseY );
//else
// trace( "x: " + WorldMC.mouseX + " y: " + WorldMC.mouseY );
}
private function testHulls ( screenZ:Number, obb:Object ) : Boolean
{
var wb:Array = WorldBlockers;
var hlen:int = wb.length;
if ( hlen == 1 ) return true;
var i:int = 0;
var tmph:convexHull2D;
var obbMaxX:int = obb.max.x;
var obbMinX:int = obb.min.x;
var obbMaxY:int = obb.max.y;
var obbMinY:int = obb.min.y;
for each ( tmph in wb )
{
if ( tmph.screenZ >= screenZ ) continue;
if ( tmph.contains( obbMaxX,obbMinY ) )
if ( tmph.contains( obbMaxX,obbMaxY ) )
if ( tmph.contains( obbMinX,obbMaxY ) )
if ( tmph.contains( obbMinX,obbMinY ) )
return false;
}
return true;
}
private function testFaces ( obj:Object3D ) : void
{
var o:Object3D = obj; // local object reference
var faces:Array = o.faces;
var f:Face;
var i:int = faces.length;
var pb:Array = polyBuffer;
// push available faces to poly buffer
while ( f = faces[--i] )
{
if ( f.setVisible (WorldMC,-zClipping,o.flipNormals,o.cull) ) pb.push (f);
}
}
private function renderPolys () : void
{
// local variables for speeding up
var vp:int = 0;
var wmc:Sprite = WorldMC;
// get poly list
var pb:Array = polyBuffer;
// tmp object for mouse events
var o:Object3D = null;
//g.clear();
//pb.sortOn ( 'Z', Array.DESCENDING | Array.NUMERIC );
// sort visible polys
pb.sortOn ( 'Z', Array.NUMERIC );
var i:int = pb.length;
while ( --i > -1 )
{
// renders face and retrieves current object under the mouse if any
o = pb[i].render ( wmc );
if ( o ) mouseOverObj = o;
++vp;
}
// deal with mouse events on object closer to the camera
// only deals with one object at a time
if ( mouseOverObj )
{
if ( mouseOverObj !== mouseOutObj && mouseOutObj != null )
{
mouseOutObj.broadcastMOUSEOUT();
mouseOutObj = null;
}
if ( !mouseOutObj )
{
mouseOverObj.broadcastMOUSEOVER();
mouseOverFlag = true;
mouseOutObj = mouseOverObj;
}
wmc.buttonMode = wmc.useHandCursor = true;
} else {
if ( mouseOutObj )
{
mouseOutObj.broadcastMOUSEOUT();
mouseOutObj = null;
}
}
visiblePolys = vp;
}
private function PolygonArea (v0:Vector,v1:Vector,v2:Vector) : Number
{
return .5 * (v0.x*v1.y-v1.x*v0.y) + (v1.x*v2.y-v2.x*v1.y) + (v2.x*v0.y-v0.x*v2.y);
}
private function contains(x:Number, y:Number, f:Face):Boolean
{
var v0:Vector = f.v1.screen;
var v1:Vector = f.v2.screen;
var v2:Vector = f.v3.screen;
if (v0.x*(y - v1.y) + v1.x*(v0.y - y) + x*(v1.y - v0.y) < -0.001)
return false;
if (v0.x*(v2.y - y) + x*(v0.y - v2.y) + v2.x*(y - v0.y) < -0.001)
return false;
if (x*(v2.y - v1.y) + v1.x*(y - v2.y) + v2.x*(v1.y - y) < -0.001)
return false;
return true;
}
// mouse events
private function MouseEventCLICK ( e:MouseEvent ) : void
{
if ( mouseOverObj ) mouseOverObj.broadcastCLICK ();
}
private function MouseEventMouseDown ( e:MouseEvent ) : void
{
if ( mouseOverObj ) mouseOverObj.broadcastMouseDown ();
}
private function MouseEventMouseUp ( e:MouseEvent ) : void
{
if ( mouseOverObj ) mouseOverObj.broadcastMouseUp ();
}
//rotation around X-axis :
/*
function rotateX(rot:Number,Mesh:Object3D)
{
var len:Number = Mesh.aVertexs.length;
var i:Number = 0;
while (i<len)
{
var X = Mesh.aVertexs[i]["x"];
var Y = Mesh.aVertexs[i]["y"];
var Z = Mesh.aVertexs[i]["z"];
var Xrotated = cos(rot)*X - sin(rot)*Z
var Yrotated = Y
var Zrotated = sin(rot)*X + cos(rot)*Z
Mesh.aVertexs[i]["x"] = Xrotated;
Mesh.aVertexs[i]["y"] = Yrotated;
Mesh.aVertexs[i]["z"] = Zrotated;
i++;
}
}
//rotation around Z-axis :
function rotateZ(rot,Mesh:Object3D)
{
var len:Number = Mesh.aVertexs.length;
var i:Number = 0;
while (i<len)
{
var X = Mesh.aVertexs[i]["x"];
var Y = Mesh.aVertexs[i]["y"];
var Z = Mesh.aVertexs[i]["z"];
var Xrotated = X
var Yrotated = cos(rot)*Y - sin(rot)*Z;
var Zrotated = sin(rot)*Y + cos(rot)*Z;
Mesh.aVertexs[i]["x"] = Xrotated;
Mesh.aVertexs[i]["y"] = Yrotated;
Mesh.aVertexs[i]["z"] = Zrotated;
i++;
}
}
function moveXYZ(x:Number,y:Number,z:Number,Mesh:Object3D)
{
var len:Number = Mesh.aVertexs.length;
var i:Number = 0;
for (i=0; i<len; i++) {
var X = Mesh.aVertexs[i]["x"];
var Y = Mesh.aVertexs[i]["y"];
var Z = Mesh.aVertexs[i]["z"];
var newX = X+x;
var newY = Y+y;
var newZ = Z+z;
Mesh.aVertexs[i]["x"] = newX;
Mesh.aVertexs[i]["y"] = newY;
Mesh.aVertexs[i]["z"] = newZ;
}
}
public static function buildTrig() : void
{
var sinus=new float[4096];
cosinus=new float[4096];
for (int i=0;i<4096;i++)
{
sinus[i]=(float)Math.sin((float)i/rad2scale);
cosinus[i]=(float)Math.cos((float)i/rad2scale);
}
trig=true;
}*/
// --------- RGB to HEX
public function rgbToHex ( r:Number, g:Number, b:Number ) : Number
{
return (r<<16 | g<<8 | b);
}
// ------ HEX to RGB
public function hexTorgb ( hex:Number ) : Object
{
var red:Number = hex >> 16;
var grnBlu:Number = hex - (red << 16);
var grn:Number = grnBlu >> 8;
var blu:Number = grnBlu - (grn << 8);
return ( {r:red, g:grn, b:blu} );
}
public function toRadian ( n:Number ) : Number
{
return n * toRADIANS;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -