KDImageDanboCity (Test walkaround)
Testing walkaround KDImage city with Danbot.
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/kbKq
*/
/**
フラクタルで画像を描画
パクリ元ネタ:
fladdict » コンピューターに絵画を描かせる
http://fladdict.net/blog/2009/05/computer-painting.html
標準偏差:
http://www.cap.or.jp/~toukei/kandokoro/html/14/14_2migi.htm
画像の読み込み処理:
http://wonderfl.kayac.com/code/3fb2258386320fe6d2b0fe17d6861e7da700706a
RGB->HSB変換:
http://d.hatena.ne.jp/flashrod/20060930#1159622027
**/
package
{
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Debug;
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.core.Wrapper;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.materials.Material;
import alternativ7.engine3d.objects.Mesh;
import alternativ7.engine3d.objects.SkyBox;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.primitives.Plane;
import com.greensock.easing.Cubic;
import com.greensock.easing.Elastic;
import com.greensock.easing.Linear;
import com.greensock.plugins.HexColorsPlugin;
import com.greensock.plugins.TweenPlugin;
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Vector3D;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.ui.Keyboard;
import flash.utils.getTimer;
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.View;
use namespace alternativa3d;
/**
* WIP: Real-time processed image using Alternativa3D's KDContainer to generate a "growing" * city.
*
* Since image is used, can consider using perlin noise or other procedural means to
* generate city layout.
*
* I was thinking a Blade runner style city would be nice.
* Would be good to add in geometry/splitting animations (buildings pushing in/out) as part
* of the "growing" effect.
* W-S-A-D to fly around with mouse drag look.
*
* @author Glidias
*/
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#ffffff")]
public class KDImageDanboCity extends Sprite
{
static public const FLOOR_COLOR:uint = 0xAAAAAA;
static public const SPEC_RADIUS:Number = 32;
private const IMAGE_URL:String = "http://farm4.static.flickr.com/3639/3538831894_cca4aabd68.jpg";
//標準偏差の閾値。小さくすると細かくなるけど、小さすぎるとただのモザイクみたくなる。
private const THRESHOLD:Number = 0.1;
private var fillRectangleArray:Array;
private var image:Bitmap;
private var imageData:BitmapData;
private var _canvas:Sprite;
public static const DUMMY_VECTOR1:Vector3D = new Vector3D();
public static const DUMMY_VECTOR2:Vector3D = new Vector3D();
public static const DUMMY_DISPLACE:Vector3D = new Vector3D(0,0,0.1);
private var camera:Camera3D;
private var kdContainer:KDContainer;
private var cameraController:SimpleFlyController;
private var KD_NODE:Class;
private var _lastKDNode:*;
private static const WORLD_SCALE:Number = 128;
private static const INV_255:Number = 1 / 255;
private static const MAX_HEIGHT:Number = 12000;
private static const MAX_Z_BOUNDS:Number = 12000 + 99100;
private static const MAX_Z_0:Number = 0;
private var rootContainer:Object3DContainer;
private var _tint:Number;
private var textures:Textures;
public function KDImageDanboCity():void
{
super();
NodeUtils.MAX_ALTITUDE = MAX_Z_BOUNDS;
NodeUtils.MIN_ALTITUDE = MAX_Z_0;
TweenPlugin.activate( [HexColorsPlugin] );
textures = new Textures();
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
initA3D();
removeEventListener(Event.ADDED_TO_STAGE, init);
//画像の読み込み
var req:URLRequest = new URLRequest(IMAGE_URL);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( req, new LoaderContext(true));
}
private function initA3D():void
{
fillA3DBuffers(); // may improve performance for initial run
camera = new Camera3D();
camera.view = new View(stage.stageWidth, stage.stageHeight);
stage.addEventListener(Event.RESIZE, onStageResize);
addChild(camera.view);
kdContainer = new KDContainer();
KD_NODE = getKDNodeClass();
rootContainer = new Object3DContainer();
rootContainer.addChild(camera);
cameraController = new SimpleFlyController(new MyEllipsoidCollider(SPEC_RADIUS, SPEC_RADIUS, SPEC_RADIUS), rootContainer, stage, camera, 800, 8);
//camera.addToDebug(Debug.BOUNDS, Box);
camera.addToDebug(Debug.NODES, KDContainer);
//camera.addToDebug(Debug.BOUNDS, KDContainer);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
private function fillA3DBuffers():void {
Vertex.collector = Vertex.createList(300);
Face.collector = createFaceList(100);
Wrapper.collector = createWrapperList(300);
}
private function createFaceList(i:int):Face {
var f:Face = new Face();
while ( --i > -1) {
f = f.next = new Face();
}
return f;
}
private function createWrapperList(i:int):Wrapper {
var f:Wrapper = new Wrapper();
while ( --i > -1) {
f = f.next = new Wrapper();
}
return f;
}
private var _doGenerateBuildings:Boolean = true;
private var player:DanboPlayer;
private function onKeyDown(e:KeyboardEvent):void
{
if (e.keyCode === Keyboard.TAB) {
//camera.debug = !camera.debug;
}
if (e.keyCode === Keyboard.BACKSPACE) {
_doGenerateBuildings = !_doGenerateBuildings;
}
}
private function getKDNodeClass():Class {
var dummy:KDContainer = new KDContainer();
dummy.createTree(new <Object3D>[new Box(8,8,8)]);
return Object(dummy.root).constructor;
}
private function onStageResize(e:Event):void
{
camera.view.width = stage.stageWidth;
camera.view.height = stage.stageHeight;
}
//画像読み込み後の処理
public function loadComplete(e:Event = null):void
{
e.target.removeEventListener(Event.COMPLETE, loadComplete);
image = e.target.loader.content as Bitmap;
imageData = image.bitmapData;
//キャンバス用スプライト
_canvas = new Sprite;
var p:RectanglePiece = new RectanglePiece();
var node:*;
var threshold:Number = kdContainer.threshold;
p.x0 = 0;
p.y0 = 0;
p.x1 = imageData.width;
p.y1 = imageData.height;
p.c = 0;
// Setup root starting
kdContainer.root = NodeUtils.ROOT = setupNode(p);
kdContainer.boundMinX = 0;
kdContainer.boundMinY = 0;
kdContainer.boundMaxX = p.x1 * WORLD_SCALE;
kdContainer.boundMaxY = p.y1 * WORLD_SCALE;
kdContainer.boundMinZ = 0;
kdContainer.boundMaxZ = MAX_Z_BOUNDS;
camera.x = kdContainer.boundMaxX * .5;
camera.y = kdContainer.boundMaxY * .5;
camera.z = MAX_HEIGHT + 63400;
cameraController.updateObjectTransform();
cameraController.lookAtXYZ(camera.x, camera.y, 0);
var skybox:SkyBox = new SkyBox(99999999);
skybox.setMaterialToAllFaces( new FillMaterial(0xEEFEFF) );
rootContainer.addChild(skybox);
var floor:Plane = new Plane(kdContainer.boundMaxX, kdContainer.boundMaxY,1,1,false,false,false,null, new FillMaterial(FLOOR_COLOR) );
floor.clipping = 2;
rootContainer.addChild(floor);
floor.x = kdContainer.boundMaxX * .5;
floor.y = kdContainer.boundMaxY * .5;
rootContainer.addChild(kdContainer);
//フラクタルデータ保持用配列に初期値挿入
fillRectangleArray = new Array(p);
addChild(_canvas);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
TweenLite.delayedCall(1, createPlayer);
}
private function setupNode(p:RectanglePiece):* {
var node:*;
p.node = node = new KD_NODE();
node.boundMinX = p.x0 * WORLD_SCALE;
node.boundMinY = p.y0 * WORLD_SCALE;
node.boundMinZ = 0;
node.boundMaxX = p.x1 * WORLD_SCALE;
node.boundMaxY = p.y1 * WORLD_SCALE;
node.boundMaxZ = MAX_Z_BOUNDS;
var bNode:*;
node.positive = bNode = new KD_NODE();
bNode.boundMinX = node.boundMinX;
bNode.boundMinY = node.boundMinY;
//bNode.boundMinZ = height; // to be set later
bNode.boundMaxX = node.boundMaxX;
bNode.boundMaxY = node.boundMaxY;
bNode.boundMaxZ = MAX_Z_BOUNDS;
node.negative = bNode = new KD_NODE();
bNode.boundMinX = node.boundMinX;
bNode.boundMinY = node.boundMinY;
bNode.boundMinZ = 0;
bNode.boundMaxX = node.boundMaxX;
bNode.boundMaxY = node.boundMaxY;
//bNode.boundMaxZ = height; // to be set later
return node;
}
private function createBuilding(p:RectanglePiece, color:uint, fromHeight:Number, fromColor:uint):void
{
var node:* = p.node;
var height:Number = 1 + _tint * MAX_HEIGHT;
var w:Number = (p.x1 - p.x0) * WORLD_SCALE;
var h:Number = (p.y1 - p.y0) * WORLD_SCALE;
// set positive/negative dimensions
node.positive.boundMinZ = height;
node.negative.boundMaxZ = height;
node.axis = 2;
node.coord = height;
node.minCoord = height - 0.01;
node.maxCoord = height + 0.01;
var building:KDBuilding = new KDBuilding(p.x0 * WORLD_SCALE, p.y0 * WORLD_SCALE, w, h, height, color, node);
node = node.negative;
node.objectList = building;
node.objectBoundList = building;
building.height = fromHeight;
TweenLite.to(building, 1.6, { height:height, ease:Cubic.easeInOut } );
(building.faceList.material as FillMaterial).color = fromColor;
TweenLite.to(building.faceList.material, .4, { hexColors: { color:color }, ease:Linear.easeNone } );
}
private function createPlayer():void {
var playerDanbord:Danbord = addDanbord();
//if (Danbord.RENDERER == null) {
playerDanbord.setupRenderer(camera);
Danbord.precomputeAll();
//}
player = new DanboPlayer(playerDanbord, camera, rootContainer, stage);
var child:DisplayObject = addChild( new CrossHair() );
child.x = stage.stageWidth * .5;
child.y = stage.stageHeight * .5;
}
private function addDanbord(posX:Number=-1, posY:Number=-1, posZ:Number=-1):Danbord {
var mySc:Number = Math.random() * 1 + 0.5;
var myDanbord:Danbord = new Danbord(textures);
myDanbord.x = posX >= 0 ? posX : Math.random() * kdContainer.boundMaxX;
myDanbord.y = posY >=0 ? posY : Math.random() * kdContainer.boundMaxY;
//myDanbord.z = posZ >=0 ? posZ : Math.random() * AREA_
myDanbord.scaleX = myDanbord.scaleY = myDanbord.scaleZ = mySc;
// container.addChild(myDanbord);
var myKage:Kage = new Kage(textures);
myKage.x = myDanbord.x
myKage.y = myDanbord.y
myKage.scaleX = myKage.scaleY = myKage.scaleZ = mySc;
// kageContainer.addChild(myKage);
myKage.calculateBounds();
myDanbord.boundMinZ = 0;
myDanbord.boundMaxZ = 288;
const padd:Number = 1;
myDanbord.boundMinX = (myKage.boundMinX+padd) * mySc ;
myDanbord.boundMaxX =( myKage.boundMaxX-padd) * mySc;
myDanbord.boundMinY = (myKage.boundMinY+padd) * mySc;
myDanbord.boundMaxY = (myKage.boundMaxY-padd) * mySc;
myDanbord.kage = myKage;
// find zone for danbord
myDanbord.node = insertObjectIntoZNode(kdContainer.root, myDanbord);
myDanbord.worldBounds = myDanbord.node.positive.objectBoundList;
//rootContainer.addChild(myDanbord);
camera.lookAt(myDanbord.x, myDanbord.y, myDanbord.z);
cameraController.setObjectPosXYZ(myDanbord.x, myDanbord.y, myDanbord.z);
//
//rootContainer.addChild(myDanbord);
kdContainer.addChild(myDanbord);
return myDanbord;
}
// Inserts object into single node based on origin position without considering if it's bounds straddles multiple partitions
public function insertObjectIntoNode(node:*, obj:Object3D):* {
if (node.negative == null) {
// leaf object (Should consider if got multiple nodes)??
if (node.objectList != null) throw new Error("ALREAdy occupied!");
node.objectList = obj;
return node;
}
var axis:int = node.axis;
var value:int = axis == 0 ? obj.x : axis == 1? obj.y : obj.z;
return value >= node.coord ? insertObjectIntoNode(node.positive,obj) : insertObjectIntoNode(node.negative,obj);
}
// Inserts object into single z-axis split node based on 2d origin position without considering if it's bounds straddles multiple partitions, and placing it in positive half-space region (assuming it's a leaf rooftop zone)
public function insertObjectIntoZNode(node:*, obj:Object3D):* {
var axis:int = node.axis;
var bounds:Object3D;
if (axis == 2) {
// leaf object (Should consider if got multiple nodes)??
if (node.positive.objectList != null) throw new Error("ALREAdy occupied!"+node.positive.objectList);
//node.positive.objectList = obj;
node.positive.objectBoundList = bounds = new Object3D();
obj.z = node.maxCoord;
bounds.boundMinX = obj.boundMinX*obj.scaleX + obj.x;
bounds.boundMinY = obj.boundMinY*obj.scaleY + obj.y;
bounds.boundMinZ = obj.boundMinZ*obj.scaleZ + obj.z;
bounds.boundMaxX = obj.boundMaxX*obj.scaleX + obj.x;
bounds.boundMaxY = obj.boundMaxY*obj.scaleY + obj.y;
bounds.boundMaxZ = obj.boundMaxZ*obj.scaleZ + obj.z;
return node;
}
var value:int = axis != 0 ? obj.y : obj.x;
return value >= node.coord ? insertObjectIntoZNode(node.positive,obj) : insertObjectIntoZNode(node.negative,obj);
}
private var _lastTime:int = 0;
private static const MS:Number = 1 / 1000;
//毎フレーム実行
//ループ
private function onEnterFrame(e:Event):void
{
var node:*;
//ダンボーのモーション
var curTime:Number = getTimer();
var timeElapsed:Number = _lastTime != 0 ? curTime-_lastTime : 30;
timeElapsed *= MS;
_lastTime = curTime;
if (!_doGenerateBuildings) {
doPreRenderUpdates( timeElapsed);
camera.transformId++;
camera.render();
return;
}
//フラクタル処理終了
if (fillRectangleArray.length < 1) {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
var tx:TextField = new TextField();
tx.text = '終了';
tx.textColor = 0xFFFFFF;
addChild(tx);
}else {
//フラクタルデータ保持用配列から1つ取り出す
var rect:RectanglePiece = fillRectangleArray.shift();
var cArray:Array = deviationLogic(rect.x0, rect.y0, rect.x1, rect.y1);
rect.c = cArray[0];
rect.color = cArray[1];
var halfWidth:Number = (rect.x1 - rect.x0) * .5;
var halfHeight:Number = (rect.y1 - rect.y0) * .5;
// 指定した矩形内の輝度の標準偏差値が閾値以上なら2分木して処理続行
if (rect.c > THRESHOLD && (halfWidth > 2 || halfHeight > 2)) {
//矩形を書くよ
/*
_canvas.graphics.lineStyle(0, 0xAAAAAA);
_canvas.graphics.beginFill(cArray[1]);
_canvas.graphics.drawRect(rect.x0, rect.y0, (rect.x1 - rect.x0), (rect.y1 - rect.y0));
*/
node = rect.node;
var removeObj:KDBuilding; // the building object holder
if (rect.parent != null ) {
if ((removeObj=rect.parent.node.negative.objectList) != null) {
rect.parent.node.negative.objectList = null;
rect.parent.node.negative.objectBoundList = null;
var removeObj2:Danbord = rect.parent.node.positive.objectList; // the rooftop object holder
// do main starting cut
rect.parent.node.axis = rect.parent.axis;
rect.parent.node.coord = rect.parent.coord;
rect.parent.node.minCoord = rect.parent.coord - 0.01;
rect.parent.node.maxCoord = rect.parent.coord + 0.01;
if (rect.positive) {
rect.parent.node.positive = node;
rect.parent.node.negative = rect.sibling.node;
}
else {
rect.parent.node.negative = node;
rect.parent.node.positive = rect.sibling.node;
}
TweenLite.killTweensOf(removeObj);
createBuilding(rect, rect.color, removeObj.height, rect.parent.color);
createBuilding(rect.sibling, rect.parent.color, removeObj.height, rect.parent.color);
if (removeObj2 != null) {
// rect.parent.node.positive.objectList = null;
// rect.parent.node.positive.objectBoundList = null;
}
}
else { // fill up remaining branch (either negative or postiive)
if (rect.positive) {
rect.parent.node.positive = node;
}
else {
rect.parent.node.negative = node;
}
removeObj = rect.node.negative.objectList;
createBuilding(rect, rect.color, removeObj.height, rect.parent.color);
}
}
else { // Root node case!
createBuilding(rect, rect.color, 0, FLOOR_COLOR);
}
//矩形を2分割してフラクタルデータ保持用配列に突っ込む
var rect0:RectanglePiece = new RectanglePiece();
var rect1:RectanglePiece = new RectanglePiece();
var randomX:Number = Math.floor(Math.random()*(halfWidth-4)+4);
var randomY:Number = Math.floor(Math.random()*(halfHeight-4)+4);
// Rather hackish pointers here!
rect0.positive = false;
rect1.positive = true;
rect0.sibling = rect1;
rect1.sibling = rect0;
rect0.parent = rect;
rect1.parent = rect;
if (halfWidth > halfHeight) {
// node.axis = 0;
// node.coord = (rect.x0 + randomX) * WORLD_SCALE;
// node.minCoord = node.coord - kdContainer.threshold;
// node.maxCoord = node.coord + kdContainer.threshold;
rect.axis = 0;
rect.coord = (rect.x0 + randomX) * WORLD_SCALE;
rect0.x0 = rect.x0; // negative x
rect0.y0 = rect.y0;
rect0.x1 = rect.x0+randomX;
rect0.y1 = rect.y1;
fillRectangleArray.push(rect0);
// setupNodeExisting(rect0, node.negative);
setupNode(rect0);
rect1.x0 = rect.x0+randomX; // postive x
rect1.y0 = rect.y0;
rect1.x1 = rect.x1;
rect1.y1 = rect.y1;
fillRectangleArray.push(rect1);
//setupNodeExisting(rect1, node.positive);
setupNode(rect1);
}else {
// node.axis = 1;
// node.coord = (rect.y0 + randomY) * WORLD_SCALE;
// node.minCoord = node.coord - kdContainer.threshold;
// node.maxCoord = node.coord + kdContainer.threshold;
rect.axis = 1;
rect.coord = (rect.y0 + randomY) * WORLD_SCALE;
rect0.x0 = rect.x0; // negative y
rect0.y0 = rect.y0;
rect0.x1 = rect.x1;
rect0.y1 = rect.y0+randomY;
fillRectangleArray.push(rect0);
// setupNodeExisting(rect0, node.negative);
setupNode(rect0);
rect1.x0 = rect.x0; //postive y
rect1.y0 = rect.y0+randomY;
rect1.x1 = rect.x1;
rect1.y1 = rect.y1;
fillRectangleArray.push(rect1);
// setupNodeExisting(rect1, node.positive);
setupNode(rect1);
}
}
}
doPreRenderUpdates(timeElapsed);
camera.transformId++;
camera.render();
}
private function doPreRenderUpdates(t:Number):void
{
if (player == null) updateSpecCamera()
else {
player.update(t);
player.preRender();
}
}
private function updateSpecCamera():void {
cameraController.update();
var sphere:Vector3D = new Vector3D(camera.x, camera.y, camera.z, SPEC_RADIUS);
var len:int = KDBuilding.numCollidables;
var collidables:Vector.<KDBuilding> = KDBuilding.collidables;
var collidable:KDBuilding;
var highestZ:Number = 0;
if (!cameraController.hasMoved) {
// do a dummy collision test
cameraController.collider.getCollision( new Vector3D(camera.x, camera.y, camera.z), DUMMY_DISPLACE ,DUMMY_VECTOR1, DUMMY_VECTOR2, rootContainer);
}
len = KDBuilding.numCollidables;
for (var i:int = 0; i < len; i++) {
collidable = collidables[i];
if (kdContainer.boundIntersectSphere(sphere,collidable.boundMinX, collidable.boundMinY, collidable.boundMinZ, collidable.boundMaxX, collidable.boundMaxY, collidable.boundMaxZ) && collidable.boundMaxZ > highestZ) {
highestZ = collidable.boundMaxZ;
}
}
if ( camera.z < highestZ + SPEC_RADIUS) {
cameraController.setObjectPosXYZ(camera.x, camera.y, highestZ + SPEC_RADIUS);
camera.z = highestZ + SPEC_RADIUS;
}
}
/**
* 指定した矩形間の輝度の標準偏差を求める
* @param x0 左上のx座標
* @param y0 左上のy座標
* @param x1 右下のx座標
* @param y1 右下のy座標
* @return 標準偏差値とカラーの平均
*/
private function deviationLogic(x0:Number,y0:Number,x1:Number,y1:Number):Array {
var rgb:uint = 0;
var r:uint = 0;
var g:uint = 0;
var b:uint = 0;
var hsb:Array = new Array();
var bArray:Array = new Array();
var br:Number = 0;
var av:Number = 0;
//輝度の平均を計算
for (var i:int = x0; i < x1;i++ ) {
for (var j:int = y0; j < y1; j++ ) {
rgb = imageData.getPixel(i, j);
r += (rgb >> 16) & 255;
g += (rgb >> 8) & 255;
b += rgb & 255;
hsb = uintRGBtoHSB(rgb);
br += hsb[2];
bArray.push(hsb[2]);
}
}
av = br / bArray.length;
r = r / bArray.length;
g = g / bArray.length;
b = b / bArray.length;
rgb = (r << 16) | (g << 8) | (b << 0);
_tint = (255 - ( 0.21 * r + 0.71 * g + 0.07 * b )) * INV_255;
//標準偏差を計算
br = 0;
for (i = 0; i < bArray.length; i++ ) {
br += (bArray[i] - av) *(bArray[i] - av);
}
return [Math.sqrt(br / bArray.length),rgb];
}
/**
*
* @param rgb RGB成分(uint)
* @return HSB配列([0]=hue, [1]=saturation, [2]=brightness)
*/
private function uintRGBtoHSB(rgb:uint):Array {
var r:uint = (rgb >> 16) & 255;
var g:uint = (rgb >> 8) & 255;
var b:uint = rgb & 255;
return RGBtoHSB(r, g, b);
}
/** RGBからHSBをつくる
* @param r 色の赤色成分(0~255)
* @param g 色の緑色成分(0~255)
* @param b 色の青色成分(0~255)
* @return HSB配列([0]=hue, [1]=saturation, [2]=brightness)
*/
private function RGBtoHSB(r:int, g:int, b:int):Array {
var cmax:Number = Math.max(r, g, b);
var cmin:Number = Math.min(r, g, b);
var brightness:Number = cmax / 255.0;
var hue:Number = 0;
var saturation:Number = (cmax != 0) ? (cmax - cmin) / cmax : 0;
if (saturation != 0) {
var redc:Number = (cmax - r) / (cmax - cmin);
var greenc:Number = (cmax - g) / (cmax - cmin);
var bluec:Number = (cmax - b) / (cmax - cmin);
if (r == cmax) {
hue = bluec - greenc;
} else if (g == cmax) {
hue = 2.0 + redc - bluec;
} else {
hue = 4.0 + greenc - redc;
}
hue = hue / 6.0;
if (hue < 0) {
hue = hue + 1.0;
}
}
return [hue, saturation, brightness];
}
}
}
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Canvas;
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.core.VG;
import alternativ7.engine3d.core.Wrapper;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.objects.Mesh;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.alternativa3d;
import flash.filters.DropShadowFilter;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* ...
* @author DefaultUser (Tools -> Custom Arguments...)
*/
class RectanglePiece
{
public var x0:Number;
public var y0:Number;
public var x1:Number;
public var y1:Number;
public var c:Number;
public var node:*;
public var parent:RectanglePiece;
public var positive:Boolean;
public var color:uint;
public var sibling:RectanglePiece;
// split kd info
public var axis:int;
public var coord:Number;
public function RectanglePiece()
{
this.x0 = 0;
this.y0 = 0;
this.x1 = 0;
this.x1 = 0;
this.c = 0;
}
}
class MyEllipsoidCollider extends EllipsoidCollider {
public function MyEllipsoidCollider(radiusX:Number, radiusY:Number, radiusZ:Number) {
super(radiusX, radiusY, radiusZ);
}
override public function calculateDestination (source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Vector3D {
KDBuilding.numCollidables = 0;
return super.calculateDestination(source, displacement, object, excludedObjects);
}
override public function getCollision (source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Boolean {
KDBuilding.numCollidables = 0;
return super.getCollision(source, displacement, resCollisionPoint, resCollisionPlane, object, excludedObjects);
}
public function $calculateDestination(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Vector3D {
return super.calculateDestination(source, displacement, object, excludedObjects);
}
public function $getCollision (source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Boolean {
return super.getCollision(source, displacement, resCollisionPoint, resCollisionPlane, object, excludedObjects);
}
}
class KDBuilding extends Mesh
{
// TODO: Can consider recycling of KDBuildings
public static var collidables:Vector.<KDBuilding> = new Vector.<KDBuilding>();
public static var numCollidables:int = 0;
//public var childrenList:Object3D;
public static var DS_SORT:DistanceSortContainer = new DistanceSortContainer();
public static var CONFLICT:ConflictContainer = new ConflictContainer();
public var node:*;
public function KDBuilding(xPos:Number, yPos:Number, width:Number, length:Number, height:Number, color:uint, node:*)
{
clipping = 2;
sorting = 0;
this.node = node;
var v1:Vertex;
var v2:Vertex;
var v3:Vertex;
var v4:Vertex;
var mat:FillMaterial = new FillMaterial(color); //, 1, 0, 0xDDCC66
var v:Vertex;
var f:Face;
var w:Wrapper;
// Define top roof vertices
vertexList = v = Vertex.collector || new Vertex(); v.u = 0; v.v = 0;
Vertex.collector = v.next;
v.x = xPos;
v.y = yPos;
v.z = height;
v1 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos + width;
v.y = yPos;
v.z = height;
v2 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos + width;
v.y = yPos + length;
v.z = height;
v3 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos;
v.y = yPos + length;
v.z = height;
v4 = v;
var v5:Vertex;
var v6:Vertex;
var v7:Vertex;
var v8:Vertex;
// Define bottom vertices
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos;
v.y = yPos;
v.z = 0;
v5 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos + width;
v.y = yPos;
v.z = 0;
v6 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos + width;
v.y = yPos + length;
v.z = 0;
v7 = v;
v.next = v = v.create(); v.u = 0; v.v = 0;
v.x = xPos;
v.y = yPos + length;
v.z = 0;
v8 = v;
// top face
faceList = f = Face.collector || new Face();
Face.collector = f.next;
f.material = mat;
f.wrapper = w = Wrapper.collector || new Wrapper();
Wrapper.collector = w.next;
w.vertex = v1;
w.next = w = w.create();
w.vertex = v2;
w.next = w = w.create();
w.vertex = v3;
w.next = w = w.create();
w.vertex = v4;
f.normalX = 0;
f.normalY = 0;
f.normalZ = 1;
f.offset = height;
//f.calculateBestSequenceAndNormal();
//if (!f.normal.nearEquals(new Vector3D(0,0,1), 0.001)) throw new Error("MISMATCH top!"+f.normal);
// South face
f.next = f = f.create();
f.material = mat;
f.wrapper = w = w.create();
w.vertex = v5;
w.next = w = w.create();
w.vertex = v6;
w.next = w = w.create();
w.vertex = v2;
w.next = w = w.create();
w.vertex = v1;
f.normalX = 0;
f.normalY = -1;
f.normalZ = 0;
f.offset = -yPos;
//f.calculateBestSequenceAndNormal();
//if (!f.normal.nearEquals(new Vector3D(0,-1,0), 0.001)) throw new Error("MISMATCH south!"+f.normal);
// East Face
f.next = f = f.create();
f.material = mat;
f.wrapper = w = w.create();
w.vertex = v6;
w.next = w = w.create();
w.vertex = v7;
w.next = w = w.create();
w.vertex = v3;
w.next = w = w.create();
w.vertex = v2;
f.normalX = 1;
f.normalY = 0;
f.normalZ = 0;
f.offset = xPos + width;
//f.calculateBestSequenceAndNormal();
//if (!f.normal.nearEquals(new Vector3D(1,0,0), 0.001)) throw new Error("MISMATCH east!"+f.normal);
// North Face
f.next = f = f.create();
f.material = mat;
f.wrapper = w = w.create();
w.vertex = v7;
w.next = w = w.create();
w.vertex = v8;
w.next = w = w.create();
w.vertex = v4;
w.next = w = w.create();
w.vertex = v3;
f.normalX = 0;
f.normalY = 1;
f.normalZ = 0;
f.offset = yPos + length;
//f.calculateBestSequenceAndNormal();
//if (!f.normal.nearEquals(new Vector3D(0,1,0), 0.001)) throw new Error("MISMATCH north!"+f.normal);
// West Face
f.next = f = f.create();
f.material = mat;
f.wrapper = w = w.create();
w.vertex = v8;
w.next = w = w.create();
w.vertex = v5;
w.next = w = w.create();
w.vertex = v1;
w.next = w = w.create();
w.vertex = v4;
f.normalX = -1;
f.normalY = 0;
f.normalZ = 0;
f.offset = -xPos;
//f.calculateBestSequenceAndNormal();
//if (!f.normal.nearEquals(new Vector3D(-1,0,0), 0.001)) throw new Error("MISMATCH west!"+f.normal);
// ^^^ note vertex normals not coded in!
// calculate bounds
boundMinX = xPos;
boundMinY = yPos;
boundMinZ = 0;
boundMaxX = xPos + width;
boundMaxY = yPos + length;
boundMaxZ = height;
//if (node.axis != 2) throw new Error("SHOULD NOT BE!");
// for debugging (checking) purposes
//calculateFacesNormals();
}
// Boiler-plate mesh draw implementation to prevent errors with camera.debug mode due to
// private classes.
override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {
calculateInverseMatrix();
// either transformId++; or if camera.transformId used as an incrementing timestamp
transformId = camera.transformId;
//if (childrenList != null) drawChildren(camera, parentCanvas);
// shoudl check below bounds if need to draw??
var f:Face = prepareFaces(camera);
if (f == null) return;
if (culling > 0) {
f = camera.clip(f, culling);
if (f == null) return;
}
drawFaces(camera, parentCanvas.getChildCanvas(true, false, this, 1, blendMode, colorTransform, filters), f);
}
/*
private function drawChildren(camera:Camera3D, parentCanvas:Canvas):void {
// draw children on rooftop.
const DS_SORT:DistanceSortContainer = DS_SORT;
DS_SORT.childrenList = childrenList;
DS_SORT.boundMinX = boundMinX;
DS_SORT.boundMinY = boundMinY;
DS_SORT.boundMinZ = boundMinZ;
DS_SORT.boundMaxX = boundMaxX;
DS_SORT.boundMaxY = boundMaxY;
DS_SORT.boundMaxZ = boundMaxZ;
if (DS_SORT.transformId != transformId) {
DS_SORT.ma = ma;
}
if (DS_SORT.cullingInCamera(camera, culling) < 0) return;
DS_SORT.draw(camera, parentCanvas);
}
*/
/*
override alternativa3d function getVG(camera:Camera3D):VG { // wip
calculateInverseMatrix();
// either transformId++; or if camera.transformId used as an incrementing timestamp
//transformId = camera.transformId;
transformId++;
var f:Face = prepareFaces(camera);
if (f == null) return null;
if (culling > 0) {
camera.clip(f, culling);
if (f == null) return null;
}
return VG.create(this, f, 2, 0, false);
}
*/
override alternativa3d function collectPlanes(center:Vector3D, a:Vector3D, b:Vector3D, c:Vector3D, d:Vector3D, collector:Vector.<Face>, excludedObjects:Dictionary=null) : void {
transformId++;
collidables[numCollidables++] = this;
super.collectPlanes(center, a, b, c, d, collector, excludedObjects);
}
public function get height():Number { return vertexList.z; }
public function set height(value:Number):void
{
// todo: investigate... tween should have been killed once axis==2 changes to 0 or 1.
if (node.axis != 2) return;// throw new Error("SHOudl nto be!");
var v:Vertex = vertexList;
v.z = value; v = v.next;
v.z = value; v = v.next;
v.z = value; v = v.next;
v.z = value;
faceList.offset = value;
boundMaxZ = value;
node.negative.boundMaxZ = value;
node.positive.boundMinZ = value;
}
}
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Object3D;
import flash.display.InteractiveObject;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
/**
* ...
* @author Glenn Ko
*/
class SimpleFlyController extends SimpleObjectController
{
public var currentPosition:Vector3D;
public var collider:EllipsoidCollider;
public var collidable:Object3D;
public var displacement:Vector3D = new Vector3D();
public var collisionPoint:Vector3D = new Vector3D();
public var lastPosition:Vector3D = new Vector3D();
public var excludedObjects:Dictionary = null;
public var gotCollision:Boolean;
public var collisionPlane:Vector3D = new Vector3D();
public var hasMoved:Boolean = false;
public function SimpleFlyController(collider:EllipsoidCollider, collidable:Object3D, eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1)
{
super(eventSource, object, speed, speedMultiplier, mouseSensitivity);
this.collider = collider;
this.collidable = collidable;
}
override public function update():void {
var object:Object3D = this.object;
if (object == null) return;
if (collider && collidable) {
lastPosition.x = object.x;
lastPosition.y = object.y;
lastPosition.z = object.z;
super.update();
displacement.x = object.x - lastPosition.x;
displacement.y = object.y - lastPosition.y;
displacement.z = object.z - lastPosition.z;
if (displacement.x * displacement.x + displacement.y * displacement.y + displacement.z * displacement.z == 0) {
gotCollision = false;
hasMoved = false;
return;
}
hasMoved = true;
gotCollision = collider.getCollision(lastPosition, displacement, collisionPoint, collisionPlane, collidable);
var dest:Vector3D = collider.calculateDestination(lastPosition, displacement, collidable, excludedObjects);
// set back frame coherant transform values
setObjectPosXYZ(dest.x, dest.y, dest.z);
// refresh values immediately
object.x = dest.x;
object.y = dest.y;
object.z = dest.z;
displacement.x = dest.x - lastPosition.x;
displacement.y = dest.y - lastPosition.y;
displacement.z = dest.z - lastPosition.z;
currentPosition = dest;
}
else {
super.update();
}
}
}
//Texture.as
//テクスチャを一括読み込み
//package {
import alternativ7.engine3d.containers.BSPContainer;
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Canvas;
import alternativ7.engine3d.core.Debug;
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.VG;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.materials.Material;
import alternativ7.engine3d.objects.Mesh;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.primitives.Sphere;
import flash.display.FrameLabel;
import flash.display.Shape;
import flash.display.Stage;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Vector3D;
import flash.system.LoaderContext;
import flash.ui.Keyboard;
import flash.utils.Dictionary;
//
//Alternativa3D
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.loaders.MaterialLoader;
import alternativ7.engine3d.materials.TextureMaterial;
//public
class Textures {
//3D関連
private var loaderContext:LoaderContext;
private var materialLoader:MaterialLoader;
private var textureMats:Vector.<TextureMaterial>;
public var txMatDanboTop:TextureMaterial;
public var txMatDanboBottom:TextureMaterial;
public var txMatDanboFront:TextureMaterial;
public var txMatDanboLeft:TextureMaterial;
public var txMatDanboBack:TextureMaterial;
public var txMatDanboRight:TextureMaterial;
public var txMatDanboFace:TextureMaterial;
public var txMatDanboHeadTop:TextureMaterial;
public var txMatDanboBodyFront:TextureMaterial;
public var txMatKage:TextureMaterial;
public function Textures():void {
//テクスチャの読み込み
txMatDanboTop = new TextureMaterial(); txMatDanboTop.name = "top";
txMatDanboBottom = new TextureMaterial(); txMatDanboBottom.name = "bottom";
txMatDanboFront = new TextureMaterial(); txMatDanboFront.name = "front";
txMatDanboLeft = new TextureMaterial(); txMatDanboLeft.name = "left";
txMatDanboBack = new TextureMaterial(); txMatDanboBack.name = "back";
txMatDanboRight = new TextureMaterial(); txMatDanboRight.name = "right";
txMatDanboFace = new TextureMaterial(); txMatDanboFace.name = "face";
txMatDanboHeadTop = new TextureMaterial(); txMatDanboHeadTop.name = "headTop";
txMatDanboBodyFront = new TextureMaterial(); txMatDanboBodyFront.name = "bodyFront";
txMatKage = new TextureMaterial();
txMatDanboTop.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_top.png";
txMatDanboBottom.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_bottom.png";
txMatDanboFront.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_front.png";
txMatDanboLeft.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_left.png";
txMatDanboBack.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_back.png";
txMatDanboRight.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_right.png";
txMatDanboFace.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_face.png";
txMatDanboHeadTop.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_headtop.png";
txMatDanboBodyFront.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_bodyfront.png";
txMatKage.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/tx_kage.png";
textureMats = new Vector.<TextureMaterial>();
textureMats.push(txMatDanboTop);
textureMats.push(txMatDanboBottom);
textureMats.push(txMatDanboFront);
textureMats.push(txMatDanboLeft);
textureMats.push(txMatDanboBack);
textureMats.push(txMatDanboRight);
textureMats.push(txMatDanboFace);
textureMats.push(txMatDanboHeadTop);
textureMats.push(txMatDanboBodyFront);
textureMats.push(txMatKage);
//
//MaterialLoaderに読み込むテクスチャのリストを投げて、読み込み開始
loaderContext = new LoaderContext(true);
materialLoader = new MaterialLoader();
materialLoader.load(textureMats, loaderContext);
}
}
class DanboPlayer {
private var danbo:Danbord;
private var scene:Object3D;
public var thirdPerson:OrbitCameraMan;
public var surfaceMovement:SurfaceMovement;
public var jump:Jump;
public var velocity:Vector3D = new Vector3D();
public var position:Vector3D = new Vector3D();
public var rotation:Vector3D = new Vector3D();
public var radius:Vector3D = new Vector3D(16,16,32);
private var /*static const*/ playerMover:PlayerMover = new PlayerMover();
private var /*static const*/ qPhysics:QPhysics = new QPhysics(QPhysics.FLAG_GRAVITY);
private var lookAtObject:Object3D = new Object3D();
public var excludedObjects:Dictionary = new Dictionary();
private var lookAtOffset:Vector3D = new Vector3D();
private var keyBinder:KeyBinder = new KeyBinder();
private var colliderPosition:Vector3D = new Vector3D();
private var walkSpeed:Number;
private var runSpeed:Number;
public function calculateMovementSpeeds():void {
walkSpeed = 1 + (10 * radius.z/400);
runSpeed = 1 + (30 * radius.z/400);
}
public function DanboPlayer(danbo:Danbord, camera:Camera3D, scene:Object3D, stage:Stage) {
this.scene = scene;
// -- Player-Specific stuff for Client
thirdPerson = new OrbitCameraMan(new OrbitCameraController(camera, lookAtObject, stage, stage, stage, false, false), scene, danbo );
//thirdPerson.controller.easingSeparator = 12;
thirdPerson.preferedZoom = 1000;
thirdPerson.controller.minDistance = 100;
thirdPerson.controller.maxDistance = 1800;
//thirdPerson.controller.minAngleLatitude = 5;
thirdPerson.controller.minAngleLatitude = -85;
thirdPerson.controller.maxAngleLatidude =75;
thirdPerson.followAzimuth = true;
thirdPerson.useFadeDistance = true;
thirdPerson.maxFadeAlpha = 1;
keyBinder.clear();
keyBinder.setupStageListeners(stage);
keyBinder.bindKey(Keyboard.W, forward);
keyBinder.bindKey(Keyboard.S, back);
keyBinder.bindKey(Keyboard.A, left);
keyBinder.bindKey(Keyboard.D, right);
keyBinder.bindKey(Keyboard.SPACE, space);
keyBinder.bindKey(Keyboard.SHIFT, speedChange);
// stage.addEventListener(MouseEvent.MOUSE_WHEEL, thirdPerson.mouseWheelHandler);
// -- The below also applies to NPCs or "other players" besides Client
this.danbo = danbo;
updateRadius();
lookAtOffset.y = 0;
lookAtOffset.z = 200;
surfaceMovement = new SurfaceMovement();
jump = new Jump(1, 2136);// new Jump(1, 2135);
excludedObjects[danbo] = true;
excludedObjects[danbo.kage] = true;
//danbo.z += 100;
setupPosition();
// view collision sphere
/*
var sphere:Object3D = new Sphere(radius.z, 8, 7, false, new FillMaterial(0xFF0000) );
sphere.x = position.x;
sphere.y = position.y;
sphere.z = position.z;
(scene as Object3DContainer).addChild( sphere);
excludedObjects[sphere] = true;
*/
calculateMovementSpeeds();
}
// Client controls
public function getZoomRatio():Number {
return (thirdPerson.preferedZoom- thirdPerson.controller.minDistance) / (thirdPerson.controller.maxDistance - thirdPerson.controller.minDistance);
}
public function set zoom(ratio:Number):void {
ratio = 1 - ratio;
thirdPerson.preferedZoom = thirdPerson.controller.minDistance + (thirdPerson.controller.maxDistance - thirdPerson.controller.minDistance) * ratio;
}
private function speedChange(boo:Boolean):void
{
_runMode = !boo;
}
private function space(boo:Boolean):void
{
if (boo == _space) return;
_space = boo;
}
private function right(boo:Boolean):void
{
if (boo == _right) return;
surfaceMovement.strafe_state += boo ? 1 : -1;
_right = boo;
}
private function left(boo:Boolean):void
{
if (boo == _left) return;
surfaceMovement.strafe_state += boo ? -1 : 1;
_left = boo;
}
private function back(boo:Boolean):void
{
if (boo == _back) return;
surfaceMovement.walk_state += boo ? -1 : 1;
_back = boo;
}
private function forward(boo:Boolean):void
{
if (boo == _forward) return;
surfaceMovement.walk_state += boo ? 1 : -1;
_forward = boo;
}
// Methods
private function updateRadius():void
{
radius.x = (danbo.boundMaxX - danbo.boundMinX + 12)*danbo.scaleX * .5;
radius.y = (danbo.boundMaxY - danbo.boundMinY + 12)*danbo.scaleY * .5;
radius.z = (danbo.boundMaxZ - danbo.boundMinZ + 35)*danbo.scaleZ * .5;
}
private function setupPosition():void
{
position.x = danbo.x;
position.y = danbo.y;
position.z = danbo.z + radius.z;
}
private function preRenderPosition():void {
danbo.x = position.x;
danbo.y = position.y;
danbo.z = position.z - radius.z;
danbo.kage.x = danbo.x;
danbo.kage.y = danbo.y;
//danbo.kage.z = danbo.z;
}
private var _back:Boolean= false;
private var _forward:Boolean= false;
private var _left:Boolean = false;
private var _right:Boolean = false;
private var _space:Boolean = false;
private var _runMode:Boolean = true;
private var maxGroundNormal:Vector3D;
public static const GROUND_NORMAL:Vector3D = new Vector3D();
public function update(t:Number):void {
// Update global environs
qPhysics.update(t, velocity);
rotation.x = danbo.rotationX;
rotation.y = danbo.rotationY;
rotation.z = danbo.rotationZ;
// Jump
jump.update(t);
var gotJump:Boolean = false;
if ( _space) { //maxGroundNormal != null &&
gotJump = jump.do_jump(velocity, t);
}
// Movement along surface (todo: need to pre-determine any potential moving surface contacts based
// on current kd building node contacts in 2D). If building moves and got current maximim groound normal
// on building contact, character should with the platform as well, unless he's jumping?
var lastPosition:Vector3D = position.clone();
maxGroundNormal = playerMover.queryMove(radius, position, velocity, scene, excludedObjects);
var numCollidables:int = KDBuilding.numCollidables;
var collidables:Vector.<KDBuilding> = KDBuilding.collidables;
var collidable:KDBuilding;
var worldBounds:Object3D = danbo.worldBounds;
var highestZ:Number = position.z
// hack atm
// /*
preRenderPosition(); // call this so can check node...(hmm.. shoudl refactor this)
danbo.checkNode();
if (numCollidables > 0) {
// check buildings in vincity to clamp z position up to positive (rooftop) node
for ( var i:int = 0; i < numCollidables; i++) {
collidable = collidables[i];
if (!(worldBounds.boundMinX > collidable.boundMaxX ||
worldBounds.boundMinY > collidable.boundMaxY ||
worldBounds.boundMaxX < collidable.boundMinX ||
worldBounds.boundMaxY < collidable.boundMaxY)) {
if (collidable.boundMaxZ + radius.z + 0.01 > highestZ) {
highestZ = collidable.boundMaxZ + radius.z + 0.01;
maxGroundNormal = GROUND_NORMAL
}
}
}
}
position.z = highestZ;
if (danbo.node.axis != 2) migrate(danbo.node);
if (danbo.node.positive.boundMinZ + radius.z > highestZ ) {
position.z = danbo.node.positive.boundMinZ + radius.z;
maxGroundNormal = GROUND_NORMAL;
}
// */
var baseSpeed:Number = _runMode ? runSpeed : walkSpeed;
surfaceMovement.WALK_SPEED = baseSpeed;
surfaceMovement.WALKBACK_SPEED = baseSpeed * 1;
surfaceMovement.STRAFE_SPEED = baseSpeed * 1;
surfaceMovement.update(t, position, rotation, velocity, maxGroundNormal );
if (maxGroundNormal == null) {
danbo.spZ -= 9.8 / 60;
// myDanbord.z += myDanbord.spZ;
}
// Animations
danbo.setMotion( maxGroundNormal == null ? "jump" :
maxGroundNormal != null && (surfaceMovement.strafe_state != 0 || surfaceMovement.walk_state != 0) ?
_runMode ? "dash" : "walk"
:
"stand"
);
danbo.enterFrameEvent2();
// Update animations for Client
/*
var useLat:Number = thirdPerson.controller.angleLatitude;
if (useLat > 45 ) useLat = 45;
if (useLat < 0 ) useLat = 0;
danbo.danHead.rotationX = -useLat * DEG_TO_RAD * .7;
*/
}
private static const DEG_TO_RAD:Number = Math.PI / 180;
private function preRenderCallback():void {
preRenderPosition();
// Adjust look at position for Client Camera
// This is basically a hardcoded way to translate position of lookAtObject from local coordinate space to global coordinate space.
// If not, one would need to uncomment "// testLook = _followTarget.localToGlobal(testLook);"
// from OrbitCameraController class.
lookAtObject.x = lookAtOffset.x + danbo.danHead.x + danbo.danBody.x;
lookAtObject.y = lookAtOffset.y + danbo.danHead.y + danbo.danBody.y;
lookAtObject.z = lookAtOffset.z + danbo.danHead.z + danbo.danBody.z;
danbo.composeMatrix();
lookAtObject.composeAndAppend(danbo);
lookAtObject.x = lookAtObject.md;
lookAtObject.y = lookAtObject.mh;
lookAtObject.z = lookAtObject.ml;
}
private function get pitchRatio():Number {
return (thirdPerson.controller.angleLatitude - thirdPerson.controller.minAngleLatitude) / (thirdPerson.controller.maxAngleLatidude - thirdPerson.controller.minAngleLatitude);
}
private function get arcRatio():Number {
return thirdPerson.controller.angleLatitude < 0 ? -thirdPerson.controller.angleLatitude/thirdPerson.controller.minAngleLatitude : thirdPerson.controller.angleLatitude/thirdPerson.controller.maxAngleLatidude;
}
private function migrate(node:*):void { // need to migrate down to sub KD node split
var axis:int = node.axis;
var value:Number = axis == 0 ? danbo.x : danbo.y;
node.objectBoundList = null;
node.objectList = null;
node = value >= node.coord ? node.positive : node.negative;
danbo.node = node;
if (node.axis != 2) throw new Error("STILL NEED !");
node = node.positive;
node.objectBoundList = danbo.worldBounds;
node.objectList = danbo;
}
public function preRender():void {
danbo.updateWorldBounds();
// Client view
lookAtOffset.y = 90 * arcRatio; // adjust so that got crosshair to view at center
// This is stupid, have to call this multiple times to ensure Danbo's rotation transform of lookAtOffset vector appears more accruately without jittering.
preRenderCallback();
thirdPerson.update();
preRenderCallback();
thirdPerson.update();
preRenderCallback();
thirdPerson.update();
}
}
class PlayerMover {
public var collisionPoint:Vector3D = new Vector3D();
public var collisionNormal:Vector3D = new Vector3D();
private static const GRAVITY_DIR:Vector3D = new Vector3D(0, 0, -1);
public static var COLLIDER:MyEllipsoidCollider = new MyEllipsoidCollider(2, 2, 2);
public function queryMove(radius:Vector3D, position:Vector3D, displacement:Vector3D, scene:Object3D, excludedObjects:Dictionary = null):Vector3D {
const collider:MyEllipsoidCollider = COLLIDER;
collider.radiusX = radius.x;
collider.radiusY = radius.y;
collider.radiusZ = radius.z;
const collisionPoint:Vector3D = collisionPoint;
const collisionNormal:Vector3D = collisionNormal;
var dest:Vector3D = collider.calculateDestination(position, displacement, scene, excludedObjects);
var maxGroundNormal:Vector3D
if (collider.getCollision(position, displacement, collisionPoint, collisionNormal, scene, excludedObjects) ) {
if (collisionNormal.z >= .8) {
maxGroundNormal = collisionNormal; // <- shoudl still count this?
}
else {
GRAVITY_DIR.z = displacement.z;
if ( collider.$getCollision(dest, GRAVITY_DIR, collisionPoint, collisionNormal, scene, excludedObjects) ) {
if (collisionNormal.z >= .8) {
maxGroundNormal = collisionNormal;
}
// collisionEvent.next = groundCollision;
}
}
// p.move.collisions = collisionEvent;
// return maxGroundNormal;
}
else {
// p.move.clearCollisions();
}
position.x = dest.x;
position.y = dest.y;
position.z = dest.z;
if (position.z > NodeUtils.MAX_ALTITUDE - radius.z) position.z = NodeUtils.MAX_ALTITUDE - radius.z;
if (position.z < NodeUtils.MIN_ALTITUDE + radius.z) position.z = NodeUtils.MIN_ALTITUDE + radius.z;
return maxGroundNormal;
}
}
class Jump
{
// State Settings
public var JUMP_COOLDOWN:Number;
// State for thing
private var jump_timer:Number;
private var jump_speed:Number;
public var enabled:Boolean; // use this as a master lock to enable/disable jump depending on situation
public function Jump(timeCooldown:Number, jumpSpeed:Number)
{
JUMP_COOLDOWN = timeCooldown;
jump_speed = jumpSpeed;
jump_timer = 0;
enabled = true;
}
public function update(time:Number ):void {
jump_timer = jump_timer-time < 0 ? 0 : jump_timer - time;
}
public function do_jump(velocity:Vector3D, time:Number):Boolean {
if (enabled && this.jump_timer == 0)
{
velocity.z += jump_speed * time;
jump_timer = JUMP_COOLDOWN;
return true;
}
return false;
}
}
class SurfaceMovement
{
// State settings
public var WALK_SPEED:Number;
public var WALKBACK_SPEED:Number;
public var STRAFE_SPEED:Number;
// State for thing
// delta walk/strafe state (-1 for backwards/left, 0 for neither direction, 1 for forwards/right)
public var walk_state:int;
public var strafe_state:int;
public static const WALK_FORWARD:int = 1;
public static const WALK_STOP:int = 0;
public static const WALK_BACK:int = -1;
public static const STRAFE_LEFT:int = -1;
public static const STRAFE_STOP:int = 0;
public static const STRAFE_RIGHT:int = 1;
// Normalized forward direction along surface
public var forwardVec:Vector3D;
public var rightVec:Vector3D;
public var friction:Number;
public function SurfaceMovement()
{
walk_state = 0;
strafe_state = 0;
forwardVec = new Vector3D();
rightVec = new Vector3D();
friction = .25;
setWalkSpeeds(16);
setStrafeSpeed(10);
}
public function setWalkSpeeds(forwardSpeed:Number, backspeed:Number = -1):void {
WALK_SPEED = forwardSpeed;
WALKBACK_SPEED = (backspeed != -1) ? backspeed : forwardSpeed;
}
public function setStrafeSpeed(val:Number):void {
STRAFE_SPEED = val;
}
public function setAllSpeeds(val:Number):void {
WALK_SPEED = val;
WALKBACK_SPEED = val;
STRAFE_SPEED = val;
}
public function respond_move_forward():void {
walk_state = 1;
}
public function respond_move_back():void {
walk_state = -1;
}
public function respond_move_stop():void {
walk_state = 0;
}
public function respond_strafe_left():void {
strafe_state = -1;
}
public function respond_strafe_right():void {
strafe_state = 1;
}
public function respond_strafe_stop():void {
strafe_state = 0;
}
public function update(time:Number, position:Vector3D, rotation:Vector3D, velocity:Vector3D, ground_normal:Vector3D = null):void {
var multiplier:Number;
if (ground_normal != null) { // can walk on ground
velocity.x *= friction;
velocity.y *= friction;
velocity.z *= friction;
/*
* Math.cos(this.thingBase.azimuth) * Math.cos(this.thingBase.elevation), Math.sin(this.thingBase.azimuth) * Math.cos(this.thingBase.elevation)
*/
forwardVec.x = -Math.sin(rotation.z); //Math.cos(rotation.z) * Math.cos(rotation.y); // frm rotation.z azimith
forwardVec.y = Math.cos(rotation.z);//Math.sin(rotation.z) * Math.cos(rotation.y); // frm rotation.x pitch. //* Math.cos(rotation.x)
forwardVec.z = 0;
if (forwardVec.dotProduct(ground_normal) > 0) {
multiplier = ground_normal.x * forwardVec.x + ground_normal.y * forwardVec.y + ground_normal.z * forwardVec.z;
forwardVec.x -= ground_normal.x * multiplier;
forwardVec.y -= ground_normal.y * multiplier;
forwardVec.z -= ground_normal.z * multiplier;
}
forwardVec.normalize();
if (walk_state != 0 ) {
multiplier = (walk_state != WALK_BACK) ? WALK_SPEED : -WALKBACK_SPEED;
velocity.x += forwardVec.x * multiplier;
velocity.y += forwardVec.y * multiplier;
velocity.z += forwardVec.z * multiplier;
}
rightVec = forwardVec.crossProduct(ground_normal); // TODO: inline
rightVec.normalize();
if (strafe_state != 0) {
multiplier = strafe_state != STRAFE_LEFT ? STRAFE_SPEED : -STRAFE_SPEED;
velocity.x += rightVec.x * multiplier;
velocity.y += rightVec.y * multiplier;
velocity.z += rightVec.z * multiplier;
}
}
}
}
/**
* Quake-like physics static controller or stateful component.
* @author Glidias
*/
class QPhysics
{
public static const FLAG_STICKY:int = 8;
public static const FLAG_DAMPING:int = 4;
public static const FLAG_BOUNCE:int = 2;
public static const FLAG_GRAVITY:int = 1;
// State settings
public var FLAGS:int;
public var T_BOUNCE:Number;
public var TAU_DAMP:Number;
public var N_BOUNCE:Number;
public static var GRAVITY:Number = 40;
public function QPhysics(flags:int=0) {
FLAGS = flags;
N_BOUNCE = 0.5;
T_BOUNCE = 0.9;
TAU_DAMP = .9;
}
public function applyBounce(collisions:CollisionEvent, velocity:Vector3D):void {
var coll:CollisionEvent = collisions;
applyBounceWith(velocity, coll.normal, T_BOUNCE, N_BOUNCE);
coll = coll.next;
while (coll != null) {
applyBounceWith(velocity, coll.normal, T_BOUNCE, N_BOUNCE);
coll = coll.next;
}
}
public static function applyBounceWith(velocity:Vector3D, normal:Vector3D, T_BOUNCE:Number, N_BOUNCE:Number):void {
var pushBack:Number = normal.dotProduct( velocity);
var addVel:Vector3D = normal.clone();
addVel.scaleBy(-pushBack);
velocity.add(addVel);
velocity.scaleBy(T_BOUNCE);
addVel.scaleBy(N_BOUNCE);
velocity.add(addVel);
}
public function update(time:Number, velocity:Vector3D, rotation:Vector3D = null, ang_velocity:Vector3D=null):void {
if (rotation != null) {
rotation.x += ang_velocity.x * time; // roll
rotation.y += ang_velocity.y * time; // elevation
rotation.z += ang_velocity.z * time; // azimuth
}
if ( (FLAGS & FLAG_DAMPING) != 0)
{
velocity.scaleBy(Math.exp((-time) / TAU_DAMP)); // need to check on this.
}
if ( (FLAGS & FLAG_GRAVITY) != 0)
{
velocity.z -= GRAVITY * time;
}
}
}
class CollisionEvent {
public var collision:Vector3D = new Vector3D();
public var normal:Vector3D = new Vector3D();
public var offset:Number;
public var t:Number;
public static var COLLECTOR:CollisionEvent = new CollisionEvent();
public var next:CollisionEvent;
public function CollisionEvent()
{
}
// Pooling, linked list and disposal
public static function Get(collision:Vector3D, normal:Vector3D, offset:Number, t:Number, geomtype:int):CollisionEvent {
var c:CollisionEvent = COLLECTOR || (COLLECTOR = new CollisionEvent());
COLLECTOR = COLLECTOR.next;
c.next = null;
c.collision.x = collision.x;
c.collision.y = collision.y;
c.collision.z = collision.z;
c.normal.x = normal.x;
c.normal.y = normal.y;
c.normal.z = normal.z;
c.offset = offset;
c.t = t;
// c.geomtype = geomtype;
return c;
}
public function get(collision:Vector3D, normal:Vector3D, offset:Number, t:Number, geomtype:int):CollisionEvent {
//return Get(collision, normal, offset, t, geomtype);
var c:CollisionEvent = COLLECTOR || (COLLECTOR = new CollisionEvent());
COLLECTOR = COLLECTOR.next;
c.next = null;
c.collision.x = collision.x;
c.collision.y = collision.y;
c.collision.z = collision.z;
c.normal.x = normal.x;
c.normal.y = normal.y;
c.normal.z = normal.z;
c.offset = offset;
c.t = t;
// c.geomtype = geomtype;
return c;
}
public function dispose():void {
next = COLLECTOR;
COLLECTOR = this;
}
}
//}
//Danbord.as
//ダンボーの生成とモーション
//package {
//Alternativa3D
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.materials.TextureMaterial;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.primitives.Plane;
import alternativ7.engine3d.alternativa3d;
use namespace alternativa3d;
//public
class Danbord extends Object3D {
private static var AREA_W:uint = 4000;
private static var AREA_H:uint = 4000;
private static var FPS:uint = 60;
//3D関連
private var boxHead:Box;
private var boxBody:Box;
private var boxSldL:Box;
private var boxSldR:Box;
private var boxArmL:Box;
private var boxArmR:Box;
private var boxKneL:Box;
private var boxKneR:Box;
private var boxLegL:Box;
private var boxLegR:Box;
private var planeSktL:Plane;
private var planeSktR:Plane;
private var planeSktF:Plane;
private var planeSktB:Plane;
public var danHead:Object3DContainer;
public var danBody:Object3D;
private var danSldL:Object3D;
private var danSldR:Object3D;
private var danArmL:Object3D;
private var danArmR:Object3D;
private var danKneL:Object3D;
private var danKneR:Object3D;
private var danLegL:Object3D;
private var danLegR:Object3D;
private var danSktL:Object3D;
private var danSktR:Object3D;
private var danSktF:Object3D;
private var danSktB:Object3D;
private var danUpper:Object3D;
private var motionR:Number = 0;
private var motionType:String = "stand";
private var motionTypes:Vector.<String>;
//
private var textures:Textures;
public var bodyR:Number = 0;
public var sp:Number = 0;
public var spZ:Number = 0;
public var ground:Boolean = true;
public var node:*;
public var worldBounds:Object3D;
public var kage:Kage;
private var brushes:Vector.<Mesh>;
private static var RENDERER:BSPTreeRendererI;
// Must call this method once to setup renderer!
public function setupRenderer(camera:Camera3D):void {
RENDERER = new BSPTreeRendererI(camera, new BSPXMLNode(getBSPXML(), this, brushes) );
}
private function calculateTransforms():void {
calculateInverseMatrix();
danBody.composeAndAppend(this);
danUpper.composeAndAppend(danBody);
boxBody.composeAndAppend(danUpper); boxBody.calculateInverseMatrix();
danHead.composeAndAppend(danUpper);
boxHead.composeAndAppend(danHead); boxHead.calculateInverseMatrix();
danSldL.composeAndAppend(danUpper);
boxSldL.composeAndAppend(danSldL); boxSldL.calculateInverseMatrix();
danArmL.composeAndAppend(danSldL);
boxArmL.composeAndAppend(danArmL); boxArmL.calculateInverseMatrix();
danSldR.composeAndAppend(danUpper);
boxSldR.composeAndAppend(danSldR); boxSldR.calculateInverseMatrix();
danArmR.composeAndAppend(danSldR);
boxArmR.composeAndAppend(danArmR); boxArmR.calculateInverseMatrix();
danKneL.composeAndAppend(danBody);
boxKneL.composeAndAppend(danKneL); boxKneL.calculateInverseMatrix();
danLegL.composeAndAppend(danKneL);
boxLegL.composeAndAppend(danLegL); boxLegL.calculateInverseMatrix();
danKneR.composeAndAppend(danBody);
boxKneR.composeAndAppend(danKneR); boxKneR.calculateInverseMatrix();
danLegR.composeAndAppend(danKneR);
boxLegR.composeAndAppend(danLegR); boxLegR.calculateInverseMatrix();
danSktL.composeAndAppend(danUpper);
planeSktL.composeAndAppend(danSktL); planeSktL.calculateInverseMatrix();
danSktR.composeAndAppend(danUpper);
planeSktR.composeAndAppend(danSktR); planeSktR.calculateInverseMatrix();
danSktF.composeAndAppend(danUpper);
planeSktF.composeAndAppend(danSktF); planeSktF.calculateInverseMatrix();
danSktB.composeAndAppend(danUpper);
planeSktB.composeAndAppend(danSktB); planeSktB.calculateInverseMatrix();
}
override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {
calculateTransforms();
RENDERER.draw(parentCanvas, brushes, culling);
//Debug.drawBounds(camera, parentCanvas.getChildCanvas(true, false, this), this, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
override alternativa3d function getVG(camera:Camera3D):VG {
calculateTransforms();
return RENDERER.collectVG(brushes, culling, this);
//Debug.drawBounds(camera, parentCanvas.getChildCanvas(true, false, this), this, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);
}
public function getBSPXML():XML {
boxHead._parent = danHead;
return <bsp>
<back>{_(boxHead)}</back>
<front>
<front>
<front>{_(planeSktL)}</front>
<back>
<back>{_(boxSldL)}</back>
<front>{_(boxArmL)}</front>
{planeOf(boxSldL, "bottom")}
</back>
{planeOf(planeSktL, "bottom")}
</front>
<back>
<front>
<front>{_(planeSktR)}</front>
<back>
<back>{_(boxSldR)}</back>
<front>{_(boxArmR)}</front>
{planeOf(boxSldR, "bottom")}
</back>
{planeOf(planeSktR, "bottom")}
</front>
<back>
<front>{_(planeSktF)}</front>
<back>
<front>{_(planeSktB)}</front>
<back>
<front>
<front>{_(boxLegL)}</front>
<back>{_(boxLegR)}</back>
{planeOf(boxLegR, "left")}
</front>
<back>{_(boxBody)}</back>
{planeOf(boxBody, "bottom")}
</back>
{planeOf(boxBody, "back")}
</back>
{planeOf(boxBody, "bodyFront")}
</back>
{planeOf(boxBody, "right")}
</back>
{planeOf(boxBody, "left")}
</front>
{planeOf(boxHead, "bottom")}
</bsp>
}
/**
* Gets face from mesh using material id
* @param mesh
* @param matId
* @return
*/
private static function getFaceOf(mesh:Mesh, matId:String):Face {
var face:Face = mesh.faceList;
while ( face) {
if (face.material && face.material.name === matId) {
return face;
}
face = face.next;
}
throw new Error("could not find face by matId:"+matId);
return null;
}
public function planeOf(mesh:Mesh, matId:String, animated:Boolean=true):XML {
var face:Face = getFaceOf(mesh, matId);
return <plane animated={animated}>
<nx>{face.normalX}</nx>
<ny>{face.normalY}</ny>
<nz>{face.normalZ}</nz>
<offset>{face.offset}</offset>
<brush>{brushes.indexOf(mesh)}</brush>
</plane>;
}
/**
* Gets brush index of brush for bsp xml node
* @param searchBrush
* @return The brush index xml node
*/
private function _(searchBrush:Object3D):XML {
return <brush>{brushes.indexOf(searchBrush)}</brush>;
}
//ダンボー
public function Danbord(_textures:Textures):void {
textures = _textures;
//
//パーツを埋め込むコンテナの作成(回転軸を中心以外の場所にしたいためコンテナに埋め込んでます)
// 14 dans
danHead = new Object3DContainer();
danBody = new Object3D();
danSldL = new Object3D();
danSldR = new Object3D() ;
danArmL = new Object3D();
danArmR = new Object3D();
danKneL = new Object3D();
danKneR = new Object3D();
danLegL = new Object3D();
danLegR = new Object3D() ;
danSktL = new Object3D();
danSktR = new Object3D();
danSktF = new Object3D();
danSktB = new Object3D();
danUpper = new Object3D();
/*
danBody:体
-danHead:頭
-danSldL:左肩
--danArmL:左腕
-danSldR:右肩
--danArmR:右腕
-boxKneL:左膝
--boxLegL:左足
-boxKneR:右膝
--boxLegR:右足
-danSktL:スカート右
-danSktR:スカート左
-danSktF:スカート前
-danSktB:スカート後
*/
boundMinX = 0;
boundMaxX = 0;
boundMinY = 0;
boundMaxY = 0;
boundMinZ = 0;
boundMaxZ = 0;
// 10 box
//パーツを作成
boxBody = new Box(76, 60, 102, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboBodyFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxHead = new Box(160, 102, 102, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFace, textures.txMatDanboBottom, textures.txMatDanboHeadTop);
boxSldL = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxSldR = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxArmL = new Box(106, 26, 26, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxArmR = new Box(106, 26, 26, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxKneL = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboTop);
boxKneR = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboTop);
boxLegL = new Box(30, 56, 66, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
boxLegR = new Box(30, 56, 66, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
// 4 plane
planeSktL = new Plane(60, 30, 1, 1, true, false, false, textures.txMatDanboLeft, textures.txMatDanboBottom);
planeSktR = new Plane(60, 30, 1, 1, true, false, false, textures.txMatDanboRight, textures.txMatDanboBottom);
planeSktF = new Plane(76, 30, 1, 1, true, false, false, textures.txMatDanboFront, textures.txMatDanboBottom);
planeSktB = new Plane(76, 30, 1, 1, true, false, false, textures.txMatDanboBack, textures.txMatDanboBottom);
//ステージに追加(パーツごとに親子関係になっています)
brushes = new Vector.<Mesh>(14, true);
brushes[0] = boxBody; boxBody.sorting = 0;
brushes[1] = boxHead; boxHead.sorting = 0;
brushes[2] = boxSldL; boxSldL.sorting = 0;
brushes[3] = boxSldR; boxSldR.sorting = 0;
brushes[4] = boxArmL; boxArmL.sorting = 0;
brushes[5] = boxArmR; boxArmR.sorting = 0;
brushes[6] = boxKneL; boxKneL.sorting = 0;
brushes[7] = boxKneR; boxKneR.sorting = 0;
brushes[8] = boxLegL; boxLegL.sorting = 0;
brushes[9] = boxLegR; boxLegR.sorting = 0;
brushes[10] = planeSktL; planeSktL.sorting = 0;
brushes[11] = planeSktR; planeSktR.sorting = 0;
brushes[12] = planeSktF; planeSktF.sorting = 0;
brushes[13] = planeSktB; planeSktB.sorting = 0;
//座標・角度設定
boxBody.z = 137
boxHead.z = 51;
boxArmL.x = -63;
boxArmR.x = 63;
boxLegL.z = -38;
boxLegR.z = -38;
danArmL.rotationY = -1.3;
danArmR.rotationY = 1.3;
planeSktL.rotationX = Math.PI * 0.5;
planeSktL.rotationZ = Math.PI * 0.5;
planeSktL.z = -15;
planeSktR.rotationX = Math.PI * 0.5;
planeSktR.rotationZ = Math.PI * -0.5;
planeSktR.z = -15
planeSktF.rotationX = Math.PI * 0.5;
planeSktF.rotationZ = Math.PI * 0;
planeSktF.z = -15
planeSktB.rotationX = Math.PI * 0.5;
planeSktB.rotationZ = Math.PI * 1;
planeSktB.z = -15
danBody.z = 0;
danHead.z = 188;
danSldL.x = -48;
danSldL.z = 178;
danSldR.x = 48;
danSldR.z = 178;
danKneL.x = -17;
danKneL.z = 76;
danKneR.x = 17;
danKneR.z = 76;
danSktL.x = -38;
danSktL.z = 86;
danSktL.rotationY = 0.5;
danSktR.x = 38;
danSktR.z = 86;
danSktR.rotationY = -0.5;
danSktF.y = 30;
danSktF.z = 86;
danSktF.rotationX = 0.5;
danSktB.y = -30;
danSktB.z = 86;
danSktB.rotationX = -0.5;
//
motionTypes = new Vector.<String>();
motionTypes[0] = "stand";
motionTypes[1] = "walk";
motionTypes[2] = "dash";
// motionTypes[3] = "jump";
// bodyR = Math.random() * Math.PI * 2;
//danBody.rotationZ = bodyR;
//setMotionRandom();
}
public var motionI:int = 0;
//モーションを設定
public function setMotion(_motion:String):void {
if(motionType != _motion){
motionType = _motion;
motionR = 0;
motionI = 0;
danBody.rotationX = 0;
danBody.rotationY = 0;
switch(_motion) {
case "stand":
sp = 0;
break;
case "walk":
bodyR = Math.random() * Math.PI * 2;
// danBody.rotationZ = bodyR
sp = (2 + Math.random() * 2) * scaleX;
break;
case "dash":
bodyR = Math.random() * Math.PI * 2;
// danBody.rotationZ = bodyR
sp = (6 + Math.random() * 4) * scaleX;
break;
case "jump":
spZ = (8 + Math.random() * 8) * scaleX;
ground = false;
break;
default :
break;
}
}
}
//ランダムにモーションを設定(ジャンプの有無)
public function setMotionRandom(_jump:Boolean = true):void {
if (_jump) {
setMotion(motionTypes[Math.floor(Math.random() * motionTypes.length)])
}else {
setMotion(motionTypes[Math.floor(Math.random() * (motionTypes.length - 1))])
}
}
public static const MOTION_TIMES:Object = {
stand: 0.03,
walk: 0.1,
dash: 0.23
//jump: 0.23
}
public static var STAND:DangBoardClip;
public static var WALK:DangBoardClip;
public static var DASH:DangBoardClip;
public static function precomputeAll():void {
STAND = precompute("stand");
WALK = precompute("walk");
DASH = precompute("dash");
}
private static function precompute(motionType:String):DangBoardClip {
var clip:DangBoardClip = new DangBoardClip();
var amt:Number = MOTION_TIMES[motionType];
var units:int = Math.round( 2 * Math.PI / amt );
clip.motionR = amt = 2 * Math.PI / units;
for (var i:int = 0 ; i < units; i++) {
var frame:DangBoardFrame = new DangBoardFrame();
clip.frames[clip.frameLen++] = frame;
var motionR:Number = i * amt;
switch(motionType) {
case "stand":
frame.danBody_z = 0;
frame.danHead_rotationX = Math.sin(-motionR) * 0.04;
frame.danArmL_rotationY = Math.sin(-motionR) * 0.1 + -1.3;
frame.danArmR_rotationY = Math.sin(motionR) * 0.1 + 1.3;
frame.danSldL_rotationX = 0;
frame.danSldR_rotationX = 0;
frame.danLegL_rotationX = 0;
frame.danLegR_rotationX = 0;
frame.danKneL_rotationY = 0;
frame.danKneR_rotationY = 0;
break;
case "walk":
frame.danBody_z = Math.sin(motionR * 2) * 3 + 3;
frame.danHead_rotationX = Math.cos(motionR*2) * 0.03;
frame.danArmL_rotationY = -1.3;
frame.danArmR_rotationY = 1.3;
frame.danSldL_rotationX = Math.sin(-motionR) * 0.3;
frame.danSldR_rotationX = Math.sin(motionR) * 0.3;
frame.danLegL_rotationX = Math.sin(motionR) * 0.4;
frame.danLegR_rotationX = Math.sin( -motionR) * 0.4;
frame.danKneL_rotationY = 0;
frame.danKneR_rotationY = 0;
break;
case "dash":
frame.danBody_z = Math.sin(motionR * 2) * 8 + 8;
frame.danHead_rotationX = Math.cos(motionR*2) * 0.05;
frame.danArmL_rotationY = -1.3;
frame.danArmR_rotationY = 1.3;
frame.danSldL_rotationX = Math.sin(-motionR) * 0.6;
frame.danSldR_rotationX = Math.sin(motionR) * 0.6;
frame.danLegL_rotationX = Math.sin(motionR) * 0.6;
frame.danLegR_rotationX = Math.sin( -motionR) * 0.6;
frame.danKneL_rotationY = 0;
frame.danKneR_rotationY = 0;
break;
/*
case "jump":
frame.danBody_z = 0;
frame.danHead_rotationX = Math.max(Math.min(spZ * 0.1, 0.8), -0.5);
frame.danArmL_rotationY = -Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
frame.danArmR_rotationY = Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
frame.danSldL_rotationX = 0;
frame.danSldR_rotationX = 0;
frame.danKneL_rotationY = -Math.max(Math.min(spZ * 0.2, 0), -0.2);
frame.danKneR_rotationY = Math.max(Math.min(spZ * 0.2, 0), -0.2);
frame.danLegL_rotationX = 0;
frame.danLegR_rotationX = 0;
break;
*/
default :
break;
}
}
return clip;
}
public function enterFrameEvent2() :void {
//danUpper.rotationZ += .1;
// danHead.rotationZ += .1;
// danBody.rotationZ = bodyR;
var clip:DangBoardClip;
switch(motionType) {
case "stand":
clip = STAND;
danBody.y = 0;
break;
case "walk":
clip = WALK;
break;
case "dash":
clip = DASH;
break;
case "jump":
motionR += 0.23;
danBody.z = 0;
danHead.rotationX = Math.max(Math.min(spZ * 0.1, 0.8), -0.5);
danArmL.rotationY = -Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
danArmR.rotationY = Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
danSldL.rotationX = 0;
danSldR.rotationX = 0;
danKneL.rotationY = -Math.max(Math.min(spZ * 0.2, 0), -0.2);
danKneR.rotationY = Math.max(Math.min(spZ * 0.2, 0), -0.2);
danLegL.rotationX = 0;
danLegR.rotationX = 0;
return;
default :
return;
}
if (motionI >= clip.frameLen) motionI = 0;
var frame:DangBoardFrame = clip.frames[motionI];
danBody.z = frame.danBody_z;
danHead.rotationX = frame.danHead_rotationX;
danArmL.rotationY = frame.danArmL_rotationY
danArmR.rotationY = frame.danArmR_rotationY
danSldL.rotationX = frame.danSldL_rotationX
danSldR.rotationX = frame.danSldR_rotationX
danKneL.rotationY = frame.danKneL_rotationY
danKneR.rotationY = frame.danKneR_rotationY
danLegL.rotationX = frame.danLegL_rotationX
danLegR.rotationX = frame.danLegR_rotationX
motionI++;
}
public function updateWorldBounds():void
{
worldBounds.boundMinX = boundMinX*scaleX + x;
worldBounds.boundMinY = boundMinY*scaleY + y;
worldBounds.boundMinZ = boundMinZ*scaleZ + z;
worldBounds.boundMaxX = boundMaxX*scaleX + x;
worldBounds.boundMaxY = boundMaxY*scaleY + y;
worldBounds.boundMaxZ = boundMaxZ*scaleZ + z;
}
public function checkNode():void
{
if ((x > node.boundMaxX ||
y > node.boundMaxY ||
x < node.boundMinX ||
y < node.boundMaxY)) {
//node = ;
var lastNode:* = node;
node = NodeUtils.find2DNode(x, y);
if (lastNode != node) { // todo: adding/removal instead for multiuser
//lastNode.positive.objectBoundList = null;
//lastNode.positive.objectList = null;
// node.positive.objectList = this;
//node.positive.objectBoundList = worldBounds;
}
}
}
}
//}
//Kage.as
//影
//package {
//Alternativa3D
import alternativ7.engine3d.primitives.Plane;
//public
class Kage extends Plane {
public function Kage(textures:Textures):void {
super(200, 200, 1, 1, true, false, false, textures.txMatKage, textures.txMatKage);
}
}
//}
import alternativ7.engine3d.primitives.Plane;
//public
class NodeUtils {
public static var ROOT:*;
public static var MAX_ALTITUDE:Number = Number.MAX_VALUE;
public static var MIN_ALTITUDE:Number = -Number.MAX_VALUE;
public static function find2DNode(x:Number, y:Number):* {
return find2DNode_aux(ROOT, x, y);
}
private static function find2DNode_aux(node:*, x:Number, y:Number):* {
var axis:int = node.axis;
var bounds:Object3D;
if (axis == 2) return node;
var value:int = axis != 0 ? y : x;
return value >= node.coord ? find2DNode_aux(node.positive,x,y) : find2DNode_aux(node.negative,x,y);
}
}
//package bsp
//{
/**
* Generic cross-platform BSP node construct for any engine
* @author Glenn Ko
*/
//public
class BSPNode
{
public var front:BSPNode;
public var back:BSPNode;
public var brushIndex:int = -1;
public var normalX:Number;
public var normalY:Number;
public var normalZ:Number;
public var offset:Number;
public function BSPNode()
{
}
/** Recursively counts the number of nodes in this tree (including this). */
public function size():int
{
var c:int = 1;
if (front) {
// assumption made both available
c += front.size();
c += back.size();
}
return c;
}
}
//}
//package bsp
//{
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Object3DContainer;
use namespace alternativa3d;
/**
* Custom xml parsing construct of BSPNode for Alternativa3D on animatable brush-based entities
* @author Glenn Ko
*/
//public
class BSPXMLNode extends BSPNode
{
public function BSPXMLNode(xml:XML, rootBrush:Object3D, brushContext:Vector.<Mesh>)
{
var planeXML:XML;
// Node validation
//#if debug
///*
if ( xml.hasOwnProperty("front") || xml.hasOwnProperty("back") ) {
if (!(xml.hasOwnProperty("front") && xml.hasOwnProperty("back"))) {
throw new Error("Do not have both front and back!");
}
planeXML = xml.hasOwnProperty("plane") ? xml.plane[0] : null;
if (planeXML == null) throw new Error("Could not find split plane node <plane>!");
if ( !planeXML.hasOwnProperty("nx") || !planeXML.hasOwnProperty("ny") || !planeXML.hasOwnProperty("nz") || !planeXML.hasOwnProperty("offset") ) {
throw new Error("Do not have valid plane variables!");
}
}
else if (!xml.hasOwnProperty("brush")) {
throw new Error("No leaf node brush reference found!!");
}
//*/
//#end
if (xml.hasOwnProperty("front")) {
planeXML = xml.plane[0];
normalX = Number(planeXML.nx[0]);
normalY = Number(planeXML.ny[0]);
normalZ = Number(planeXML.nz[0]);
offset = Number(planeXML.offset[0]);
brushIndex = planeXML.hasOwnProperty("brush") ? int(planeXML.brush[0]) : -1;
front = new BSPXMLNode(xml.front[0], rootBrush, brushContext);
back = new BSPXMLNode(xml.back[0], rootBrush, brushContext);
}
else {
brushIndex = int(xml.brush[0]);
}
}
}
//}
//package bsp
//{
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Canvas;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* @author Glenn Ko
*/
//public
class BSPTreeRendererI
{
public var rootNode:BSPNode;
public var camera:Camera3D;
private var stack:Vector.<BSPNode>;
private var stackResults:Vector.<BSPNode>;
/**
*
* @param camera The camera for rendering
* @param rootNode Reference bsp model node to use for rendering.
*/
public function BSPTreeRendererI(camera:Camera3D, rootNode:BSPNode )
{
this.camera = camera;
this.rootNode = rootNode;
stack = new Vector.<BSPNode>( rootNode.size(), true );
stackResults = new Vector.<BSPNode>( stack.length, true );
}
public function collectVG(brushContext:Vector.<Mesh>, culling:int, obj:Object3D):VG {
// inline vars
/*
var faceList:Face;
var f:Face;
var v:Vertex;
var w:Wrapper;
var lastF:Face;
*/
// drawing vars
var brush:Mesh;
// node vars
var node:BSPNode;
var top:int = 0;
var prev:BSPNode;
var prevResult:BSPNode;
stack[top++] = rootNode;
var vgList:VG;
var vg:VG;
for (var i:int = 0; i < brushContext.length; i++) {
brush = brushContext[i];
brush.clipping = 2
brush.culling = culling;
vg = brush.getVG(camera);
if (vg != null) {
vg.next = vgList;
vgList = vg;
}
}
return vgList;
// Darn can't inline the below...
while (top != 0) {
node = stack[top - 1];
if (!node.front) { // No more children, so spit leaf out for drawing
top--;
// todo: inline drawing of faces if rootNode is BSPNodeNiva
brush = brushContext[node.brushIndex];
brush.culling = culling;
vg = brush.getVG(camera);
if (vg != null) {
vg.next = vgList;
vgList = vg;
}
/*
brush.transformId = camera.transformId;
lastF = f = brush.prepareFaces(camera); // todo: inline this
for (f = f.processNext; f != null; f = f.processNext) {
lastF = f;
}
if (lastF!=null) {
lastF.processNext = faceList;
faceList = lastF;
}
*/
prev = node;
continue;
}
// got children to recurse into
if (!prev || prev.front === node || prev.back === node) { // if curr node is child of previous
//brush = node.brushIndex >= 0 ? brushContext[node.brushIndex] : rootBrush;
brush = brushContext[node.brushIndex];
stackResults[top - 1] = prevResult = brush.imd * node.normalX + brush.imh * node.normalY + brush.iml * node.normalZ > node.offset ? node.front : node.back;
stack[top++] = prevResult;
prev = node;
continue;
}
else if ( (prevResult = stackResults[top - 1]) === prev ) { // if curr node's initial result was the previous, recurse other side as well
stack[top++] = prevResult === node.back ? node.front : node.back;
prev = node;
continue;
}
top--;
prev = node;
}
return vgList;// faceList != null ? VG.create(obj, faceList, 0, 0, false) : null;
}
/**
* Draw brushes onto canvas through iterative bsp traversal
* @param canvas The canvas being
* @param rootBrush Usually the entity being drawn // rootBrush:Object3D,
* @param brushContext Any related brushes for the entity to be drawn/referenced
* for split-planes/leafs.
*/
public function draw(canvas:Canvas, brushContext:Vector.<Mesh>, culling:int):void {
// drawing vars
var brush:Mesh;
// node vars
var node:BSPNode;
var top:int = 0;
var prev:BSPNode;
var prevResult:BSPNode;
stack[top++] = rootNode;
while (top != 0) {
node = stack[top - 1];
if (!node.front) { // No more children, so spit leaf out for drawing
top--;
// todo: inline drawing of faces if rootNode is BSPNodeNiva
brush = brushContext[node.brushIndex];
// if (brush.cullingInCamera(camera, culling) >= 0)
brush.culling = culling;
brush.draw(camera, canvas);
prev = node;
continue;
}
// got children to recurse into
if (!prev || prev.front === node || prev.back === node) { // if curr node is child of previous
//brush = node.brushIndex >= 0 ? brushContext[node.brushIndex] : rootBrush;
brush = brushContext[node.brushIndex];
stackResults[top - 1] = prevResult = brush.imd * node.normalX + brush.imh * node.normalY + brush.iml * node.normalZ > node.offset ? node.front : node.back;
stack[top++] = prevResult;
prev = node;
continue;
}
else if ( (prevResult = stackResults[top - 1]) === prev ) { // if curr node's initial result was the previous, recurse other side as well
stack[top++] = prevResult === node.back ? node.front : node.back;
prev = node;
continue;
}
top--;
prev = node;
}
}
}
//}
class DangBoardFrame {
public var danBody_z:Number;
public var danHead_rotationX :Number;
public var danArmL_rotationY:Number;
public var danArmR_rotationY:Number;
public var danSldL_rotationX :Number;
public var danSldR_rotationX:Number;
public var danLegL_rotationX:Number;
public var danLegR_rotationX:Number;
public var danKneL_rotationY :Number;
public var danKneR_rotationY:Number;
}
class DangBoardClip {
public var motionR:Number;
public var frameLen:int = 0;
public var frames:Vector.<DangBoardFrame> = new Vector.<DangBoardFrame>();
}
// -- Other utilities/classes
import flash.utils.Dictionary;
import flash.events.KeyboardEvent;
import flash.display.Stage;
class KeyBinder {
protected var _keys : flash.utils.Dictionary;
public function KeyBinder(stage : flash.display.Stage = null) : void {
this._keys = new flash.utils.Dictionary();
if(stage != null) this.setupStageListeners(stage);
}
public function setupStageListeners(stage : flash.display.Stage) : void {
stage.addEventListener(flash.events.KeyboardEvent.KEY_UP,this.onKeyUp);
stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN,this.onKeyDown);
}
public function removeStageListeners(stage : flash.display.Stage) : void {
stage.removeEventListener(flash.events.KeyboardEvent.KEY_UP,this.onKeyUp);
stage.removeEventListener(flash.events.KeyboardEvent.KEY_DOWN,this.onKeyDown);
}
protected function onKeyDown(e : flash.events.KeyboardEvent) : void {
if(this._keys[e.keyCode]) this._keys[e.keyCode](true);
}
protected function onKeyUp(e : flash.events.KeyboardEvent) : void {
if(this._keys[e.keyCode]) this._keys[e.keyCode](false);
}
public function bindKey(key : int,method : *) : void {
this._keys[key] = method;
}
public function hasBinding(key : int) : Boolean {
return this._keys[key] != null;
}
public function clear() : void {
this._keys = new flash.utils.Dictionary();
}
}
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.RayIntersectionData;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
import alternativ7.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* Does adjustments to orbit controller for chase-cams/follow target in relation to entire environment
* and various game-types.
*
* @author Glidias
*/
class OrbitCameraMan
{
private var _followTarget:Object3D;
public var controller:OrbitCameraController;
protected var _preferedZoom:Number;
public var maxFadeAlpha:Number = .8;
public var minFadeAlpha:Number = .2;
protected var _fadeDistance:Number = 200;
public var useFadeDistance:Boolean = true;
public var visBufferLength:Number = 0;
public var preferedAlpha:Number = 1;
public var followAzimuth:Boolean = false;
public var followPitch:Boolean = false;
protected static const ORIGIN:Vector3D = new Vector3D();
protected var scene:Object3D;
//x: -45 * (Math.PI / 180)
public var cameraForward:Vector3D = new Vector3D();
public var cameraReverse:Vector3D = new Vector3D();
public var ignoreDict:Dictionary = new Dictionary();
public var collideOffset:Number = 24;
public var threshold:Number = 0.01;
public var instant:Boolean = false;
public var mouseWheelSensitivity:Number = 30;
public function OrbitCameraMan(controller:OrbitCameraController, scene:Object3D, followTarget:Object3D=null)
{
this._followTarget = followTarget || (controller._followTarget);
this.scene = scene;
this.controller = controller;
_preferedZoom = controller.getDistance();
ignoreDict[controller._followTarget] = true;
controller.minPitch = Math.PI*.5;
}
/*
_object.x += -Math.sin(_object.rotationZ) * 120;
_object.y += Math.cos(_object.rotationZ) * 120;
_object.x += forward.x * 120;
_object.y += forward.y * 120;
_object.z += forward.z * 120;
*/
public function mouseWheelHandler(e:MouseEvent):void {
var _lastLength:Number = controller.getDistance();
_lastLength -= e.delta * mouseWheelSensitivity;
if (_lastLength < controller.minDistance)
{
_lastLength = controller.minDistance
}
else if (_lastLength > controller.maxDistance)
{
_lastLength = controller.maxDistance
}
preferedZoom = _lastLength;
}
public function update():void {
controller.update();
var camera:Camera3D = controller._target;
var camLookAtTarget:Object3D = controller._followTarget;
if (followAzimuth) _followTarget.rotationZ = camera.rotationZ;
if (followPitch) _followTarget.rotationX = camera.rotationX + Math.PI * .5;
camera.calculateRay(ORIGIN, cameraForward, camera.view._width*.5, camera.view._height*.5);
cameraReverse.x = -cameraForward.x;
cameraReverse.y = -cameraForward.y;
cameraReverse.z = -cameraForward.z;
var data:RayIntersectionData = getBackRayIntersectionData( new Vector3D(camLookAtTarget.x + cameraForward.x*-threshold, camLookAtTarget.y + cameraForward.y*-threshold, camLookAtTarget.z + cameraForward.z*-threshold) );
_followTarget.visible = true;
var tarDist:Number = _preferedZoom;
if (data != null) {
//cameraReverse.normalize();
data.point = data.object.localToGlobal(data.point);
tarDist = data.point.subtract( new Vector3D(camLookAtTarget.x, camLookAtTarget.y, camLookAtTarget.z) ).length - collideOffset;
if (tarDist > _preferedZoom) tarDist = _preferedZoom;
if (tarDist < controller.minDistance) {
tarDist = controller.minDistance;
}
var limit:Number = (controller.minDistance + visBufferLength);
_followTarget.visible = tarDist >= limit;
if (useFadeDistance) {
limit = (tarDist - limit) / (_preferedZoom - _fadeDistance);
if (limit < 1) {
limit = limit < 0 ? 0 : limit;
limit = minFadeAlpha + limit * (maxFadeAlpha - minFadeAlpha);
_followTarget.alpha = limit;
}
else _followTarget.alpha = preferedAlpha;
}
controller.setDistance(tarDist, instant);
controller.setObjectPosXYZ(camera.x = camLookAtTarget.x + cameraReverse.x * tarDist,
camera.y= camLookAtTarget.y + cameraReverse.y * tarDist,
camera.z = camLookAtTarget.z + cameraReverse.z * tarDist);
}
else {
controller.setDistance(tarDist, instant);
if (useFadeDistance) _followTarget.alpha = preferedAlpha;
}
//*/
}
public function get sceneCollidable():Object3D {
return scene;
}
protected function getBackRayIntersectionData(objPosition:Vector3D):RayIntersectionData
{
return scene.intersectRay( objPosition, cameraReverse, ignoreDict);
}
public function get preferedZoom():Number
{
return _preferedZoom;
}
public function set preferedZoom(value:Number):void
{
_preferedZoom = value;
controller.setDistance(value);
}
public function get fadeDistance():Number
{
return _fadeDistance;
}
public function set fadeDistance(value:Number):void
{
_fadeDistance = value;
}
}
import alternativ7.engine3d.containers.BSPContainer;
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.containers.LODContainer;
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.View;
import flash.events.IEventDispatcher;
import flash.display.DisplayObjectContainer;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
/**
* GeoCameraController は 3D オブジェクトの周りに配置することのできるコントローラークラスです。
* 緯度・経度で配置することができます。
*
* @author narutohyper
* @author clockmaker
*
* @see http://wonderfl.net/c/fwPU
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
*/
class OrbitCameraController extends SimpleObjectController
{
//----------------------------------------------------------
//
// Static Property
//
//----------------------------------------------------------
/** 中心と方向へ移動するアクションを示す定数です。 */
public static const ACTION_FORWARD:String = "actionForward";
/** 中心と反対方向へ移動するアクションを示す定数です。 */
public static const ACTION_BACKWARD:String = "actionBackward";
/** イージングの終了判断に用いるパラメーターです。0〜0.2で設定し、0に近いほどイージングが残されます。 */
private static const ROUND_VALUE:Number = 0.1;
private var _lockRotationZ:Boolean = false;
//----------------------------------------------------------
//
// Constructor
//
//----------------------------------------------------------
/**
* 新しい GeoCameraController インスタンスを作成します。
* @param targetObject コントローラーで制御したいオブジェクトです。
* @param mouseDownEventSource マウスダウンイベントとひもづけるオブジェクトです。
* @param mouseUpEventSource マウスアップイベントとひもづけるオブジェクトです。推奨は stage です。
* @param keyEventSource キーダウン/キーマップイベントとひもづけるオブジェクトです。推奨は stage です。
* @param useKeyControl キーコントロールを使用するか指定します。
* @param useMouseWheelControl マウスホイールコントロールを使用するか指定します。
*/
public function OrbitCameraController(
targetObject:Camera3D,
followTarget:Object3D,
mouseDownEventSource:InteractiveObject,
mouseUpEventSource:InteractiveObject,
keyEventSource:InteractiveObject,
useKeyControl:Boolean = true,
useMouseWheelControl:Boolean = true
)
{
_target = targetObject;
_followTarget = followTarget;
super(mouseDownEventSource, targetObject, 0, 3, mouseSensitivity);
super.mouseSensitivity = 0;
super.unbindAll();
super.accelerate(true);
this._mouseDownEventSource = mouseDownEventSource;
this._mouseUpEventSource = mouseUpEventSource;
this._keyEventSource = keyEventSource;
_mouseDownEventSource.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
_mouseUpEventSource.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
// マウスホイール操作
if (useMouseWheelControl)
{
_mouseDownEventSource.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
}
// キーボード操作
if (useKeyControl)
{
_keyEventSource.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_keyEventSource.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
}
public function reset():void {
_angleLongitude = 0;
_lastLongitude = 0;
_angleLatitude = 0;
// _mouseMove = false;
if (useHandCursor)
Mouse.cursor = MouseCursor.AUTO;
}
//----------------------------------------------------------
//
// Property
//
//----------------------------------------------------------
//--------------------------------------
// easingSeparator
//--------------------------------------
private var _easingSeparator:Number = 1.5;
/**
* イージング時の現在の位置から最後の位置までの分割係数
* フレームレートと相談して使用
* 1〜
* 正の整数のみ。0 を指定しても 1 になります。
* 1 でイージング無し。数値が高いほど、遅延しぬるぬるします
*/
public function set easingSeparator(value:uint):void
{
if (value)
{
_easingSeparator = value;
}
else
{
_easingSeparator = 1;
}
}
/** Camera から、中心までの最大距離です。デフォルト値は 2000 です。 */
public var maxDistance:Number = 2000;
/** Camera から、中心までの最小距離です。デフォルト値は 200 です。 */
public var minDistance:Number = 200;
//--------------------------------------
// mouseSensitivityX
//--------------------------------------
private var _mouseSensitivityX:Number = -1;
/**
* マウスの X 方向の感度
*/
public function set mouseSensitivityX(value:Number):void
{
_mouseSensitivityX = value;
}
//--------------------------------------
// mouseSensitivityY
//--------------------------------------
private var _mouseSensitivityY:Number = 1;
/**
* マウスの Y 方向の感度
*/
public function set mouseSensitivityY(value:Number):void
{
_mouseSensitivityY = value;
}
public var multiplyValue:Number = 10;
//--------------------------------------
// needsRendering
//--------------------------------------
private var _needsRendering:Boolean;
/**
* レンダリングが必要かどうかを取得します。
* この値は update() メソッドが呼び出されたタイミングで更新されます。
*
* @see GeoCameraController.update
*/
public function get needsRendering():Boolean
{
return _needsRendering;
}
//--------------------------------------
// pitchSpeed
//--------------------------------------
private var _pitchSpeed:Number = 5;
/**
* 上下スピード
* 初期値は5(px)
*/
public function set pitchSpeed(value:Number):void
{
_pitchSpeed = value
}
public var useHandCursor:Boolean = true;
//--------------------------------------
// yawSpeed
//--------------------------------------
private var _yawSpeed:Number = 5;
/**
* 回転スピード
* 初期値は5(度)
*/
public function set yawSpeed(value:Number):void
{
_yawSpeed = value * Math.PI / 180;
}
/**
* Zoomスピード
* 初期値は5(px)
*/
public function set zoomSpeed(value:Number):void
{
_distanceSpeed = value;
}
public function get lockRotationZ():Boolean
{
return _lockRotationZ;
}
public function set lockRotationZ(value:Boolean):void
{
_lockRotationZ = value;
}
public function get followTarget():Object3D
{
return _followTarget;
}
public function get angleLatitude():Number
{
return _angleLatitude;
}
public function set angleLatitude(value:Number):void
{
_angleLatitude = value;
_lastLatitude = value;
}
public function get angleLongitude():Number
{
return _angleLongitude;
}
public function set angleLongitude(value:Number):void
{
_angleLongitude = value;
_lastLongitude = value;
}
public function get minAngleLatitude():Number
{
return _minAngleLatitude;
}
public function set minAngleLatitude(value:Number):void
{
_minAngleLatitude = value;
}
public function get maxAngleLatidude():Number
{
return _maxAngleLatidude;
}
public function set maxAngleLatidude(value:Number):void
{
_maxAngleLatidude = value;
}
private var _minAngleLatitude:Number = -Number.MAX_VALUE;
private var _maxAngleLatidude:Number = Number.MAX_VALUE;
private var _angleLatitude:Number = 0;
private var _angleLongitude:Number = 0;
private var _distanceSpeed:Number = 5;
private var _keyEventSource:InteractiveObject;
private var _lastLatitude:Number = 0;
private var _lastLength:Number = 700;
private var _lastLongitude:Number = _angleLongitude;
private var _lastLookAtX:Number = _lookAtX;
private var _lastLookAtY:Number = _lookAtY;
private var _lastLookAtZ:Number = _lookAtZ;
private var _length:Number = 700;
private var _lookAtX:Number = 0;
private var _lookAtY:Number = 0;
private var _lookAtZ:Number = 0;
private var _mouseDownEventSource:InteractiveObject;
private var _mouseMove:Boolean;
private var _mouseUpEventSource:InteractiveObject;
private var _mouseX:Number;
private var _mouseY:Number;
private var _oldLatitude:Number;
private var _oldLongitude:Number;
private var _pitchDown:Boolean;
private var _pitchUp:Boolean;
private var _taregetDistanceValue:Number = 0;
private var _taregetPitchValue:Number = 0;
private var _taregetYawValue:Number = 0;
public var _target:Camera3D
private var _yawLeft:Boolean;
private var _yawRight:Boolean;
private var _zoomIn:Boolean;
private var _zoomOut:Boolean;
public var _followTarget:Object3D;
//----------------------------------------------------------
//
// Function
//
//----------------------------------------------------------
/**
* 自動的に適切なキーを割り当てます。
*/
public function bindBasicKey():void
{
bindKey(Keyboard.LEFT, SimpleObjectController.ACTION_YAW_LEFT);
bindKey(Keyboard.RIGHT, SimpleObjectController.ACTION_YAW_RIGHT);
bindKey(Keyboard.DOWN, SimpleObjectController.ACTION_PITCH_DOWN);
bindKey(Keyboard.UP, SimpleObjectController.ACTION_PITCH_UP);
bindKey(Keyboard.PAGE_UP, OrbitCameraController.ACTION_BACKWARD);
bindKey(Keyboard.PAGE_DOWN, OrbitCameraController.ACTION_FORWARD);
}
/** 上方向に移動します。 */
public function pitchUp():void
{
_taregetPitchValue = _pitchSpeed * multiplyValue;
}
/** 下方向に移動します。 */
public function pitchDown():void
{
_taregetPitchValue = _pitchSpeed * -multiplyValue;
}
/** 左方向に移動します。 */
public function yawLeft():void
{
_taregetYawValue = _yawSpeed * multiplyValue;
}
/** 右方向に移動します。 */
public function yawRight():void
{
_taregetYawValue = _yawSpeed * -multiplyValue;
}
/** 中心へ向かって近づきます。 */
public function moveForeward():void
{
_taregetDistanceValue -= _distanceSpeed * multiplyValue;
}
/** 中心から遠くに離れます。 */
public function moveBackward():void
{
_taregetDistanceValue += _distanceSpeed * multiplyValue;
}
/**
* @inheritDoc
*/
override public function bindKey(keyCode:uint, action:String):void
{
switch (action)
{
case ACTION_FORWARD:
keyBindings[keyCode] = toggleForward;
break
case ACTION_BACKWARD:
keyBindings[keyCode] = toggleBackward;
break
case ACTION_YAW_LEFT:
keyBindings[keyCode] = toggleYawLeft;
break
case ACTION_YAW_RIGHT:
keyBindings[keyCode] = toggleYawRight;
break
case ACTION_PITCH_DOWN:
keyBindings[keyCode] = togglePitchDown;
break
case ACTION_PITCH_UP:
keyBindings[keyCode] = togglePitchUp;
break
}
//super.bindKey(keyCode, action)
}
/**
* 下方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function togglePitchDown(value:Boolean):void
{
_pitchDown = value
}
/**
* 上方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function togglePitchUp(value:Boolean):void
{
_pitchUp = value
}
/**
* 左方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleYawLeft(value:Boolean):void
{
_yawLeft = value;
}
/**
* 右方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleYawRight(value:Boolean):void
{
_yawRight = value;
}
/**
* 中心方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleForward(value:Boolean):void
{
_zoomIn = value;
}
/**
* 中心と反対方向に移動し続けるかを設定します。
* @param value true の場合は移動が有効になります。
*/
public function toggleBackward(value:Boolean):void
{
_zoomOut = value;
}
private var testLook:Vector3D = new Vector3D();
/**
* @inheritDoc
*/
override public function update():void
{
const RADIAN:Number = Math.PI / 180;
var oldAngleLatitude:Number = _angleLatitude;
var oldAngleLongitude:Number = _angleLongitude;
var oldLengh:Number = _lastLength;
//CameraZoom制御
if (_zoomIn)
{
_lastLength -= _distanceSpeed;
}
else if (_zoomOut)
{
_lastLength += _distanceSpeed;
}
// ズーム制御
if (_taregetDistanceValue != 0)
{
_lastLength += _taregetDistanceValue;
_taregetDistanceValue = 0;
}
if (_lastLength < minDistance)
{
_lastLength = minDistance;
}
else if (_lastLength > maxDistance)
{
_lastLength = maxDistance;
}
if (_lastLength - _length)
{
_length += (_lastLength - _length) / _easingSeparator;
}
if (Math.abs(_lastLength - _length) < ROUND_VALUE)
{
_length = _lastLength;
}
//Camera回転制御
if (_mouseMove)
{
_lastLongitude = _oldLongitude + (_mouseDownEventSource.mouseX - _mouseX) * _mouseSensitivityX
}
else if (_yawLeft)
{
_lastLongitude += _yawSpeed;
}
else if (_yawRight)
{
_lastLongitude -= _yawSpeed;
}
if (_taregetYawValue)
{
_lastLongitude += _taregetYawValue;
_taregetYawValue = 0;
}
if (_lastLongitude - _angleLongitude)
{
_angleLongitude += (_lastLongitude - _angleLongitude) / _easingSeparator;
}
if (Math.abs(_lastLatitude - _angleLatitude) < ROUND_VALUE)
{
_angleLatitude = _lastLatitude;
}
//CameraZ位置制御
if (_mouseMove)
{
_lastLatitude = _oldLatitude + (_mouseDownEventSource.mouseY - _mouseY) * _mouseSensitivityY;
}
else if (_pitchDown)
{
_lastLatitude -= _pitchSpeed;
}
else if (_pitchUp)
{
_lastLatitude += _pitchSpeed;
}
if (_taregetPitchValue)
{
_lastLatitude += _taregetPitchValue;
_taregetPitchValue = 0;
}
_lastLatitude = Math.max(-89.9, Math.min(_lastLatitude, 89.9));
if (_lastLatitude - _angleLatitude)
{
_angleLatitude += (_lastLatitude - _angleLatitude) / _easingSeparator;
}
if (Math.abs(_lastLongitude - _angleLongitude) < ROUND_VALUE)
{
_angleLongitude = _lastLongitude;
}
if (_angleLatitude < _minAngleLatitude) _angleLatitude = _minAngleLatitude;
if (_angleLatitude > _maxAngleLatidude) _angleLatitude = _maxAngleLatidude;
var vec3d:Vector3D = this.translateGeoCoords(_angleLatitude, _angleLongitude, _length);
testLook.x = _followTarget.x;
testLook.y = _followTarget.y;
testLook.z = _followTarget.z;
// testLook = _followTarget.localToGlobal(testLook);
_target.x = testLook.x + vec3d.x;
_target.y = testLook.y + vec3d.y;
_target.z = testLook.z + vec3d.z;
//lookAt制御
if (_lastLookAtX - _lookAtX)
{
_lookAtX += (_lastLookAtX - _lookAtX) / _easingSeparator;
}
if (_lastLookAtY - _lookAtY)
{
_lookAtY += (_lastLookAtY - _lookAtY) / _easingSeparator;
}
if (_lastLookAtZ - _lookAtZ)
{
_lookAtZ += (_lastLookAtZ - _lookAtZ) / _easingSeparator;
}
//super.update()
updateObjectTransform();
lookAtXYZ(_lookAtX + testLook.x, _lookAtY + testLook.y, _lookAtZ + testLook.z);
_needsRendering = oldAngleLatitude != _angleLatitude
|| oldAngleLongitude != _angleLongitude
|| oldLengh != _length;
}
/** @inheritDoc */
override public function startMouseLook():void
{
// 封印
}
/** @inheritDoc */
override public function stopMouseLook():void
{
// 封印
}
/**
* Cameraの向く方向を指定します。
* lookAtやlookAtXYZとの併用は不可。
* @param x
* @param y
* @param z
* @param immediate trueで、イージングしないで変更
*/
public function lookAtPosition(x:Number, y:Number, z:Number, immediate:Boolean = false):void
{
if (immediate)
{
_lookAtX = x
_lookAtY = y
_lookAtZ = z
}
_lastLookAtX = x
_lastLookAtY = y
_lastLookAtZ = z
}
/**
* 経度を設定します。
* @param value 0で、正面から中央方向(lookAtPosition)を見る
* @param immediate trueで、イージングしないで変更
*/
public function setLongitude(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_angleLongitude = value;
}
_lastLongitude = value;
}
/**
* 緯度を設定します。
* @param value 0で、正面から中央方向(lookAtPosition)を見る
* @param immediate trueで、イージングしないで変更
*/
public function setLatitude(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_angleLatitude = value;
}
_lastLatitude = value;
}
/**
* Cameraから、targetObjectまでの距離を設定します。
* @param value Cameraから、targetObjectまでの距離
* @param immediate trueで、イージングしないで変更
*/
public function setDistance(value:Number, immediate:Boolean = false):void
{
if (immediate)
{
_length = value;
}
_lastLength = value;
}
public function getDistance():Number {
return _length;
}
/**
* オブジェクトを使用不可にしてメモリを解放します。
*/
public function dispose():void
{
// イベントの解放
if (_mouseDownEventSource)
_mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
// マウスホイール操作
if (_mouseDownEventSource)
_mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
// キーボード操作
if (_keyEventSource)
{
_keyEventSource.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
_keyEventSource.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
// プロパティーの解放
_easingSeparator = 0;
maxDistance = 0;
minDistance = 0;
_mouseSensitivityX = 0;
_mouseSensitivityY = 0;
multiplyValue = 0;
_needsRendering = false;
_pitchSpeed = 0;
useHandCursor = false;
_yawSpeed = 0;
_angleLatitude = 0;
_angleLongitude = 0;
_distanceSpeed = 0;
_keyEventSource = null;
_lastLatitude = 0;
_lastLength = 0;
_lastLongitude = 0;
_lastLookAtX = 0;
_lastLookAtY = 0;
_lastLookAtZ = 0;
_length = 0;
_lookAtX = 0;
_lookAtY = 0;
_lookAtZ = 0;
_mouseDownEventSource = null;
_mouseMove = false;
_mouseUpEventSource = null;
_mouseX = 0;
_mouseY = 0;
_oldLatitude = 0;
_oldLongitude = 0;
_pitchDown = false;
_pitchUp = false;
_taregetDistanceValue = 0;
_taregetPitchValue = 0;
_taregetYawValue = 0;
_target = null;
_yawLeft = false;
_yawRight = false;
_zoomIn = false;
_zoomOut = false;
}
protected function mouseDownHandler(event:Event):void
{
_oldLongitude = _lastLongitude;
_oldLatitude = _lastLatitude;
_mouseX = _mouseDownEventSource.mouseX;
_mouseY = _mouseDownEventSource.mouseY;
_mouseMove = true;
if (useHandCursor)
Mouse.cursor = MouseCursor.HAND;
}
protected function mouseUpHandler(event:Event):void
{
if (!_mouseMove) return;
if (useHandCursor)
Mouse.cursor = MouseCursor.AUTO;
_mouseMove = false;
}
private function keyDownHandler(event:KeyboardEvent):void
{
for (var key:String in keyBindings)
{
if (String(event.keyCode) == key)
{
keyBindings[key](true)
}
}
}
private function keyUpHandler(event:KeyboardEvent = null):void
{
for (var key:String in keyBindings)
{
keyBindings[key](false)
}
}
private function mouseWheelHandler(event:MouseEvent):void
{
_lastLength -= event.delta * 20;
if (_lastLength < minDistance)
{
_lastLength = minDistance
}
else if (_lastLength > maxDistance)
{
_lastLength = maxDistance
}
}
/**
* 経度と緯度から位置を算出します。
* @param latitude 緯度
* @param longitude 経度
* @param radius 半径
* @return 位置情報
*/
private function translateGeoCoords(latitude:Number, longitude:Number, radius:Number):Vector3D
{
const latitudeDegreeOffset:Number = 90;
const longitudeDegreeOffset:Number = -90;
latitude = Math.PI * latitude / 180;
longitude = Math.PI * longitude / 180;
latitude -= (latitudeDegreeOffset * (Math.PI / 180));
longitude -= (longitudeDegreeOffset * (Math.PI / 180));
var x:Number = radius * Math.sin(latitude) * Math.cos(longitude);
var y:Number = radius * Math.cos(latitude);
var z:Number = radius * Math.sin(latitude) * Math.sin(longitude);
return new Vector3D(x, z, y);
}
}
//}
class CrossHair extends Sprite {
public function CrossHair() {
graphics.lineStyle(1, 0xDDEE44, 1);
graphics.moveTo(0, -2);
graphics.lineTo(0, -6);
graphics.moveTo(0, 2);
graphics.lineTo(0, 6);
graphics.moveTo(2, 0);
graphics.lineTo(6, 0);
graphics.moveTo(-2, 0);
graphics.lineTo( -6, 0);
filters = [new DropShadowFilter(1,45,0,1,0,0,1,1,false,false, false)];
}
}
// Object3D node to hold multiple children to draw with either distance-sort/conflict
class ChildrenList extends Object3D {
public static var collector:Object3D;
// We assume this is called after numChildren was 1 and decremented to 0 after removeChild()
public function destroy():void {
childrenList = null;
next = collector;
collector = this;
}
// We asume this is called when a child is created and forms up a list of his own...
public static function create(child:Object3D):ChildrenList {
var instance:ChildrenList = (collector as ChildrenList) || new ChildrenList();
collector = instance.next;
instance.next = null;
instance.numChildren = 1;
instance.childrenList = child;
return instance;
}
public static var DS_SORT:DistanceSortContainer = new DistanceSortContainer();
public static var CONFICT:ConflictContainer = new ConflictContainer();
public var childrenList:Object3D;
public var numChildren:int = 0;
public function addChild(child:Object3D):void { //note: this doesnt validate if child is already added!
numChildren++;
child.next = childrenList;
childrenList = child;
}
public function removeChild(child:Object3D):void {
var nextChild:Object3D;
for (var c:Object3D = childrenList; c != null; c = nextChild) {
nextChild = c.next;
if (c === child) {
c.next = nextChild;
numChildren--;
return;
}
}
throw new Error("FAILED to remove child from list!");
}
}
/**
NodeResidency
ROOT
nodes:Array=[];
object:Object3D;
objectBounds:Object3D;
update() {
// update object bounds
// check nodes that collided against..if >1 node, CropObject
}
*/