render arena

This commit is contained in:
sm
2015-08-27 22:29:50 -07:00
parent 6bad86730e
commit 33f94af784
10 changed files with 3954 additions and 1 deletions
+431
View File
@@ -0,0 +1,431 @@
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.OBJLoader = function ( manager ) {
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
};
THREE.OBJLoader.prototype = {
constructor: THREE.OBJLoader,
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var loader = new THREE.XHRLoader( scope.manager );
loader.setCrossOrigin( this.crossOrigin );
loader.load( url, function ( text ) {
onLoad( scope.parse( text ) );
} );
},
parse: function ( text ) {
function vector( x, y, z ) {
return new THREE.Vector3( x, y, z );
}
function uv( u, v ) {
return new THREE.Vector2( u, v );
}
function face3( a, b, c, normals ) {
return new THREE.Face3( a, b, c, normals );
}
var object = new THREE.Object3D();
var geometry, material, mesh;
// create mesh if no objects in text
if ( /^o /gm.test( text ) === false ) {
geometry = new THREE.Geometry();
material = new THREE.MeshLambertMaterial();
mesh = new THREE.Mesh( geometry, material );
object.add( mesh );
}
var vertices = [];
var verticesCount = 0;
var normals = [];
var uvs = [];
// v float float float
var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
// vn float float float
var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
// vt float float
var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
// f vertex vertex vertex ...
var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/;
// f vertex/uv vertex/uv vertex/uv ...
var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/;
// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/;
// f vertex//normal vertex//normal vertex//normal ...
var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/
//
var lines = text.split( '\n' );
for ( var i = 0; i < lines.length; i ++ ) {
var line = lines[ i ];
line = line.trim();
var result;
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
continue;
} else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
vertices.push( vector(
parseFloat( result[ 1 ] ),
parseFloat( result[ 2 ] ),
parseFloat( result[ 3 ] )
) );
} else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
normals.push( vector(
parseFloat( result[ 1 ] ),
parseFloat( result[ 2 ] ),
parseFloat( result[ 3 ] )
) );
} else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
// ["vt 0.1 0.2", "0.1", "0.2"]
uvs.push( uv(
parseFloat( result[ 1 ] ),
parseFloat( result[ 2 ] )
) );
} else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
// ["f 1 2 3", "1", "2", "3", undefined]
if ( result[ 4 ] === undefined ) {
geometry.vertices.push(
vertices[ parseInt( result[ 1 ] ) - 1 ],
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 3 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount ++,
verticesCount ++,
verticesCount ++
) );
} else {
geometry.vertices.push(
vertices[ parseInt( result[ 1 ] ) - 1 ],
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 3 ] ) - 1 ],
vertices[ parseInt( result[ 4 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount,
verticesCount + 1,
verticesCount + 3
) );
geometry.faces.push( face3(
verticesCount + 1,
verticesCount + 2,
verticesCount + 3
) );
verticesCount += 4;
}
} else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
// ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
if ( result[ 10 ] === undefined ) {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 5 ] ) - 1 ],
vertices[ parseInt( result[ 8 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount ++,
verticesCount ++,
verticesCount ++
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 3 ] ) - 1 ],
uvs[ parseInt( result[ 6 ] ) - 1 ],
uvs[ parseInt( result[ 9 ] ) - 1 ]
] );
} else {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 5 ] ) - 1 ],
vertices[ parseInt( result[ 8 ] ) - 1 ],
vertices[ parseInt( result[ 11 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount,
verticesCount + 1,
verticesCount + 3
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 3 ] ) - 1 ],
uvs[ parseInt( result[ 6 ] ) - 1 ],
uvs[ parseInt( result[ 12 ] ) - 1 ]
] );
geometry.faces.push( face3(
verticesCount + 1,
verticesCount + 2,
verticesCount + 3
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 6 ] ) - 1 ],
uvs[ parseInt( result[ 9 ] ) - 1 ],
uvs[ parseInt( result[ 12 ] ) - 1 ]
] );
verticesCount += 4;
}
} else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
// ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
if ( result[ 13 ] === undefined ) {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 6 ] ) - 1 ],
vertices[ parseInt( result[ 10 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount ++,
verticesCount ++,
verticesCount ++,
[
normals[ parseInt( result[ 4 ] ) - 1 ],
normals[ parseInt( result[ 8 ] ) - 1 ],
normals[ parseInt( result[ 12 ] ) - 1 ]
]
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 3 ] ) - 1 ],
uvs[ parseInt( result[ 7 ] ) - 1 ],
uvs[ parseInt( result[ 11 ] ) - 1 ]
] );
} else {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 6 ] ) - 1 ],
vertices[ parseInt( result[ 10 ] ) - 1 ],
vertices[ parseInt( result[ 14 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount,
verticesCount + 1,
verticesCount + 3,
[
normals[ parseInt( result[ 4 ] ) - 1 ],
normals[ parseInt( result[ 8 ] ) - 1 ],
normals[ parseInt( result[ 16 ] ) - 1 ]
]
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 3 ] ) - 1 ],
uvs[ parseInt( result[ 7 ] ) - 1 ],
uvs[ parseInt( result[ 15 ] ) - 1 ]
] );
geometry.faces.push( face3(
verticesCount + 1,
verticesCount + 2,
verticesCount + 3,
[
normals[ parseInt( result[ 8 ] ) - 1 ],
normals[ parseInt( result[ 12 ] ) - 1 ],
normals[ parseInt( result[ 16 ] ) - 1 ]
]
) );
geometry.faceVertexUvs[ 0 ].push( [
uvs[ parseInt( result[ 7 ] ) - 1 ],
uvs[ parseInt( result[ 11 ] ) - 1 ],
uvs[ parseInt( result[ 15 ] ) - 1 ]
] );
verticesCount += 4;
}
} else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
// ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
if ( result[ 10 ] === undefined ) {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 5 ] ) - 1 ],
vertices[ parseInt( result[ 8 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount ++,
verticesCount ++,
verticesCount ++,
[
normals[ parseInt( result[ 3 ] ) - 1 ],
normals[ parseInt( result[ 6 ] ) - 1 ],
normals[ parseInt( result[ 9 ] ) - 1 ]
]
) );
} else {
geometry.vertices.push(
vertices[ parseInt( result[ 2 ] ) - 1 ],
vertices[ parseInt( result[ 5 ] ) - 1 ],
vertices[ parseInt( result[ 8 ] ) - 1 ],
vertices[ parseInt( result[ 11 ] ) - 1 ]
);
geometry.faces.push( face3(
verticesCount,
verticesCount + 1,
verticesCount + 3,
[
normals[ parseInt( result[ 3 ] ) - 1 ],
normals[ parseInt( result[ 6 ] ) - 1 ],
normals[ parseInt( result[ 12 ] ) - 1 ]
]
) );
geometry.faces.push( face3(
verticesCount + 1,
verticesCount + 2,
verticesCount + 3,
[
normals[ parseInt( result[ 6 ] ) - 1 ],
normals[ parseInt( result[ 9 ] ) - 1 ],
normals[ parseInt( result[ 12 ] ) - 1 ]
]
) );
verticesCount += 4;
}
} else if ( /^o /.test( line ) ) {
// object
geometry = new THREE.Geometry();
material = new THREE.MeshLambertMaterial();
mesh = new THREE.Mesh( geometry, material );
mesh.name = line.substring( 2 ).trim();
object.add( mesh );
verticesCount = 0;
} else if ( /^g /.test( line ) ) {
// group
} else if ( /^usemtl /.test( line ) ) {
// material
material.name = line.substring( 7 ).trim();
} else if ( /^mtllib /.test( line ) ) {
// mtl file
} else if ( /^s /.test( line ) ) {
// smooth shading
} else {
// console.log( "THREE.OBJLoader: Unhandled line " + line );
}
}
for ( var i = 0, l = object.children.length; i < l; i ++ ) {
var geometry = object.children[ i ].geometry;
geometry.computeCentroids();
geometry.computeFaceNormals();
geometry.computeBoundingSphere();
}
return object;
}
};
+607
View File
@@ -0,0 +1,607 @@
/**
* @author Eberhard Graether / http://egraether.com/
* @author Mark Lundin / http://mark-lundin.com
*/
THREE.TrackballControls = function ( object, domElement ) {
var _this = this;
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;
this.noRotate = false;
this.noZoom = false;
this.noPan = false;
this.noRoll = true;
this.staticMoving = true;
this.dynamicDampingFactor = 0.2;
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
// internals
this.target = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var _state = STATE.NONE,
_prevState = STATE.NONE,
_eye = new THREE.Vector3(),
_rotateStart = new THREE.Vector3(),
_rotateEnd = new THREE.Vector3(),
_zoomStart = new THREE.Vector2(),
_zoomEnd = new THREE.Vector2(),
_touchZoomDistanceStart = 0,
_touchZoomDistanceEnd = 0,
_panStart = new THREE.Vector2(),
_panEnd = new THREE.Vector2();
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.up0 = this.object.up.clone();
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start'};
var endEvent = { type: 'end'};
// methods
this.handleResize = function () {
if ( this.domElement === document ) {
this.screen.left = 0;
this.screen.top = 0;
this.screen.width = window.innerWidth;
this.screen.height = window.innerHeight;
} else {
this.screen = this.domElement.getBoundingClientRect();
// adjustments come from similar code in the jquery offset() function
var d = this.domElement.ownerDocument.documentElement
this.screen.left += window.pageXOffset - d.clientLeft
this.screen.top += window.pageYOffset - d.clientTop
}
};
this.handleEvent = function ( event ) {
if ( typeof this[ event.type ] == 'function' ) {
this[ event.type ]( event );
}
};
this.getMouseOnScreen = function ( pageX, pageY, vector ) {
return vector.set(
( pageX - _this.screen.left ) / _this.screen.width,
( pageY - _this.screen.top ) / _this.screen.height
);
};
this.getMouseProjectionOnBall = (function(){
var objectUp = new THREE.Vector3(),
mouseOnBall = new THREE.Vector3();
return function ( pageX, pageY, projection ) {
mouseOnBall.set(
( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5),
0.0
);
var length = mouseOnBall.length();
if ( _this.noRoll ) {
if ( length < Math.SQRT1_2 ) {
mouseOnBall.z = Math.sqrt( 1.0 - length*length );
} else {
mouseOnBall.z = .5 / length;
}
} else if ( length > 1.0 ) {
mouseOnBall.normalize();
} else {
mouseOnBall.z = Math.sqrt( 1.0 - length * length );
}
_eye.copy( _this.object.position ).sub( _this.target );
projection.copy( _this.object.up ).setLength( mouseOnBall.y )
projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
projection.add( _eye.setLength( mouseOnBall.z ) );
return projection;
}
}());
this.rotateCamera = (function(){
var axis = new THREE.Vector3(),
quaternion = new THREE.Quaternion();
return function () {
var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
if ( angle ) {
axis.crossVectors( _rotateStart, _rotateEnd ).normalize();
angle *= _this.rotateSpeed;
quaternion.setFromAxisAngle( axis, -angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_rotateEnd.applyQuaternion( quaternion );
if ( _this.staticMoving ) {
_rotateStart.copy( _rotateEnd );
} else {
quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
_rotateStart.applyQuaternion( quaternion );
}
}
}
}());
this.zoomCamera = function () {
if ( _state === STATE.TOUCH_ZOOM ) {
var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
_eye.multiplyScalar( factor );
} else {
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
}
};
this.panCamera = (function(){
var mouseChange = new THREE.Vector2(),
objectUp = new THREE.Vector3(),
pan = new THREE.Vector3();
return function () {
mouseChange.copy( _panEnd ).sub( _panStart );
if ( mouseChange.lengthSq() ) {
mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
_this.object.position.add( pan );
_this.target.add( pan );
if ( _this.staticMoving ) {
_panStart.copy( _panEnd );
} else {
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
}
}
}
}());
this.checkDistances = function () {
if ( !_this.noZoom || !_this.noPan ) {
if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
}
if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
}
}
};
this.update = function () {
var old = new THREE.Vector3();
old.copy(_this.object.position);
_eye.subVectors( _this.object.position, _this.target );
if ( !_this.noRotate ) {
_this.rotateCamera();
}
if ( !_this.noZoom ) {
_this.zoomCamera();
}
if ( !_this.noPan ) {
_this.panCamera();
}
_this.object.position.addVectors( _this.target, _eye );
// HACKERBOTS CHANGES
// Up is always up
_this.object.up.copy( _this.up0 );
// Stay above ground
if (_this.object.position.z <= 20){
_this.object.position.copy(old);
}
if (_this.object.position.z >= 500){
_this.object.position.copy(old);
}
// END HACKERBOTS
_this.checkDistances();
_this.object.lookAt( _this.target );
if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
};
this.reset = function () {
_state = STATE.NONE;
_prevState = STATE.NONE;
_this.target.copy( _this.target0 );
_this.object.position.copy( _this.position0 );
_this.object.up.copy( _this.up0 );
_eye.subVectors( _this.object.position, _this.target );
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
};
// listeners
function keydown( event ) {
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
_prevState = _state;
if ( _state !== STATE.NONE ) {
return;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
_state = STATE.ROTATE;
} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
_state = STATE.ZOOM;
} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
_state = STATE.PAN;
}
}
function keyup( event ) {
if ( _this.enabled === false ) return;
_state = _prevState;
window.addEventListener( 'keydown', keydown, false );
}
function mousedown( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.NONE ) {
_state = event.button;
}
if ( _state === STATE.ROTATE && !_this.noRotate ) {
_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
_rotateEnd.copy(_rotateStart)
} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
_this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
_zoomEnd.copy(_zoomStart);
} else if ( _state === STATE.PAN && !_this.noPan ) {
_this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
_panEnd.copy(_panStart)
}
document.addEventListener( 'mousemove', mousemove, false );
document.addEventListener( 'mouseup', mouseup, false );
_this.dispatchEvent( startEvent );
}
function mousemove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.ROTATE && !_this.noRotate ) {
_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
_this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
} else if ( _state === STATE.PAN && !_this.noPan ) {
_this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
}
}
function mouseup( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state = STATE.NONE;
document.removeEventListener( 'mousemove', mousemove );
document.removeEventListener( 'mouseup', mouseup );
_this.dispatchEvent( endEvent );
}
function mousewheel( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta / 40;
} else if ( event.detail ) { // Firefox
delta = - event.detail / 3;
}
_zoomStart.y += delta * 0.01;
_this.dispatchEvent( startEvent );
_this.dispatchEvent( endEvent );
}
function touchstart( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
break;
case 2:
_state = STATE.TOUCH_ZOOM;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
break;
case 3:
_state = STATE.TOUCH_PAN;
_panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
break;
default:
_state = STATE.NONE;
}
_this.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
break;
case 2:
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
break;
case 3:
_this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
break;
default:
_state = STATE.NONE;
}
}
function touchend( event ) {
if ( _this.enabled === false ) return;
switch ( event.touches.length ) {
case 1:
_rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
break;
case 2:
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
break;
case 3:
_panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
break;
}
_state = STATE.NONE;
_this.dispatchEvent( endEvent );
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'mousedown', mousedown, false );
this.domElement.addEventListener( 'mousewheel', mousewheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
this.domElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
window.addEventListener( 'keydown', keydown, false );
window.addEventListener( 'keyup', keyup, false );
this.handleResize();
};
THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
+148
View File
@@ -0,0 +1,148 @@
var savebot = function(name, callback){
data = {
"id": $("#robot_picker").val(),
"code": editor.getValue()
};
if (name){
data.name = name;
}
$.ajax({
method:"POST",
data: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
url:"/api/v1/bot/" + $("#robot_picker").val() + "/",
success: function(data){
var d = new Date();
$("#lastsave").html("Saved at " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds());
if (callback){
callback();
}
}
});
};
var newbot = function(){
data = {
"code": editor.getValue(),
};
$.ajax({
method:"POST",
data: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
url:"/api/v1/bot/" + $("#robot_picker").val() + "/",
success: function(data){
console.log(data);
if (data.id){
$("#robot_picker").val(data.id);
}
// I'm lazy
set_current_bot(data.id);
location.reload();
}
});
};
var set_current_bot = function(name){
var queryParameters = {}, queryString = location.search.substring(1),
re = /([^&=]+)=([^&]*)/g, m;
while (m = re.exec(queryString)) {
queryParameters[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
queryParameters['bot'] = name;
history.pushState(null, null, "?"+ $.param(queryParameters));
};
$(document).ready(function(){
$("#error").hide();
editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
function resizeAce() {
return $('#editor').height($(window).height() - $("#editor").offset().top - 20);
};
$(window).resize(resizeAce);
resizeAce();
var gorobots = gorobots_init({}, true);
$("#robot_picker").change(function(e){
$.ajax({
url:"/api/v1/bot/" + $("#robot_picker").val(),
success: function(data){
editor.setValue(data.code);
set_current_bot($("#robot_picker").val());
}
});
});
$("#save").click(function(e){
savebot();
});
$("#new").click(function(e){
newbot();
});
var renaming = false;
$("#rename").click(function(e){
if (renaming){
// POST new name to server
savebot($("#newname").val(), function(){
// BE LAZY
location.reload();
});
$("#rename-block").hide();
$("#rename").html("Rename Bot");
renaming = false;
} else {
$("#rename-block").show();
$("#rename").html("Ok");
renaming = true;
}
});
$.ajax({
url:"/api/v1/bot/1/",
success: function(data){
editor.setValue(data.code);
$("#robot_picker").val(1);
}
});
$('#starter').click(function() {
savebot();
$('#tabs a[href="#arena"]').tab('show');
var url = "ws://{{ server.hostname }}:8666/ws/";
gorobots.set_spectator(false);
gorobots.set_server(
url,
"{{ game }}"
);
});
$('#watch').click(function() {
savebot();
$('#tabs a[href="#arena"]').tab('show');
var url = "ws://{{ server.hostname }}:8666/ws/";
gorobots.set_spectator(true);
gorobots.set_server(
url,
"{{ game }}"
);
});
$('#tabs a[href="#code"]').tab('show');
});
+1 -1
View File
@@ -11,7 +11,7 @@ function loadGames() {
$("#games").empty();
for (var key in data) {
var name = data[key]["id"];
var s = "<li><a href='/ui/game/" + name + "/'>join</a> " + name + "</li>";
var s = "<li><a href='/ui/arena/?id=" + name + "'>join</a> " + name + "</li>";
$("#games").append(s);
}
});
+561
View File
@@ -0,0 +1,561 @@
var gorobots_init = function gorobots(my, webgl){
my.width = null;
my.height = null;
my.server = "ws://localhost:8666/ws/";
my.websocket = null;
my.id = null;
my.connection_retry = 3000;
my.state = null;
my.spectator = false;
my.debug = false;
my.debug_draw = true;
var grey = "#999999";
var colors = [
"#222222", // Grey
"#000099",
"#009900",
"#990000",
"#009999",
"#990099",
"#999900",
"#000044",
"#004400",
"#440000",
"#004444",
"#440044",
"#444400",
"#0000cc",
"#00cc00",
"#cc0000",
"#00cccc",
"#cc00cc",
"#cccc00"
];
my.connect = function(server){
connection = new WebSocket(server, null);
my.websocket = connection;
console.log(connection);
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
connection.onopen = function(){
console.log("Connected to " + server);
my.send_game_request();
};
connection.onclose = function(){
my.id = null;
my.state = null;
my.renderer.reset();
console.log("Connection Closed");
if (my.connection_retry > 0){
// Retry every few seconds
console.log("Lost Connection: " + server);
setTimeout(function(){
my.websocket = my.connect(my.server);
}, my.connection_retry);
}
};
connection.onmessage = function (e) {
// Handshake protocol, we must establish a game ID and send
// the robot config for approval before we can play
new_data = JSON.parse(e.data);
if (my.state === null) {
if (new_data.type == "idreq") {
if ('id' in new_data){
my.id = new_data['id'];
console.log("Assigned ID " + my.id + " by server");
} else {
console.log("server failed to send us an id");
}
my.send_client_id();
my.state = "gameparam";
// send supported encodings
my.websocket.send(JSON.stringify(["json"]));
}
} else if (my.state == "gameparam"){
if (new_data.type == "gameparam") {
// > [OK | FULL | NOT AUTH], board size, game params
my.parse_game_params(new_data);
my.state = "handshake";
my.setup_robot();
}
} else if (my.state == "handshake") {
// We have game details, make the render target
console.log("completed handshake");
console.log(new_data);
if('success' in new_data) {
if (!new_data.success){
alert("Your Robot Config is not valid, adjust your point distributions and try again");
return false;
}
}
my.renderer.setup_render_target(my.width, my.height, my.width, my.height);
my.state = "play";
} else if(my.state == "play") {
if (new_data.type == "handshake") {
// This is the handshake response, we've been assigned an ID
// and are in the game (or TODO: in a lobby).
console.log("we got a handshake when we expect a boardstate packet");
} else if (new_data.type == "stats") {
// XXX: need to actually assign this to the player/robots
// ...
console.log("\\o/ we have stats: " + JSON.stringify(new_data));
} else if (new_data.type == "boardstate") {
my.renderer.begin_frame(new_data['turn']);
var draw_list = my.process_gameplay_packet(new_data);
my.renderer.render(draw_list);
my.renderer.end_frame();
} else if (new_data.type == "gameover") {
my.renderer.reset();
} else {
console.error("unexpected state ...");
}
}
};
return connection;
};
my.prepare_data = function(data){
// get all the robot id's in the scanner list and find them in the
// overall robot list so we can pass them to the robot code
if (data['my_robots']){
for (var i=0; i < data['my_robots'].length; i++){
var bot = data['my_robots'][i];
bot.messages = data.messages;
for(var p in bot['scanners']){
var bot_id = bot['scanners'][p]['id'];
// find the scanner object in the pool of objects
if (bot['scanners'][p]['type'] == 'robot'){
for (var b in data['robots']){
if (data['robots'][b]['id'] == bot_id){
bot['scanners'][p] = data['robots'][b];
bot['scanners'][p].type = "robot";
bot['scanners'][p].friend = false;
}
}
for (var mybot in data['my_robots']){
if (data['my_robots'][mybot].id == bot['scanners'][p].id){
bot['scanners'][p].friend = true;
}
}
}
if (bot['scanners'][p]['type'] == 'projectile'){
for (var b in data['projectiles']){
if (data['projectiles'][b]['id'] == bot_id){
bot['scanners'][p] = data['projectiles'][b];
bot['scanners'][p].type = "projectile";
}
}
}
}
}
}
return data;
};
my.process_gameplay_packet = function(new_data){
my.stats.begin();
my.renderer.clear_debug();
var draw_list = [];
if (new_data.reset){
// my.setup_robot();
my.renderer.reset();
return;
}
if (!my.paused) {
var debug_div = document.getElementById("raw-debug");
if (debug_div){
debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
}
}
// Prep the data
new_data = my.prepare_data(new_data);
// Dump the contents of the message from the server
if (!my.paused) {
var debug_div = document.getElementById("debug");
if (debug_div){
debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
}
}
// Update the robots and build a list of what to draw
var my_robots = new_data['my_robots'];
var robots = new_data['robots'];
var all_bots = new_data['all_bots'];
my.all_bots = [];
my.my_bots = my_robots;
// Set the list of players and assign colors
var players = "";
var col = 2;
for (i=0; i < all_bots.length; i++){
my.all_bots.push(all_bots[i]['robot_id']);
// I hate javascript
var health_str = "";
if (all_bots[i]['health'] < 0) {
health_str = "000";
all_bots[i]['color'] = grey;
}
else if (all_bots[i]['health'] < 10) {
health_str = "00" + all_bots[i]['health'];
all_bots[i]['color'] = colors[col];
}
else if (all_bots[i]['health'] < 100){
health_str = "0" + all_bots[i]['health'];
all_bots[i]['color'] = colors[col];
}
else{
health_str = all_bots[i]['health'];
all_bots[i]['color'] = colors[col];
}
var mine = false;
for (var mybot in my.my_bots){
if (my.my_bots[mybot].id == all_bots[i]['robot_id']){
mine = true;
all_bots[i]['color'] = colors[1];
}
}
if (!mine){
col++;
}
players += ("<span style='color: " +
all_bots[i]['color'] +
";'>&nbsp&nbsp" +
all_bots[i]['robot_id'] +
" [" + health_str + "]</span>");
}
var players_div = document.getElementById("players");
if (players_div){
players_div.innerHTML = players;
}
// Now let's deal with all the things in the world
var i = 0;
if (my_robots){
instructions = {};
for (i=0; i < my_robots.length; i++){
if (my_robots[i].health > 0) {
instructions[my_robots[i]['id']] = my.update_robot(my_robots[i], new_data['obj']);
}
for (var bot in all_bots){
if (all_bots[bot]['robot_id'] == my_robots[i]['id']){
col = all_bots[bot]['color'];
}
}
if (my_robots[i].health <= 0){
col = grey;
}
if ("position" in my_robots[i]){
my_robots[i]['color'] = col;
my_robots[i]['type'] = 'robot';
draw_list.push(my_robots[i]);
}
}
if (my.websocket){
var payload = JSON.stringify(instructions);
// console.log(payload);
my.websocket.send(payload);
}
if (my.target < 0 && !my.spectator){
console.log("Watching " + my.my_bots[0].id);
my.renderer.set_focus_robot(my.my_bots[0].id);
my.target = 0;
}
}
if (robots){
for (i=0; i < robots.length; i++){
// Get the color from all_bots
var col = grey;
for (var bot in all_bots){
if (all_bots[bot]['robot_id'] == robots[i]['id']){
col = all_bots[bot]['color'];
}
}
if (robots[i].health <= 0){
col = grey;
}
if ("position" in robots[i]){
robots[i]['color'] = col;
robots[i]['type'] = 'robot';
draw_list.push(robots[i]);
}
}
}
// Draw the projecticles
var projectiles = new_data['projectiles'];
if (projectiles){
for (i=0; i < projectiles.length; i++){
if ("position" in projectiles[i]){
projectiles[i]['type'] = 'bullet';
draw_list.push(projectiles[i]);
}
}
}
// Draw the projecticles
var splosions = new_data['splosions'];
if (splosions){
for (i=0; i < splosions.length; i++){
if ("position" in splosions[i]){
splosions[i]['type'] = 'explosion';
draw_list.push(splosions[i]);
}
}
}
// Draw the objects
var obj = new_data['objects'];
if (obj){
for (i=0; i < obj.length; i++){
var o = {
bounds : obj[i]
};
o['type'] = "object";
o['position'] = {x: obj[i][0], y: obj[i][1]};
draw_list.push(o);
}
}
my.stats.end();
return draw_list;
};
my.eval_input = function( input, output ){
var theResult, evalSucceeded;
try{
theResult = eval( input );
evalSucceeded = true;
}
catch(e){
output.innerHTML = e;
}
if ( evalSucceeded ) {
output.innerHTML = "";
// console.log("OK");
}
return theResult;
};
my.get_robot_code = function(){
var robot_code = editor.getSession().getValue();
var output = document.getElementById('output');
var code = "( " + robot_code + " )";
var rc = my.eval_input(code, output);
return rc.call(this);
};
my.send_game_request = function() {
game = {
"id": my.game_id,
};
console.log(my.websocket.readyState);
my.websocket.send(JSON.stringify(game));
console.log("sent clientid: " + JSON.stringify(game));
};
my.send_client_id = function() {
client_id = {
"type": my.spectator ? "spectator" : "robot",
"name": "dummy",
"id": "24601",
"useragent": "gorobots.js",
};
my.websocket.send(JSON.stringify(client_id));
};
my.parse_game_params = function(params) {
// TODO: flesh out validation?
my.width = new_data.boardsize.width;
my.height = new_data.boardsize.height;
return true;
};
my.setup_robot = function(){
var robot = my.get_robot_code();
if ('setup' in robot){
var map = {"width": my.width, "height": my.height};
var config = {
"stats": robot.setup(map),
"id": my.id
};
// console.log(config);
if (!my.spectator) {
my.websocket.send(JSON.stringify(config));
}
}
};
my.update_robot = function(data, objects){
var robot = my.get_robot_code();
var map = {"width": my.width, "height": my.height, "obj": objects};
var instructions = null;
if ('update' in robot){
var dbg = {
"msg" : function(x, y, msg){
my.renderer.debug_label(x,y,msg);
},
"msg_screen" : function(x, y, msg){
my.renderer.debug_label_screen(x,y,msg);
},
"line" : function(x, y, x2, y2){
my.renderer.debug_line(x,y,x2,y2);
},
};
instructions = robot.update(data, map, dbg);
}
return instructions;
};
my.set_spectator = function(s){
my.spectator = s;
my.target=-1;
$('body').keypress(function(e){
if (my.renderer && my.renderer.canvas.clientWidth === 0){
// dont respond to key events when canvas is not in view
return;
}
if(e.which == 116){
// t
if (my.spectator){
my.target++;
if( my.target < my.all_bots.length){
console.log("Watching " + my.all_bots[my.target]);
my.renderer.set_focus_robot(my.all_bots[my.target]);
}
else {
my.target = -1;
my.renderer.set_focus_robot(null);
}
} else {
my.target++;
if( my.target < my.my_bots.length){
console.log(my.my_bots);
console.log(my.my_bots[my.target].id);
console.log("Watching " + my.my_bots[my.target].id);
my.renderer.set_focus_robot(my.my_bots[my.target].id);
}
else {
my.target = -1;
my.renderer.set_focus_robot(null);
}
}
}
});
};
my.set_server = function(server_name, game_id){
my.server = server_name;
my.game_id = game_id;
my.state = null;
if (my.websocket){
console.log("Switching Server: " + my.server);
my.websocket.close();
my.renderer.shutdown();
}
else{
console.log("Setting Server: " + my.server);
my.websocket = my.connect(my.server);
}
};
my.init = function(){
console.log("Welcome to GoRobots");
if (webgl){
my.renderer = gorobots_render_webgl();
}
else{
my.renderer = gorobots_render_canvas();
}
my.renderer.init("battlefield");
my.stats = new Stats();
my.stats.setMode(0); // 0: fps, 1: ms
// Align top-left
my.stats.domElement.style.position = 'absolute';
my.stats.domElement.style.left = '5px';
my.stats.domElement.style.top = '120px';
document.body.appendChild( my.stats.domElement );
$(my.stats.domElement).hide();
my.paused = false;
$('body').keypress(function(e){
// alert(e.which);
if(e.which == 112){
// p
my.paused = !my.paused;
}
if (my.renderer && my.renderer.canvas.clientWidth === 0){
// dont respond to key events when canvas is not in view
return;
}
if(e.which == 100){
// d
$(my.stats.domElement).toggle();
}
});
};
my.init();
return my;
};
+608
View File
@@ -0,0 +1,608 @@
var gorobots_render_webgl = function(){
var my = {};
var grey = "rgba(0, 0, 0, 0.5)";
var red = "rgba(200, 0, 0, 0.5)";
// Tracking last 50 frame lengths
var d = new Date();
my.delta_history = [];
my.last_frame_time = d.getTime();
my.delta = 0;
my.width = null;
my.height = null;
my.y_base = 16; // The top status bar
my.scene = null;
// Tracking the objects in the scene
my.robots = {};
my.objects = {};
my.dom_id = null;
my.target = null;
my.init = function(dom_id) {
my.dom_id = dom_id;
my.canvas = document.getElementById(dom_id);
$('body').keypress(function(e){
if (my.canvas.clientWidth === 0){
// dont respond to key events when canvas is not in view
return;
}
if(e.which == 118){
// v
my.currentcamera += 1;
if (my.currentcamera >= my.cameras.length) {
my.currentcamera = 0;
}
my.use_camera = my.cameras[my.currentcamera];
}
if (e.which == 45){
// -
if (my.use_camera == my.followcamera){
my.followcamera.fov = my.followcamera.fov - 5;
if (my.followcamera.fov < 5){
my.followcamera.fov = 5;
}
my.followcamera.updateProjectionMatrix();
}
if (my.use_camera == my.trackcamera){
my.track_area -= 10;
if (my.track_area < 50){
my.track_area = 50;
}
my.resize_view();
}
}
if (e.which == 61){
// =
if (my.use_camera == my.followcamera){
my.followcamera.fov = my.followcamera.fov + 5;
if (my.followcamera.fov > 120){
my.followcamera.fov = 120;
}
my.followcamera.updateProjectionMatrix();
}
if (my.use_camera == my.trackcamera){
my.track_area += 10;
if (my.track_area > 1000){
my.track_area = 1000;
}
my.resize_view();
}
}
if (e.which == 100){
// d
$(my.stats.domElement).toggle();
}
});
my.stats = new Stats();
my.stats.setMode(0); // 0: fps, 1: ms
// Align top-left
my.stats.domElement.style.position = 'absolute';
my.stats.domElement.style.left = '5px';
my.stats.domElement.style.top = '200px';
document.body.appendChild( my.stats.domElement );
$(my.stats.domElement).hide();
};
var random_color = function () {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
};
my.setup_cameras = function(){
console.log("Setting up cameras");
my.canvas = document.getElementById(my.dom_id);
my.cameras = [];
var pos = new THREE.Vector3(my.width / 2, my.height / 2, 0);
var game_aspect = my.height / my.width;
var aspect = my.canvas.clientHeight / my.canvas.clientWidth;
var scale = game_aspect/aspect;
if (scale < 1){
var new_scale = (1/game_aspect) / (1/aspect);
var s = (my.height * new_scale) / 2;
my.camera = new THREE.OrthographicCamera( 0, my.width , s + (my.height/2), -(s - (my.height/2)), 0.1, 200 );
} else {
var s = (my.width * scale) / 2;
my.camera = new THREE.OrthographicCamera( -(s - (my.width/2)), s + (my.width/2), my.height, 0, 0.1, 200 );
}
my.camera.position.z = 99;
my.camera.updateProjectionMatrix();
my.cameras.push(my.camera);
var view_aspect = my.canvas.clientHeight / my.canvas.clientWidth;
my.track_area = 300;
my.trackcamera = new THREE.OrthographicCamera( -my.track_area, my.track_area, my.track_area * view_aspect, -my.track_area * view_aspect, -100, 200 );
my.trackcamera.position.z = 100;
my.trackcamera.updateProjectionMatrix();
my.cameras.push(my.trackcamera);
my.followcamera = new THREE.PerspectiveCamera( 35, my.canvas.clientWidth / my.canvas.clientHeight, 1, 10000 );
my.followcamera.position = new THREE.Vector3(my.width / 2, -my.height + (my.height/2), (my.height / 2) + 200);
my.followcamera.up = new THREE.Vector3(0,0,1);
my.followcamera.lookAt(pos);
my.followcamera.updateProjectionMatrix();
my.controls = new THREE.TrackballControls( my.followcamera );
my.cameras.push(my.followcamera);
};
my.setup_render_target = function(map_width, map_height, screen_width, screen_height){
if (my.renderer){
console.log("Renderer already initialized");
my.reset();
return;
}
my.turn = 0;
my.robots = {};
my.objects = {};
my.labels = [];
my.scene = new THREE.Scene();
my.quit = false;
my.width = screen_width;
my.height = screen_height;
my.renderer = new THREE.WebGLRenderer({"canvas": my.canvas, antialias: true });
my.setup_cameras();
my.floorobj = new THREE.CubeGeometry(my.width, my.height, 0.1);
var material = new THREE.MeshLambertMaterial( { color: random_color() } );
my.floor = new THREE.Mesh(my.floorobj, material);
my.floor.position.x = my.width / 2;
my.floor.position.y = my.height / 2 ;
my.floor.position.z = -2;
my.scene.add(my.floor);
// Basic primitives
my.grey_material = new THREE.MeshLambertMaterial( { color: "#999999", transparent: false } );
my.red_material = new THREE.MeshLambertMaterial( { color: "#ff0000", transparent: true } );
my.black_material = new THREE.MeshLambertMaterial( { color: "#000000", transparent: false } );
my.robot_geom = new THREE.CubeGeometry(5,5,5);
var loader = new THREE.OBJLoader();
loader.load( '/s/robot.obj', function ( object ) {
object.position.x = 100;
object.position.y = 100;
object.position.z = 1;
object.up = new THREE.Vector3(0,0,1);
object.scale.set(5,5,5);
my.robot_geom = object;
} );
my.bullet_geom = new THREE.CubeGeometry(3,3,3);
my.splosion_geom = new THREE.SphereGeometry(1,50,50);
var axis = new THREE.AxisHelper(100);
my.scene.add(axis);
my.wallobjx = new THREE.CubeGeometry(my.width, 3, 3);
my.wallobjy = new THREE.CubeGeometry(3, my.height, 3);
var material = new THREE.MeshLambertMaterial( { color: "#cccccc" } );
my.wall = new THREE.Mesh(my.wallobjx, material);
my.wall.position.x = my.width / 2;
my.scene.add(my.wall);
my.wall2 = new THREE.Mesh(my.wallobjx, material);
my.wall2.position.x = my.width / 2;
my.wall2.position.y = my.height ;
my.scene.add(my.wall2);
my.wall3 = new THREE.Mesh(my.wallobjy, material);
my.wall3.position.y = my.height / 2;
my.scene.add(my.wall3);
my.wall4 = new THREE.Mesh(my.wallobjy, material);
my.wall4.position.y = my.height / 2;
my.wall4.position.x = my.width ;
my.scene.add(my.wall4);
my.scene.add(my.camera);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.9 );
directionalLight.position.set( 0, 0, -1 );
my.scene.add( directionalLight );
var directionalLight2 = new THREE.DirectionalLight( 0xffffff, 0.9 );
directionalLight2.position.set( 0, 0.707, 0.707 );
my.scene.add( directionalLight2 );
var ambientLight = new THREE.AmbientLight(0x222222, 0.7);
my.scene.add(ambientLight);
my.currentcamera = 2;
my.use_camera = my.cameras[my.currentcamera];
var render = function render() {
my.stats.begin();
my.renderer.render(my.scene, my.use_camera);
my.update_labels();
if (my.controls){
my.controls.update();
}
if (!my.quit)
requestAnimationFrame(render);
my.stats.end();
};
render();
$(window).resize(function(){
my.resize_view();
});
my.resize_view();
};
my.resize_view = function(){
my.canvas.height = ($(window).height() - $(my.canvas).offset().top);
my.canvas.width = $(window).width();
var game_aspect = my.height / my.width;
var aspect = my.canvas.clientHeight / my.canvas.clientWidth;
var scale = game_aspect/aspect;
if (scale < 1){
var new_scale = (1/game_aspect) / (1/aspect);
var s = (my.height * new_scale) / 2;
my.camera.left = 0;
my.camera.right = my.width;
my.camera.top = s + (my.height/2);
my.camera.bottom = -(s - (my.height/2));
} else {
var s = (my.width * scale) / 2;
my.camera.left = -(s - (my.width/2));
my.camera.right = s + (my.width/2);
my.camera.top = my.height;
my.camera.bottom = 0;
}
my.camera.updateProjectionMatrix();
var view_aspect = my.canvas.clientHeight / my.canvas.clientWidth;
my.trackcamera.left = -my.track_area;
my.trackcamera.right = my.track_area;
my.trackcamera.top = my.track_area * view_aspect;
my.trackcamera.bottom = -my.track_area * view_aspect;
my.trackcamera.updateProjectionMatrix();
my.followcamera.aspect = my.canvas.clientWidth / my.canvas.clientHeight;
my.followcamera.updateProjectionMatrix();
my.use_camera = my.cameras[my.currentcamera];
my.renderer.context.viewport(0, 0, my.canvas.width, my.canvas.height);
};
my.begin_frame = function(turn){
my.turn = turn;
var d = new Date();
var time = d.getTime();
my.delta = time - my.last_frame_time;
my.delta_history.unshift(my.delta);
if (my.delta_history.length > 50){
my.delta_history.pop();
}
my.last_frame_time = time;
// Status Bar Text
var slowest = Math.max.apply(null, my.delta_history);
var debug_string = slowest + "ms";
};
my.render = function(draw_list){
var obj_count = 0;
for (var i in draw_list) {
var robot_data = draw_list[i];
var key, geometry, material, cube = null;
if (robot_data['type'] == 'object'){
key = robot_data['type'] + robot_data.bounds[0] + robot_data.bounds[1];
if (!(key in my.objects)){
obj_count++;
console.log(robot_data);
x = robot_data.bounds[0];
y = robot_data.bounds[1];
w = robot_data.bounds[2] - robot_data.bounds[0];
h = robot_data.bounds[3] - robot_data.bounds[1];
geometry = new THREE.CubeGeometry(w,h,5 + Math.round(Math.random() * 25));
var material = new THREE.MeshLambertMaterial( { color: random_color(), transparent: false } );
cube = new THREE.Mesh( geometry, material );
my.scene.add( cube );
cube.position.x = x + (w / 2);
cube.position.y = y + (h / 2);
cube.position.z = 0;
cube.turn = my.turn;
my.objects[key] = cube;
continue;
} else {
my.objects[key].turn = my.turn;
}
}
else{
key = robot_data['type'] + robot_data['id'];
if (!(key in my.robots)){
if (robot_data['type'] == 'bullet'){
material = my.black_material;
geometry = my.bullet_geom;
cube = new THREE.Mesh( geometry, material );
}
else if (robot_data['type'] == 'explosion'){
size = robot_data.radius;
material = my.red_material;
geometry = my.splosion_geom;
material.opacity = 0.8;
cube = new THREE.Mesh( geometry, material );
cube.scale.set(size, size, size);
}
else {
material = new THREE.MeshLambertMaterial( { color: robot_data['color'], transparent: false } );
geometry = my.robot_geom;
cube = geometry.clone();
cube.traverse(function ( geo ) {
geo.material = new THREE.MeshLambertMaterial( { color: robot_data['color'] } );
} );
my.make_bot_label(robot_data.id, robot_data.name, cube);
}
my.robots[key] = cube;
my.scene.add(cube);
cube.position.x = robot_data['position']['x'];
cube.position.y = robot_data['position']['y'];
cube.position.z = -1;
cube.turn = my.turn;
}
else{
my.robots[key].position.x = robot_data['position']['x'];
my.robots[key].position.y = robot_data['position']['y'];
my.robots[key].turn = my.turn;
if ("heading" in robot_data){
if (robot_data['heading']['x'] == 0 && robot_data['heading']['y'] == 0) {
robot_data['heading']['y'] = 1;
}
var tmp = new THREE.Vector3(
robot_data['heading']['x'] + robot_data.position.x,
robot_data['heading']['y'] + robot_data.position.y,
-1);
my.robots[key].up = new THREE.Vector3(0,0,1);
my.robots[key].lookAt( tmp );
}
if (robot_data.health){
var h = Math.round((robot_data.health / 200) * 100) + "%";
var x = "#health_" + robot_data.id;
$(x).css({
"width": h
});
}
}
}
}
// Worth noting this list contains robots, bullets and explosions
// it's badly named, I am sorry!
for (var q in my.robots){
if (my.turn - my.robots[q].turn > 1){
// find and remove label?
for (var w in my.labels){
if (my.labels[w].target == my.robots[q]){
my.labels[w].label.remove();
my.labels.splice(w, 1);
}
}
// Can I do this? I dont think so, we dont know
// if these things will ever be back
// my.robots[q].traverse(function ( geo ) {
// geo.visible = false;
// });
my.scene.remove(my.robots[q]);
delete my.robots[q];
}
}
for (var q in my.objects){
if (my.turn - my.objects[q].turn > 1){
my.objects[q].visible = false;
} else {
my.objects[q].visible = true;
}
}
for (var p in draw_list){
if (draw_list[p]['type'] == "robot"){
if (draw_list[p]['id'] == my.target){
var pos = new THREE.Vector3(draw_list[p].position.x, draw_list[p].position.y, 0);
my.followcamera.lookAt(pos);
my.controls.target = pos;
my.trackcamera.position.x = pos.x;
my.trackcamera.position.y = pos.y;
my.trackcamera.updateProjectionMatrix();
break;
}
}
}
};
var projector = new THREE.Projector();
my.update_labels = function(){
for (var i in my.labels){
var label = my.labels[i];
var screen_pos = label.target.position.clone();
var x = $(my.canvas).position();
if (my.canvas.clientWidth === 0){
$(label.label).css({
display: "none"});
} else {
projector.projectVector( screen_pos, my.use_camera );
if (screen_pos.x > 1 || screen_pos.x < -1 || screen_pos.y > 1 || screen_pos.y < -1){
$(label.label).css({
display: "none"});
continue;
}
var sx = (screen_pos.x * ((my.canvas.clientWidth)/2)) + ((my.canvas.clientWidth)/2) + x.left;
var sy = -(screen_pos.y * ((my.canvas.clientHeight)/2)) + ((my.canvas.clientHeight)/2) + x.top - 30;
$(label.label).css({
position: "absolute",
display:"block",
marginLeft: 0,
marginTop: 0,
top: sy,
left: sx});
}
}
};
my.make_bot_label = function(id, text, track_object){
var obj = $("<div class='label' style='display:none;'>" + text + "<div class='health' id='health_" + id + "'></div></div>");
$("body").append(obj);
my.labels.push({
label: obj,
target: track_object
});
};
my.debug_labels = [];
my.debug_lines = [];
my.clear_debug = function(){
for (var i in my.debug_labels){
my.debug_labels[i].remove();
}
my.debug_labels = [];
for (var i in my.debug_lines){
my.scene.remove(my.debug_lines[i]);
}
delete my.debug_lines;
my.debug_lines = [];
};
my.debug_label = function(x, y, msg){
if (my.canvas.clientWidth > 0){
var screen_pos = new THREE.Vector3(x, y, 0);
var canvas_pos = $(my.canvas).position();
projector.projectVector( screen_pos, my.use_camera );
if (screen_pos.x > 1 || screen_pos.x < -1 || screen_pos.y > 1 || screen_pos.y < -1){
return;
}
var sx = (screen_pos.x * ((my.canvas.clientWidth)/2)) + ((my.canvas.clientWidth)/2) + canvas_pos.left;
var sy = -(screen_pos.y * ((my.canvas.clientHeight)/2)) + ((my.canvas.clientHeight)/2) + canvas_pos.top;
var obj = $("<div class='label'>" + msg + "</div>");
$("body").append(obj);
$(obj).css({
position: "absolute",
marginLeft: 0,
marginTop: 0,
top: sy,
left: sx});
my.debug_labels.push(obj);
}
};
my.debug_label_screen = function(x, y, msg){
if (my.canvas.clientWidth > 0){
var canvas_pos = $(my.canvas).position();
var obj = $("<div class='label'>" + msg + "</div>");
$("body").append(obj);
$(obj).css({
position: "absolute",
marginLeft: 0,
marginTop: 0,
top: y + canvas_pos.top,
left: x + canvas_pos.left});
my.debug_labels.push(obj);
}
};
my.debug_line = function(x1, y1, x2, y2){
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(x1, y1, 0));
geometry.vertices.push(new THREE.Vector3(x2, y2, 0));
var line = new THREE.Line(geometry, material);
my.scene.add(line);
my.debug_lines.push(line);
};
my.set_focus_robot = function(id){
my.target = id;
};
my.reset = function(){
for (var i in my.objects){
my.scene.remove(my.objects[i]);
}
delete my.objects;
my.objects = {};
for (i in my.robots){
my.scene.remove(my.robots[i]);
}
delete my.robots;
my.robots = {};
my.clear_debug();
for (i in my.labels){
my.labels[i].label.remove();
}
my.labels = [];
};
my.end_frame = function(){
};
my.shutdown = function(){
my.reset();
my.quit = true;
};
return my;
};
+6
View File
@@ -0,0 +1,6 @@
// stats.js - http://github.com/mrdoob/stats.js
var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};
+705
View File
File diff suppressed because one or more lines are too long
+774
View File
@@ -0,0 +1,774 @@
// Copyright (c) 2013 Evan Shortiss
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
(function() {
// Default is to not use objects
var useObjects = false;
// Default is to use standard array
var AxesArray = Array;
/**
* Parse the arguments passed into Vec2D and Vec3D.
* @param {Mixed} args The argument array from caller function.
* @return {Array} The resulting axes array.
*/
function parseArgs(args) {
// User passed x and y
if (args.length === 2) {
return new Array(args[0], args[1]);
}
// User passed an array
else if (Object.prototype.toString.call(args[0]) === '[object Array]') {
return args[0];
}
// User passed x and y in an object
else if (typeof(args[0]) === 'object') {
return [args[0].x, args[0].y];
} else {
return [0, 0];
}
}
/**
* Check is the passed param a Vec2D instance.
* @param {Mixed} v The var to check.
* @return {Boolean} The result of check.
*/
function isVector(v) {
return v instanceof Vector || v instanceof ObjectVector;
};
/****************************************************************************
* Main array based vector class. Has instance methods attached to prototype.
* @constructor
* @param {Array} axes
****************************************************************************/
var Vector = function(axes) {
// Constructor for each array type is different. (Float32Array vs Array)
if (AxesArray.name === 'Float32Array') {
this._axes = new AxesArray([axes[0], axes[1]]);
} else {
this._axes = new AxesArray(axes[0], axes[1]);
}
};
Vector.prototype = {
/**
* Set both x and y
* @param x New x val
* @param y New y val
*/
setAxes: function(x, y) {
this._axes[0] = x;
this._axes[1] = y;
},
/**
* Getter for x axis.
* @return {Number}
*/
getX: function() {
return this._axes[0];
},
/**
* Setter for x axis.
*/
setX: function(x) {
this._axes[0] = x;
},
/**
* Getter for y axis.
* @return {Number}
*/
getY: function() {
return this._axes[1];
},
/**
* Setter for y axis.
*/
setY: function(y) {
this._axes[1] = y;
},
/**
* View vector as a string such as "Vec2D: (0, 4)"
* @param {Boolean}
* @return {String}
*/
toString: function(round) {
if (round) {
return '(' + Math.round(this.getX()) + ', ' + Math.round(this.getY()) + ')'
}
return '(' + this.getX() + ', ' + this.getY() + ')'
},
/**
* Return an array containing the vector axes.
* @return {Array}
*/
toArray: function() {
return new Array(this.getX(), this.getY());
},
/**
* Return an array containing the vector axes.
* @return {Object}
*/
toObject: function() {
return {
"x": this.getX(),
"y": this.getY()
};
},
/**
* Add the provided Vector to this one.
* @param {Vector} vec
*/
add: function(vec) {
this._axes[0] += vec._axes[0];
this._axes[1] += vec._axes[1];
return this;
},
/**
* Subtract the provided vector from this one.
* @param {Vector} vec
*/
subtract: function(vec) {
this._axes[0] -= vec._axes[0];
this._axes[1] -= vec._axes[1];
return this;
},
/**
* Check is the vector provided equal to this one.
* @param {Vec2D} vec
* @return {Boolean}
*/
equals: function(vec) {
return (vec._axes[0] == this._axes[0] && vec._axes[1] == this._axes[1]);
},
/**
* Multiply this vector by the provided vector.
* @param {Vector} vec
*/
multiplyByVector: function(vec) {
this._axes[0] *= vec._axes[0];
this._axes[1] *= vec._axes[1];
return this;
},
/**
* Multiply this vector by the provided vector.
* @param {Vector} vec
*/
divideByVector: function(vec) {
this._axes[0] /= vec._axes[0];
this._axes[1] /= vec._axes[1];
return this;
},
/**
* Multiply this vector by the provided number
* @param {Number} n
*/
multiplyByScalar: function(n) {
this._axes[0] *= n;
this._axes[1] *= n;
return this;
},
/**
* Divive this vector by the provided number
* @param {Number} n
*/
divideByScalar: function(n) {
this._axes[0] /= n;
this._axes[1] /= n;
return this;
},
/**
* Normalise this vector. Directly affects this vector.
* Use Vec2D.normalise(vector) to create a normalised clone of this.
*/
normalise: function() {
return this.multiplyByScalar(1 / this.magnitude());
},
/**
* For American spelling.
* Same as unit/normalise function.
*/
normalize: function() {
return this.normalise();
},
/**
* The same as normalise.
*/
unit: function() {
return this.normalise();
},
/**
* Return the magnitude (length) of this vector.
* @return {Number}
*/
magnitude: function() {
return Math.sqrt((this._axes[0] * this._axes[0]) + (this._axes[1] * this._axes[1]));
},
/**
* Return the magnitude (length) of this vector.
* @return {Number}
*/
length: function() {
return this.magnitude();
},
/**
* Get the dot product of this vector by another.
* @param {Vector} vec
* @return {Number}
*/
dot: function(vec) {
return (vec._axes[0] * this._axes[0]) + (vec._axes[1] * this._axes[1]);
},
/**
* Get the cross product of this vector by another.
* @param {Vector} vec
* @return {Number}
*/
cross: function(vec) {
return ((this._axes[0] * vec._axes[1]) - (this._axes[1] * vec._axes[0]));
},
/**
* Returns the reverse of the provided vector.
* @param {Vector} vec
*/
reverse: function(vec) {
this._axes[0] = -this._axes[0];
this._axes[1] = -this._axes[1];
return this;
},
/**
* Create a copy of this vector.
* @return {Vector}
*/
clone: function() {
return new Vector([this._axes[0], this._axes[1]]);
}
};
/****************************************************************************
* Main object based vector class. Has instance methods attached to prototype.
* @constructor
* @param {Array} axes
****************************************************************************/
var ObjectVector = function(axes) {
// Constructor for each array type is different. (Float32Array vs Array)
this._axes = {
x: axes[0],
y: axes[1]
};
};
ObjectVector.prototype = {
/**
* Set both x and y
* @param x New x val
* @param y New y val
*/
setAxes: function(x, y) {
this._axes.x = x;
this._axes.y = y;
},
/**
* Getter for x axis.
* @return {Number}
*/
getX: function() {
return this._axes.x;
},
/**
* Setter for x axis.
*/
setX: function(x) {
this._axes.x = x;
},
/**
* Getter for y axis.
* @return {Number}
*/
getY: function() {
return this._axes.y;
},
/**
* Setter for y axis.
*/
setY: function(y) {
this._axes.y = y;
},
/**
* View vector as a string such as "Vec2D: (0, 4)"
* @param {Boolean}
* @return {String}
*/
toString: function(round) {
if (round) {
return '(' + Math.round(this.getX()) + ', ' + Math.round(this.getY()) + ')'
}
return '(' + this.getX() + ', ' + this.getY() + ')'
},
/**
* Return an array containing the vector axes.
* @return {Array}
*/
toArray: function() {
return new Array(this.getX(), this.getY());
},
/**
* Return an array containing the vector axes.
* @return {Object}
*/
toObject: function() {
return {
"x": this.getX(),
"y": this.getY()
};
},
/**
* Add the provided Vector to this one.
* @param {Vector} vec
*/
add: function(vec) {
this._axes.x += vec._axes.x;
this._axes.y += vec._axes.y;
return this;
},
/**
* Subtract the provided vector from this one.
* @param {Vector} vec
*/
subtract: function(vec) {
this._axes.x -= vec._axes.x;
this._axes.y -= vec._axes.y;
return this;
},
/**
* Check is the vector provided equal to this one.
* @param {Vec2D} vec
* @return {Boolean}
*/
equals: function(vec) {
return (vec._axes.x == this._axes.x && vec._axes.y == this._axes.y);
},
/**
* Multiply this vector by the provided vector.
* @param {Vector} vec
*/
multiplyByVector: function(vec) {
this._axes.x *= vec._axes.x;
this._axes.y *= vec._axes.y;
return this;
},
/**
* Multiply this vector by the provided vector.
* @param {Vector} vec
*/
divideByVector: function(vec) {
this._axes.x /= vec._axes.x;
this._axes.y /= vec._axes.y;
return this;
},
/**
* Multiply this vector by the provided number
* @param {Number} n
*/
multiplyByScalar: function(n) {
this._axes.x *= n;
this._axes.y *= n;
return this;
},
/**
* Divive this vector by the provided number
* @param {Number} n
*/
divideByScalar: function(n) {
this._axes.x /= n;
this._axes.y /= n;
return this;
},
/**
* Normalise this vector. Directly affects this vector.
* Use Vec2D.normalise(vector) to create a normalised clone of this.
*/
normalise: function() {
return this.multiplyByScalar(1 / this.magnitude());
},
/**
* For American spelling.
* Same as unit/normalise function.
*/
normalize: function() {
return this.normalise();
},
/**
* The same as normalise.
*/
unit: function() {
return this.normalise();
},
/**
* Return the magnitude (length) of this vector.
* @return {Number}
*/
magnitude: function() {
return Math.sqrt((this._axes.x * this._axes.x) + (this._axes.y * this._axes.y));
},
/**
* Return the magnitude (length) of this vector.
* @return {Number}
*/
length: function() {
return this.magnitude();
},
/**
* Get the dot product of this vector by another.
* @param {Vector} vec
* @return {Number}
*/
dot: function(vec) {
return (vec._axes.x * this._axes.x) + (vec._axes.y * this._axes.y);
},
/**
* Get the cross product of this vector by another.
* @param {Vector} vec
* @return {Number}
*/
cross: function(vec) {
return ((this._axes.x * vec._axes.y) - (this._axes.y * vec._axes.x));
},
/**
* Returns the reverse of the provided vector.
* @param {Vector} vec
*/
reverse: function(vec) {
this._axes.x = -this._axes.x;
this._axes.y = -this._axes.y;
return this;
},
/**
* Create a copy of this vector.
* @return {Vector}
*/
clone: function() {
return new ObjectVector([this._axes.x, this._axes.y]);
}
};
/****************************************************************************
Publically exposed Vector interface.
****************************************************************************/
var Vec2D = function() {
};
Vec2D.prototype = {
Vector: Vector,
ObjectVector: ObjectVector,
/**
* Create a new Vector.
* @param {Number} [x]
* @param {Number} [y]
* @param {Object} [axes]
* @param {Array} [axes]
*/
create: function() {
if(useObjects) {
return new this.ObjectVector(parseArgs(arguments));
}
return new this.Vector(parseArgs(arguments));
},
/**
* Instruct the library to use standard JavaScript arrays.
* Otherwise the library will try use Float32Arrays if available. (This is default)
*/
useStandardArrays: function() {
useObjects = false;
AxesArray = Array;
},
/**
* Instruct the library to use Float32 JavaScript arrays. (This is default)
* Otherwise the library will use standard array.
*/
useFloat32Arrays: function() {
useObjects = false;
AxesArray = Float32Array;
},
/**
* Instruct library to use Objects to represent vectors.
*/
useObjects: function() {
useObjects = true;
},
/**
* Add v0 to v1 to produce a new vector
* @param {Vector} v0
* @param {Vector} v1
* @return {Vector}
*/
add: function(v0, v1) {
return this.create(v0.getX() + v1.getX(), v0.getY() + v1.getY())
},
/**
* Subtract v0 from v1 to produce a new Vector.
* @param {Vector} v0
* @param {Vector} v1
* @return {Vector}
*/
subtract: function(v0, v1) {
return this.create(v0.getX() - v1.getX(), v0.getY()- v1.getY())
},
/**
* Check are the provided vectors equal.
* @param {Vector} v0
* @param {Vector} v1
* @return {Boolean}
*/
equals: function(v0, v1) {
return v0.equals(v1);
},
/**
* Multiply a vector by a vector to produce a new Vector
* @param {Vector} v0
* @param {Vector} v1
* @return {Vector}
*/
vectorTimesVector: function(v0, v1) {
return this.create(v0.getX() * v1.getX(), v0.getY() * v1.getY());
},
/**
* Multiply a vector by a number to produce a new vector.
* @param {Vector} vec The var to store a result in.
* @param {Number} n The Vector to subtract from this one.
* @return {Vector}
*/
vectorTimesScalar: function(vec, n) {
return this.create(vec.getX() * n, vec.getY() * n);
},
/**
* Return a normalised version of provided vector.
* @param {Vector} vec
* @return {Vector}
*/
nomalise: function(vec) {
return this.timesScalar(vec, 1 / vec.magnitude());
},
/**
* Same as normalise
*/
normalize: function(vec) {
return this.normalise(vec);
},
/**
* Same as normalise.
*/
unit: function(vec) {
return this.normalise(vec);
},
/**
* Return the dot product of two vectors or this vector dot another.
* @param {Vector} v0
* @param {Vector} v1
* @return {Number}
*/
dot: function(v0, v1) {
return (v0.getX() * v1.getX()) + (v0.getY() * v1.getY());
},
/**
* Calculate the cross product of two vectors.
* @param {Vector} v0
* @param {Vector} v1
* @param {Vector}
*/
cross: function(v0, v1) {
return ((v0.getX() * v1.getY()) - (v0.getY() * v1.getX()));
},
/**
* Return the magnitude (length) of a vector.
* @param {Vector} vec
* @return {Number}
*/
magnitude: function(vec) {
return vec.magnitude();
},
/**
* Same as the magnitude.
*/
length: function(vec) {
return vec.magnitude();
},
/**
* Find sqaure distance between two vectors.
* @param {Vector} v0
* @param {Vector} v1
* @return {Number}
*/
distance: function(v0, v1) {
return Math.sqrt((v0.getX() - v1.getX()) * (v0.getX() - v1.getX()) + (v0.getY() - v1.getY()) * (v0.getY() - v1.getY()))
},
/**
* Returns the reverse of the provided vector.
* @param {Vector} vec
* @return {Vector}
*/
reverse: function(vec) {
return this.create(-vec.getX(), -vec.getY());
}
};
// Expose publically
if (typeof window !== 'undefined') {
window.Vec2D = new Vec2D();
} else {
module.exports = new Vec2D();
}
})();