CC3DPreview : CCD with Alternativa3D EllipsoidColliders
Sector-based Continuous collision detection/response framework with ellipsoid colliders. NOTE: still rather incomplete atm.
http://github.com/Glidias/CC3D-The-AlternSector
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/r7WP
*/
package {
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.*;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.objects.Mesh;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.primitives.GeoSphere;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Vector3D;
import flash.text.TextField;
import flash.utils.getTimer;
import alternativ7.engine3d.alternativa3d;
use namespace alternativa3d;
/**
* CC3D preview demo. A continous collision detection (CCD) framework
* for Alternativa3D and (possibly other games).
*/
public class CC3DPreviewOnline extends Sprite {
private var rootContainer:Object3DContainer = new DistanceSortContainer();
private var camera:Camera3D;
private var controller:SimpleObjectController;
private var _debugField:TextField;
private var errors:String = "";
public static var _GROUPS:CollisionGroups;
public static var _WORLD:SectorWorld;
public function CC3DPreviewOnline() {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
// Camera and view
// Создание камеры и вьюпорта
camera = new Camera3D();
camera.view = new View(stage.stageWidth, stage.stageHeight);
addChild(camera.view);
addChild(camera.diagram);
// Initial position
// Установка начального положения камеры
camera.rotationX = -130*Math.PI/180;
camera.rotationZ = -30*Math.PI/180;
camera.x = -594;
camera.y = 212;
camera.z = -127;
camera.rotationX = -1.2;
camera.rotationZ = -2;
controller = new SimpleObjectController(stage, camera, 200);
rootContainer.addChild(camera);
rootContainer.name = "Root container!";
// Listeners
// Подписка на события
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(Event.RESIZE, onResize);
setupDebugField();
setupWorld();
_currTime = getTimer();
}
private function setupWorld():void
{
// World and collision groups
_WORLD = new SectorWorld();
_GROUPS = new CollisionGroups();
var ball:TestBall;
var testSector:Sector;
var container:Object3DContainer = new DistanceSortContainer();
container.name = "CONTAINER_1";
container.x = 0;
container.y = 0;
container.z = 0;
var polyhedron:Mesh;
polyhedron = new Box(712, 512, 400, 1, 1, 1, true);
polyhedron.sorting = 0;
// jitter vertex hack required because EllipsoidCollider has bug that returns zero-length collision
// normal for axis-aligned surfaces
for (var v:Vertex = polyhedron.vertexList; v != null; v = v.next) {
v.x += Math.random() * 1e-8;
v.y += Math.random() * 1e-8;
v.z += Math.random() * 1e-8;
}
polyhedron.calculateNormals();
polyhedron.calculateBounds();
polyhedron.setMaterialToAllFaces(new FillMaterial(0, 0.2, 1, 0));
FaceMacros.makeDoubleSided(polyhedron);
testSector = new Sector();
testSector.container = container;
testSector.collisionTarget = polyhedron;
container.addChild(polyhedron);
rootContainer.addChild(container);
ball = getRandomBall(testSector, 50, 0, 0, 1000);
ball.object.x += 80;
ball = getRandomBall(testSector, 50, 0, 0, 1000);
ball.object.x -= 80;
getRandomBall(testSector, 51, 50, 0, 1000);
ball.object.y -= 80;
// /*
polyhedron = new GeoSphere(512, 1, true);
polyhedron.setMaterialToAllFaces(new FillMaterial(0, .1, 1));
container = new DistanceSortContainer();
container.name = "CONTAINER 2";
container.x = 1600;
container.y = 0;
container.z = 0;
testSector = new Sector();
testSector.collisionTarget = polyhedron;
testSector.container = container;
container.addChild(polyhedron);
rootContainer.addChild(container);
polyhedron.calculateNormals();
polyhedron.calculateBounds();
polyhedron.sorting = 0;
FaceMacros.makeDoubleSided(polyhedron);
ball = getRandomBall(testSector, 20);
ball.object.x += 210;
ball = getRandomBall(testSector, 50, 50, 20);
ball.object.y += 220;
ball = getRandomBall(testSector, 20);
ball.object.y -= 220;
ball = getRandomBall(testSector, 20);
ball.object.z -= 220;
ball = getRandomBall(testSector, 60);
ball.object.z += 220;
// */
}
private function getRandomBall(sector:Sector, radius:Number=20, radiusY:Number=0, radiusZ:Number=0, baseSpeed:Number=1500):TestBall {
var ball:TestBall = new TestBall(sector, radius, radiusY, radiusZ);
var cloneSector:Object3D = sector.collisionTarget;
ball.object.x = cloneSector.x + ( cloneSector.boundMinX + (cloneSector.boundMaxX-cloneSector.boundMinX)*.5);
ball.object.y = cloneSector.y + ( cloneSector.boundMinY + (cloneSector.boundMaxY-cloneSector.boundMinY)*.5);
ball.object.z = cloneSector.z + ( cloneSector.boundMinZ + (cloneSector.boundMaxZ - cloneSector.boundMinZ) * .5);
var dir:Vector3D =new Vector3D(Math.random() * 100, Math.random() * 100, Math.random() * 100);
dir.normalize();
dir.scaleBy(baseSpeed);
ball.cc3D.vx = dir.x;
ball.cc3D.vy = dir.y;
ball.cc3D.vz = dir.z;
sector.container.addChild(ball.object);
return ball;
}
private function setupDebugField():void
{
_debugField = new TextField();
_debugField.autoSize = "left";
addChild(_debugField);
}
private var _currTime:int;
private function onEnterFrame(e:Event):void {
var newTime:int = getTimer()
var timeElapsed:int = 30; // newTime - _currTime;
_currTime = newTime;
_WORLD.update(timeElapsed);
var errorShow:Error;
if ( (errorShow= _WORLD.getTraceError()) ) {
errors += "\n" + errorShow.message;
}
camera.transformId++; // timestamp for cam
_debugField.text = "Updating...::"+ errors;
controller.update();
camera.render();
}
private function onResize(e:Event = null):void {
// Width and height of view
// Установка ширины и высоты вьюпорта
camera.view.width = stage.stageWidth;
camera.view.height = stage.stageHeight;
}
}
}
//package portal
//{
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.objects.Mesh;
import flash.geom.Point;
import flash.geom.Vector3D;
/**
* Example bare-bones class in AS3 for Alternativa3D context.
*
* @author Glenn Ko
*/
//public
class Sector
{
public var physics:SectorPhysics;
public var collisionTarget:Object3D;
public var container:Object3DContainer; // not being used atm
public function Sector()
{
}
}
//}
//package cc3dexample.entity
//{
/**
* Example collision groups setup for specific applications.
* @author Glenn Ko
*/
//public
class CollisionGroups
{
//eg. buffer in 16 BallToWorld collision pairs
public const BALL:int = CollisionTable.Get().getNewCollisionGroupId(BallToWorld, 16);
// (Not yet available)
//public const PLAYER:int = CollisionTable.Get().getNewCollisionGroupId(PlayerToWorld);
//public const PROJECTILES:int = CollisionTable.Get().getNewCollisionGroupId(ProjectileToWorld);
public function CollisionGroups() {
// Setup table and filtering
var table:CollisionTable = CollisionTable.Get();
// eg. buffer in 8 BallToBall collision pairs
table.registerCollisionPair(BALL, BALL, BallToBall);
table.getPool(BALL, BALL).allocate(8);
// (Not yet available)
//table.registerCollisionPair(PLAYER, BALL, PlayerToBall);
//table.registerCollisionPair(PLAYER, PROJECTILES, PlayerToProjectile);
//table.setFilterMask(PROJECTILES, PLAYER);
// Finalize table
table.finalize();
}
}
//}
//package cc3dexample.entity
//{
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.primitives.Sphere;
import flash.geom.Vector3D;
/**
* ...
* @author Glenn Ko
*/
//public
class TestBall
{
private var _object:Sphere;
private var _radius:Number;
private var _radiusY:Number;
private var _radiusZ:Number;
public var cc3D:CC3D;
public static var COUNT:int = 0;
public function TestBall(sector:Sector, radius:Number, radiusY:Number=0, radiusZ:Number=0)
{
this._radiusZ = radiusZ!= 0 ? radiusZ : radius;
this._radiusY = radiusY!= 0 ? radiusY : radius;
this._radius = radius;
this._object = new Sphere(radius);
_object.name = "Ball" + COUNT++;
if (radiusY != 0 || radiusZ != 0) {
var normScale:Vector3D = new Vector3D(_radius, _radiusY, _radiusZ);
normScale.normalize();
_object.scaleX = 1;
_object.scaleY = _radiusY / radius;
_object.scaleZ = _radiusZ / radius;
}
_object.sorting = 0;
testCC3D(sector);
_object.setMaterialToAllFaces( new FillMaterial(0xFF0000, 1, 1,0) )
}
public function testCC3D(sector:Sector):void
{
cc3D = CC3D.create(_object, this, CC3DPreviewOnline._GROUPS.BALL, 0, 0, 0, _radius, _radiusY, _radiusZ, sector);
// cc3D.collider.threshold = 0.0000001
CC3DPreviewOnline._WORLD.addCC3D(cc3D);
}
public function get object():Sphere { return _object; }
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
class CC3DPair {
public function CC3DPair() : void {
null;
}
public var next : CC3DPair;
public var pool : CC3DPairPool;
public var t : Number;
public var c1 : CC3D;
public var c2 : CC3D;
public function _init() : void {
null;
}
public function destroy() : void {
this.c1 = null;
this.c2 = null;
this._dispose();
}
protected function _dispose() : void {
this.next = this.pool.collector;
this.pool.collector = this;
}
public function willCollide(dt : Number) : Boolean {
return false;
}
public function resolve() : void {
null;
}
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
class CC3DPairPool {
public function CC3DPairPool() : void {
null;
}
public var collector : CC3DPair;
public var classe : Class;
public function allocate(amount : int) : void {
var result : CC3DPair;
while(--amount > -1) {
result = new classe();
result._init();
result.pool = this;
result.next = this.collector;
this.collector = result;
}
}
public function create() : CC3DPair {
var result : CC3DPair;
if(this.collector != null) {
result = this.collector;
result.next = null;
result.pool = this;
this.collector = this.collector.next;
}
else {
result = new classe();
result._init();
result.pool = this;
}
return result;
}
static protected var EMPTY_PARAMS : Array = [];
static public function getNewPool(classe : *) : CC3DPairPool {
var newPool : CC3DPairPool = new CC3DPairPool();
newPool.classe = classe;
return newPool;
}
}
//}
//package cc3dexample.entity.collisions
//{
import flash.geom.Vector3D;
/**
* Example:
* Supports moving ellipsoid to ellipsoid collision detection and resolution.
* @author Glenn Ko
*/
//public
class BallToBall extends CC3DPair
{
public function BallToBall()
{
}
private var ellipsoid_radius:Vector3D = new Vector3D();
private var ray_travel:Vector3D = new Vector3D();
private var ray_origin:Vector3D = new Vector3D();
// /*
override public function willCollide(dt:Number):Boolean {
// This is a C1 ray hit test against....
ray_origin.x = c1.object.x - c2.object.x;
ray_origin.y = c1.object.y - c2.object.y;
ray_origin.z = c1.object.z - c2.object.z;
// ...inflated ellipsoid (sum of radii)
ellipsoid_radius.x = c1.collider.radiusX + c2.collider.radiusX;
ellipsoid_radius.y = c1.collider.radiusY + c2.collider.radiusY;
ellipsoid_radius.z = c1.collider.radiusZ + c2.collider.radiusZ;
// ...based on relative velocities of c1/c2.
ray_travel.x = c1.vx - c2.vx;
ray_travel.y = c1.vy - c2.vy;
ray_travel.z = c1.vz - c2.vz;
// Find "d" in normalized unit time.
// Quadratic formula (to consider: simplified to 1 solution: b^2-ac. instead)
var a:Number = ((ray_travel.x*ray_travel.x)/(ellipsoid_radius.x*ellipsoid_radius.x))
+ ((ray_travel.y*ray_travel.y)/(ellipsoid_radius.y*ellipsoid_radius.y))
+ ((ray_travel.z*ray_travel.z)/(ellipsoid_radius.z*ellipsoid_radius.z));
var b:Number = ((2*ray_origin.x*ray_travel.x)/(ellipsoid_radius.x*ellipsoid_radius.x))
+ ((2*ray_origin.y*ray_travel.y)/(ellipsoid_radius.y*ellipsoid_radius.y))
+ ((2*ray_origin.z*ray_travel.z)/(ellipsoid_radius.z*ellipsoid_radius.z));
var c:Number = ((ray_origin.x*ray_origin.x)/(ellipsoid_radius.x*ellipsoid_radius.x))
+ ((ray_origin.y*ray_origin.y)/(ellipsoid_radius.y*ellipsoid_radius.y))
+ ((ray_origin.z*ray_origin.z)/(ellipsoid_radius.z*ellipsoid_radius.z))
- 1;
var d:Number = b*b-4*a*c;
if ( d < 0 ) { // no real roots
return false;
}
d = Math.sqrt(d);
const multiplier:Number = 1/(2*a);
var hit:Number = (-b + d)*multiplier;
var hitsecond:Number = (-b - d)*multiplier;
d = hit < hitsecond ? hit : hitsecond; // 2 solutions, bah...
if (d < 0) {
return false;
}
t = d * dt;
return t <= dt; // collision happened within timeframe
}
override public function resolve():void {
var cn:Vector3D = c1.collisionNormal;
cn.x = c2.object.x - c1.object.x;
cn.y = c2.object.y - c1.object.y;
cn.z = c2.object.z - c1.object.z;
cn.normalize();
//relative velocity
var dv:Vector3D = new Vector3D(c2.vx - c1.vx, c2.vy - c1.vy, c2.vz - c1.vz); // p2.v.minus( p1.v );
const p2Mass:Number = 1;
const p1Mass:Number = 1;
//const mass:Number = 1;
//perfectly elastic impulse
dv.x *= -2; dv.y *= -2; dv.z *= -2;
var cn2:Vector3D = cn.clone();
cn2.scaleBy( 1 / p1Mass + 1 / p2Mass );
var impulse:Number = cn.dotProduct( dv ) / cn.dotProduct( cn2);
var multiplier:Number = -impulse / p1Mass;
c1.vx += cn.x * multiplier;
c1.vy += cn.y * multiplier;
c1.vz += cn.z * multiplier;
multiplier = impulse / p2Mass;
c2.vx += cn.x * multiplier;
c2.vy += cn.y * multiplier;
c2.vz += cn.z * multiplier;
}
}
//}
//package cc3dexample.entity.collisions
//{
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.RayIntersectionData;
import alternativ7.engine3d.materials.FillMaterial;
import flash.geom.Vector3D;
/**
* Example:
* Original implementation: http://wonderfl.net/c/8RNL
* generalrelativity's Elastic Collision
*
* @author Glenn Ko
*/
//public
class BallToWorld extends CC3DPair
{
public function BallToWorld()
{
}
override public function resolve():void
{
var cn:Vector3D = c1.collisionNormal.clone();
//relative velocity
var dv:Vector3D = new Vector3D(c1.vx, c1.vy, c1.vz);
//perfectly elastic
const mass:Number = 1;
var impulse:Number = cn.dotProduct( Vector3DUtils.getNewScaledVector(dv, -2 ) ) / ( 1 / mass );
var scalar:Number = impulse / mass;
cn.scaleBy( scalar);
dv.incrementBy( cn );
c1.vx = dv.x;
c1.vy = dv.y;
c1.vz = dv.z;
}
}
//}
//package cc3dexample
//{
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Wrapper;
import alternativ7.engine3d.objects.Mesh;
use namespace alternativa3d;
/**
* ...
* @author Glenn Ko
*/
//public
class FaceMacros
{
public static function makeDoubleSided(mesh:Mesh):void {
// Create new list of faces and combine them together
var tailNewFace:Face;
var newFace:Face;
var headNewFace:Face;
var lastFace:Face;
for (var face:Face = mesh.faceList; face!=null; face= face.next) {
newFace = cloneFace(face);
flipFaces(newFace);
if (tailNewFace != null) tailNewFace.next = newFace
else headNewFace = newFace
tailNewFace = newFace;
lastFace = face;
}
// combine lists with new
lastFace.next = headNewFace;
}
public static function cloneFace(face:Face):Face
{
// Prepare cloned face
var clipFace:Face = face.create();
clipFace.material = face.material;
clipFace.offset = face.offset;
clipFace.normalX = face.normalX;
clipFace.normalY = face.normalY;
clipFace.normalZ = face.normalZ;
// deepCloneWrapper() inline
var wrapper:Wrapper = face.wrapper;
var wrapperClone:Wrapper = wrapper.create();
wrapperClone.vertex = wrapper.vertex;
var w:Wrapper = wrapper.next;
var tailWrapper:Wrapper = wrapperClone;
var wClone:Wrapper;
while (w != null) {
wClone = w.create();
wClone.vertex = w.vertex;
tailWrapper.next = wClone;
tailWrapper = wClone;
w = w.next;
}
clipFace.wrapper = wrapperClone;
return clipFace;
}
public static function flipFaces(list:Face):void {
for (var f:Face = list; f != null; f = f.next) {
// Flip normal/offset values
f.normalX = -f.normalX;
f.normalY = -f.normalY;
f.normalZ = -f.normalZ;
f.offset = -f.offset;
// Reverse vertex order
var nextWrapper:Wrapper;
var headerWrapper:Wrapper = null;
for (var w:Wrapper = f.wrapper; w != null; w = nextWrapper) {
nextWrapper = w.next;
w.next = headerWrapper;
headerWrapper = w;
}
f.wrapper = headerWrapper;
}
}
}
//}
// -- CORE Framework code below (haxe-as3-output). Can ignore...
// CC3D Framework
//package alternsector.physics.adaptors.alternativa3d.ccd {
import flash.geom.Vector3D;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.EllipsoidCollider;
//public
class CC3D {
public function CC3D() : void {
null;
}
public var object : alternativa.engine3d.core.Object3D;
public var collider : alternativa.engine3d.core.EllipsoidCollider;
public var vx : Number;
public var vy : Number;
public var vz : Number;
public var entity : *;
public var collisionGroupId : int;
public var sleep : Boolean;
public var staticCollision : CC3DPair;
public var collisionPoint : flash.geom.Vector3D;
public var collisionNormal : flash.geom.Vector3D;
public var position : flash.geom.Vector3D;
public var displacement : flash.geom.Vector3D;
public var typeA : Boolean;
public var sector : Sector;
public var id : int;
public var _parent : CC3DList;
public var prev : CC3D;
public var next : CC3D;
public var boundMaxX : Number;
public var boundMaxY : Number;
public var boundMaxZ : Number;
public var boundMinX : Number;
public var boundMinY : Number;
public var boundMinZ : Number;
public var mass : Number;
public function _integrate(dt : Number) : void {
this.object.x += this.vx * dt;
this.object.y += this.vy * dt;
this.object.z += this.vz * dt;
}
public function _updateAABB(dt : Number) : void {
var dest : Number = this.object.x + this.vx * dt;
this.boundMinX = PMath.minF(this.object.x,dest) - this.collider.radiusX;
this.boundMaxX = PMath.maxF(this.object.x,dest) + this.collider.radiusX;
dest = this.object.y + this.vy * dt;
this.boundMinY = PMath.minF(this.object.y,dest) - this.collider.radiusY;
this.boundMaxY = PMath.maxF(this.object.y,dest) + this.collider.radiusY;
dest = this.object.z + this.vz * dt;
this.boundMinZ = PMath.minF(this.object.z,dest) - this.collider.radiusZ;
this.boundMaxZ = PMath.maxF(this.object.z,dest) + this.collider.radiusZ;
}
public function isAABBOverlapping(sibling : CC3D) : Boolean {
return !(this.boundMinX > sibling.boundMaxX || this.boundMinY > sibling.boundMaxY || this.boundMinZ > sibling.boundMaxZ || this.boundMaxX < sibling.boundMinX || this.boundMaxY < sibling.boundMinY || this.boundMaxZ < sibling.boundMinZ);
}
public function collidesWithWorld(dt : Number) : Boolean {
this._updateVectors(dt);
return this._gotCollideWithWorld(dt);
}
public function _updateVectors(dt : Number) : void {
this.position.x = this.object.x;
this.position.y = this.object.y;
this.position.z = this.object.z;
this.displacement.x = this.vx * dt;
this.displacement.y = this.vy * dt;
this.displacement.z = this.vz * dt;
}
public function _gotCollideWithWorld(dt : Number) : Boolean {
return this.collider.getCollision(this.position,this.displacement,this.collisionPoint,this.collisionNormal,this.sector.collisionTarget);
}
public function _removeFromParent() : void {
null;
}
public function _dispose() : void {
null;
}
static public var collector : CC3D;
static public function create(obj : alternativa.engine3d.core.Object3D,entity : *,collisionGroupId : int,vx : Number,vy : Number,vz : Number,radiusX : Number,radiusY : Number,radiusZ : Number,sector : Sector) : CC3D {
var result : CC3D;
if(CC3D.collector != null) {
result = collector;
result.collider.radiusX = radiusX;
result.collider.radiusY = radiusY;
result.collider.radiusZ = radiusZ;
CC3D.collector = collector.next;
}
else {
result = new CC3D();
result.collider = new alternativa.engine3d.core.EllipsoidCollider(radiusX,radiusY,radiusZ);
result.collisionNormal = new flash.geom.Vector3D();
result.collisionPoint = new flash.geom.Vector3D();
result.position = new flash.geom.Vector3D();
result.displacement = new flash.geom.Vector3D();
}
result.next = null;
result.sleep = false;
result.sector = sector;
result.mass = 1;
result.typeA = true;
result.object = obj;
result.vx = vx;
result.vy = vy;
result.vz = vz;
result.entity = entity;
result.collisionGroupId = collisionGroupId;
return result;
}
static public function createStatic(obj : alternativa.engine3d.core.Object3D) : void {
var result : CC3D;
if(CC3D.collector != null) {
result = collector;
CC3D.collector = collector.next;
}
else result = new CC3D();
result.next = null;
result.object = obj;
}
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
class CC3DList {
public function CC3DList() : void {
null;
}
public var head : CC3D;
public function _integrate(dt : Number) : void {
var h : CC3D = this.head;
while(h != null) {
h._integrate(dt);
h = h.next;
}
}
public function prepend(cc : CC3D) : void {
if(this.head != null) null;
cc.next = this.head;
this.head = cc;
}
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
class CollisionTable {
public function CollisionTable() : void {
this.reset();
}
protected var _COUNT : int;
public function getNewCollisionGroupId(staticPairClasse : * = null,staticAllocate : int = 0) : int {
var row : Vector.<CC3DPairPool> = new Vector.<CC3DPairPool>(this._COUNT);
this._poolTable.push(row);
var newPool : CC3DPairPool;
{
var _g1 : int = 0, _g : int = this._COUNT + 1;
while(_g1 < _g) {
var i : int = _g1++;
newPool = new CC3DPairPool();
newPool.classe = CC3DPairPool;
row[i] = newPool;
}
}
if(staticPairClasse != null) this.registerStaticCollisionPair(this._COUNT,staticPairClasse,staticAllocate);
else this.registerStaticCollisionPair(this._COUNT,CC3DPair,0);
return this._COUNT++;
}
public function setFilterMask(groupId : int,groupMask : int) : void {
null;
}
public function registerStaticCollisionPair(groupId : int,classe : *,staticAmount : int = 0) : void {
var newPool : CC3DPairPool = new CC3DPairPool();
newPool.classe = classe;
this._staticPoolTable[groupId] = newPool;
if(staticAmount != 0) {
newPool.allocate(staticAmount);
}
}
protected var _poolTable : Vector.<Vector.<CC3DPairPool>>;
protected var _staticPoolTable : Vector.<CC3DPairPool>;
public function reset() : void {
this._poolTable = new Vector.<Vector.<CC3DPairPool>>();
this._staticPoolTable = new Vector.<CC3DPairPool>();
this._COUNT = 0;
}
public function getPair(groupId1 : int,groupId2 : int) : CC3DPair {
var pairPool : CC3DPairPool = this._poolTable[groupId1][groupId2];
return pairPool.create();
}
public function getStaticPair(groupId1 : int) : CC3DPair {
var pairPool : CC3DPairPool = this._staticPoolTable[groupId1];
return pairPool.create();
}
public function registerCollisionPair(groupId : int,groupId2 : int,classe : *,allocateAmount : int = 0) : void {
var pairPool : CC3DPairPool = new CC3DPairPool();
pairPool.classe = classe;
this._poolTable[groupId][groupId2] = pairPool;
this._poolTable[groupId2][groupId] = pairPool;
if(allocateAmount > 0) {
pairPool.allocate(allocateAmount);
}
}
public function finalize() : void {
this._poolTable.fixed = true;
{
var _g1 : int = 0, _g : int = this._COUNT;
while(_g1 < _g) {
var i : int = _g1++;
this._poolTable[i].fixed = true;
}
}
this._staticPoolTable.fixed = true;
}
public function getStaticPool(id : int) : CC3DPairPool {
return this._staticPoolTable[id];
}
public function getPool(id1 : int,id2 : int) : CC3DPairPool {
return this._poolTable[id1][id2];
}
static protected var _instance : CollisionTable = new CollisionTable();
static public function Get() : CollisionTable {
return _instance;
}
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
interface ICC3DWorld {
function update(dtms : int) : void ;
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
import flash.geom.Vector3D;
//public
class SectorPhysics {
public function SectorPhysics() : void {
null;
}
public var sector : Sector;
public var wakeA : CC3DList;
public var wakeB : CC3DList;
public var sleepA : CC3DList;
public var sleepB : CC3DList;
public var sleepId : int;
public var ccCount : int;
public var staticPairList : CC3DPair;
public var minPair : CC3DPair;
public var pairList : CC3DPair;
protected var _lastPair : CC3DPair;
public var next : SectorPhysics;
protected var _deltaTime : Number;
public var _remainingTime : Number;
protected var _collisionTable : CollisionTable;
public function findClosestPair() : void {
var pair : CC3DPair;
var minT : Number = this._remainingTime;
this.minPair = null;
pair = this.staticPairList;
while(pair != null) {
if(pair.t < minT) {
this.minPair = pair;
minT = pair.t;
}
pair = pair.next;
}
pair = this.pairList;
while(pair != null) {
if(pair.willCollide(this._remainingTime) && pair.t < minT) {
this.minPair = pair;
minT = pair.t;
}
this._lastPair = pair;
pair = pair.next;
}
this._deltaTime = minT;
}
protected function _init(sec : Sector) : void {
this.wakeA = new CC3DList();
this.wakeB = new CC3DList();
this.sleepA = new CC3DList();
this.sleepB = new CC3DList();
this._reset();
this.sector = sec;
this._collisionTable = CollisionTable.Get();
sec.physics = this;
}
protected function _reset() : void {
this._deltaTime = 0;
this.ccCount = 0;
this.sleepId = -1;
}
public function integrateResolve() : void {
if(this.minPair != null) {
this.minPair.c1.staticCollision = null;
}
var deferedCollection : CC3DPair = this._integrate();
if(this.minPair != null) {
this.minPair.resolve();
}
var nextPair : CC3DPair;
if(this._lastPair != null) {
this._lastPair.next = deferedCollection;
deferedCollection = this.pairList;
}
while(deferedCollection != null) {
nextPair = deferedCollection.next;
deferedCollection.destroy();
deferedCollection = nextPair;
}
this._lastPair = null;
this.pairList = null;
}
protected function _integrate() : CC3DPair {
var pair : CC3DPair = this.staticPairList;
this._remainingTime -= this._deltaTime;
var nextPair : CC3DPair;
var deferedCollection : CC3DPair = null;
var lastPair : CC3DPair = null;
while(pair != null) {
nextPair = pair.next;
if(pair.c1.staticCollision == null) {
if(lastPair != null) lastPair.next = nextPair;
else {
this.staticPairList = nextPair;
}
pair.next = deferedCollection;
deferedCollection = pair;
pair = nextPair;
continue;
}
pair.t -= this._deltaTime;
lastPair = pair;
pair = nextPair;
}
var dt : Number = this._deltaTime - 1e-8;
this.wakeA._integrate(dt);
this.wakeB._integrate(dt);
this.sleepA._integrate(dt);
this.sleepB._integrate(dt);
return deferedCollection;
}
public function _notifyWake(remainingTime : Number) : void {
this._remainingTime = remainingTime;
this._deltaTime = remainingTime;
}
public function doCoarsePhase() : void {
this._updateList(this.wakeA);
this._collectMovingCollisionPairs();
}
protected function _collectMovingCollisionPairs() : void {
var h : CC3D = this.wakeA.head;
var v : CC3D;
while(h != null) {
v = h.next;
while(v != null) {
if(h.isAABBOverlapping(v)) {
var pair : CC3DPair = this._getCollisionPairFor(h,v);
pair.next = this.pairList;
this.pairList = pair;
}
v = v.next;
}
h = h.next;
}
}
protected function _updateList(list : CC3DList) : void {
var h : CC3D = list.head;
while(h != null) {
h._updateAABB(this._remainingTime);
h._updateVectors(this._remainingTime);
if(h.staticCollision == null) {
var staticPair : CC3DPair = (h._gotCollideWithWorld(this._remainingTime)?this._getStaticCollisionFor(h):null);
if(staticPair != null) {
var collDisplacement : flash.geom.Vector3D = h.collisionPoint.subtract(h.position);
var dirNorm : flash.geom.Vector3D = collDisplacement.clone();
dirNorm.normalize();
dirNorm.scaleBy(-(h.collider.radiusX));
collDisplacement.x = collDisplacement.x + dirNorm.x;
collDisplacement.y = collDisplacement.y + dirNorm.y;
collDisplacement.z = collDisplacement.z + dirNorm.z;
var lengther : Number = (Vector3DUtils.lengthOf(collDisplacement));
var t : Number = lengther / Vector3DUtils.lengthOf(h.displacement) * this._remainingTime;
if(PMath.isNaN(t)) {
throw new Error("IT nAN!:" + this._remainingTime + " , " + t) + ", " + Vector3DUtils.lengthOf(collDisplacement) + ", " + collDisplacement;
t = 0;
}
if(t < 0) {
t = 0;
throw new Error("Delta time is lower than zero!");
}
staticPair.next = this.staticPairList;
this.staticPairList = staticPair;
staticPair.c1 = h;
staticPair.t = t;
h.staticCollision = staticPair;
}
}
h = h.next;
}
}
protected function _getStaticCollisionFor(h : CC3D) : CC3DPair {
var pair : CC3DPair = this._collisionTable.getStaticPair(h.collisionGroupId);
pair.c1 = h;
return pair;
}
protected function _getCollisionPairFor(h : CC3D,v : CC3D) : CC3DPair {
var pair : CC3DPair = this._collisionTable.getPair(h.collisionGroupId,v.collisionGroupId);
pair.c1 = h;
pair.c2 = v;
return pair;
}
public function addChild(cc : CC3D) : void {
this.ccCount++;
var list : CC3DList = (cc.sleep?(cc.typeA?this.sleepA:this.sleepB):(cc.typeA?this.wakeA:this.wakeB));
list.prepend(cc);
}
public function removeChild(cc : CC3D) : void {
cc._removeFromParent();
cc._dispose();
this.ccCount--;
}
public function dispose() : void {
this.sector.physics = null;
this.sector = null;
this.minPair = null;
this.next = collector;
SectorPhysics.collector = this;
}
public function getDeltaTime() : Number {
return this._deltaTime;
}
static public var EPSILON : Number = 1e-8;
static public var collector : SectorPhysics;
static public function Create(sec : Sector) : SectorPhysics {
var result : SectorPhysics;
if(SectorPhysics.collector != null) {
result = collector;
result.sector = sec;
result._reset();
sec.physics = result;
SectorPhysics.collector = collector.next;
}
else {
result = new SectorPhysics();
result._init(sec);
}
return result;
}
}
//}
//package alternsector.physics.adaptors.alternativa3d.ccd {
//public
class SectorWorld implements ICC3DWorld{
public function SectorWorld(sleepBufferSize : int = 64) : void {
this._sleepCount = 0;
this._sleepingSectors = new Vector.<SectorPhysics>(sleepBufferSize,true);
}
protected var _awakeSectors : SectorPhysics;
protected var _sleepingSectors : Vector.<SectorPhysics>;
protected var _sleepCount : int;
protected var _traceError : Error;
public function getTraceError() : Error {
var err : Error = this._traceError;
if(this._traceError != null) this._traceError = null;
return err;
}
public function update(dtms : int) : void {
var doSleep : Boolean;
var doDispose : Boolean;
var elapsed : Number = dtms * (1 / 1000);
var iteration : int = 0;
var sector : SectorPhysics;
var nextSector : SectorPhysics;
var tailSector : SectorPhysics;
while(this._sleepCount > 0) {
sector = this._sleepingSectors[--this._sleepCount];
if(sector.sleepId < 0) throw new Error("ALREADY waken!");
sector.sleepId = -1;
sector.next = this._awakeSectors;
this._awakeSectors = sector;
}
sector = this._awakeSectors;
while(sector != null) {
sector._notifyWake(elapsed);
sector = sector.next;
}
while(this._awakeSectors != null) {
sector = this._awakeSectors;
while(sector != null) {
sector.doCoarsePhase();
sector = sector.next;
}
sector = this._awakeSectors;
tailSector = null;
while(sector != null) {
nextSector = sector.next;
sector.findClosestPair();
sector.integrateResolve();
doSleep = false;
doDispose = false;
if(doSleep = sector._remainingTime <= 0) {
if(tailSector != null) {
tailSector.next = nextSector;
}
else {
this._awakeSectors = nextSector;
}
if(doSleep) {
this.sleepSector(sector);
sector.next = null;
}
else {
throw new Error("THIs should not happen as of yet!");
sector.dispose();
}
sector = nextSector;
continue;
}
tailSector = sector;
sector = nextSector;
}
iteration++;
if(iteration > 100) {
this._traceError = new Error("ERROR: MAx iterations reached!:" + tailSector._remainingTime + " left from: " + elapsed + ", +=" + tailSector.getDeltaTime());
break;
}
}
}
public function addCC3D(cc : CC3D) : void {
var sector : Sector = cc.sector;
var sectorPhysics : SectorPhysics;
if(sector.physics != null) {
sectorPhysics = sector.physics;
}
else {
sectorPhysics = SectorPhysics.Create(sector);
sectorPhysics.next = this._awakeSectors;
this._awakeSectors = sectorPhysics;
}
sectorPhysics.addChild(cc);
}
protected function _wakeSector(sectorPhysics : SectorPhysics) : void {
var tailIndex : int = this._sleepCount - 1;
if(sectorPhysics.sleepId < 0) throw new Error("Sector isn't sleeping!");
var secPhysics2 : SectorPhysics;
if(sectorPhysics.sleepId != tailIndex) {
secPhysics2 = this._sleepingSectors[tailIndex];
secPhysics2.sleepId = sectorPhysics.sleepId;
this._sleepingSectors[secPhysics2.sleepId] = secPhysics2;
this._sleepingSectors[tailIndex] = null;
}
else {
this._sleepingSectors[sectorPhysics.sleepId] = null;
}
this._sleepCount--;
sectorPhysics.sleepId = -1;
sectorPhysics.next = this._awakeSectors;
this._awakeSectors = sectorPhysics;
}
protected function sleepSector(sectorPhysics : SectorPhysics) : void {
sectorPhysics.sleepId = this._sleepCount;
this._sleepingSectors[this._sleepCount++] = sectorPhysics;
}
public function removeCC3D(cc : CC3D) : void {
var sectorNode : SectorPhysics = cc.sector.physics;
sectorNode.removeChild(cc);
if(sectorNode.ccCount == 0) {
sectorNode.dispose();
}
}
static protected var MAX_ITERATIONS : int = 100;
static protected var MS : Number = 1 / 1000;
}
//}
// -- Vector3DUtils
//package alternsector.physics.adaptors.utils {
import flash.geom.Vector3D;
//public
class Vector3DUtils {
static public function distanceBetween(p1 : flash.geom.Vector3D,p2 : flash.geom.Vector3D) : Number {
var dx : Number = p2.x - p1.x;
var dy : Number = p2.y - p1.y;
var dz : Number = p2.z - p1.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
static public function lengthOf(vec : flash.geom.Vector3D) : Number {
return Math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
}
static public function getNewScaledVector(src : flash.geom.Vector3D,scalar : Number) : flash.geom.Vector3D {
return new flash.geom.Vector3D(src.x * scalar,src.y * scalar,src.z * scalar);
}
}
//}
//package alternsector.math {
//public
class PMath {
static public function isNaN(val : Number) : Boolean {
return val != val;
}
static public function min(x : int,y : int) : int {
return (x < y?x:y);
}
static public function minF(x : Number,y : Number) : Number {
return (x < y?x:y);
}
static public function max(x : int,y : int) : int {
return (x > y?x:y);
}
static public function maxF(x : Number,y : Number) : Number {
return (x > y?x:y);
}
}
//}