package
{
import away3d.cameras.*;
import away3d.cameras.lenses.*;
import away3d.containers.*;
import away3d.core.base.*;
import away3d.core.clip.*;
import away3d.core.draw.*;
import away3d.core.math.*;
import away3d.core.render.*;
import away3d.core.utils.*;
import away3d.events.*;
import away3d.loaders.*;
import away3d.loaders.data.*;
import away3d.materials.*;
import away3d.primitives.*;
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
import flash.ui.*;
[SWF(backgroundColor="#000000", frameRate="30", quality="LOW", width="800", height="600")]
public class Advanced_FrustumHotelRoom extends Sprite
{
static private var toRADIANS:Number = Math.PI / 180;
[Embed(source="assets/signature_dave.swf", symbol="Signature")]
public var SignatureSwf:Class;
[Embed(source="assets/room/collision.png")]
private var CollisionBitmap:Class;
private var scene:Scene3D;
private var camera:Camera3D;
private var view:View3D;
private var clipping:Clipping;
private var Signature:Sprite;
private var SignatureBitmap:Bitmap;
private var tvMaterial:BitmapMaterial;
private var materialData:MaterialData;
private var loader:Object3DLoader;
private var model:ObjectContainer3D;
private var move:Boolean = false;
private var lastPanAngle:Number;
private var lastTiltAngle:Number;
private var lastMouseX:Number;
private var lastMouseY:Number;
private var upFlag:Boolean = false;
private var downFlag:Boolean = false;
private var leftFlag:Boolean = false;
private var rightFlag:Boolean = false;
private var cameraForwardSpeed:Number = 0;
private var cameraForwardAcc:Number = 2;
private var cameraForwardDrag:Number = 0.3;
private var cameraRightSpeed:Number = 0;
private var cameraRightAcc:Number = 2;
private var cameraRightDrag:Number = 0.3;
private var forwardVector:Number3D = new Number3D();
private var rightVector:Number3D = new Number3D();
private var tiltangle:Number = 0;
private var panangle:Number = -100;
private var target:Number3D = new Number3D();
private var targettiltangle:Number = 0;
private var targetpanangle:Number = 0;
private var collisionBitmapData:BitmapData;
private var collisionBitmap:Bitmap;
private var sampleBitmapData:BitmapData;
private var sampleRect:Rectangle = new Rectangle();
private var samplePoint:Point = new Point();
private var collisionRect:Rectangle;
private var cameraX:Number;
private var cameraY:Number;
private var collisionShape:Shape = new Shape();
private var collisionVector:Point = new Point();
private var collisionMatrix:Matrix = new Matrix();
private var collisionDot:Number;
private var collisionDistance:Number = 30;
private var debugPrecise:Boolean = false;
private var preciseMaterials:Array = [];
private var material:BitmapMaterial;
private var sortObjects:Boolean = true;
private var sortedObjects:Array = [];
private var object:Object3D;
private var text:TextField;
/**
* Constructor
*/
public function Advanced_FrustumHotelRoom()
{
init();
}
/**
* Global initialise function
*/
private function init():void
{
initEngine();
initLoaders();
initCollisionBitmap();
initText();
initListeners();
}
/**
* Initialise the engine
*/
private function initEngine():void
{
Debug.active = true;
scene = new Scene3D();
clipping = new FrustumClipping({minZ:10});
camera = new Camera3D({zoom:6, focus:100, x:176, z:54});
camera.lens = new PerspectiveLens();
view = new View3D({scene:scene, camera:camera, clipping:clipping});
view.x = 400;
view.y = 300;
view.addSourceURL("srcview/index.html");
addChild( view );
Signature = Sprite(new SignatureSwf());
SignatureBitmap = new Bitmap(new BitmapData(Signature.width, Signature.height, true, 0));
stage.quality = StageQuality.HIGH;
SignatureBitmap.bitmapData.draw(Signature);
stage.quality = StageQuality.LOW;
addChild(SignatureBitmap);
}
/**
* Initialise the scene objects
*/
private function initLoaders():void
{
loader = Max3DS.load("assets/room/interior.3ds", {name:"model", loadersize:40, centerMeshes:true});
scene.addChild(loader);
loader.addOnSuccess(onSuccess);
}
/**
* Listener function for loading complete event on loader
*/
public function onSuccess(event:Event):void
{
initModel();
initMaterials();
initObjects();
}
/**
* Initialise the scene objects
*/
private function initModel():void
{
model = loader.handle as ObjectContainer3D;
model.rotationX = 90;
model.y = -80;
}
/**
* Initialise the materials
*/
private function initMaterials():void
{
for each (materialData in model.materialLibrary) {
if (materialData.name == "baked_Mati_re5" ||
materialData.name == "Speaker_" ||
materialData.name == "Ivory" ||
materialData.name == "Ivory3" ||
materialData.name == "_Snow_1" ||
materialData.name == "baked__Snow_11" ||
materialData.name == "baked__Snow_1" ||
materialData.name == "orig__Snow_12" ||
materialData.name == "SaddleBr1" ||
materialData.name == "Mati_re81") {
(materialData.material as BitmapMaterial).precision = 3;
preciseMaterials.push(materialData.material);
} else if (materialData.name == "Charcoal") {
tvMaterial = new BitmapMaterial(materialData.textureBitmap);
tvMaterial.precision = 3;
preciseMaterials.push(tvMaterial);
}
}
}
/**
* Initialise the scene objects
*/
private function initObjects():void
{
var pivot:Number3D;
var mesh:Mesh;
var material:BitmapMaterial;
for each (var child:Object3D in model.children)
{
if (child.name == "wall_l" || child.name == "wall_b" || child.name == "wall_r") {
child.pushback = true;
child.ownCanvas = true;
} else if (child.name == "wall_f") {
child.pushback = true;
} else if (child.name == "Bedside " || child.name == "TV Cabin" || child.name == "TV") {
if (child.name == "TV") {
mesh = (child as Mesh);
(mesh.faces[52] as Face).material = tvMaterial;
(mesh.faces[53] as Face).material = tvMaterial;
}
child.ownCanvas = true;
child.renderer = Renderer.CORRECT_Z_ORDER as IPrimitiveConsumer
sortedObjects.push(child);
} else if (child.name == "Door01") {
child.pushfront = true;
} else if (child.name == "Bed") {
child.pushfront = true;
} else if (child.name == "matress") {
child.pushfront = true;
child.ownCanvas = true;
pivot = new Number3D(0, -20, 0);
pivot.transform(pivot, child.transform);
child.pivotPoint = pivot;
child.moveTo(child.x, child.y - 20, child.z);
} else if (child.name == "Pillows") {
child.pushfront = true;
child.ownCanvas = true;
pivot = new Number3D(0, -90, 0);
pivot.transform(pivot, child.transform);
child.pivotPoint = pivot;
child.moveTo(child.x, child.y - 90, child.z);
} else if (child.name == "Lampsma" || child.name == "books" || child.name == "plant" || child.name == "picture" || child.name == "alarm") {
if (child.name == "books" || child.name == "plant") {
pivot = new Number3D(0, 0, 20);
pivot.transform(pivot, child.transform);
child.pivotPoint = pivot;
child.moveTo(child.x, child.y, child.z + 20);
}
child.pushfront = true;
child.ownCanvas = true;
} else if (child.name == "Vase" || child.name == "wineglss") {
(child as Mesh).bothsides = true;
} else if (child.name == "Floor") {
child.pushback = true;
child.ownCanvas = true;
}
}
}
/**
* Create collision map for camera movement
*/
private function initCollisionBitmap():void
{
collisionBitmap = new CollisionBitmap();
collisionBitmapData = collisionBitmap.bitmapData;
sampleBitmapData = new BitmapData(collisionDistance*2, collisionDistance*2, false, 0);
sampleRect = new Rectangle(0, 0, collisionDistance*2, collisionDistance*2);
}
/**
* Create an instructions overlay
*/
private function initText():void
{
text = new TextField();
text.defaultTextFormat = new TextFormat("Verdana", 10, 0xFFFFFF);
text.x = 0;
text.y = 0;
text.width = 220;
text.height = 100;
text.selectable = false;
text.mouseEnabled = false;
text.text = "D - toggle debug triangles\n" +
"Z - toggle local z-sort correction\n" +
"F - Frustum Clipping mode (default)\n" +
"N - Nearfield Clipping mode\n" +
"R - Rectangle Clipping mode\n";
addChild(text);
}
/**
* Initialise the listeners
*/
private function initListeners():void
{
addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
onResize(null);
}
/**
* Navigation and render loop
*/
private function onEnterFrame(event:Event):void
{
cameraForwardSpeed *= (1-cameraForwardDrag);
cameraRightSpeed *= (1-cameraRightDrag);
if (cameraForwardSpeed < 0.01 && cameraForwardSpeed > -0.01)
cameraForwardSpeed = 0;
if (cameraRightSpeed < 0.01 && cameraRightSpeed > -0.01)
cameraRightSpeed = 0;
if (upFlag)
cameraForwardSpeed += cameraForwardAcc;
if (downFlag)
cameraForwardSpeed -= cameraForwardAcc;
if (rightFlag)
cameraRightSpeed += cameraRightAcc;
if (leftFlag)
cameraRightSpeed -= cameraRightAcc;
forwardVector.rotate(Number3D.FORWARD, camera.transform);
forwardVector.y = 0;
forwardVector.normalize();
camera.x += forwardVector.x*cameraForwardSpeed;
camera.z += forwardVector.z*cameraForwardSpeed;
rightVector.rotate(Number3D.RIGHT, camera.transform);
rightVector.y = 0;
rightVector.normalize();
camera.x += rightVector.x*cameraRightSpeed;
camera.z += rightVector.z*cameraRightSpeed;
if (move) {
panangle = 0.3*(stage.mouseX - lastMouseX) + lastPanAngle;
tiltangle = -0.3*(stage.mouseY - lastMouseY) + lastTiltAngle;
if (tiltangle > 70)
tiltangle = 70;
if (tiltangle < -70)
tiltangle = -70;
}
checkCollisionData();
target.x = 100 * Math.sin(panangle * toRADIANS) * Math.cos(tiltangle * toRADIANS) + camera.x;
target.z = 100 * Math.cos(panangle * toRADIANS) * Math.cos(tiltangle * toRADIANS) + camera.z;
target.y = 100 * Math.sin(tiltangle * toRADIANS) + camera.y;
camera.lookAt(target);
view.render();
if (!move && !cameraRightSpeed && !cameraForwardSpeed)
stage.quality = StageQuality.HIGH;
else
stage.quality = StageQuality.LOW;
}
/**
* Checks the camera position with the collision bitmap and resolves any collisions
*/
private function checkCollisionData():void
{
cameraX = 350 - camera.x*1.87;
cameraY = 510 + camera.z*1.87;
sampleRect.x = int(cameraX - collisionDistance);
sampleRect.y = int(cameraY - collisionDistance);
var i:int = collisionDistance + 1;
do {
i--;
collisionRect = sampleBitmapData.getColorBoundsRect(0xFFFFFF, 0x000000);
collisionShape.graphics.clear();
collisionShape.graphics.beginFill(0x660000);
collisionShape.graphics.drawCircle(collisionDistance, collisionDistance, i);
collisionShape.graphics.endFill();
sampleBitmapData.copyPixels(collisionBitmapData, sampleRect, samplePoint);
sampleBitmapData.draw(collisionShape, null, null, BlendMode.MULTIPLY);
} while (sampleBitmapData.getColorBoundsRect(0xFFFFFF, 0x000000).width && i > 1)
if (i < collisionDistance) {
collisionVector.x = collisionRect.x + collisionRect.width/2 - collisionDistance;
collisionVector.y = collisionRect.y + collisionRect.height/2 - collisionDistance;
collisionVector.normalize(1);
cameraX -= collisionVector.x*(collisionDistance - i);
cameraY -= collisionVector.y*(collisionDistance - i);
camera.x = (350 - cameraX)/1.87;
camera.z = (cameraY - 510)/1.87;
collisionMatrix.identity();
collisionMatrix.rotate(panangle * toRADIANS);
collisionVector.x = -collisionVector.x;
collisionVector = collisionMatrix.deltaTransformPoint(collisionVector);
collisionDot = cameraRightSpeed*collisionVector.x + cameraForwardSpeed*collisionVector.y;
cameraRightSpeed -= collisionDot*collisionVector.x;
cameraForwardSpeed -= collisionDot*collisionVector.y;
checkCollisionData();
}
}
/**
* Mouse down handler for navigation
*/
private function onMouseDown(event:MouseEvent):void
{
lastPanAngle = panangle;
lastTiltAngle = tiltangle;
lastMouseX = stage.mouseX;
lastMouseY = stage.mouseY;
move = true;
stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
}
/**
* Mouse up handler for navigation
*/
private function onMouseUp(event:MouseEvent):void
{
move = false;
stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
}
/**
* Key down handler for key controls
*/
public function onKeyDown(e:KeyboardEvent):void {
switch(e.keyCode)
{
case Keyboard.UP:
upFlag = true;
break;
case Keyboard.DOWN:
downFlag = true;
break;
case Keyboard.LEFT:
leftFlag = true;
break;
case Keyboard.RIGHT:
rightFlag = true;
break;
default:
}
}
/**
* Key up handler for key controls
*/
public function onKeyUp(e:KeyboardEvent):void {
switch(e.keyCode)
{
case Keyboard.UP:
upFlag = false;
break;
case Keyboard.DOWN:
downFlag = false;
break;
case Keyboard.LEFT:
leftFlag = false;
break;
case Keyboard.RIGHT:
rightFlag = false;
break;
case "D".charCodeAt():
debugPrecise = !debugPrecise;
for each (material in preciseMaterials)
material.debug = debugPrecise;
break;
case "Z".charCodeAt():
sortObjects = !sortObjects;
for each (object in sortedObjects) {
if (sortObjects)
object.renderer = Renderer.CORRECT_Z_ORDER as IPrimitiveConsumer;
else
object.renderer = Renderer.BASIC as IPrimitiveConsumer;
}
break;
case "F".charCodeAt():
view.clipping = new FrustumClipping({minZ:10});
break;
case "N".charCodeAt():
view.clipping = new NearfieldClipping({minZ:10});
break;
case "R".charCodeAt():
view.clipping = new RectangleClipping();
break;
default:
}
}
/**
* Mouse stage leave listener for navigation
*/
private function onStageMouseLeave(event:Event):void
{
move = false;
stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
}
/**
* stage listener for resize events
*/
private function onResize(event:Event):void
{
view.x = stage.stageWidth / 2;
view.y = stage.stageHeight / 2;
SignatureBitmap.y = stage.stageHeight - Signature.height;
}
}
}