forked from: ArrowLobMeshSet2.as - Looping Fountain of Arrows example
forked from ArrowLobMeshSet2.as - Looping Fountain of Arrows example (diff: 1)
ActionScript3 source code
/**
* Copyright raa ( http://wonderfl.net/user/raa )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/WI6h
*/
// forked from Glidias's ArrowLobMeshSet2.as - Looping Fountain of Arrows example
// forked from Glidias's ArrowLobMeshSet2.as - Fountain of Arrows example
package
{
//import alternativa.a3d.objects.ArrowLobMeshSet2;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Resource;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.primitives.Plane;
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import alternativa.engine3d.resources.BitmapTextureResource;
import flash.display.MovieClip;
import flash.events.KeyboardEvent;
import flash.geom.Vector3D;
import alternativa.engine3d.primitives.*;
/**
*
* Current code allows batching up to ~60 arrows per draw call!
* THat's a lot of arrows for a single draw call!
*
* All you need to do is launch an arrow with a start-position and
* end position accordingly.
*
Of course, you can use your own custom meshes for the arrows and all..
*
* @author Glidias
*/
[SWF(frameRate="60", backgroundColor="#FFFFFF")]
public class RainOfArrowsProofOfConcept extends MovieClip
{
private var _template3D:MainView3D;
private var engine:Engine;
private var ticker:FrameTickProvider;
public function RainOfArrowsProofOfConcept()
{
engine = new Engine();
Wonderfl.disable_capture();
ReflectUtil.registerComponents([ Object3D]);
addChild( _template3D = new MainView3D() );
_template3D.onViewCreate.add(onReady3D);
}
private function onReady3D():void
{
//SpawnerBundle.context3D = _template3D.stage3D.context3D;
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
//engine.addSystem( new RenderingSystem(_template3D.scene), 1 );
var spectatorPerson:SimpleObjectController =new SimpleObjectController(
stage,
_template3D.camera,
155,
4);
engine.addSystem( spectatorPerson, 2) ;
setupCustomEnvironment();
// Setup floor and some basic parameters
var plane:Plane = new Plane(4000, 4000, 1, 1, false, false, null, new FillMaterial(0x111111));
plane.z = -1;
_template3D.scene.addChild(plane);
_template3D.camera.z = 66;
spectatorPerson.setObjectPosXYZ(1100, 1400, 133);
spectatorPerson.lookAt(new Vector3D(0, 0, 0));
uploadResources(_template3D.scene.getResources(true));
_template3D.render();
// Let's go
ticker = new FrameTickProvider(stage);
ticker.add(tick);
ticker.start();
}
// Do whatever custom environment setups here!
private function setupCustomEnvironment():void
{
// example visual scene
var planeFloor:Mesh = new Plane(2048, 2048, 1, 1, false, false, null, new FillMaterial(0xBBBBBB, 1) );
_template3D.scene.addChild(planeFloor);
//arenaSpawner.addCrossStage(SpawnerBundle.context3D);
//SpawnerBundle.uploadResources(planeFloor.getResources(true, null));
var box:Box = new Box(26, 3, 3, 1, 1, 1, false, null);
arrows = new ArrowLobMeshSet2(box.geometry, new FillMaterial(0xFF0000, 1));
//arrows.z = 1333;
arrows.setGravity(266 * 3);
arrows.idleArrowLifetime = 4; // idle time before arrow vanishes
arrows.maxTimeInterval = 1; // for periodic cleanups
startPosition = new Vector3D(0,0,0);
endPosition = new Vector3D();
launchProjectileLooping();
// arrows.setPermanentArrows();
_template3D.scene.addChild ( arrows);
//throw new Error(planeFloor.geometry.getVertexBuffer(VertexAttributes.POSITION));
// collision scene (can be something else)
// (Optional) Enforced ground plane collision
//game.gameStates.thirdPerson.addInstance( new GroundPlaneCollisionSystem(0, true) ).withPriority(SystemPriorities.resolveCollisions);
}
private function launchProjectileLooping():void {
TweenLite.delayedCall(1 / 30, launchProjectileLoop);
}
private function launchProjectileLoop():void {
for (var i:int = 0 ; i < 50; i++) {
TweenLite.delayedCall(i*.01, launchProjectile);
}
TweenLite.delayedCall(1 / 30, launchProjectileLoop);
}
private function launchProjectile():void
{
var randAng:Number = Math.random() * Math.PI;
var d:Number = Math.random() * 555 + 1410;
endPosition.x = Math.cos(randAng)*d;
endPosition.y = Math.sin(randAng) * d;
endPosition.z = 0 ;
arrows.launchNewProjectile( startPosition, endPosition, 777);
}
private function onKeyDown(e:KeyboardEvent):void
{
var kc:uint = e.keyCode;
if (kc === Keyboard.F7) {
_scrnie=_template3D.takeScreenshot(screenieMethod2);
}
}
private function screenieMethod2():Boolean
{
stage.addEventListener(MouseEvent.CLICK, removeScreenie);
return false;
}
private var _scrnie:Bitmap;
private function removeScreenie(e:Event=null):void {
if (_scrnie == null) return;
stage.removeEventListener(MouseEvent.CLICK, removeScreenie);
_scrnie.parent.removeChild(_scrnie);
_scrnie = null;
}
public function uploadResources(vec:Vector.<Resource>):void {
var i:int = vec.length;
while (--i > -1) {
vec[i].upload(_template3D.stage3D.context3D);
}
}
private var timePassed:Number = 0;
private var arrows:ArrowLobMeshSet2;
private var startPosition:Vector3D;
private var endPosition:Vector3D;
private function tick(time:Number):void
{
timePassed += time;
arrows.update(time);
engine.update(time);
_template3D.render();
}
}
}
//package alternativa.engine3d.utils
//{
import alternativa.engine3d.core.VertexStream;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.Geometry;
import flash.utils.ByteArray;
import flash.utils.Endian;
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* ...
* @author Glenn Ko
*/
//public
class GeometryUtil
{
public static const ATTRIBUTE:uint = 20;
private static function collectAttributes(geom:Geometry, attributesDict:Vector.<int>, attributesLengths:Vector.<int>):void {
for each (var stream:VertexStream in geom._vertexStreams) {
var prev:int = -1;
var attributes:Array = stream.attributes;
for each (var attr:int in attributes) {
attributesLengths[attr]++;
if (attr == prev) continue;
attributesDict[attr]++;
prev = attr;
}
}
}
private static function appendGeometry(geometry:Geometry, geom:Geometry, constantsPerMesh:int):void {
var stream:VertexStream;
var i:int, j:int;
var length:uint = geom._vertexStreams.length;
var numVertices:int = geom._numVertices;
for (i = 0; i < length; i++) {
stream = geom._vertexStreams[i];
var attributes:Array = geometry._vertexStreams[i].attributes;
var attribtuesLength:int = attributes.length;
var destStream:VertexStream = geometry._vertexStreams[i];
var newOffset:int = destStream.data.length;
destStream.data.position = newOffset;
stream.data.position = 0;
var stride:int = stream.attributes.length*4;
var destStride:int = destStream.attributes.length*4;
for (j = 0; j < numVertices; j++) {
var prev:int = -1;
for (var k:int = 0; k < attribtuesLength; k++) {
var attr:int = attributes[k];
if (attr == ATTRIBUTE) {
destStream.data.writeFloat(0);
continue;
}
if (attr != prev) {
stream.data.position = geom._attributesOffsets[attr]*4 + stride*j;
destStream.data.position = newOffset + geometry._attributesOffsets[attr]*4 + destStride*j;
}
destStream.data.writeFloat(stream.data.readFloat());
prev = attr;
}
}
}
geometry._numVertices += geom._numVertices;
}
private static function appendGeom(geometry:Geometry, geom:Geometry, constantsPerMesh:int):void {
var vertexOffset:uint;
var i:int, j:int;
vertexOffset = geometry._numVertices;
appendGeometry(geometry, geom, constantsPerMesh);
// Copy indexes
var indexEnd:uint = geom.numTriangles*3 + 0;
for (j = 0; j < indexEnd; j++) {
geometry._indices.push(geom._indices[j] + vertexOffset);
}
}
private static function getJointGeometry(geom:Geometry, constantsPerMesh:int):Geometry {
var geometry:Geometry = new Geometry(0);
var numAttributes:int = 32;
var attributesDict:Vector.<int> = new Vector.<int>(numAttributes, true);
var attributesLengths:Vector.<int> = new Vector.<int>(numAttributes, true);
collectAttributes(geom, attributesDict, attributesLengths);
var attributes:Array = [];
var i:int;
for (i = 0; i < numAttributes; i++) {
if (attributesDict[i] > 0) {
attributesLengths[i] = attributesLengths[i]/attributesDict[i];
}
}
for (i = 0; i < numAttributes; i++) {
if (Number(attributesDict[i]) == 1) {
for (var j:int = 0; j < attributesLengths[i]; j++) {
attributes.push(i);
}
}
}
attributes.push(ATTRIBUTE);
geometry.addVertexStream(attributes);
//if (root is Mesh) appendMesh(root as Mesh);
//collectMeshes(root);
appendGeom(geometry, geom, constantsPerMesh);
return geometry;
}
// Borrowed from MeshSetClonesContainer (based off MeshSet).
/**
* Allows you to convert a single geometry instance into a single geometry cloned-chain with joint ID indices for draw-batching!
* @param geometry The geometry sample to duplicate
* @param cap The total amount to duplicate! Give a value higher than 1! Usually the predicted maximum amount of instances per draw batch!)
* @param constantsPerMesh The amount of constants being used per mesh clone, used to offset the joint indices accordingly to match the correct starting constant register index.
*/
public static function createDuplicateGeometry(geometry:Geometry, cap:int, constantsPerMesh:int):Geometry {
geometry = getJointGeometry(geometry, constantsPerMesh);
//transformProcedure = calculateTransformProcedure(cap);
//deltaTransformProcedure = calculateDeltaTransformProcedure(cap);
var bytes:ByteArray;
//throw new Error(geometry.getAttributeValues(VertexAttributes.POSITION))
// get samples
var protoJointIndices:Vector.<Number> = geometry.getAttributeValues(ATTRIBUTE);
var protoNumVertices:int = geometry.numVertices;
//_numVertices = protoNumVertices;
var protoByteArrayStreams:Vector.<ByteArray> = new Vector.<ByteArray>();
var len:int = geometry._vertexStreams.length;
// copy all geometry bytearray data samples for all vertex streams
for (var i:int = 0; i < len; i++) {
protoByteArrayStreams[i] = bytes = new ByteArray();
bytes.endian = Endian.LITTLE_ENDIAN;
for (var u:int = 0; u < cap; u++) {
bytes.writeBytes( geometry._vertexStreams[i].data );
}
}
// paste geometry data for all the vertex streams
for (i = 0; i < len; i++) {
bytes =protoByteArrayStreams[i];
for (u = 0; u < cap; u++) {
var data:ByteArray = geometry._vertexStreams[i].data;
data.position = data.length;
data.writeBytes(bytes, data.length);
}
}
// set number of vertices to match new vertex data size
geometry._numVertices = protoNumVertices * cap;
var indices:Vector.<uint> = geometry.indices;
// duplicate indices with offsets
len = indices.length;
for (i = 1; i < cap; i++) {
var indexOffset:int = i * protoNumVertices;
for (u = 0; u < len; u++) {
indices.push(indexOffset+ indices[u]);
}
}
geometry.indices = indices;
// paste joint attribute values with offsets
var jointIndices:Vector.<Number> = geometry.getAttributeValues(ATTRIBUTE);
len = protoJointIndices.length;
var duplicateMultiplier:Number = constantsPerMesh;
var totalLen:int = jointIndices.length;
for (i = len; i < totalLen; i += len) {
for (u = i; u < i+len; u++) {
jointIndices[u] += duplicateMultiplier;
}
duplicateMultiplier+= constantsPerMesh;
}
geometry.setAttributeValues(ATTRIBUTE, jointIndices);
return geometry;
}
}
//}
//package alternativa.a3d.objects
//{
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.Geometry;
// import alternativa.engine3d.utils.GeometryUtil;
import alternativa.engine3d.alternativa3d;
import flash.display3D.Context3DVertexBufferFormat;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
use namespace alternativa3d;
/**
* Yes, ~120 arrows/projectiles per draw call! Let thy arrows blot the sun!!
* We determine arrow position and orientation, from velocity, offset and time...within GPU itself.
* @author Glenn Ko
*/
//public
class ArrowLobMeshSet2 extends Mesh
{
private var batchAmount:int;
private var sampleNumTris:int;
private static var BATCH_AMOUNT:int = 60;
private static var _transformProcedures:Dictionary = new Dictionary();
private static var _deltaTransformProcedures:Dictionary = new Dictionary();
public var toUpload:Vector.<Number> = new Vector.<Number>();
public var total:int = 0;
private static const ATTRIBUTE:int = GeometryUtil.ATTRIBUTE;
private var constantsPerMesh:int;
public var maxTimeInterval:Number = 4;
public var gravity:Number = 266;
public var idleArrowLifetime:Number = 4;
public function adjustArrowLifeSettings(idleTimeBeforeCleanup:Number = 4, registryFlushTime:Number = 4 ):void {
idleArrowLifetime = idleTimeBeforeCleanup;
maxTimeInterval = 4;
}
public function setPermanentArrows():void {
idleArrowLifetime = Number.MAX_VALUE;
maxTimeInterval = Number.MAX_VALUE;
}
// private var freeSlots:Vector.<int> = new Vector.<int>();
public function setGravity(val:Number):void {
gravity = val;
}
public function ArrowLobMeshSet2(arrowGeometry:Geometry, material:Material)
{
super();
batchAmount = BATCH_AMOUNT;
sampleNumTris = arrowGeometry.numTriangles;
constantsPerMesh = 2;
geometry = GeometryUtil.createDuplicateGeometry(arrowGeometry, batchAmount, constantsPerMesh);
mySurface = addSurface( material, 0, sampleNumTris*total );
transformProcedure = calculateTransformProcedure(batchAmount);
// deltaTransformProcedure = calculateDeltaTransformProcedure(batchAmount);
// setPermanentArrows();
boundBox = null;
}
private var _remainingTotal:int;
private var _toUploadAmount:int;
private var timeStamp:Number = 0;
alternativa3d function setVertexConstantsFromVector(drawUnit:DrawUnit, firstRegister:int, data:Vector.<Number>, numRegisters:int, fromParticleIndex:int):void {
if (uint(firstRegister) > (128 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
var vertexConstants:Vector.<Number> = drawUnit.vertexConstants;
fromParticleIndex *= (constantsPerMesh<<2);
var offset:int = firstRegister << 2;
if (firstRegister + numRegisters > drawUnit.vertexConstantsRegistersCount) {
drawUnit.vertexConstantsRegistersCount = firstRegister + numRegisters;
vertexConstants.length = drawUnit.vertexConstantsRegistersCount << 2;
}
for (var i:int = fromParticleIndex, len:int = fromParticleIndex + (numRegisters << 2); i < len; i++) {
vertexConstants[offset] = data[i];
offset++;
}
}
private var particleDrawCount:int = 0;
alternativa3d override function setTransformConstants(drawUnit:DrawUnit, surface:Surface, vertexShader:Linker, camera:Camera3D):void {
drawUnit.setVertexBufferAt(vertexShader.getVariableIndex("joint"), geometry.getVertexBuffer(ATTRIBUTE), geometry._attributesOffsets[ATTRIBUTE], Context3DVertexBufferFormat.FLOAT_1);
drawUnit.setVertexConstantsFromNumbers( vertexShader.getVariableIndex("cVars"), -.5 * gravity, idleArrowLifetime, gravity, timeStamp);
drawUnit.setVertexConstantsFromNumbers( vertexShader.getVariableIndex("cUp"), 0, 0, 1, 0);
//drawUnit.setVertexConstantsFromVector(0, toUploadSpriteData, toUploadNumSprites*NUM_REGISTERS_PER_SPR );
setVertexConstantsFromVector(drawUnit, 0, toUpload, _toUploadAmount*constantsPerMesh, particleDrawCount );
//throw new Error(e.message + ":"+_toUploadAmount + ", " + toUploadData.length);
//if (triCount != surface.numTriangles) {
surface.numTriangles = _toUploadAmount * sampleNumTris;
//}
particleDrawCount += _toUploadAmount;
}
public function update(time:Number):void {
timeStamp += time;
if (timeStamp >= maxTimeInterval) {
cleanup();
}
}
private function cleanup():void
{
var capCheck:int = total - 1;
for (var i:int = 0; i < total; i++) {
var base:int = i * 8;
var timeOfLaunch:Number = toUpload[base + 3];
var t:Number = timeStamp - timeOfLaunch;
if ( t - toUpload[base+7] < idleArrowLifetime ) { // still within visible state
toUpload[base+3] = timeOfLaunch - timeStamp;
}
else { // remove
if (i != capCheck ) { // do pop back
var count:int = capCheck * 8;
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
toUpload[base++] = toUpload[count++];
i--;
}
total--;
capCheck--;
}
}
timeStamp = 0;
}
public function reset():void {
timeStamp = 0;
toUpload.length = 0;
total = 0;
}
public function launchNewProjectile(startPosition:Vector3D, endPosition:Vector3D, speed:Number=1044 ):void {
//launchProjectileAtIndex(total, startPosition, endPosition);
displace.x = endPosition.x - startPosition.x;
displace.y = endPosition.y - startPosition.y;
displace.z = endPosition.z - startPosition.z;
var totalTime:Number = displace.length / speed;
var base:int = total * 8;
toUpload[base++] = startPosition.x;
toUpload[base++] =startPosition.y;
toUpload[base++] = startPosition.z;
toUpload[base++ ] = timeStamp;
toUpload[base++] = endPosition.x;
toUpload[base++] =endPosition.y;
toUpload[base++] =endPosition.z;
toUpload[base++ ] = totalTime;
total++;
}
public function launchNewProjectileWithTimeSpan(startPosition:Vector3D, endPosition:Vector3D, time:Number=1 ):void {
//launchProjectileAtIndex(total, startPosition, endPosition);
var base:int = total * 8;
toUpload[base++] = startPosition.x;
toUpload[base++] =startPosition.y;
toUpload[base++] = startPosition.z;
toUpload[base++ ] = timeStamp;
toUpload[base++] = endPosition.x;
toUpload[base++] =endPosition.y;
toUpload[base++] =endPosition.z;
toUpload[base++ ] = time;
total++;
}
private var displace:Vector3D = new Vector3D();
private var mySurface:Surface;
override alternativa3d function collectDraws(camera:Camera3D, lights:Vector.<Light3D>, lightsLength:int, useShadow:Boolean):void {
_remainingTotal = total;
particleDrawCount = 0;
if (_remainingTotal < batchAmount) {
mySurface = _surfaces[0];
_toUploadAmount = _remainingTotal;
// mySurface.numTriangles = _remainingTotal * sampleNumTris;
mySurface.material.collectDraws(camera, mySurface, geometry, lights, lightsLength, useShadow, -1);
}
else {
var count:int = 0;
//if (tempUploadData == null) tempUploadData = new Vector.<Number>(batchAmount*constantsPerMesh*4, true);
//toUploadData = tempUploadData;
while (_remainingTotal > 0) {
if (count >= _surfaces.length) {
_surfaces[count] = _surfaces[0].clone();
_surfacesLength = count;
}
mySurface = _surfaces[count];
//fillTempUploadData( total - _remainingTotal, _remainingTotal);
//mySurface.numTriangles = _remainingTotal*sampleNumTris;
_toUploadAmount = _remainingTotal;
if (_toUploadAmount > batchAmount) _toUploadAmount = batchAmount;
mySurface.material.collectDraws(camera, mySurface, geometry, lights, lightsLength, useShadow, -1);
_remainingTotal -= batchAmount;
count++;
}
}
}
// -------------------
///*
private function calculateTransformProcedure(numMeshes:int):Procedure {
var res:Procedure = _transformProcedures[numMeshes];
if (res != null) return res;
res = _transformProcedures[numMeshes] = new Procedure(null, "ArrowLobMeshSet2TransformProcedure");
res.compileFromArray(["#a0=joint", "#c1=cVars", "#c2=cUp",
// dummy declarations (can remove once done..)
//"mov t0, c1",
//"mov t1, c2",
// "mov t1, a0",
// "mov t1, i0",
// ----
"mov t1, c[a0.x]", // start position t1
"add t0.x, c2.z, a0.x",
"mov t0, c[t0.x]", // end position t0
"mov t2.w, t0.w", // we need this later, t2.w will be the totalTime of arrow travel
// get velocity t0.xyz
"sub t0.xyz, t0.xyz, t1.xyz",
"div t0.xyz, t0.xyz, t0.www",
"mul t0.w, c1.x, t0.w",
"sub t0.z, t0.z, t0.w",
// get time t0.w
"sub t0.w, c1.w, t1.w", // now, save actual time by subtracting timeStamp - launchTime.
"sub t4.w, t0.w, t2.w", // t - totalTime // t4 is reserved for visMultiplier
"slt t4.w, t4.w, c1.y", //visMmultiplier 1/0
"min t0.w, t0.w, t2.w", // cap time for rendering of arrow (so arrows stick to the ground..)
//t = min( t, totalTime)
//"mov t0.w, c2.x",
"mul t1.w, t0.x, t0.w, ", // save out velocity.x*t offset
"add t1.x, t1.x, t1.w", // and add it to the launch position to get actual x arrow origin position
"mul t1.w, t0.y, t0.w, ", // save out velocity.y*t offset
"add t1.y, t1.y, t1.w", // and add it to the launch position to get actual y arrow origin position
"mul t1.w, c1.x, t0.w", // save out -.5*GRAVITY*t*t + velocity.z*t gravitational offset over time
"mul t1.w, t1.w, t0.w",
"add t1.z, t1.z, t1.w", // and LHS operand done, ADD it in!!
"mul t1.w, t0.z, t0.w",
"add t1.z, t1.z, t1.w", // and RHS operand done, subtract it out to get actual z arrow origin position
///* // if considering orientation
"mul t0.w, c1.z, t0.w", // multiply gravitaitonal offset over time: GRAVITY*t
"sub t0.z, t0.z, t0.w", // subtract this from the z alt component velocity to get final unnormalized velocity vector
"nrm t0.xyz, t0.xyz", // normalize the vector to get forward vector of arrow...along X direction! (2d east)
"mov t3, t0", // no choice, t0 is used, t1 is to hold position, t2 to hold axes! need to maintain reference to foward vector to allow crs later
"mul t2.xyz, i0.xxx, t0.xyz", // extend out position along x offset of vertex
"add t1.xyz, t1.xyz, t2.xyz",
"crs t0.xyz, t0.xyz, c2.xyz", // cross product forward vector with up to get right...along Y direction (2d north)!
"mul t2.xyz, i0.yyy, t0.xyz", // extend out position along y offset of vertex
"add t1.xyz, t1.xyz, t2.xyz",
"crs t0.xyz, t0.xyz, t3.xyz", // cross product right vector with forward vector to get actual UP...along Z direction
"mul t2.xyz, i0.zzz, t0.xyz", // extend out position along z offset of vertex
"add t1.xyz, t1.xyz, t2.xyz",
"mov t1.w, c1.z", // w property of 1 needed?
// */
// if not considering orientation
//"add t1.xyz, t1.xyz, i0.xyz",
"mul t1.xyz, t1.xyz, t4.www",
"mov t1.w, i0.w",
"mov o0, t1"]);
res.assignConstantsArray(numMeshes * constantsPerMesh);
return res;
}
//*/
/*
private function calculateDeltaTransformProcedure(numMeshes:int):Procedure {
var res:Procedure = _deltaTransformProcedures[numMeshes];
if (res != null) return res;
res = _deltaTransformProcedures[numMeshes] = new Procedure(null, "ArrowLobMeshSet2DeltaTransformProcedure");
res.compileFromArray(["#a0=joint", "#c1=cVars", "sub t0, a0.x, c1.x", "m33 o0.xyz, i0, c[t0.x]", "mov o0.w, i0.w"]);
return res;
}
*/
}
//}
/**
* Bare bones main view3d class
* @author Glenn Ko
*/
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.View;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Stage;
//import systems.rendering.IRenderable;
//import ash.signals.Signal0;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
class MainView3D extends Sprite {
private var _stage:Stage;
public var onViewCreate:Signal0 = new Signal0();
public var stage3D:Stage3D
public var camera:Camera3D
public var scene:Object3D
public var directionalLight:DirectionalLight;
public var ambientLight:AmbientLight;
public function MainView3D() {
addEventListener(Event.ADDED_TO_STAGE, init);
}
protected function init(e:Event = null):void
{
_stage = stage;
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
//Stage3Dを用意
stage3D = stage.stage3Ds[0];
//Context3Dの生成、呼び出し、初期化
stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
stage3D.requestContext3D();
}
private function onContextCreate(e:Event):void {
stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
//View3D(表示エリア)の作成
var view:View = new View(stage.stageWidth, stage.stageHeight);
view.antiAlias = 4
addChild(view);
//Scene(コンテナ)の作成
scene = new Object3D();
//Camera(カメラ)の作成
camera = new Camera3D(1, 100000);
camera.view = view;
scene.addChild(camera)
// ..camera.diagram
addChild(camera.diagram);
view.hideLogo();
//Lightを追加
ambientLight = new AmbientLight(0xFFFFFF);
ambientLight.intensity = 0.5;
scene.addChild(ambientLight);
//Lightを追加
directionalLight = new DirectionalLight(0xFFFFFF);
//手前右上から中央へ向けた指向性light
directionalLight.x = 0;
directionalLight.y = -100;
directionalLight.z = -100;
directionalLight.lookAt(0, 0, 0);
scene.addChild(directionalLight);
//directionalLight.visible = false;
onViewCreate.dispatch();
stage.addEventListener(Event.RESIZE, onStageResize);
}
private function onStageResize(e:Event):void
{
camera.view.width = stage.stageWidth;
camera.view.height = stage.stageHeight;
}
public function takeScreenshot( method:Function=null) : Bitmap //width:int, height:int,
{
var view:View = camera.view;
view.renderToBitmap = true;
camera.render(stage3D);
var canvas:BitmapData = view.canvas.clone();
// var bitmapData:BitmapData = view.canvas.clone();
view.renderToBitmap = false;
// view.width = oldWidth;
// view.height = oldHeight;
var child:Bitmap = new Bitmap(canvas);
stage.addChildAt( child,0 );
// take screenshot here
if (method!= null && method() ) {
if (child.parent) child.parent.removeChild(child);
}
return child;
}
/*
public function startRendering():void {
addEventListener(Event.ENTER_FRAME, onRenderTick);
}
private function onRenderTick(e:Event):void
{
render();
}
public function stopRendering():void {
removeEventListener(Event.ENTER_FRAME, onRenderTick);
}
*/
/* INTERFACE systems.rendering.IRenderable */
public function render():void
{
camera.render(stage3D);
}
public function get viewBackgroundColor():uint
{
return camera.view.backgroundColor;
}
public function set viewBackgroundColor(value:uint):void
{
camera.view.backgroundColor = value;
}
}
//}//package {
//import Engine;
//import Node;
//import NodeList;
//import System;
//}
//package {
//import ash.signals.Signal2;
import flash.utils.Dictionary;
import flash.utils.getQualifiedClassName;
/*public*/ class Entity
{
private static var nameCount : int = 0;
private var _name : String;
public var componentAdded : Signal2;
public var componentRemoved : Signal2;
public var nameChanged : Signal2;
public var previous : Entity;
public var next : Entity;
public var components : Dictionary;
public function Entity( name : String = "" )
{
componentAdded = new Signal2( Entity, Class );
componentRemoved = new Signal2( Entity, Class );
nameChanged = new Signal2( Entity, String );
components = new Dictionary();
if( name )
{
_name = name;
}
else
{
_name = "_entity" + (++nameCount);
}
}
public function get name() : String
{
return _name;
}
public function set name( value : String ) : void
{
if( _name != value )
{
var previous : String = _name;
_name = value;
nameChanged.dispatch( this, previous );
}
}
public function add( component : Object, componentClass : Class = null ) : Entity
{
if ( !componentClass )
{
componentClass = Class( component.constructor );
}
if ( components[ componentClass ] )
{
remove( componentClass );
}
components[ componentClass ] = component;
componentAdded.dispatch( this, componentClass );
return this;
}
public function remove( componentClass : Class ) : *
{
var component : * = components[ componentClass ];
if ( component )
{
delete components[ componentClass ];
componentRemoved.dispatch( this, componentClass );
return component;
}
return null;
}
public function get( componentClass : Class ) : *
{
return components[ componentClass ];
}
public function getAll() : Array
{
var componentArray : Array = new Array();
for each( var component : * in components )
{
componentArray.push( component );
}
return componentArray;
}
public function has( componentClass : Class ) : Boolean
{
return components[ componentClass ] != null;
}
}
//}
//package {
/*public*/ class System
{
public var previous : System;
public var next : System;
public var priority : int = 0;
public function addToEngine( engine : Engine ) : void
{
}
public function removeFromEngine( engine : Engine ) : void
{
}
public function update( time : Number ) : void
{
}
}
//}
//package {
/*public*/ class Node
{
public var entity : Entity;
public var previous : *;
public var next : *;
}
//}
//}
class ListenerNodePool
{
private var tail : ListenerNode;
private var cacheTail : ListenerNode;
public function get():ListenerNode
{
if( tail )
{
var node : ListenerNode = tail;
tail = tail.previous;
node.previous = null;
return node;
}
else
{
return new ListenerNode();
}
}
public function dispose( node : ListenerNode ):void
{
node.listener = null;
node.once = false;
node.next = null;
node.previous = tail;
tail = node;
}
public function cache( node : ListenerNode ) : void
{
node.listener = null;
node.previous = cacheTail;
cacheTail = node;
}
public function releaseCache() : void
{
while( cacheTail )
{
var node : ListenerNode = cacheTail;
cacheTail = node.previous;
node.next = null;
node.previous = tail;
tail = node;
}
}
}
class ListenerNode
{
public var previous : ListenerNode;
public var next : ListenerNode;
public var listener : Function;
public var once : Boolean;
}
/*public*/ class ListIteratingSystem extends System
{
protected var nodeList : NodeList;
protected var nodeClass : Class;
protected var nodeUpdateFunction : Function;
protected var nodeAddedFunction : Function;
protected var nodeRemovedFunction : Function;
public function ListIteratingSystem( nodeClass : Class, nodeUpdateFunction : Function, nodeAddedFunction : Function = null, nodeRemovedFunction : Function = null )
{
this.nodeClass = nodeClass;
this.nodeUpdateFunction = nodeUpdateFunction;
this.nodeAddedFunction = nodeAddedFunction;
this.nodeRemovedFunction = nodeRemovedFunction;
}
override public function addToEngine( engine : Engine ) : void
{
nodeList = engine.getNodeList( nodeClass );
if( nodeAddedFunction != null )
{
for( var node : Node = nodeList.head; node; node = node.next )
{
nodeAddedFunction( node );
}
nodeList.nodeAdded.add( nodeAddedFunction );
}
if( nodeRemovedFunction != null )
{
nodeList.nodeRemoved.add( nodeRemovedFunction );
}
}
override public function removeFromEngine( engine : Engine ) : void
{
if( nodeAddedFunction != null )
{
nodeList.nodeAdded.remove( nodeAddedFunction );
}
if( nodeRemovedFunction != null )
{
nodeList.nodeRemoved.remove( nodeRemovedFunction );
}
nodeList = null;
}
override public function update( time : Number ) : void
{
for( var node : Node = nodeList.head; node; node = node.next )
{
nodeUpdateFunction( node, time );
}
}
}
//}
//package {
//import ash.signals.Signal1;
/*public*/ class NodeList
{
public var head : *;
public var tail : *;
public var nodeAdded : Signal1;
public var nodeRemoved : Signal1;
public function NodeList()
{
nodeAdded = new Signal1( Node );
nodeRemoved = new Signal1( Node );
}
public function add( node : Node ) : void
{
if( ! head )
{
head = tail = node;
node.next = node.previous = null;
}
else
{
tail.next = node;
node.previous = tail;
node.next = null;
tail = node;
}
nodeAdded.dispatch( node );
}
public function remove( node : Node ) : void
{
if ( head == node)
{
head = head.next;
}
if ( tail == node)
{
tail = tail.previous;
}
if (node.previous)
{
node.previous.next = node.next;
}
if (node.next)
{
node.next.previous = node.previous;
}
nodeRemoved.dispatch( node );
}
public function removeAll() : void
{
while( head )
{
var node : Node = head;
head = node.next;
node.previous = null;
node.next = null;
nodeRemoved.dispatch( node );
}
tail = null;
}
public function get empty() : Boolean
{
return head == null;
}
public function swap( node1 : Node, node2 : Node ) : void
{
if( node1.previous == node2 )
{
node1.previous = node2.previous;
node2.previous = node1;
node2.next = node1.next;
node1.next = node2;
}
else if( node2.previous == node1 )
{
node2.previous = node1.previous;
node1.previous = node2;
node1.next = node2.next;
node2.next = node1;
}
else
{
var temp : Node = node1.previous;
node1.previous = node2.previous;
node2.previous = temp;
temp = node1.next;
node1.next = node2.next;
node2.next = temp;
}
if( head == node1 )
{
head = node2;
}
else if( head == node2 )
{
head = node1;
}
if( tail == node1 )
{
tail = node2;
}
else if( tail == node2 )
{
tail = node1;
}
if( node1.previous )
{
node1.previous.next = node1;
}
if( node2.previous )
{
node2.previous.next = node2;
}
if( node1.next )
{
node1.next.previous = node1;
}
if( node2.next )
{
node2.next.previous = node2;
}
}
public function insertionSort( sortFunction : Function ) : void
{
if( head == tail )
{
return;
}
var remains : Node = head.next;
for( var node : Node = remains; node; node = remains )
{
remains = node.next;
for( var other : Node = node.previous; other; other = other.previous )
{
if( sortFunction( node, other ) >= 0 )
{
if( node != other.next )
{
if ( tail == node)
{
tail = node.previous;
}
node.previous.next = node.next;
if (node.next)
{
node.next.previous = node.previous;
}
node.next = other.next;
node.previous = other;
node.next.previous = node;
other.next = node;
}
break;
}
}
if( !other )
{
if ( tail == node)
{
tail = node.previous;
}
node.previous.next = node.next;
if (node.next)
{
node.next.previous = node.previous;
}
node.next = head;
head.previous = node;
node.previous = null;
head = node;
}
}
}
public function mergeSort( sortFunction : Function ) : void
{
if( head == tail )
{
return;
}
var lists : Vector.<Node> = new Vector.<Node>;
var start : Node = head;
var end : Node;
while( start )
{
end = start;
while( end.next && sortFunction( end, end.next ) <= 0 )
{
end = end.next;
}
var next : Node = end.next;
start.previous = end.next = null;
lists.push( start );
start = next;
}
while( lists.length > 1 )
{
lists.push( merge( lists.shift(), lists.shift(), sortFunction ) );
}
tail = head = lists[0];
while( tail.next )
{
tail = tail.next;
}
}
private function merge( head1 : Node, head2 : Node, sortFunction : Function ) : Node
{
var node : Node;
var head : Node;
if( sortFunction( head1, head2 ) <= 0 )
{
head = node = head1;
head1 = head1.next;
}
else
{
head = node = head2;
head2 = head2.next;
}
while( head1 && head2 )
{
if( sortFunction( head1, head2 ) <= 0 )
{
node.next = head1;
head1.previous = node;
node = head1;
head1 = head1.next;
}
else
{
node.next = head2;
head2.previous = node;
node = head2;
head2 = head2.next;
}
}
if( head1 )
{
node.next = head1;
head1.previous = node;
}
else
{
node.next = head2;
head2.previous = node;
}
return head;
}
}
//}
//package {
import flash.utils.Dictionary;
/*public*/ class SignalBase
{
public var head : ListenerNode;
public var tail : ListenerNode;
private var nodes : Dictionary;
private var listenerNodePool : ListenerNodePool;
private var toAddHead : ListenerNode;
private var toAddTail : ListenerNode;
private var dispatching : Boolean;
private var _numListeners : int = 0;
public function SignalBase()
{
nodes = new Dictionary( true );
listenerNodePool = new ListenerNodePool();
}
protected function startDispatch() : void
{
dispatching = true;
}
protected function endDispatch() : void
{
dispatching = false;
if( toAddHead )
{
if( !head )
{
head = toAddHead;
tail = toAddTail;
}
else
{
tail.next = toAddHead;
toAddHead.previous = tail;
tail = toAddTail;
}
toAddHead = null;
toAddTail = null;
}
listenerNodePool.releaseCache();
}
public function get numListeners() : int
{
return _numListeners;
}
public function add( listener : Function ) : void
{
if( nodes[ listener ] )
{
return;
}
var node : ListenerNode = listenerNodePool.get();
node.listener = listener;
nodes[ listener ] = node;
addNode( node );
}
public function addOnce( listener : Function ) : void
{
if( nodes[ listener ] )
{
return;
}
var node : ListenerNode = listenerNodePool.get();
node.listener = listener;
node.once = true;
nodes[ listener ] = node;
addNode( node );
}
protected function addNode( node : ListenerNode ) : void
{
if( dispatching )
{
if( !toAddHead )
{
toAddHead = toAddTail = node;
}
else
{
toAddTail.next = node;
node.previous = toAddTail;
toAddTail = node;
}
}
else
{
if ( !head )
{
head = tail = node;
}
else
{
tail.next = node;
node.previous = tail;
tail = node;
}
}
_numListeners++;
}
public function remove( listener : Function ) : void
{
var node : ListenerNode = nodes[ listener ];
if ( node )
{
if ( head == node)
{
head = head.next;
}
if ( tail == node)
{
tail = tail.previous;
}
if ( toAddHead == node)
{
toAddHead = toAddHead.next;
}
if ( toAddTail == node)
{
toAddTail = toAddTail.previous;
}
if (node.previous)
{
node.previous.next = node.next;
}
if (node.next)
{
node.next.previous = node.previous;
}
delete nodes[ listener ];
if( dispatching )
{
listenerNodePool.cache( node );
}
else
{
listenerNodePool.dispose( node );
}
_numListeners--;
}
}
public function removeAll() : void
{
while( head )
{
var node : ListenerNode = head;
head = head.next;
delete nodes[ node.listener ];
listenerNodePool.dispose( node );
node.previous = null;
node.next = null;
}
tail = null;
toAddHead = null;
toAddTail = null;
_numListeners = 0;
}
}
//}
//package {
//import ash.signals.Signal0;
import flash.utils.Dictionary;
/*public*/ class Engine
{
private var entityNames : Dictionary;
private var entityList : EntityList;
private var systemList : SystemList;
private var families : Dictionary;
public var updating : Boolean;
public var updateComplete : Signal0;
public var familyClass : Class = ComponentMatchingFamily;
public function Engine()
{
entityList = new EntityList();
entityNames = new Dictionary();
systemList = new SystemList();
families = new Dictionary();
updateComplete = new Signal0();
}
public function addEntity( entity : Entity ) : void
{
if( entityNames[ entity.name ] )
{
throw new Error( "The entity name " + entity.name + " is already in use by another entity." );
}
entityList.add( entity );
entityNames[ entity.name ] = entity;
entity.componentAdded.add( componentAdded );
entity.componentRemoved.add( componentRemoved );
entity.nameChanged.add( entityNameChanged );
for each( var family : IFamily in families )
{
family.newEntity( entity );
}
}
public function removeEntity( entity : Entity ) : void
{
entity.componentAdded.remove( componentAdded );
entity.componentRemoved.remove( componentRemoved );
entity.nameChanged.remove( entityNameChanged );
for each( var family : IFamily in families )
{
family.removeEntity( entity );
}
delete entityNames[ entity.name ];
entityList.remove( entity );
}
private function entityNameChanged( entity : Entity, oldName : String ) : void
{
if( entityNames[ oldName ] == entity )
{
delete entityNames[ oldName ];
entityNames[ entity.name ] = entity;
}
}
public function getEntityByName( name : String ) : Entity
{
return entityNames[ name ];
}
public function removeAllEntities() : void
{
while( entityList.head )
{
removeEntity( entityList.head );
}
}
public function get entities() : Vector.<Entity>
{
var entities : Vector.<Entity> = new Vector.<Entity>();
for( var entity : Entity = entityList.head; entity; entity = entity.next )
{
entities.push( entity );
}
return entities;
}
private function componentAdded( entity : Entity, componentClass : Class ) : void
{
for each( var family : IFamily in families )
{
family.componentAddedToEntity( entity, componentClass );
}
}
private function componentRemoved( entity : Entity, componentClass : Class ) : void
{
for each( var family : IFamily in families )
{
family.componentRemovedFromEntity( entity, componentClass );
}
}
public function getNodeList( nodeClass : Class ) : NodeList
{
if( families[nodeClass] )
{
return IFamily( families[nodeClass] ).nodeList;
}
var family : IFamily = new familyClass( nodeClass, this );
families[nodeClass] = family;
for( var entity : Entity = entityList.head; entity; entity = entity.next )
{
family.newEntity( entity );
}
return family.nodeList;
}
public function releaseNodeList( nodeClass : Class ) : void
{
if( families[nodeClass] )
{
families[nodeClass].cleanUp();
}
delete families[nodeClass];
}
public function addSystem( system : System, priority : int ) : void
{
system.priority = priority;
system.addToEngine( this );
systemList.add( system );
}
public function getSystem( type : Class ) : System
{
return systemList.get( type );
}
public function get systems() : Vector.<System>
{
var systems : Vector.<System> = new Vector.<System>();
for( var system : System = systemList.head; system; system = system.next )
{
systems.push( system );
}
return systems;
}
public function removeSystem( system : System ) : void
{
systemList.remove( system );
system.removeFromEngine( this );
}
public function removeAllSystems() : void
{
while( systemList.head )
{
removeSystem( systemList.head );
}
}
public function update( time : Number ) : void
{
updating = true;
for( var system : System = systemList.head; system; system = system.next )
{
system.update( time );
}
updating = false;
updateComplete.dispatch();
}
}
//package {
/*public*/ class Signal0 extends SignalBase
{
public function Signal0()
{
}
public function dispatch() : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener();
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
class ReflectUtil {
private static var CACHE:Dictionary = new Dictionary();
public static var HACHE_COMPONENTS:Dictionary = new Dictionary();
public static function registerComponents(arrClasses:Array):void {
var i:int = arrClasses.length;
while (--i > -1) {
HACHE_COMPONENTS[getQualifiedClassName(arrClasses[i])] = arrClasses[i];
}
}
public static function getFields(nodeClass:Class, arrOfClasses:Array):Dictionary {
if (CACHE[nodeClass]) return CACHE[nodeClass];
var variables : XMLList = describeType( nodeClass ).factory.variable;
var components:Dictionary = new Dictionary();
var i:int = arrOfClasses.length;
var hash:Object = { };
while (--i > -1) {
hash[ getQualifiedClassName(arrOfClasses[i]) ] = arrOfClasses[i];
}
for each ( var atom:XML in variables )
{
if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
{
var componentClass : Class = hash[ atom.@type.toString()] || HACHE_COMPONENTS[atom.@type.toString()];
if (componentClass == null) throw new Error("Could not find component class>" + atom.@type + ", for "+nodeClass);
components[componentClass] = atom.@name.toString();
}
}
CACHE[nodeClass] = components;
return components;
}
}
//package {
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
/*public*/ class ComponentMatchingFamily implements IFamily
{
private var nodes : NodeList;
private var entities : Dictionary;
private var nodeClass : Class;
private var components : Dictionary;
private var nodePool : NodePool;
private var engine : Engine;
public function ComponentMatchingFamily( nodeClass : Class, engine : Engine )
{
this.nodeClass = nodeClass;
this.engine = engine;
init();
}
private function init() : void
{
nodes = new NodeList();
entities = new Dictionary();
components = new Dictionary();
nodePool = new NodePool( nodeClass, components );
nodePool.dispose( nodePool.get() );
try {
var dict:Dictionary = nodeClass["_getFields"]();
}
catch (e:Error) {
var variables : XMLList = describeType( nodeClass ).factory.variable;
for each ( var atom:XML in variables )
{
if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
{
var componentClass : Class = ReflectUtil.HACHE_COMPONENTS[ atom.@type.toString()];
if (componentClass == null) throw new Error("Component class is undefined! "+atom.@type);
components[componentClass] = atom.@name.toString();
}
}
dict = new Dictionary();
}
for each(var key:* in dict) {
components[key] = dict[key];
}
}
public function get nodeList() : NodeList
{
return nodes;
}
public function newEntity( entity : Entity ) : void
{
addIfMatch( entity );
}
public function componentAddedToEntity( entity : Entity, componentClass : Class ) : void
{
addIfMatch( entity );
}
public function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void
{
if( components[componentClass] )
{
removeIfMatch( entity );
}
}
public function removeEntity( entity : Entity ) : void
{
removeIfMatch( entity );
}
private function addIfMatch( entity : Entity ) : void
{
if( !entities[entity] )
{
var componentClass : *;
for ( componentClass in components )
{
if ( !entity.has( componentClass ) )
{
return;
}
}
var node : Node = nodePool.get();
node.entity = entity;
for ( componentClass in components )
{
node[components[componentClass]] = entity.get( componentClass );
}
entities[entity] = node;
nodes.add( node );
}
}
private function removeIfMatch( entity : Entity ) : void
{
if( entities[entity] )
{
var node : Node = entities[entity];
delete entities[entity];
nodes.remove( node );
if( engine.updating )
{
nodePool.cache( node );
engine.updateComplete.add( releaseNodePoolCache );
}
else
{
nodePool.dispose( node );
}
}
}
private function releaseNodePoolCache() : void
{
engine.updateComplete.remove( releaseNodePoolCache );
nodePool.releaseCache();
}
public function cleanUp() : void
{
for( var node : Node = nodes.head; node; node = node.next )
{
delete entities[node.entity];
}
nodes.removeAll();
}
}
//}
//package {
/*public*/ class Signal2 extends SignalBase
{
private var type1 : Class;
private var type2 : Class;
public function Signal2( type1 : Class, type2 : Class )
{
this.type1 = type1;
this.type2 = type2;
}
public function dispatch( object1 : *, object2 : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object1, object2 );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
/*
* Based on ideas used in Robert Penner's AS3-signals - https://github.com/robertpenner/as3-signals
*/
class Signal3 extends SignalBase
{
private var type1 : Class;
private var type2 : Class;
private var type3 : Class;
public function Signal3( type1 : Class, type2 : Class, type3 : Class )
{
this.type1 = type1;
this.type2 = type2;
this.type3 = type3;
}
public function dispatch( object1 : *, object2 : *, object3 : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object1, object2, object3 );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
class SignalAny extends SignalBase
{
protected var classes : Array;
public function SignalAny( ...classes )
{
this.classes = classes;
}
public function dispatch( ...objects ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener.apply( null, objects );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//package {
/*public*/ class Signal1 extends SignalBase
{
private var type : Class;
public function Signal1( type : Class )
{
this.type = type;
}
public function dispatch( object : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
interface IFamily {
function get nodeList() : NodeList;
function newEntity( entity : Entity ) : void;
function removeEntity( entity : Entity ) : void;
function componentAddedToEntity( entity : Entity, componentClass : Class ) : void;
function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void;
function cleanUp() : void;
}
interface ITickProvider
{
function get playing() : Boolean;
function add( listener : Function ) : void;
function remove( listener : Function ) : void;
function start() : void;
function stop() : void;
}
class EntityList
{
public var head : Entity;
public var tail : Entity;
public function add( entity : Entity ) : void
{
if( ! head )
{
head = tail = entity;
entity.next = entity.previous = null;
}
else
{
tail.next = entity;
entity.previous = tail;
entity.next = null;
tail = entity;
}
}
public function remove( entity : Entity ) : void
{
if ( head == entity)
{
head = head.next;
}
if ( tail == entity)
{
tail = tail.previous;
}
if (entity.previous)
{
entity.previous.next = entity.next;
}
if (entity.next)
{
entity.next.previous = entity.previous;
}
// N.B. Don't set node.next and node.previous to null because that will break the list iteration if node is the current node in the iteration.
}
public function removeAll() : void
{
while( head )
{
var entity : Entity = head;
head = head.next;
entity.previous = null;
entity.next = null;
}
tail = null;
}
}
class SystemList
{
public var head : System;
public var tail : System;
public function add( system : System ) : void
{
if( ! head )
{
head = tail = system;
system.next = system.previous = null;
}
else
{
for( var node : System = tail; node; node = node.previous )
{
if( node.priority <= system.priority )
{
break;
}
}
if( node == tail )
{
tail.next = system;
system.previous = tail;
system.next = null;
tail = system;
}
else if( !node )
{
system.next = head;
system.previous = null;
head.previous = system;
head = system;
}
else
{
system.next = node.next;
system.previous = node;
node.next.previous = system;
node.next = system;
}
}
}
public function remove( system : System ) : void
{
if ( head == system)
{
head = head.next;
}
if ( tail == system)
{
tail = tail.previous;
}
if (system.previous)
{
system.previous.next = system.next;
}
if (system.next)
{
system.next.previous = system.previous;
}
// N.B. Don't set system.next and system.previous to null because that will break the list iteration if node is the current node in the iteration.
}
public function removeAll() : void
{
while( head )
{
var system : System = head;
head = head.next;
system.previous = null;
system.next = null;
}
tail = null;
}
public function get( type : Class ) : System
{
for( var system : System = head; system; system = system.next )
{
if ( system is type )
{
return system;
}
}
return null;
}
}
//package
//{
import flash.utils.Dictionary;
class NodePool
{
private var tail : Node;
private var nodeClass : Class;
private var cacheTail : Node;
private var components : Dictionary;
/**
* Creates a pool for the given node class.
*/
public function NodePool( nodeClass : Class, components : Dictionary )
{
this.nodeClass = nodeClass;
this.components = components;
}
/**
* Fetches a node from the pool.
*/
internal function get() : Node
{
if ( tail )
{
var node : Node = tail;
tail = tail.previous;
node.previous = null;
return node;
}
else
{
return new nodeClass();
}
}
/**
* Adds a node to the pool.
*/
internal function dispose( node : Node ) : void
{
for each( var componentName : String in components )
{
node[ componentName ] = null;
}
node.entity = null;
node.next = null;
node.previous = tail;
tail = node;
}
/**
* Adds a node to the cache
*/
internal function cache( node : Node ) : void
{
node.previous = cacheTail;
cacheTail = node;
}
/**
* Releases all nodes from the cache into the pool
*/
internal function releaseCache() : void
{
while( cacheTail )
{
var node : Node = cacheTail;
cacheTail = node.previous;
dispose( node );
}
}
}
//}
//package {
//import ash.signals.Signal1;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.utils.getTimer;
/*public*/ class FrameTickProvider extends Signal1 implements ITickProvider
{
private var displayObject : DisplayObject;
private var previousTime : Number;
private var maximumFrameTime : Number;
private var isPlaying : Boolean = false;
public var timeAdjustment : Number = 1;
public function FrameTickProvider( displayObject : DisplayObject, maximumFrameTime : Number = Number.MAX_VALUE )
{
super( Number );
this.displayObject = displayObject;
this.maximumFrameTime = maximumFrameTime;
}
public function start() : void
{
previousTime = getTimer();
displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
isPlaying = true;
}
public function stop() : void
{
isPlaying = false;
displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
}
private function dispatchTick( event : Event ) : void
{
var temp : Number = previousTime;
previousTime = getTimer();
var frameTime : Number = ( previousTime - temp ) / 1000;
if( frameTime > maximumFrameTime )
{
frameTime = maximumFrameTime;
}
dispatch( frameTime * timeAdjustment );
}
public function get playing() : Boolean
{
return isPlaying;
}
}
//}
//package ash.tick
//{
import flash.display.DisplayObject;
import flash.events.Event;
//public
class FixedTickProvider extends Signal1 implements ITickProvider
{
private var displayObject : DisplayObject;
private var frameTime : Number;
private var isPlaying : Boolean = false;
public var timeAdjustment : Number = 1;
public function FixedTickProvider( displayObject : DisplayObject, frameTime : Number )
{
super( Number );
this.displayObject = displayObject;
this.frameTime = frameTime;
}
public function start() : void
{
displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
isPlaying = true;
}
public function stop() : void
{
isPlaying = false;
displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
}
private function dispatchTick( event : Event ) : void
{
dispatch( frameTime * timeAdjustment );
}
public function get playing() : Boolean
{
return isPlaying;
}
}
//}
// -- Ash framework ends here
/**
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
* If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
* You may add additional accurate notices of copyright ownership.
*
* It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/
* */
//package alternativa.a3d.controller {
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
//import ash.core.Engine;
//import ash.core.System;
import flash.display.InteractiveObject;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
import flash.utils.getTimer;
/**
* Controller for <code>Object3D</code>. Allow to handle the object with a keyboard and mouse.
*
* @see alternativa.engine3d.core.Object3D //
*/
//public
class SimpleObjectController extends System {
/**
* Name of action for binding "forward" action.
*/
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
/**
* Name of action for binding "back" action.
*/
public static const ACTION_BACK:String = "ACTION_BACK";
/**
* Name of action for binding "left" action.
*/
public static const ACTION_LEFT:String = "ACTION_LEFT";
/**
* Name of action for binding "right" action.
*/
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
/**
* Name of action for binding "up" action.
*/
public static const ACTION_UP:String = "ACTION_UP";
/**
* Name of action for binding "down" action.
*/
public static const ACTION_DOWN:String = "ACTION_DOWN";
/**
* Name of action for binding "pitch up" action.
*/
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
/**
* Name of action for binding "pitch down" action.
*/
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
/**
* Name of action for binding "yaw left" action.
*/
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
/**
* Name of action for binding "yaw right" action.
*/
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
/**
* Name of action for binding "accelerate" action.
*/
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
/**
* ИName of action for binding "mouse look" action.
*/
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
/**
* Speed.
*/
public var speed:Number;
/**
* Speed multiplier for acceleration mode.
*/
public var speedMultiplier:Number;
/**
* Mouse sensitivity.
*/
public var mouseSensitivity:Number;
/**
* The maximal slope in the vertical plane in radians.
*/
public var maxPitch:Number = 1e+22;
/**
* The minimal slope in the vertical plane in radians.
*/
public var minPitch:Number = -1e+22;
private var eventSource:InteractiveObject;
private var _object:Object3D;
private var _up:Boolean;
private var _down:Boolean;
private var _forward:Boolean;
private var _back:Boolean;
private var _left:Boolean;
private var _right:Boolean;
private var _accelerate:Boolean;
private var displacement:Vector3D = new Vector3D();
private var mousePoint:Point = new Point();
private var mouseLook:Boolean;
private var objectTransform:Vector.<Vector3D>;
/**
* The hash for binding names of action and functions. The functions should be at a form are follows:
* <code>
* function(value:Boolean):void
* </code>
*
* <code>value</code> argument defines if bound key pressed down or up.
*/
private var actionBindings:Object = {};
/**
* The hash for binding key codes and action names.
*/
protected var keyBindings:Object = {};
/**
* Creates a SimpleObjectController object.
* @param eventSource Source for event listening.
* @param speed Speed of movement.
* @param mouseSensitivity Mouse sensitivity, i.e. number of degrees per each pixel of mouse movement.
*/
public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
this.eventSource = eventSource;
this.object = object;
this.speed = speed;
this.speedMultiplier = speedMultiplier;
this.mouseSensitivity = mouseSensitivity;
actionBindings[ACTION_FORWARD] = moveForward;
actionBindings[ACTION_BACK] = moveBack;
actionBindings[ACTION_LEFT] = moveLeft;
actionBindings[ACTION_RIGHT] = moveRight;
actionBindings[ACTION_UP] = moveUp;
actionBindings[ACTION_DOWN] = moveDown;
actionBindings[ACTION_ACCELERATE] = accelerate;
setDefaultBindings();
}
override public function addToEngine(engine:Engine):void {
enable();
updateObjectTransform();
}
override public function removeFromEngine(engine:Engine):void {
disable();
}
/**
* Enables the controler.
*/
public function enable():void {
eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
/**
* Disables the controller.
*/
public function disable():void {
eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stopMouseLook();
}
private function onMouseDown(e:MouseEvent):void {
startMouseLook();
}
private function onMouseUp(e:MouseEvent):void {
stopMouseLook();
}
/**
* Enables mouse look mode.
*/
public function startMouseLook():void {
mousePoint.x = eventSource.mouseX;
mousePoint.y = eventSource.mouseY;
mouseLook = true;
}
/**
* Disables mouse look mode.
*/
public function stopMouseLook():void {
mouseLook = false;
}
private function onKey(e:KeyboardEvent):void {
var method:Function = keyBindings[e.keyCode];
if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
}
/**
* Target of handling.
*/
public function get object():Object3D {
return _object;
}
/**
* @private
*/
public function set object(value:Object3D):void {
_object = value;
updateObjectTransform();
}
/**
* Refreshes controller state from state of handled object. Should be called if object was moved without the controller (i.e. <code>object.x = 100;</code>).
*/
public function updateObjectTransform():void {
if (_object != null) objectTransform = _object.matrix.decompose();
}
/**
* Calculates and sets new object position.
*/
override public function update(frameTime:Number):void {
if (_object == null) return;
if (frameTime > 0.1) frameTime = 0.1;
var moved:Boolean = false;
if (mouseLook) {
var dx:Number = eventSource.mouseX - mousePoint.x;
var dy:Number = eventSource.mouseY - mousePoint.y;
mousePoint.x = eventSource.mouseX;
mousePoint.y = eventSource.mouseY;
var v:Vector3D = objectTransform[1];
v.x -= dy*Math.PI/180*mouseSensitivity;
if (v.x > maxPitch) v.x = maxPitch;
if (v.x < minPitch) v.x = minPitch;
v.z -= dx*Math.PI/180*mouseSensitivity;
moved = true;
}
displacement.x = _right ? 1 : (_left ? -1 : 0);
displacement.y = _forward ? 1 : (_back ? -1 : 0);
displacement.z = _up ? 1 : (_down ? -1 : 0);
if (displacement.lengthSquared > 0) {
if (_object is Camera3D) {
var tmp:Number = displacement.z;
displacement.z = displacement.y;
displacement.y = -tmp;
}
deltaTransformVector(displacement);
if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
else displacement.scaleBy(speed*frameTime/displacement.length);
(objectTransform[0] as Vector3D).incrementBy(displacement);
moved = true;
}
if (moved) {
var m:Matrix3D = new Matrix3D();
m.recompose(objectTransform);
_object.matrix = m;
}
}
/**
* Sets object at given position.
* @param pos The position.
*/
public function setObjectPos(pos:Vector3D):void {
if (_object != null) {
var v:Vector3D = objectTransform[0];
v.x = pos.x;
v.y = pos.y;
v.z = pos.z;
}
}
/**
* Sets object at given position.
* @param x X.
* @param y Y.
* @param z Z.
*/
public function setObjectPosXYZ(x:Number, y:Number, z:Number):void {
if (_object != null) {
var v:Vector3D = objectTransform[0];
v.x = x;
v.y = y;
v.z = z;
}
}
/**
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
* @param point Point to look at.
*/
public function lookAt(point:Vector3D):void {
lookAtXYZ(point.x, point.y, point.z);
}
/**
* Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
* @param x X.
* @param y Y.
* @param z Z.
*/
public function lookAtXYZ(x:Number, y:Number, z:Number):void {
if (_object == null) return;
var v:Vector3D = objectTransform[0];
var dx:Number = x - v.x;
var dy:Number = y - v.y;
var dz:Number = z - v.z;
v = objectTransform[1];
v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
if (_object is Camera3D) v.x -= 0.5*Math.PI;
v.y = 0;
v.z = -Math.atan2(dx, dy);
var m:Matrix3D = _object.matrix;
m.recompose(objectTransform);
_object.matrix = m;
}
private var _vin:Vector.<Number> = new Vector.<Number>(3);
private var _vout:Vector.<Number> = new Vector.<Number>(3);
private function deltaTransformVector(v:Vector3D):void {
_vin[0] = v.x;
_vin[1] = v.y;
_vin[2] = v.z;
_object.matrix.transformVectors(_vin, _vout);
var c:Vector3D = objectTransform[0];
v.x = _vout[0] - c.x;
v.y = _vout[1] - c.y;
v.z = _vout[2] - c.z;
}
/**
* Starts and stops move forward according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveForward(value:Boolean):void {
_forward = value;
}
/**
* Starts and stops move backward according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveBack(value:Boolean):void {
_back = value;
}
/**
* Starts and stops move to left according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveLeft(value:Boolean):void {
_left = value;
}
/**
* Starts and stops move to right according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveRight(value:Boolean):void {
_right = value;
}
/**
* Starts and stops move up according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveUp(value:Boolean):void {
_up = value;
}
/**
* Starts and stops move down according to <code>true</code> or <code>false</code> was passed.
* @param value Action switcher.
*/
public function moveDown(value:Boolean):void {
_down = value;
}
/**
* Switches acceleration mode.
* @param value <code>true</code> turns acceleration on, <code>false</code> turns off.
*/
public function accelerate(value:Boolean):void {
_accelerate = value;
}
/**
* Binds key and action. Only one action can be assigned to one key.
* @param keyCode Key code.
* @param action Action name.
* @see #unbindKey()
* @see #unbindAll()
*/
public function bindKey(keyCode:uint, action:String):void {
var method:Function = actionBindings[action];
if (method != null) keyBindings[keyCode] = method;
}
/**
* Binds keys and actions. Only one action can be assigned to one key.
* @param bindings Array which consists of sequence of couples of key code and action. An example are follows: <code> [ keyCode1, action1, keyCode2, action2 ] </code>.
*/
public function bindKeys(bindings:Array):void {
for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
}
/**
* Clear binding for given keyCode.
* @param keyCode Key code.
* @see #bindKey()
* @see #unbindAll()
*/
public function unbindKey(keyCode:uint):void {
delete keyBindings[keyCode];
}
/**
* Clear binding of all keys.
* @see #bindKey()
* @see #unbindKey()
*/
public function unbindAll():void {
for (var key:String in keyBindings) delete keyBindings[key];
}
/**
* Sets default binding.
* @see #bindKey()
* @see #unbindKey()
* @see #unbindAll()
*/
public function setDefaultBindings():void {
bindKey(87, ACTION_FORWARD);
bindKey(83, ACTION_BACK);
bindKey(65, ACTION_LEFT);
bindKey(68, ACTION_RIGHT);
bindKey(69, ACTION_UP);
bindKey(67, ACTION_DOWN);
bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
bindKey(Keyboard.UP, ACTION_FORWARD);
bindKey(Keyboard.DOWN, ACTION_BACK);
bindKey(Keyboard.LEFT, ACTION_LEFT);
bindKey(Keyboard.RIGHT, ACTION_RIGHT);
}
}
//}
import flash.utils.ByteArray;
class Base64 {
private static const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
public static const version:String = "1.1.0";
public static function encode(data:String):String {
// Convert string to ByteArray
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes(data);
// Return encoded ByteArray
return encodeByteArray(bytes);
}
public static function encodeByteArray(data:ByteArray):String {
// Initialise output
var output:String = "";
// Create data and output buffers
var dataBuffer:Array;
var outputBuffer:Array = new Array(4);
// Rewind ByteArray
data.position = 0;
// while there are still bytes to be processed
while (data.bytesAvailable > 0) {
// Create new data buffer and populate next 3 bytes from data
dataBuffer = new Array();
for (var i:uint = 0; i < 3 && data.bytesAvailable > 0; i++) {
dataBuffer[i] = data.readUnsignedByte();
}
// Convert to data buffer Base64 character positions and
// store in output buffer
outputBuffer[0] = (dataBuffer[0] & 0xfc) >> 2;
outputBuffer[1] = ((dataBuffer[0] & 0x03) << 4) | ((dataBuffer[1]) >> 4);
outputBuffer[2] = ((dataBuffer[1] & 0x0f) << 2) | ((dataBuffer[2]) >> 6);
outputBuffer[3] = dataBuffer[2] & 0x3f;
// If data buffer was short (i.e not 3 characters) then set
// end character indexes in data buffer to index of '=' symbol.
// This is necessary because Base64 data is always a multiple of
// 4 bytes and is basses with '=' symbols.
for (var j:uint = dataBuffer.length; j < 3; j++) {
outputBuffer[j + 1] = 64;
}
// Loop through output buffer and add Base64 characters to
// encoded data string for each character.
for (var k:uint = 0; k < outputBuffer.length; k++) {
output += BASE64_CHARS.charAt(outputBuffer[k]);
}
}
// Return encoded data
return output;
}
public static function decode(data:String):String {
// Decode data to ByteArray
var bytes:ByteArray = decodeToByteArray(data);
// Convert to string and return
return bytes.readUTFBytes(bytes.length);
}
public static function decodeToByteArray(data:String):ByteArray {
// Initialise output ByteArray for decoded data
var output:ByteArray = new ByteArray();
// Create data and output buffers
var dataBuffer:Array = new Array(4);
var outputBuffer:Array = new Array(3);
// While there are data bytes left to be processed
for (var i:uint = 0; i < data.length; i += 4) {
// Populate data buffer with position of Base64 characters for
// next 4 bytes from encoded data
for (var j:uint = 0; j < 4 && i + j < data.length; j++) {
dataBuffer[j] = BASE64_CHARS.indexOf(data.charAt(i + j));
}
// Decode data buffer back into bytes
outputBuffer[0] = (dataBuffer[0] << 2) + ((dataBuffer[1] & 0x30) >> 4);
outputBuffer[1] = ((dataBuffer[1] & 0x0f) << 4) + ((dataBuffer[2] & 0x3c) >> 2);
outputBuffer[2] = ((dataBuffer[2] & 0x03) << 6) + dataBuffer[3];
// Add all non-padded bytes in output buffer to decoded data
for (var k:uint = 0; k < outputBuffer.length; k++) {
if (dataBuffer[k+1] == 64) break;
output.writeByte(outputBuffer[k]);
}
}
// Rewind decoded data ByteArray
output.position = 0;
// Return decoded data
return output;
}
public function Base64() {
throw new Error("Base64 class is static container only");
}
}