Space.as
From Organic Design wiki
// Liscenced under LGPL: www.gnu.org/copyleft/lesser.html
//
// space.as - 3D space with dynamics and recursion
// Nad - 2003 for Amanda's SIC-Games project
//
// Initialise passed movieclip as a 3D space
create.space = function( name, layer ) {
this.createEmptyMovieClip( name, layer );
var newSpace = this[name];
newSpace._x = _root.width / 2;
newSpace._y = _root.height / 2;
newSpace.create = _root.space.create;
newSpace.reduce = _root.space.reduce;
newSpace.space = newSpace; // create() function used at all levels expects space to be this.space;
newSpace.rotation = 0; // global rotation of space
newSpace.zbuff = []; // Z ordered symbol-refs of all 3D objects
newSpace.zbLength = 0; // Since zbuff is sorted, we can use an end-ptr instead of pushing/splicing
newSpace.children = []; // Top layer children
newSpace.template = _root.template; // Template object for creating new symbols into the space
newSpace.zSCREEN = 1000; // Distance from origin (eye) to screen
// Sort function for zbuff array
newSpace.zcmp = function( a, b ) { return a.az < b.az; };
return newSpace;
};
// SPACE.CREATE
// Method: The space and all its objects inherit this method for creating child objects
// Creates a symbol for the 3D-object (spacer is used if classes[thisClass].symbol == null)
// The passed data object extends the space.template used to initialise the new object
space = {};
space.create = function( parameters ) {
var space = this.space;
var time = _root.time;
var instanceName = parameters[1];
if ( instanceName == null ) instanceName = 'i' + int( 1000000 * Math.random() ) + getTimer();
var symbol, hasSymbol = false;
var class = parameters[0];
var classRef = _root.classes[class];
if ( class == 'obj' ) symbol = parameters[2];
else symbol = classRef.symbol;
// Create new child symbol using the 3D-object template (space.template)
space.createEmptyMovieClip( instanceName, space.zbLength );
var child = space[instanceName];
if ( symbol != null ) {
_root.createSymbol( child, symbol, 'swf', 0 );
child.hasSymbol = true;
} else child.hasSymbol = false;
var template = space.template;
for ( var i in template ) child[i] = template[i];
this.children.push( child ); // object available in hierachical update tree of reduce() calls
space.zbuff[space.zbLength++] = child; // place object into z-buffer for layer sorting
// Populate new child symbol/object
for ( var i in classRef ) child[i] = classRef[i];
child.space = space;
child.class = class;
child.parent = this;
child.children = [];
child.time = time;
child.colour = new Color( child.swf );
child.die = false;
// Set the objects' dynamics() and call its init() - parameters[0] is 3DO-class
if ( classRef.init != null ) classRef.init.call( child, parameters );
return child;
};
// SPACE.REDUCE
space.reduce = function() {
var space = this.space;
var zbuff = space.zbuff;
// Execute the template.reduce() of each top-level child
for ( var i = this.children.length-1; i >= 0; i-- ) {
var obj = this.children[i];
obj.ax = obj.ay = obj.az = 0;
obj.reduce();
// delete from this child list if marked for death
if ( obj.die ) this.children.splice( i, 1 );
}
// Reduce the 3D coordinates to 2D for *ALL* objects with symbols (ie all layers, empty)
// - ax,ay,az are absolute positions calculated in child reduce() exec layers
for ( var i = 0; i < space.zbLength; i++ ) {
var obj = zbuff[i];
if ( obj.die ) obj.az = -100000000; // dead ones will get sorted to the end
else {
// Global rotation
if ( space.rotation != 0 ) obj.rotate( 'ax', 'az', space.rotation );
// Reduce 3D ( x,y,x -> _x,_y,scale )
var m = space.zSCREEN / obj.az;
obj._x = obj.ax * m;
obj._y = obj.ay * m;
if ( m < 0 ) m = 0; else if ( m > 10 ) m = 10;
obj._xscale = obj._yscale = obj.scale * m;
// Lightness ( based on 3D scale multiplier )
if ( m < 0.5 ) light = 0.5;
else if ( m < 1.5 ) light = 0.5 + ( m - 0.5 ); else light = 1;
obj.colour.setTransform( { ra:obj.ra * light, ga:obj.ga * light, ba:obj.ba * light, ab:obj.alpha } );
}
}
// Sort zbuff, set depths and remove dead children
zbuff.sort( space.zcmp );
while ( zbuff[space.zbLength-1].die ) zbuff[--space.zbLength].removeMovieClip();
for ( var i = 0; i < space.zbLength; i++ ) zbuff[i].swapDepths(i);
};
// TEMPLATE
// - Create a set of methods and properties inherited by all symbols created in the space
// - various dynamics() and effects() can add and maintain other properties
template = {};
template.space = null; // set by create() A ref to the space object
template.create = space.create; // Inherits generic create() method
template.parent = null; // Set by create(), a ref to the parent of this object
template.children = null; // Children of this object (must be initialised by create)
template.die = false; // Object deletion must be handled ny the space.reduce()
template.motion = []; // Motion functions to call in reduce()
template.colour = null; // Set by create() - objects colour transform
template.scale = 100; // symbol scale
template.alpha = 100; // symbol scale
template.time = 0; // Objects' absolute birth time
template.x = 0; // Objects relative position (ie relative to birth location and time)
template.y = 0; //
template.z = 0; //
template.ra = 100; // Colour transform properties
template.ga = 100; //
template.ba = 100; //
// per-frame method for 3D-objects (not the space itself which has its own frame function)
// - reduce() calculates the absolute position from the relative positions in the parent-child chain of each object
template.reduce = function() {
var t = _root.time - this.time;
// Update objects dynamic properties
if ( !this.die ) {
if ( this.dynamics != null ) this.dynamics(t);
// Execute this objects' current motion effects x += f(t)
for ( var i = 0; i < this.motion.length; i++ ) this.motion[i].call( this, t );
// Update absolute location for space.reduce() to render
this.ax += this.x;
this.ay += this.y;
this.az += this.z;
}
// Execute the reduce() of each child
// - if this is dead, propagate death to descendents
for ( var i = this.children.length-1; i >= 0; --i ) {
var obj = this.children[i];
if ( this.die ) obj.die = true;
else {
obj.ax = this.ax;
obj.ay = this.ay;
obj.az = this.az;
}
obj.reduce.call(obj);
}
};
// Rotate an object
template.rotate = function( x, y, a ) {
a += Math.atan2( this.y, this.x );
var r = Math.sqrt( this.x*this.x+this.y*this.y );
this.x = Math.cos(a)*r;
this.y = Math.sin(a)*r;
};
// 3D OBJECT CLASSES
// NOTE: (Not using languages' classes here as actionscript too lame-arse)
// Each class has:
// - init(data) used to create and layout child objects within itself (and can instantiate an asscoiated symbol too)
// - dynamics() called from it's reduce() which then also calls space.update
// each dynamics can call a set of effects functions depending on its state and time
// NOTE: dynamics must base all changes on time
// NOTE: dynamics are responsible for their coordinates being relative to parent
// - this is because some dynamics update their own coodinates and some their childrens
// - classes may has only a symbol and no init() or dynamics()
// these phases and layers should be tree nodes
classes = {};
// SPARK
classes.spark = {};
classes.spark.init = function(parameters) {
this.lat = parameters[2];
this.lon = parameters[3];
};
classes.spark.symbol = 'radial';
// BALL
#include "ball.as"
classes.ball = {};
classes.ball.init = function(parameters) {
this.lat = parameters[2];
this.lon = parameters[3];
this.motion = [ _root.motion.float ];
};
classes.ball.symbol = 'ball';
// STAR-BURST CLASS
classes.starBurst = {};
classes.starBurst.symbol = 'radial';
classes.subBurst = {};
// starBurst: stars,force,x,y,z
classes.starBurst.init = function(parameters) {
var guid = parameters[0];
this.stars = parameters[2];
this.force = parameters[3];
this.x = 1.0 * parameters[4];
this.y = 1.0 * parameters[5];
this.z = 1.0 * parameters[5];
this._visible = false;
var ri = this.time;
for ( var i = 0; i < this.stars; i++ ) {
var lat = _root.rnd( ri, 1 ) * 2 * Math.PI;
var lon = _root.rnd( ri + 10, 1 ) * 2 * Math.PI;
ri += 20;
var obj = this.create( [ 'subBurst', null, 4, 1.5, 500, lat, lon, ri ] );
}
};
// subBurst: stars,force,start,fadeout,lat,lon
classes.subBurst.init = function( parameters ) {
this.stars = parameters[2];
this.force = parameters[3];
this.start = parameters[4];
this.lat = parameters[5];
this.lon = parameters[5];
var ri = 1 * parameters[7];
for ( var i = 0; i < this.stars; i++ ) {
var lat = _root.rnd( ri, 3 ) * 2 * Math.PI;
var lon = _root.rnd( ri + 31, 3) * 2 * Math.PI;
ri += 51;
var obj = this.create( [ 'spark', null, lat, lon ] );
obj.index = i;
}
};
classes.subBurst.symbol = 'radial';
classes.starBurst.dynamics = function(t) {
var onlyOne = true;
// This is a subBurst
if (this.start != null) {
this.swf.gotoAndStop(4);
if (t > this.start) {
this.radius = (t-this.start)*this.force;
onlyOne = false;
}
}
else {
this.swf.gotoAndStop(1);
this.radius = 100+t*this.force;
}
this.force = this.force*0.97;
if (this.alpha < 0) this.alpha = 0;
var a = 100-(t/30);
if (a < 0) {
if (this.parent == this.space) this.die = true;
a = 0;
}
// Update child positions: x = 0 + f(t) stylz
for (var i = 0; i < this.children.length; i++) {
var star = this.children[i];
var s = this.radius*Math.cos(star.lon);
star.x = s*Math.cos(star.lat+t/500);
star.y = this.radius*Math.sin(star.lon)+t*t/8000;
star.z = s*Math.sin(star.lat+t/500);
if (onlyOne && (i>0)) star.alpha = 0; else star.alpha = a;
}
};
classes.subBurst.dynamics = classes.starBurst.dynamics;
// TORUS CLASS
classes.torus = {};
classes.torus.init = function( parameters ) {
this.points = 1 * parameters[2];
this.radius = 1.0 * parameters[3];
this.speed = 1.0 * parameters[4];
var arc = 2 * Math.PI / this.points;
for ( var i = 0; i < this.points; i++ ) {
obj = this.create( [ 'ball', null, 50 ] );
obj.arc = arc * i;
obj.floatOffset = Math.random() * 2 * Math.PI;
j = i + 1;
//obj.ra = (j & 1) ? 0xff : 0;
//obj.ga = (j & 2) ? 0xff : 0;
//obj.ba = (j & 4) ? 0xff : 0;
}
};
classes.torus.dynamics = function(t) {
this.x = _root._xmouse - _root.width / 2;
this.y = _root._ymouse - _root.height / 2;
this.Z = 1000;
for ( var i = 0; i < this.children.length; i++ ) {
var point = this.children[i];
var angle = point.arc + this.angle / this.children.length + t / this.speed;
var r = this.parent.radius + this.radius * Math.sin( angle );
point.x = this.x + r * Math.cos( this.angle );
point.z = this.z + 5 * this.radius * Math.cos( angle );
point.y = this.y + r * Math.sin( this.angle );
}
};
// IMPLODE CLASS
classes.implode = {};
classes.implode.init = function( parameters ) {
this.stars = 25;
this.radius = 200;
this.x = 1.0 * parameters[2];
this.y = 1.0 * parameters[3];
this.z = 1.0 * parameters[4];
var ri = this.time;
for ( var i = 0; i < this.stars; i++ ) {
var lat = _root.rnd( ri, 1 ) * 2 * Math.PI;
var lon = _root.rnd( ri + 10, 1 ) * 2 * Math.PI;
var obj = this.create( [ 'spark', null, lat, lon ] );
obj.v = _root.rnd( ri + 20, 1 ) * 100 + 10;
obj.scale = 50;
ri += 30;
}
};
classes.implode.dynamics = function(t) {
this.radius = 500 - t / 3;
if ( this.radius < 100 ) this.radius = 100;
var a = t / 10;
if ( a > 200 ) this.die = true;
if ( a > 100 ) a = 200 - a;
for ( var i = 0; i < this.children.length; i++ ) {
var star = this.children[i];
var r = this.radius * star.v / 50;
var s = r * Math.cos( star.lon );
star.x = s * Math.cos( star.lat + t / star.v / 2 );
star.y = r * Math.sin( star.lon);
star.z = s * Math.sin( star.lat + t / star.v / 2 );
star.alpha = a / 2;
}
};
// STARFIELD CLASS
classes.starField = {};
classes.starField.init = function(parameters) {
this.stars = 100;
this.radius = 2000;
var ri = this.time;
for ( var i = 0; i < this.stars; i++ ) {
var lat = _root.rnd( ri, 1 ) * 2 * Math.PI;
var lon = _root.rnd( ri + 10, 1 ) * 2 * Math.PI;
var obj = this.create( [ 'spark', null, lat, lon ] );
obj.v = _root.rnd( ri + 20, 1 ) * 100 + 10;
obj.scale = 50;
ri += 30;
}
};
classes.starField.dynamics = function(t) {
this.radius = 500 - t/3;
if ( this.radius < 100 ) this.radius = 100;
var a = t / 10;
if ( a > 200 ) this.die = true;
if ( a > 100 ) a = 200 - a;
for ( var i = 0; i < this.children.length; i++ ) {
var star = this.children[i];
var r = this.radius * star.v / 50;
var s = r * Math.cos( star.lon );
star.x = s * Math.cos( star.lat + t / star.v / 2 );
star.y = r * Math.sin( star.lon );
star.z = s * Math.sin( star.lat + t / star.v / 2 );
star.alpha = a / 2;
}
};
// 3D MOTION f(t) CLASSES
// - 3D Motion functions called by an objects' reduce() if in its motion[] list
// Float
// - Adds the metaparticles movement to passed object
motion = {};
motion.float = function(t) {
var a = this.floatOffset + t / 400;
var m = ( this._width + this._height ) * this.scale / 1000;
var api4 = a * Math.PI / 4;
var api8 = a * Math.PI / 8;
var cospi4 = Math.cos( api4 );
var cospi8 = Math.cos( api8 );
var sinpi4 = Math.sin( api4 );
var sinpi8 = Math.sin( api8 );
this.x += m * cospi4 * sinpi8;
this.y += m * cospi4 * cospi8;
this.z += m * sinpi4;
};
// Square-sine
motion.squareSine = function(t) {
var i = Math.floor( t/Math.PI ) % 6;
if ( (i == 1) || (i == 2) ) return -1;
if ( i > 3 ) return 1;
return Math.cos(t);
};