Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active January 13, 2017 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micahstubbs/201876c23dfe243f6e9a9c0be681367e to your computer and use it in GitHub Desktop.
Save micahstubbs/201876c23dfe243f6e9a9c0be681367e to your computer and use it in GitHub Desktop.
aframe-blocks roomscale 03
license: mit
border: no

a roomscale VR scene targeting the HTC Vive

use the front-trigger on either Vive controller to pick up and inspect a block

in 2D mode, press W to go back and bring the scene into view. for a nicer camera position, checkout this commit commit hash 47adcf3b99846d309bb3770a516fa917782597f7

follow the stackoverflow question how to reset camera position on enter VR? for a possible method to show a nice custom camera position in 2D mode and a glued-to-the-hmd camera position when in VR

block a-boxes positions are closer to the origin than in previous examples so that they are within reach from in roomscale VR. ROADMAP: teleport locomotion ;-)

many thanks to @bryik_ws, @utopiah, @donrmccurdy over at the A-Frame slack for help getting the interaction working.

do get a slack invite of your own and check out the other places the A-Frame community gathers


all the blocks with thumbnail images created during the 2016 #d3unconf

here we use aframe's a-boxes, which are kind of like SVG rects

a fork of aframe + d3 + bl.ocks from @donrmccurdy

falling blocks brought to you by the aframe-physics-system, also from @donrmccurdy

inspired by the conversations at the 3d and VR 11am session in the Alcatraz Room at the 2016 d3 unconference

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
require('./').registerAll();
},{"./":2}],2:[function(require,module,exports){
module.exports = {
controls: require('./src/controls'),
loaders: require('./src/loaders'),
misc: require('./src/misc'),
physics: require('aframe-physics-system'),
primitives: require('./src/primitives'),
shadows: require('./src/shadows'),
registerAll: function () {
this.controls.registerAll();
this.loaders.registerAll();
this.misc.registerAll();
this.physics.registerAll();
this.primitives.registerAll();
this.shadows.registerAll();
}
};
},{"./src/controls":22,"./src/loaders":27,"./src/misc":32,"./src/primitives":40,"./src/shadows":41,"aframe-physics-system":7}],3:[function(require,module,exports){
module.exports = Object.assign(function GamepadButton () {}, {
FACE_1: 0,
FACE_2: 1,
FACE_3: 2,
FACE_4: 3,
L_SHOULDER_1: 4,
R_SHOULDER_1: 5,
L_SHOULDER_2: 6,
R_SHOULDER_2: 7,
SELECT: 8,
START: 9,
DPAD_UP: 12,
DPAD_DOWN: 13,
DPAD_LEFT: 14,
DPAD_RIGHT: 15,
VENDOR: 16,
});
},{}],4:[function(require,module,exports){
function GamepadButtonEvent (type, index, details) {
this.type = type;
this.index = index;
this.pressed = details.pressed;
this.value = details.value;
}
module.exports = GamepadButtonEvent;
},{}],5:[function(require,module,exports){
/**
* @author Wei Meng / http://about.me/menway
*
* Description: A THREE loader for PLY ASCII files (known as the Polygon File Format or the Stanford Triangle Format).
*
*
* Limitations: ASCII decoding assumes file is UTF-8.
*
* Usage:
* var loader = new THREE.PLYLoader();
* loader.load('./models/ply/ascii/dolphins.ply', function (geometry) {
*
* scene.add( new THREE.Mesh( geometry ) );
*
* } );
*
* If the PLY file uses non standard property names, they can be mapped while
* loading. For example, the following maps the properties
* “diffuse_(red|green|blue)” in the file to standard color names.
*
* loader.setPropertyNameMapping( {
* diffuse_red: 'red',
* diffuse_green: 'green',
* diffuse_blue: 'blue'
* } );
*
*/
module.exports = THREE.PLYLoader = function ( manager ) {
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
this.propertyNameMapping = {};
};
THREE.PLYLoader.prototype = {
constructor: THREE.PLYLoader,
load: function ( url, onLoad, onProgress, onError ) {
var scope = this;
var loader = new THREE.XHRLoader( this.manager );
loader.setResponseType( 'arraybuffer' );
loader.load( url, function ( text ) {
onLoad( scope.parse( text ) );
}, onProgress, onError );
},
setPropertyNameMapping: function ( mapping ) {
this.propertyNameMapping = mapping;
},
bin2str: function ( buf ) {
var array_buffer = new Uint8Array( buf );
var str = '';
for ( var i = 0; i < buf.byteLength; i ++ ) {
str += String.fromCharCode( array_buffer[ i ] ); // implicitly assumes little-endian
}
return str;
},
isASCII: function( data ) {
var header = this.parseHeader( this.bin2str( data ) );
return header.format === "ascii";
},
parse: function ( data ) {
if ( data instanceof ArrayBuffer ) {
return this.isASCII( data )
? this.parseASCII( this.bin2str( data ) )
: this.parseBinary( data );
} else {
return this.parseASCII( data );
}
},
parseHeader: function ( data ) {
var patternHeader = /ply([\s\S]*)end_header\s/;
var headerText = "";
var headerLength = 0;
var result = patternHeader.exec( data );
if ( result !== null ) {
headerText = result [ 1 ];
headerLength = result[ 0 ].length;
}
var header = {
comments: [],
elements: [],
headerLength: headerLength
};
var lines = headerText.split( '\n' );
var currentElement = undefined;
var lineType, lineValues;
function make_ply_element_property( propertValues, propertyNameMapping ) {
var property = {
type: propertValues[ 0 ]
};
if ( property.type === 'list' ) {
property.name = propertValues[ 3 ];
property.countType = propertValues[ 1 ];
property.itemType = propertValues[ 2 ];
} else {
property.name = propertValues[ 1 ];
}
if ( property.name in propertyNameMapping ) {
property.name = propertyNameMapping[ property.name ];
}
return property;
}
for ( var i = 0; i < lines.length; i ++ ) {
var line = lines[ i ];
line = line.trim();
if ( line === "" ) {
continue;
}
lineValues = line.split( /\s+/ );
lineType = lineValues.shift();
line = lineValues.join( " " );
switch ( lineType ) {
case "format":
header.format = lineValues[ 0 ];
header.version = lineValues[ 1 ];
break;
case "comment":
header.comments.push( line );
break;
case "element":
if ( ! ( currentElement === undefined ) ) {
header.elements.push( currentElement );
}
currentElement = Object();
currentElement.name = lineValues[ 0 ];
currentElement.count = parseInt( lineValues[ 1 ] );
currentElement.properties = [];
break;
case "property":
currentElement.properties.push( make_ply_element_property( lineValues, this.propertyNameMapping ) );
break;
default:
console.log( "unhandled", lineType, lineValues );
}
}
if ( ! ( currentElement === undefined ) ) {
header.elements.push( currentElement );
}
return header;
},
parseASCIINumber: function ( n, type ) {
switch ( type ) {
case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint':
case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32':
return parseInt( n );
case 'float': case 'double': case 'float32': case 'float64':
return parseFloat( n );
}
},
parseASCIIElement: function ( properties, line ) {
var values = line.split( /\s+/ );
var element = Object();
for ( var i = 0; i < properties.length; i ++ ) {
if ( properties[ i ].type === "list" ) {
var list = [];
var n = this.parseASCIINumber( values.shift(), properties[ i ].countType );
for ( var j = 0; j < n; j ++ ) {
list.push( this.parseASCIINumber( values.shift(), properties[ i ].itemType ) );
}
element[ properties[ i ].name ] = list;
} else {
element[ properties[ i ].name ] = this.parseASCIINumber( values.shift(), properties[ i ].type );
}
}
return element;
},
parseASCII: function ( data ) {
// PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
var geometry = new THREE.Geometry();
var result;
var header = this.parseHeader( data );
var patternBody = /end_header\s([\s\S]*)$/;
var body = "";
if ( ( result = patternBody.exec( data ) ) !== null ) {
body = result [ 1 ];
}
var lines = body.split( '\n' );
var currentElement = 0;
var currentElementCount = 0;
geometry.useColor = false;
for ( var i = 0; i < lines.length; i ++ ) {
var line = lines[ i ];
line = line.trim();
if ( line === "" ) {
continue;
}
if ( currentElementCount >= header.elements[ currentElement ].count ) {
currentElement ++;
currentElementCount = 0;
}
var element = this.parseASCIIElement( header.elements[ currentElement ].properties, line );
this.handleElement( geometry, header.elements[ currentElement ].name, element );
currentElementCount ++;
}
return this.postProcess( geometry );
},
postProcess: function ( geometry ) {
if ( geometry.useColor ) {
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].vertexColors = [
geometry.colors[ geometry.faces[ i ].a ],
geometry.colors[ geometry.faces[ i ].b ],
geometry.colors[ geometry.faces[ i ].c ]
];
}
geometry.elementsNeedUpdate = true;
}
geometry.computeBoundingSphere();
return geometry;
},
handleElement: function ( geometry, elementName, element ) {
if ( elementName === "vertex" ) {
geometry.vertices.push(
new THREE.Vector3( element.x, element.y, element.z )
);
if ( 'red' in element && 'green' in element && 'blue' in element ) {
geometry.useColor = true;
var color = new THREE.Color();
color.setRGB( element.red / 255.0, element.green / 255.0, element.blue / 255.0 );
geometry.colors.push( color );
}
} else if ( elementName === "face" ) {
// BEGIN: Edits by donmccurdy.
var vertex_indices = element.vertex_indices || element.vertex_index;
// END: Edits by donmccurdy.
if ( vertex_indices.length === 3 ) {
geometry.faces.push(
new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] )
);
} else if ( vertex_indices.length === 4 ) {
geometry.faces.push(
new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] ),
new THREE.Face3( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] )
);
}
}
},
binaryRead: function ( dataview, at, type, little_endian ) {
switch ( type ) {
// corespondences for non-specific length types here match rply:
case 'int8': case 'char': return [ dataview.getInt8( at ), 1 ];
case 'uint8': case 'uchar': return [ dataview.getUint8( at ), 1 ];
case 'int16': case 'short': return [ dataview.getInt16( at, little_endian ), 2 ];
case 'uint16': case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ];
case 'int32': case 'int': return [ dataview.getInt32( at, little_endian ), 4 ];
case 'uint32': case 'uint': return [ dataview.getUint32( at, little_endian ), 4 ];
case 'float32': case 'float': return [ dataview.getFloat32( at, little_endian ), 4 ];
case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ];
}
},
binaryReadElement: function ( dataview, at, properties, little_endian ) {
var element = Object();
var result, read = 0;
for ( var i = 0; i < properties.length; i ++ ) {
if ( properties[ i ].type === "list" ) {
var list = [];
result = this.binaryRead( dataview, at + read, properties[ i ].countType, little_endian );
var n = result[ 0 ];
read += result[ 1 ];
for ( var j = 0; j < n; j ++ ) {
result = this.binaryRead( dataview, at + read, properties[ i ].itemType, little_endian );
list.push( result[ 0 ] );
read += result[ 1 ];
}
element[ properties[ i ].name ] = list;
} else {
result = this.binaryRead( dataview, at + read, properties[ i ].type, little_endian );
element[ properties[ i ].name ] = result[ 0 ];
read += result[ 1 ];
}
}
return [ element, read ];
},
parseBinary: function ( data ) {
var geometry = new THREE.Geometry();
var header = this.parseHeader( this.bin2str( data ) );
var little_endian = ( header.format === "binary_little_endian" );
var body = new DataView( data, header.headerLength );
var result, loc = 0;
for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) {
for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) {
result = this.binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian );
loc += result[ 1 ];
var element = result[ 0 ];
this.handleElement( geometry, header.elements[ currentElement ].name, element );
}
}
return this.postProcess( geometry );
}
};
},{}],6:[function(require,module,exports){
/**
* Polyfill for the additional KeyboardEvent properties defined in the D3E and
* D4E draft specifications, by @inexorabletash.
*
* See: https://github.com/inexorabletash/polyfill
*/
(function(global) {
var nativeKeyboardEvent = ('KeyboardEvent' in global);
if (!nativeKeyboardEvent)
global.KeyboardEvent = function KeyboardEvent() { throw TypeError('Illegal constructor'); };
global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location
global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key
global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key
global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or +
var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
//--------------------------------------------------------------------
//
// Utilities
//
//--------------------------------------------------------------------
function contains(s, ss) { return String(s).indexOf(ss) !== -1; }
var os = (function() {
if (contains(navigator.platform, 'Win')) { return 'win'; }
if (contains(navigator.platform, 'Mac')) { return 'mac'; }
if (contains(navigator.platform, 'CrOS')) { return 'cros'; }
if (contains(navigator.platform, 'Linux')) { return 'linux'; }
if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) { return 'ios'; }
return '';
} ());
var browser = (function() {
if (contains(navigator.userAgent, 'Chrome/')) { return 'chrome'; }
if (contains(navigator.vendor, 'Apple')) { return 'safari'; }
if (contains(navigator.userAgent, 'MSIE')) { return 'ie'; }
if (contains(navigator.userAgent, 'Gecko/')) { return 'moz'; }
if (contains(navigator.userAgent, 'Opera/')) { return 'opera'; }
return '';
} ());
var browser_os = browser + '-' + os;
function mergeIf(baseTable, select, table) {
if (browser_os === select || browser === select || os === select) {
Object.keys(table).forEach(function(keyCode) {
baseTable[keyCode] = table[keyCode];
});
}
}
function remap(o, key) {
var r = {};
Object.keys(o).forEach(function(k) {
var item = o[k];
if (key in item) {
r[item[key]] = item;
}
});
return r;
}
function invert(o) {
var r = {};
Object.keys(o).forEach(function(k) {
r[o[k]] = k;
});
return r;
}
//--------------------------------------------------------------------
//
// Generic Mappings
//
//--------------------------------------------------------------------
// "keyInfo" is a dictionary:
// code: string - name from DOM Level 3 KeyboardEvent code Values
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
// location (optional): number - one of the DOM_KEY_LOCATION values
// keyCap (optional): string - keyboard label in en-US locale
// USB code Usage ID from page 0x07 unless otherwise noted (Informative)
// Map of keyCode to keyInfo
var keyCodeToInfoTable = {
// 0x01 - VK_LBUTTON
// 0x02 - VK_RBUTTON
0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E)
// 0x04 - VK_MBUTTON
// 0x05 - VK_XBUTTON1
// 0x06 - VK_XBUTTON2
0x06: { code: 'Help' }, // [USB: 0x75] ???
// 0x07 - undefined
0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards.
0x09: { code: 'Tab' }, // [USB: 0x2b]
// 0x0A-0x0B - reserved
0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E)
0X0D: { code: 'Enter' }, // [USB: 0x28]
// 0x0E-0x0F - undefined
0x10: { code: 'Shift' },
0x11: { code: 'Control' },
0x12: { code: 'Alt' },
0x13: { code: 'Pause' }, // [USB: 0x48]
0x14: { code: 'CapsLock' }, // [USB: 0x39]
0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout
0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ???
0x17: { code: 'JunjaMode' }, // (Not in D3E)
0x18: { code: 'FinalMode' }, // (Not in D3E)
0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout
// 0x1A - undefined
0x1B: { code: 'Escape' }, // [USB: 0x29]
0x1C: { code: 'Convert' }, // [USB: 0x8a]
0x1D: { code: 'NonConvert' }, // [USB: 0x8b]
0x1E: { code: 'Accept' }, // (Not in D3E)
0x1F: { code: 'ModeChange' }, // (Not in D3E)
0x20: { code: 'Space' }, // [USB: 0x2c]
0x21: { code: 'PageUp' }, // [USB: 0x4b]
0x22: { code: 'PageDown' }, // [USB: 0x4e]
0x23: { code: 'End' }, // [USB: 0x4d]
0x24: { code: 'Home' }, // [USB: 0x4a]
0x25: { code: 'ArrowLeft' }, // [USB: 0x50]
0x26: { code: 'ArrowUp' }, // [USB: 0x52]
0x27: { code: 'ArrowRight' }, // [USB: 0x4f]
0x28: { code: 'ArrowDown' }, // [USB: 0x51]
0x29: { code: 'Select' }, // (Not in D3E)
0x2A: { code: 'Print' }, // (Not in D3E)
0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E)
0x2C: { code: 'PrintScreen' }, // [USB: 0x46]
0x2D: { code: 'Insert' }, // [USB: 0x49]
0x2E: { code: 'Delete' }, // [USB: 0x4c]
0x2F: { code: 'Help' }, // [USB: 0x75] ???
0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0)
0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1!
0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@
0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3#
0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$
0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5%
0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^
0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7&
0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8*
0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9(
// 0x3A-0x40 - undefined
0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04]
0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05]
0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06]
0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07]
0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08]
0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09]
0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a]
0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b]
0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c]
0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d]
0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e]
0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f]
0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10]
0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11]
0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12]
0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13]
0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14]
0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15]
0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16]
0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17]
0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18]
0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19]
0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a]
0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b]
0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c]
0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d]
0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu
// 0x5E - reserved
0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep
0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62]
0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59]
0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a]
0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b]
0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c]
0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d]
0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e]
0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f]
0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60]
0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61]
0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55]
0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85]
0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56]
0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63]
0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54]
0x70: { code: 'F1' }, // [USB: 0x3a]
0x71: { code: 'F2' }, // [USB: 0x3b]
0x72: { code: 'F3' }, // [USB: 0x3c]
0x73: { code: 'F4' }, // [USB: 0x3d]
0x74: { code: 'F5' }, // [USB: 0x3e]
0x75: { code: 'F6' }, // [USB: 0x3f]
0x76: { code: 'F7' }, // [USB: 0x40]
0x77: { code: 'F8' }, // [USB: 0x41]
0x78: { code: 'F9' }, // [USB: 0x42]
0x79: { code: 'F10' }, // [USB: 0x43]
0x7A: { code: 'F11' }, // [USB: 0x44]
0x7B: { code: 'F12' }, // [USB: 0x45]
0x7C: { code: 'F13' }, // [USB: 0x68]
0x7D: { code: 'F14' }, // [USB: 0x69]
0x7E: { code: 'F15' }, // [USB: 0x6a]
0x7F: { code: 'F16' }, // [USB: 0x6b]
0x80: { code: 'F17' }, // [USB: 0x6c]
0x81: { code: 'F18' }, // [USB: 0x6d]
0x82: { code: 'F19' }, // [USB: 0x6e]
0x83: { code: 'F20' }, // [USB: 0x6f]
0x84: { code: 'F21' }, // [USB: 0x70]
0x85: { code: 'F22' }, // [USB: 0x71]
0x86: { code: 'F23' }, // [USB: 0x72]
0x87: { code: 'F24' }, // [USB: 0x73]
// 0x88-0x8F - unassigned
0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
0x91: { code: 'ScrollLock' }, // [USB: 0x47]
// 0x92-0x96 - OEM specific
// 0x97-0x9F - unassigned
// NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers
0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2]
0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6]
0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224]
0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225]
0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227]
0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226]
0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221]
0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228]
0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222]
0xAD: { code: 'VolumeMute' }, // [USB: 0x7f]
0xAE: { code: 'VolumeDown' }, // [USB: 0x81]
0xAF: { code: 'VolumeUp' }, // [USB: 0x80]
0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5]
0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6]
0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7]
0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd]
0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a]
0xB5: { code: 'MediaSelect' },
0xB6: { code: 'LaunchApp1' },
0xB7: { code: 'LaunchApp2' },
// 0xB8-0xB9 - reserved
0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,<
0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .>
0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101)
0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101)
// 0xC1-0xCF - reserved
// 0xD0-0xD7 - reserved
// 0xD8-0xDA - unassigned
0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101)
0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101)
0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
// 0xDF - miscellaneous/varies
// 0xE0 - reserved
// 0xE1 - OEM specific
0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102)
// 0xE3-0xE4 - OEM specific
0xE5: { code: 'Process' }, // (Not in D3E)
// 0xE6 - OEM specific
// 0xE7 - VK_PACKET
// 0xE8 - unassigned
// 0xE9-0xEF - OEM specific
// 0xF0-0xF5 - OEM specific
0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E)
0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E)
0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E)
0xF9: { code: 'EraseEof' }, // (Not in D3E)
0xFA: { code: 'Play' }, // (Not in D3E)
0xFB: { code: 'ZoomToggle' }, // (Not in D3E)
// 0xFC - VK_NONAME - reserved
// 0xFD - VK_PA1
0xFE: { code: 'Clear' } // [USB: 0x9c] (Not in D3E)
};
// No legacy keyCode, but listed in D3E:
// code: usb
// 'IntlHash': 0x070032,
// 'IntlRo': 0x070087,
// 'IntlYen': 0x070089,
// 'NumpadBackspace': 0x0700bb,
// 'NumpadClear': 0x0700d8,
// 'NumpadClearEntry': 0x0700d9,
// 'NumpadMemoryAdd': 0x0700d3,
// 'NumpadMemoryClear': 0x0700d2,
// 'NumpadMemoryRecall': 0x0700d1,
// 'NumpadMemoryStore': 0x0700d0,
// 'NumpadMemorySubtract': 0x0700d4,
// 'NumpadParenLeft': 0x0700b6,
// 'NumpadParenRight': 0x0700b7,
//--------------------------------------------------------------------
//
// Browser/OS Specific Mappings
//
//--------------------------------------------------------------------
mergeIf(keyCodeToInfoTable,
'moz', {
0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD } // [USB: 0x56]
});
mergeIf(keyCodeToInfoTable,
'moz-mac', {
0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
});
mergeIf(keyCodeToInfoTable,
'moz-win', {
0xAD: { code: 'Minus', keyCap: '-' } // [USB: 0x2d] -_
});
mergeIf(keyCodeToInfoTable,
'chrome-mac', {
0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
});
// Windows via Bootcamp (!)
if (0) {
mergeIf(keyCodeToInfoTable,
'chrome-win', {
0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
});
mergeIf(keyCodeToInfoTable,
'ie', {
0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
0xDF: { code: 'Backquote', keyCap: '`' } // [USB: 0x35] `~ (US Standard 101)
});
}
mergeIf(keyCodeToInfoTable,
'safari', {
0x03: { code: 'Enter' }, // [USB: 0x28] old Safari
0x19: { code: 'Tab' } // [USB: 0x2b] old Safari for Shift+Tab
});
mergeIf(keyCodeToInfoTable,
'ios', {
0x0A: { code: 'Enter', location: STANDARD } // [USB: 0x28]
});
mergeIf(keyCodeToInfoTable,
'safari-mac', {
0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
0xE5: { code: 'KeyQ', keyCap: 'Q' } // [USB: 0x14] On alternate presses, Ctrl+Q sends this
});
//--------------------------------------------------------------------
//
// Identifier Mappings
//
//--------------------------------------------------------------------
// Cases where newer-ish browsers send keyIdentifier which can be
// used to disambiguate keys.
// keyIdentifierTable[keyIdentifier] -> keyInfo
var keyIdentifierTable = {};
if ('cros' === os) {
keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT };
keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT };
keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT };
keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT };
keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT };
keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT };
}
if ('chrome-mac' === browser_os) {
keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
}
if ('safari-mac' === browser_os) {
keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
}
if ('ios' === os) {
// These only generate keyup events
keyIdentifierTable['U+0010'] = { code: 'Function' };
keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' };
keyIdentifierTable['U+001D'] = { code: 'ArrowRight' };
keyIdentifierTable['U+001E'] = { code: 'ArrowUp' };
keyIdentifierTable['U+001F'] = { code: 'ArrowDown' };
keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft
keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight
keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp
keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown
}
//--------------------------------------------------------------------
//
// Location Mappings
//
//--------------------------------------------------------------------
// Cases where newer-ish browsers send location/keyLocation which
// can be used to disambiguate keys.
// locationTable[location][keyCode] -> keyInfo
var locationTable = [];
locationTable[LEFT] = {
0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
0x12: { code: 'AltLeft', location: LEFT } // [USB: 0xe2]
};
locationTable[RIGHT] = {
0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
0x12: { code: 'AltRight', location: RIGHT } // [USB: 0xe6]
};
locationTable[NUMPAD] = {
0x0D: { code: 'NumpadEnter', location: NUMPAD } // [USB: 0x58]
};
mergeIf(locationTable[NUMPAD], 'moz', {
0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
0x6B: { code: 'NumpadAdd', location: NUMPAD } // [USB: 0x57]
});
mergeIf(locationTable[LEFT], 'moz-mac', {
0xE0: { code: 'OSLeft', location: LEFT } // [USB: 0xe3]
});
mergeIf(locationTable[RIGHT], 'moz-mac', {
0xE0: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
});
mergeIf(locationTable[RIGHT], 'moz-win', {
0x5B: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
});
mergeIf(locationTable[RIGHT], 'mac', {
0x5D: { code: 'OSRight', location: RIGHT } // [USB: 0xe7]
});
mergeIf(locationTable[NUMPAD], 'chrome-mac', {
0x0C: { code: 'NumLock', location: NUMPAD } // [USB: 0x53]
});
mergeIf(locationTable[NUMPAD], 'safari-mac', {
0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57]
0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63]
0xBF: { code: 'NumpadDivide', location: NUMPAD } // [USB: 0x54]
});
//--------------------------------------------------------------------
//
// Key Values
//
//--------------------------------------------------------------------
// Mapping from `code` values to `key` values. Values defined at:
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
// Entries are only provided when `key` differs from `code`. If
// printable, `shiftKey` has the shifted printable character. This
// assumes US Standard 101 layout
var codeToKeyTable = {
// Modifier Keys
ShiftLeft: { key: 'Shift' },
ShiftRight: { key: 'Shift' },
ControlLeft: { key: 'Control' },
ControlRight: { key: 'Control' },
AltLeft: { key: 'Alt' },
AltRight: { key: 'Alt' },
OSLeft: { key: 'OS' },
OSRight: { key: 'OS' },
// Whitespace Keys
NumpadEnter: { key: 'Enter' },
Space: { key: ' ' },
// Printable Keys
Digit0: { key: '0', shiftKey: ')' },
Digit1: { key: '1', shiftKey: '!' },
Digit2: { key: '2', shiftKey: '@' },
Digit3: { key: '3', shiftKey: '#' },
Digit4: { key: '4', shiftKey: '$' },
Digit5: { key: '5', shiftKey: '%' },
Digit6: { key: '6', shiftKey: '^' },
Digit7: { key: '7', shiftKey: '&' },
Digit8: { key: '8', shiftKey: '*' },
Digit9: { key: '9', shiftKey: '(' },
KeyA: { key: 'a', shiftKey: 'A' },
KeyB: { key: 'b', shiftKey: 'B' },
KeyC: { key: 'c', shiftKey: 'C' },
KeyD: { key: 'd', shiftKey: 'D' },
KeyE: { key: 'e', shiftKey: 'E' },
KeyF: { key: 'f', shiftKey: 'F' },
KeyG: { key: 'g', shiftKey: 'G' },
KeyH: { key: 'h', shiftKey: 'H' },
KeyI: { key: 'i', shiftKey: 'I' },
KeyJ: { key: 'j', shiftKey: 'J' },
KeyK: { key: 'k', shiftKey: 'K' },
KeyL: { key: 'l', shiftKey: 'L' },
KeyM: { key: 'm', shiftKey: 'M' },
KeyN: { key: 'n', shiftKey: 'N' },
KeyO: { key: 'o', shiftKey: 'O' },
KeyP: { key: 'p', shiftKey: 'P' },
KeyQ: { key: 'q', shiftKey: 'Q' },
KeyR: { key: 'r', shiftKey: 'R' },
KeyS: { key: 's', shiftKey: 'S' },
KeyT: { key: 't', shiftKey: 'T' },
KeyU: { key: 'u', shiftKey: 'U' },
KeyV: { key: 'v', shiftKey: 'V' },
KeyW: { key: 'w', shiftKey: 'W' },
KeyX: { key: 'x', shiftKey: 'X' },
KeyY: { key: 'y', shiftKey: 'Y' },
KeyZ: { key: 'z', shiftKey: 'Z' },
Numpad0: { key: '0' },
Numpad1: { key: '1' },
Numpad2: { key: '2' },
Numpad3: { key: '3' },
Numpad4: { key: '4' },
Numpad5: { key: '5' },
Numpad6: { key: '6' },
Numpad7: { key: '7' },
Numpad8: { key: '8' },
Numpad9: { key: '9' },
NumpadMultiply: { key: '*' },
NumpadAdd: { key: '+' },
NumpadComma: { key: ',' },
NumpadSubtract: { key: '-' },
NumpadDecimal: { key: '.' },
NumpadDivide: { key: '/' },
Semicolon: { key: ';', shiftKey: ':' },
Equal: { key: '=', shiftKey: '+' },
Comma: { key: ',', shiftKey: '<' },
Minus: { key: '-', shiftKey: '_' },
Period: { key: '.', shiftKey: '>' },
Slash: { key: '/', shiftKey: '?' },
Backquote: { key: '`', shiftKey: '~' },
BracketLeft: { key: '[', shiftKey: '{' },
Backslash: { key: '\\', shiftKey: '|' },
BracketRight: { key: ']', shiftKey: '}' },
Quote: { key: '\'', shiftKey: '"' },
IntlBackslash: { key: '\\', shiftKey: '|' }
};
mergeIf(codeToKeyTable, 'mac', {
OSLeft: { key: 'Meta' },
OSRight: { key: 'Meta' }
});
// Corrections for 'key' names in older browsers (e.g. FF36-)
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values
var keyFixTable = {
Esc: 'Escape',
Nonconvert: 'NonConvert',
Left: 'ArrowLeft',
Up: 'ArrowUp',
Right: 'ArrowRight',
Down: 'ArrowDown',
Del: 'Delete',
Menu: 'ContextMenu',
MediaNextTrack: 'MediaTrackNext',
MediaPreviousTrack: 'MediaTrackPrevious',
SelectMedia: 'MediaSelect',
HalfWidth: 'Hankaku',
FullWidth: 'Zenkaku',
RomanCharacters: 'Romaji',
Crsel: 'CrSel',
Exsel: 'ExSel',
Zoom: 'ZoomToggle'
};
//--------------------------------------------------------------------
//
// Exported Functions
//
//--------------------------------------------------------------------
var codeTable = remap(keyCodeToInfoTable, 'code');
try {
var nativeLocation = nativeKeyboardEvent && ('location' in new KeyboardEvent(''));
} catch (_) {}
function keyInfoForEvent(event) {
var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0;
var keyInfo = (function(){
if (nativeLocation || 'keyLocation' in event) {
var location = nativeLocation ? event.location : event.keyLocation;
if (location && keyCode in locationTable[location]) {
return locationTable[location][keyCode];
}
}
if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) {
return keyIdentifierTable[event.keyIdentifier];
}
if (keyCode in keyCodeToInfoTable) {
return keyCodeToInfoTable[keyCode];
}
return null;
}());
// TODO: Track these down and move to general tables
if (0) {
// TODO: Map these for newerish browsers?
// TODO: iOS only?
// TODO: Override with more common keyIdentifier name?
switch (event.keyIdentifier) {
case 'U+0010': keyInfo = { code: 'Function' }; break;
case 'U+001C': keyInfo = { code: 'ArrowLeft' }; break;
case 'U+001D': keyInfo = { code: 'ArrowRight' }; break;
case 'U+001E': keyInfo = { code: 'ArrowUp' }; break;
case 'U+001F': keyInfo = { code: 'ArrowDown' }; break;
}
}
if (!keyInfo)
return null;
var key = (function() {
var entry = codeToKeyTable[keyInfo.code];
if (!entry) return keyInfo.code;
return (event.shiftKey && 'shiftKey' in entry) ? entry.shiftKey : entry.key;
}());
return {
code: keyInfo.code,
key: key,
location: keyInfo.location,
keyCap: keyInfo.keyCap
};
}
function queryKeyCap(code, locale) {
code = String(code);
if (!codeTable.hasOwnProperty(code)) return 'Undefined';
if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale');
var keyInfo = codeTable[code];
return keyInfo.keyCap || keyInfo.code || 'Undefined';
}
if ('KeyboardEvent' in global && 'defineProperty' in Object) {
(function() {
function define(o, p, v) {
if (p in o) return;
Object.defineProperty(o, p, v);
}
define(KeyboardEvent.prototype, 'code', { get: function() {
var keyInfo = keyInfoForEvent(this);
return keyInfo ? keyInfo.code : '';
}});
// Fix for nonstandard `key` values (FF36-)
if ('key' in KeyboardEvent.prototype) {
var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key');
Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function() {
var key = desc.get.call(this);
return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key;
}});
}
define(KeyboardEvent.prototype, 'key', { get: function() {
var keyInfo = keyInfoForEvent(this);
return (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
}});
define(KeyboardEvent.prototype, 'location', { get: function() {
var keyInfo = keyInfoForEvent(this);
return (keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
}});
define(KeyboardEvent.prototype, 'locale', { get: function() {
return '';
}});
}());
}
if (!('queryKeyCap' in global.KeyboardEvent))
global.KeyboardEvent.queryKeyCap = queryKeyCap;
// Helper for IE8-
global.identifyKey = function(event) {
if ('code' in event)
return;
var keyInfo = keyInfoForEvent(event);
event.code = keyInfo ? keyInfo.code : '';
event.key = (keyInfo && 'key' in keyInfo) ? keyInfo.key : 'Unidentified';
event.location = ('location' in event) ? event.location :
('keyLocation' in event) ? event.keyLocation :
(keyInfo && 'location' in keyInfo) ? keyInfo.location : STANDARD;
event.locale = '';
};
} (window));
},{}],7:[function(require,module,exports){
var CANNON = require('cannon'),
math = require('./src/components/math');
module.exports = {
'dynamic-body': require('./src/components/body/dynamic-body'),
'static-body': require('./src/components/body/static-body'),
'system': require('./src/system/physics'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
math.registerAll();
if (!AFRAME.systems.physics) AFRAME.registerSystem('physics', this.system);
if (!AFRAME.components['dynamic-body']) AFRAME.registerComponent('dynamic-body', this['dynamic-body']);
if (!AFRAME.components['static-body']) AFRAME.registerComponent('static-body', this['static-body']);
this._registered = true;
}
};
// Export CANNON.js.
window.CANNON = window.CANNON || CANNON;
},{"./src/components/body/dynamic-body":10,"./src/components/body/static-body":11,"./src/components/math":12,"./src/system/physics":16,"cannon":45}],8:[function(require,module,exports){
/**
* CANNON.shape2mesh
*
* Source: http://schteppe.github.io/cannon.js/build/cannon.demo.js
* Author: @schteppe
*/
var CANNON = require('cannon');
CANNON.shape2mesh = function(body){
var obj = new THREE.Object3D();
for (var l = 0; l < body.shapes.length; l++) {
var shape = body.shapes[l];
var mesh;
switch(shape.type){
case CANNON.Shape.types.SPHERE:
var sphere_geometry = new THREE.SphereGeometry( shape.radius, 8, 8);
mesh = new THREE.Mesh( sphere_geometry, this.currentMaterial );
break;
case CANNON.Shape.types.PARTICLE:
mesh = new THREE.Mesh( this.particleGeo, this.particleMaterial );
var s = this.settings;
mesh.scale.set(s.particleSize,s.particleSize,s.particleSize);
break;
case CANNON.Shape.types.PLANE:
var geometry = new THREE.PlaneGeometry(10, 10, 4, 4);
mesh = new THREE.Object3D();
var submesh = new THREE.Object3D();
var ground = new THREE.Mesh( geometry, this.currentMaterial );
ground.scale.set(100, 100, 100);
submesh.add(ground);
ground.castShadow = true;
ground.receiveShadow = true;
mesh.add(submesh);
break;
case CANNON.Shape.types.BOX:
var box_geometry = new THREE.BoxGeometry( shape.halfExtents.x*2,
shape.halfExtents.y*2,
shape.halfExtents.z*2 );
mesh = new THREE.Mesh( box_geometry, this.currentMaterial );
break;
case CANNON.Shape.types.CONVEXPOLYHEDRON:
var geo = new THREE.Geometry();
// Add vertices
for (var i = 0; i < shape.vertices.length; i++) {
var v = shape.vertices[i];
geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
}
for(var i=0; i < shape.faces.length; i++){
var face = shape.faces[i];
// add triangles
var a = face[0];
for (var j = 1; j < face.length - 1; j++) {
var b = face[j];
var c = face[j + 1];
geo.faces.push(new THREE.Face3(a, b, c));
}
}
geo.computeBoundingSphere();
geo.computeFaceNormals();
mesh = new THREE.Mesh( geo, this.currentMaterial );
break;
case CANNON.Shape.types.HEIGHTFIELD:
var geometry = new THREE.Geometry();
var v0 = new CANNON.Vec3();
var v1 = new CANNON.Vec3();
var v2 = new CANNON.Vec3();
for (var xi = 0; xi < shape.data.length - 1; xi++) {
for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
for (var k = 0; k < 2; k++) {
shape.getConvexTrianglePillar(xi, yi, k===0);
v0.copy(shape.pillarConvex.vertices[0]);
v1.copy(shape.pillarConvex.vertices[1]);
v2.copy(shape.pillarConvex.vertices[2]);
v0.vadd(shape.pillarOffset, v0);
v1.vadd(shape.pillarOffset, v1);
v2.vadd(shape.pillarOffset, v2);
geometry.vertices.push(
new THREE.Vector3(v0.x, v0.y, v0.z),
new THREE.Vector3(v1.x, v1.y, v1.z),
new THREE.Vector3(v2.x, v2.y, v2.z)
);
var i = geometry.vertices.length - 3;
geometry.faces.push(new THREE.Face3(i, i+1, i+2));
}
}
}
geometry.computeBoundingSphere();
geometry.computeFaceNormals();
mesh = new THREE.Mesh(geometry, this.currentMaterial);
break;
case CANNON.Shape.types.TRIMESH:
var geometry = new THREE.Geometry();
var v0 = new CANNON.Vec3();
var v1 = new CANNON.Vec3();
var v2 = new CANNON.Vec3();
for (var i = 0; i < shape.indices.length / 3; i++) {
shape.getTriangleVertices(i, v0, v1, v2);
geometry.vertices.push(
new THREE.Vector3(v0.x, v0.y, v0.z),
new THREE.Vector3(v1.x, v1.y, v1.z),
new THREE.Vector3(v2.x, v2.y, v2.z)
);
var j = geometry.vertices.length - 3;
geometry.faces.push(new THREE.Face3(j, j+1, j+2));
}
geometry.computeBoundingSphere();
geometry.computeFaceNormals();
mesh = new THREE.Mesh(geometry, this.currentMaterial);
break;
default:
throw "Visual type not recognized: "+shape.type;
}
mesh.receiveShadow = true;
mesh.castShadow = true;
if(mesh.children){
for(var i=0; i<mesh.children.length; i++){
mesh.children[i].castShadow = true;
mesh.children[i].receiveShadow = true;
if(mesh.children[i]){
for(var j=0; j<mesh.children[i].length; j++){
mesh.children[i].children[j].castShadow = true;
mesh.children[i].children[j].receiveShadow = true;
}
}
}
}
var o = body.shapeOffsets[l];
var q = body.shapeOrientations[l];
mesh.position.set(o.x, o.y, o.z);
mesh.quaternion.set(q.x, q.y, q.z, q.w);
obj.add(mesh);
}
return obj;
};
module.exports = CANNON.shape2mesh;
},{"cannon":45}],9:[function(require,module,exports){
var CANNON = require('cannon'),
mesh2shape = require('three-to-cannon');
require('../../../lib/CANNON-shape2mesh');
module.exports = {
schema: {
shape: {default: 'auto', oneOf: ['auto', 'box', 'cylinder', 'sphere', 'hull']},
cylinderAxis: {default: 'y', oneOf: ['x', 'y', 'z']},
sphereRadius: {default: NaN}
},
/**
* Initializes a body component, assigning it to the physics system and binding listeners for
* parsing the elements geometry.
*/
init: function () {
this.system = this.el.sceneEl.systems.physics;
if (this.el.sceneEl.hasLoaded) {
this.initBody();
} else {
this.el.sceneEl.addEventListener('loaded', this.initBody.bind(this));
}
},
/**
* Parses an element's geometry and component metadata to create a CANNON.Body instance for the
* component.
*/
initBody: function () {
var shape,
el = this.el,
data = this.data,
pos = el.getComputedAttribute('position'),
options = data.shape === 'auto' ? undefined : AFRAME.utils.extend({}, this.data, {
type: mesh2shape.Type[data.shape.toUpperCase()]
});
// Matrix World must be updated at root level, if scale is to be applied – updateMatrixWorld()
// only checks an object's parent, not the rest of the ancestors. Hence, a wrapping entity with
// scale="0.5 0.5 0.5" will be ignored.
// Reference: https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L511-L541
// Potential fix: https://github.com/mrdoob/three.js/pull/7019
this.el.object3D.updateMatrixWorld(true);
shape = mesh2shape(this.el.object3D, options);
if (!shape) {
this.el.addEventListener('model-loaded', this.initBody.bind(this));
return;
}
this.body = new CANNON.Body({
mass: data.mass || 0,
material: this.system.material,
position: new CANNON.Vec3(pos.x, pos.y, pos.z),
linearDamping: data.linearDamping,
angularDamping: data.angularDamping
});
this.body.addShape(shape, shape.offset, shape.orientation);
// Apply rotation
var rot = el.getComputedAttribute('rotation');
this.body.quaternion.setFromEuler(
THREE.Math.degToRad(rot.x),
THREE.Math.degToRad(rot.y),
THREE.Math.degToRad(rot.z),
'XYZ'
).normalize();
// Show wireframe
if (this.system.debug) {
this.createWireframe(this.body, shape);
}
this.el.body = this.body;
this.body.el = this.el;
this.isLoaded = true;
// If component wasn't initialized when play() was called, finish up.
if (this.isPlaying) {
this._play();
}
this.el.emit('body-loaded', {body: this.el.body});
},
/**
* Registers the component with the physics system, if ready.
*/
play: function () {
if (this.isLoaded) this._play();
},
/**
* Internal helper to register component with physics system.
*/
_play: function () {
this.system.addBehavior(this, this.system.Phase.SIMULATE);
this.system.addBody(this.body);
if (this.wireframe) this.el.sceneEl.object3D.add(this.wireframe);
this.syncToPhysics();
},
/**
* Unregisters the component with the physics system.
*/
pause: function () {
if (!this.isLoaded) return;
this.system.removeBehavior(this, this.system.Phase.SIMULATE);
this.system.removeBody(this.body);
if (this.wireframe) this.el.sceneEl.object3D.remove(this.wireframe);
},
/**
* Removes the component and all physics and scene side effects.
*/
remove: function () {
this.pause();
delete this.body.el;
delete this.body;
delete this.el.body;
delete this.wireframe;
},
/**
* Creates a wireframe for the body, for debugging.
* TODO(donmccurdy) – Refactor this into a standalone utility or component.
* @param {CANNON.Body} body
* @param {CANNON.Shape} shape
*/
createWireframe: function (body, shape) {
var offset = shape.offset,
orientation = shape.orientation,
mesh = CANNON.shape2mesh(body).children[0];
this.wireframe = new THREE.EdgesHelper(mesh, 0xff0000);
if (offset) {
this.wireframe.offset = offset.clone();
}
if (orientation) {
orientation.inverse(orientation);
this.wireframe.orientation = new THREE.Quaternion(
orientation.x,
orientation.y,
orientation.z,
orientation.w
);
}
this.syncWireframe();
},
/**
* Updates the debugging wireframe's position and rotation.
*/
syncWireframe: function () {
var offset,
wireframe = this.wireframe;
if (!this.wireframe) return;
// Apply rotation. If the shape required custom orientation, also apply
// that on the wireframe.
wireframe.quaternion.copy(this.body.quaternion);
if (wireframe.orientation) {
wireframe.quaternion.multiply(wireframe.orientation);
}
// Apply position. If the shape required custom offset, also apply that on
// the wireframe.
wireframe.position.copy(this.body.position);
if (wireframe.offset) {
offset = wireframe.offset.clone().applyQuaternion(wireframe.quaternion);
wireframe.position.add(offset);
}
wireframe.updateMatrix();
},
/**
* Updates the CANNON.Body instance's position, velocity, and rotation, based on the scene.
*/
syncToPhysics: (function () {
var q = new THREE.Quaternion(),
v = new THREE.Vector3();
return function () {
var el = this.el,
parentEl = el.parentEl,
body = this.body;
if (!body) return;
if (el.components.velocity) body.velocity.copy(el.getComputedAttribute('velocity'));
if (parentEl.isScene) {
body.quaternion.copy(el.object3D.quaternion);
body.position.copy(el.object3D.position);
} else {
el.object3D.getWorldQuaternion(q);
body.quaternion.copy(q);
el.object3D.getWorldPosition(v);
body.position.copy(v);
}
if (this.wireframe) this.syncWireframe();
};
}()),
/**
* Updates the scene object's position and rotation, based on the physics simulation.
*/
syncFromPhysics: (function () {
var v = new THREE.Vector3(),
q1 = new THREE.Quaternion(),
q2 = new THREE.Quaternion();
return function () {
var el = this.el,
parentEl = el.parentEl,
body = this.body;
if (!body) return;
if (parentEl.isScene) {
el.setAttribute('quaternion', body.quaternion);
el.setAttribute('position', body.position);
} else {
// TODO - Nested rotation doesn't seem to be working as expected.
q1.copy(body.quaternion);
parentEl.object3D.getWorldQuaternion(q2);
q1.multiply(q2.inverse());
el.setAttribute('quaternion', {x: q1.x, y: q1.y, z: q1.z, w: q1.w});
v.copy(body.position);
parentEl.object3D.worldToLocal(v);
el.setAttribute('position', {x: v.x, y: v.y, z: v.z});
}
if (this.wireframe) this.syncWireframe();
};
}())
};
},{"../../../lib/CANNON-shape2mesh":8,"cannon":45,"three-to-cannon":17}],10:[function(require,module,exports){
var Body = require('./body');
/**
* Dynamic body.
*
* Moves according to physics simulation, and may collide with other objects.
*/
module.exports = AFRAME.utils.extend({}, Body, {
dependencies: ['quaternion', 'velocity'],
schema: AFRAME.utils.extend({}, Body.schema, {
mass: { default: 5 },
linearDamping: { default: 0.01 },
angularDamping: { default: 0.01 }
}),
step: function () {
this.syncFromPhysics();
}
});
},{"./body":9}],11:[function(require,module,exports){
var Body = require('./body');
/**
* Static body.
*
* Solid body with a fixed position. Unaffected by gravity and collisions, but
* other objects may collide with it.
*/
module.exports = AFRAME.utils.extend({}, Body, {
step: function () {
this.syncToPhysics();
}
});
},{"./body":9}],12:[function(require,module,exports){
module.exports = {
'velocity': require('./velocity'),
'quaternion': require('./quaternion'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
if (!AFRAME.components['velocity']) AFRAME.registerComponent('velocity', this.velocity);
if (!AFRAME.components['quaternion']) AFRAME.registerComponent('quaternion', this.quaternion);
this._registered = true;
}
};
},{"./quaternion":13,"./velocity":14}],13:[function(require,module,exports){
/**
* Quaternion.
*
* Represents orientation of object in three dimensions. Similar to `rotation`
* component, but avoids problems of gimbal lock.
*
* See: https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
*/
module.exports = {
schema: {type: 'vec4'},
play: function () {
var el = this.el,
q = el.object3D.quaternion;
if (el.hasAttribute('rotation')) {
el.components.rotation.update();
el.setAttribute('quaternion', {x: q.x, y: q.y, z: q.z, w: q.w});
el.removeAttribute('rotation');
this.update();
}
},
update: function () {
var data = this.data;
this.el.object3D.quaternion.set(data.x, data.y, data.z, data.w);
}
};
},{}],14:[function(require,module,exports){
/**
* Velocity, in m/s.
*/
module.exports = {
schema: {type: 'vec3'},
init: function () {
this.system = this.el.sceneEl.systems.physics;
if (this.system) {
this.system.addBehavior(this, this.system.Phase.RENDER);
}
},
remove: function () {
if (this.system) {
this.system.removeBehavior(this, this.system.Phase.RENDER);
}
},
tick: function (t, dt) {
if (!dt) return;
if (this.system) return;
this.step(t, dt);
},
step: function (t, dt) {
if (!dt) return;
var physics = this.el.sceneEl.systems.physics || {data: {maxInterval: 1 / 60}},
// TODO - There's definitely a bug with getComputedAttribute and el.data.
velocity = this.el.getAttribute('velocity') || {x: 0, y: 0, z: 0},
position = this.el.getAttribute('position') || {x: 0, y: 0, z: 0};
dt = Math.min(dt, physics.data.maxInterval * 1000);
this.el.setAttribute('position', {
x: position.x + velocity.x * dt / 1000,
y: position.y + velocity.y * dt / 1000,
z: position.z + velocity.z * dt / 1000
});
}
};
},{}],15:[function(require,module,exports){
module.exports = {
GRAVITY: -9.8,
MAX_INTERVAL: 4 / 60,
ITERATIONS: 10,
CONTACT_MATERIAL: {
friction: 0.01,
restitution: 0.3,
contactEquationStiffness: 1e8,
contactEquationRelaxation: 3,
frictionEquationStiffness: 1e8,
frictionEquationRegularization: 3
}
};
},{}],16:[function(require,module,exports){
var CANNON = require('cannon'),
CONSTANTS = require('../constants'),
C_GRAV = CONSTANTS.GRAVITY,
C_MAT = CONSTANTS.CONTACT_MATERIAL;
/**
* Physics system.
*/
module.exports = {
schema: {
gravity: { default: C_GRAV },
iterations: { default: CONSTANTS.ITERATIONS },
friction: { default: C_MAT.friction },
restitution: { default: C_MAT.restitution },
contactEquationStiffness: { default: C_MAT.contactEquationStiffness },
contactEquationRelaxation: { default: C_MAT.contactEquationRelaxation },
frictionEquationStiffness: { default: C_MAT.frictionEquationStiffness },
frictionEquationRegularization: { default: C_MAT.frictionEquationRegularization },
// Never step more than four frames at once. Effectively pauses the scene
// when out of focus, and prevents weird "jumps" when focus returns.
maxInterval: { default: 4 / 60 },
// If true, show wireframes around physics bodies.
debug: { default: false },
},
/**
* Update phases, used to separate physics simulation from updates to A-Frame scene.
* @enum {string}
*/
Phase: {
SIMULATE: 'sim',
RENDER: 'render'
},
/**
* Initializes the physics system.
*/
init: function () {
var data = this.data;
// If true, show wireframes around physics bodies.
this.debug = data.debug;
this.children = {};
this.children[this.Phase.SIMULATE] = [];
this.children[this.Phase.RENDER] = [];
this.listeners = {};
this.world = new CANNON.World();
this.world.quatNormalizeSkip = 0;
this.world.quatNormalizeFast = false;
// this.world.solver.setSpookParams(300,10);
this.world.solver.iterations = data.iterations;
this.world.gravity.set(0, data.gravity, 0);
this.world.broadphase = new CANNON.NaiveBroadphase();
this.material = new CANNON.Material({name: 'defaultMaterial'});
this.contactMaterial = new CANNON.ContactMaterial(this.material, this.material, {
friction: data.friction,
restitution: data.restitution,
contactEquationStiffness: data.contactEquationStiffness,
contactEquationRelaxation: data.contactEquationRelaxation,
frictionEquationStiffness: data.frictionEquationStiffness,
frictionEquationRegularization: data.frictionEquationRegularization
});
this.world.addContactMaterial(this.contactMaterial);
},
/**
* Updates the physics world on each tick of the A-Frame scene. It would be
* entirely possible to separate the two – updating physics more or less
* frequently than the scene – if greater precision or performance were
* necessary.
* @param {number} t
* @param {number} dt
*/
tick: function (t, dt) {
if (!dt) return;
this.world.step(Math.min(dt / 1000, this.data.maxInterval));
var i;
for (i = 0; i < this.children[this.Phase.SIMULATE].length; i++) {
this.children[this.Phase.SIMULATE][i].step(t, dt);
}
for (i = 0; i < this.children[this.Phase.RENDER].length; i++) {
this.children[this.Phase.RENDER][i].step(t, dt);
}
},
/**
* Adds a body to the scene, and binds collision events to the element.
* @param {CANNON.Body} body
*/
addBody: function (body) {
this.listeners[body.id] = function (e) { body.el.emit('collide', e); };
body.addEventListener('collide', this.listeners[body.id]);
this.world.addBody(body);
},
/**
* Removes a body, and its listeners, from the scene.
* @param {CANNON.Body} body
*/
removeBody: function (body) {
body.removeEventListener('collide', this.listeners[body.id]);
delete this.listeners[body.id];
this.world.removeBody(body);
},
/**
* Adds a component instance to the system, to be invoked on each tick during
* the given phase.
* @param {Component} component
* @param {string} phase
*/
addBehavior: function (component, phase) {
this.children[phase].push(component);
},
/**
* Removes a component instance from the system.
* @param {Component} component
* @param {string} phase
*/
removeBehavior: function (component, phase) {
this.children[phase].splice(this.children[phase].indexOf(component), 1);
},
/**
* Sets an option on the physics system, affecting future simulation steps.
* @param {string} opt
* @param {mixed} value
*/
update: function (previousData) {
var data = this.data;
if (data.debug !== previousData.debug) {
console.warn('[physics] `debug` cannot be changed dynamically.');
}
if (data.maxInterval !== previousData.maxInterval); // noop;
if (data.gravity !== previousData.gravity) this.world.gravity.set(0, data.gravity, 0);
this.contactMaterial.friction = data.friction;
this.contactMaterial.restitution = data.restitution;
this.contactMaterial.contactEquationStiffness = data.contactEquationStiffness;
this.contactMaterial.contactEquationRelaxation = data.contactEquationRelaxation;
this.contactMaterial.frictionEquationStiffness = data.frictionEquationStiffness;
this.contactMaterial.frictionEquationRegularization = data.frictionEquationRegularization;
}
};
},{"../constants":15,"cannon":45}],17:[function(require,module,exports){
var CANNON = require('cannon'),
quickhull = require('./lib/THREE.quickhull');
var PI_2 = Math.PI / 2;
var Type = {
BOX: 'Box',
CYLINDER: 'Cylinder',
SPHERE: 'Sphere',
HULL: 'ConvexPolyhedron'
};
/**
* Given a THREE.Object3D instance, creates a corresponding CANNON shape.
* @param {THREE.Object3D} object
* @return {CANNON.Shape}
*/
module.exports = CANNON.mesh2shape = function (object, options) {
options = options || {};
if (options.type === Type.BOX) {
return createBoundingBoxShape(object);
} else if (options.type === Type.CYLINDER) {
return createBoundingCylinderShape(object, options);
} else if (options.type === Type.SPHERE) {
return createBoundingSphereShape(object, options);
} else if (options.type === Type.HULL) {
return createConvexPolyhedron(object);
} else if (options.type) {
throw new Error('[CANNON.mesh2shape] Invalid type "%s".', options.type);
}
var geometry = getGeometry(object);
if (!geometry) return null;
var type = geometry.metadata
? geometry.metadata.type
: geometry.type;
switch (type) {
case 'BoxGeometry':
case 'BoxBufferGeometry':
return createBoxShape(geometry);
case 'CylinderGeometry':
case 'CylinderBufferGeometry':
return createCylinderShape(geometry);
case 'PlaneGeometry':
case 'PlaneBufferGeometry':
return createPlaneShape(geometry);
case 'SphereGeometry':
case 'SphereBufferGeometry':
return createSphereShape(geometry);
case 'TubeGeometry':
return createTubeShape(geometry);
case 'Geometry':
case 'BufferGeometry':
return createTrimeshShape(geometry);
default:
console.warn('Unrecognized geometry: "%s". Using bounding box as shape.', geometry.type);
return createBoxShape(geometry);
}
};
CANNON.mesh2shape.Type = Type;
/******************************************************************************
* Shape construction
*/
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createBoxShape (geometry) {
var vertices = getVertices(geometry);
if (!vertices.length) return null;
geometry.computeBoundingBox();
var box = geometry.boundingBox;
return new CANNON.Box(new CANNON.Vec3(
(box.max.x - box.min.x) / 2,
(box.max.y - box.min.y) / 2,
(box.max.z - box.min.z) / 2
));
}
/**
* Bounding box needs to be computed with the entire mesh, not just geometry.
* @param {THREE.Object3D} mesh
* @return {CANNON.Shape}
*/
function createBoundingBoxShape (object) {
var box, shape, localPosition, worldPosition,
helper = new THREE.BoundingBoxHelper(object);
helper.update();
box = helper.box;
if (!isFinite(box.min.lengthSq())) return null;
shape = new CANNON.Box(new CANNON.Vec3(
(box.max.x - box.min.x) / 2,
(box.max.y - box.min.y) / 2,
(box.max.z - box.min.z) / 2
));
object.updateMatrixWorld();
worldPosition = new THREE.Vector3();
worldPosition.setFromMatrixPosition(object.matrixWorld);
localPosition = helper.position.sub(worldPosition);
if (localPosition.lengthSq()) {
shape.offset = localPosition;
}
return shape;
}
/**
* Computes 3D convex hull as a CANNON.ConvexPolyhedron.
* @param {THREE.Object3D} mesh
* @return {CANNON.Shape}
*/
function createConvexPolyhedron (object) {
var i, vertices, faces, hull,
eps = 1e-4,
geometry = getGeometry(object);
if (!geometry || !geometry.vertices.length) return null;
// Perturb.
for (i = 0; i < geometry.vertices.length; i++) {
geometry.vertices[i].x += (Math.random() - 0.5) * eps;
geometry.vertices[i].y += (Math.random() - 0.5) * eps;
geometry.vertices[i].z += (Math.random() - 0.5) * eps;
}
// Compute the 3D convex hull.
hull = quickhull(geometry);
// Convert from THREE.Vector3 to CANNON.Vec3.
vertices = new Array(hull.vertices.length);
for (i = 0; i < hull.vertices.length; i++) {
vertices[i] = new CANNON.Vec3(hull.vertices[i].x, hull.vertices[i].y, hull.vertices[i].z);
}
// Convert from THREE.Face to Array<number>.
faces = new Array(hull.faces.length);
for (i = 0; i < hull.faces.length; i++) {
faces[i] = [hull.faces[i].a, hull.faces[i].b, hull.faces[i].c];
}
return new CANNON.ConvexPolyhedron(vertices, faces);
}
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createCylinderShape (geometry) {
var shape,
params = geometry.metadata
? geometry.metadata.parameters
: geometry.parameters;
shape = new CANNON.Cylinder(
params.radiusTop,
params.radiusBottom,
params.height,
params.radialSegments
);
shape.orientation = new CANNON.Quaternion();
shape.orientation.setFromEuler(THREE.Math.degToRad(-90), 0, 0, 'XYZ').normalize();
return shape;
}
/**
* @param {THREE.Object3D} object
* @return {CANNON.Shape}
*/
function createBoundingCylinderShape (object, options) {
var shape, height, radius,
geometry = getGeometry(object),
axes = ['x', 'y', 'z'],
majorAxis = options.cylinderAxis || 'y',
minorAxes = axes.splice(axes.indexOf(majorAxis), 1) && axes;
// Compute cylinder dimensions.
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
height = geometry.boundingBox.max[majorAxis] - geometry.boundingBox.min[majorAxis];
radius = 0.5 * Math.max(
geometry.boundingBox.max[minorAxes[0]] - geometry.boundingBox.min[minorAxes[0]],
geometry.boundingBox.max[minorAxes[1]] - geometry.boundingBox.min[minorAxes[1]]
);
// Create shape.
shape = new CANNON.Cylinder(radius, radius, height, 12);
shape.orientation = new CANNON.Quaternion();
shape.orientation.setFromEuler(
majorAxis === 'y' ? PI_2 : 0,
majorAxis === 'z' ? PI_2 : 0,
0,
'XYZ'
).normalize();
return shape;
}
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createPlaneShape (geometry) {
geometry.computeBoundingBox();
var box = geometry.boundingBox;
return new CANNON.Box(new CANNON.Vec3(
(box.max.x - box.min.x) / 2 || 0.1,
(box.max.y - box.min.y) / 2 || 0.1,
(box.max.z - box.min.z) / 2 || 0.1
));
}
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createSphereShape (geometry) {
var params = geometry.metadata
? geometry.metadata.parameters
: geometry.parameters;
return new CANNON.Sphere(params.radius);
}
/**
* @param {THREE.Object3D} object
* @return {CANNON.Shape}
*/
function createBoundingSphereShape (object, options) {
var geometry = getGeometry(object);
geometry.computeBoundingSphere();
return new CANNON.Sphere(options.sphereRadius || geometry.boundingSphere.radius);
}
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createTubeShape (geometry) {
var tmp = new THREE.BufferGeometry();
tmp.fromGeometry(geometry);
return createTrimeshShape(tmp);
}
/**
* @param {THREE.Geometry} geometry
* @return {CANNON.Shape}
*/
function createTrimeshShape (geometry) {
var indices,
vertices = getVertices(geometry);
if (!vertices.length) return null;
indices = Object.keys(vertices).map(Number);
return new CANNON.Trimesh(vertices, indices);
}
/******************************************************************************
* Utils
*/
/**
* Returns a single geometry for the given object. If the object is compound,
* its geometries are automatically merged.
* @param {THREE.Object3D} object
* @return {THREE.Geometry}
*/
function getGeometry (object) {
var matrix, mesh,
meshes = getMeshes(object),
tmp = new THREE.Geometry(),
combined = new THREE.Geometry();
if (meshes.length === 0) return null;
// Apply scale – it can't easily be applied to a CANNON.Shape later.
if (meshes.length === 1) {
var position = new THREE.Vector3(),
quaternion = new THREE.Quaternion(),
scale = new THREE.Vector3();
tmp = meshes[0].geometry.clone();
tmp.metadata = meshes[0].geometry.metadata;
meshes[0].updateMatrixWorld();
meshes[0].matrixWorld.decompose(position, quaternion, scale);
return tmp.scale(scale.x, scale.y, scale.z);
}
// Recursively merge geometry, preserving local transforms.
while ((mesh = meshes.pop())) {
mesh.updateMatrixWorld();
if (mesh.geometry instanceof THREE.BufferGeometry) {
tmp.fromBufferGeometry(mesh.geometry);
combined.merge(tmp, mesh.matrixWorld);
} else {
combined.merge(mesh.geometry, mesh.matrixWorld);
}
}
matrix = new THREE.Matrix4();
matrix.scale(object.scale);
combined.applyMatrix(matrix);
return combined;
}
/**
* @param {THREE.Geometry} geometry
* @return {Array<number>}
*/
function getVertices (geometry) {
if (!geometry.attributes) {
geometry = new THREE.BufferGeometry().fromGeometry(geometry);
}
return geometry.attributes.position.array;
}
/**
* Returns a flat array of THREE.Mesh instances from the given object. If
* nested transformations are found, they are applied to child meshes
* as mesh.userData.matrix, so that each mesh has its position/rotation/scale
* independently of all of its parents except the top-level object.
* @param {THREE.Object3D} object
* @return {Array<THREE.Mesh>}
*/
function getMeshes (object) {
var meshes = [];
object.traverse(function (o) {
if (o.type === 'Mesh') {
meshes.push(o);
}
});
return meshes;
}
},{"./lib/THREE.quickhull":18,"cannon":45}],18:[function(require,module,exports){
/**
QuickHull
---------
The MIT License
Copyright &copy; 2010-2014 three.js authors
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.
@author mark lundin / http://mark-lundin.com
This is a 3D implementation of the Quick Hull algorithm.
It is a fast way of computing a convex hull with average complexity
of O(n log(n)).
It uses depends on three.js and is supposed to create THREE.Geometry.
It's also very messy
*/
module.exports = (function(){
var faces = [],
faceStack = [],
i, NUM_POINTS, extremes,
max = 0,
dcur, current, j, v0, v1, v2, v3,
N, D;
var ab, ac, ax,
suba, subb, normal,
diff, subaA, subaB, subC;
function reset(){
ab = new THREE.Vector3(),
ac = new THREE.Vector3(),
ax = new THREE.Vector3(),
suba = new THREE.Vector3(),
subb = new THREE.Vector3(),
normal = new THREE.Vector3(),
diff = new THREE.Vector3(),
subaA = new THREE.Vector3(),
subaB = new THREE.Vector3(),
subC = new THREE.Vector3();
}
//temporary vectors
function process( points ){
// Iterate through all the faces and remove
while( faceStack.length > 0 ){
cull( faceStack.shift(), points );
}
}
var norm = function(){
var ca = new THREE.Vector3(),
ba = new THREE.Vector3(),
N = new THREE.Vector3();
return function( a, b, c ){
ca.subVectors( c, a );
ba.subVectors( b, a );
N.crossVectors( ca, ba );
return N.normalize();
}
}();
function getNormal( face, points ){
if( face.normal !== undefined ) return face.normal;
var p0 = points[face[0]],
p1 = points[face[1]],
p2 = points[face[2]];
ab.subVectors( p1, p0 );
ac.subVectors( p2, p0 );
normal.crossVectors( ac, ab );
normal.normalize();
return face.normal = normal.clone();
}
function assignPoints( face, pointset, points ){
// ASSIGNING POINTS TO FACE
var p0 = points[face[0]],
dots = [], apex,
norm = getNormal( face, points );
// Sory all the points by there distance from the plane
pointset.sort( function( aItem, bItem ){
dots[aItem.x/3] = dots[aItem.x/3] !== undefined ? dots[aItem.x/3] : norm.dot( suba.subVectors( aItem, p0 ));
dots[bItem.x/3] = dots[bItem.x/3] !== undefined ? dots[bItem.x/3] : norm.dot( subb.subVectors( bItem, p0 ));
return dots[aItem.x/3] - dots[bItem.x/3] ;
});
//TODO :: Must be a faster way of finding and index in this array
var index = pointset.length;
if( index === 1 ) dots[pointset[0].x/3] = norm.dot( suba.subVectors( pointset[0], p0 ));
while( index-- > 0 && dots[pointset[index].x/3] > 0 )
var point;
if( index + 1 < pointset.length && dots[pointset[index+1].x/3] > 0 ){
face.visiblePoints = pointset.splice( index + 1 );
}
}
function cull( face, points ){
var i = faces.length,
dot, visibleFace, currentFace,
visibleFaces = [face];
var apex = points.indexOf( face.visiblePoints.pop() );
// Iterate through all other faces...
while( i-- > 0 ){
currentFace = faces[i];
if( currentFace !== face ){
// ...and check if they're pointing in the same direction
dot = getNormal( currentFace, points ).dot( diff.subVectors( points[apex], points[currentFace[0]] ));
if( dot > 0 ){
visibleFaces.push( currentFace );
}
}
}
var index, neighbouringIndex, vertex;
// Determine Perimeter - Creates a bounded horizon
// 1. Pick an edge A out of all possible edges
// 2. Check if A is shared by any other face. a->b === b->a
// 2.1 for each edge in each triangle, isShared = ( f1.a == f2.a && f1.b == f2.b ) || ( f1.a == f2.b && f1.b == f2.a )
// 3. If not shared, then add to convex horizon set,
//pick an end point (N) of the current edge A and choose a new edge NA connected to A.
//Restart from 1.
// 4. If A is shared, it is not an horizon edge, therefore flag both faces that share this edge as candidates for culling
// 5. If candidate geometry is a degenrate triangle (ie. the tangent space normal cannot be computed) then remove that triangle from all further processing
var j = i = visibleFaces.length;
var isDistinct = false,
hasOneVisibleFace = i === 1,
cull = [],
perimeter = [],
edgeIndex = 0, compareFace, nextIndex,
a, b;
var allPoints = [];
var originFace = [visibleFaces[0][0], visibleFaces[0][1], visibleFaces[0][1], visibleFaces[0][2], visibleFaces[0][2], visibleFaces[0][0]];
if( visibleFaces.length === 1 ){
currentFace = visibleFaces[0];
perimeter = [currentFace[0], currentFace[1], currentFace[1], currentFace[2], currentFace[2], currentFace[0]];
// remove visible face from list of faces
if( faceStack.indexOf( currentFace ) > -1 ){
faceStack.splice( faceStack.indexOf( currentFace ), 1 );
}
if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
faces.splice( faces.indexOf( currentFace ), 1 );
}else{
while( i-- > 0 ){ // for each visible face
currentFace = visibleFaces[i];
// remove visible face from list of faces
if( faceStack.indexOf( currentFace ) > -1 ){
faceStack.splice( faceStack.indexOf( currentFace ), 1 );
}
if( currentFace.visiblePoints ) allPoints = allPoints.concat( currentFace.visiblePoints );
faces.splice( faces.indexOf( currentFace ), 1 );
var isSharedEdge;
cEdgeIndex = 0;
while( cEdgeIndex < 3 ){ // Iterate through it's edges
isSharedEdge = false;
j = visibleFaces.length;
a = currentFace[cEdgeIndex]
b = currentFace[(cEdgeIndex+1)%3];
while( j-- > 0 && !isSharedEdge ){ // find another visible faces
compareFace = visibleFaces[j];
edgeIndex = 0;
// isSharedEdge = compareFace == currentFace;
if( compareFace !== currentFace ){
while( edgeIndex < 3 && !isSharedEdge ){ //Check all it's indices
nextIndex = ( edgeIndex + 1 );
isSharedEdge = ( compareFace[edgeIndex] === a && compareFace[nextIndex%3] === b ) ||
( compareFace[edgeIndex] === b && compareFace[nextIndex%3] === a );
edgeIndex++;
}
}
}
if( !isSharedEdge || hasOneVisibleFace ){
perimeter.push( a );
perimeter.push( b );
}
cEdgeIndex++;
}
}
}
// create new face for all pairs around edge
i = 0;
var l = perimeter.length/2;
var f;
while( i < l ){
f = [ perimeter[i*2+1], apex, perimeter[i*2] ];
assignPoints( f, allPoints, points );
faces.push( f )
if( f.visiblePoints !== undefined )faceStack.push( f );
i++;
}
}
var distSqPointSegment = function(){
var ab = new THREE.Vector3(),
ac = new THREE.Vector3(),
bc = new THREE.Vector3();
return function( a, b, c ){
ab.subVectors( b, a );
ac.subVectors( c, a );
bc.subVectors( c, b );
var e = ac.dot(ab);
if (e < 0.0) return ac.dot( ac );
var f = ab.dot( ab );
if (e >= f) return bc.dot( bc );
return ac.dot( ac ) - e * e / f;
}
}();
return function( geometry ){
reset();
points = geometry.vertices;
faces = [],
faceStack = [],
i = NUM_POINTS = points.length,
extremes = points.slice( 0, 6 ),
max = 0;
/*
* FIND EXTREMETIES
*/
while( i-- > 0 ){
if( points[i].x < extremes[0].x ) extremes[0] = points[i];
if( points[i].x > extremes[1].x ) extremes[1] = points[i];
if( points[i].y < extremes[2].y ) extremes[2] = points[i];
if( points[i].y < extremes[3].y ) extremes[3] = points[i];
if( points[i].z < extremes[4].z ) extremes[4] = points[i];
if( points[i].z < extremes[5].z ) extremes[5] = points[i];
}
/*
* Find the longest line between the extremeties
*/
j = i = 6;
while( i-- > 0 ){
j = i - 1;
while( j-- > 0 ){
if( max < (dcur = extremes[i].distanceToSquared( extremes[j] )) ){
max = dcur;
v0 = extremes[ i ];
v1 = extremes[ j ];
}
}
}
// 3. Find the most distant point to the line segment, this creates a plane
i = 6;
max = 0;
while( i-- > 0 ){
dcur = distSqPointSegment( v0, v1, extremes[i]);
if( max < dcur ){
max = dcur;
v2 = extremes[ i ];
}
}
// 4. Find the most distant point to the plane.
N = norm(v0, v1, v2);
D = N.dot( v0 );
max = 0;
i = NUM_POINTS;
while( i-- > 0 ){
dcur = Math.abs( points[i].dot( N ) - D );
if( max < dcur ){
max = dcur;
v3 = points[i];
}
}
var v0Index = points.indexOf( v0 ),
v1Index = points.indexOf( v1 ),
v2Index = points.indexOf( v2 ),
v3Index = points.indexOf( v3 );
// We now have a tetrahedron as the base geometry.
// Now we must subdivide the
var tetrahedron =[
[ v2Index, v1Index, v0Index ],
[ v1Index, v3Index, v0Index ],
[ v2Index, v3Index, v1Index ],
[ v0Index, v3Index, v2Index ],
];
subaA.subVectors( v1, v0 ).normalize();
subaB.subVectors( v2, v0 ).normalize();
subC.subVectors ( v3, v0 ).normalize();
var sign = subC.dot( new THREE.Vector3().crossVectors( subaB, subaA ));
// Reverse the winding if negative sign
if( sign < 0 ){
tetrahedron[0].reverse();
tetrahedron[1].reverse();
tetrahedron[2].reverse();
tetrahedron[3].reverse();
}
//One for each face of the pyramid
var pointsCloned = points.slice();
pointsCloned.splice( pointsCloned.indexOf( v0 ), 1 );
pointsCloned.splice( pointsCloned.indexOf( v1 ), 1 );
pointsCloned.splice( pointsCloned.indexOf( v2 ), 1 );
pointsCloned.splice( pointsCloned.indexOf( v3 ), 1 );
var i = tetrahedron.length;
while( i-- > 0 ){
assignPoints( tetrahedron[i], pointsCloned, points );
if( tetrahedron[i].visiblePoints !== undefined ){
faceStack.push( tetrahedron[i] );
}
faces.push( tetrahedron[i] );
}
process( points );
// Assign to our geometry object
var ll = faces.length;
while( ll-- > 0 ){
geometry.faces[ll] = new THREE.Face3( faces[ll][2], faces[ll][1], faces[ll][0], faces[ll].normal )
}
geometry.normalsNeedUpdate = true;
return geometry;
}
}())
},{}],19:[function(require,module,exports){
var EPS = 0.1;
module.exports = {
schema: {
enabled: {default: true},
mode: {default: 'teleport', oneOf: ['teleport', 'animate']},
animateSpeed: {default: 3.0}
},
init: function () {
this.active = true;
this.checkpoint = null;
this.offset = new THREE.Vector3();
this.position = new THREE.Vector3();
this.targetPosition = new THREE.Vector3();
},
play: function () { this.active = true; },
pause: function () { this.active = false; },
setCheckpoint: function (checkpoint) {
if (!this.active) return;
this.checkpoint = checkpoint;
if (this.data.mode === 'teleport') {
this.sync();
this.el.setAttribute('position', this.targetPosition);
}
},
isVelocityActive: function () {
return !!(this.active && this.checkpoint);
},
getVelocity: function () {
if (!this.active) return;
var data = this.data,
offset = this.offset,
position = this.position,
targetPosition = this.targetPosition;
this.sync();
if (position.distanceTo(targetPosition) < EPS) {
this.checkpoint = null;
return offset.set(0, 0, 0);
}
offset.setLength(data.animateSpeed);
return offset;
},
sync: function () {
var offset = this.offset,
position = this.position,
targetPosition = this.targetPosition;
position.copy(this.el.getComputedAttribute('position'));
targetPosition.copy(this.checkpoint.getComputedAttribute('position'));
// TODO - Cleverer ways around this?
targetPosition.y = position.y;
offset.copy(targetPosition).sub(position);
}
};
},{}],20:[function(require,module,exports){
/**
* Gamepad controls for A-Frame.
*
* Stripped-down version of: https://github.com/donmccurdy/aframe-gamepad-controls
*
* For more information about the Gamepad API, see:
* https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
*/
var GamepadButton = require('../../lib/GamepadButton'),
GamepadButtonEvent = require('../../lib/GamepadButtonEvent');
var JOYSTICK_EPS = 0.2;
module.exports = {
/*******************************************************************
* Statics
*/
GamepadButton: GamepadButton,
/*******************************************************************
* Schema
*/
schema: {
// Controller 0-3
controller: { default: 0, oneOf: [0, 1, 2, 3] },
// Enable/disable features
enabled: { default: true },
// Debugging
debug: { default: false }
},
/*******************************************************************
* Core
*/
/**
* Called once when component is attached. Generally for initial setup.
*/
init: function () {
var scene = this.el.sceneEl;
this.prevTime = window.performance.now();
// Button state
this.buttons = {};
scene.addBehavior(this);
if (!this.getGamepad()) {
console.warn(
'Gamepad #%d not found. Connect controller and press any button to continue.',
this.data.controller
);
}
},
/**
* Called when component is attached and when component data changes.
* Generally modifies the entity based on the data.
*/
update: function () { this.tick(); },
/**
* Called on each iteration of main render loop.
*/
tick: function () {
this.updateButtonState();
},
/**
* Called when a component is removed (e.g., via removeAttribute).
* Generally undoes all modifications to the entity.
*/
remove: function () { },
/*******************************************************************
* Universal controls - movement
*/
isVelocityActive: function () {
if (!this.data.enabled || !this.isConnected()) return false;
var dpad = this.getDpad(),
joystick0 = this.getJoystick(0),
inputX = dpad.x || joystick0.x,
inputY = dpad.y || joystick0.y;
return Math.abs(inputX) > JOYSTICK_EPS || Math.abs(inputY) > JOYSTICK_EPS;
},
getVelocityDelta: function () {
var dpad = this.getDpad(),
joystick0 = this.getJoystick(0),
inputX = dpad.x || joystick0.x,
inputY = dpad.y || joystick0.y,
dVelocity = new THREE.Vector3();
if (Math.abs(inputX) > JOYSTICK_EPS) {
dVelocity.x += inputX;
}
if (Math.abs(inputY) > JOYSTICK_EPS) {
dVelocity.z += inputY;
}
return dVelocity;
},
/*******************************************************************
* Universal controls - rotation
*/
isRotationActive: function () {
if (!this.data.enabled || !this.isConnected()) return false;
var joystick1 = this.getJoystick(1);
return Math.abs(joystick1.x) > JOYSTICK_EPS || Math.abs(joystick1.y) > JOYSTICK_EPS;
},
getRotationDelta: function () {
var lookVector = this.getJoystick(1);
if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0;
if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0;
return lookVector;
},
/*******************************************************************
* Button events
*/
updateButtonState: function () {
var gamepad = this.getGamepad();
if (this.data.enabled && gamepad) {
// Fire DOM events for button state changes.
for (var i = 0; i < gamepad.buttons.length; i++) {
if (gamepad.buttons[i].pressed && !this.buttons[i]) {
this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i]));
} else if (!gamepad.buttons[i].pressed && this.buttons[i]) {
this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i]));
}
this.buttons[i] = gamepad.buttons[i].pressed;
}
} else if (Object.keys(this.buttons)) {
// Reset state if controls are disabled or controller is lost.
this.buttons = {};
}
},
emit: function (event) {
// Emit original event.
this.el.emit(event.type, event);
// Emit convenience event, identifying button index.
this.el.emit(
event.type + ':' + event.index,
new GamepadButtonEvent(event.type, event.index, event)
);
},
/*******************************************************************
* Gamepad state
*/
/**
* Returns the Gamepad instance attached to the component. If connected,
* a proxy-controls component may provide access to Gamepad input from a
* remote device.
*
* @return {Gamepad}
*/
getGamepad: function () {
var localGamepad = navigator.getGamepads
&& navigator.getGamepads()[this.data.controller],
proxyControls = this.el.sceneEl.components['proxy-controls'],
proxyGamepad = proxyControls && proxyControls.isConnected()
&& proxyControls.getGamepad(this.data.controller);
return proxyGamepad || localGamepad;
},
/**
* Returns the state of the given button.
* @param {number} index The button (0-N) for which to find state.
* @return {GamepadButton}
*/
getButton: function (index) {
return this.getGamepad().buttons[index];
},
/**
* Returns state of the given axis. Axes are labelled 0-N, where 0-1 will
* represent X/Y on the first joystick, and 2-3 X/Y on the second.
* @param {number} index The axis (0-N) for which to find state.
* @return {number} On the interval [-1,1].
*/
getAxis: function (index) {
return this.getGamepad().axes[index];
},
/**
* Returns the state of the given joystick (0 or 1) as a THREE.Vector2.
* @param {number} id The joystick (0, 1) for which to find state.
* @return {THREE.Vector2}
*/
getJoystick: function (index) {
var gamepad = this.getGamepad();
switch (index) {
case 0: return new THREE.Vector2(gamepad.axes[0], gamepad.axes[1]);
case 1: return new THREE.Vector2(gamepad.axes[2], gamepad.axes[3]);
default: throw new Error('Unexpected joystick index "%d".', index);
}
},
/**
* Returns the state of the dpad as a THREE.Vector2.
* @return {THREE.Vector2}
*/
getDpad: function () {
var gamepad = this.getGamepad();
if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) {
return new THREE.Vector2();
}
return new THREE.Vector2(
(gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0)
+ (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0),
(gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0)
+ (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0)
);
},
/**
* Returns true if the gamepad is currently connected to the system.
* @return {boolean}
*/
isConnected: function () {
var gamepad = this.getGamepad();
return !!(gamepad && gamepad.connected);
},
/**
* Returns a string containing some information about the controller. Result
* may vary across browsers, for a given controller.
* @return {string}
*/
getID: function () {
return this.getGamepad().id;
}
};
},{"../../lib/GamepadButton":3,"../../lib/GamepadButtonEvent":4}],21:[function(require,module,exports){
var radToDeg = THREE.Math.radToDeg,
isMobile = AFRAME.utils.isMobile();
module.exports = {
schema: {
enabled: {default: true},
standing: {default: true}
},
init: function () {
this.isPositionCalibrated = false;
this.dolly = new THREE.Object3D();
this.hmdEuler = new THREE.Euler();
this.previousHMDPosition = new THREE.Vector3();
this.deltaHMDPosition = new THREE.Vector3();
this.vrControls = new THREE.VRControls(this.dolly);
this.rotation = new THREE.Vector3();
},
update: function () {
var data = this.data;
var vrControls = this.vrControls;
vrControls.standing = data.standing;
vrControls.update();
},
tick: function () {
this.vrControls.update();
},
remove: function () {
this.vrControls.dispose();
},
isRotationActive: function () {
var hmdEuler = this.hmdEuler;
if (!this.data.enabled || !(this.el.sceneEl.is('vr-mode') || isMobile)) {
return false;
}
hmdEuler.setFromQuaternion(this.dolly.quaternion, 'YXZ');
return !isNullVector(hmdEuler);
},
getRotation: function () {
var hmdEuler = this.hmdEuler;
return this.rotation.set(
radToDeg(hmdEuler.x),
radToDeg(hmdEuler.y),
radToDeg(hmdEuler.z)
);
},
isVelocityActive: function () {
var deltaHMDPosition = this.deltaHMDPosition;
var previousHMDPosition = this.previousHMDPosition;
var currentHMDPosition = this.calculateHMDPosition();
this.isPositionCalibrated = this.isPositionCalibrated || !isNullVector(previousHMDPosition);
if (!this.data.enabled || !this.el.sceneEl.is('vr-mode') || isMobile) {
return false;
}
deltaHMDPosition.copy(currentHMDPosition).sub(previousHMDPosition);
previousHMDPosition.copy(currentHMDPosition);
return this.isPositionCalibrated && !isNullVector(deltaHMDPosition);
},
getPositionDelta: function () {
return this.deltaHMDPosition;
},
calculateHMDPosition: function () {
var dolly = this.dolly;
var position = new THREE.Vector3();
dolly.updateMatrix();
position.setFromMatrixPosition(dolly.matrix);
return position;
}
};
function isNullVector (vector) {
return vector.x === 0 && vector.y === 0 && vector.z === 0;
}
},{}],22:[function(require,module,exports){
var physics = require('aframe-physics-system');
module.exports = {
'checkpoint-controls': require('./checkpoint-controls'),
'gamepad-controls': require('./gamepad-controls'),
'hmd-controls': require('./hmd-controls'),
'keyboard-controls': require('./keyboard-controls'),
'mouse-controls': require('./mouse-controls'),
'touch-controls': require('./touch-controls'),
'universal-controls': require('./universal-controls'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
physics.registerAll();
if (!AFRAME.components['checkpoint-controls']) AFRAME.registerComponent('checkpoint-controls', this['checkpoint-controls']);
if (!AFRAME.components['gamepad-controls']) AFRAME.registerComponent('gamepad-controls', this['gamepad-controls']);
if (!AFRAME.components['hmd-controls']) AFRAME.registerComponent('hmd-controls', this['hmd-controls']);
if (!AFRAME.components['keyboard-controls']) AFRAME.registerComponent('keyboard-controls', this['keyboard-controls']);
if (!AFRAME.components['mouse-controls']) AFRAME.registerComponent('mouse-controls', this['mouse-controls']);
if (!AFRAME.components['touch-controls']) AFRAME.registerComponent('touch-controls', this['touch-controls']);
if (!AFRAME.components['universal-controls']) AFRAME.registerComponent('universal-controls', this['universal-controls']);
this._registered = true;
}
};
},{"./checkpoint-controls":19,"./gamepad-controls":20,"./hmd-controls":21,"./keyboard-controls":23,"./mouse-controls":24,"./touch-controls":25,"./universal-controls":26,"aframe-physics-system":7}],23:[function(require,module,exports){
require('../../lib/keyboard.polyfill');
var MAX_DELTA = 0.2,
PROXY_FLAG = '__keyboard-controls-proxy';
var KeyboardEvent = window.KeyboardEvent;
/**
* Keyboard Controls component.
*
* Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
*
* Bind keyboard events to components, or control your entities with the WASD keys.
*
* Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
* generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
* Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
* when you care about which physical key was pressed, rather thanwhich character it corresponds
* to. For example, if you’re a writing a game, you might want a certain set of keys to move the
* player in different directions, and that mapping should ideally be independent of keyboard
* layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
*
* @namespace wasd-controls
* keys the entity moves and if you release it will stop. Easing simulates friction.
* to the entity when pressing the keys.
* @param {bool} [enabled=true] - To completely enable or disable the controls
*/
module.exports = {
schema: {
enabled: { default: true },
debug: { default: false }
},
init: function () {
this.dVelocity = new THREE.Vector3();
this.localKeys = {};
this.listeners = {
keydown: this.onKeyDown.bind(this),
keyup: this.onKeyUp.bind(this),
blur: this.onBlur.bind(this)
};
this.attachEventListeners();
},
/*******************************************************************
* Movement
*/
isVelocityActive: function () {
return this.data.enabled && !!Object.keys(this.getKeys()).length;
},
getVelocityDelta: function () {
var data = this.data,
keys = this.getKeys();
this.dVelocity.set(0, 0, 0);
if (data.enabled) {
if (keys.KeyW || keys.ArrowUp) { this.dVelocity.z -= 1; }
if (keys.KeyA || keys.ArrowLeft) { this.dVelocity.x -= 1; }
if (keys.KeyS || keys.ArrowDown) { this.dVelocity.z += 1; }
if (keys.KeyD || keys.ArrowRight) { this.dVelocity.x += 1; }
}
return this.dVelocity.clone();
},
/*******************************************************************
* Events
*/
play: function () {
this.attachEventListeners();
},
pause: function () {
this.removeEventListeners();
},
remove: function () {
this.pause();
},
attachEventListeners: function () {
window.addEventListener('keydown', this.listeners.keydown, false);
window.addEventListener('keyup', this.listeners.keyup, false);
window.addEventListener('blur', this.listeners.blur, false);
},
removeEventListeners: function () {
window.removeEventListener('keydown', this.listeners.keydown);
window.removeEventListener('keyup', this.listeners.keyup);
window.removeEventListener('blur', this.listeners.blur);
},
onKeyDown: function (event) {
this.localKeys[event.code] = true;
this.emit(event);
},
onKeyUp: function (event) {
delete this.localKeys[event.code];
this.emit(event);
},
onBlur: function () {
for (var code in this.localKeys) {
if (this.localKeys.hasOwnProperty(code)) {
delete this.localKeys[code];
}
}
},
emit: function (event) {
// TODO - keydown only initially?
// TODO - where the f is the spacebar
// Emit original event.
if (PROXY_FLAG in event) {
// TODO - Method never triggered.
this.el.emit(event.type, event);
}
// Emit convenience event, identifying key.
this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
if (this.data.debug) console.log(event.type + ':' + event.code);
},
/*******************************************************************
* Accessors
*/
isPressed: function (code) {
return code in this.getKeys();
},
getKeys: function () {
if (this.isProxied()) {
return this.el.sceneEl.components['proxy-controls'].getKeyboard();
}
return this.localKeys;
},
isProxied: function () {
var proxyControls = this.el.sceneEl.components['proxy-controls'];
return proxyControls && proxyControls.isConnected();
}
};
},{"../../lib/keyboard.polyfill":6}],24:[function(require,module,exports){
/**
* Mouse + Pointerlock controls.
*
* Based on: https://github.com/aframevr/aframe/pull/1056
*/
module.exports = {
schema: {
enabled: { default: true },
pointerlockEnabled: { default: true },
sensitivity: { default: 1 / 25 }
},
init: function () {
this.mouseDown = false;
this.pointerLocked = false;
this.lookVector = new THREE.Vector2();
this.bindMethods();
},
play: function () {
this.addEventListeners();
},
pause: function () {
this.removeEventListeners();
this.lookVector.set(0, 0);
},
remove: function () {
this.pause();
},
bindMethods: function () {
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onPointerLockChange = this.onPointerLockChange.bind(this);
this.onPointerLockChange = this.onPointerLockChange.bind(this);
this.onPointerLockChange = this.onPointerLockChange.bind(this);
},
addEventListeners: function () {
var sceneEl = this.el.sceneEl;
var canvasEl = sceneEl.canvas;
var data = this.data;
if (!canvasEl) {
sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
return;
}
canvasEl.addEventListener('mousedown', this.onMouseDown, false);
canvasEl.addEventListener('mousemove', this.onMouseMove, false);
canvasEl.addEventListener('mouseup', this.onMouseUp, false);
canvasEl.addEventListener('mouseout', this.onMouseUp, false);
if (data.pointerlockEnabled) {
document.addEventListener('pointerlockchange', this.onPointerLockChange, false);
document.addEventListener('mozpointerlockchange', this.onPointerLockChange, false);
document.addEventListener('pointerlockerror', this.onPointerLockError, false);
}
},
removeEventListeners: function () {
var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
if (canvasEl) {
canvasEl.removeEventListener('mousedown', this.onMouseDown, false);
canvasEl.removeEventListener('mousemove', this.onMouseMove, false);
canvasEl.removeEventListener('mouseup', this.onMouseUp, false);
canvasEl.removeEventListener('mouseout', this.onMouseUp, false);
}
document.removeEventListener('pointerlockchange', this.onPointerLockChange, false);
document.removeEventListener('mozpointerlockchange', this.onPointerLockChange, false);
document.removeEventListener('pointerlockerror', this.onPointerLockError, false);
},
isRotationActive: function () {
return this.data.enabled && (this.mouseDown || this.pointerLocked);
},
/**
* Returns the sum of all mouse movement since last call.
*/
getRotationDelta: function () {
var dRotation = this.lookVector.clone().multiplyScalar(this.data.sensitivity);
this.lookVector.set(0, 0);
return dRotation;
},
onMouseMove: function (event) {
var previousMouseEvent = this.previousMouseEvent;
if (!this.data.enabled || !(this.mouseDown || this.pointerLocked)) {
return;
}
var movementX = event.movementX || event.mozMovementX || 0;
var movementY = event.movementY || event.mozMovementY || 0;
if (!this.pointerLocked) {
movementX = event.screenX - previousMouseEvent.screenX;
movementY = event.screenY - previousMouseEvent.screenY;
}
this.lookVector.x += movementX;
this.lookVector.y += movementY;
this.previousMouseEvent = event;
},
onMouseDown: function (event) {
var canvasEl = this.el.sceneEl.canvas;
this.mouseDown = true;
this.previousMouseEvent = event;
if (this.data.pointerlockEnabled && !this.pointerLocked) {
if (canvasEl.requestPointerLock) {
canvasEl.requestPointerLock();
} else if (canvasEl.mozRequestPointerLock) {
canvasEl.mozRequestPointerLock();
}
}
},
onMouseUp: function () {
this.mouseDown = false;
},
onPointerLockChange: function () {
this.pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement);
},
onPointerLockError: function () {
this.pointerLocked = false;
}
};
},{}],25:[function(require,module,exports){
module.exports = {
schema: {
enabled: { default: true }
},
init: function () {
this.dVelocity = new THREE.Vector3();
this.bindMethods();
},
play: function () {
this.addEventListeners();
},
pause: function () {
this.removeEventListeners();
this.dVelocity.set(0, 0, 0);
},
remove: function () {
this.pause();
},
addEventListeners: function () {
var sceneEl = this.el.sceneEl;
var canvasEl = sceneEl.canvas;
if (!canvasEl) {
sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
return;
}
canvasEl.addEventListener('touchstart', this.onTouchStart);
canvasEl.addEventListener('touchend', this.onTouchEnd);
},
removeEventListeners: function () {
var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
if (!canvasEl) { return; }
canvasEl.removeEventListener('touchstart', this.onTouchStart);
canvasEl.removeEventListener('touchend', this.onTouchEnd);
},
isVelocityActive: function () {
return this.data.enabled && this.isMoving;
},
getVelocityDelta: function () {
this.dVelocity.z = this.isMoving ? -1 : 0;
return this.dVelocity.clone();
},
bindMethods: function () {
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
},
onTouchStart: function (e) {
this.isMoving = true;
e.preventDefault();
},
onTouchEnd: function (e) {
this.isMoving = false;
e.preventDefault();
}
};
},{}],26:[function(require,module,exports){
/**
* Universal Controls
*
* @author Don McCurdy <dm@donmccurdy.com>
*/
var COMPONENT_SUFFIX = '-controls',
MAX_DELTA = 0.2, // ms
PI_2 = Math.PI / 2;
module.exports = {
/*******************************************************************
* Schema
*/
dependencies: ['velocity', 'rotation'],
schema: {
enabled: { default: true },
movementEnabled: { default: true },
movementControls: { default: ['gamepad', 'keyboard', 'touch', 'hmd'] },
rotationEnabled: { default: true },
rotationControls: { default: ['hmd', 'gamepad', 'mouse'] },
movementSpeed: { default: 5 }, // m/s
movementEasing: { default: 15 }, // m/s2
movementAcceleration: { default: 80 }, // m/s2
rotationSensitivity: { default: 0.05 }, // radians/frame, ish
fly: { default: false }
},
/*******************************************************************
* Lifecycle
*/
init: function () {
// Movement
this.velocity = new THREE.Vector3();
// Rotation
this.pitch = new THREE.Object3D();
this.yaw = new THREE.Object3D();
this.yaw.position.y = 10;
this.yaw.add(this.pitch);
this.heading = new THREE.Euler(0, 0, 0, 'YXZ');
if (this.el.sceneEl.hasLoaded) {
this.injectControls();
} else {
this.el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
}
},
update: function () {
if (this.el.sceneEl.hasLoaded) {
this.injectControls();
}
},
injectControls: function () {
var i, name,
data = this.data;
for (i = 0; i < data.movementControls.length; i++) {
name = data.movementControls[i] + COMPONENT_SUFFIX;
if (!this.el.components[name]) {
this.el.setAttribute(name, '');
}
}
for (i = 0; i < data.rotationControls.length; i++) {
name = data.rotationControls[i] + COMPONENT_SUFFIX;
if (!this.el.components[name]) {
this.el.setAttribute(name, '');
}
}
},
/*******************************************************************
* Tick
*/
tick: function (t, dt) {
if (!dt) { return; }
// Update rotation.
if (this.data.rotationEnabled) this.updateRotation(dt);
// Update velocity. If FPS is too low, reset.
if (this.data.movementEnabled && dt / 1000 > MAX_DELTA) {
this.velocity.set(0, 0, 0);
this.el.setAttribute('velocity', this.velocity);
} else {
this.updateVelocity(dt);
}
},
/*******************************************************************
* Rotation
*/
updateRotation: function (dt) {
var control, dRotation,
data = this.data;
for (var i = 0, l = data.rotationControls.length; i < l; i++) {
control = this.el.components[data.rotationControls[i] + COMPONENT_SUFFIX];
if (control && control.isRotationActive()) {
if (control.getRotationDelta) {
dRotation = control.getRotationDelta(dt);
dRotation.multiplyScalar(data.rotationSensitivity);
this.yaw.rotation.y -= dRotation.x;
this.pitch.rotation.x -= dRotation.y;
this.pitch.rotation.x = Math.max(-PI_2, Math.min(PI_2, this.pitch.rotation.x));
this.el.setAttribute('rotation', {
x: THREE.Math.radToDeg(this.pitch.rotation.x),
y: THREE.Math.radToDeg(this.yaw.rotation.y),
z: 0
});
} else if (control.getRotation) {
this.el.setAttribute('rotation', control.getRotation());
} else {
throw new Error('Incompatible rotation controls: %s', data.rotationControls[i]);
}
break;
}
}
},
/*******************************************************************
* Movement
*/
updateVelocity: function (dt) {
var control, dVelocity,
velocity = this.velocity,
data = this.data;
if (data.movementEnabled) {
for (var i = 0, l = data.movementControls.length; i < l; i++) {
control = this.el.components[data.movementControls[i] + COMPONENT_SUFFIX];
if (control && control.isVelocityActive()) {
if (control.getVelocityDelta) {
dVelocity = control.getVelocityDelta(dt);
} else if (control.getVelocity) {
this.el.setAttribute('velocity', control.getVelocity());
return;
} else if (control.getPositionDelta) {
velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
this.el.setAttribute('velocity', velocity);
return;
} else {
throw new Error('Incompatible movement controls: ', data.movementControls[i]);
}
break;
}
}
}
velocity.copy(this.el.getComputedAttribute('velocity'));
velocity.x -= velocity.x * data.movementEasing * dt / 1000;
velocity.z -= velocity.z * data.movementEasing * dt / 1000;
if (dVelocity && data.movementEnabled) {
// Set acceleration
if (dVelocity.length() > 1) {
dVelocity.setLength(this.data.movementAcceleration * dt / 1000);
} else {
dVelocity.multiplyScalar(this.data.movementAcceleration * dt / 1000);
}
// Rotate to heading
var rotation = this.el.getAttribute('rotation');
if (rotation) {
this.heading.set(
data.fly ? THREE.Math.degToRad(rotation.x) : 0,
THREE.Math.degToRad(rotation.y),
0
);
dVelocity.applyEuler(this.heading);
}
velocity.add(dVelocity);
// TODO - Several issues here:
// (1) Interferes w/ gravity.
// (2) Interferes w/ jumping.
// (3) Likely to interfere w/ relative position to moving platform.
// if (velocity.length() > data.movementSpeed) {
// velocity.setLength(data.movementSpeed);
// }
}
this.el.setAttribute('velocity', velocity);
}
};
},{}],27:[function(require,module,exports){
module.exports = {
'ply-model': require('./ply-model'),
'three-model': require('./three-model'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
if (!AFRAME.systems['ply-model']) {
AFRAME.registerSystem('ply-model', this['ply-model'].System);
}
if (!AFRAME.components['ply-model']) {
AFRAME.registerComponent('ply-model', this['ply-model'].Component);
}
if (!AFRAME.components['three-model']) {
AFRAME.registerComponent('three-model', this['three-model']);
}
this._registered = true;
}
};
},{"./ply-model":28,"./three-model":29}],28:[function(require,module,exports){
/**
* ply-model
*
* Wraps THREE.PLYLoader.
*/
THREE.PLYLoader = require('../../lib/PLYLoader');
/**
* Loads, caches, resolves geometries.
*
* @member cache - Promises that resolve geometries keyed by `src`.
*/
module.exports.System = {
init: function () {
this.cache = {};
},
/**
* @returns {Promise}
*/
getOrLoadGeometry: function (src, skipCache) {
var cache = this.cache;
var cacheItem = cache[src];
if (!skipCache && cacheItem) {
return cacheItem;
}
cache[src] = new Promise(function (resolve) {
var loader = new THREE.PLYLoader();
loader.load(src, function (geometry) {
resolve(geometry);
});
});
return cache[src];
},
};
module.exports.Component = {
schema: {
skipCache: {type: 'boolean', default: false},
src: {type: 'src'}
},
init: function () {
this.model = null;
},
update: function () {
var data = this.data;
var el = this.el;
var loader;
if (!data.src) {
console.warn('[%s] `src` property is required.', this.name);
return;
}
// Get geometry from system, create and set mesh.
this.system.getOrLoadGeometry(data.src, data.skipCache).then(function (geometry) {
var model = createModel(geometry);
el.setObject3D('mesh', model);
el.emit('model-loaded', {format: 'ply', model: model});
});
},
remove: function () {
if (this.model) { this.el.removeObject3D('mesh'); }
}
};
function createModel (geometry) {
return new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
shading: THREE.FlatShading,
vertexColors: THREE.VertexColors,
shininess: 0
}));
}
},{"../../lib/PLYLoader":5}],29:[function(require,module,exports){
var DEFAULT_ANIMATION = '__auto__';
/**
* three-model
*
* Loader for THREE.js JSON format. Somewhat confusingly, there are two
* different THREE.js formats, both having the .json extension. This loader
* supports both, but requires you to specify the mode as "object" or "json".
*
* Typically, you will use "json" for a single mesh, and "object" for a scene
* or multiple meshes. Check the console for errors, if in doubt.
*
* See: https://clara.io/learn/user-guide/data_exchange/threejs_export
*/
module.exports = {
schema: {
src: { type: 'src' },
loader: { default: 'object', oneOf: ['object', 'json'] },
enableAnimation: { default: true },
animation: { default: DEFAULT_ANIMATION },
animationDuration: { default: 0 }
},
init: function () {
this.model = null;
this.mixer = null;
},
update: function (previousData) {
previousData = previousData || {};
var loader,
data = this.data;
if (!data.src) {
this.remove();
return;
}
// First load.
if (!Object.keys(previousData).length) {
this.remove();
if (data.loader === 'object') {
loader = new THREE.ObjectLoader();
loader.load(data.src, function(loaded) {
loaded.traverse( function(object) {
if (object instanceof THREE.SkinnedMesh)
loaded = object;
});
if(loaded.material)
loaded.material.skinning = !!((loaded.geometry && loaded.geometry.bones) || []).length;
this.load(loaded);
}.bind(this));
} else if (data.loader === 'json') {
loader = new THREE.JSONLoader();
loader.load(data.src, function (geometry, materials) {
// Attempt to automatically detect common material options.
materials.forEach(function (mat) {
mat.vertexColors = (geometry.faces[0] || {}).color ? THREE.FaceColors : THREE.NoColors;
mat.skinning = !!(geometry.bones || []).length;
mat.morphTargets = !!(geometry.morphTargets || []).length;
mat.morphNormals = !!(geometry.morphNormals || []).length;
});
var mesh = (geometry.bones || []).length
? new THREE.SkinnedMesh(geometry, new THREE.MultiMaterial(materials))
: new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
this.load(mesh);
}.bind(this));
} else {
throw new Error('[three-model] Invalid mode "%s".', data.mode);
}
return;
}
var activeAction = this.model && this.model.activeAction;
if (data.animation !== previousData.animation) {
if (activeAction) activeAction.stop();
this.playAnimation();
return;
}
if (activeAction && data.enableAnimation !== activeAction.isRunning()) {
data.enableAnimation ? this.playAnimation() : activeAction.stop();
}
if (activeAction && data.animationDuration) {
activeAction.setDuration(data.animationDuration);
}
},
load: function (model) {
this.model = model;
this.mixer = new THREE.AnimationMixer(this.model);
this.el.setObject3D('mesh', model);
this.el.emit('model-loaded', {format: 'three', model: model});
if (this.data.enableAnimation) this.playAnimation();
},
playAnimation: function () {
var clip,
data = this.data,
animations = this.model.animations || this.model.geometry.animations || [];
if (!data.enableAnimation || !data.animation || !animations.length) {
return;
}
clip = data.animation === DEFAULT_ANIMATION
? animations[0]
: THREE.AnimationClip.findByName(animations, data.animation);
if (!clip) {
console.error('[three-model] Animation "%s" not found.', data.animation);
return;
}
this.model.activeAction = this.mixer.clipAction(clip, this.model);
if (data.animationDuration) {
this.model.activeAction.setDuration(data.animationDuration);
}
this.model.activeAction.play();
},
remove: function () {
if (this.mixer) this.mixer.stopAllAction();
if (this.model) this.el.removeObject3D('mesh');
},
tick: function (t, dt) {
if (this.mixer && !isNaN(dt)) {
this.mixer.update(dt / 1000);
}
}
};
},{}],30:[function(require,module,exports){
module.exports = {
schema: {
defaultRotation: {type: 'vec3'},
enableDefaultRotation: {default: false}
},
init: function () {
this.active = false;
this.targetEl = null;
this.fire = this.fire.bind(this);
},
play: function () { this.el.addEventListener('click', this.fire); },
pause: function () { this.el.addEventListener('click', this.fire); },
remove: function () { this.pause(); },
fire: function () {
var targetEl = this.el.sceneEl.querySelector('[checkpoint-controls]');
if (!targetEl) {
throw new Error('No `checkpoint-controls` component found.');
}
targetEl.components['checkpoint-controls'].setCheckpoint(this.el);
}
};
},{}],31:[function(require,module,exports){
/**
* Based on aframe/examples/showcase/tracked-controls.
*
* Handles events coming from the hand-controls.
* Determines if the entity is grabbed or released.
* Updates its position to move along the controller.
*/
module.exports = {
init: function () {
this.GRABBED_STATE = 'grabbed';
this.grabbing = false;
this.hitEl = /** @type {AFRAME.Element} */ null;
this.physics = /** @type {AFRAME.System} */ this.el.sceneEl.systems.physics;
this.constraint = /** @type {CANNON.Constraint} */ null;
// Bind event handlers
this.onHit = this.onHit.bind(this);
this.onGripOpen = this.onGripOpen.bind(this);
this.onGripClose = this.onGripClose.bind(this);
},
play: function () {
var el = this.el;
el.addEventListener('hit', this.onHit);
el.addEventListener('gripdown', this.onGripClose);
el.addEventListener('gripup', this.onGripOpen);
el.addEventListener('trackpaddown', this.onGripClose);
el.addEventListener('trackpadup', this.onGripOpen);
el.addEventListener('triggerdown', this.onGripClose);
el.addEventListener('triggerup', this.onGripOpen);
},
pause: function () {
var el = this.el;
el.removeEventListener('hit', this.onHit);
el.removeEventListener('gripdown', this.onGripClose);
el.removeEventListener('gripup', this.onGripOpen);
el.removeEventListener('trackpaddown', this.onGripClose);
el.removeEventListener('trackpadup', this.onGripOpen);
el.removeEventListener('triggerdown', this.onGripClose);
el.removeEventListener('triggerup', this.onGripOpen);
},
onGripClose: function (evt) {
this.grabbing = true;
},
onGripOpen: function (evt) {
var hitEl = this.hitEl;
this.grabbing = false;
if (!hitEl) { return; }
hitEl.removeState(this.GRABBED_STATE);
this.hitEl = undefined;
this.physics.world.removeConstraint(this.constraint);
this.constraint = null;
},
onHit: function (evt) {
var hitEl = evt.detail.el;
// If the element is already grabbed (it could be grabbed by another controller).
// If the hand is not grabbing the element does not stick.
// If we're already grabbing something you can't grab again.
if (!hitEl || hitEl.is(this.GRABBED_STATE) || !this.grabbing || this.hitEl) { return; }
hitEl.addState(this.GRABBED_STATE);
this.hitEl = hitEl;
this.constraint = new CANNON.LockConstraint(this.el.body, hitEl.body);
this.physics.world.addConstraint(this.constraint);
}
};
},{}],32:[function(require,module,exports){
var physics = require('aframe-physics-system');
module.exports = {
'checkpoint': require('./checkpoint'),
'grab': require('./grab'),
'jump-ability': require('./jump-ability'),
'kinematic-body': require('./kinematic-body'),
'sphere-collider': require('./sphere-collider'),
'toggle-velocity': require('./toggle-velocity'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
physics.registerAll();
if (!AFRAME.components['checkpoint']) AFRAME.registerComponent('checkpoint', this['checkpoint']);
if (!AFRAME.components['grab']) AFRAME.registerComponent('grab', this['grab']);
if (!AFRAME.components['jump-ability']) AFRAME.registerComponent('jump-ability', this['jump-ability']);
if (!AFRAME.components['kinematic-body']) AFRAME.registerComponent('kinematic-body', this['kinematic-body']);
if (!AFRAME.components['sphere-collider']) AFRAME.registerComponent('sphere-collider', this['sphere-collider']);
if (!AFRAME.components['toggle-velocity']) AFRAME.registerComponent('toggle-velocity', this['toggle-velocity']);
this._registered = true;
}
};
},{"./checkpoint":30,"./grab":31,"./jump-ability":33,"./kinematic-body":34,"./sphere-collider":35,"./toggle-velocity":36,"aframe-physics-system":7}],33:[function(require,module,exports){
var ACCEL_G = -9.8, // m/s^2
EASING = -15; // m/s^2
/**
* Jump ability.
*/
module.exports = {
dependencies: ['velocity'],
/* Schema
——————————————————————————————————————————————*/
schema: {
on: { default: 'keydown:Space gamepadbuttondown:0' },
playerHeight: { default: 1.764 },
maxJumps: { default: 1 },
distance: { default: 5 },
soundJump: { default: '' },
soundLand: { default: '' },
debug: { default: false }
},
init: function () {
this.velocity = 0;
this.numJumps = 0;
var beginJump = this.beginJump.bind(this),
events = this.data.on.split(' ');
this.bindings = {};
for (var i = 0; i < events.length; i++) {
this.bindings[events[i]] = beginJump;
this.el.addEventListener(events[i], beginJump);
}
this.bindings.collide = this.onCollide.bind(this);
this.el.addEventListener('collide', this.bindings.collide);
},
remove: function () {
for (var event in this.bindings) {
if (this.bindings.hasOwnProperty(event)) {
this.el.removeEventListener(event, this.bindings[event]);
delete this.bindings[event];
}
}
this.el.removeEventListener('collide', this.bindings.collide);
delete this.bindings.collide;
},
beginJump: function () {
if (this.numJumps < this.data.maxJumps) {
var data = this.data,
initialVelocity = Math.sqrt(-2 * data.distance * (ACCEL_G + EASING)),
v = this.el.getAttribute('velocity');
this.el.setAttribute('velocity', {x: v.x, y: initialVelocity, z: v.z});
this.numJumps++;
}
},
onCollide: function () {
this.numJumps = 0;
}
};
},{}],34:[function(require,module,exports){
/**
* Kinematic body.
*
* Managed dynamic body, which moves but is not affected (directly) by the
* physics engine. This is not a true kinematic body, in the sense that we are
* letting the physics engine _compute_ collisions against it and selectively
* applying those collisions to the object. The physics engine does not decide
* the position/velocity/rotation of the element.
*
* Used for the camera object, because full physics simulation would create
* movement that feels unnatural to the player. Bipedal movement does not
* translate nicely to rigid body physics.
*
* See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
* And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
*/
var CANNON = require('cannon');
var EPS = 0.000001;
module.exports = {
dependencies: ['velocity'],
/*******************************************************************
* Schema
*/
schema: {
mass: { default: 5 },
radius: { default: 1.3 },
height: { default: 1.764 },
linearDamping: { default: 0.05 },
enableSlopes: { default: true }
},
/*******************************************************************
* Lifecycle
*/
init: function () {
this.system = this.el.sceneEl.systems.physics;
this.system.addBehavior(this, this.system.Phase.SIMULATE);
var el = this.el,
data = this.data,
position = (new CANNON.Vec3()).copy(el.getAttribute('position'));
this.body = new CANNON.Body({
material: this.system.material,
position: position,
mass: data.mass,
linearDamping: data.linearDamping,
fixedRotation: true
});
this.body.addShape(
new CANNON.Sphere(data.radius),
new CANNON.Vec3(0, data.radius - data.height, 0)
);
this.body.el = this.el;
this.system.addBody(this.body);
},
remove: function () {
this.system.removeBody(this.body);
this.system.removeBehavior(this, this.system.Phase.SIMULATE);
},
/*******************************************************************
* Tick
*/
/**
* Checks CANNON.World for collisions and attempts to apply them to the
* element automatically, in a player-friendly way.
*
* There's extra logic for horizontal surfaces here. The basic requirements:
* (1) Only apply gravity when not in contact with _any_ horizontal surface.
* (2) When moving, project the velocity against exactly one ground surface.
* If in contact with two ground surfaces (e.g. ground + ramp), choose
* the one that collides with current velocity, if any.
*/
step: (function () {
var velocity = new THREE.Vector3(),
normalizedVelocity = new THREE.Vector3(),
currentSurfaceNormal = new THREE.Vector3(),
groundNormal = new THREE.Vector3();
return function (t, dt) {
if (!dt) return;
var body = this.body,
data = this.data,
didCollide = false,
height, groundHeight = -Infinity,
groundBody;
dt = Math.min(dt, this.system.data.maxInterval * 1000);
groundNormal.set(0, 0, 0);
velocity.copy(this.el.getAttribute('velocity'));
body.velocity.copy(velocity);
body.position.copy(this.el.getAttribute('position'));
for (var i = 0, contact; (contact = this.system.world.contacts[i]); i++) {
// 1. Find any collisions involving this element. Get the contact
// normal, and make sure it's oriented _out_ of the other object.
if (body.id === contact.bi.id) {
contact.ni.negate(currentSurfaceNormal);
} else if (body.id === contact.bj.id) {
currentSurfaceNormal.copy(contact.ni);
} else {
continue;
}
didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
if (didCollide && currentSurfaceNormal.y <= 0.5) {
// 2. If current trajectory attempts to move _through_ another
// object, project the velocity against the collision plane to
// prevent passing through.
velocity = velocity.projectOnPlane(currentSurfaceNormal);
} else if (currentSurfaceNormal.y > 0.5) {
// 3. If in contact with something roughly horizontal (+/- 45º) then
// consider that the current ground. Only the highest qualifying
// ground is retained.
height = body.id === contact.bi.id
? Math.abs(contact.rj.y + contact.bj.position.y)
: Math.abs(contact.ri.y + contact.bi.position.y);
if (height > groundHeight) {
groundHeight = height;
groundNormal.copy(currentSurfaceNormal);
groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
}
}
}
normalizedVelocity.copy(velocity).normalize();
if (groundBody && normalizedVelocity.y < 0.5) {
if (!data.enableSlopes) {
groundNormal.set(0, 1, 0);
} else if (groundNormal.y < 1 - EPS) {
groundNormal = this.raycastToGround(groundBody, groundNormal);
}
// 4. Project trajectory onto the top-most ground object, unless
// trajectory is > 45º.
velocity = velocity.projectOnPlane(groundNormal);
} else {
// 5. If not in contact with anything horizontal, apply world gravity.
// TODO - Why is the 4x scalar necessary.
velocity.add(this.system.world.gravity.scale(dt * 4.0 / 1000));
}
// 6. If the ground surface has a velocity, apply it directly to current
// position, not velocity, to preserve relative velocity.
if (groundBody && groundBody.el && groundBody.el.components.velocity) {
var groundVelocity = groundBody.el.getComputedAttribute('velocity');
body.position.copy({
x: body.position.x + groundVelocity.x * dt / 1000,
y: body.position.y + groundVelocity.y * dt / 1000,
z: body.position.z + groundVelocity.z * dt / 1000
});
this.el.setAttribute('position', body.position);
}
body.velocity.copy(velocity);
this.el.setAttribute('velocity', velocity);
};
}()),
/**
* When walking on complex surfaces (trimeshes, borders between two shapes),
* the collision normals returned for the player sphere can be very
* inconsistent. To address this, raycast straight down, find the collision
* normal, and return whichever normal is more vertical.
* @param {CANNON.Body} groundBody
* @param {CANNON.Vec3} groundNormal
* @return {CANNON.Vec3}
*/
raycastToGround: function (groundBody, groundNormal) {
var ray,
hitNormal,
vFrom = this.body.position,
vTo = this.body.position.clone();
vTo.y -= this.data.height;
ray = new CANNON.Ray(vFrom, vTo);
ray._updateDirection(); // TODO - Report bug.
ray.intersectBody(groundBody);
if (!ray.hasHit) return groundNormal;
// Compare ABS, in case we're projecting against the inside of the face.
hitNormal = ray.result.hitNormalWorld;
return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
}
};
},{"cannon":45}],35:[function(require,module,exports){
/**
* Based on aframe/examples/showcase/tracked-controls.
*
* Implement bounding sphere collision detection for entities with a mesh.
* Sets the specified state on the intersected entities.
*
* @property {string} objects - Selector of the entities to test for collision.
* @property {string} state - State to set on collided entities.
*
*/
module.exports = {
schema: {
objects: {default: ''},
state: {default: 'collided'},
radius: {default: 0.05}
},
init: function () {
this.els = [];
this.collisions = [];
},
/**
* Update list of entities to test for collision.
*/
update: function () {
var data = this.data;
var objectEls;
// Push entities into list of els to intersect.
if (data.objects) {
objectEls = this.el.sceneEl.querySelectorAll(data.objects);
} else {
// If objects not defined, intersect with everything.
objectEls = this.el.sceneEl.children;
}
// Convert from NodeList to Array
this.els = Array.prototype.slice.call(objectEls);
},
tick: (function () {
var position = new THREE.Vector3(),
meshPosition = new THREE.Vector3();
return function () {
var el = this.el,
data = this.data,
mesh = el.getObject3D('mesh'),
collisions = [];
if (!mesh) { return; }
position.copy(el.getComputedAttribute('position'));
// Update collisions.
this.els.forEach(intersect);
// Emit events.
collisions.forEach(handleHit);
// No collisions.
if (collisions.length === 0) { el.emit('hit', {el: null}); }
// Updated the state of the elements that are not intersected anymore.
this.collisions.filter(function (el) {
return collisions.indexOf(el) === -1;
}).forEach(function removeState (el) {
el.removeState(data.state);
});
// Store new collisions
this.collisions = collisions;
// AABB collision detection
function intersect (el) {
var radius,
mesh = el.getObject3D('mesh');
if (!mesh) return;
mesh.getWorldPosition(meshPosition);
mesh.geometry.computeBoundingSphere();
radius = mesh.geometry.boundingSphere.radius;
if (position.distanceTo(meshPosition) < radius + data.radius) {
collisions.push(el);
}
}
function handleHit (hitEl) {
hitEl.emit('hit');
hitEl.addState(data.state);
el.emit('hit', {el: hitEl});
}
};
})()
};
},{}],36:[function(require,module,exports){
/**
* Toggle velocity.
*
* Moves an object back and forth along an axis, within a min/max extent.
*/
module.exports = {
dependencies: ['velocity'],
schema: {
axis: { default: 'x', oneOf: ['x', 'y', 'z'] },
min: { default: 0 },
max: { default: 0 },
speed: { default: 1 }
},
init: function () {
var velocity = {x: 0, y: 0, z: 0};
velocity[this.data.axis] = this.data.speed;
this.el.setAttribute('velocity', velocity);
if (this.el.sceneEl.addBehavior) this.el.sceneEl.addBehavior(this);
},
remove: function () {},
update: function () { this.tick(); },
tick: function () {
var data = this.data,
velocity = this.el.getAttribute('velocity'),
position = this.el.getAttribute('position');
if (velocity[data.axis] > 0 && position[data.axis] > data.max) {
velocity[data.axis] = -data.speed;
this.el.setAttribute('velocity', velocity);
} else if (velocity[data.axis] < 0 && position[data.axis] < data.min) {
velocity[data.axis] = data.speed;
this.el.setAttribute('velocity', velocity);
}
},
};
},{}],37:[function(require,module,exports){
/**
* Flat grid.
*
* Defaults to 75x75.
*/
module.exports = {
defaultComponents: {
geometry: {
primitive: 'plane',
width: 75,
height: 75
},
rotation: {x: -90, y: 0, z: 0},
material: {
src: 'url(https://cdn.rawgit.com/donmccurdy/aframe-extras/v1.16.3/assets/grid.png)',
repeat: '75 75'
}
},
mappings: {
width: 'geometry.width',
height: 'geometry.height',
src: 'material.src'
}
};
},{}],38:[function(require,module,exports){
/**
* Flat-shaded ocean primitive.
*
* Based on a Codrops tutorial:
* http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
*/
module.exports.Primitive = {
defaultComponents: {
ocean: {},
rotation: {x: -90, y: 0, z: 0}
},
mappings: {
width: 'ocean.width',
depth: 'ocean.depth',
density: 'ocean.density',
color: 'ocean.color',
opacity: 'ocean.opacity'
}
};
module.exports.Component = {
schema: {
// Dimensions of the ocean area.
width: {default: 10, min: 0},
depth: {default: 10, min: 0},
// Density of waves.
density: {default: 10},
// Wave amplitude and variance.
amplitude: {default: 0.1},
amplitudeVariance: {default: 0.3},
// Wave speed and variance.
speed: {default: 1},
speedVariance: {default: 2},
// Material.
color: {default: 0x7AD2F7},
opacity: {default: 0.8}
},
/**
* Use play() instead of init(), because component mappings – unavailable as dependencies – are
* not guaranteed to have parsed when this component is initialized.
*/
play: function () {
var el = this.el,
data = this.data,
material = el.components.material;
var geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
geometry.mergeVertices();
this.waves = [];
for (var v, i = 0, l = geometry.vertices.length; i < l; i++) {
v = geometry.vertices[i];
this.waves.push({
z: v.z,
ang: Math.random() * Math.PI * 2,
amp: data.amplitude + Math.random() * data.amplitudeVariance,
speed: (data.speed + Math.random() * data.speedVariance) / 1000 // radians / frame
});
}
if (!material) {
material = {};
material.material = new THREE.MeshPhongMaterial({
color: data.color,
transparent: data.opacity < 1,
opacity: data.opacity,
shading: THREE.FlatShading,
});
}
this.mesh = new THREE.Mesh(geometry, material.material);
el.object3D.add(this.mesh);
},
remove: function () {
this.el.object3D.remove(this.mesh);
},
tick: function (t, dt) {
if (!dt) return;
var verts = this.mesh.geometry.vertices;
for (var v, vprops, i = 0; (v = verts[i]); i++){
vprops = this.waves[i];
v.z = vprops.z + Math.sin(vprops.ang) * vprops.amp;
vprops.ang += vprops.speed * dt;
}
this.mesh.geometry.verticesNeedUpdate = true;
}
};
},{}],39:[function(require,module,exports){
/**
* Tube following a custom path.
*
* Usage:
*
* ```html
* <a-tube path="5 0 5, 5 0 -5, -5 0 -5" radius="0.5"></a-tube>
* ```
*/
module.exports.Primitive = {
defaultComponents: {
tube: {},
},
mappings: {
path: 'tube.path',
segments: 'tube.segments',
radius: 'tube.radius',
radialSegments: 'tube.radialSegments',
closed: 'tube.closed'
}
};
module.exports.Component = {
schema: {
path: {default: []},
segments: {default: 64},
radius: {default: 1},
radialSegments: {default: 8},
closed: {default: false}
},
init: function () {
var el = this.el,
data = this.data,
material = el.components.material;
if (!data.path.length) {
console.error('[a-tube] `path` property expected but not found.');
return;
}
var curve = new THREE.CatmullRomCurve3(data.path.map(function (point) {
point = point.split(' ');
return new THREE.Vector3(Number(point[0]), Number(point[1]), Number(point[2]));
}));
var geometry = new THREE.TubeGeometry(
curve, data.segments, data.radius, data.radialSegments, data.closed
);
if (!material) {
material = {};
material.material = new THREE.MeshPhongMaterial();
}
this.mesh = new THREE.Mesh(geometry, material.material);
this.el.setObject3D('mesh', this.mesh);
},
remove: function () {
if (this.mesh) this.el.removeObject3D('mesh');
}
};
},{}],40:[function(require,module,exports){
module.exports = {
'a-grid': require('./a-grid'),
'a-ocean': require('./a-ocean'),
'a-tube': require('./a-tube'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
AFRAME.registerPrimitive('a-grid', this['a-grid']);
AFRAME.registerComponent('ocean', this['a-ocean'].Component);
AFRAME.registerPrimitive('a-ocean', this['a-ocean'].Primitive);
AFRAME.registerComponent('tube', this['a-tube'].Component);
AFRAME.registerPrimitive('a-tube', this['a-tube'].Primitive);
this._registered = true;
}
};
},{"./a-grid":37,"./a-ocean":38,"./a-tube":39}],41:[function(require,module,exports){
module.exports = {
'shadow': require('./shadow'),
'shadow-light': require('./shadow-light'),
registerAll: function (AFRAME) {
if (this._registered) return;
AFRAME = AFRAME || window.AFRAME;
if (!AFRAME.components['shadow']) AFRAME.registerComponent('shadow', this['shadow']);
if (!AFRAME.components['shadow-light']) AFRAME.registerComponent('shadow-light', this['shadow-light']);
this._registered = true;
}
};
},{"./shadow":43,"./shadow-light":42}],42:[function(require,module,exports){
/**
* Light component.
*
* Source: https://github.com/aframevr/aframe-core/pull/348
*
* @namespace light
* @param {number} [angle=PI / 3] - Maximum extent of light from its direction,
in radians. For spot lights.
* @param {bool} [castShadow=false] - Whether light will cast shadows.
Only applies to directional, point, and spot lights.
* @param {string} [color=#FFF] - Light color. For every light.
* @param {number} [decay=1] - Amount the light dims along the distance of the
light. For point and spot lights.
* @param {number} [exponent=10.0] - Rapidity of falloff of light from its
target direction. For spot lights.
* @param {string} [groundColor=#FFF] - Ground light color.
For hemisphere lights.
* @param {number} [intensity=1.0] - Light strength.
For every light except ambient.
* @param {number} [shadowBias=0] - How much to add or subtract from the
normalized depth when deciding whether a surface is in shadow.
* @param {number} [shadowCameraFar=5000] - Orthographic shadow camera frustum
parameter.
* @param {number} [shadowCameraNear=50] - Orthographic shadow camera frustum
parameter.
* @param {number} [shadowDarkness=0.5] - Darkness of shadow cast, from 0 to 1.
* @param {number} [shadowMapHeight=512] - Shadow map texture height in pixels.
* @param {number} [shadowMapWidth=512] - Shadow map texture height in pixels.
* @param {string} [type=directional] - Light type (i.e., ambient, directional,
hemisphere, point, spot).
*/
module.exports = {
schema: {
angle: { default: Math.PI / 3 },
castShadow: { default: false },
color: { default: '#FFF' },
groundColor: { default: '#FFF' },
decay: { default: 1 },
distance: { default: 0.0 },
exponent: { default: 10.0 },
intensity: { default: 1.0 },
shadowBias: { default: 0 },
shadowCameraFar: { default: 5000 },
shadowCameraFov: { default: 50 },
shadowCameraNear: { default: 0.5 },
shadowDarkness: { default: 0.5 },
shadowMapHeight: { default: 512 },
shadowMapWidth: { default: 512 },
type: { default: 'directional' }
},
init: function () {
var el = this.el;
this.light = this.getLight();
el.object3D.add(this.light);
el.sceneEl.systems.light.registerLight(el);
if (!el.sceneEl.hasLoaded) {
el.sceneEl.addEventListener('loaded', this.play.bind(this));
}
},
update: function (previousData) {
previousData = previousData || {};
if (!Object.keys(previousData).length) { return; }
this.el.object3D.remove(this.light);
this.light = this.getLight();
this.el.object3D.add(this.light);
},
play: function () {
var el = this.el,
renderer = el.sceneEl.renderer;
if (renderer && !renderer.shadowMap.enabled) {
renderer.shadowMap.enabled = true;
}
},
/**
* Creates a new three.js light object given the current attributes of the
* component.
*
* @namespace light
*/
getLight: function () {
var data = this.data;
var color = new THREE.Color(data.color).getHex();
var intensity = data.intensity;
var type = data.type;
if (type) {
type = type.toLowerCase();
}
switch (type) {
case 'ambient': {
return new THREE.AmbientLight(color);
}
case 'directional': {
return this.setShadow(new THREE.DirectionalLight(color, intensity));
}
case 'hemisphere': {
return new THREE.HemisphereLight(color, data.groundColor,
intensity);
}
case 'point': {
return this.setShadow(
new THREE.PointLight(color, intensity, data.distance, data.decay));
}
case 'spot': {
return this.setShadow(
new THREE.SpotLight(color, intensity, data.distance, data.angle,
data.exponent, data.decay));
}
default: {
return new THREE.AmbientLight(color);
}
}
},
/**
* Copy over shadow-related data from the component onto the light.
*
* @param {object} light
*/
setShadow: function (light) {
var data = this.data;
if (!data.castShadow) { return light; }
light.castShadow = data.castShadow;
light.shadow.camera.near = data.shadowCameraNear;
light.shadow.camera.far = data.shadowCameraFar;
light.shadow.camera.fov = data.shadowCameraFov;
light.shadow.darkness = data.shadowDarkness;
light.shadow.mapSize.height = data.shadowMapHeight;
light.shadow.mapSize.width = data.shadowMapWidth;
return light;
}
};
},{}],43:[function(require,module,exports){
/**
* Shadow component.
*
* Source: https://github.com/aframevr/aframe-core/pull/348
*
* @namespace shadow
* @param {bool} [cast=false] - whether object will cast shadows.
* @param {bool} [receive=false] - whether object will receive shadows.
*/
module.exports = {
schema: {
cast: { default: false },
receive: { default: false }
},
init: function () {
this.el.addEventListener('model-loaded', this.update.bind(this));
},
update: function () {
var data = this.data;
// Applied recursively to support imported models.
this.el.object3D.traverse(function(node) {
if (node instanceof THREE.Mesh) {
node.castShadow = data.cast;
node.receiveShadow = data.receive;
}
});
},
remove: function () {}
};
},{}],44:[function(require,module,exports){
module.exports={
"name": "cannon",
"version": "0.6.2",
"description": "A lightweight 3D physics engine written in JavaScript.",
"homepage": "https://github.com/schteppe/cannon.js",
"author": "Stefan Hedman <schteppe@gmail.com> (http://steffe.se)",
"keywords": [
"cannon.js",
"cannon",
"physics",
"engine",
"3d"
],
"main": "./src/Cannon.js",
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/schteppe/cannon.js.git"
},
"bugs": {
"url": "https://github.com/schteppe/cannon.js/issues"
},
"licenses": [
{
"type": "MIT"
}
],
"devDependencies": {
"jshint": "latest",
"uglify-js": "latest",
"nodeunit": "^0.9.0",
"grunt": "~0.4.0",
"grunt-contrib-jshint": "~0.1.1",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-concat": "~0.1.3",
"grunt-contrib-uglify": "^0.5.1",
"grunt-browserify": "^2.1.4",
"grunt-contrib-yuidoc": "^0.5.2",
"browserify": "*"
},
"dependencies": {}
}
},{}],45:[function(require,module,exports){
// Export classes
module.exports = {
version : require('../package.json').version,
AABB : require('./collision/AABB'),
ArrayCollisionMatrix : require('./collision/ArrayCollisionMatrix'),
Body : require('./objects/Body'),
Box : require('./shapes/Box'),
Broadphase : require('./collision/Broadphase'),
Constraint : require('./constraints/Constraint'),
ContactEquation : require('./equations/ContactEquation'),
Narrowphase : require('./world/Narrowphase'),
ConeTwistConstraint : require('./constraints/ConeTwistConstraint'),
ContactMaterial : require('./material/ContactMaterial'),
ConvexPolyhedron : require('./shapes/ConvexPolyhedron'),
Cylinder : require('./shapes/Cylinder'),
DistanceConstraint : require('./constraints/DistanceConstraint'),
Equation : require('./equations/Equation'),
EventTarget : require('./utils/EventTarget'),
FrictionEquation : require('./equations/FrictionEquation'),
GSSolver : require('./solver/GSSolver'),
GridBroadphase : require('./collision/GridBroadphase'),
Heightfield : require('./shapes/Heightfield'),
HingeConstraint : require('./constraints/HingeConstraint'),
LockConstraint : require('./constraints/LockConstraint'),
Mat3 : require('./math/Mat3'),
Material : require('./material/Material'),
NaiveBroadphase : require('./collision/NaiveBroadphase'),
ObjectCollisionMatrix : require('./collision/ObjectCollisionMatrix'),
Pool : require('./utils/Pool'),
Particle : require('./shapes/Particle'),
Plane : require('./shapes/Plane'),
PointToPointConstraint : require('./constraints/PointToPointConstraint'),
Quaternion : require('./math/Quaternion'),
Ray : require('./collision/Ray'),
RaycastVehicle : require('./objects/RaycastVehicle'),
RaycastResult : require('./collision/RaycastResult'),
RigidVehicle : require('./objects/RigidVehicle'),
RotationalEquation : require('./equations/RotationalEquation'),
RotationalMotorEquation : require('./equations/RotationalMotorEquation'),
SAPBroadphase : require('./collision/SAPBroadphase'),
SPHSystem : require('./objects/SPHSystem'),
Shape : require('./shapes/Shape'),
Solver : require('./solver/Solver'),
Sphere : require('./shapes/Sphere'),
SplitSolver : require('./solver/SplitSolver'),
Spring : require('./objects/Spring'),
Transform : require('./math/Transform'),
Trimesh : require('./shapes/Trimesh'),
Vec3 : require('./math/Vec3'),
Vec3Pool : require('./utils/Vec3Pool'),
World : require('./world/World'),
};
},{"../package.json":44,"./collision/AABB":46,"./collision/ArrayCollisionMatrix":47,"./collision/Broadphase":48,"./collision/GridBroadphase":49,"./collision/NaiveBroadphase":50,"./collision/ObjectCollisionMatrix":51,"./collision/Ray":53,"./collision/RaycastResult":54,"./collision/SAPBroadphase":55,"./constraints/ConeTwistConstraint":56,"./constraints/Constraint":57,"./constraints/DistanceConstraint":58,"./constraints/HingeConstraint":59,"./constraints/LockConstraint":60,"./constraints/PointToPointConstraint":61,"./equations/ContactEquation":63,"./equations/Equation":64,"./equations/FrictionEquation":65,"./equations/RotationalEquation":66,"./equations/RotationalMotorEquation":67,"./material/ContactMaterial":68,"./material/Material":69,"./math/Mat3":71,"./math/Quaternion":72,"./math/Transform":73,"./math/Vec3":74,"./objects/Body":75,"./objects/RaycastVehicle":76,"./objects/RigidVehicle":77,"./objects/SPHSystem":78,"./objects/Spring":79,"./shapes/Box":81,"./shapes/ConvexPolyhedron":82,"./shapes/Cylinder":83,"./shapes/Heightfield":84,"./shapes/Particle":85,"./shapes/Plane":86,"./shapes/Shape":87,"./shapes/Sphere":88,"./shapes/Trimesh":89,"./solver/GSSolver":90,"./solver/Solver":91,"./solver/SplitSolver":92,"./utils/EventTarget":93,"./utils/Pool":95,"./utils/Vec3Pool":98,"./world/Narrowphase":99,"./world/World":100}],46:[function(require,module,exports){
var Vec3 = require('../math/Vec3');
var Utils = require('../utils/Utils');
module.exports = AABB;
/**
* Axis aligned bounding box class.
* @class AABB
* @constructor
* @param {Object} [options]
* @param {Vec3} [options.upperBound]
* @param {Vec3} [options.lowerBound]
*/
function AABB(options){
options = options || {};
/**
* The lower bound of the bounding box.
* @property lowerBound
* @type {Vec3}
*/
this.lowerBound = new Vec3();
if(options.lowerBound){
this.lowerBound.copy(options.lowerBound);
}
/**
* The upper bound of the bounding box.
* @property upperBound
* @type {Vec3}
*/
this.upperBound = new Vec3();
if(options.upperBound){
this.upperBound.copy(options.upperBound);
}
}
var tmp = new Vec3();
/**
* Set the AABB bounds from a set of points.
* @method setFromPoints
* @param {Array} points An array of Vec3's.
* @param {Vec3} position
* @param {Quaternion} quaternion
* @param {number} skinSize
* @return {AABB} The self object
*/
AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){
var l = this.lowerBound,
u = this.upperBound,
q = quaternion;
// Set to the first point
l.copy(points[0]);
if(q){
q.vmult(l, l);
}
u.copy(l);
for(var i = 1; i<points.length; i++){
var p = points[i];
if(q){
q.vmult(p, tmp);
p = tmp;
}
if(p.x > u.x){ u.x = p.x; }
if(p.x < l.x){ l.x = p.x; }
if(p.y > u.y){ u.y = p.y; }
if(p.y < l.y){ l.y = p.y; }
if(p.z > u.z){ u.z = p.z; }
if(p.z < l.z){ l.z = p.z; }
}
// Add offset
if (position) {
position.vadd(l, l);
position.vadd(u, u);
}
if(skinSize){
l.x -= skinSize;
l.y -= skinSize;
l.z -= skinSize;
u.x += skinSize;
u.y += skinSize;
u.z += skinSize;
}
return this;
};
/**
* Copy bounds from an AABB to this AABB
* @method copy
* @param {AABB} aabb Source to copy from
* @return {AABB} The this object, for chainability
*/
AABB.prototype.copy = function(aabb){
this.lowerBound.copy(aabb.lowerBound);
this.upperBound.copy(aabb.upperBound);
return this;
};
/**
* Clone an AABB
* @method clone
*/
AABB.prototype.clone = function(){
return new AABB().copy(this);
};
/**
* Extend this AABB so that it covers the given AABB too.
* @method extend
* @param {AABB} aabb
*/
AABB.prototype.extend = function(aabb){
this.lowerBound.x = Math.min(this.lowerBound.x, aabb.lowerBound.x);
this.upperBound.x = Math.max(this.upperBound.x, aabb.upperBound.x);
this.lowerBound.y = Math.min(this.lowerBound.y, aabb.lowerBound.y);
this.upperBound.y = Math.max(this.upperBound.y, aabb.upperBound.y);
this.lowerBound.z = Math.min(this.lowerBound.z, aabb.lowerBound.z);
this.upperBound.z = Math.max(this.upperBound.z, aabb.upperBound.z);
};
/**
* Returns true if the given AABB overlaps this AABB.
* @method overlaps
* @param {AABB} aabb
* @return {Boolean}
*/
AABB.prototype.overlaps = function(aabb){
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |--------|
// l1 u1
var overlapsX = ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x));
var overlapsY = ((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y));
var overlapsZ = ((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z));
return overlapsX && overlapsY && overlapsZ;
};
// Mostly for debugging
AABB.prototype.volume = function(){
var l = this.lowerBound,
u = this.upperBound;
return (u.x - l.x) * (u.y - l.y) * (u.z - l.z);
};
/**
* Returns true if the given AABB is fully contained in this AABB.
* @method contains
* @param {AABB} aabb
* @return {Boolean}
*/
AABB.prototype.contains = function(aabb){
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |---------------|
// l1 u1
return (
(l1.x <= l2.x && u1.x >= u2.x) &&
(l1.y <= l2.y && u1.y >= u2.y) &&
(l1.z <= l2.z && u1.z >= u2.z)
);
};
/**
* @method getCorners
* @param {Vec3} a
* @param {Vec3} b
* @param {Vec3} c
* @param {Vec3} d
* @param {Vec3} e
* @param {Vec3} f
* @param {Vec3} g
* @param {Vec3} h
*/
AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){
var l = this.lowerBound,
u = this.upperBound;
a.copy(l);
b.set( u.x, l.y, l.z );
c.set( u.x, u.y, l.z );
d.set( l.x, u.y, u.z );
e.set( u.x, l.y, l.z );
f.set( l.x, u.y, l.z );
g.set( l.x, l.y, u.z );
h.copy(u);
};
var transformIntoFrame_corners = [
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3()
];
/**
* Get the representation of an AABB in another frame.
* @method toLocalFrame
* @param {Transform} frame
* @param {AABB} target
* @return {AABB} The "target" AABB object.
*/
AABB.prototype.toLocalFrame = function(frame, target){
var corners = transformIntoFrame_corners;
var a = corners[0];
var b = corners[1];
var c = corners[2];
var d = corners[3];
var e = corners[4];
var f = corners[5];
var g = corners[6];
var h = corners[7];
// Get corners in current frame
this.getCorners(a, b, c, d, e, f, g, h);
// Transform them to new local frame
for(var i=0; i !== 8; i++){
var corner = corners[i];
frame.pointToLocal(corner, corner);
}
return target.setFromPoints(corners);
};
/**
* Get the representation of an AABB in the global frame.
* @method toWorldFrame
* @param {Transform} frame
* @param {AABB} target
* @return {AABB} The "target" AABB object.
*/
AABB.prototype.toWorldFrame = function(frame, target){
var corners = transformIntoFrame_corners;
var a = corners[0];
var b = corners[1];
var c = corners[2];
var d = corners[3];
var e = corners[4];
var f = corners[5];
var g = corners[6];
var h = corners[7];
// Get corners in current frame
this.getCorners(a, b, c, d, e, f, g, h);
// Transform them to new local frame
for(var i=0; i !== 8; i++){
var corner = corners[i];
frame.pointToWorld(corner, corner);
}
return target.setFromPoints(corners);
};
/**
* Check if the AABB is hit by a ray.
* @param {Ray} ray
* @return {number}
*/
AABB.prototype.overlapsRay = function(ray){
var t = 0;
// ray.direction is unit direction vector of ray
var dirFracX = 1 / ray._direction.x;
var dirFracY = 1 / ray._direction.y;
var dirFracZ = 1 / ray._direction.z;
// this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
var t1 = (this.lowerBound.x - ray.from.x) * dirFracX;
var t2 = (this.upperBound.x - ray.from.x) * dirFracX;
var t3 = (this.lowerBound.y - ray.from.y) * dirFracY;
var t4 = (this.upperBound.y - ray.from.y) * dirFracY;
var t5 = (this.lowerBound.z - ray.from.z) * dirFracZ;
var t6 = (this.upperBound.z - ray.from.z) * dirFracZ;
// var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)));
// var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)));
var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
if (tmax < 0){
//t = tmax;
return false;
}
// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax){
//t = tmax;
return false;
}
return true;
};
},{"../math/Vec3":74,"../utils/Utils":97}],47:[function(require,module,exports){
module.exports = ArrayCollisionMatrix;
/**
* Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step
* @class ArrayCollisionMatrix
* @constructor
*/
function ArrayCollisionMatrix() {
/**
* The matrix storage
* @property matrix
* @type {Array}
*/
this.matrix = [];
}
/**
* Get an element
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
ArrayCollisionMatrix.prototype.get = function(i, j) {
i = i.index;
j = j.index;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
return this.matrix[(i*(i + 1)>>1) + j-1];
};
/**
* Set an element
* @method set
* @param {Number} i
* @param {Number} j
* @param {Number} value
*/
ArrayCollisionMatrix.prototype.set = function(i, j, value) {
i = i.index;
j = j.index;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0;
};
/**
* Sets all elements to zero
* @method reset
*/
ArrayCollisionMatrix.prototype.reset = function() {
for (var i=0, l=this.matrix.length; i!==l; i++) {
this.matrix[i]=0;
}
};
/**
* Sets the max number of objects
* @method setNumObjects
* @param {Number} n
*/
ArrayCollisionMatrix.prototype.setNumObjects = function(n) {
this.matrix.length = n*(n-1)>>1;
};
},{}],48:[function(require,module,exports){
var Body = require('../objects/Body');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Shape = require('../shapes/Shape');
var Plane = require('../shapes/Plane');
module.exports = Broadphase;
/**
* Base class for broadphase implementations
* @class Broadphase
* @constructor
* @author schteppe
*/
function Broadphase(){
/**
* The world to search for collisions in.
* @property world
* @type {World}
*/
this.world = null;
/**
* If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.
* @property useBoundingBoxes
* @type {Boolean}
*/
this.useBoundingBoxes = false;
/**
* Set to true if the objects in the world moved.
* @property {Boolean} dirty
*/
this.dirty = true;
}
/**
* Get the collision pairs from the world
* @method collisionPairs
* @param {World} world The world to search in
* @param {Array} p1 Empty array to be filled with body objects
* @param {Array} p2 Empty array to be filled with body objects
*/
Broadphase.prototype.collisionPairs = function(world,p1,p2){
throw new Error("collisionPairs not implemented for this BroadPhase class!");
};
/**
* Check if a body pair needs to be intersection tested at all.
* @method needBroadphaseCollision
* @param {Body} bodyA
* @param {Body} bodyB
* @return {bool}
*/
Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){
// Check collision filter masks
if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){
return false;
}
// Check types
if(((bodyA.type & Body.STATIC)!==0 || bodyA.sleepState === Body.SLEEPING) &&
((bodyB.type & Body.STATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) {
// Both bodies are static or sleeping. Skip.
return false;
}
return true;
};
/**
* Check if the bounding volumes of two bodies intersect.
* @method intersectionTest
* @param {Body} bodyA
* @param {Body} bodyB
* @param {array} pairs1
* @param {array} pairs2
*/
Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){
if(this.useBoundingBoxes){
this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2);
} else {
this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2);
}
};
/**
* Check if the bounding spheres of two bodies are intersecting.
* @method doBoundingSphereBroadphase
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Array} pairs1 bodyA is appended to this array if intersection
* @param {Array} pairs2 bodyB is appended to this array if intersection
*/
var Broadphase_collisionPairs_r = new Vec3(), // Temp objects
Broadphase_collisionPairs_normal = new Vec3(),
Broadphase_collisionPairs_quat = new Quaternion(),
Broadphase_collisionPairs_relpos = new Vec3();
Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){
var r = Broadphase_collisionPairs_r;
bodyB.position.vsub(bodyA.position,r);
var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2);
var norm2 = r.norm2();
if(norm2 < boundingRadiusSum2){
pairs1.push(bodyA);
pairs2.push(bodyB);
}
};
/**
* Check if the bounding boxes of two bodies are intersecting.
* @method doBoundingBoxBroadphase
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Array} pairs1
* @param {Array} pairs2
*/
Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){
if(bodyA.aabbNeedsUpdate){
bodyA.computeAABB();
}
if(bodyB.aabbNeedsUpdate){
bodyB.computeAABB();
}
// Check AABB / AABB
if(bodyA.aabb.overlaps(bodyB.aabb)){
pairs1.push(bodyA);
pairs2.push(bodyB);
}
};
/**
* Removes duplicate pairs from the pair arrays.
* @method makePairsUnique
* @param {Array} pairs1
* @param {Array} pairs2
*/
var Broadphase_makePairsUnique_temp = { keys:[] },
Broadphase_makePairsUnique_p1 = [],
Broadphase_makePairsUnique_p2 = [];
Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){
var t = Broadphase_makePairsUnique_temp,
p1 = Broadphase_makePairsUnique_p1,
p2 = Broadphase_makePairsUnique_p2,
N = pairs1.length;
for(var i=0; i!==N; i++){
p1[i] = pairs1[i];
p2[i] = pairs2[i];
}
pairs1.length = 0;
pairs2.length = 0;
for(var i=0; i!==N; i++){
var id1 = p1[i].id,
id2 = p2[i].id;
var key = id1 < id2 ? id1+","+id2 : id2+","+id1;
t[key] = i;
t.keys.push(key);
}
for(var i=0; i!==t.keys.length; i++){
var key = t.keys.pop(),
pairIndex = t[key];
pairs1.push(p1[pairIndex]);
pairs2.push(p2[pairIndex]);
delete t[key];
}
};
/**
* To be implemented by subcasses
* @method setWorld
* @param {World} world
*/
Broadphase.prototype.setWorld = function(world){
};
/**
* Check if the bounding spheres of two bodies overlap.
* @method boundingSphereCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {boolean}
*/
var bsc_dist = new Vec3();
Broadphase.boundingSphereCheck = function(bodyA,bodyB){
var dist = bsc_dist;
bodyA.position.vsub(bodyB.position,dist);
return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2();
};
/**
* Returns all the bodies within the AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
Broadphase.prototype.aabbQuery = function(world, aabb, result){
console.warn('.aabbQuery is not implemented in this Broadphase subclass.');
return [];
};
},{"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Plane":86,"../shapes/Shape":87}],49:[function(require,module,exports){
module.exports = GridBroadphase;
var Broadphase = require('./Broadphase');
var Vec3 = require('../math/Vec3');
var Shape = require('../shapes/Shape');
/**
* Axis aligned uniform grid broadphase.
* @class GridBroadphase
* @constructor
* @extends Broadphase
* @todo Needs support for more than just planes and spheres.
* @param {Vec3} aabbMin
* @param {Vec3} aabbMax
* @param {Number} nx Number of boxes along x
* @param {Number} ny Number of boxes along y
* @param {Number} nz Number of boxes along z
*/
function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){
Broadphase.apply(this);
this.nx = nx || 10;
this.ny = ny || 10;
this.nz = nz || 10;
this.aabbMin = aabbMin || new Vec3(100,100,100);
this.aabbMax = aabbMax || new Vec3(-100,-100,-100);
var nbins = this.nx * this.ny * this.nz;
if (nbins <= 0) {
throw "GridBroadphase: Each dimension's n must be >0";
}
this.bins = [];
this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow
this.bins.length = nbins;
this.binLengths.length = nbins;
for (var i=0;i<nbins;i++) {
this.bins[i]=[];
this.binLengths[i]=0;
}
}
GridBroadphase.prototype = new Broadphase();
GridBroadphase.prototype.constructor = GridBroadphase;
/**
* Get all the collision pairs in the physics world
* @method collisionPairs
* @param {World} world
* @param {Array} pairs1
* @param {Array} pairs2
*/
var GridBroadphase_collisionPairs_d = new Vec3();
var GridBroadphase_collisionPairs_binPos = new Vec3();
GridBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
var N = world.numObjects(),
bodies = world.bodies;
var max = this.aabbMax,
min = this.aabbMin,
nx = this.nx,
ny = this.ny,
nz = this.nz;
var xstep = ny*nz;
var ystep = nz;
var zstep = 1;
var xmax = max.x,
ymax = max.y,
zmax = max.z,
xmin = min.x,
ymin = min.y,
zmin = min.z;
var xmult = nx / (xmax-xmin),
ymult = ny / (ymax-ymin),
zmult = nz / (zmax-zmin);
var binsizeX = (xmax - xmin) / nx,
binsizeY = (ymax - ymin) / ny,
binsizeZ = (zmax - zmin) / nz;
var binRadius = Math.sqrt(binsizeX*binsizeX + binsizeY*binsizeY + binsizeZ*binsizeZ) * 0.5;
var types = Shape.types;
var SPHERE = types.SPHERE,
PLANE = types.PLANE,
BOX = types.BOX,
COMPOUND = types.COMPOUND,
CONVEXPOLYHEDRON = types.CONVEXPOLYHEDRON;
var bins=this.bins,
binLengths=this.binLengths,
Nbins=this.bins.length;
// Reset bins
for(var i=0; i!==Nbins; i++){
binLengths[i] = 0;
}
var ceil = Math.ceil;
var min = Math.min;
var max = Math.max;
function addBoxToBins(x0,y0,z0,x1,y1,z1,bi) {
var xoff0 = ((x0 - xmin) * xmult)|0,
yoff0 = ((y0 - ymin) * ymult)|0,
zoff0 = ((z0 - zmin) * zmult)|0,
xoff1 = ceil((x1 - xmin) * xmult),
yoff1 = ceil((y1 - ymin) * ymult),
zoff1 = ceil((z1 - zmin) * zmult);
if (xoff0 < 0) { xoff0 = 0; } else if (xoff0 >= nx) { xoff0 = nx - 1; }
if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; }
if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; }
if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; }
if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; }
if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; }
xoff0 *= xstep;
yoff0 *= ystep;
zoff0 *= zstep;
xoff1 *= xstep;
yoff1 *= ystep;
zoff1 *= zstep;
for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) {
for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) {
for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) {
var idx = xoff+yoff+zoff;
bins[idx][binLengths[idx]++] = bi;
}
}
}
}
// Put all bodies into the bins
for(var i=0; i!==N; i++){
var bi = bodies[i];
var si = bi.shape;
switch(si.type){
case SPHERE:
// Put in bin
// check if overlap with other bins
var x = bi.position.x,
y = bi.position.y,
z = bi.position.z;
var r = si.radius;
addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi);
break;
case PLANE:
if(si.worldNormalNeedsUpdate){
si.computeWorldNormal(bi.quaternion);
}
var planeNormal = si.worldNormal;
//Relative position from origin of plane object to the first bin
//Incremented as we iterate through the bins
var xreset = xmin + binsizeX*0.5 - bi.position.x,
yreset = ymin + binsizeY*0.5 - bi.position.y,
zreset = zmin + binsizeZ*0.5 - bi.position.z;
var d = GridBroadphase_collisionPairs_d;
d.set(xreset, yreset, zreset);
for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) {
for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) {
for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) {
if (d.dot(planeNormal) < binRadius) {
var idx = xoff + yoff + zoff;
bins[idx][binLengths[idx]++] = bi;
}
}
}
}
break;
default:
if (bi.aabbNeedsUpdate) {
bi.computeAABB();
}
addBoxToBins(
bi.aabb.lowerBound.x,
bi.aabb.lowerBound.y,
bi.aabb.lowerBound.z,
bi.aabb.upperBound.x,
bi.aabb.upperBound.y,
bi.aabb.upperBound.z,
bi);
break;
}
}
// Check each bin
for(var i=0; i!==Nbins; i++){
var binLength = binLengths[i];
//Skip bins with no potential collisions
if (binLength > 1) {
var bin = bins[i];
// Do N^2 broadphase inside
for(var xi=0; xi!==binLength; xi++){
var bi = bin[xi];
for(var yi=0; yi!==xi; yi++){
var bj = bin[yi];
if(this.needBroadphaseCollision(bi,bj)){
this.intersectionTest(bi,bj,pairs1,pairs2);
}
}
}
}
}
// for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) {
// console.log("layer "+zi);
// for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) {
// var row = '';
// for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) {
// var idx = xoff + yoff + zoff;
// row += ' ' + binLengths[idx];
// }
// console.log(row);
// }
// }
this.makePairsUnique(pairs1,pairs2);
};
},{"../math/Vec3":74,"../shapes/Shape":87,"./Broadphase":48}],50:[function(require,module,exports){
module.exports = NaiveBroadphase;
var Broadphase = require('./Broadphase');
var AABB = require('./AABB');
/**
* Naive broadphase implementation, used in lack of better ones.
* @class NaiveBroadphase
* @constructor
* @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad)
* @extends Broadphase
*/
function NaiveBroadphase(){
Broadphase.apply(this);
}
NaiveBroadphase.prototype = new Broadphase();
NaiveBroadphase.prototype.constructor = NaiveBroadphase;
/**
* Get all the collision pairs in the physics world
* @method collisionPairs
* @param {World} world
* @param {Array} pairs1
* @param {Array} pairs2
*/
NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
var bodies = world.bodies,
n = bodies.length,
i,j,bi,bj;
// Naive N^2 ftw!
for(i=0; i!==n; i++){
for(j=0; j!==i; j++){
bi = bodies[i];
bj = bodies[j];
if(!this.needBroadphaseCollision(bi,bj)){
continue;
}
this.intersectionTest(bi,bj,pairs1,pairs2);
}
}
};
var tmpAABB = new AABB();
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){
result = result || [];
for(var i = 0; i < world.bodies.length; i++){
var b = world.bodies[i];
if(b.aabbNeedsUpdate){
b.computeAABB();
}
// Ugly hack until Body gets aabb
if(b.aabb.overlaps(aabb)){
result.push(b);
}
}
return result;
};
},{"./AABB":46,"./Broadphase":48}],51:[function(require,module,exports){
module.exports = ObjectCollisionMatrix;
/**
* Records what objects are colliding with each other
* @class ObjectCollisionMatrix
* @constructor
*/
function ObjectCollisionMatrix() {
/**
* The matrix storage
* @property matrix
* @type {Object}
*/
this.matrix = {};
}
/**
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
ObjectCollisionMatrix.prototype.get = function(i, j) {
i = i.id;
j = j.id;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
return i+'-'+j in this.matrix;
};
/**
* @method set
* @param {Number} i
* @param {Number} j
* @param {Number} value
*/
ObjectCollisionMatrix.prototype.set = function(i, j, value) {
i = i.id;
j = j.id;
if (j > i) {
var temp = j;
j = i;
i = temp;
}
if (value) {
this.matrix[i+'-'+j] = true;
}
else {
delete this.matrix[i+'-'+j];
}
};
/**
* Empty the matrix
* @method reset
*/
ObjectCollisionMatrix.prototype.reset = function() {
this.matrix = {};
};
/**
* Set max number of objects
* @method setNumObjects
* @param {Number} n
*/
ObjectCollisionMatrix.prototype.setNumObjects = function(n) {
};
},{}],52:[function(require,module,exports){
module.exports = OverlapKeeper;
/**
* @class OverlapKeeper
* @constructor
*/
function OverlapKeeper() {
this.current = [];
this.previous = [];
}
OverlapKeeper.prototype.getKey = function(i, j) {
if (j < i) {
var temp = j;
j = i;
i = temp;
}
return (i << 16) | j;
};
/**
* @method set
* @param {Number} i
* @param {Number} j
*/
OverlapKeeper.prototype.set = function(i, j) {
// Insertion sort. This way the diff will have linear complexity.
var key = this.getKey(i, j);
var current = this.current;
var index = 0;
while(key > current[index]){
index++;
}
if(key === current[index]){
return; // Pair was already added
}
for(var j=current.length-1; j>=index; j--){
current[j + 1] = current[j];
}
current[index] = key;
};
/**
* @method tick
*/
OverlapKeeper.prototype.tick = function() {
var tmp = this.current;
this.current = this.previous;
this.previous = tmp;
this.current.length = 0;
};
function unpackAndPush(array, key){
array.push((key & 0xFFFF0000) >> 16, key & 0x0000FFFF);
}
/**
* @method getDiff
* @param {array} additions
* @param {array} removals
*/
OverlapKeeper.prototype.getDiff = function(additions, removals) {
var a = this.current;
var b = this.previous;
var al = a.length;
var bl = b.length;
var j=0;
for (var i = 0; i < al; i++) {
var found = false;
var keyA = a[i];
while(keyA > b[j]){
j++;
}
found = keyA === b[j];
if(!found){
unpackAndPush(additions, keyA);
}
}
j = 0;
for (var i = 0; i < bl; i++) {
var found = false;
var keyB = b[i];
while(keyB > a[j]){
j++;
}
found = a[j] === keyB;
if(!found){
unpackAndPush(removals, keyB);
}
}
};
},{}],53:[function(require,module,exports){
module.exports = Ray;
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Transform = require('../math/Transform');
var ConvexPolyhedron = require('../shapes/ConvexPolyhedron');
var Box = require('../shapes/Box');
var RaycastResult = require('../collision/RaycastResult');
var Shape = require('../shapes/Shape');
var AABB = require('../collision/AABB');
/**
* A line in 3D space that intersects bodies and return points.
* @class Ray
* @constructor
* @param {Vec3} from
* @param {Vec3} to
*/
function Ray(from, to){
/**
* @property {Vec3} from
*/
this.from = from ? from.clone() : new Vec3();
/**
* @property {Vec3} to
*/
this.to = to ? to.clone() : new Vec3();
/**
* @private
* @property {Vec3} _direction
*/
this._direction = new Vec3();
/**
* The precision of the ray. Used when checking parallelity etc.
* @property {Number} precision
*/
this.precision = 0.0001;
/**
* Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.
* @property {Boolean} checkCollisionResponse
*/
this.checkCollisionResponse = true;
/**
* If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.
* @property {Boolean} skipBackfaces
*/
this.skipBackfaces = false;
/**
* @property {number} collisionFilterMask
* @default -1
*/
this.collisionFilterMask = -1;
/**
* @property {number} collisionFilterGroup
* @default -1
*/
this.collisionFilterGroup = -1;
/**
* The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST.
* @property {number} mode
*/
this.mode = Ray.ANY;
/**
* Current result object.
* @property {RaycastResult} result
*/
this.result = new RaycastResult();
/**
* Will be set to true during intersectWorld() if the ray hit anything.
* @property {Boolean} hasHit
*/
this.hasHit = false;
/**
* Current, user-provided result callback. Will be used if mode is Ray.ALL.
* @property {Function} callback
*/
this.callback = function(result){};
}
Ray.prototype.constructor = Ray;
Ray.CLOSEST = 1;
Ray.ANY = 2;
Ray.ALL = 4;
var tmpAABB = new AABB();
var tmpArray = [];
/**
* Do itersection against all bodies in the given World.
* @method intersectWorld
* @param {World} world
* @param {object} options
* @return {Boolean} True if the ray hit anything, otherwise false.
*/
Ray.prototype.intersectWorld = function (world, options) {
this.mode = options.mode || Ray.ANY;
this.result = options.result || new RaycastResult();
this.skipBackfaces = !!options.skipBackfaces;
this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1;
this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1;
if(options.from){
this.from.copy(options.from);
}
if(options.to){
this.to.copy(options.to);
}
this.callback = options.callback || function(){};
this.hasHit = false;
this.result.reset();
this._updateDirection();
this.getAABB(tmpAABB);
tmpArray.length = 0;
world.broadphase.aabbQuery(world, tmpAABB, tmpArray);
this.intersectBodies(tmpArray);
return this.hasHit;
};
var v1 = new Vec3(),
v2 = new Vec3();
/*
* As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division
*/
Ray.pointInTriangle = pointInTriangle;
function pointInTriangle(p, a, b, c) {
c.vsub(a,v0);
b.vsub(a,v1);
p.vsub(a,v2);
var dot00 = v0.dot( v0 );
var dot01 = v0.dot( v1 );
var dot02 = v0.dot( v2 );
var dot11 = v1.dot( v1 );
var dot12 = v1.dot( v2 );
var u,v;
return ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) &&
( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) &&
( u + v < ( dot00 * dot11 - dot01 * dot01 ) );
}
/**
* Shoot a ray at a body, get back information about the hit.
* @method intersectBody
* @private
* @param {Body} body
* @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead.
*/
var intersectBody_xi = new Vec3();
var intersectBody_qi = new Quaternion();
Ray.prototype.intersectBody = function (body, result) {
if(result){
this.result = result;
this._updateDirection();
}
var checkCollisionResponse = this.checkCollisionResponse;
if(checkCollisionResponse && !body.collisionResponse){
return;
}
if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){
return;
}
var xi = intersectBody_xi;
var qi = intersectBody_qi;
for (var i = 0, N = body.shapes.length; i < N; i++) {
var shape = body.shapes[i];
if(checkCollisionResponse && !shape.collisionResponse){
continue; // Skip
}
body.quaternion.mult(body.shapeOrientations[i], qi);
body.quaternion.vmult(body.shapeOffsets[i], xi);
xi.vadd(body.position, xi);
this.intersectShape(
shape,
qi,
xi,
body
);
if(this.result._shouldStop){
break;
}
}
};
/**
* @method intersectBodies
* @param {Array} bodies An array of Body objects.
* @param {RaycastResult} [result] Deprecated
*/
Ray.prototype.intersectBodies = function (bodies, result) {
if(result){
this.result = result;
this._updateDirection();
}
for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) {
this.intersectBody(bodies[i]);
}
};
/**
* Updates the _direction vector.
* @private
* @method _updateDirection
*/
Ray.prototype._updateDirection = function(){
this.to.vsub(this.from, this._direction);
this._direction.normalize();
};
/**
* @method intersectShape
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectShape = function(shape, quat, position, body){
var from = this.from;
// Checking boundingSphere
var distance = distanceFromIntersection(from, this._direction, position);
if ( distance > shape.boundingSphereRadius ) {
return;
}
var intersectMethod = this[shape.type];
if(intersectMethod){
intersectMethod.call(this, shape, quat, position, body, shape);
}
};
var vector = new Vec3();
var normal = new Vec3();
var intersectPoint = new Vec3();
var a = new Vec3();
var b = new Vec3();
var c = new Vec3();
var d = new Vec3();
var tmpRaycastResult = new RaycastResult();
/**
* @method intersectBox
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectBox = function(shape, quat, position, body, reportedShape){
return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body, reportedShape);
};
Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox;
/**
* @method intersectPlane
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectPlane = function(shape, quat, position, body, reportedShape){
var from = this.from;
var to = this.to;
var direction = this._direction;
// Get plane normal
var worldNormal = new Vec3(0, 0, 1);
quat.vmult(worldNormal, worldNormal);
var len = new Vec3();
from.vsub(position, len);
var planeToFrom = len.dot(worldNormal);
to.vsub(position, len);
var planeToTo = len.dot(worldNormal);
if(planeToFrom * planeToTo > 0){
// "from" and "to" are on the same side of the plane... bail out
return;
}
if(from.distanceTo(to) < planeToFrom){
return;
}
var n_dot_dir = worldNormal.dot(direction);
if (Math.abs(n_dot_dir) < this.precision) {
// No intersection
return;
}
var planePointToFrom = new Vec3();
var dir_scaled_with_t = new Vec3();
var hitPointWorld = new Vec3();
from.vsub(position, planePointToFrom);
var t = -worldNormal.dot(planePointToFrom) / n_dot_dir;
direction.scale(t, dir_scaled_with_t);
from.vadd(dir_scaled_with_t, hitPointWorld);
this.reportIntersection(worldNormal, hitPointWorld, reportedShape, body, -1);
};
Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane;
/**
* Get the world AABB of the ray.
* @method getAABB
* @param {AABB} aabb
*/
Ray.prototype.getAABB = function(result){
var to = this.to;
var from = this.from;
result.lowerBound.x = Math.min(to.x, from.x);
result.lowerBound.y = Math.min(to.y, from.y);
result.lowerBound.z = Math.min(to.z, from.z);
result.upperBound.x = Math.max(to.x, from.x);
result.upperBound.y = Math.max(to.y, from.y);
result.upperBound.z = Math.max(to.z, from.z);
};
var intersectConvexOptions = {
faceList: [0]
};
var worldPillarOffset = new Vec3();
var intersectHeightfield_localRay = new Ray();
var intersectHeightfield_index = [];
var intersectHeightfield_minMax = [];
/**
* @method intersectHeightfield
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectHeightfield = function(shape, quat, position, body, reportedShape){
var data = shape.data,
w = shape.elementSize;
// Convert the ray to local heightfield coordinates
var localRay = intersectHeightfield_localRay; //new Ray(this.from, this.to);
localRay.from.copy(this.from);
localRay.to.copy(this.to);
Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from);
Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to);
localRay._updateDirection();
// Get the index of the data points to test against
var index = intersectHeightfield_index;
var iMinX, iMinY, iMaxX, iMaxY;
// Set to max
iMinX = iMinY = 0;
iMaxX = iMaxY = shape.data.length - 1;
var aabb = new AABB();
localRay.getAABB(aabb);
shape.getIndexOfPosition(aabb.lowerBound.x, aabb.lowerBound.y, index, true);
iMinX = Math.max(iMinX, index[0]);
iMinY = Math.max(iMinY, index[1]);
shape.getIndexOfPosition(aabb.upperBound.x, aabb.upperBound.y, index, true);
iMaxX = Math.min(iMaxX, index[0] + 1);
iMaxY = Math.min(iMaxY, index[1] + 1);
for(var i = iMinX; i < iMaxX; i++){
for(var j = iMinY; j < iMaxY; j++){
if(this.result._shouldStop){
return;
}
shape.getAabbAtIndex(i, j, aabb);
if(!aabb.overlapsRay(localRay)){
continue;
}
// Lower triangle
shape.getConvexTrianglePillar(i, j, false);
Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions);
if(this.result._shouldStop){
return;
}
// Upper triangle
shape.getConvexTrianglePillar(i, j, true);
Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions);
}
}
};
Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield;
var Ray_intersectSphere_intersectionPoint = new Vec3();
var Ray_intersectSphere_normal = new Vec3();
/**
* @method intersectSphere
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
*/
Ray.prototype.intersectSphere = function(shape, quat, position, body, reportedShape){
var from = this.from,
to = this.to,
r = shape.radius;
var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2);
var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z));
var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2);
var delta = Math.pow(b, 2) - 4 * a * c;
var intersectionPoint = Ray_intersectSphere_intersectionPoint;
var normal = Ray_intersectSphere_normal;
if(delta < 0){
// No intersection
return;
} else if(delta === 0){
// single intersection point
from.lerp(to, delta, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1);
} else {
var d1 = (- b - Math.sqrt(delta)) / (2 * a);
var d2 = (- b + Math.sqrt(delta)) / (2 * a);
if(d1 >= 0 && d1 <= 1){
from.lerp(to, d1, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1);
}
if(this.result._shouldStop){
return;
}
if(d2 >= 0 && d2 <= 1){
from.lerp(to, d2, intersectionPoint);
intersectionPoint.vsub(position, normal);
normal.normalize();
this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1);
}
}
};
Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere;
var intersectConvex_normal = new Vec3();
var intersectConvex_minDistNormal = new Vec3();
var intersectConvex_minDistIntersect = new Vec3();
var intersectConvex_vector = new Vec3();
/**
* @method intersectConvex
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
* @param {object} [options]
* @param {array} [options.faceList]
*/
Ray.prototype.intersectConvex = function intersectConvex(
shape,
quat,
position,
body,
reportedShape,
options
){
var minDistNormal = intersectConvex_minDistNormal;
var normal = intersectConvex_normal;
var vector = intersectConvex_vector;
var minDistIntersect = intersectConvex_minDistIntersect;
var faceList = (options && options.faceList) || null;
// Checking faces
var faces = shape.faces,
vertices = shape.vertices,
normals = shape.faceNormals;
var direction = this._direction;
var from = this.from;
var to = this.to;
var fromToDistance = from.distanceTo(to);
var minDist = -1;
var Nfaces = faceList ? faceList.length : faces.length;
var result = this.result;
for (var j = 0; !result._shouldStop && j < Nfaces; j++) {
var fi = faceList ? faceList[j] : j;
var face = faces[fi];
var faceNormal = normals[fi];
var q = quat;
var x = position;
// determine if ray intersects the plane of the face
// note: this works regardless of the direction of the face normal
// Get plane point in world coordinates...
vector.copy(vertices[face[0]]);
q.vmult(vector,vector);
vector.vadd(x,vector);
// ...but make it relative to the ray from. We'll fix this later.
vector.vsub(from,vector);
// Get plane normal
q.vmult(faceNormal,normal);
// If this dot product is negative, we have something interesting
var dot = direction.dot(normal);
// Bail out if ray and plane are parallel
if ( Math.abs( dot ) < this.precision ){
continue;
}
// calc distance to plane
var scalar = normal.dot(vector) / dot;
// if negative distance, then plane is behind ray
if (scalar < 0){
continue;
}
// if (dot < 0) {
// Intersection point is from + direction * scalar
direction.mult(scalar,intersectPoint);
intersectPoint.vadd(from,intersectPoint);
// a is the point we compare points b and c with.
a.copy(vertices[face[0]]);
q.vmult(a,a);
x.vadd(a,a);
for(var i = 1; !result._shouldStop && i < face.length - 1; i++){
// Transform 3 vertices to world coords
b.copy(vertices[face[i]]);
c.copy(vertices[face[i+1]]);
q.vmult(b,b);
q.vmult(c,c);
x.vadd(b,b);
x.vadd(c,c);
var distance = intersectPoint.distanceTo(from);
if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){
continue;
}
this.reportIntersection(normal, intersectPoint, reportedShape, body, fi);
}
// }
}
};
Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex;
var intersectTrimesh_normal = new Vec3();
var intersectTrimesh_localDirection = new Vec3();
var intersectTrimesh_localFrom = new Vec3();
var intersectTrimesh_localTo = new Vec3();
var intersectTrimesh_worldNormal = new Vec3();
var intersectTrimesh_worldIntersectPoint = new Vec3();
var intersectTrimesh_localAABB = new AABB();
var intersectTrimesh_triangles = [];
var intersectTrimesh_treeTransform = new Transform();
/**
* @method intersectTrimesh
* @private
* @param {Shape} shape
* @param {Quaternion} quat
* @param {Vec3} position
* @param {Body} body
* @param {object} [options]
* @todo Optimize by transforming the world to local space first.
* @todo Use Octree lookup
*/
Ray.prototype.intersectTrimesh = function intersectTrimesh(
mesh,
quat,
position,
body,
reportedShape,
options
){
var normal = intersectTrimesh_normal;
var triangles = intersectTrimesh_triangles;
var treeTransform = intersectTrimesh_treeTransform;
var minDistNormal = intersectConvex_minDistNormal;
var vector = intersectConvex_vector;
var minDistIntersect = intersectConvex_minDistIntersect;
var localAABB = intersectTrimesh_localAABB;
var localDirection = intersectTrimesh_localDirection;
var localFrom = intersectTrimesh_localFrom;
var localTo = intersectTrimesh_localTo;
var worldIntersectPoint = intersectTrimesh_worldIntersectPoint;
var worldNormal = intersectTrimesh_worldNormal;
var faceList = (options && options.faceList) || null;
// Checking faces
var indices = mesh.indices,
vertices = mesh.vertices,
normals = mesh.faceNormals;
var from = this.from;
var to = this.to;
var direction = this._direction;
var minDist = -1;
treeTransform.position.copy(position);
treeTransform.quaternion.copy(quat);
// Transform ray to local space!
Transform.vectorToLocalFrame(position, quat, direction, localDirection);
Transform.pointToLocalFrame(position, quat, from, localFrom);
Transform.pointToLocalFrame(position, quat, to, localTo);
localTo.x *= mesh.scale.x;
localTo.y *= mesh.scale.y;
localTo.z *= mesh.scale.z;
localFrom.x *= mesh.scale.x;
localFrom.y *= mesh.scale.y;
localFrom.z *= mesh.scale.z;
localTo.vsub(localFrom, localDirection);
localDirection.normalize();
var fromToDistanceSquared = localFrom.distanceSquared(localTo);
mesh.tree.rayQuery(this, treeTransform, triangles);
for (var i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) {
var trianglesIndex = triangles[i];
mesh.getNormal(trianglesIndex, normal);
// determine if ray intersects the plane of the face
// note: this works regardless of the direction of the face normal
// Get plane point in world coordinates...
mesh.getVertex(indices[trianglesIndex * 3], a);
// ...but make it relative to the ray from. We'll fix this later.
a.vsub(localFrom,vector);
// If this dot product is negative, we have something interesting
var dot = localDirection.dot(normal);
// Bail out if ray and plane are parallel
// if (Math.abs( dot ) < this.precision){
// continue;
// }
// calc distance to plane
var scalar = normal.dot(vector) / dot;
// if negative distance, then plane is behind ray
if (scalar < 0){
continue;
}
// Intersection point is from + direction * scalar
localDirection.scale(scalar,intersectPoint);
intersectPoint.vadd(localFrom,intersectPoint);
// Get triangle vertices
mesh.getVertex(indices[trianglesIndex * 3 + 1], b);
mesh.getVertex(indices[trianglesIndex * 3 + 2], c);
var squaredDistance = intersectPoint.distanceSquared(localFrom);
if(!(pointInTriangle(intersectPoint, b, a, c) || pointInTriangle(intersectPoint, a, b, c)) || squaredDistance > fromToDistanceSquared){
continue;
}
// transform intersectpoint and normal to world
Transform.vectorToWorldFrame(quat, normal, worldNormal);
Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint);
this.reportIntersection(worldNormal, worldIntersectPoint, reportedShape, body, trianglesIndex);
}
triangles.length = 0;
};
Ray.prototype[Shape.types.TRIMESH] = Ray.prototype.intersectTrimesh;
/**
* @method reportIntersection
* @private
* @param {Vec3} normal
* @param {Vec3} hitPointWorld
* @param {Shape} shape
* @param {Body} body
* @return {boolean} True if the intersections should continue
*/
Ray.prototype.reportIntersection = function(normal, hitPointWorld, shape, body, hitFaceIndex){
var from = this.from;
var to = this.to;
var distance = from.distanceTo(hitPointWorld);
var result = this.result;
// Skip back faces?
if(this.skipBackfaces && normal.dot(this._direction) > 0){
return;
}
result.hitFaceIndex = typeof(hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1;
switch(this.mode){
case Ray.ALL:
this.hasHit = true;
result.set(
from,
to,
normal,
hitPointWorld,
shape,
body,
distance
);
result.hasHit = true;
this.callback(result);
break;
case Ray.CLOSEST:
// Store if closer than current closest
if(distance < result.distance || !result.hasHit){
this.hasHit = true;
result.hasHit = true;
result.set(
from,
to,
normal,
hitPointWorld,
shape,
body,
distance
);
}
break;
case Ray.ANY:
// Report and stop.
this.hasHit = true;
result.hasHit = true;
result.set(
from,
to,
normal,
hitPointWorld,
shape,
body,
distance
);
result._shouldStop = true;
break;
}
};
var v0 = new Vec3(),
intersect = new Vec3();
function distanceFromIntersection(from, direction, position) {
// v0 is vector from from to position
position.vsub(from,v0);
var dot = v0.dot(direction);
// intersect = direction*dot + from
direction.mult(dot,intersect);
intersect.vadd(from,intersect);
var distance = position.distanceTo(intersect);
return distance;
}
},{"../collision/AABB":46,"../collision/RaycastResult":54,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../shapes/Box":81,"../shapes/ConvexPolyhedron":82,"../shapes/Shape":87}],54:[function(require,module,exports){
var Vec3 = require('../math/Vec3');
module.exports = RaycastResult;
/**
* Storage for Ray casting data.
* @class RaycastResult
* @constructor
*/
function RaycastResult(){
/**
* @property {Vec3} rayFromWorld
*/
this.rayFromWorld = new Vec3();
/**
* @property {Vec3} rayToWorld
*/
this.rayToWorld = new Vec3();
/**
* @property {Vec3} hitNormalWorld
*/
this.hitNormalWorld = new Vec3();
/**
* @property {Vec3} hitPointWorld
*/
this.hitPointWorld = new Vec3();
/**
* @property {boolean} hasHit
*/
this.hasHit = false;
/**
* The hit shape, or null.
* @property {Shape} shape
*/
this.shape = null;
/**
* The hit body, or null.
* @property {Body} body
*/
this.body = null;
/**
* The index of the hit triangle, if the hit shape was a trimesh.
* @property {number} hitFaceIndex
* @default -1
*/
this.hitFaceIndex = -1;
/**
* Distance to the hit. Will be set to -1 if there was no hit.
* @property {number} distance
* @default -1
*/
this.distance = -1;
/**
* If the ray should stop traversing the bodies.
* @private
* @property {Boolean} _shouldStop
* @default false
*/
this._shouldStop = false;
}
/**
* Reset all result data.
* @method reset
*/
RaycastResult.prototype.reset = function () {
this.rayFromWorld.setZero();
this.rayToWorld.setZero();
this.hitNormalWorld.setZero();
this.hitPointWorld.setZero();
this.hasHit = false;
this.shape = null;
this.body = null;
this.hitFaceIndex = -1;
this.distance = -1;
this._shouldStop = false;
};
/**
* @method abort
*/
RaycastResult.prototype.abort = function(){
this._shouldStop = true;
};
/**
* @method set
* @param {Vec3} rayFromWorld
* @param {Vec3} rayToWorld
* @param {Vec3} hitNormalWorld
* @param {Vec3} hitPointWorld
* @param {Shape} shape
* @param {Body} body
* @param {number} distance
*/
RaycastResult.prototype.set = function(
rayFromWorld,
rayToWorld,
hitNormalWorld,
hitPointWorld,
shape,
body,
distance
){
this.rayFromWorld.copy(rayFromWorld);
this.rayToWorld.copy(rayToWorld);
this.hitNormalWorld.copy(hitNormalWorld);
this.hitPointWorld.copy(hitPointWorld);
this.shape = shape;
this.body = body;
this.distance = distance;
};
},{"../math/Vec3":74}],55:[function(require,module,exports){
var Shape = require('../shapes/Shape');
var Broadphase = require('../collision/Broadphase');
module.exports = SAPBroadphase;
/**
* Sweep and prune broadphase along one axis.
*
* @class SAPBroadphase
* @constructor
* @param {World} [world]
* @extends Broadphase
*/
function SAPBroadphase(world){
Broadphase.apply(this);
/**
* List of bodies currently in the broadphase.
* @property axisList
* @type {Array}
*/
this.axisList = [];
/**
* The world to search in.
* @property world
* @type {World}
*/
this.world = null;
/**
* Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.
* @property axisIndex
* @type {Number}
*/
this.axisIndex = 0;
var axisList = this.axisList;
this._addBodyHandler = function(e){
axisList.push(e.body);
};
this._removeBodyHandler = function(e){
var idx = axisList.indexOf(e.body);
if(idx !== -1){
axisList.splice(idx,1);
}
};
if(world){
this.setWorld(world);
}
}
SAPBroadphase.prototype = new Broadphase();
/**
* Change the world
* @method setWorld
* @param {World} world
*/
SAPBroadphase.prototype.setWorld = function(world){
// Clear the old axis array
this.axisList.length = 0;
// Add all bodies from the new world
for(var i=0; i<world.bodies.length; i++){
this.axisList.push(world.bodies[i]);
}
// Remove old handlers, if any
world.removeEventListener("addBody", this._addBodyHandler);
world.removeEventListener("removeBody", this._removeBodyHandler);
// Add handlers to update the list of bodies.
world.addEventListener("addBody", this._addBodyHandler);
world.addEventListener("removeBody", this._removeBodyHandler);
this.world = world;
this.dirty = true;
};
/**
* @static
* @method insertionSortX
* @param {Array} a
* @return {Array}
*/
SAPBroadphase.insertionSortX = function(a) {
for(var i=1,l=a.length;i<l;i++) {
var v = a[i];
for(var j=i - 1;j>=0;j--) {
if(a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x){
break;
}
a[j+1] = a[j];
}
a[j+1] = v;
}
return a;
};
/**
* @static
* @method insertionSortY
* @param {Array} a
* @return {Array}
*/
SAPBroadphase.insertionSortY = function(a) {
for(var i=1,l=a.length;i<l;i++) {
var v = a[i];
for(var j=i - 1;j>=0;j--) {
if(a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y){
break;
}
a[j+1] = a[j];
}
a[j+1] = v;
}
return a;
};
/**
* @static
* @method insertionSortZ
* @param {Array} a
* @return {Array}
*/
SAPBroadphase.insertionSortZ = function(a) {
for(var i=1,l=a.length;i<l;i++) {
var v = a[i];
for(var j=i - 1;j>=0;j--) {
if(a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z){
break;
}
a[j+1] = a[j];
}
a[j+1] = v;
}
return a;
};
/**
* Collect all collision pairs
* @method collisionPairs
* @param {World} world
* @param {Array} p1
* @param {Array} p2
*/
SAPBroadphase.prototype.collisionPairs = function(world,p1,p2){
var bodies = this.axisList,
N = bodies.length,
axisIndex = this.axisIndex,
i, j;
if(this.dirty){
this.sortList();
this.dirty = false;
}
// Look through the list
for(i=0; i !== N; i++){
var bi = bodies[i];
for(j=i+1; j < N; j++){
var bj = bodies[j];
if(!this.needBroadphaseCollision(bi,bj)){
continue;
}
if(!SAPBroadphase.checkBounds(bi,bj,axisIndex)){
break;
}
this.intersectionTest(bi,bj,p1,p2);
}
}
};
SAPBroadphase.prototype.sortList = function(){
var axisList = this.axisList;
var axisIndex = this.axisIndex;
var N = axisList.length;
// Update AABBs
for(var i = 0; i!==N; i++){
var bi = axisList[i];
if(bi.aabbNeedsUpdate){
bi.computeAABB();
}
}
// Sort the list
if(axisIndex === 0){
SAPBroadphase.insertionSortX(axisList);
} else if(axisIndex === 1){
SAPBroadphase.insertionSortY(axisList);
} else if(axisIndex === 2){
SAPBroadphase.insertionSortZ(axisList);
}
};
/**
* Check if the bounds of two bodies overlap, along the given SAP axis.
* @static
* @method checkBounds
* @param {Body} bi
* @param {Body} bj
* @param {Number} axisIndex
* @return {Boolean}
*/
SAPBroadphase.checkBounds = function(bi, bj, axisIndex){
var biPos;
var bjPos;
if(axisIndex === 0){
biPos = bi.position.x;
bjPos = bj.position.x;
} else if(axisIndex === 1){
biPos = bi.position.y;
bjPos = bj.position.y;
} else if(axisIndex === 2){
biPos = bi.position.z;
bjPos = bj.position.z;
}
var ri = bi.boundingRadius,
rj = bj.boundingRadius,
boundA1 = biPos - ri,
boundA2 = biPos + ri,
boundB1 = bjPos - rj,
boundB2 = bjPos + rj;
return boundB1 < boundA2;
};
/**
* Computes the variance of the body positions and estimates the best
* axis to use. Will automatically set property .axisIndex.
* @method autoDetectAxis
*/
SAPBroadphase.prototype.autoDetectAxis = function(){
var sumX=0,
sumX2=0,
sumY=0,
sumY2=0,
sumZ=0,
sumZ2=0,
bodies = this.axisList,
N = bodies.length,
invN=1/N;
for(var i=0; i!==N; i++){
var b = bodies[i];
var centerX = b.position.x;
sumX += centerX;
sumX2 += centerX*centerX;
var centerY = b.position.y;
sumY += centerY;
sumY2 += centerY*centerY;
var centerZ = b.position.z;
sumZ += centerZ;
sumZ2 += centerZ*centerZ;
}
var varianceX = sumX2 - sumX*sumX*invN,
varianceY = sumY2 - sumY*sumY*invN,
varianceZ = sumZ2 - sumZ*sumZ*invN;
if(varianceX > varianceY){
if(varianceX > varianceZ){
this.axisIndex = 0;
} else{
this.axisIndex = 2;
}
} else if(varianceY > varianceZ){
this.axisIndex = 1;
} else{
this.axisIndex = 2;
}
};
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){
result = result || [];
if(this.dirty){
this.sortList();
this.dirty = false;
}
var axisIndex = this.axisIndex, axis = 'x';
if(axisIndex === 1){ axis = 'y'; }
if(axisIndex === 2){ axis = 'z'; }
var axisList = this.axisList;
var lower = aabb.lowerBound[axis];
var upper = aabb.upperBound[axis];
for(var i = 0; i < axisList.length; i++){
var b = axisList[i];
if(b.aabbNeedsUpdate){
b.computeAABB();
}
if(b.aabb.overlaps(aabb)){
result.push(b);
}
}
return result;
};
},{"../collision/Broadphase":48,"../shapes/Shape":87}],56:[function(require,module,exports){
module.exports = ConeTwistConstraint;
var Constraint = require('./Constraint');
var PointToPointConstraint = require('./PointToPointConstraint');
var ConeEquation = require('../equations/ConeEquation');
var RotationalEquation = require('../equations/RotationalEquation');
var ContactEquation = require('../equations/ContactEquation');
var Vec3 = require('../math/Vec3');
/**
* @class ConeTwistConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {object} [options]
* @param {Vec3} [options.pivotA]
* @param {Vec3} [options.pivotB]
* @param {Vec3} [options.axisA]
* @param {Vec3} [options.axisB]
* @param {Number} [options.maxForce=1e6]
* @extends PointToPointConstraint
*/
function ConeTwistConstraint(bodyA, bodyB, options){
options = options || {};
var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
// Set pivot point in between
var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3();
var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3();
this.axisA = options.axisA ? options.axisA.clone() : new Vec3();
this.axisB = options.axisB ? options.axisB.clone() : new Vec3();
PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
this.collideConnected = !!options.collideConnected;
this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0;
/**
* @property {ConeEquation} coneEquation
*/
var c = this.coneEquation = new ConeEquation(bodyA,bodyB,options);
/**
* @property {RotationalEquation} twistEquation
*/
var t = this.twistEquation = new RotationalEquation(bodyA,bodyB,options);
this.twistAngle = typeof(options.twistAngle) !== 'undefined' ? options.twistAngle : 0;
// Make the cone equation push the bodies toward the cone axis, not outward
c.maxForce = 0;
c.minForce = -maxForce;
// Make the twist equation add torque toward the initial position
t.maxForce = 0;
t.minForce = -maxForce;
this.equations.push(c, t);
}
ConeTwistConstraint.prototype = new PointToPointConstraint();
ConeTwistConstraint.constructor = ConeTwistConstraint;
var ConeTwistConstraint_update_tmpVec1 = new Vec3();
var ConeTwistConstraint_update_tmpVec2 = new Vec3();
ConeTwistConstraint.prototype.update = function(){
var bodyA = this.bodyA,
bodyB = this.bodyB,
cone = this.coneEquation,
twist = this.twistEquation;
PointToPointConstraint.prototype.update.call(this);
// Update the axes to the cone constraint
bodyA.vectorToWorldFrame(this.axisA, cone.axisA);
bodyB.vectorToWorldFrame(this.axisB, cone.axisB);
// Update the world axes in the twist constraint
this.axisA.tangents(twist.axisA, twist.axisA);
bodyA.vectorToWorldFrame(twist.axisA, twist.axisA);
this.axisB.tangents(twist.axisB, twist.axisB);
bodyB.vectorToWorldFrame(twist.axisB, twist.axisB);
cone.angle = this.angle;
twist.maxAngle = this.twistAngle;
};
},{"../equations/ConeEquation":62,"../equations/ContactEquation":63,"../equations/RotationalEquation":66,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],57:[function(require,module,exports){
module.exports = Constraint;
var Utils = require('../utils/Utils');
/**
* Constraint base class
* @class Constraint
* @author schteppe
* @constructor
* @param {Body} bodyA
* @param {Body} bodyB
* @param {object} [options]
* @param {boolean} [options.collideConnected=true]
* @param {boolean} [options.wakeUpBodies=true]
*/
function Constraint(bodyA, bodyB, options){
options = Utils.defaults(options,{
collideConnected : true,
wakeUpBodies : true,
});
/**
* Equations to be solved in this constraint
* @property equations
* @type {Array}
*/
this.equations = [];
/**
* @property {Body} bodyA
*/
this.bodyA = bodyA;
/**
* @property {Body} bodyB
*/
this.bodyB = bodyB;
/**
* @property {Number} id
*/
this.id = Constraint.idCounter++;
/**
* Set to true if you want the bodies to collide when they are connected.
* @property collideConnected
* @type {boolean}
*/
this.collideConnected = options.collideConnected;
if(options.wakeUpBodies){
if(bodyA){
bodyA.wakeUp();
}
if(bodyB){
bodyB.wakeUp();
}
}
}
/**
* Update all the equations with data.
* @method update
*/
Constraint.prototype.update = function(){
throw new Error("method update() not implmemented in this Constraint subclass!");
};
/**
* Enables all equations in the constraint.
* @method enable
*/
Constraint.prototype.enable = function(){
var eqs = this.equations;
for(var i=0; i<eqs.length; i++){
eqs[i].enabled = true;
}
};
/**
* Disables all equations in the constraint.
* @method disable
*/
Constraint.prototype.disable = function(){
var eqs = this.equations;
for(var i=0; i<eqs.length; i++){
eqs[i].enabled = false;
}
};
Constraint.idCounter = 0;
},{"../utils/Utils":97}],58:[function(require,module,exports){
module.exports = DistanceConstraint;
var Constraint = require('./Constraint');
var ContactEquation = require('../equations/ContactEquation');
/**
* Constrains two bodies to be at a constant distance from each others center of mass.
* @class DistanceConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Number} [distance] The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB
* @param {Number} [maxForce=1e6]
* @extends Constraint
*/
function DistanceConstraint(bodyA,bodyB,distance,maxForce){
Constraint.call(this,bodyA,bodyB);
if(typeof(distance)==="undefined") {
distance = bodyA.position.distanceTo(bodyB.position);
}
if(typeof(maxForce)==="undefined") {
maxForce = 1e6;
}
/**
* @property {number} distance
*/
this.distance = distance;
/**
* @property {ContactEquation} distanceEquation
*/
var eq = this.distanceEquation = new ContactEquation(bodyA, bodyB);
this.equations.push(eq);
// Make it bidirectional
eq.minForce = -maxForce;
eq.maxForce = maxForce;
}
DistanceConstraint.prototype = new Constraint();
DistanceConstraint.prototype.update = function(){
var bodyA = this.bodyA;
var bodyB = this.bodyB;
var eq = this.distanceEquation;
var halfDist = this.distance * 0.5;
var normal = eq.ni;
bodyB.position.vsub(bodyA.position, normal);
normal.normalize();
normal.mult(halfDist, eq.ri);
normal.mult(-halfDist, eq.rj);
};
},{"../equations/ContactEquation":63,"./Constraint":57}],59:[function(require,module,exports){
module.exports = HingeConstraint;
var Constraint = require('./Constraint');
var PointToPointConstraint = require('./PointToPointConstraint');
var RotationalEquation = require('../equations/RotationalEquation');
var RotationalMotorEquation = require('../equations/RotationalMotorEquation');
var ContactEquation = require('../equations/ContactEquation');
var Vec3 = require('../math/Vec3');
/**
* Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation.
* @class HingeConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {object} [options]
* @param {Vec3} [options.pivotA] A point defined locally in bodyA. This defines the offset of axisA.
* @param {Vec3} [options.axisA] An axis that bodyA can rotate around, defined locally in bodyA.
* @param {Vec3} [options.pivotB]
* @param {Vec3} [options.axisB]
* @param {Number} [options.maxForce=1e6]
* @extends PointToPointConstraint
*/
function HingeConstraint(bodyA, bodyB, options){
options = options || {};
var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3();
var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3();
PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
/**
* Rotation axis, defined locally in bodyA.
* @property {Vec3} axisA
*/
var axisA = this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1,0,0);
axisA.normalize();
/**
* Rotation axis, defined locally in bodyB.
* @property {Vec3} axisB
*/
var axisB = this.axisB = options.axisB ? options.axisB.clone() : new Vec3(1,0,0);
axisB.normalize();
/**
* @property {RotationalEquation} rotationalEquation1
*/
var r1 = this.rotationalEquation1 = new RotationalEquation(bodyA,bodyB,options);
/**
* @property {RotationalEquation} rotationalEquation2
*/
var r2 = this.rotationalEquation2 = new RotationalEquation(bodyA,bodyB,options);
/**
* @property {RotationalMotorEquation} motorEquation
*/
var motor = this.motorEquation = new RotationalMotorEquation(bodyA,bodyB,maxForce);
motor.enabled = false; // Not enabled by default
// Equations to be fed to the solver
this.equations.push(
r1, // rotational1
r2, // rotational2
motor
);
}
HingeConstraint.prototype = new PointToPointConstraint();
HingeConstraint.constructor = HingeConstraint;
/**
* @method enableMotor
*/
HingeConstraint.prototype.enableMotor = function(){
this.motorEquation.enabled = true;
};
/**
* @method disableMotor
*/
HingeConstraint.prototype.disableMotor = function(){
this.motorEquation.enabled = false;
};
/**
* @method setMotorSpeed
* @param {number} speed
*/
HingeConstraint.prototype.setMotorSpeed = function(speed){
this.motorEquation.targetVelocity = speed;
};
/**
* @method setMotorMaxForce
* @param {number} maxForce
*/
HingeConstraint.prototype.setMotorMaxForce = function(maxForce){
this.motorEquation.maxForce = maxForce;
this.motorEquation.minForce = -maxForce;
};
var HingeConstraint_update_tmpVec1 = new Vec3();
var HingeConstraint_update_tmpVec2 = new Vec3();
HingeConstraint.prototype.update = function(){
var bodyA = this.bodyA,
bodyB = this.bodyB,
motor = this.motorEquation,
r1 = this.rotationalEquation1,
r2 = this.rotationalEquation2,
worldAxisA = HingeConstraint_update_tmpVec1,
worldAxisB = HingeConstraint_update_tmpVec2;
var axisA = this.axisA;
var axisB = this.axisB;
PointToPointConstraint.prototype.update.call(this);
// Get world axes
bodyA.quaternion.vmult(axisA, worldAxisA);
bodyB.quaternion.vmult(axisB, worldAxisB);
worldAxisA.tangents(r1.axisA, r2.axisA);
r1.axisB.copy(worldAxisB);
r2.axisB.copy(worldAxisB);
if(this.motorEquation.enabled){
bodyA.quaternion.vmult(this.axisA, motor.axisA);
bodyB.quaternion.vmult(this.axisB, motor.axisB);
}
};
},{"../equations/ContactEquation":63,"../equations/RotationalEquation":66,"../equations/RotationalMotorEquation":67,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],60:[function(require,module,exports){
module.exports = LockConstraint;
var Constraint = require('./Constraint');
var PointToPointConstraint = require('./PointToPointConstraint');
var RotationalEquation = require('../equations/RotationalEquation');
var RotationalMotorEquation = require('../equations/RotationalMotorEquation');
var ContactEquation = require('../equations/ContactEquation');
var Vec3 = require('../math/Vec3');
/**
* Lock constraint. Will remove all degrees of freedom between the bodies.
* @class LockConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {object} [options]
* @param {Number} [options.maxForce=1e6]
* @extends PointToPointConstraint
*/
function LockConstraint(bodyA, bodyB, options){
options = options || {};
var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
// Set pivot point in between
var pivotA = new Vec3();
var pivotB = new Vec3();
var halfWay = new Vec3();
bodyA.position.vadd(bodyB.position, halfWay);
halfWay.scale(0.5, halfWay);
bodyB.pointToLocalFrame(halfWay, pivotB);
bodyA.pointToLocalFrame(halfWay, pivotA);
// The point-to-point constraint will keep a point shared between the bodies
PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
// Store initial rotation of the bodies as unit vectors in the local body spaces
this.xA = bodyA.vectorToLocalFrame(Vec3.UNIT_X);
this.xB = bodyB.vectorToLocalFrame(Vec3.UNIT_X);
this.yA = bodyA.vectorToLocalFrame(Vec3.UNIT_Y);
this.yB = bodyB.vectorToLocalFrame(Vec3.UNIT_Y);
this.zA = bodyA.vectorToLocalFrame(Vec3.UNIT_Z);
this.zB = bodyB.vectorToLocalFrame(Vec3.UNIT_Z);
// ...and the following rotational equations will keep all rotational DOF's in place
/**
* @property {RotationalEquation} rotationalEquation1
*/
var r1 = this.rotationalEquation1 = new RotationalEquation(bodyA,bodyB,options);
/**
* @property {RotationalEquation} rotationalEquation2
*/
var r2 = this.rotationalEquation2 = new RotationalEquation(bodyA,bodyB,options);
/**
* @property {RotationalEquation} rotationalEquation3
*/
var r3 = this.rotationalEquation3 = new RotationalEquation(bodyA,bodyB,options);
this.equations.push(r1, r2, r3);
}
LockConstraint.prototype = new PointToPointConstraint();
LockConstraint.constructor = LockConstraint;
var LockConstraint_update_tmpVec1 = new Vec3();
var LockConstraint_update_tmpVec2 = new Vec3();
LockConstraint.prototype.update = function(){
var bodyA = this.bodyA,
bodyB = this.bodyB,
motor = this.motorEquation,
r1 = this.rotationalEquation1,
r2 = this.rotationalEquation2,
r3 = this.rotationalEquation3,
worldAxisA = LockConstraint_update_tmpVec1,
worldAxisB = LockConstraint_update_tmpVec2;
PointToPointConstraint.prototype.update.call(this);
// These vector pairs must be orthogonal
bodyA.vectorToWorldFrame(this.xA, r1.axisA);
bodyB.vectorToWorldFrame(this.yB, r1.axisB);
bodyA.vectorToWorldFrame(this.yA, r2.axisA);
bodyB.vectorToWorldFrame(this.zB, r2.axisB);
bodyA.vectorToWorldFrame(this.zA, r3.axisA);
bodyB.vectorToWorldFrame(this.xB, r3.axisB);
};
},{"../equations/ContactEquation":63,"../equations/RotationalEquation":66,"../equations/RotationalMotorEquation":67,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],61:[function(require,module,exports){
module.exports = PointToPointConstraint;
var Constraint = require('./Constraint');
var ContactEquation = require('../equations/ContactEquation');
var Vec3 = require('../math/Vec3');
/**
* Connects two bodies at given offset points.
* @class PointToPointConstraint
* @extends Constraint
* @constructor
* @param {Body} bodyA
* @param {Vec3} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
* @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
* @param {Vec3} pivotB See pivotA.
* @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
*
* @example
* var bodyA = new Body({ mass: 1 });
* var bodyB = new Body({ mass: 1 });
* bodyA.position.set(-1, 0, 0);
* bodyB.position.set(1, 0, 0);
* bodyA.addShape(shapeA);
* bodyB.addShape(shapeB);
* world.addBody(bodyA);
* world.addBody(bodyB);
* var localPivotA = new Vec3(1, 0, 0);
* var localPivotB = new Vec3(-1, 0, 0);
* var constraint = new PointToPointConstraint(bodyA, localPivotA, bodyB, localPivotB);
* world.addConstraint(constraint);
*/
function PointToPointConstraint(bodyA,pivotA,bodyB,pivotB,maxForce){
Constraint.call(this,bodyA,bodyB);
maxForce = typeof(maxForce) !== 'undefined' ? maxForce : 1e6;
/**
* Pivot, defined locally in bodyA.
* @property {Vec3} pivotA
*/
this.pivotA = pivotA ? pivotA.clone() : new Vec3();
/**
* Pivot, defined locally in bodyB.
* @property {Vec3} pivotB
*/
this.pivotB = pivotB ? pivotB.clone() : new Vec3();
/**
* @property {ContactEquation} equationX
*/
var x = this.equationX = new ContactEquation(bodyA,bodyB);
/**
* @property {ContactEquation} equationY
*/
var y = this.equationY = new ContactEquation(bodyA,bodyB);
/**
* @property {ContactEquation} equationZ
*/
var z = this.equationZ = new ContactEquation(bodyA,bodyB);
// Equations to be fed to the solver
this.equations.push(x, y, z);
// Make the equations bidirectional
x.minForce = y.minForce = z.minForce = -maxForce;
x.maxForce = y.maxForce = z.maxForce = maxForce;
x.ni.set(1, 0, 0);
y.ni.set(0, 1, 0);
z.ni.set(0, 0, 1);
}
PointToPointConstraint.prototype = new Constraint();
PointToPointConstraint.prototype.update = function(){
var bodyA = this.bodyA;
var bodyB = this.bodyB;
var x = this.equationX;
var y = this.equationY;
var z = this.equationZ;
// Rotate the pivots to world space
bodyA.quaternion.vmult(this.pivotA,x.ri);
bodyB.quaternion.vmult(this.pivotB,x.rj);
y.ri.copy(x.ri);
y.rj.copy(x.rj);
z.ri.copy(x.ri);
z.rj.copy(x.rj);
};
},{"../equations/ContactEquation":63,"../math/Vec3":74,"./Constraint":57}],62:[function(require,module,exports){
module.exports = ConeEquation;
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
var Equation = require('./Equation');
/**
* Cone equation. Works to keep the given body world vectors aligned, or tilted within a given angle from each other.
* @class ConeEquation
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Vec3} [options.axisA] Local axis in A
* @param {Vec3} [options.axisB] Local axis in B
* @param {Vec3} [options.angle] The "cone angle" to keep
* @param {number} [options.maxForce=1e6]
* @extends Equation
*/
function ConeEquation(bodyA, bodyB, options){
options = options || {};
var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
Equation.call(this,bodyA,bodyB,-maxForce, maxForce);
this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0);
this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0);
/**
* The cone angle to keep
* @property {number} angle
*/
this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0;
}
ConeEquation.prototype = new Equation();
ConeEquation.prototype.constructor = ConeEquation;
var tmpVec1 = new Vec3();
var tmpVec2 = new Vec3();
ConeEquation.prototype.computeB = function(h){
var a = this.a,
b = this.b,
ni = this.axisA,
nj = this.axisB,
nixnj = tmpVec1,
njxni = tmpVec2,
GA = this.jacobianElementA,
GB = this.jacobianElementB;
// Caluclate cross products
ni.cross(nj, nixnj);
nj.cross(ni, njxni);
// The angle between two vector is:
// cos(theta) = a * b / (length(a) * length(b) = { len(a) = len(b) = 1 } = a * b
// g = a * b
// gdot = (b x a) * wi + (a x b) * wj
// G = [0 bxa 0 axb]
// W = [vi wi vj wj]
GA.rotational.copy(njxni);
GB.rotational.copy(nixnj);
var g = Math.cos(this.angle) - ni.dot(nj),
GW = this.computeGW(),
GiMf = this.computeGiMf();
var B = - g * a - GW * b - h * GiMf;
return B;
};
},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],63:[function(require,module,exports){
module.exports = ContactEquation;
var Equation = require('./Equation');
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
/**
* Contact/non-penetration constraint equation
* @class ContactEquation
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @extends Equation
*/
function ContactEquation(bodyA, bodyB, maxForce){
maxForce = typeof(maxForce) !== 'undefined' ? maxForce : 1e6;
Equation.call(this, bodyA, bodyB, 0, maxForce);
/**
* @property restitution
* @type {Number}
*/
this.restitution = 0.0; // "bounciness": u1 = -e*u0
/**
* World-oriented vector that goes from the center of bi to the contact point.
* @property {Vec3} ri
*/
this.ri = new Vec3();
/**
* World-oriented vector that starts in body j position and goes to the contact point.
* @property {Vec3} rj
*/
this.rj = new Vec3();
/**
* Contact normal, pointing out of body i.
* @property {Vec3} ni
*/
this.ni = new Vec3();
}
ContactEquation.prototype = new Equation();
ContactEquation.prototype.constructor = ContactEquation;
var ContactEquation_computeB_temp1 = new Vec3(); // Temp vectors
var ContactEquation_computeB_temp2 = new Vec3();
var ContactEquation_computeB_temp3 = new Vec3();
ContactEquation.prototype.computeB = function(h){
var a = this.a,
b = this.b,
bi = this.bi,
bj = this.bj,
ri = this.ri,
rj = this.rj,
rixn = ContactEquation_computeB_temp1,
rjxn = ContactEquation_computeB_temp2,
vi = bi.velocity,
wi = bi.angularVelocity,
fi = bi.force,
taui = bi.torque,
vj = bj.velocity,
wj = bj.angularVelocity,
fj = bj.force,
tauj = bj.torque,
penetrationVec = ContactEquation_computeB_temp3,
GA = this.jacobianElementA,
GB = this.jacobianElementB,
n = this.ni;
// Caluclate cross products
ri.cross(n,rixn);
rj.cross(n,rjxn);
// g = xj+rj -(xi+ri)
// G = [ -ni -rixn ni rjxn ]
n.negate(GA.spatial);
rixn.negate(GA.rotational);
GB.spatial.copy(n);
GB.rotational.copy(rjxn);
// Calculate the penetration vector
penetrationVec.copy(bj.position);
penetrationVec.vadd(rj,penetrationVec);
penetrationVec.vsub(bi.position,penetrationVec);
penetrationVec.vsub(ri,penetrationVec);
var g = n.dot(penetrationVec);
// Compute iteration
var ePlusOne = this.restitution + 1;
var GW = ePlusOne * vj.dot(n) - ePlusOne * vi.dot(n) + wj.dot(rjxn) - wi.dot(rixn);
var GiMf = this.computeGiMf();
var B = - g * a - GW * b - h*GiMf;
return B;
};
var ContactEquation_getImpactVelocityAlongNormal_vi = new Vec3();
var ContactEquation_getImpactVelocityAlongNormal_vj = new Vec3();
var ContactEquation_getImpactVelocityAlongNormal_xi = new Vec3();
var ContactEquation_getImpactVelocityAlongNormal_xj = new Vec3();
var ContactEquation_getImpactVelocityAlongNormal_relVel = new Vec3();
/**
* Get the current relative velocity in the contact point.
* @method getImpactVelocityAlongNormal
* @return {number}
*/
ContactEquation.prototype.getImpactVelocityAlongNormal = function(){
var vi = ContactEquation_getImpactVelocityAlongNormal_vi;
var vj = ContactEquation_getImpactVelocityAlongNormal_vj;
var xi = ContactEquation_getImpactVelocityAlongNormal_xi;
var xj = ContactEquation_getImpactVelocityAlongNormal_xj;
var relVel = ContactEquation_getImpactVelocityAlongNormal_relVel;
this.bi.position.vadd(this.ri, xi);
this.bj.position.vadd(this.rj, xj);
this.bi.getVelocityAtWorldPoint(xi, vi);
this.bj.getVelocityAtWorldPoint(xj, vj);
vi.vsub(vj, relVel);
return this.ni.dot(relVel);
};
},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],64:[function(require,module,exports){
module.exports = Equation;
var JacobianElement = require('../math/JacobianElement'),
Vec3 = require('../math/Vec3');
/**
* Equation base class
* @class Equation
* @constructor
* @author schteppe
* @param {Body} bi
* @param {Body} bj
* @param {Number} minForce Minimum (read: negative max) force to be applied by the constraint.
* @param {Number} maxForce Maximum (read: positive max) force to be applied by the constraint.
*/
function Equation(bi,bj,minForce,maxForce){
this.id = Equation.id++;
/**
* @property {number} minForce
*/
this.minForce = typeof(minForce)==="undefined" ? -1e6 : minForce;
/**
* @property {number} maxForce
*/
this.maxForce = typeof(maxForce)==="undefined" ? 1e6 : maxForce;
/**
* @property bi
* @type {Body}
*/
this.bi = bi;
/**
* @property bj
* @type {Body}
*/
this.bj = bj;
/**
* SPOOK parameter
* @property {number} a
*/
this.a = 0.0;
/**
* SPOOK parameter
* @property {number} b
*/
this.b = 0.0;
/**
* SPOOK parameter
* @property {number} eps
*/
this.eps = 0.0;
/**
* @property {JacobianElement} jacobianElementA
*/
this.jacobianElementA = new JacobianElement();
/**
* @property {JacobianElement} jacobianElementB
*/
this.jacobianElementB = new JacobianElement();
/**
* @property {boolean} enabled
* @default true
*/
this.enabled = true;
/**
* A number, proportional to the force added to the bodies.
* @property {number} multiplier
* @readonly
*/
this.multiplier = 0;
// Set typical spook params
this.setSpookParams(1e7,4,1/60);
}
Equation.prototype.constructor = Equation;
Equation.id = 0;
/**
* Recalculates a,b,eps.
* @method setSpookParams
*/
Equation.prototype.setSpookParams = function(stiffness,relaxation,timeStep){
var d = relaxation,
k = stiffness,
h = timeStep;
this.a = 4.0 / (h * (1 + 4 * d));
this.b = (4.0 * d) / (1 + 4 * d);
this.eps = 4.0 / (h * h * k * (1 + 4 * d));
};
/**
* Computes the RHS of the SPOOK equation
* @method computeB
* @return {Number}
*/
Equation.prototype.computeB = function(a,b,h){
var GW = this.computeGW(),
Gq = this.computeGq(),
GiMf = this.computeGiMf();
return - Gq * a - GW * b - GiMf*h;
};
/**
* Computes G*q, where q are the generalized body coordinates
* @method computeGq
* @return {Number}
*/
Equation.prototype.computeGq = function(){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
xi = bi.position,
xj = bj.position;
return GA.spatial.dot(xi) + GB.spatial.dot(xj);
};
var zero = new Vec3();
/**
* Computes G*W, where W are the body velocities
* @method computeGW
* @return {Number}
*/
Equation.prototype.computeGW = function(){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
vi = bi.velocity,
vj = bj.velocity,
wi = bi.angularVelocity,
wj = bj.angularVelocity;
return GA.multiplyVectors(vi,wi) + GB.multiplyVectors(vj,wj);
};
/**
* Computes G*Wlambda, where W are the body velocities
* @method computeGWlambda
* @return {Number}
*/
Equation.prototype.computeGWlambda = function(){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
vi = bi.vlambda,
vj = bj.vlambda,
wi = bi.wlambda,
wj = bj.wlambda;
return GA.multiplyVectors(vi,wi) + GB.multiplyVectors(vj,wj);
};
/**
* Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.
* @method computeGiMf
* @return {Number}
*/
var iMfi = new Vec3(),
iMfj = new Vec3(),
invIi_vmult_taui = new Vec3(),
invIj_vmult_tauj = new Vec3();
Equation.prototype.computeGiMf = function(){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
fi = bi.force,
ti = bi.torque,
fj = bj.force,
tj = bj.torque,
invMassi = bi.invMassSolve,
invMassj = bj.invMassSolve;
fi.scale(invMassi,iMfi);
fj.scale(invMassj,iMfj);
bi.invInertiaWorldSolve.vmult(ti,invIi_vmult_taui);
bj.invInertiaWorldSolve.vmult(tj,invIj_vmult_tauj);
return GA.multiplyVectors(iMfi,invIi_vmult_taui) + GB.multiplyVectors(iMfj,invIj_vmult_tauj);
};
/**
* Computes G*inv(M)*G'
* @method computeGiMGt
* @return {Number}
*/
var tmp = new Vec3();
Equation.prototype.computeGiMGt = function(){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
invMassi = bi.invMassSolve,
invMassj = bj.invMassSolve,
invIi = bi.invInertiaWorldSolve,
invIj = bj.invInertiaWorldSolve,
result = invMassi + invMassj;
invIi.vmult(GA.rotational,tmp);
result += tmp.dot(GA.rotational);
invIj.vmult(GB.rotational,tmp);
result += tmp.dot(GB.rotational);
return result;
};
var addToWlambda_temp = new Vec3(),
addToWlambda_Gi = new Vec3(),
addToWlambda_Gj = new Vec3(),
addToWlambda_ri = new Vec3(),
addToWlambda_rj = new Vec3(),
addToWlambda_Mdiag = new Vec3();
/**
* Add constraint velocity to the bodies.
* @method addToWlambda
* @param {Number} deltalambda
*/
Equation.prototype.addToWlambda = function(deltalambda){
var GA = this.jacobianElementA,
GB = this.jacobianElementB,
bi = this.bi,
bj = this.bj,
temp = addToWlambda_temp;
// Add to linear velocity
// v_lambda += inv(M) * delta_lamba * G
bi.vlambda.addScaledVector(bi.invMassSolve * deltalambda, GA.spatial, bi.vlambda);
bj.vlambda.addScaledVector(bj.invMassSolve * deltalambda, GB.spatial, bj.vlambda);
// Add to angular velocity
bi.invInertiaWorldSolve.vmult(GA.rotational,temp);
bi.wlambda.addScaledVector(deltalambda, temp, bi.wlambda);
bj.invInertiaWorldSolve.vmult(GB.rotational,temp);
bj.wlambda.addScaledVector(deltalambda, temp, bj.wlambda);
};
/**
* Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps
* @method computeInvC
* @param {Number} eps
* @return {Number}
*/
Equation.prototype.computeC = function(){
return this.computeGiMGt() + this.eps;
};
},{"../math/JacobianElement":70,"../math/Vec3":74}],65:[function(require,module,exports){
module.exports = FrictionEquation;
var Equation = require('./Equation');
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
/**
* Constrains the slipping in a contact along a tangent
* @class FrictionEquation
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Number} slipForce should be +-F_friction = +-mu * F_normal = +-mu * m * g
* @extends Equation
*/
function FrictionEquation(bodyA, bodyB, slipForce){
Equation.call(this,bodyA, bodyB, -slipForce, slipForce);
this.ri = new Vec3();
this.rj = new Vec3();
this.t = new Vec3(); // tangent
}
FrictionEquation.prototype = new Equation();
FrictionEquation.prototype.constructor = FrictionEquation;
var FrictionEquation_computeB_temp1 = new Vec3();
var FrictionEquation_computeB_temp2 = new Vec3();
FrictionEquation.prototype.computeB = function(h){
var a = this.a,
b = this.b,
bi = this.bi,
bj = this.bj,
ri = this.ri,
rj = this.rj,
rixt = FrictionEquation_computeB_temp1,
rjxt = FrictionEquation_computeB_temp2,
t = this.t;
// Caluclate cross products
ri.cross(t,rixt);
rj.cross(t,rjxt);
// G = [-t -rixt t rjxt]
// And remember, this is a pure velocity constraint, g is always zero!
var GA = this.jacobianElementA,
GB = this.jacobianElementB;
t.negate(GA.spatial);
rixt.negate(GA.rotational);
GB.spatial.copy(t);
GB.rotational.copy(rjxt);
var GW = this.computeGW();
var GiMf = this.computeGiMf();
var B = - GW * b - h * GiMf;
return B;
};
},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],66:[function(require,module,exports){
module.exports = RotationalEquation;
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
var Equation = require('./Equation');
/**
* Rotational constraint. Works to keep the local vectors orthogonal to each other in world space.
* @class RotationalEquation
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Vec3} [options.axisA]
* @param {Vec3} [options.axisB]
* @param {number} [options.maxForce]
* @extends Equation
*/
function RotationalEquation(bodyA, bodyB, options){
options = options || {};
var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
Equation.call(this,bodyA,bodyB,-maxForce, maxForce);
this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0);
this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0);
this.maxAngle = Math.PI / 2;
}
RotationalEquation.prototype = new Equation();
RotationalEquation.prototype.constructor = RotationalEquation;
var tmpVec1 = new Vec3();
var tmpVec2 = new Vec3();
RotationalEquation.prototype.computeB = function(h){
var a = this.a,
b = this.b,
ni = this.axisA,
nj = this.axisB,
nixnj = tmpVec1,
njxni = tmpVec2,
GA = this.jacobianElementA,
GB = this.jacobianElementB;
// Caluclate cross products
ni.cross(nj, nixnj);
nj.cross(ni, njxni);
// g = ni * nj
// gdot = (nj x ni) * wi + (ni x nj) * wj
// G = [0 njxni 0 nixnj]
// W = [vi wi vj wj]
GA.rotational.copy(njxni);
GB.rotational.copy(nixnj);
var g = Math.cos(this.maxAngle) - ni.dot(nj),
GW = this.computeGW(),
GiMf = this.computeGiMf();
var B = - g * a - GW * b - h * GiMf;
return B;
};
},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],67:[function(require,module,exports){
module.exports = RotationalMotorEquation;
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
var Equation = require('./Equation');
/**
* Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value.
* @class RotationalMotorEquation
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Number} maxForce
* @extends Equation
*/
function RotationalMotorEquation(bodyA, bodyB, maxForce){
maxForce = typeof(maxForce)!=='undefined' ? maxForce : 1e6;
Equation.call(this,bodyA,bodyB,-maxForce,maxForce);
/**
* World oriented rotational axis
* @property {Vec3} axisA
*/
this.axisA = new Vec3();
/**
* World oriented rotational axis
* @property {Vec3} axisB
*/
this.axisB = new Vec3(); // World oriented rotational axis
/**
* Motor velocity
* @property {Number} targetVelocity
*/
this.targetVelocity = 0;
}
RotationalMotorEquation.prototype = new Equation();
RotationalMotorEquation.prototype.constructor = RotationalMotorEquation;
RotationalMotorEquation.prototype.computeB = function(h){
var a = this.a,
b = this.b,
bi = this.bi,
bj = this.bj,
axisA = this.axisA,
axisB = this.axisB,
GA = this.jacobianElementA,
GB = this.jacobianElementB;
// g = 0
// gdot = axisA * wi - axisB * wj
// gdot = G * W = G * [vi wi vj wj]
// =>
// G = [0 axisA 0 -axisB]
GA.rotational.copy(axisA);
axisB.negate(GB.rotational);
var GW = this.computeGW() - this.targetVelocity,
GiMf = this.computeGiMf();
var B = - GW * b - h * GiMf;
return B;
};
},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],68:[function(require,module,exports){
var Utils = require('../utils/Utils');
module.exports = ContactMaterial;
/**
* Defines what happens when two materials meet.
* @class ContactMaterial
* @constructor
* @param {Material} m1
* @param {Material} m2
* @param {object} [options]
* @param {Number} [options.friction=0.3]
* @param {Number} [options.restitution=0.3]
* @param {number} [options.contactEquationStiffness=1e7]
* @param {number} [options.contactEquationRelaxation=3]
* @param {number} [options.frictionEquationStiffness=1e7]
* @param {Number} [options.frictionEquationRelaxation=3]
*/
function ContactMaterial(m1, m2, options){
options = Utils.defaults(options, {
friction: 0.3,
restitution: 0.3,
contactEquationStiffness: 1e7,
contactEquationRelaxation: 3,
frictionEquationStiffness: 1e7,
frictionEquationRelaxation: 3
});
/**
* Identifier of this material
* @property {Number} id
*/
this.id = ContactMaterial.idCounter++;
/**
* Participating materials
* @property {Array} materials
* @todo Should be .materialA and .materialB instead
*/
this.materials = [m1, m2];
/**
* Friction coefficient
* @property {Number} friction
*/
this.friction = options.friction;
/**
* Restitution coefficient
* @property {Number} restitution
*/
this.restitution = options.restitution;
/**
* Stiffness of the produced contact equations
* @property {Number} contactEquationStiffness
*/
this.contactEquationStiffness = options.contactEquationStiffness;
/**
* Relaxation time of the produced contact equations
* @property {Number} contactEquationRelaxation
*/
this.contactEquationRelaxation = options.contactEquationRelaxation;
/**
* Stiffness of the produced friction equations
* @property {Number} frictionEquationStiffness
*/
this.frictionEquationStiffness = options.frictionEquationStiffness;
/**
* Relaxation time of the produced friction equations
* @property {Number} frictionEquationRelaxation
*/
this.frictionEquationRelaxation = options.frictionEquationRelaxation;
}
ContactMaterial.idCounter = 0;
},{"../utils/Utils":97}],69:[function(require,module,exports){
module.exports = Material;
/**
* Defines a physics material.
* @class Material
* @constructor
* @param {object} [options]
* @author schteppe
*/
function Material(options){
var name = '';
options = options || {};
// Backwards compatibility fix
if(typeof(options) === 'string'){
name = options;
options = {};
} else if(typeof(options) === 'object') {
name = '';
}
/**
* @property name
* @type {String}
*/
this.name = name;
/**
* material id.
* @property id
* @type {number}
*/
this.id = Material.idCounter++;
/**
* Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.
* @property {number} friction
*/
this.friction = typeof(options.friction) !== 'undefined' ? options.friction : -1;
/**
* Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.
* @property {number} restitution
*/
this.restitution = typeof(options.restitution) !== 'undefined' ? options.restitution : -1;
}
Material.idCounter = 0;
},{}],70:[function(require,module,exports){
module.exports = JacobianElement;
var Vec3 = require('./Vec3');
/**
* An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom.
* @class JacobianElement
* @constructor
*/
function JacobianElement(){
/**
* @property {Vec3} spatial
*/
this.spatial = new Vec3();
/**
* @property {Vec3} rotational
*/
this.rotational = new Vec3();
}
/**
* Multiply with other JacobianElement
* @method multiplyElement
* @param {JacobianElement} element
* @return {Number}
*/
JacobianElement.prototype.multiplyElement = function(element){
return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational);
};
/**
* Multiply with two vectors
* @method multiplyVectors
* @param {Vec3} spatial
* @param {Vec3} rotational
* @return {Number}
*/
JacobianElement.prototype.multiplyVectors = function(spatial,rotational){
return spatial.dot(this.spatial) + rotational.dot(this.rotational);
};
},{"./Vec3":74}],71:[function(require,module,exports){
module.exports = Mat3;
var Vec3 = require('./Vec3');
/**
* A 3x3 matrix.
* @class Mat3
* @constructor
* @param array elements Array of nine elements. Optional.
* @author schteppe / http://github.com/schteppe
*/
function Mat3(elements){
/**
* A vector of length 9, containing all matrix elements
* @property {Array} elements
*/
if(elements){
this.elements = elements;
} else {
this.elements = [0,0,0,0,0,0,0,0,0];
}
}
/**
* Sets the matrix to identity
* @method identity
* @todo Should perhaps be renamed to setIdentity() to be more clear.
* @todo Create another function that immediately creates an identity matrix eg. eye()
*/
Mat3.prototype.identity = function(){
var e = this.elements;
e[0] = 1;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 1;
e[5] = 0;
e[6] = 0;
e[7] = 0;
e[8] = 1;
};
/**
* Set all elements to zero
* @method setZero
*/
Mat3.prototype.setZero = function(){
var e = this.elements;
e[0] = 0;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = 0;
e[6] = 0;
e[7] = 0;
e[8] = 0;
};
/**
* Sets the matrix diagonal elements from a Vec3
* @method setTrace
* @param {Vec3} vec3
*/
Mat3.prototype.setTrace = function(vec3){
var e = this.elements;
e[0] = vec3.x;
e[4] = vec3.y;
e[8] = vec3.z;
};
/**
* Gets the matrix diagonal elements
* @method getTrace
* @return {Vec3}
*/
Mat3.prototype.getTrace = function(target){
var target = target || new Vec3();
var e = this.elements;
target.x = e[0];
target.y = e[4];
target.z = e[8];
};
/**
* Matrix-Vector multiplication
* @method vmult
* @param {Vec3} v The vector to multiply with
* @param {Vec3} target Optional, target to save the result in.
*/
Mat3.prototype.vmult = function(v,target){
target = target || new Vec3();
var e = this.elements,
x = v.x,
y = v.y,
z = v.z;
target.x = e[0]*x + e[1]*y + e[2]*z;
target.y = e[3]*x + e[4]*y + e[5]*z;
target.z = e[6]*x + e[7]*y + e[8]*z;
return target;
};
/**
* Matrix-scalar multiplication
* @method smult
* @param {Number} s
*/
Mat3.prototype.smult = function(s){
for(var i=0; i<this.elements.length; i++){
this.elements[i] *= s;
}
};
/**
* Matrix multiplication
* @method mmult
* @param {Mat3} m Matrix to multiply with from left side.
* @return {Mat3} The result.
*/
Mat3.prototype.mmult = function(m,target){
var r = target || new Mat3();
for(var i=0; i<3; i++){
for(var j=0; j<3; j++){
var sum = 0.0;
for(var k=0; k<3; k++){
sum += m.elements[i+k*3] * this.elements[k+j*3];
}
r.elements[i+j*3] = sum;
}
}
return r;
};
/**
* Scale each column of the matrix
* @method scale
* @param {Vec3} v
* @return {Mat3} The result.
*/
Mat3.prototype.scale = function(v,target){
target = target || new Mat3();
var e = this.elements,
t = target.elements;
for(var i=0; i!==3; i++){
t[3*i + 0] = v.x * e[3*i + 0];
t[3*i + 1] = v.y * e[3*i + 1];
t[3*i + 2] = v.z * e[3*i + 2];
}
return target;
};
/**
* Solve Ax=b
* @method solve
* @param {Vec3} b The right hand side
* @param {Vec3} target Optional. Target vector to save in.
* @return {Vec3} The solution x
* @todo should reuse arrays
*/
Mat3.prototype.solve = function(b,target){
target = target || new Vec3();
// Construct equations
var nr = 3; // num rows
var nc = 4; // num cols
var eqns = [];
for(var i=0; i<nr*nc; i++){
eqns.push(0);
}
var i,j;
for(i=0; i<3; i++){
for(j=0; j<3; j++){
eqns[i+nc*j] = this.elements[i+3*j];
}
}
eqns[3+4*0] = b.x;
eqns[3+4*1] = b.y;
eqns[3+4*2] = b.z;
// Compute right upper triangular version of the matrix - Gauss elimination
var n = 3, k = n, np;
var kp = 4; // num rows
var p, els;
do {
i = k - n;
if (eqns[i+nc*i] === 0) {
// the pivot is null, swap lines
for (j = i + 1; j < k; j++) {
if (eqns[i+nc*j] !== 0) {
np = kp;
do { // do ligne( i ) = ligne( i ) + ligne( k )
p = kp - np;
eqns[p+nc*i] += eqns[p+nc*j];
} while (--np);
break;
}
}
}
if (eqns[i+nc*i] !== 0) {
for (j = i + 1; j < k; j++) {
var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
np = kp;
do { // do ligne( k ) = ligne( k ) - multiplier * ligne( i )
p = kp - np;
eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
} while (--np);
}
}
} while (--n);
// Get the solution
target.z = eqns[2*nc+3] / eqns[2*nc+2];
target.y = (eqns[1*nc+3] - eqns[1*nc+2]*target.z) / eqns[1*nc+1];
target.x = (eqns[0*nc+3] - eqns[0*nc+2]*target.z - eqns[0*nc+1]*target.y) / eqns[0*nc+0];
if(isNaN(target.x) || isNaN(target.y) || isNaN(target.z) || target.x===Infinity || target.y===Infinity || target.z===Infinity){
throw "Could not solve equation! Got x=["+target.toString()+"], b=["+b.toString()+"], A=["+this.toString()+"]";
}
return target;
};
/**
* Get an element in the matrix by index. Index starts at 0, not 1!!!
* @method e
* @param {Number} row
* @param {Number} column
* @param {Number} value Optional. If provided, the matrix element will be set to this value.
* @return {Number}
*/
Mat3.prototype.e = function( row , column ,value){
if(value===undefined){
return this.elements[column+3*row];
} else {
// Set value
this.elements[column+3*row] = value;
}
};
/**
* Copy another matrix into this matrix object.
* @method copy
* @param {Mat3} source
* @return {Mat3} this
*/
Mat3.prototype.copy = function(source){
for(var i=0; i < source.elements.length; i++){
this.elements[i] = source.elements[i];
}
return this;
};
/**
* Returns a string representation of the matrix.
* @method toString
* @return string
*/
Mat3.prototype.toString = function(){
var r = "";
var sep = ",";
for(var i=0; i<9; i++){
r += this.elements[i] + sep;
}
return r;
};
/**
* reverse the matrix
* @method reverse
* @param {Mat3} target Optional. Target matrix to save in.
* @return {Mat3} The solution x
*/
Mat3.prototype.reverse = function(target){
target = target || new Mat3();
// Construct equations
var nr = 3; // num rows
var nc = 6; // num cols
var eqns = [];
for(var i=0; i<nr*nc; i++){
eqns.push(0);
}
var i,j;
for(i=0; i<3; i++){
for(j=0; j<3; j++){
eqns[i+nc*j] = this.elements[i+3*j];
}
}
eqns[3+6*0] = 1;
eqns[3+6*1] = 0;
eqns[3+6*2] = 0;
eqns[4+6*0] = 0;
eqns[4+6*1] = 1;
eqns[4+6*2] = 0;
eqns[5+6*0] = 0;
eqns[5+6*1] = 0;
eqns[5+6*2] = 1;
// Compute right upper triangular version of the matrix - Gauss elimination
var n = 3, k = n, np;
var kp = nc; // num rows
var p;
do {
i = k - n;
if (eqns[i+nc*i] === 0) {
// the pivot is null, swap lines
for (j = i + 1; j < k; j++) {
if (eqns[i+nc*j] !== 0) {
np = kp;
do { // do line( i ) = line( i ) + line( k )
p = kp - np;
eqns[p+nc*i] += eqns[p+nc*j];
} while (--np);
break;
}
}
}
if (eqns[i+nc*i] !== 0) {
for (j = i + 1; j < k; j++) {
var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
np = kp;
do { // do line( k ) = line( k ) - multiplier * line( i )
p = kp - np;
eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
} while (--np);
}
}
} while (--n);
// eliminate the upper left triangle of the matrix
i = 2;
do {
j = i-1;
do {
var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
np = nc;
do {
p = nc - np;
eqns[p+nc*j] = eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
} while (--np);
} while (j--);
} while (--i);
// operations on the diagonal
i = 2;
do {
var multiplier = 1 / eqns[i+nc*i];
np = nc;
do {
p = nc - np;
eqns[p+nc*i] = eqns[p+nc*i] * multiplier ;
} while (--np);
} while (i--);
i = 2;
do {
j = 2;
do {
p = eqns[nr+j+nc*i];
if( isNaN( p ) || p ===Infinity ){
throw "Could not reverse! A=["+this.toString()+"]";
}
target.e( i , j , p );
} while (j--);
} while (i--);
return target;
};
/**
* Set the matrix from a quaterion
* @method setRotationFromQuaternion
* @param {Quaternion} q
*/
Mat3.prototype.setRotationFromQuaternion = function( q ) {
var x = q.x, y = q.y, z = q.z, w = q.w,
x2 = x + x, y2 = y + y, z2 = z + z,
xx = x * x2, xy = x * y2, xz = x * z2,
yy = y * y2, yz = y * z2, zz = z * z2,
wx = w * x2, wy = w * y2, wz = w * z2,
e = this.elements;
e[3*0 + 0] = 1 - ( yy + zz );
e[3*0 + 1] = xy - wz;
e[3*0 + 2] = xz + wy;
e[3*1 + 0] = xy + wz;
e[3*1 + 1] = 1 - ( xx + zz );
e[3*1 + 2] = yz - wx;
e[3*2 + 0] = xz - wy;
e[3*2 + 1] = yz + wx;
e[3*2 + 2] = 1 - ( xx + yy );
return this;
};
/**
* Transpose the matrix
* @method transpose
* @param {Mat3} target Where to store the result.
* @return {Mat3} The target Mat3, or a new Mat3 if target was omitted.
*/
Mat3.prototype.transpose = function( target ) {
target = target || new Mat3();
var Mt = target.elements,
M = this.elements;
for(var i=0; i!==3; i++){
for(var j=0; j!==3; j++){
Mt[3*i + j] = M[3*j + i];
}
}
return target;
};
},{"./Vec3":74}],72:[function(require,module,exports){
module.exports = Quaternion;
var Vec3 = require('./Vec3');
/**
* A Quaternion describes a rotation in 3D space. The Quaternion is mathematically defined as Q = x*i + y*j + z*k + w, where (i,j,k) are imaginary basis vectors. (x,y,z) can be seen as a vector related to the axis of rotation, while the real multiplier, w, is related to the amount of rotation.
* @class Quaternion
* @constructor
* @param {Number} x Multiplier of the imaginary basis vector i.
* @param {Number} y Multiplier of the imaginary basis vector j.
* @param {Number} z Multiplier of the imaginary basis vector k.
* @param {Number} w Multiplier of the real part.
* @see http://en.wikipedia.org/wiki/Quaternion
*/
function Quaternion(x,y,z,w){
/**
* @property {Number} x
*/
this.x = x!==undefined ? x : 0;
/**
* @property {Number} y
*/
this.y = y!==undefined ? y : 0;
/**
* @property {Number} z
*/
this.z = z!==undefined ? z : 0;
/**
* The multiplier of the real quaternion basis vector.
* @property {Number} w
*/
this.w = w!==undefined ? w : 1;
}
/**
* Set the value of the quaternion.
* @method set
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @param {Number} w
*/
Quaternion.prototype.set = function(x,y,z,w){
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
};
/**
* Convert to a readable format
* @method toString
* @return string
*/
Quaternion.prototype.toString = function(){
return this.x+","+this.y+","+this.z+","+this.w;
};
/**
* Convert to an Array
* @method toArray
* @return Array
*/
Quaternion.prototype.toArray = function(){
return [this.x, this.y, this.z, this.w];
};
/**
* Set the quaternion components given an axis and an angle.
* @method setFromAxisAngle
* @param {Vec3} axis
* @param {Number} angle in radians
*/
Quaternion.prototype.setFromAxisAngle = function(axis,angle){
var s = Math.sin(angle*0.5);
this.x = axis.x * s;
this.y = axis.y * s;
this.z = axis.z * s;
this.w = Math.cos(angle*0.5);
return this;
};
/**
* Converts the quaternion to axis/angle representation.
* @method toAxisAngle
* @param {Vec3} [targetAxis] A vector object to reuse for storing the axis.
* @return {Array} An array, first elemnt is the axis and the second is the angle in radians.
*/
Quaternion.prototype.toAxisAngle = function(targetAxis){
targetAxis = targetAxis || new Vec3();
this.normalize(); // if w>1 acos and sqrt will produce errors, this cant happen if quaternion is normalised
var angle = 2 * Math.acos(this.w);
var s = Math.sqrt(1-this.w*this.w); // assuming quaternion normalised then w is less than 1, so term always positive.
if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
// if s close to zero then direction of axis not important
targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0;
targetAxis.y = this.y;
targetAxis.z = this.z;
} else {
targetAxis.x = this.x / s; // normalise axis
targetAxis.y = this.y / s;
targetAxis.z = this.z / s;
}
return [targetAxis,angle];
};
var sfv_t1 = new Vec3(),
sfv_t2 = new Vec3();
/**
* Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v.
* @method setFromVectors
* @param {Vec3} u
* @param {Vec3} v
*/
Quaternion.prototype.setFromVectors = function(u,v){
if(u.isAntiparallelTo(v)){
var t1 = sfv_t1;
var t2 = sfv_t2;
u.tangents(t1,t2);
this.setFromAxisAngle(t1,Math.PI);
} else {
var a = u.cross(v);
this.x = a.x;
this.y = a.y;
this.z = a.z;
this.w = Math.sqrt(Math.pow(u.norm(),2) * Math.pow(v.norm(),2)) + u.dot(v);
this.normalize();
}
return this;
};
/**
* Quaternion multiplication
* @method mult
* @param {Quaternion} q
* @param {Quaternion} target Optional.
* @return {Quaternion}
*/
var Quaternion_mult_va = new Vec3();
var Quaternion_mult_vb = new Vec3();
var Quaternion_mult_vaxvb = new Vec3();
Quaternion.prototype.mult = function(q,target){
target = target || new Quaternion();
var ax = this.x, ay = this.y, az = this.z, aw = this.w,
bx = q.x, by = q.y, bz = q.z, bw = q.w;
target.x = ax * bw + aw * bx + ay * bz - az * by;
target.y = ay * bw + aw * by + az * bx - ax * bz;
target.z = az * bw + aw * bz + ax * by - ay * bx;
target.w = aw * bw - ax * bx - ay * by - az * bz;
return target;
};
/**
* Get the inverse quaternion rotation.
* @method inverse
* @param {Quaternion} target
* @return {Quaternion}
*/
Quaternion.prototype.inverse = function(target){
var x = this.x, y = this.y, z = this.z, w = this.w;
target = target || new Quaternion();
this.conjugate(target);
var inorm2 = 1/(x*x + y*y + z*z + w*w);
target.x *= inorm2;
target.y *= inorm2;
target.z *= inorm2;
target.w *= inorm2;
return target;
};
/**
* Get the quaternion conjugate
* @method conjugate
* @param {Quaternion} target
* @return {Quaternion}
*/
Quaternion.prototype.conjugate = function(target){
target = target || new Quaternion();
target.x = -this.x;
target.y = -this.y;
target.z = -this.z;
target.w = this.w;
return target;
};
/**
* Normalize the quaternion. Note that this changes the values of the quaternion.
* @method normalize
*/
Quaternion.prototype.normalize = function(){
var l = Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);
if ( l === 0 ) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 0;
} else {
l = 1 / l;
this.x *= l;
this.y *= l;
this.z *= l;
this.w *= l;
}
return this;
};
/**
* Approximation of quaternion normalization. Works best when quat is already almost-normalized.
* @method normalizeFast
* @see http://jsperf.com/fast-quaternion-normalization
* @author unphased, https://github.com/unphased
*/
Quaternion.prototype.normalizeFast = function () {
var f = (3.0-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2.0;
if ( f === 0 ) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 0;
} else {
this.x *= f;
this.y *= f;
this.z *= f;
this.w *= f;
}
return this;
};
/**
* Multiply the quaternion by a vector
* @method vmult
* @param {Vec3} v
* @param {Vec3} target Optional
* @return {Vec3}
*/
Quaternion.prototype.vmult = function(v,target){
target = target || new Vec3();
var x = v.x,
y = v.y,
z = v.z;
var qx = this.x,
qy = this.y,
qz = this.z,
qw = this.w;
// q*v
var ix = qw * x + qy * z - qz * y,
iy = qw * y + qz * x - qx * z,
iz = qw * z + qx * y - qy * x,
iw = -qx * x - qy * y - qz * z;
target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return target;
};
/**
* Copies value of source to this quaternion.
* @method copy
* @param {Quaternion} source
* @return {Quaternion} this
*/
Quaternion.prototype.copy = function(source){
this.x = source.x;
this.y = source.y;
this.z = source.z;
this.w = source.w;
return this;
};
/**
* Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm
* @method toEuler
* @param {Vec3} target
* @param string order Three-character string e.g. "YZX", which also is default.
*/
Quaternion.prototype.toEuler = function(target,order){
order = order || "YZX";
var heading, attitude, bank;
var x = this.x, y = this.y, z = this.z, w = this.w;
switch(order){
case "YZX":
var test = x*y + z*w;
if (test > 0.499) { // singularity at north pole
heading = 2 * Math.atan2(x,w);
attitude = Math.PI/2;
bank = 0;
}
if (test < -0.499) { // singularity at south pole
heading = -2 * Math.atan2(x,w);
attitude = - Math.PI/2;
bank = 0;
}
if(isNaN(heading)){
var sqx = x*x;
var sqy = y*y;
var sqz = z*z;
heading = Math.atan2(2*y*w - 2*x*z , 1 - 2*sqy - 2*sqz); // Heading
attitude = Math.asin(2*test); // attitude
bank = Math.atan2(2*x*w - 2*y*z , 1 - 2*sqx - 2*sqz); // bank
}
break;
default:
throw new Error("Euler order "+order+" not supported yet.");
}
target.y = heading;
target.z = attitude;
target.x = bank;
};
/**
* See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
* @method setFromEuler
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @param {String} order The order to apply angles: 'XYZ' or 'YXZ' or any other combination
*/
Quaternion.prototype.setFromEuler = function ( x, y, z, order ) {
order = order || "XYZ";
var c1 = Math.cos( x / 2 );
var c2 = Math.cos( y / 2 );
var c3 = Math.cos( z / 2 );
var s1 = Math.sin( x / 2 );
var s2 = Math.sin( y / 2 );
var s3 = Math.sin( z / 2 );
if ( order === 'XYZ' ) {
this.x = s1 * c2 * c3 + c1 * s2 * s3;
this.y = c1 * s2 * c3 - s1 * c2 * s3;
this.z = c1 * c2 * s3 + s1 * s2 * c3;
this.w = c1 * c2 * c3 - s1 * s2 * s3;
} else if ( order === 'YXZ' ) {
this.x = s1 * c2 * c3 + c1 * s2 * s3;
this.y = c1 * s2 * c3 - s1 * c2 * s3;
this.z = c1 * c2 * s3 - s1 * s2 * c3;
this.w = c1 * c2 * c3 + s1 * s2 * s3;
} else if ( order === 'ZXY' ) {
this.x = s1 * c2 * c3 - c1 * s2 * s3;
this.y = c1 * s2 * c3 + s1 * c2 * s3;
this.z = c1 * c2 * s3 + s1 * s2 * c3;
this.w = c1 * c2 * c3 - s1 * s2 * s3;
} else if ( order === 'ZYX' ) {
this.x = s1 * c2 * c3 - c1 * s2 * s3;
this.y = c1 * s2 * c3 + s1 * c2 * s3;
this.z = c1 * c2 * s3 - s1 * s2 * c3;
this.w = c1 * c2 * c3 + s1 * s2 * s3;
} else if ( order === 'YZX' ) {
this.x = s1 * c2 * c3 + c1 * s2 * s3;
this.y = c1 * s2 * c3 + s1 * c2 * s3;
this.z = c1 * c2 * s3 - s1 * s2 * c3;
this.w = c1 * c2 * c3 - s1 * s2 * s3;
} else if ( order === 'XZY' ) {
this.x = s1 * c2 * c3 - c1 * s2 * s3;
this.y = c1 * s2 * c3 - s1 * c2 * s3;
this.z = c1 * c2 * s3 + s1 * s2 * c3;
this.w = c1 * c2 * c3 + s1 * s2 * s3;
}
return this;
};
/**
* @method clone
* @return {Quaternion}
*/
Quaternion.prototype.clone = function(){
return new Quaternion(this.x, this.y, this.z, this.w);
};
/**
* Performs a spherical linear interpolation between two quat
*
* @method slerp
* @param {Quaternion} toQuat second operand
* @param {Number} t interpolation amount between the self quaternion and toQuat
* @param {Quaternion} [target] A quaternion to store the result in. If not provided, a new one will be created.
* @returns {Quaternion} The "target" object
*/
Quaternion.prototype.slerp = function (toQuat, t, target) {
target = target || new Quaternion();
var ax = this.x,
ay = this.y,
az = this.z,
aw = this.w,
bx = toQuat.x,
by = toQuat.y,
bz = toQuat.z,
bw = toQuat.w;
var omega, cosom, sinom, scale0, scale1;
// calc cosine
cosom = ax * bx + ay * by + az * bz + aw * bw;
// adjust signs (if necessary)
if ( cosom < 0.0 ) {
cosom = -cosom;
bx = - bx;
by = - by;
bz = - bz;
bw = - bw;
}
// calculate coefficients
if ( (1.0 - cosom) > 0.000001 ) {
// standard case (slerp)
omega = Math.acos(cosom);
sinom = Math.sin(omega);
scale0 = Math.sin((1.0 - t) * omega) / sinom;
scale1 = Math.sin(t * omega) / sinom;
} else {
// "from" and "to" quaternions are very close
// ... so we can do a linear interpolation
scale0 = 1.0 - t;
scale1 = t;
}
// calculate final values
target.x = scale0 * ax + scale1 * bx;
target.y = scale0 * ay + scale1 * by;
target.z = scale0 * az + scale1 * bz;
target.w = scale0 * aw + scale1 * bw;
return target;
};
/**
* Rotate an absolute orientation quaternion given an angular velocity and a time step.
* @param {Vec3} angularVelocity
* @param {number} dt
* @param {Vec3} angularFactor
* @param {Quaternion} target
* @return {Quaternion} The "target" object
*/
Quaternion.prototype.integrate = function(angularVelocity, dt, angularFactor, target){
target = target || new Quaternion();
var ax = angularVelocity.x * angularFactor.x,
ay = angularVelocity.y * angularFactor.y,
az = angularVelocity.z * angularFactor.z,
bx = this.x,
by = this.y,
bz = this.z,
bw = this.w;
var half_dt = dt * 0.5;
target.x += half_dt * (ax * bw + ay * bz - az * by);
target.y += half_dt * (ay * bw + az * bx - ax * bz);
target.z += half_dt * (az * bw + ax * by - ay * bx);
target.w += half_dt * (- ax * bx - ay * by - az * bz);
return target;
};
},{"./Vec3":74}],73:[function(require,module,exports){
var Vec3 = require('./Vec3');
var Quaternion = require('./Quaternion');
module.exports = Transform;
/**
* @class Transform
* @constructor
*/
function Transform(options) {
options = options || {};
/**
* @property {Vec3} position
*/
this.position = new Vec3();
if(options.position){
this.position.copy(options.position);
}
/**
* @property {Quaternion} quaternion
*/
this.quaternion = new Quaternion();
if(options.quaternion){
this.quaternion.copy(options.quaternion);
}
}
var tmpQuat = new Quaternion();
/**
* @static
* @method pointToLocaFrame
* @param {Vec3} position
* @param {Quaternion} quaternion
* @param {Vec3} worldPoint
* @param {Vec3} result
*/
Transform.pointToLocalFrame = function(position, quaternion, worldPoint, result){
var result = result || new Vec3();
worldPoint.vsub(position, result);
quaternion.conjugate(tmpQuat);
tmpQuat.vmult(result, result);
return result;
};
/**
* Get a global point in local transform coordinates.
* @method pointToLocal
* @param {Vec3} point
* @param {Vec3} result
* @return {Vec3} The "result" vector object
*/
Transform.prototype.pointToLocal = function(worldPoint, result){
return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result);
};
/**
* @static
* @method pointToWorldFrame
* @param {Vec3} position
* @param {Vec3} quaternion
* @param {Vec3} localPoint
* @param {Vec3} result
*/
Transform.pointToWorldFrame = function(position, quaternion, localPoint, result){
var result = result || new Vec3();
quaternion.vmult(localPoint, result);
result.vadd(position, result);
return result;
};
/**
* Get a local point in global transform coordinates.
* @method pointToWorld
* @param {Vec3} point
* @param {Vec3} result
* @return {Vec3} The "result" vector object
*/
Transform.prototype.pointToWorld = function(localPoint, result){
return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result);
};
Transform.prototype.vectorToWorldFrame = function(localVector, result){
var result = result || new Vec3();
this.quaternion.vmult(localVector, result);
return result;
};
Transform.vectorToWorldFrame = function(quaternion, localVector, result){
quaternion.vmult(localVector, result);
return result;
};
Transform.vectorToLocalFrame = function(position, quaternion, worldVector, result){
var result = result || new Vec3();
quaternion.w *= -1;
quaternion.vmult(worldVector, result);
quaternion.w *= -1;
return result;
};
},{"./Quaternion":72,"./Vec3":74}],74:[function(require,module,exports){
module.exports = Vec3;
var Mat3 = require('./Mat3');
/**
* 3-dimensional vector
* @class Vec3
* @constructor
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @author schteppe
* @example
* var v = new Vec3(1, 2, 3);
* console.log('x=' + v.x); // x=1
*/
function Vec3(x,y,z){
/**
* @property x
* @type {Number}
*/
this.x = x||0.0;
/**
* @property y
* @type {Number}
*/
this.y = y||0.0;
/**
* @property z
* @type {Number}
*/
this.z = z||0.0;
}
/**
* @static
* @property {Vec3} ZERO
*/
Vec3.ZERO = new Vec3(0, 0, 0);
/**
* @static
* @property {Vec3} UNIT_X
*/
Vec3.UNIT_X = new Vec3(1, 0, 0);
/**
* @static
* @property {Vec3} UNIT_Y
*/
Vec3.UNIT_Y = new Vec3(0, 1, 0);
/**
* @static
* @property {Vec3} UNIT_Z
*/
Vec3.UNIT_Z = new Vec3(0, 0, 1);
/**
* Vector cross product
* @method cross
* @param {Vec3} v
* @param {Vec3} target Optional. Target to save in.
* @return {Vec3}
*/
Vec3.prototype.cross = function(v,target){
var vx=v.x, vy=v.y, vz=v.z, x=this.x, y=this.y, z=this.z;
target = target || new Vec3();
target.x = (y * vz) - (z * vy);
target.y = (z * vx) - (x * vz);
target.z = (x * vy) - (y * vx);
return target;
};
/**
* Set the vectors' 3 elements
* @method set
* @param {Number} x
* @param {Number} y
* @param {Number} z
* @return Vec3
*/
Vec3.prototype.set = function(x,y,z){
this.x = x;
this.y = y;
this.z = z;
return this;
};
/**
* Set all components of the vector to zero.
* @method setZero
*/
Vec3.prototype.setZero = function(){
this.x = this.y = this.z = 0;
};
/**
* Vector addition
* @method vadd
* @param {Vec3} v
* @param {Vec3} target Optional.
* @return {Vec3}
*/
Vec3.prototype.vadd = function(v,target){
if(target){
target.x = v.x + this.x;
target.y = v.y + this.y;
target.z = v.z + this.z;
} else {
return new Vec3(this.x + v.x,
this.y + v.y,
this.z + v.z);
}
};
/**
* Vector subtraction
* @method vsub
* @param {Vec3} v
* @param {Vec3} target Optional. Target to save in.
* @return {Vec3}
*/
Vec3.prototype.vsub = function(v,target){
if(target){
target.x = this.x - v.x;
target.y = this.y - v.y;
target.z = this.z - v.z;
} else {
return new Vec3(this.x-v.x,
this.y-v.y,
this.z-v.z);
}
};
/**
* Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c
* @method crossmat
* @see http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf
* @return {Mat3}
*/
Vec3.prototype.crossmat = function(){
return new Mat3([ 0, -this.z, this.y,
this.z, 0, -this.x,
-this.y, this.x, 0]);
};
/**
* Normalize the vector. Note that this changes the values in the vector.
* @method normalize
* @return {Number} Returns the norm of the vector
*/
Vec3.prototype.normalize = function(){
var x=this.x, y=this.y, z=this.z;
var n = Math.sqrt(x*x + y*y + z*z);
if(n>0.0){
var invN = 1/n;
this.x *= invN;
this.y *= invN;
this.z *= invN;
} else {
// Make something up
this.x = 0;
this.y = 0;
this.z = 0;
}
return n;
};
/**
* Get the version of this vector that is of length 1.
* @method unit
* @param {Vec3} target Optional target to save in
* @return {Vec3} Returns the unit vector
*/
Vec3.prototype.unit = function(target){
target = target || new Vec3();
var x=this.x, y=this.y, z=this.z;
var ninv = Math.sqrt(x*x + y*y + z*z);
if(ninv>0.0){
ninv = 1.0/ninv;
target.x = x * ninv;
target.y = y * ninv;
target.z = z * ninv;
} else {
target.x = 1;
target.y = 0;
target.z = 0;
}
return target;
};
/**
* Get the length of the vector
* @method norm
* @return {Number}
* @deprecated Use .length() instead
*/
Vec3.prototype.norm = function(){
var x=this.x, y=this.y, z=this.z;
return Math.sqrt(x*x + y*y + z*z);
};
/**
* Get the length of the vector
* @method length
* @return {Number}
*/
Vec3.prototype.length = Vec3.prototype.norm;
/**
* Get the squared length of the vector
* @method norm2
* @return {Number}
* @deprecated Use .lengthSquared() instead.
*/
Vec3.prototype.norm2 = function(){
return this.dot(this);
};
/**
* Get the squared length of the vector.
* @method lengthSquared
* @return {Number}
*/
Vec3.prototype.lengthSquared = Vec3.prototype.norm2;
/**
* Get distance from this point to another point
* @method distanceTo
* @param {Vec3} p
* @return {Number}
*/
Vec3.prototype.distanceTo = function(p){
var x=this.x, y=this.y, z=this.z;
var px=p.x, py=p.y, pz=p.z;
return Math.sqrt((px-x)*(px-x)+
(py-y)*(py-y)+
(pz-z)*(pz-z));
};
/**
* Get squared distance from this point to another point
* @method distanceSquared
* @param {Vec3} p
* @return {Number}
*/
Vec3.prototype.distanceSquared = function(p){
var x=this.x, y=this.y, z=this.z;
var px=p.x, py=p.y, pz=p.z;
return (px-x)*(px-x) + (py-y)*(py-y) + (pz-z)*(pz-z);
};
/**
* Multiply all the components of the vector with a scalar.
* @deprecated Use .scale instead
* @method mult
* @param {Number} scalar
* @param {Vec3} target The vector to save the result in.
* @return {Vec3}
* @deprecated Use .scale() instead
*/
Vec3.prototype.mult = function(scalar,target){
target = target || new Vec3();
var x = this.x,
y = this.y,
z = this.z;
target.x = scalar * x;
target.y = scalar * y;
target.z = scalar * z;
return target;
};
/**
* Multiply the vector with an other vector, component-wise.
* @method mult
* @param {Number} vector
* @param {Vec3} target The vector to save the result in.
* @return {Vec3}
*/
Vec3.prototype.vmul = function(vector, target){
target = target || new Vec3();
target.x = vector.x * this.x;
target.y = vector.y * this.y;
target.z = vector.z * this.z;
return target;
};
/**
* Multiply the vector with a scalar.
* @method scale
* @param {Number} scalar
* @param {Vec3} target
* @return {Vec3}
*/
Vec3.prototype.scale = Vec3.prototype.mult;
/**
* Scale a vector and add it to this vector. Save the result in "target". (target = this + vector * scalar)
* @method addScaledVector
* @param {Number} scalar
* @param {Vec3} vector
* @param {Vec3} target The vector to save the result in.
* @return {Vec3}
*/
Vec3.prototype.addScaledVector = function(scalar, vector, target){
target = target || new Vec3();
target.x = this.x + scalar * vector.x;
target.y = this.y + scalar * vector.y;
target.z = this.z + scalar * vector.z;
return target;
};
/**
* Calculate dot product
* @method dot
* @param {Vec3} v
* @return {Number}
*/
Vec3.prototype.dot = function(v){
return this.x * v.x + this.y * v.y + this.z * v.z;
};
/**
* @method isZero
* @return bool
*/
Vec3.prototype.isZero = function(){
return this.x===0 && this.y===0 && this.z===0;
};
/**
* Make the vector point in the opposite direction.
* @method negate
* @param {Vec3} target Optional target to save in
* @return {Vec3}
*/
Vec3.prototype.negate = function(target){
target = target || new Vec3();
target.x = -this.x;
target.y = -this.y;
target.z = -this.z;
return target;
};
/**
* Compute two artificial tangents to the vector
* @method tangents
* @param {Vec3} t1 Vector object to save the first tangent in
* @param {Vec3} t2 Vector object to save the second tangent in
*/
var Vec3_tangents_n = new Vec3();
var Vec3_tangents_randVec = new Vec3();
Vec3.prototype.tangents = function(t1,t2){
var norm = this.norm();
if(norm>0.0){
var n = Vec3_tangents_n;
var inorm = 1/norm;
n.set(this.x*inorm,this.y*inorm,this.z*inorm);
var randVec = Vec3_tangents_randVec;
if(Math.abs(n.x) < 0.9){
randVec.set(1,0,0);
n.cross(randVec,t1);
} else {
randVec.set(0,1,0);
n.cross(randVec,t1);
}
n.cross(t1,t2);
} else {
// The normal length is zero, make something up
t1.set(1, 0, 0);
t2.set(0, 1, 0);
}
};
/**
* Converts to a more readable format
* @method toString
* @return string
*/
Vec3.prototype.toString = function(){
return this.x+","+this.y+","+this.z;
};
/**
* Converts to an array
* @method toArray
* @return Array
*/
Vec3.prototype.toArray = function(){
return [this.x, this.y, this.z];
};
/**
* Copies value of source to this vector.
* @method copy
* @param {Vec3} source
* @return {Vec3} this
*/
Vec3.prototype.copy = function(source){
this.x = source.x;
this.y = source.y;
this.z = source.z;
return this;
};
/**
* Do a linear interpolation between two vectors
* @method lerp
* @param {Vec3} v
* @param {Number} t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them.
* @param {Vec3} target
*/
Vec3.prototype.lerp = function(v,t,target){
var x=this.x, y=this.y, z=this.z;
target.x = x + (v.x-x)*t;
target.y = y + (v.y-y)*t;
target.z = z + (v.z-z)*t;
};
/**
* Check if a vector equals is almost equal to another one.
* @method almostEquals
* @param {Vec3} v
* @param {Number} precision
* @return bool
*/
Vec3.prototype.almostEquals = function(v,precision){
if(precision===undefined){
precision = 1e-6;
}
if( Math.abs(this.x-v.x)>precision ||
Math.abs(this.y-v.y)>precision ||
Math.abs(this.z-v.z)>precision){
return false;
}
return true;
};
/**
* Check if a vector is almost zero
* @method almostZero
* @param {Number} precision
*/
Vec3.prototype.almostZero = function(precision){
if(precision===undefined){
precision = 1e-6;
}
if( Math.abs(this.x)>precision ||
Math.abs(this.y)>precision ||
Math.abs(this.z)>precision){
return false;
}
return true;
};
var antip_neg = new Vec3();
/**
* Check if the vector is anti-parallel to another vector.
* @method isAntiparallelTo
* @param {Vec3} v
* @param {Number} precision Set to zero for exact comparisons
* @return {Boolean}
*/
Vec3.prototype.isAntiparallelTo = function(v,precision){
this.negate(antip_neg);
return antip_neg.almostEquals(v,precision);
};
/**
* Clone the vector
* @method clone
* @return {Vec3}
*/
Vec3.prototype.clone = function(){
return new Vec3(this.x, this.y, this.z);
};
},{"./Mat3":71}],75:[function(require,module,exports){
module.exports = Body;
var EventTarget = require('../utils/EventTarget');
var Shape = require('../shapes/Shape');
var Vec3 = require('../math/Vec3');
var Mat3 = require('../math/Mat3');
var Quaternion = require('../math/Quaternion');
var Material = require('../material/Material');
var AABB = require('../collision/AABB');
var Box = require('../shapes/Box');
/**
* Base class for all body types.
* @class Body
* @constructor
* @extends EventTarget
* @param {object} [options]
* @param {Vec3} [options.position]
* @param {Vec3} [options.velocity]
* @param {Vec3} [options.angularVelocity]
* @param {Quaternion} [options.quaternion]
* @param {number} [options.mass]
* @param {Material} [options.material]
* @param {number} [options.type]
* @param {number} [options.linearDamping=0.01]
* @param {number} [options.angularDamping=0.01]
* @param {boolean} [options.allowSleep=true]
* @param {number} [options.sleepSpeedLimit=0.1]
* @param {number} [options.sleepTimeLimit=1]
* @param {number} [options.collisionFilterGroup=1]
* @param {number} [options.collisionFilterMask=-1]
* @param {boolean} [options.fixedRotation=false]
* @param {Vec3} [options.linearFactor]
* @param {Vec3} [options.angularFactor]
* @param {Shape} [options.shape]
* @example
* var body = new Body({
* mass: 1
* });
* var shape = new Sphere(1);
* body.addShape(shape);
* world.addBody(body);
*/
function Body(options){
options = options || {};
EventTarget.apply(this);
this.id = Body.idCounter++;
/**
* Reference to the world the body is living in
* @property world
* @type {World}
*/
this.world = null;
/**
* Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object.
* @property preStep
* @type {Function}
* @deprecated Use World events instead
*/
this.preStep = null;
/**
* Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object.
* @property postStep
* @type {Function}
* @deprecated Use World events instead
*/
this.postStep = null;
this.vlambda = new Vec3();
/**
* @property {Number} collisionFilterGroup
*/
this.collisionFilterGroup = typeof(options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1;
/**
* @property {Number} collisionFilterMask
*/
this.collisionFilterMask = typeof(options.collisionFilterMask) === 'number' ? options.collisionFilterMask : -1;
/**
* Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.
* @property {Number} collisionResponse
*/
this.collisionResponse = true;
/**
* World space position of the body.
* @property position
* @type {Vec3}
*/
this.position = new Vec3();
/**
* @property {Vec3} previousPosition
*/
this.previousPosition = new Vec3();
/**
* Interpolated position of the body.
* @property {Vec3} interpolatedPosition
*/
this.interpolatedPosition = new Vec3();
/**
* Initial position of the body
* @property initPosition
* @type {Vec3}
*/
this.initPosition = new Vec3();
if(options.position){
this.position.copy(options.position);
this.previousPosition.copy(options.position);
this.interpolatedPosition.copy(options.position);
this.initPosition.copy(options.position);
}
/**
* World space velocity of the body.
* @property velocity
* @type {Vec3}
*/
this.velocity = new Vec3();
if(options.velocity){
this.velocity.copy(options.velocity);
}
/**
* @property initVelocity
* @type {Vec3}
*/
this.initVelocity = new Vec3();
/**
* Linear force on the body in world space.
* @property force
* @type {Vec3}
*/
this.force = new Vec3();
var mass = typeof(options.mass) === 'number' ? options.mass : 0;
/**
* @property mass
* @type {Number}
* @default 0
*/
this.mass = mass;
/**
* @property invMass
* @type {Number}
*/
this.invMass = mass > 0 ? 1.0 / mass : 0;
/**
* @property material
* @type {Material}
*/
this.material = options.material || null;
/**
* @property linearDamping
* @type {Number}
*/
this.linearDamping = typeof(options.linearDamping) === 'number' ? options.linearDamping : 0.01;
/**
* One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC.
* @property type
* @type {Number}
*/
this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC);
if(typeof(options.type) === typeof(Body.STATIC)){
this.type = options.type;
}
/**
* If true, the body will automatically fall to sleep.
* @property allowSleep
* @type {Boolean}
* @default true
*/
this.allowSleep = typeof(options.allowSleep) !== 'undefined' ? options.allowSleep : true;
/**
* Current sleep state.
* @property sleepState
* @type {Number}
*/
this.sleepState = 0;
/**
* If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.
* @property sleepSpeedLimit
* @type {Number}
* @default 0.1
*/
this.sleepSpeedLimit = typeof(options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1;
/**
* If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.
* @property sleepTimeLimit
* @type {Number}
* @default 1
*/
this.sleepTimeLimit = typeof(options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1;
this.timeLastSleepy = 0;
this._wakeUpAfterNarrowphase = false;
/**
* World space rotational force on the body, around center of mass.
* @property {Vec3} torque
*/
this.torque = new Vec3();
/**
* World space orientation of the body.
* @property quaternion
* @type {Quaternion}
*/
this.quaternion = new Quaternion();
/**
* @property initQuaternion
* @type {Quaternion}
*/
this.initQuaternion = new Quaternion();
/**
* @property {Quaternion} previousQuaternion
*/
this.previousQuaternion = new Quaternion();
/**
* Interpolated orientation of the body.
* @property {Quaternion} interpolatedQuaternion
*/
this.interpolatedQuaternion = new Quaternion();
if(options.quaternion){
this.quaternion.copy(options.quaternion);
this.initQuaternion.copy(options.quaternion);
this.previousQuaternion.copy(options.quaternion);
this.interpolatedQuaternion.copy(options.quaternion);
}
/**
* Angular velocity of the body, in world space. Think of the angular velocity as a vector, which the body rotates around. The length of this vector determines how fast (in radians per second) the body rotates.
* @property angularVelocity
* @type {Vec3}
*/
this.angularVelocity = new Vec3();
if(options.angularVelocity){
this.angularVelocity.copy(options.angularVelocity);
}
/**
* @property initAngularVelocity
* @type {Vec3}
*/
this.initAngularVelocity = new Vec3();
/**
* @property shapes
* @type {array}
*/
this.shapes = [];
/**
* Position of each Shape in the body, given in local Body space.
* @property shapeOffsets
* @type {array}
*/
this.shapeOffsets = [];
/**
* Orientation of each Shape, given in local Body space.
* @property shapeOrientations
* @type {array}
*/
this.shapeOrientations = [];
/**
* @property inertia
* @type {Vec3}
*/
this.inertia = new Vec3();
/**
* @property {Vec3} invInertia
*/
this.invInertia = new Vec3();
/**
* @property {Mat3} invInertiaWorld
*/
this.invInertiaWorld = new Mat3();
this.invMassSolve = 0;
/**
* @property {Vec3} invInertiaSolve
*/
this.invInertiaSolve = new Vec3();
/**
* @property {Mat3} invInertiaWorldSolve
*/
this.invInertiaWorldSolve = new Mat3();
/**
* Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this.
* @property {Boolean} fixedRotation
* @default false
*/
this.fixedRotation = typeof(options.fixedRotation) !== "undefined" ? options.fixedRotation : false;
/**
* @property {Number} angularDamping
*/
this.angularDamping = typeof(options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01;
/**
* Use this property to limit the motion along any world axis. (1,1,1) will allow motion along all axes while (0,0,0) allows none.
* @property {Vec3} linearFactor
*/
this.linearFactor = new Vec3(1,1,1);
if(options.linearFactor){
this.linearFactor.copy(options.linearFactor);
}
/**
* Use this property to limit the rotational motion along any world axis. (1,1,1) will allow rotation along all axes while (0,0,0) allows none.
* @property {Vec3} angularFactor
*/
this.angularFactor = new Vec3(1,1,1);
if(options.angularFactor){
this.angularFactor.copy(options.angularFactor);
}
/**
* World space bounding box of the body and its shapes.
* @property aabb
* @type {AABB}
*/
this.aabb = new AABB();
/**
* Indicates if the AABB needs to be updated before use.
* @property aabbNeedsUpdate
* @type {Boolean}
*/
this.aabbNeedsUpdate = true;
/**
* Total bounding radius of the Body including its shapes, relative to body.position.
* @property boundingRadius
* @type {Number}
*/
this.boundingRadius = 0;
this.wlambda = new Vec3();
if(options.shape){
this.addShape(options.shape);
}
this.updateMassProperties();
}
Body.prototype = new EventTarget();
Body.prototype.constructor = Body;
/**
* Dispatched after two bodies collide. This event is dispatched on each
* of the two bodies involved in the collision.
* @event collide
* @param {Body} body The body that was involved in the collision.
* @param {ContactEquation} contact The details of the collision.
*/
Body.COLLIDE_EVENT_NAME = "collide";
/**
* A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass.
* @static
* @property DYNAMIC
* @type {Number}
*/
Body.DYNAMIC = 1;
/**
* A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies.
* @static
* @property STATIC
* @type {Number}
*/
Body.STATIC = 2;
/**
* A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies.
* @static
* @property KINEMATIC
* @type {Number}
*/
Body.KINEMATIC = 4;
/**
* @static
* @property AWAKE
* @type {number}
*/
Body.AWAKE = 0;
/**
* @static
* @property SLEEPY
* @type {number}
*/
Body.SLEEPY = 1;
/**
* @static
* @property SLEEPING
* @type {number}
*/
Body.SLEEPING = 2;
Body.idCounter = 0;
/**
* Dispatched after a sleeping body has woken up.
* @event wakeup
*/
Body.wakeupEvent = {
type: "wakeup"
};
/**
* Wake the body up.
* @method wakeUp
*/
Body.prototype.wakeUp = function(){
var s = this.sleepState;
this.sleepState = 0;
this._wakeUpAfterNarrowphase = false;
if(s === Body.SLEEPING){
this.dispatchEvent(Body.wakeupEvent);
}
};
/**
* Force body sleep
* @method sleep
*/
Body.prototype.sleep = function(){
this.sleepState = Body.SLEEPING;
this.velocity.set(0,0,0);
this.angularVelocity.set(0,0,0);
this._wakeUpAfterNarrowphase = false;
};
/**
* Dispatched after a body has gone in to the sleepy state.
* @event sleepy
*/
Body.sleepyEvent = {
type: "sleepy"
};
/**
* Dispatched after a body has fallen asleep.
* @event sleep
*/
Body.sleepEvent = {
type: "sleep"
};
/**
* Called every timestep to update internal sleep timer and change sleep state if needed.
* @method sleepTick
* @param {Number} time The world time in seconds
*/
Body.prototype.sleepTick = function(time){
if(this.allowSleep){
var sleepState = this.sleepState;
var speedSquared = this.velocity.norm2() + this.angularVelocity.norm2();
var speedLimitSquared = Math.pow(this.sleepSpeedLimit,2);
if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){
this.sleepState = Body.SLEEPY; // Sleepy
this.timeLastSleepy = time;
this.dispatchEvent(Body.sleepyEvent);
} else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){
this.wakeUp(); // Wake up
} else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){
this.sleep(); // Sleeping
this.dispatchEvent(Body.sleepEvent);
}
}
};
/**
* If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass".
* @method updateSolveMassProperties
*/
Body.prototype.updateSolveMassProperties = function(){
if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){
this.invMassSolve = 0;
this.invInertiaSolve.setZero();
this.invInertiaWorldSolve.setZero();
} else {
this.invMassSolve = this.invMass;
this.invInertiaSolve.copy(this.invInertia);
this.invInertiaWorldSolve.copy(this.invInertiaWorld);
}
};
/**
* Convert a world point to local body frame.
* @method pointToLocalFrame
* @param {Vec3} worldPoint
* @param {Vec3} result
* @return {Vec3}
*/
Body.prototype.pointToLocalFrame = function(worldPoint,result){
var result = result || new Vec3();
worldPoint.vsub(this.position,result);
this.quaternion.conjugate().vmult(result,result);
return result;
};
/**
* Convert a world vector to local body frame.
* @method vectorToLocalFrame
* @param {Vec3} worldPoint
* @param {Vec3} result
* @return {Vec3}
*/
Body.prototype.vectorToLocalFrame = function(worldVector, result){
var result = result || new Vec3();
this.quaternion.conjugate().vmult(worldVector,result);
return result;
};
/**
* Convert a local body point to world frame.
* @method pointToWorldFrame
* @param {Vec3} localPoint
* @param {Vec3} result
* @return {Vec3}
*/
Body.prototype.pointToWorldFrame = function(localPoint,result){
var result = result || new Vec3();
this.quaternion.vmult(localPoint,result);
result.vadd(this.position,result);
return result;
};
/**
* Convert a local body point to world frame.
* @method vectorToWorldFrame
* @param {Vec3} localVector
* @param {Vec3} result
* @return {Vec3}
*/
Body.prototype.vectorToWorldFrame = function(localVector, result){
var result = result || new Vec3();
this.quaternion.vmult(localVector, result);
return result;
};
var tmpVec = new Vec3();
var tmpQuat = new Quaternion();
/**
* Add a shape to the body with a local offset and orientation.
* @method addShape
* @param {Shape} shape
* @param {Vec3} [_offset]
* @param {Quaternion} [_orientation]
* @return {Body} The body object, for chainability.
*/
Body.prototype.addShape = function(shape, _offset, _orientation){
var offset = new Vec3();
var orientation = new Quaternion();
if(_offset){
offset.copy(_offset);
}
if(_orientation){
orientation.copy(_orientation);
}
this.shapes.push(shape);
this.shapeOffsets.push(offset);
this.shapeOrientations.push(orientation);
this.updateMassProperties();
this.updateBoundingRadius();
this.aabbNeedsUpdate = true;
shape.body = this;
return this;
};
/**
* Update the bounding radius of the body. Should be done if any of the shapes are changed.
* @method updateBoundingRadius
*/
Body.prototype.updateBoundingRadius = function(){
var shapes = this.shapes,
shapeOffsets = this.shapeOffsets,
N = shapes.length,
radius = 0;
for(var i=0; i!==N; i++){
var shape = shapes[i];
shape.updateBoundingSphereRadius();
var offset = shapeOffsets[i].norm(),
r = shape.boundingSphereRadius;
if(offset + r > radius){
radius = offset + r;
}
}
this.boundingRadius = radius;
};
var computeAABB_shapeAABB = new AABB();
/**
* Updates the .aabb
* @method computeAABB
* @todo rename to updateAABB()
*/
Body.prototype.computeAABB = function(){
var shapes = this.shapes,
shapeOffsets = this.shapeOffsets,
shapeOrientations = this.shapeOrientations,
N = shapes.length,
offset = tmpVec,
orientation = tmpQuat,
bodyQuat = this.quaternion,
aabb = this.aabb,
shapeAABB = computeAABB_shapeAABB;
for(var i=0; i!==N; i++){
var shape = shapes[i];
// Get shape world position
bodyQuat.vmult(shapeOffsets[i], offset);
offset.vadd(this.position, offset);
// Get shape world quaternion
shapeOrientations[i].mult(bodyQuat, orientation);
// Get shape AABB
shape.calculateWorldAABB(offset, orientation, shapeAABB.lowerBound, shapeAABB.upperBound);
if(i === 0){
aabb.copy(shapeAABB);
} else {
aabb.extend(shapeAABB);
}
}
this.aabbNeedsUpdate = false;
};
var uiw_m1 = new Mat3(),
uiw_m2 = new Mat3(),
uiw_m3 = new Mat3();
/**
* Update .inertiaWorld and .invInertiaWorld
* @method updateInertiaWorld
*/
Body.prototype.updateInertiaWorld = function(force){
var I = this.invInertia;
if (I.x === I.y && I.y === I.z && !force) {
// If inertia M = s*I, where I is identity and s a scalar, then
// R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M
// where R is the rotation matrix.
// In other words, we don't have to transform the inertia if all
// inertia diagonal entries are equal.
} else {
var m1 = uiw_m1,
m2 = uiw_m2,
m3 = uiw_m3;
m1.setRotationFromQuaternion(this.quaternion);
m1.transpose(m2);
m1.scale(I,m1);
m1.mmult(m2,this.invInertiaWorld);
}
};
/**
* Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque.
* @method applyForce
* @param {Vec3} force The amount of force to add.
* @param {Vec3} relativePoint A point relative to the center of mass to apply the force on.
*/
var Body_applyForce_r = new Vec3();
var Body_applyForce_rotForce = new Vec3();
Body.prototype.applyForce = function(force,relativePoint){
if(this.type !== Body.DYNAMIC){ // Needed?
return;
}
// Compute produced rotational force
var rotForce = Body_applyForce_rotForce;
relativePoint.cross(force,rotForce);
// Add linear force
this.force.vadd(force,this.force);
// Add rotational force
this.torque.vadd(rotForce,this.torque);
};
/**
* Apply force to a local point in the body.
* @method applyLocalForce
* @param {Vec3} force The force vector to apply, defined locally in the body frame.
* @param {Vec3} localPoint A local point in the body to apply the force on.
*/
var Body_applyLocalForce_worldForce = new Vec3();
var Body_applyLocalForce_relativePointWorld = new Vec3();
Body.prototype.applyLocalForce = function(localForce, localPoint){
if(this.type !== Body.DYNAMIC){
return;
}
var worldForce = Body_applyLocalForce_worldForce;
var relativePointWorld = Body_applyLocalForce_relativePointWorld;
// Transform the force vector to world space
this.vectorToWorldFrame(localForce, worldForce);
this.vectorToWorldFrame(localPoint, relativePointWorld);
this.applyForce(worldForce, relativePointWorld);
};
/**
* Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.
* @method applyImpulse
* @param {Vec3} impulse The amount of impulse to add.
* @param {Vec3} relativePoint A point relative to the center of mass to apply the force on.
*/
var Body_applyImpulse_r = new Vec3();
var Body_applyImpulse_velo = new Vec3();
var Body_applyImpulse_rotVelo = new Vec3();
Body.prototype.applyImpulse = function(impulse, relativePoint){
if(this.type !== Body.DYNAMIC){
return;
}
// Compute point position relative to the body center
var r = relativePoint;
// Compute produced central impulse velocity
var velo = Body_applyImpulse_velo;
velo.copy(impulse);
velo.mult(this.invMass,velo);
// Add linear impulse
this.velocity.vadd(velo, this.velocity);
// Compute produced rotational impulse velocity
var rotVelo = Body_applyImpulse_rotVelo;
r.cross(impulse,rotVelo);
/*
rotVelo.x *= this.invInertia.x;
rotVelo.y *= this.invInertia.y;
rotVelo.z *= this.invInertia.z;
*/
this.invInertiaWorld.vmult(rotVelo,rotVelo);
// Add rotational Impulse
this.angularVelocity.vadd(rotVelo, this.angularVelocity);
};
/**
* Apply locally-defined impulse to a local point in the body.
* @method applyLocalImpulse
* @param {Vec3} force The force vector to apply, defined locally in the body frame.
* @param {Vec3} localPoint A local point in the body to apply the force on.
*/
var Body_applyLocalImpulse_worldImpulse = new Vec3();
var Body_applyLocalImpulse_relativePoint = new Vec3();
Body.prototype.applyLocalImpulse = function(localImpulse, localPoint){
if(this.type !== Body.DYNAMIC){
return;
}
var worldImpulse = Body_applyLocalImpulse_worldImpulse;
var relativePointWorld = Body_applyLocalImpulse_relativePoint;
// Transform the force vector to world space
this.vectorToWorldFrame(localImpulse, worldImpulse);
this.vectorToWorldFrame(localPoint, relativePointWorld);
this.applyImpulse(worldImpulse, relativePointWorld);
};
var Body_updateMassProperties_halfExtents = new Vec3();
/**
* Should be called whenever you change the body shape or mass.
* @method updateMassProperties
*/
Body.prototype.updateMassProperties = function(){
var halfExtents = Body_updateMassProperties_halfExtents;
this.invMass = this.mass > 0 ? 1.0 / this.mass : 0;
var I = this.inertia;
var fixed = this.fixedRotation;
// Approximate with AABB box
this.computeAABB();
halfExtents.set(
(this.aabb.upperBound.x-this.aabb.lowerBound.x) / 2,
(this.aabb.upperBound.y-this.aabb.lowerBound.y) / 2,
(this.aabb.upperBound.z-this.aabb.lowerBound.z) / 2
);
Box.calculateInertia(halfExtents, this.mass, I);
this.invInertia.set(
I.x > 0 && !fixed ? 1.0 / I.x : 0,
I.y > 0 && !fixed ? 1.0 / I.y : 0,
I.z > 0 && !fixed ? 1.0 / I.z : 0
);
this.updateInertiaWorld(true);
};
/**
* Get world velocity of a point in the body.
* @method getVelocityAtWorldPoint
* @param {Vec3} worldPoint
* @param {Vec3} result
* @return {Vec3} The result vector.
*/
Body.prototype.getVelocityAtWorldPoint = function(worldPoint, result){
var r = new Vec3();
worldPoint.vsub(this.position, r);
this.angularVelocity.cross(r, result);
this.velocity.vadd(result, result);
return result;
};
var torque = new Vec3();
var invI_tau_dt = new Vec3();
var w = new Quaternion();
var wq = new Quaternion();
/**
* Move the body forward in time.
* @param {number} dt Time step
* @param {boolean} quatNormalize Set to true to normalize the body quaternion
* @param {boolean} quatNormalizeFast If the quaternion should be normalized using "fast" quaternion normalization
*/
Body.prototype.integrate = function(dt, quatNormalize, quatNormalizeFast){
// Save previous position
this.previousPosition.copy(this.position);
this.previousQuaternion.copy(this.quaternion);
if(!(this.type === Body.DYNAMIC || this.type === Body.KINEMATIC) || this.sleepState === Body.SLEEPING){ // Only for dynamic
return;
}
var velo = this.velocity,
angularVelo = this.angularVelocity,
pos = this.position,
force = this.force,
torque = this.torque,
quat = this.quaternion,
invMass = this.invMass,
invInertia = this.invInertiaWorld,
linearFactor = this.linearFactor;
var iMdt = invMass * dt;
velo.x += force.x * iMdt * linearFactor.x;
velo.y += force.y * iMdt * linearFactor.y;
velo.z += force.z * iMdt * linearFactor.z;
var e = invInertia.elements;
var angularFactor = this.angularFactor;
var tx = torque.x * angularFactor.x;
var ty = torque.y * angularFactor.y;
var tz = torque.z * angularFactor.z;
angularVelo.x += dt * (e[0] * tx + e[1] * ty + e[2] * tz);
angularVelo.y += dt * (e[3] * tx + e[4] * ty + e[5] * tz);
angularVelo.z += dt * (e[6] * tx + e[7] * ty + e[8] * tz);
// Use new velocity - leap frog
pos.x += velo.x * dt;
pos.y += velo.y * dt;
pos.z += velo.z * dt;
quat.integrate(this.angularVelocity, dt, this.angularFactor, quat);
if(quatNormalize){
if(quatNormalizeFast){
quat.normalizeFast();
} else {
quat.normalize();
}
}
this.aabbNeedsUpdate = true;
// Update world inertia
this.updateInertiaWorld();
};
},{"../collision/AABB":46,"../material/Material":69,"../math/Mat3":71,"../math/Quaternion":72,"../math/Vec3":74,"../shapes/Box":81,"../shapes/Shape":87,"../utils/EventTarget":93}],76:[function(require,module,exports){
var Body = require('./Body');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var RaycastResult = require('../collision/RaycastResult');
var Ray = require('../collision/Ray');
var WheelInfo = require('../objects/WheelInfo');
module.exports = RaycastVehicle;
/**
* Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces.
* @class RaycastVehicle
* @constructor
* @param {object} [options]
* @param {Body} [options.chassisBody] The car chassis body.
* @param {integer} [options.indexRightAxis] Axis to use for right. x=0, y=1, z=2
* @param {integer} [options.indexLeftAxis]
* @param {integer} [options.indexUpAxis]
*/
function RaycastVehicle(options){
/**
* @property {Body} chassisBody
*/
this.chassisBody = options.chassisBody;
/**
* An array of WheelInfo objects.
* @property {array} wheelInfos
*/
this.wheelInfos = [];
/**
* Will be set to true if the car is sliding.
* @property {boolean} sliding
*/
this.sliding = false;
/**
* @property {World} world
*/
this.world = null;
/**
* Index of the right axis, 0=x, 1=y, 2=z
* @property {integer} indexRightAxis
* @default 1
*/
this.indexRightAxis = typeof(options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1;
/**
* Index of the forward axis, 0=x, 1=y, 2=z
* @property {integer} indexForwardAxis
* @default 0
*/
this.indexForwardAxis = typeof(options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0;
/**
* Index of the up axis, 0=x, 1=y, 2=z
* @property {integer} indexUpAxis
* @default 2
*/
this.indexUpAxis = typeof(options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2;
}
var tmpVec1 = new Vec3();
var tmpVec2 = new Vec3();
var tmpVec3 = new Vec3();
var tmpVec4 = new Vec3();
var tmpVec5 = new Vec3();
var tmpVec6 = new Vec3();
var tmpRay = new Ray();
/**
* Add a wheel. For information about the options, see WheelInfo.
* @method addWheel
* @param {object} [options]
*/
RaycastVehicle.prototype.addWheel = function(options){
options = options || {};
var info = new WheelInfo(options);
var index = this.wheelInfos.length;
this.wheelInfos.push(info);
return index;
};
/**
* Set the steering value of a wheel.
* @method setSteeringValue
* @param {number} value
* @param {integer} wheelIndex
*/
RaycastVehicle.prototype.setSteeringValue = function(value, wheelIndex){
var wheel = this.wheelInfos[wheelIndex];
wheel.steering = value;
};
var torque = new Vec3();
/**
* Set the wheel force to apply on one of the wheels each time step
* @method applyEngineForce
* @param {number} value
* @param {integer} wheelIndex
*/
RaycastVehicle.prototype.applyEngineForce = function(value, wheelIndex){
this.wheelInfos[wheelIndex].engineForce = value;
};
/**
* Set the braking force of a wheel
* @method setBrake
* @param {number} brake
* @param {integer} wheelIndex
*/
RaycastVehicle.prototype.setBrake = function(brake, wheelIndex){
this.wheelInfos[wheelIndex].brake = brake;
};
/**
* Add the vehicle including its constraints to the world.
* @method addToWorld
* @param {World} world
*/
RaycastVehicle.prototype.addToWorld = function(world){
var constraints = this.constraints;
world.addBody(this.chassisBody);
var that = this;
this.preStepCallback = function(){
that.updateVehicle(world.dt);
};
world.addEventListener('preStep', this.preStepCallback);
this.world = world;
};
/**
* Get one of the wheel axles, world-oriented.
* @private
* @method getVehicleAxisWorld
* @param {integer} axisIndex
* @param {Vec3} result
*/
RaycastVehicle.prototype.getVehicleAxisWorld = function(axisIndex, result){
result.set(
axisIndex === 0 ? 1 : 0,
axisIndex === 1 ? 1 : 0,
axisIndex === 2 ? 1 : 0
);
this.chassisBody.vectorToWorldFrame(result, result);
};
RaycastVehicle.prototype.updateVehicle = function(timeStep){
var wheelInfos = this.wheelInfos;
var numWheels = wheelInfos.length;
var chassisBody = this.chassisBody;
for (var i = 0; i < numWheels; i++) {
this.updateWheelTransform(i);
}
this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.norm();
var forwardWorld = new Vec3();
this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld);
if (forwardWorld.dot(chassisBody.velocity) < 0){
this.currentVehicleSpeedKmHour *= -1;
}
// simulate suspension
for (var i = 0; i < numWheels; i++) {
this.castRay(wheelInfos[i]);
}
this.updateSuspension(timeStep);
var impulse = new Vec3();
var relpos = new Vec3();
for (var i = 0; i < numWheels; i++) {
//apply suspension force
var wheel = wheelInfos[i];
var suspensionForce = wheel.suspensionForce;
if (suspensionForce > wheel.maxSuspensionForce) {
suspensionForce = wheel.maxSuspensionForce;
}
wheel.raycastResult.hitNormalWorld.scale(suspensionForce * timeStep, impulse);
wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos);
chassisBody.applyImpulse(impulse, relpos);
}
this.updateFriction(timeStep);
var hitNormalWorldScaledWithProj = new Vec3();
var fwd = new Vec3();
var vel = new Vec3();
for (i = 0; i < numWheels; i++) {
var wheel = wheelInfos[i];
//var relpos = new Vec3();
//wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos);
chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel);
// Hack to get the rotation in the correct direction
var m = 1;
switch(this.indexUpAxis){
case 1:
m = -1;
break;
}
if (wheel.isInContact) {
this.getVehicleAxisWorld(this.indexForwardAxis, fwd);
var proj = fwd.dot(wheel.raycastResult.hitNormalWorld);
wheel.raycastResult.hitNormalWorld.scale(proj, hitNormalWorldScaledWithProj);
fwd.vsub(hitNormalWorldScaledWithProj, fwd);
var proj2 = fwd.dot(vel);
wheel.deltaRotation = m * proj2 * timeStep / wheel.radius;
}
if((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed){
// Apply custom rotation when accelerating and sliding
wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep;
}
// Lock wheels
if(Math.abs(wheel.brake) > Math.abs(wheel.engineForce)){
wheel.deltaRotation = 0;
}
wheel.rotation += wheel.deltaRotation; // Use the old value
wheel.deltaRotation *= 0.99; // damping of rotation when not in contact
}
};
RaycastVehicle.prototype.updateSuspension = function(deltaTime) {
var chassisBody = this.chassisBody;
var chassisMass = chassisBody.mass;
var wheelInfos = this.wheelInfos;
var numWheels = wheelInfos.length;
for (var w_it = 0; w_it < numWheels; w_it++){
var wheel = wheelInfos[w_it];
if (wheel.isInContact){
var force;
// Spring
var susp_length = wheel.suspensionRestLength;
var current_length = wheel.suspensionLength;
var length_diff = (susp_length - current_length);
force = wheel.suspensionStiffness * length_diff * wheel.clippedInvContactDotSuspension;
// Damper
var projected_rel_vel = wheel.suspensionRelativeVelocity;
var susp_damping;
if (projected_rel_vel < 0) {
susp_damping = wheel.dampingCompression;
} else {
susp_damping = wheel.dampingRelaxation;
}
force -= susp_damping * projected_rel_vel;
wheel.suspensionForce = force * chassisMass;
if (wheel.suspensionForce < 0) {
wheel.suspensionForce = 0;
}
} else {
wheel.suspensionForce = 0;
}
}
};
/**
* Remove the vehicle including its constraints from the world.
* @method removeFromWorld
* @param {World} world
*/
RaycastVehicle.prototype.removeFromWorld = function(world){
var constraints = this.constraints;
world.remove(this.chassisBody);
world.removeEventListener('preStep', this.preStepCallback);
this.world = null;
};
var castRay_rayvector = new Vec3();
var castRay_target = new Vec3();
RaycastVehicle.prototype.castRay = function(wheel) {
var rayvector = castRay_rayvector;
var target = castRay_target;
this.updateWheelTransformWorld(wheel);
var chassisBody = this.chassisBody;
var depth = -1;
var raylen = wheel.suspensionRestLength + wheel.radius;
wheel.directionWorld.scale(raylen, rayvector);
var source = wheel.chassisConnectionPointWorld;
source.vadd(rayvector, target);
var raycastResult = wheel.raycastResult;
var param = 0;
raycastResult.reset();
// Turn off ray collision with the chassis temporarily
var oldState = chassisBody.collisionResponse;
chassisBody.collisionResponse = false;
// Cast ray against world
this.world.rayTest(source, target, raycastResult);
chassisBody.collisionResponse = oldState;
var object = raycastResult.body;
wheel.raycastResult.groundObject = 0;
if (object) {
depth = raycastResult.distance;
wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld;
wheel.isInContact = true;
var hitDistance = raycastResult.distance;
wheel.suspensionLength = hitDistance - wheel.radius;
// clamp on max suspension travel
var minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel;
var maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel;
if (wheel.suspensionLength < minSuspensionLength) {
wheel.suspensionLength = minSuspensionLength;
}
if (wheel.suspensionLength > maxSuspensionLength) {
wheel.suspensionLength = maxSuspensionLength;
wheel.raycastResult.reset();
}
var denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld);
var chassis_velocity_at_contactPoint = new Vec3();
chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassis_velocity_at_contactPoint);
var projVel = wheel.raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint );
if (denominator >= -0.1) {
wheel.suspensionRelativeVelocity = 0;
wheel.clippedInvContactDotSuspension = 1 / 0.1;
} else {
var inv = -1 / denominator;
wheel.suspensionRelativeVelocity = projVel * inv;
wheel.clippedInvContactDotSuspension = inv;
}
} else {
//put wheel info as in rest position
wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel;
wheel.suspensionRelativeVelocity = 0.0;
wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld);
wheel.clippedInvContactDotSuspension = 1.0;
}
return depth;
};
RaycastVehicle.prototype.updateWheelTransformWorld = function(wheel){
wheel.isInContact = false;
var chassisBody = this.chassisBody;
chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld);
chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld);
chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld);
};
/**
* Update one of the wheel transform.
* Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example.
* @method updateWheelTransform
* @param {integer} wheelIndex The wheel index to update.
*/
RaycastVehicle.prototype.updateWheelTransform = function(wheelIndex){
var up = tmpVec4;
var right = tmpVec5;
var fwd = tmpVec6;
var wheel = this.wheelInfos[wheelIndex];
this.updateWheelTransformWorld(wheel);
wheel.directionLocal.scale(-1, up);
right.copy(wheel.axleLocal);
up.cross(right, fwd);
fwd.normalize();
right.normalize();
// Rotate around steering over the wheelAxle
var steering = wheel.steering;
var steeringOrn = new Quaternion();
steeringOrn.setFromAxisAngle(up, steering);
var rotatingOrn = new Quaternion();
rotatingOrn.setFromAxisAngle(right, wheel.rotation);
// World rotation of the wheel
var q = wheel.worldTransform.quaternion;
this.chassisBody.quaternion.mult(steeringOrn, q);
q.mult(rotatingOrn, q);
q.normalize();
// world position of the wheel
var p = wheel.worldTransform.position;
p.copy(wheel.directionWorld);
p.scale(wheel.suspensionLength, p);
p.vadd(wheel.chassisConnectionPointWorld, p);
};
var directions = [
new Vec3(1, 0, 0),
new Vec3(0, 1, 0),
new Vec3(0, 0, 1)
];
/**
* Get the world transform of one of the wheels
* @method getWheelTransformWorld
* @param {integer} wheelIndex
* @return {Transform}
*/
RaycastVehicle.prototype.getWheelTransformWorld = function(wheelIndex) {
return this.wheelInfos[wheelIndex].worldTransform;
};
var updateFriction_surfNormalWS_scaled_proj = new Vec3();
var updateFriction_axle = [];
var updateFriction_forwardWS = [];
var sideFrictionStiffness2 = 1;
RaycastVehicle.prototype.updateFriction = function(timeStep) {
var surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj;
//calculate the impulse, so that the wheels don't move sidewards
var wheelInfos = this.wheelInfos;
var numWheels = wheelInfos.length;
var chassisBody = this.chassisBody;
var forwardWS = updateFriction_forwardWS;
var axle = updateFriction_axle;
var numWheelsOnGround = 0;
for (var i = 0; i < numWheels; i++) {
var wheel = wheelInfos[i];
var groundObject = wheel.raycastResult.body;
if (groundObject){
numWheelsOnGround++;
}
wheel.sideImpulse = 0;
wheel.forwardImpulse = 0;
if(!forwardWS[i]){
forwardWS[i] = new Vec3();
}
if(!axle[i]){
axle[i] = new Vec3();
}
}
for (var i = 0; i < numWheels; i++){
var wheel = wheelInfos[i];
var groundObject = wheel.raycastResult.body;
if (groundObject) {
var axlei = axle[i];
var wheelTrans = this.getWheelTransformWorld(i);
// Get world axle
wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei);
var surfNormalWS = wheel.raycastResult.hitNormalWorld;
var proj = axlei.dot(surfNormalWS);
surfNormalWS.scale(proj, surfNormalWS_scaled_proj);
axlei.vsub(surfNormalWS_scaled_proj, axlei);
axlei.normalize();
surfNormalWS.cross(axlei, forwardWS[i]);
forwardWS[i].normalize();
wheel.sideImpulse = resolveSingleBilateral(
chassisBody,
wheel.raycastResult.hitPointWorld,
groundObject,
wheel.raycastResult.hitPointWorld,
axlei
);
wheel.sideImpulse *= sideFrictionStiffness2;
}
}
var sideFactor = 1;
var fwdFactor = 0.5;
this.sliding = false;
for (var i = 0; i < numWheels; i++) {
var wheel = wheelInfos[i];
var groundObject = wheel.raycastResult.body;
var rollingFriction = 0;
wheel.slipInfo = 1;
if (groundObject) {
var defaultRollingFrictionImpulse = 0;
var maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse;
// btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse);
// rollingFriction = calcRollingFriction(contactPt);
rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse);
rollingFriction += wheel.engineForce * timeStep;
// rollingFriction = 0;
var factor = maxImpulse / rollingFriction;
wheel.slipInfo *= factor;
}
//switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break)
wheel.forwardImpulse = 0;
wheel.skidInfo = 1;
if (groundObject) {
wheel.skidInfo = 1;
var maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip;
var maximpSide = maximp;
var maximpSquared = maximp * maximpSide;
wheel.forwardImpulse = rollingFriction;//wheelInfo.engineForce* timeStep;
var x = wheel.forwardImpulse * fwdFactor;
var y = wheel.sideImpulse * sideFactor;
var impulseSquared = x * x + y * y;
wheel.sliding = false;
if (impulseSquared > maximpSquared) {
this.sliding = true;
wheel.sliding = true;
var factor = maximp / Math.sqrt(impulseSquared);
wheel.skidInfo *= factor;
}
}
}
if (this.sliding) {
for (var i = 0; i < numWheels; i++) {
var wheel = wheelInfos[i];
if (wheel.sideImpulse !== 0) {
if (wheel.skidInfo < 1){
wheel.forwardImpulse *= wheel.skidInfo;
wheel.sideImpulse *= wheel.skidInfo;
}
}
}
}
// apply the impulses
for (var i = 0; i < numWheels; i++) {
var wheel = wheelInfos[i];
var rel_pos = new Vec3();
wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos);
// cannons applyimpulse is using world coord for the position
//rel_pos.copy(wheel.raycastResult.hitPointWorld);
if (wheel.forwardImpulse !== 0) {
var impulse = new Vec3();
forwardWS[i].scale(wheel.forwardImpulse, impulse);
chassisBody.applyImpulse(impulse, rel_pos);
}
if (wheel.sideImpulse !== 0){
var groundObject = wheel.raycastResult.body;
var rel_pos2 = new Vec3();
wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2);
//rel_pos2.copy(wheel.raycastResult.hitPointWorld);
var sideImp = new Vec3();
axle[i].scale(wheel.sideImpulse, sideImp);
// Scale the relative position in the up direction with rollInfluence.
// If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over).
chassisBody.vectorToLocalFrame(rel_pos, rel_pos);
rel_pos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence;
chassisBody.vectorToWorldFrame(rel_pos, rel_pos);
chassisBody.applyImpulse(sideImp, rel_pos);
//apply friction impulse on the ground
sideImp.scale(-1, sideImp);
groundObject.applyImpulse(sideImp, rel_pos2);
}
}
};
var calcRollingFriction_vel1 = new Vec3();
var calcRollingFriction_vel2 = new Vec3();
var calcRollingFriction_vel = new Vec3();
function calcRollingFriction(body0, body1, frictionPosWorld, frictionDirectionWorld, maxImpulse) {
var j1 = 0;
var contactPosWorld = frictionPosWorld;
// var rel_pos1 = new Vec3();
// var rel_pos2 = new Vec3();
var vel1 = calcRollingFriction_vel1;
var vel2 = calcRollingFriction_vel2;
var vel = calcRollingFriction_vel;
// contactPosWorld.vsub(body0.position, rel_pos1);
// contactPosWorld.vsub(body1.position, rel_pos2);
body0.getVelocityAtWorldPoint(contactPosWorld, vel1);
body1.getVelocityAtWorldPoint(contactPosWorld, vel2);
vel1.vsub(vel2, vel);
var vrel = frictionDirectionWorld.dot(vel);
var denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld);
var denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld);
var relaxation = 1;
var jacDiagABInv = relaxation / (denom0 + denom1);
// calculate j that moves us to zero relative velocity
j1 = -vrel * jacDiagABInv;
if (maxImpulse < j1) {
j1 = maxImpulse;
}
if (j1 < -maxImpulse) {
j1 = -maxImpulse;
}
return j1;
}
var computeImpulseDenominator_r0 = new Vec3();
var computeImpulseDenominator_c0 = new Vec3();
var computeImpulseDenominator_vec = new Vec3();
var computeImpulseDenominator_m = new Vec3();
function computeImpulseDenominator(body, pos, normal) {
var r0 = computeImpulseDenominator_r0;
var c0 = computeImpulseDenominator_c0;
var vec = computeImpulseDenominator_vec;
var m = computeImpulseDenominator_m;
pos.vsub(body.position, r0);
r0.cross(normal, c0);
body.invInertiaWorld.vmult(c0, m);
m.cross(r0, vec);
return body.invMass + normal.dot(vec);
}
var resolveSingleBilateral_vel1 = new Vec3();
var resolveSingleBilateral_vel2 = new Vec3();
var resolveSingleBilateral_vel = new Vec3();
//bilateral constraint between two dynamic objects
function resolveSingleBilateral(body1, pos1, body2, pos2, normal, impulse){
var normalLenSqr = normal.norm2();
if (normalLenSqr > 1.1){
return 0; // no impulse
}
// var rel_pos1 = new Vec3();
// var rel_pos2 = new Vec3();
// pos1.vsub(body1.position, rel_pos1);
// pos2.vsub(body2.position, rel_pos2);
var vel1 = resolveSingleBilateral_vel1;
var vel2 = resolveSingleBilateral_vel2;
var vel = resolveSingleBilateral_vel;
body1.getVelocityAtWorldPoint(pos1, vel1);
body2.getVelocityAtWorldPoint(pos2, vel2);
vel1.vsub(vel2, vel);
var rel_vel = normal.dot(vel);
var contactDamping = 0.2;
var massTerm = 1 / (body1.invMass + body2.invMass);
var impulse = - contactDamping * rel_vel * massTerm;
return impulse;
}
},{"../collision/Ray":53,"../collision/RaycastResult":54,"../math/Quaternion":72,"../math/Vec3":74,"../objects/WheelInfo":80,"./Body":75}],77:[function(require,module,exports){
var Body = require('./Body');
var Sphere = require('../shapes/Sphere');
var Box = require('../shapes/Box');
var Vec3 = require('../math/Vec3');
var HingeConstraint = require('../constraints/HingeConstraint');
module.exports = RigidVehicle;
/**
* Simple vehicle helper class with spherical rigid body wheels.
* @class RigidVehicle
* @constructor
* @param {Body} [options.chassisBody]
*/
function RigidVehicle(options){
this.wheelBodies = [];
/**
* @property coordinateSystem
* @type {Vec3}
*/
this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone();
/**
* @property {Body} chassisBody
*/
this.chassisBody = options.chassisBody;
if(!this.chassisBody){
// No chassis body given. Create it!
var chassisShape = new Box(new Vec3(5, 2, 0.5));
this.chassisBody = new Body(1, chassisShape);
}
/**
* @property constraints
* @type {Array}
*/
this.constraints = [];
this.wheelAxes = [];
this.wheelForces = [];
}
/**
* Add a wheel
* @method addWheel
* @param {object} options
* @param {boolean} [options.isFrontWheel]
* @param {Vec3} [options.position] Position of the wheel, locally in the chassis body.
* @param {Vec3} [options.direction] Slide direction of the wheel along the suspension.
* @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis.
* @param {Body} [options.body] The wheel body.
*/
RigidVehicle.prototype.addWheel = function(options){
options = options || {};
var wheelBody = options.body;
if(!wheelBody){
wheelBody = new Body(1, new Sphere(1.2));
}
this.wheelBodies.push(wheelBody);
this.wheelForces.push(0);
// Position constrain wheels
var zero = new Vec3();
var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3();
// Set position locally to the chassis
var worldPosition = new Vec3();
this.chassisBody.pointToWorldFrame(position, worldPosition);
wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z);
// Constrain wheel
var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0);
this.wheelAxes.push(axis);
var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, {
pivotA: position,
axisA: axis,
pivotB: Vec3.ZERO,
axisB: axis,
collideConnected: false
});
this.constraints.push(hingeConstraint);
return this.wheelBodies.length - 1;
};
/**
* Set the steering value of a wheel.
* @method setSteeringValue
* @param {number} value
* @param {integer} wheelIndex
* @todo check coordinateSystem
*/
RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){
// Set angle of the hinge axis
var axis = this.wheelAxes[wheelIndex];
var c = Math.cos(value),
s = Math.sin(value),
x = axis.x,
y = axis.y;
this.constraints[wheelIndex].axisA.set(
c*x -s*y,
s*x +c*y,
0
);
};
/**
* Set the target rotational speed of the hinge constraint.
* @method setMotorSpeed
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){
var hingeConstraint = this.constraints[wheelIndex];
hingeConstraint.enableMotor();
hingeConstraint.motorTargetVelocity = value;
};
/**
* Set the target rotational speed of the hinge constraint.
* @method disableMotor
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.disableMotor = function(wheelIndex){
var hingeConstraint = this.constraints[wheelIndex];
hingeConstraint.disableMotor();
};
var torque = new Vec3();
/**
* Set the wheel force to apply on one of the wheels each time step
* @method setWheelForce
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){
this.wheelForces[wheelIndex] = value;
};
/**
* Apply a torque on one of the wheels.
* @method applyWheelForce
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){
var axis = this.wheelAxes[wheelIndex];
var wheelBody = this.wheelBodies[wheelIndex];
var bodyTorque = wheelBody.torque;
axis.scale(value, torque);
wheelBody.vectorToWorldFrame(torque, torque);
bodyTorque.vadd(torque, bodyTorque);
};
/**
* Add the vehicle including its constraints to the world.
* @method addToWorld
* @param {World} world
*/
RigidVehicle.prototype.addToWorld = function(world){
var constraints = this.constraints;
var bodies = this.wheelBodies.concat([this.chassisBody]);
for (var i = 0; i < bodies.length; i++) {
world.addBody(bodies[i]);
}
for (var i = 0; i < constraints.length; i++) {
world.addConstraint(constraints[i]);
}
world.addEventListener('preStep', this._update.bind(this));
};
RigidVehicle.prototype._update = function(){
var wheelForces = this.wheelForces;
for (var i = 0; i < wheelForces.length; i++) {
this.applyWheelForce(wheelForces[i], i);
}
};
/**
* Remove the vehicle including its constraints from the world.
* @method removeFromWorld
* @param {World} world
*/
RigidVehicle.prototype.removeFromWorld = function(world){
var constraints = this.constraints;
var bodies = this.wheelBodies.concat([this.chassisBody]);
for (var i = 0; i < bodies.length; i++) {
world.remove(bodies[i]);
}
for (var i = 0; i < constraints.length; i++) {
world.removeConstraint(constraints[i]);
}
};
var worldAxis = new Vec3();
/**
* Get current rotational velocity of a wheel
* @method getWheelSpeed
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){
var axis = this.wheelAxes[wheelIndex];
var wheelBody = this.wheelBodies[wheelIndex];
var w = wheelBody.angularVelocity;
this.chassisBody.vectorToWorldFrame(axis, worldAxis);
return w.dot(worldAxis);
};
},{"../constraints/HingeConstraint":59,"../math/Vec3":74,"../shapes/Box":81,"../shapes/Sphere":88,"./Body":75}],78:[function(require,module,exports){
module.exports = SPHSystem;
var Shape = require('../shapes/Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Particle = require('../shapes/Particle');
var Body = require('../objects/Body');
var Material = require('../material/Material');
/**
* Smoothed-particle hydrodynamics system
* @class SPHSystem
* @constructor
*/
function SPHSystem(){
this.particles = [];
/**
* Density of the system (kg/m3).
* @property {number} density
*/
this.density = 1;
/**
* Distance below which two particles are considered to be neighbors.
* It should be adjusted so there are about 15-20 neighbor particles within this radius.
* @property {number} smoothingRadius
*/
this.smoothingRadius = 1;
this.speedOfSound = 1;
/**
* Viscosity of the system.
* @property {number} viscosity
*/
this.viscosity = 0.01;
this.eps = 0.000001;
// Stuff Computed per particle
this.pressures = [];
this.densities = [];
this.neighbors = [];
}
/**
* Add a particle to the system.
* @method add
* @param {Body} particle
*/
SPHSystem.prototype.add = function(particle){
this.particles.push(particle);
if(this.neighbors.length < this.particles.length){
this.neighbors.push([]);
}
};
/**
* Remove a particle from the system.
* @method remove
* @param {Body} particle
*/
SPHSystem.prototype.remove = function(particle){
var idx = this.particles.indexOf(particle);
if(idx !== -1){
this.particles.splice(idx,1);
if(this.neighbors.length > this.particles.length){
this.neighbors.pop();
}
}
};
/**
* Get neighbors within smoothing volume, save in the array neighbors
* @method getNeighbors
* @param {Body} particle
* @param {Array} neighbors
*/
var SPHSystem_getNeighbors_dist = new Vec3();
SPHSystem.prototype.getNeighbors = function(particle,neighbors){
var N = this.particles.length,
id = particle.id,
R2 = this.smoothingRadius * this.smoothingRadius,
dist = SPHSystem_getNeighbors_dist;
for(var i=0; i!==N; i++){
var p = this.particles[i];
p.position.vsub(particle.position,dist);
if(id!==p.id && dist.norm2() < R2){
neighbors.push(p);
}
}
};
// Temp vectors for calculation
var SPHSystem_update_dist = new Vec3(),
SPHSystem_update_a_pressure = new Vec3(),
SPHSystem_update_a_visc = new Vec3(),
SPHSystem_update_gradW = new Vec3(),
SPHSystem_update_r_vec = new Vec3(),
SPHSystem_update_u = new Vec3(); // Relative velocity
SPHSystem.prototype.update = function(){
var N = this.particles.length,
dist = SPHSystem_update_dist,
cs = this.speedOfSound,
eps = this.eps;
for(var i=0; i!==N; i++){
var p = this.particles[i]; // Current particle
var neighbors = this.neighbors[i];
// Get neighbors
neighbors.length = 0;
this.getNeighbors(p,neighbors);
neighbors.push(this.particles[i]); // Add current too
var numNeighbors = neighbors.length;
// Accumulate density for the particle
var sum = 0.0;
for(var j=0; j!==numNeighbors; j++){
//printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z());
p.position.vsub(neighbors[j].position, dist);
var len = dist.norm();
var weight = this.w(len);
sum += neighbors[j].mass * weight;
}
// Save
this.densities[i] = sum;
this.pressures[i] = cs * cs * (this.densities[i] - this.density);
}
// Add forces
// Sum to these accelerations
var a_pressure= SPHSystem_update_a_pressure;
var a_visc = SPHSystem_update_a_visc;
var gradW = SPHSystem_update_gradW;
var r_vec = SPHSystem_update_r_vec;
var u = SPHSystem_update_u;
for(var i=0; i!==N; i++){
var particle = this.particles[i];
a_pressure.set(0,0,0);
a_visc.set(0,0,0);
// Init vars
var Pij;
var nabla;
var Vij;
// Sum up for all other neighbors
var neighbors = this.neighbors[i];
var numNeighbors = neighbors.length;
//printf("Neighbors: ");
for(var j=0; j!==numNeighbors; j++){
var neighbor = neighbors[j];
//printf("%d ",nj);
// Get r once for all..
particle.position.vsub(neighbor.position,r_vec);
var r = r_vec.norm();
// Pressure contribution
Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps));
this.gradw(r_vec, gradW);
// Add to pressure acceleration
gradW.mult(Pij , gradW);
a_pressure.vadd(gradW, a_pressure);
// Viscosity contribution
neighbor.velocity.vsub(particle.velocity, u);
u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u );
nabla = this.nablaw(r);
u.mult(nabla,u);
// Add to viscosity acceleration
a_visc.vadd( u, a_visc );
}
// Calculate force
a_visc.mult(particle.mass, a_visc);
a_pressure.mult(particle.mass, a_pressure);
// Add force to particles
particle.force.vadd(a_visc, particle.force);
particle.force.vadd(a_pressure, particle.force);
}
};
// Calculate the weight using the W(r) weightfunction
SPHSystem.prototype.w = function(r){
// 315
var h = this.smoothingRadius;
return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3);
};
// calculate gradient of the weight function
SPHSystem.prototype.gradw = function(rVec,resultVec){
var r = rVec.norm(),
h = this.smoothingRadius;
rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec);
};
// Calculate nabla(W)
SPHSystem.prototype.nablaw = function(r){
var h = this.smoothingRadius;
var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h);
return nabla;
};
},{"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Particle":85,"../shapes/Shape":87}],79:[function(require,module,exports){
var Vec3 = require('../math/Vec3');
module.exports = Spring;
/**
* A spring, connecting two bodies.
*
* @class Spring
* @constructor
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} [options]
* @param {number} [options.restLength] A number > 0. Default: 1
* @param {number} [options.stiffness] A number >= 0. Default: 100
* @param {number} [options.damping] A number >= 0. Default: 1
* @param {Vec3} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates.
* @param {Vec3} [options.worldAnchorB]
* @param {Vec3} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates.
* @param {Vec3} [options.localAnchorB]
*/
function Spring(bodyA,bodyB,options){
options = options || {};
/**
* Rest length of the spring.
* @property restLength
* @type {number}
*/
this.restLength = typeof(options.restLength) === "number" ? options.restLength : 1;
/**
* Stiffness of the spring.
* @property stiffness
* @type {number}
*/
this.stiffness = options.stiffness || 100;
/**
* Damping of the spring.
* @property damping
* @type {number}
*/
this.damping = options.damping || 1;
/**
* First connected body.
* @property bodyA
* @type {Body}
*/
this.bodyA = bodyA;
/**
* Second connected body.
* @property bodyB
* @type {Body}
*/
this.bodyB = bodyB;
/**
* Anchor for bodyA in local bodyA coordinates.
* @property localAnchorA
* @type {Vec3}
*/
this.localAnchorA = new Vec3();
/**
* Anchor for bodyB in local bodyB coordinates.
* @property localAnchorB
* @type {Vec3}
*/
this.localAnchorB = new Vec3();
if(options.localAnchorA){
this.localAnchorA.copy(options.localAnchorA);
}
if(options.localAnchorB){
this.localAnchorB.copy(options.localAnchorB);
}
if(options.worldAnchorA){
this.setWorldAnchorA(options.worldAnchorA);
}
if(options.worldAnchorB){
this.setWorldAnchorB(options.worldAnchorB);
}
}
/**
* Set the anchor point on body A, using world coordinates.
* @method setWorldAnchorA
* @param {Vec3} worldAnchorA
*/
Spring.prototype.setWorldAnchorA = function(worldAnchorA){
this.bodyA.pointToLocalFrame(worldAnchorA,this.localAnchorA);
};
/**
* Set the anchor point on body B, using world coordinates.
* @method setWorldAnchorB
* @param {Vec3} worldAnchorB
*/
Spring.prototype.setWorldAnchorB = function(worldAnchorB){
this.bodyB.pointToLocalFrame(worldAnchorB,this.localAnchorB);
};
/**
* Get the anchor point on body A, in world coordinates.
* @method getWorldAnchorA
* @param {Vec3} result The vector to store the result in.
*/
Spring.prototype.getWorldAnchorA = function(result){
this.bodyA.pointToWorldFrame(this.localAnchorA,result);
};
/**
* Get the anchor point on body B, in world coordinates.
* @method getWorldAnchorB
* @param {Vec3} result The vector to store the result in.
*/
Spring.prototype.getWorldAnchorB = function(result){
this.bodyB.pointToWorldFrame(this.localAnchorB,result);
};
var applyForce_r = new Vec3(),
applyForce_r_unit = new Vec3(),
applyForce_u = new Vec3(),
applyForce_f = new Vec3(),
applyForce_worldAnchorA = new Vec3(),
applyForce_worldAnchorB = new Vec3(),
applyForce_ri = new Vec3(),
applyForce_rj = new Vec3(),
applyForce_ri_x_f = new Vec3(),
applyForce_rj_x_f = new Vec3(),
applyForce_tmp = new Vec3();
/**
* Apply the spring force to the connected bodies.
* @method applyForce
*/
Spring.prototype.applyForce = function(){
var k = this.stiffness,
d = this.damping,
l = this.restLength,
bodyA = this.bodyA,
bodyB = this.bodyB,
r = applyForce_r,
r_unit = applyForce_r_unit,
u = applyForce_u,
f = applyForce_f,
tmp = applyForce_tmp;
var worldAnchorA = applyForce_worldAnchorA,
worldAnchorB = applyForce_worldAnchorB,
ri = applyForce_ri,
rj = applyForce_rj,
ri_x_f = applyForce_ri_x_f,
rj_x_f = applyForce_rj_x_f;
// Get world anchors
this.getWorldAnchorA(worldAnchorA);
this.getWorldAnchorB(worldAnchorB);
// Get offset points
worldAnchorA.vsub(bodyA.position,ri);
worldAnchorB.vsub(bodyB.position,rj);
// Compute distance vector between world anchor points
worldAnchorB.vsub(worldAnchorA,r);
var rlen = r.norm();
r_unit.copy(r);
r_unit.normalize();
// Compute relative velocity of the anchor points, u
bodyB.velocity.vsub(bodyA.velocity,u);
// Add rotational velocity
bodyB.angularVelocity.cross(rj,tmp);
u.vadd(tmp,u);
bodyA.angularVelocity.cross(ri,tmp);
u.vsub(tmp,u);
// F = - k * ( x - L ) - D * ( u )
r_unit.mult(-k*(rlen-l) - d*u.dot(r_unit), f);
// Add forces to bodies
bodyA.force.vsub(f,bodyA.force);
bodyB.force.vadd(f,bodyB.force);
// Angular force
ri.cross(f,ri_x_f);
rj.cross(f,rj_x_f);
bodyA.torque.vsub(ri_x_f,bodyA.torque);
bodyB.torque.vadd(rj_x_f,bodyB.torque);
};
},{"../math/Vec3":74}],80:[function(require,module,exports){
var Vec3 = require('../math/Vec3');
var Transform = require('../math/Transform');
var RaycastResult = require('../collision/RaycastResult');
var Utils = require('../utils/Utils');
module.exports = WheelInfo;
/**
* @class WheelInfo
* @constructor
* @param {Object} [options]
*
* @param {Vec3} [options.chassisConnectionPointLocal]
* @param {Vec3} [options.chassisConnectionPointWorld]
* @param {Vec3} [options.directionLocal]
* @param {Vec3} [options.directionWorld]
* @param {Vec3} [options.axleLocal]
* @param {Vec3} [options.axleWorld]
* @param {number} [options.suspensionRestLength=1]
* @param {number} [options.suspensionMaxLength=2]
* @param {number} [options.radius=1]
* @param {number} [options.suspensionStiffness=100]
* @param {number} [options.dampingCompression=10]
* @param {number} [options.dampingRelaxation=10]
* @param {number} [options.frictionSlip=10000]
* @param {number} [options.steering=0]
* @param {number} [options.rotation=0]
* @param {number} [options.deltaRotation=0]
* @param {number} [options.rollInfluence=0.01]
* @param {number} [options.maxSuspensionForce]
* @param {boolean} [options.isFrontWheel=true]
* @param {number} [options.clippedInvContactDotSuspension=1]
* @param {number} [options.suspensionRelativeVelocity=0]
* @param {number} [options.suspensionForce=0]
* @param {number} [options.skidInfo=0]
* @param {number} [options.suspensionLength=0]
* @param {number} [options.maxSuspensionTravel=1]
* @param {boolean} [options.useCustomSlidingRotationalSpeed=false]
* @param {number} [options.customSlidingRotationalSpeed=-0.1]
*/
function WheelInfo(options){
options = Utils.defaults(options, {
chassisConnectionPointLocal: new Vec3(),
chassisConnectionPointWorld: new Vec3(),
directionLocal: new Vec3(),
directionWorld: new Vec3(),
axleLocal: new Vec3(),
axleWorld: new Vec3(),
suspensionRestLength: 1,
suspensionMaxLength: 2,
radius: 1,
suspensionStiffness: 100,
dampingCompression: 10,
dampingRelaxation: 10,
frictionSlip: 10000,
steering: 0,
rotation: 0,
deltaRotation: 0,
rollInfluence: 0.01,
maxSuspensionForce: Number.MAX_VALUE,
isFrontWheel: true,
clippedInvContactDotSuspension: 1,
suspensionRelativeVelocity: 0,
suspensionForce: 0,
skidInfo: 0,
suspensionLength: 0,
maxSuspensionTravel: 1,
useCustomSlidingRotationalSpeed: false,
customSlidingRotationalSpeed: -0.1
});
/**
* Max travel distance of the suspension, in meters.
* @property {number} maxSuspensionTravel
*/
this.maxSuspensionTravel = options.maxSuspensionTravel;
/**
* Speed to apply to the wheel rotation when the wheel is sliding.
* @property {number} customSlidingRotationalSpeed
*/
this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed;
/**
* If the customSlidingRotationalSpeed should be used.
* @property {Boolean} useCustomSlidingRotationalSpeed
*/
this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed;
/**
* @property {Boolean} sliding
*/
this.sliding = false;
/**
* Connection point, defined locally in the chassis body frame.
* @property {Vec3} chassisConnectionPointLocal
*/
this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone();
/**
* @property {Vec3} chassisConnectionPointWorld
*/
this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone();
/**
* @property {Vec3} directionLocal
*/
this.directionLocal = options.directionLocal.clone();
/**
* @property {Vec3} directionWorld
*/
this.directionWorld = options.directionWorld.clone();
/**
* @property {Vec3} axleLocal
*/
this.axleLocal = options.axleLocal.clone();
/**
* @property {Vec3} axleWorld
*/
this.axleWorld = options.axleWorld.clone();
/**
* @property {number} suspensionRestLength
*/
this.suspensionRestLength = options.suspensionRestLength;
/**
* @property {number} suspensionMaxLength
*/
this.suspensionMaxLength = options.suspensionMaxLength;
/**
* @property {number} radius
*/
this.radius = options.radius;
/**
* @property {number} suspensionStiffness
*/
this.suspensionStiffness = options.suspensionStiffness;
/**
* @property {number} dampingCompression
*/
this.dampingCompression = options.dampingCompression;
/**
* @property {number} dampingRelaxation
*/
this.dampingRelaxation = options.dampingRelaxation;
/**
* @property {number} frictionSlip
*/
this.frictionSlip = options.frictionSlip;
/**
* @property {number} steering
*/
this.steering = 0;
/**
* Rotation value, in radians.
* @property {number} rotation
*/
this.rotation = 0;
/**
* @property {number} deltaRotation
*/
this.deltaRotation = 0;
/**
* @property {number} rollInfluence
*/
this.rollInfluence = options.rollInfluence;
/**
* @property {number} maxSuspensionForce
*/
this.maxSuspensionForce = options.maxSuspensionForce;
/**
* @property {number} engineForce
*/
this.engineForce = 0;
/**
* @property {number} brake
*/
this.brake = 0;
/**
* @property {number} isFrontWheel
*/
this.isFrontWheel = options.isFrontWheel;
/**
* @property {number} clippedInvContactDotSuspension
*/
this.clippedInvContactDotSuspension = 1;
/**
* @property {number} suspensionRelativeVelocity
*/
this.suspensionRelativeVelocity = 0;
/**
* @property {number} suspensionForce
*/
this.suspensionForce = 0;
/**
* @property {number} skidInfo
*/
this.skidInfo = 0;
/**
* @property {number} suspensionLength
*/
this.suspensionLength = 0;
/**
* @property {number} sideImpulse
*/
this.sideImpulse = 0;
/**
* @property {number} forwardImpulse
*/
this.forwardImpulse = 0;
/**
* The result from raycasting
* @property {RaycastResult} raycastResult
*/
this.raycastResult = new RaycastResult();
/**
* Wheel world transform
* @property {Transform} worldTransform
*/
this.worldTransform = new Transform();
/**
* @property {boolean} isInContact
*/
this.isInContact = false;
}
var chassis_velocity_at_contactPoint = new Vec3();
var relpos = new Vec3();
var chassis_velocity_at_contactPoint = new Vec3();
WheelInfo.prototype.updateWheel = function(chassis){
var raycastResult = this.raycastResult;
if (this.isInContact){
var project= raycastResult.hitNormalWorld.dot(raycastResult.directionWorld);
raycastResult.hitPointWorld.vsub(chassis.position, relpos);
chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint);
var projVel = raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint );
if (project >= -0.1) {
this.suspensionRelativeVelocity = 0.0;
this.clippedInvContactDotSuspension = 1.0 / 0.1;
} else {
var inv = -1 / project;
this.suspensionRelativeVelocity = projVel * inv;
this.clippedInvContactDotSuspension = inv;
}
} else {
// Not in contact : position wheel in a nice (rest length) position
raycastResult.suspensionLength = this.suspensionRestLength;
this.suspensionRelativeVelocity = 0.0;
raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld);
this.clippedInvContactDotSuspension = 1.0;
}
};
},{"../collision/RaycastResult":54,"../math/Transform":73,"../math/Vec3":74,"../utils/Utils":97}],81:[function(require,module,exports){
module.exports = Box;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
var ConvexPolyhedron = require('./ConvexPolyhedron');
/**
* A 3d box shape.
* @class Box
* @constructor
* @param {Vec3} halfExtents
* @author schteppe
* @extends Shape
*/
function Box(halfExtents){
Shape.call(this, {
type: Shape.types.BOX
});
/**
* @property halfExtents
* @type {Vec3}
*/
this.halfExtents = halfExtents;
/**
* Used by the contact generator to make contacts with other convex polyhedra for example
* @property convexPolyhedronRepresentation
* @type {ConvexPolyhedron}
*/
this.convexPolyhedronRepresentation = null;
this.updateConvexPolyhedronRepresentation();
this.updateBoundingSphereRadius();
}
Box.prototype = new Shape();
Box.prototype.constructor = Box;
/**
* Updates the local convex polyhedron representation used for some collisions.
* @method updateConvexPolyhedronRepresentation
*/
Box.prototype.updateConvexPolyhedronRepresentation = function(){
var sx = this.halfExtents.x;
var sy = this.halfExtents.y;
var sz = this.halfExtents.z;
var V = Vec3;
var vertices = [
new V(-sx,-sy,-sz),
new V( sx,-sy,-sz),
new V( sx, sy,-sz),
new V(-sx, sy,-sz),
new V(-sx,-sy, sz),
new V( sx,-sy, sz),
new V( sx, sy, sz),
new V(-sx, sy, sz)
];
var indices = [
[3,2,1,0], // -z
[4,5,6,7], // +z
[5,4,0,1], // -y
[2,3,7,6], // +y
[0,4,7,3], // -x
[1,2,6,5], // +x
];
var axes = [
new V(0, 0, 1),
new V(0, 1, 0),
new V(1, 0, 0)
];
var h = new ConvexPolyhedron(vertices, indices);
this.convexPolyhedronRepresentation = h;
h.material = this.material;
};
/**
* @method calculateLocalInertia
* @param {Number} mass
* @param {Vec3} target
* @return {Vec3}
*/
Box.prototype.calculateLocalInertia = function(mass,target){
target = target || new Vec3();
Box.calculateInertia(this.halfExtents, mass, target);
return target;
};
Box.calculateInertia = function(halfExtents,mass,target){
var e = halfExtents;
target.x = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.z*2*e.z );
target.y = 1.0 / 12.0 * mass * ( 2*e.x*2*e.x + 2*e.z*2*e.z );
target.z = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.x*2*e.x );
};
/**
* Get the box 6 side normals
* @method getSideNormals
* @param {array} sixTargetVectors An array of 6 vectors, to store the resulting side normals in.
* @param {Quaternion} quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame.
* @return {array}
*/
Box.prototype.getSideNormals = function(sixTargetVectors,quat){
var sides = sixTargetVectors;
var ex = this.halfExtents;
sides[0].set( ex.x, 0, 0);
sides[1].set( 0, ex.y, 0);
sides[2].set( 0, 0, ex.z);
sides[3].set( -ex.x, 0, 0);
sides[4].set( 0, -ex.y, 0);
sides[5].set( 0, 0, -ex.z);
if(quat!==undefined){
for(var i=0; i!==sides.length; i++){
quat.vmult(sides[i],sides[i]);
}
}
return sides;
};
Box.prototype.volume = function(){
return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z;
};
Box.prototype.updateBoundingSphereRadius = function(){
this.boundingSphereRadius = this.halfExtents.norm();
};
var worldCornerTempPos = new Vec3();
var worldCornerTempNeg = new Vec3();
Box.prototype.forEachWorldCorner = function(pos,quat,callback){
var e = this.halfExtents;
var corners = [[ e.x, e.y, e.z],
[ -e.x, e.y, e.z],
[ -e.x, -e.y, e.z],
[ -e.x, -e.y, -e.z],
[ e.x, -e.y, -e.z],
[ e.x, e.y, -e.z],
[ -e.x, e.y, -e.z],
[ e.x, -e.y, e.z]];
for(var i=0; i<corners.length; i++){
worldCornerTempPos.set(corners[i][0],corners[i][1],corners[i][2]);
quat.vmult(worldCornerTempPos,worldCornerTempPos);
pos.vadd(worldCornerTempPos,worldCornerTempPos);
callback(worldCornerTempPos.x,
worldCornerTempPos.y,
worldCornerTempPos.z);
}
};
var worldCornersTemp = [
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3(),
new Vec3()
];
Box.prototype.calculateWorldAABB = function(pos,quat,min,max){
var e = this.halfExtents;
worldCornersTemp[0].set(e.x, e.y, e.z);
worldCornersTemp[1].set(-e.x, e.y, e.z);
worldCornersTemp[2].set(-e.x, -e.y, e.z);
worldCornersTemp[3].set(-e.x, -e.y, -e.z);
worldCornersTemp[4].set(e.x, -e.y, -e.z);
worldCornersTemp[5].set(e.x, e.y, -e.z);
worldCornersTemp[6].set(-e.x, e.y, -e.z);
worldCornersTemp[7].set(e.x, -e.y, e.z);
var wc = worldCornersTemp[0];
quat.vmult(wc, wc);
pos.vadd(wc, wc);
max.copy(wc);
min.copy(wc);
for(var i=1; i<8; i++){
var wc = worldCornersTemp[i];
quat.vmult(wc, wc);
pos.vadd(wc, wc);
var x = wc.x;
var y = wc.y;
var z = wc.z;
if(x > max.x){
max.x = x;
}
if(y > max.y){
max.y = y;
}
if(z > max.z){
max.z = z;
}
if(x < min.x){
min.x = x;
}
if(y < min.y){
min.y = y;
}
if(z < min.z){
min.z = z;
}
}
// Get each axis max
// min.set(Infinity,Infinity,Infinity);
// max.set(-Infinity,-Infinity,-Infinity);
// this.forEachWorldCorner(pos,quat,function(x,y,z){
// if(x > max.x){
// max.x = x;
// }
// if(y > max.y){
// max.y = y;
// }
// if(z > max.z){
// max.z = z;
// }
// if(x < min.x){
// min.x = x;
// }
// if(y < min.y){
// min.y = y;
// }
// if(z < min.z){
// min.z = z;
// }
// });
};
},{"../math/Vec3":74,"./ConvexPolyhedron":82,"./Shape":87}],82:[function(require,module,exports){
module.exports = ConvexPolyhedron;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Transform = require('../math/Transform');
/**
* A set of polygons describing a convex shape.
* @class ConvexPolyhedron
* @constructor
* @extends Shape
* @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained
* in the same 3D plane), instead these should be merged into one polygon.
*
* @param {array} points An array of Vec3's
* @param {array} faces Array of integer arrays, describing which vertices that is included in each face.
*
* @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f)
* @author schteppe / https://github.com/schteppe
* @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/
* @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
*
* @todo Move the clipping functions to ContactGenerator?
* @todo Automatically merge coplanar polygons in constructor.
*/
function ConvexPolyhedron(points, faces, uniqueAxes) {
Shape.call(this, {
type: Shape.types.CONVEXPOLYHEDRON
});
/**
* Array of Vec3
* @property vertices
* @type {Array}
*/
this.vertices = points||[];
this.worldVertices = []; // World transformed version of .vertices
this.worldVerticesNeedsUpdate = true;
/**
* Array of integer arrays, indicating which vertices each face consists of
* @property faces
* @type {Array}
*/
this.faces = faces||[];
/**
* Array of Vec3
* @property faceNormals
* @type {Array}
*/
this.faceNormals = [];
this.computeNormals();
this.worldFaceNormalsNeedsUpdate = true;
this.worldFaceNormals = []; // World transformed version of .faceNormals
/**
* Array of Vec3
* @property uniqueEdges
* @type {Array}
*/
this.uniqueEdges = [];
/**
* If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.
* @property {Array} uniqueAxes
*/
this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null;
this.computeEdges();
this.updateBoundingSphereRadius();
}
ConvexPolyhedron.prototype = new Shape();
ConvexPolyhedron.prototype.constructor = ConvexPolyhedron;
var computeEdges_tmpEdge = new Vec3();
/**
* Computes uniqueEdges
* @method computeEdges
*/
ConvexPolyhedron.prototype.computeEdges = function(){
var faces = this.faces;
var vertices = this.vertices;
var nv = vertices.length;
var edges = this.uniqueEdges;
edges.length = 0;
var edge = computeEdges_tmpEdge;
for(var i=0; i !== faces.length; i++){
var face = faces[i];
var numVertices = face.length;
for(var j = 0; j !== numVertices; j++){
var k = ( j+1 ) % numVertices;
vertices[face[j]].vsub(vertices[face[k]], edge);
edge.normalize();
var found = false;
for(var p=0; p !== edges.length; p++){
if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){
found = true;
break;
}
}
if (!found){
edges.push(edge.clone());
}
}
}
};
/**
* Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.
* @method computeNormals
*/
ConvexPolyhedron.prototype.computeNormals = function(){
this.faceNormals.length = this.faces.length;
// Generate normals
for(var i=0; i<this.faces.length; i++){
// Check so all vertices exists for this face
for(var j=0; j<this.faces[i].length; j++){
if(!this.vertices[this.faces[i][j]]){
throw new Error("Vertex "+this.faces[i][j]+" not found!");
}
}
var n = this.faceNormals[i] || new Vec3();
this.getFaceNormal(i,n);
n.negate(n);
this.faceNormals[i] = n;
var vertex = this.vertices[this.faces[i][0]];
if(n.dot(vertex) < 0){
console.error(".faceNormals[" + i + "] = Vec3("+n.toString()+") looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.");
for(var j=0; j<this.faces[i].length; j++){
console.warn(".vertices["+this.faces[i][j]+"] = Vec3("+this.vertices[this.faces[i][j]].toString()+")");
}
}
}
};
/**
* Get face normal given 3 vertices
* @static
* @method getFaceNormal
* @param {Vec3} va
* @param {Vec3} vb
* @param {Vec3} vc
* @param {Vec3} target
*/
var cb = new Vec3();
var ab = new Vec3();
ConvexPolyhedron.computeNormal = function ( va, vb, vc, target ) {
vb.vsub(va,ab);
vc.vsub(vb,cb);
cb.cross(ab,target);
if ( !target.isZero() ) {
target.normalize();
}
};
/**
* Compute the normal of a face from its vertices
* @method getFaceNormal
* @param {Number} i
* @param {Vec3} target
*/
ConvexPolyhedron.prototype.getFaceNormal = function(i,target){
var f = this.faces[i];
var va = this.vertices[f[0]];
var vb = this.vertices[f[1]];
var vc = this.vertices[f[2]];
return ConvexPolyhedron.computeNormal(va,vb,vc,target);
};
/**
* @method clipAgainstHull
* @param {Vec3} posA
* @param {Quaternion} quatA
* @param {ConvexPolyhedron} hullB
* @param {Vec3} posB
* @param {Quaternion} quatB
* @param {Vec3} separatingNormal
* @param {Number} minDist Clamp distance
* @param {Number} maxDist
* @param {array} result The an array of contact point objects, see clipFaceAgainstHull
* @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
*/
var cah_WorldNormal = new Vec3();
ConvexPolyhedron.prototype.clipAgainstHull = function(posA,quatA,hullB,posB,quatB,separatingNormal,minDist,maxDist,result){
var WorldNormal = cah_WorldNormal;
var hullA = this;
var curMaxDist = maxDist;
var closestFaceB = -1;
var dmax = -Number.MAX_VALUE;
for(var face=0; face < hullB.faces.length; face++){
WorldNormal.copy(hullB.faceNormals[face]);
quatB.vmult(WorldNormal,WorldNormal);
//posB.vadd(WorldNormal,WorldNormal);
var d = WorldNormal.dot(separatingNormal);
if (d > dmax){
dmax = d;
closestFaceB = face;
}
}
var worldVertsB1 = [];
var polyB = hullB.faces[closestFaceB];
var numVertices = polyB.length;
for(var e0=0; e0<numVertices; e0++){
var b = hullB.vertices[polyB[e0]];
var worldb = new Vec3();
worldb.copy(b);
quatB.vmult(worldb,worldb);
posB.vadd(worldb,worldb);
worldVertsB1.push(worldb);
}
if (closestFaceB>=0){
this.clipFaceAgainstHull(separatingNormal,
posA,
quatA,
worldVertsB1,
minDist,
maxDist,
result);
}
};
/**
* Find the separating axis between this hull and another
* @method findSeparatingAxis
* @param {ConvexPolyhedron} hullB
* @param {Vec3} posA
* @param {Quaternion} quatA
* @param {Vec3} posB
* @param {Quaternion} quatB
* @param {Vec3} target The target vector to save the axis in
* @return {bool} Returns false if a separation is found, else true
*/
var fsa_faceANormalWS3 = new Vec3(),
fsa_Worldnormal1 = new Vec3(),
fsa_deltaC = new Vec3(),
fsa_worldEdge0 = new Vec3(),
fsa_worldEdge1 = new Vec3(),
fsa_Cross = new Vec3();
ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){
var faceANormalWS3 = fsa_faceANormalWS3,
Worldnormal1 = fsa_Worldnormal1,
deltaC = fsa_deltaC,
worldEdge0 = fsa_worldEdge0,
worldEdge1 = fsa_worldEdge1,
Cross = fsa_Cross;
var dmin = Number.MAX_VALUE;
var hullA = this;
var curPlaneTests=0;
if(!hullA.uniqueAxes){
var numFacesA = faceListA ? faceListA.length : hullA.faces.length;
// Test face normals from hullA
for(var i=0; i<numFacesA; i++){
var fi = faceListA ? faceListA[i] : i;
// Get world face normal
faceANormalWS3.copy(hullA.faceNormals[fi]);
quatA.vmult(faceANormalWS3,faceANormalWS3);
var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
if(d===false){
return false;
}
if(d<dmin){
dmin = d;
target.copy(faceANormalWS3);
}
}
} else {
// Test unique axes
for(var i = 0; i !== hullA.uniqueAxes.length; i++){
// Get world axis
quatA.vmult(hullA.uniqueAxes[i],faceANormalWS3);
var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
if(d===false){
return false;
}
if(d<dmin){
dmin = d;
target.copy(faceANormalWS3);
}
}
}
if(!hullB.uniqueAxes){
// Test face normals from hullB
var numFacesB = faceListB ? faceListB.length : hullB.faces.length;
for(var i=0;i<numFacesB;i++){
var fi = faceListB ? faceListB[i] : i;
Worldnormal1.copy(hullB.faceNormals[fi]);
quatB.vmult(Worldnormal1,Worldnormal1);
curPlaneTests++;
var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
if(d===false){
return false;
}
if(d<dmin){
dmin = d;
target.copy(Worldnormal1);
}
}
} else {
// Test unique axes in B
for(var i = 0; i !== hullB.uniqueAxes.length; i++){
quatB.vmult(hullB.uniqueAxes[i],Worldnormal1);
curPlaneTests++;
var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
if(d===false){
return false;
}
if(d<dmin){
dmin = d;
target.copy(Worldnormal1);
}
}
}
// Test edges
for(var e0=0; e0 !== hullA.uniqueEdges.length; e0++){
// Get world edge
quatA.vmult(hullA.uniqueEdges[e0],worldEdge0);
for(var e1=0; e1 !== hullB.uniqueEdges.length; e1++){
// Get world edge 2
quatB.vmult(hullB.uniqueEdges[e1], worldEdge1);
worldEdge0.cross(worldEdge1,Cross);
if(!Cross.almostZero()){
Cross.normalize();
var dist = hullA.testSepAxis(Cross, hullB, posA, quatA, posB, quatB);
if(dist === false){
return false;
}
if(dist < dmin){
dmin = dist;
target.copy(Cross);
}
}
}
}
posB.vsub(posA,deltaC);
if((deltaC.dot(target))>0.0){
target.negate(target);
}
return true;
};
var maxminA=[], maxminB=[];
/**
* Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.
* @method testSepAxis
* @param {Vec3} axis
* @param {ConvexPolyhedron} hullB
* @param {Vec3} posA
* @param {Quaternion} quatA
* @param {Vec3} posB
* @param {Quaternion} quatB
* @return {number} The overlap depth, or FALSE if no penetration.
*/
ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){
var hullA=this;
ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA);
ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB);
var maxA = maxminA[0];
var minA = maxminA[1];
var maxB = maxminB[0];
var minB = maxminB[1];
if(maxA<minB || maxB<minA){
return false; // Separated
}
var d0 = maxA - minB;
var d1 = maxB - minA;
var depth = d0<d1 ? d0:d1;
return depth;
};
var cli_aabbmin = new Vec3(),
cli_aabbmax = new Vec3();
/**
* @method calculateLocalInertia
* @param {Number} mass
* @param {Vec3} target
*/
ConvexPolyhedron.prototype.calculateLocalInertia = function(mass,target){
// Approximate with box inertia
// Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it
this.computeLocalAABB(cli_aabbmin,cli_aabbmax);
var x = cli_aabbmax.x - cli_aabbmin.x,
y = cli_aabbmax.y - cli_aabbmin.y,
z = cli_aabbmax.z - cli_aabbmin.z;
target.x = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*z*2*z );
target.y = 1.0 / 12.0 * mass * ( 2*x*2*x + 2*z*2*z );
target.z = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*x*2*x );
};
/**
* @method getPlaneConstantOfFace
* @param {Number} face_i Index of the face
* @return {Number}
*/
ConvexPolyhedron.prototype.getPlaneConstantOfFace = function(face_i){
var f = this.faces[face_i];
var n = this.faceNormals[face_i];
var v = this.vertices[f[0]];
var c = -n.dot(v);
return c;
};
/**
* Clip a face against a hull.
* @method clipFaceAgainstHull
* @param {Vec3} separatingNormal
* @param {Vec3} posA
* @param {Quaternion} quatA
* @param {Array} worldVertsB1 An array of Vec3 with vertices in the world frame.
* @param {Number} minDist Distance clamping
* @param {Number} maxDist
* @param Array result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates.
*/
var cfah_faceANormalWS = new Vec3(),
cfah_edge0 = new Vec3(),
cfah_WorldEdge0 = new Vec3(),
cfah_worldPlaneAnormal1 = new Vec3(),
cfah_planeNormalWS1 = new Vec3(),
cfah_worldA1 = new Vec3(),
cfah_localPlaneNormal = new Vec3(),
cfah_planeNormalWS = new Vec3();
ConvexPolyhedron.prototype.clipFaceAgainstHull = function(separatingNormal, posA, quatA, worldVertsB1, minDist, maxDist,result){
var faceANormalWS = cfah_faceANormalWS,
edge0 = cfah_edge0,
WorldEdge0 = cfah_WorldEdge0,
worldPlaneAnormal1 = cfah_worldPlaneAnormal1,
planeNormalWS1 = cfah_planeNormalWS1,
worldA1 = cfah_worldA1,
localPlaneNormal = cfah_localPlaneNormal,
planeNormalWS = cfah_planeNormalWS;
var hullA = this;
var worldVertsB2 = [];
var pVtxIn = worldVertsB1;
var pVtxOut = worldVertsB2;
// Find the face with normal closest to the separating axis
var closestFaceA = -1;
var dmin = Number.MAX_VALUE;
for(var face=0; face<hullA.faces.length; face++){
faceANormalWS.copy(hullA.faceNormals[face]);
quatA.vmult(faceANormalWS,faceANormalWS);
//posA.vadd(faceANormalWS,faceANormalWS);
var d = faceANormalWS.dot(separatingNormal);
if (d < dmin){
dmin = d;
closestFaceA = face;
}
}
if (closestFaceA < 0){
// console.log("--- did not find any closest face... ---");
return;
}
//console.log("closest A: ",closestFaceA);
// Get the face and construct connected faces
var polyA = hullA.faces[closestFaceA];
polyA.connectedFaces = [];
for(var i=0; i<hullA.faces.length; i++){
for(var j=0; j<hullA.faces[i].length; j++){
if(polyA.indexOf(hullA.faces[i][j])!==-1 /* Sharing a vertex*/ && i!==closestFaceA /* Not the one we are looking for connections from */ && polyA.connectedFaces.indexOf(i)===-1 /* Not already added */ ){
polyA.connectedFaces.push(i);
}
}
}
// Clip the polygon to the back of the planes of all faces of hull A, that are adjacent to the witness face
var numContacts = pVtxIn.length;
var numVerticesA = polyA.length;
var res = [];
for(var e0=0; e0<numVerticesA; e0++){
var a = hullA.vertices[polyA[e0]];
var b = hullA.vertices[polyA[(e0+1)%numVerticesA]];
a.vsub(b,edge0);
WorldEdge0.copy(edge0);
quatA.vmult(WorldEdge0,WorldEdge0);
posA.vadd(WorldEdge0,WorldEdge0);
worldPlaneAnormal1.copy(this.faceNormals[closestFaceA]);//transA.getBasis()* btVector3(polyA.m_plane[0],polyA.m_plane[1],polyA.m_plane[2]);
quatA.vmult(worldPlaneAnormal1,worldPlaneAnormal1);
posA.vadd(worldPlaneAnormal1,worldPlaneAnormal1);
WorldEdge0.cross(worldPlaneAnormal1,planeNormalWS1);
planeNormalWS1.negate(planeNormalWS1);
worldA1.copy(a);
quatA.vmult(worldA1,worldA1);
posA.vadd(worldA1,worldA1);
var planeEqWS1 = -worldA1.dot(planeNormalWS1);
var planeEqWS;
if(true){
var otherFace = polyA.connectedFaces[e0];
localPlaneNormal.copy(this.faceNormals[otherFace]);
var localPlaneEq = this.getPlaneConstantOfFace(otherFace);
planeNormalWS.copy(localPlaneNormal);
quatA.vmult(planeNormalWS,planeNormalWS);
//posA.vadd(planeNormalWS,planeNormalWS);
var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
} else {
planeNormalWS.copy(planeNormalWS1);
planeEqWS = planeEqWS1;
}
// Clip face against our constructed plane
this.clipFaceAgainstPlane(pVtxIn, pVtxOut, planeNormalWS, planeEqWS);
// Throw away all clipped points, but save the reamining until next clip
while(pVtxIn.length){
pVtxIn.shift();
}
while(pVtxOut.length){
pVtxIn.push(pVtxOut.shift());
}
}
//console.log("Resulting points after clip:",pVtxIn);
// only keep contact points that are behind the witness face
localPlaneNormal.copy(this.faceNormals[closestFaceA]);
var localPlaneEq = this.getPlaneConstantOfFace(closestFaceA);
planeNormalWS.copy(localPlaneNormal);
quatA.vmult(planeNormalWS,planeNormalWS);
var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
for (var i=0; i<pVtxIn.length; i++){
var depth = planeNormalWS.dot(pVtxIn[i]) + planeEqWS; //???
/*console.log("depth calc from normal=",planeNormalWS.toString()," and constant "+planeEqWS+" and vertex ",pVtxIn[i].toString()," gives "+depth);*/
if (depth <=minDist){
console.log("clamped: depth="+depth+" to minDist="+(minDist+""));
depth = minDist;
}
if (depth <=maxDist){
var point = pVtxIn[i];
if(depth<=0){
/*console.log("Got contact point ",point.toString(),
", depth=",depth,
"contact normal=",separatingNormal.toString(),
"plane",planeNormalWS.toString(),
"planeConstant",planeEqWS);*/
var p = {
point:point,
normal:planeNormalWS,
depth: depth,
};
result.push(p);
}
}
}
};
/**
* Clip a face in a hull against the back of a plane.
* @method clipFaceAgainstPlane
* @param {Array} inVertices
* @param {Array} outVertices
* @param {Vec3} planeNormal
* @param {Number} planeConstant The constant in the mathematical plane equation
*/
ConvexPolyhedron.prototype.clipFaceAgainstPlane = function(inVertices,outVertices, planeNormal, planeConstant){
var n_dot_first, n_dot_last;
var numVerts = inVertices.length;
if(numVerts < 2){
return outVertices;
}
var firstVertex = inVertices[inVertices.length-1],
lastVertex = inVertices[0];
n_dot_first = planeNormal.dot(firstVertex) + planeConstant;
for(var vi = 0; vi < numVerts; vi++){
lastVertex = inVertices[vi];
n_dot_last = planeNormal.dot(lastVertex) + planeConstant;
if(n_dot_first < 0){
if(n_dot_last < 0){
// Start < 0, end < 0, so output lastVertex
var newv = new Vec3();
newv.copy(lastVertex);
outVertices.push(newv);
} else {
// Start < 0, end >= 0, so output intersection
var newv = new Vec3();
firstVertex.lerp(lastVertex,
n_dot_first / (n_dot_first - n_dot_last),
newv);
outVertices.push(newv);
}
} else {
if(n_dot_last<0){
// Start >= 0, end < 0 so output intersection and end
var newv = new Vec3();
firstVertex.lerp(lastVertex,
n_dot_first / (n_dot_first - n_dot_last),
newv);
outVertices.push(newv);
outVertices.push(lastVertex);
}
}
firstVertex = lastVertex;
n_dot_first = n_dot_last;
}
return outVertices;
};
// Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){
var N = this.vertices.length;
while(this.worldVertices.length < N){
this.worldVertices.push( new Vec3() );
}
var verts = this.vertices,
worldVerts = this.worldVertices;
for(var i=0; i!==N; i++){
quat.vmult( verts[i] , worldVerts[i] );
position.vadd( worldVerts[i] , worldVerts[i] );
}
this.worldVerticesNeedsUpdate = false;
};
var computeLocalAABB_worldVert = new Vec3();
ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){
var n = this.vertices.length,
vertices = this.vertices,
worldVert = computeLocalAABB_worldVert;
aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
for(var i=0; i<n; i++){
var v = vertices[i];
if (v.x < aabbmin.x){
aabbmin.x = v.x;
} else if(v.x > aabbmax.x){
aabbmax.x = v.x;
}
if (v.y < aabbmin.y){
aabbmin.y = v.y;
} else if(v.y > aabbmax.y){
aabbmax.y = v.y;
}
if (v.z < aabbmin.z){
aabbmin.z = v.z;
} else if(v.z > aabbmax.z){
aabbmax.z = v.z;
}
}
};
/**
* Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
* @method computeWorldFaceNormals
* @param {Quaternion} quat
*/
ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){
var N = this.faceNormals.length;
while(this.worldFaceNormals.length < N){
this.worldFaceNormals.push( new Vec3() );
}
var normals = this.faceNormals,
worldNormals = this.worldFaceNormals;
for(var i=0; i!==N; i++){
quat.vmult( normals[i] , worldNormals[i] );
}
this.worldFaceNormalsNeedsUpdate = false;
};
/**
* @method updateBoundingSphereRadius
*/
ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){
// Assume points are distributed with local (0,0,0) as center
var max2 = 0;
var verts = this.vertices;
for(var i=0, N=verts.length; i!==N; i++) {
var norm2 = verts[i].norm2();
if(norm2 > max2){
max2 = norm2;
}
}
this.boundingSphereRadius = Math.sqrt(max2);
};
var tempWorldVertex = new Vec3();
/**
* @method calculateWorldAABB
* @param {Vec3} pos
* @param {Quaternion} quat
* @param {Vec3} min
* @param {Vec3} max
*/
ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){
var n = this.vertices.length, verts = this.vertices;
var minx,miny,minz,maxx,maxy,maxz;
for(var i=0; i<n; i++){
tempWorldVertex.copy(verts[i]);
quat.vmult(tempWorldVertex,tempWorldVertex);
pos.vadd(tempWorldVertex,tempWorldVertex);
var v = tempWorldVertex;
if (v.x < minx || minx===undefined){
minx = v.x;
} else if(v.x > maxx || maxx===undefined){
maxx = v.x;
}
if (v.y < miny || miny===undefined){
miny = v.y;
} else if(v.y > maxy || maxy===undefined){
maxy = v.y;
}
if (v.z < minz || minz===undefined){
minz = v.z;
} else if(v.z > maxz || maxz===undefined){
maxz = v.z;
}
}
min.set(minx,miny,minz);
max.set(maxx,maxy,maxz);
};
/**
* Get approximate convex volume
* @method volume
* @return {Number}
*/
ConvexPolyhedron.prototype.volume = function(){
return 4.0 * Math.PI * this.boundingSphereRadius / 3.0;
};
/**
* Get an average of all the vertices positions
* @method getAveragePointLocal
* @param {Vec3} target
* @return {Vec3}
*/
ConvexPolyhedron.prototype.getAveragePointLocal = function(target){
target = target || new Vec3();
var n = this.vertices.length,
verts = this.vertices;
for(var i=0; i<n; i++){
target.vadd(verts[i],target);
}
target.mult(1/n,target);
return target;
};
/**
* Transform all local points. Will change the .vertices
* @method transformAllPoints
* @param {Vec3} offset
* @param {Quaternion} quat
*/
ConvexPolyhedron.prototype.transformAllPoints = function(offset,quat){
var n = this.vertices.length,
verts = this.vertices;
// Apply rotation
if(quat){
// Rotate vertices
for(var i=0; i<n; i++){
var v = verts[i];
quat.vmult(v,v);
}
// Rotate face normals
for(var i=0; i<this.faceNormals.length; i++){
var v = this.faceNormals[i];
quat.vmult(v,v);
}
/*
// Rotate edges
for(var i=0; i<this.uniqueEdges.length; i++){
var v = this.uniqueEdges[i];
quat.vmult(v,v);
}*/
}
// Apply offset
if(offset){
for(var i=0; i<n; i++){
var v = verts[i];
v.vadd(offset,v);
}
}
};
/**
* Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.
* @method pointIsInside
* @param {Vec3} p A point given in local coordinates
* @return {Boolean}
*/
var ConvexPolyhedron_pointIsInside = new Vec3();
var ConvexPolyhedron_vToP = new Vec3();
var ConvexPolyhedron_vToPointInside = new Vec3();
ConvexPolyhedron.prototype.pointIsInside = function(p){
var n = this.vertices.length,
verts = this.vertices,
faces = this.faces,
normals = this.faceNormals;
var positiveResult = null;
var N = this.faces.length;
var pointInside = ConvexPolyhedron_pointIsInside;
this.getAveragePointLocal(pointInside);
for(var i=0; i<N; i++){
var numVertices = this.faces[i].length;
var n = normals[i];
var v = verts[faces[i][0]]; // We only need one point in the face
// This dot product determines which side of the edge the point is
var vToP = ConvexPolyhedron_vToP;
p.vsub(v,vToP);
var r1 = n.dot(vToP);
var vToPointInside = ConvexPolyhedron_vToPointInside;
pointInside.vsub(v,vToPointInside);
var r2 = n.dot(vToPointInside);
if((r1<0 && r2>0) || (r1>0 && r2<0)){
return false; // Encountered some other sign. Exit.
} else {
}
}
// If we got here, all dot products were of the same sign.
return positiveResult ? 1 : -1;
};
/**
* Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin.
* @static
* @method project
* @param {ConvexPolyhedron} hull
* @param {Vec3} axis
* @param {Vec3} pos
* @param {Quaternion} quat
* @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively.
*/
var project_worldVertex = new Vec3();
var project_localAxis = new Vec3();
var project_localOrigin = new Vec3();
ConvexPolyhedron.project = function(hull, axis, pos, quat, result){
var n = hull.vertices.length,
worldVertex = project_worldVertex,
localAxis = project_localAxis,
max = 0,
min = 0,
localOrigin = project_localOrigin,
vs = hull.vertices;
localOrigin.setZero();
// Transform the axis to local
Transform.vectorToLocalFrame(pos, quat, axis, localAxis);
Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin);
var add = localOrigin.dot(localAxis);
min = max = vs[0].dot(localAxis);
for(var i = 1; i < n; i++){
var val = vs[i].dot(localAxis);
if(val > max){
max = val;
}
if(val < min){
min = val;
}
}
min -= add;
max -= add;
if(min > max){
// Inconsistent - swap
var temp = min;
min = max;
max = temp;
}
// Output
result[0] = max;
result[1] = min;
};
},{"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"./Shape":87}],83:[function(require,module,exports){
module.exports = Cylinder;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var ConvexPolyhedron = require('./ConvexPolyhedron');
/**
* @class Cylinder
* @constructor
* @extends ConvexPolyhedron
* @author schteppe / https://github.com/schteppe
* @param {Number} radiusTop
* @param {Number} radiusBottom
* @param {Number} height
* @param {Number} numSegments The number of segments to build the cylinder out of
*/
function Cylinder( radiusTop, radiusBottom, height , numSegments ) {
var N = numSegments,
verts = [],
axes = [],
faces = [],
bottomface = [],
topface = [],
cos = Math.cos,
sin = Math.sin;
// First bottom point
verts.push(new Vec3(radiusBottom*cos(0),
radiusBottom*sin(0),
-height*0.5));
bottomface.push(0);
// First top point
verts.push(new Vec3(radiusTop*cos(0),
radiusTop*sin(0),
height*0.5));
topface.push(1);
for(var i=0; i<N; i++){
var theta = 2*Math.PI/N * (i+1);
var thetaN = 2*Math.PI/N * (i+0.5);
if(i<N-1){
// Bottom
verts.push(new Vec3(radiusBottom*cos(theta),
radiusBottom*sin(theta),
-height*0.5));
bottomface.push(2*i+2);
// Top
verts.push(new Vec3(radiusTop*cos(theta),
radiusTop*sin(theta),
height*0.5));
topface.push(2*i+3);
// Face
faces.push([2*i+2, 2*i+3, 2*i+1,2*i]);
} else {
faces.push([0,1, 2*i+1, 2*i]); // Connect
}
// Axis: we can cut off half of them if we have even number of segments
if(N % 2 === 1 || i < N / 2){
axes.push(new Vec3(cos(thetaN), sin(thetaN), 0));
}
}
faces.push(topface);
axes.push(new Vec3(0,0,1));
// Reorder bottom face
var temp = [];
for(var i=0; i<bottomface.length; i++){
temp.push(bottomface[bottomface.length - i - 1]);
}
faces.push(temp);
ConvexPolyhedron.call( this, verts, faces, axes );
}
Cylinder.prototype = new ConvexPolyhedron();
},{"../math/Quaternion":72,"../math/Vec3":74,"./ConvexPolyhedron":82,"./Shape":87}],84:[function(require,module,exports){
var Shape = require('./Shape');
var ConvexPolyhedron = require('./ConvexPolyhedron');
var Vec3 = require('../math/Vec3');
var Utils = require('../utils/Utils');
module.exports = Heightfield;
/**
* Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a given distance.
* @class Heightfield
* @extends Shape
* @constructor
* @param {Array} data An array of Y values that will be used to construct the terrain.
* @param {object} options
* @param {Number} [options.minValue] Minimum value of the data points in the data array. Will be computed automatically if not given.
* @param {Number} [options.maxValue] Maximum value.
* @param {Number} [options.elementSize=0.1] World spacing between the data points in X direction.
* @todo Should be possible to use along all axes, not just y
* @todo should be possible to scale along all axes
*
* @example
* // Generate some height data (y-values).
* var data = [];
* for(var i = 0; i < 1000; i++){
* var y = 0.5 * Math.cos(0.2 * i);
* data.push(y);
* }
*
* // Create the heightfield shape
* var heightfieldShape = new Heightfield(data, {
* elementSize: 1 // Distance between the data points in X and Y directions
* });
* var heightfieldBody = new Body();
* heightfieldBody.addShape(heightfieldShape);
* world.addBody(heightfieldBody);
*/
function Heightfield(data, options){
options = Utils.defaults(options, {
maxValue : null,
minValue : null,
elementSize : 1
});
/**
* An array of numbers, or height values, that are spread out along the x axis.
* @property {array} data
*/
this.data = data;
/**
* Max value of the data
* @property {number} maxValue
*/
this.maxValue = options.maxValue;
/**
* Max value of the data
* @property {number} minValue
*/
this.minValue = options.minValue;
/**
* The width of each element
* @property {number} elementSize
* @todo elementSizeX and Y
*/
this.elementSize = options.elementSize;
if(options.minValue === null){
this.updateMinValue();
}
if(options.maxValue === null){
this.updateMaxValue();
}
this.cacheEnabled = true;
Shape.call(this, {
type: Shape.types.HEIGHTFIELD
});
this.pillarConvex = new ConvexPolyhedron();
this.pillarOffset = new Vec3();
this.updateBoundingSphereRadius();
// "i_j_isUpper" => { convex: ..., offset: ... }
// for example:
// _cachedPillars["0_2_1"]
this._cachedPillars = {};
}
Heightfield.prototype = new Shape();
/**
* Call whenever you change the data array.
* @method update
*/
Heightfield.prototype.update = function(){
this._cachedPillars = {};
};
/**
* Update the .minValue property
* @method updateMinValue
*/
Heightfield.prototype.updateMinValue = function(){
var data = this.data;
var minValue = data[0][0];
for(var i=0; i !== data.length; i++){
for(var j=0; j !== data[i].length; j++){
var v = data[i][j];
if(v < minValue){
minValue = v;
}
}
}
this.minValue = minValue;
};
/**
* Update the .maxValue property
* @method updateMaxValue
*/
Heightfield.prototype.updateMaxValue = function(){
var data = this.data;
var maxValue = data[0][0];
for(var i=0; i !== data.length; i++){
for(var j=0; j !== data[i].length; j++){
var v = data[i][j];
if(v > maxValue){
maxValue = v;
}
}
}
this.maxValue = maxValue;
};
/**
* Set the height value at an index. Don't forget to update maxValue and minValue after you're done.
* @method setHeightValueAtIndex
* @param {integer} xi
* @param {integer} yi
* @param {number} value
*/
Heightfield.prototype.setHeightValueAtIndex = function(xi, yi, value){
var data = this.data;
data[xi][yi] = value;
// Invalidate cache
this.clearCachedConvexTrianglePillar(xi, yi, false);
if(xi > 0){
this.clearCachedConvexTrianglePillar(xi - 1, yi, true);
this.clearCachedConvexTrianglePillar(xi - 1, yi, false);
}
if(yi > 0){
this.clearCachedConvexTrianglePillar(xi, yi - 1, true);
this.clearCachedConvexTrianglePillar(xi, yi - 1, false);
}
if(yi > 0 && xi > 0){
this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true);
}
};
/**
* Get max/min in a rectangle in the matrix data
* @method getRectMinMax
* @param {integer} iMinX
* @param {integer} iMinY
* @param {integer} iMaxX
* @param {integer} iMaxY
* @param {array} [result] An array to store the results in.
* @return {array} The result array, if it was passed in. Minimum will be at position 0 and max at 1.
*/
Heightfield.prototype.getRectMinMax = function (iMinX, iMinY, iMaxX, iMaxY, result) {
result = result || [];
// Get max and min of the data
var data = this.data,
max = this.minValue; // Set first value
for(var i = iMinX; i <= iMaxX; i++){
for(var j = iMinY; j <= iMaxY; j++){
var height = data[i][j];
if(height > max){
max = height;
}
}
}
result[0] = this.minValue;
result[1] = max;
};
/**
* Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1.
* @method getIndexOfPosition
* @param {number} x
* @param {number} y
* @param {array} result Two-element array
* @param {boolean} clamp If the position should be clamped to the heightfield edge.
* @return {boolean}
*/
Heightfield.prototype.getIndexOfPosition = function (x, y, result, clamp) {
// Get the index of the data points to test against
var w = this.elementSize;
var data = this.data;
var xi = Math.floor(x / w);
var yi = Math.floor(y / w);
result[0] = xi;
result[1] = yi;
if(clamp){
// Clamp index to edges
if(xi < 0){ xi = 0; }
if(yi < 0){ yi = 0; }
if(xi >= data.length - 1){ xi = data.length - 1; }
if(yi >= data[0].length - 1){ yi = data[0].length - 1; }
}
// Bail out if we are out of the terrain
if(xi < 0 || yi < 0 || xi >= data.length-1 || yi >= data[0].length-1){
return false;
}
return true;
};
var getHeightAt_idx = [];
var getHeightAt_weights = new Vec3();
var getHeightAt_a = new Vec3();
var getHeightAt_b = new Vec3();
var getHeightAt_c = new Vec3();
Heightfield.prototype.getTriangleAt = function(x, y, edgeClamp, a, b, c){
var idx = getHeightAt_idx;
this.getIndexOfPosition(x, y, idx, edgeClamp);
var xi = idx[0];
var yi = idx[1];
var data = this.data;
if(edgeClamp){
xi = Math.min(data.length - 2, Math.max(0, xi));
yi = Math.min(data[0].length - 2, Math.max(0, yi));
}
var elementSize = this.elementSize;
var lowerDist2 = Math.pow(x / elementSize - xi, 2) + Math.pow(y / elementSize - yi, 2);
var upperDist2 = Math.pow(x / elementSize - (xi + 1), 2) + Math.pow(y / elementSize - (yi + 1), 2);
var upper = lowerDist2 > upperDist2;
this.getTriangle(xi, yi, upper, a, b, c);
return upper;
};
var getNormalAt_a = new Vec3();
var getNormalAt_b = new Vec3();
var getNormalAt_c = new Vec3();
var getNormalAt_e0 = new Vec3();
var getNormalAt_e1 = new Vec3();
Heightfield.prototype.getNormalAt = function(x, y, edgeClamp, result){
var a = getNormalAt_a;
var b = getNormalAt_b;
var c = getNormalAt_c;
var e0 = getNormalAt_e0;
var e1 = getNormalAt_e1;
this.getTriangleAt(x, y, edgeClamp, a, b, c);
b.vsub(a, e0);
c.vsub(a, e1);
e0.cross(e1, result);
result.normalize();
};
/**
* Get an AABB of a square in the heightfield
* @param {number} xi
* @param {number} yi
* @param {AABB} result
*/
Heightfield.prototype.getAabbAtIndex = function(xi, yi, result){
var data = this.data;
var elementSize = this.elementSize;
result.lowerBound.set(
xi * elementSize,
yi * elementSize,
data[xi][yi]
);
result.upperBound.set(
(xi + 1) * elementSize,
(yi + 1) * elementSize,
data[xi + 1][yi + 1]
);
};
/**
* Get the height in the heightfield at a given position
* @param {number} x
* @param {number} y
* @param {boolean} edgeClamp
* @return {number}
*/
Heightfield.prototype.getHeightAt = function(x, y, edgeClamp){
var data = this.data;
var a = getHeightAt_a;
var b = getHeightAt_b;
var c = getHeightAt_c;
var idx = getHeightAt_idx;
this.getIndexOfPosition(x, y, idx, edgeClamp);
var xi = idx[0];
var yi = idx[1];
if(edgeClamp){
xi = Math.min(data.length - 2, Math.max(0, xi));
yi = Math.min(data[0].length - 2, Math.max(0, yi));
}
var upper = this.getTriangleAt(x, y, edgeClamp, a, b, c);
barycentricWeights(x, y, a.x, a.y, b.x, b.y, c.x, c.y, getHeightAt_weights);
var w = getHeightAt_weights;
if(upper){
// Top triangle verts
return data[xi + 1][yi + 1] * w.x + data[xi][yi + 1] * w.y + data[xi + 1][yi] * w.z;
} else {
// Top triangle verts
return data[xi][yi] * w.x + data[xi + 1][yi] * w.y + data[xi][yi + 1] * w.z;
}
};
// from https://en.wikipedia.org/wiki/Barycentric_coordinate_system
function barycentricWeights(x, y, ax, ay, bx, by, cx, cy, result){
result.x = ((by - cy) * (x - cx) + (cx - bx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy));
result.y = ((cy - ay) * (x - cx) + (ax - cx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy));
result.z = 1 - result.x - result.y;
}
Heightfield.prototype.getCacheConvexTrianglePillarKey = function(xi, yi, getUpperTriangle){
return xi + '_' + yi + '_' + (getUpperTriangle ? 1 : 0);
};
Heightfield.prototype.getCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){
return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)];
};
Heightfield.prototype.setCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle, convex, offset){
this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = {
convex: convex,
offset: offset
};
};
Heightfield.prototype.clearCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){
delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)];
};
/**
* Get a triangle from the heightfield
* @param {number} xi
* @param {number} yi
* @param {boolean} upper
* @param {Vec3} a
* @param {Vec3} b
* @param {Vec3} c
*/
Heightfield.prototype.getTriangle = function(xi, yi, upper, a, b, c){
var data = this.data;
var elementSize = this.elementSize;
if(upper){
// Top triangle verts
a.set(
(xi + 1) * elementSize,
(yi + 1) * elementSize,
data[xi + 1][yi + 1]
);
b.set(
xi * elementSize,
(yi + 1) * elementSize,
data[xi][yi + 1]
);
c.set(
(xi + 1) * elementSize,
yi * elementSize,
data[xi + 1][yi]
);
} else {
// Top triangle verts
a.set(
xi * elementSize,
yi * elementSize,
data[xi][yi]
);
b.set(
(xi + 1) * elementSize,
yi * elementSize,
data[xi + 1][yi]
);
c.set(
xi * elementSize,
(yi + 1) * elementSize,
data[xi][yi + 1]
);
}
};
/**
* Get a triangle in the terrain in the form of a triangular convex shape.
* @method getConvexTrianglePillar
* @param {integer} i
* @param {integer} j
* @param {boolean} getUpperTriangle
*/
Heightfield.prototype.getConvexTrianglePillar = function(xi, yi, getUpperTriangle){
var result = this.pillarConvex;
var offsetResult = this.pillarOffset;
if(this.cacheEnabled){
var data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle);
if(data){
this.pillarConvex = data.convex;
this.pillarOffset = data.offset;
return;
}
result = new ConvexPolyhedron();
offsetResult = new Vec3();
this.pillarConvex = result;
this.pillarOffset = offsetResult;
}
var data = this.data;
var elementSize = this.elementSize;
var faces = result.faces;
// Reuse verts if possible
result.vertices.length = 6;
for (var i = 0; i < 6; i++) {
if(!result.vertices[i]){
result.vertices[i] = new Vec3();
}
}
// Reuse faces if possible
faces.length = 5;
for (var i = 0; i < 5; i++) {
if(!faces[i]){
faces[i] = [];
}
}
var verts = result.vertices;
var h = (Math.min(
data[xi][yi],
data[xi+1][yi],
data[xi][yi+1],
data[xi+1][yi+1]
) - this.minValue ) / 2 + this.minValue;
if (!getUpperTriangle) {
// Center of the triangle pillar - all polygons are given relative to this one
offsetResult.set(
(xi + 0.25) * elementSize, // sort of center of a triangle
(yi + 0.25) * elementSize,
h // vertical center
);
// Top triangle verts
verts[0].set(
-0.25 * elementSize,
-0.25 * elementSize,
data[xi][yi] - h
);
verts[1].set(
0.75 * elementSize,
-0.25 * elementSize,
data[xi + 1][yi] - h
);
verts[2].set(
-0.25 * elementSize,
0.75 * elementSize,
data[xi][yi + 1] - h
);
// bottom triangle verts
verts[3].set(
-0.25 * elementSize,
-0.25 * elementSize,
-h-1
);
verts[4].set(
0.75 * elementSize,
-0.25 * elementSize,
-h-1
);
verts[5].set(
-0.25 * elementSize,
0.75 * elementSize,
-h-1
);
// top triangle
faces[0][0] = 0;
faces[0][1] = 1;
faces[0][2] = 2;
// bottom triangle
faces[1][0] = 5;
faces[1][1] = 4;
faces[1][2] = 3;
// -x facing quad
faces[2][0] = 0;
faces[2][1] = 2;
faces[2][2] = 5;
faces[2][3] = 3;
// -y facing quad
faces[3][0] = 1;
faces[3][1] = 0;
faces[3][2] = 3;
faces[3][3] = 4;
// +xy facing quad
faces[4][0] = 4;
faces[4][1] = 5;
faces[4][2] = 2;
faces[4][3] = 1;
} else {
// Center of the triangle pillar - all polygons are given relative to this one
offsetResult.set(
(xi + 0.75) * elementSize, // sort of center of a triangle
(yi + 0.75) * elementSize,
h // vertical center
);
// Top triangle verts
verts[0].set(
0.25 * elementSize,
0.25 * elementSize,
data[xi + 1][yi + 1] - h
);
verts[1].set(
-0.75 * elementSize,
0.25 * elementSize,
data[xi][yi + 1] - h
);
verts[2].set(
0.25 * elementSize,
-0.75 * elementSize,
data[xi + 1][yi] - h
);
// bottom triangle verts
verts[3].set(
0.25 * elementSize,
0.25 * elementSize,
- h-1
);
verts[4].set(
-0.75 * elementSize,
0.25 * elementSize,
- h-1
);
verts[5].set(
0.25 * elementSize,
-0.75 * elementSize,
- h-1
);
// Top triangle
faces[0][0] = 0;
faces[0][1] = 1;
faces[0][2] = 2;
// bottom triangle
faces[1][0] = 5;
faces[1][1] = 4;
faces[1][2] = 3;
// +x facing quad
faces[2][0] = 2;
faces[2][1] = 5;
faces[2][2] = 3;
faces[2][3] = 0;
// +y facing quad
faces[3][0] = 3;
faces[3][1] = 4;
faces[3][2] = 1;
faces[3][3] = 0;
// -xy facing quad
faces[4][0] = 1;
faces[4][1] = 4;
faces[4][2] = 5;
faces[4][3] = 2;
}
result.computeNormals();
result.computeEdges();
result.updateBoundingSphereRadius();
this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult);
};
Heightfield.prototype.calculateLocalInertia = function(mass, target){
target = target || new Vec3();
target.set(0, 0, 0);
return target;
};
Heightfield.prototype.volume = function(){
return Number.MAX_VALUE; // The terrain is infinite
};
Heightfield.prototype.calculateWorldAABB = function(pos, quat, min, max){
// TODO: do it properly
min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
};
Heightfield.prototype.updateBoundingSphereRadius = function(){
// Use the bounding box of the min/max values
var data = this.data,
s = this.elementSize;
this.boundingSphereRadius = new Vec3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).norm();
};
/**
* Sets the height values from an image. Currently only supported in browser.
* @method setHeightsFromImage
* @param {Image} image
* @param {Vec3} scale
*/
Heightfield.prototype.setHeightsFromImage = function(image, scale){
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
var imageData = context.getImageData(0, 0, image.width, image.height);
var matrix = this.data;
matrix.length = 0;
this.elementSize = Math.abs(scale.x) / imageData.width;
for(var i=0; i<imageData.height; i++){
var row = [];
for(var j=0; j<imageData.width; j++){
var a = imageData.data[(i*imageData.height + j) * 4];
var b = imageData.data[(i*imageData.height + j) * 4 + 1];
var c = imageData.data[(i*imageData.height + j) * 4 + 2];
var height = (a + b + c) / 4 / 255 * scale.z;
if(scale.x < 0){
row.push(height);
} else {
row.unshift(height);
}
}
if(scale.y < 0){
matrix.unshift(row);
} else {
matrix.push(row);
}
}
this.updateMaxValue();
this.updateMinValue();
this.update();
};
},{"../math/Vec3":74,"../utils/Utils":97,"./ConvexPolyhedron":82,"./Shape":87}],85:[function(require,module,exports){
module.exports = Particle;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
/**
* Particle shape.
* @class Particle
* @constructor
* @author schteppe
* @extends Shape
*/
function Particle(){
Shape.call(this, {
type: Shape.types.PARTICLE
});
}
Particle.prototype = new Shape();
Particle.prototype.constructor = Particle;
/**
* @method calculateLocalInertia
* @param {Number} mass
* @param {Vec3} target
* @return {Vec3}
*/
Particle.prototype.calculateLocalInertia = function(mass,target){
target = target || new Vec3();
target.set(0, 0, 0);
return target;
};
Particle.prototype.volume = function(){
return 0;
};
Particle.prototype.updateBoundingSphereRadius = function(){
this.boundingSphereRadius = 0;
};
Particle.prototype.calculateWorldAABB = function(pos,quat,min,max){
// Get each axis max
min.copy(pos);
max.copy(pos);
};
},{"../math/Vec3":74,"./Shape":87}],86:[function(require,module,exports){
module.exports = Plane;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
/**
* A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a Body and rotate that body. See the demos.
* @class Plane
* @constructor
* @extends Shape
* @author schteppe
*/
function Plane(){
Shape.call(this, {
type: Shape.types.PLANE
});
// World oriented normal
this.worldNormal = new Vec3();
this.worldNormalNeedsUpdate = true;
this.boundingSphereRadius = Number.MAX_VALUE;
}
Plane.prototype = new Shape();
Plane.prototype.constructor = Plane;
Plane.prototype.computeWorldNormal = function(quat){
var n = this.worldNormal;
n.set(0,0,1);
quat.vmult(n,n);
this.worldNormalNeedsUpdate = false;
};
Plane.prototype.calculateLocalInertia = function(mass,target){
target = target || new Vec3();
return target;
};
Plane.prototype.volume = function(){
return Number.MAX_VALUE; // The plane is infinite...
};
var tempNormal = new Vec3();
Plane.prototype.calculateWorldAABB = function(pos, quat, min, max){
// The plane AABB is infinite, except if the normal is pointing along any axis
tempNormal.set(0,0,1); // Default plane normal is z
quat.vmult(tempNormal,tempNormal);
var maxVal = Number.MAX_VALUE;
min.set(-maxVal, -maxVal, -maxVal);
max.set(maxVal, maxVal, maxVal);
if(tempNormal.x === 1){ max.x = pos.x; }
if(tempNormal.y === 1){ max.y = pos.y; }
if(tempNormal.z === 1){ max.z = pos.z; }
if(tempNormal.x === -1){ min.x = pos.x; }
if(tempNormal.y === -1){ min.y = pos.y; }
if(tempNormal.z === -1){ min.z = pos.z; }
};
Plane.prototype.updateBoundingSphereRadius = function(){
this.boundingSphereRadius = Number.MAX_VALUE;
};
},{"../math/Vec3":74,"./Shape":87}],87:[function(require,module,exports){
module.exports = Shape;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Material = require('../material/Material');
/**
* Base class for shapes
* @class Shape
* @constructor
* @param {object} [options]
* @param {number} [options.collisionFilterGroup=1]
* @param {number} [options.collisionFilterMask=-1]
* @param {number} [options.collisionResponse=true]
* @param {number} [options.material=null]
* @author schteppe
*/
function Shape(options){
options = options || {};
/**
* Identifyer of the Shape.
* @property {number} id
*/
this.id = Shape.idCounter++;
/**
* The type of this shape. Must be set to an int > 0 by subclasses.
* @property type
* @type {Number}
* @see Shape.types
*/
this.type = options.type || 0;
/**
* The local bounding sphere radius of this shape.
* @property {Number} boundingSphereRadius
*/
this.boundingSphereRadius = 0;
/**
* Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.
* @property {boolean} collisionResponse
*/
this.collisionResponse = options.collisionResponse ? options.collisionResponse : true;
/**
* @property {Number} collisionFilterGroup
*/
this.collisionFilterGroup = options.collisionFilterGroup !== undefined ? options.collisionFilterGroup : 1;
/**
* @property {Number} collisionFilterMask
*/
this.collisionFilterMask = options.collisionFilterMask !== undefined ? options.collisionFilterMask : -1;
/**
* @property {Material} material
*/
this.material = options.material ? options.material : null;
/**
* @property {Body} body
*/
this.body = null;
}
Shape.prototype.constructor = Shape;
/**
* Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius
* @method updateBoundingSphereRadius
*/
Shape.prototype.updateBoundingSphereRadius = function(){
throw "computeBoundingSphereRadius() not implemented for shape type "+this.type;
};
/**
* Get the volume of this shape
* @method volume
* @return {Number}
*/
Shape.prototype.volume = function(){
throw "volume() not implemented for shape type "+this.type;
};
/**
* Calculates the inertia in the local frame for this shape.
* @method calculateLocalInertia
* @param {Number} mass
* @param {Vec3} target
* @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia
*/
Shape.prototype.calculateLocalInertia = function(mass,target){
throw "calculateLocalInertia() not implemented for shape type "+this.type;
};
Shape.idCounter = 0;
/**
* The available shape types.
* @static
* @property types
* @type {Object}
*/
Shape.types = {
SPHERE:1,
PLANE:2,
BOX:4,
COMPOUND:8,
CONVEXPOLYHEDRON:16,
HEIGHTFIELD:32,
PARTICLE:64,
CYLINDER:128,
TRIMESH:256
};
},{"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"./Shape":87}],88:[function(require,module,exports){
module.exports = Sphere;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
/**
* Spherical shape
* @class Sphere
* @constructor
* @extends Shape
* @param {Number} radius The radius of the sphere, a non-negative number.
* @author schteppe / http://github.com/schteppe
*/
function Sphere(radius){
Shape.call(this, {
type: Shape.types.SPHERE
});
/**
* @property {Number} radius
*/
this.radius = radius !== undefined ? radius : 1.0;
if(this.radius < 0){
throw new Error('The sphere radius cannot be negative.');
}
this.updateBoundingSphereRadius();
}
Sphere.prototype = new Shape();
Sphere.prototype.constructor = Sphere;
Sphere.prototype.calculateLocalInertia = function(mass,target){
target = target || new Vec3();
var I = 2.0*mass*this.radius*this.radius/5.0;
target.x = I;
target.y = I;
target.z = I;
return target;
};
Sphere.prototype.volume = function(){
return 4.0 * Math.PI * this.radius / 3.0;
};
Sphere.prototype.updateBoundingSphereRadius = function(){
this.boundingSphereRadius = this.radius;
};
Sphere.prototype.calculateWorldAABB = function(pos,quat,min,max){
var r = this.radius;
var axes = ['x','y','z'];
for(var i=0; i<axes.length; i++){
var ax = axes[i];
min[ax] = pos[ax] - r;
max[ax] = pos[ax] + r;
}
};
},{"../math/Vec3":74,"./Shape":87}],89:[function(require,module,exports){
module.exports = Trimesh;
var Shape = require('./Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Transform = require('../math/Transform');
var AABB = require('../collision/AABB');
var Octree = require('../utils/Octree');
/**
* @class Trimesh
* @constructor
* @param {array} vertices
* @param {array} indices
* @extends Shape
* @example
* // How to make a mesh with a single triangle
* var vertices = [
* 0, 0, 0, // vertex 0
* 1, 0, 0, // vertex 1
* 0, 1, 0 // vertex 2
* ];
* var indices = [
* 0, 1, 2 // triangle 0
* ];
* var trimeshShape = new Trimesh(vertices, indices);
*/
function Trimesh(vertices, indices) {
Shape.call(this, {
type: Shape.types.TRIMESH
});
/**
* @property vertices
* @type {Array}
*/
this.vertices = new Float32Array(vertices);
/**
* Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles.
* @property indices
* @type {Array}
*/
this.indices = new Int16Array(indices);
/**
* The normals data.
* @property normals
* @type {Array}
*/
this.normals = new Float32Array(indices.length);
/**
* The local AABB of the mesh.
* @property aabb
* @type {Array}
*/
this.aabb = new AABB();
/**
* References to vertex pairs, making up all unique edges in the trimesh.
* @property {array} edges
*/
this.edges = null;
/**
* Local scaling of the mesh. Use .setScale() to set it.
* @property {Vec3} scale
*/
this.scale = new Vec3(1, 1, 1);
/**
* The indexed triangles. Use .updateTree() to update it.
* @property {Octree} tree
*/
this.tree = new Octree();
this.updateEdges();
this.updateNormals();
this.updateAABB();
this.updateBoundingSphereRadius();
this.updateTree();
}
Trimesh.prototype = new Shape();
Trimesh.prototype.constructor = Trimesh;
var computeNormals_n = new Vec3();
/**
* @method updateTree
*/
Trimesh.prototype.updateTree = function(){
var tree = this.tree;
tree.reset();
tree.aabb.copy(this.aabb);
var scale = this.scale; // The local mesh AABB is scaled, but the octree AABB should be unscaled
tree.aabb.lowerBound.x *= 1 / scale.x;
tree.aabb.lowerBound.y *= 1 / scale.y;
tree.aabb.lowerBound.z *= 1 / scale.z;
tree.aabb.upperBound.x *= 1 / scale.x;
tree.aabb.upperBound.y *= 1 / scale.y;
tree.aabb.upperBound.z *= 1 / scale.z;
// Insert all triangles
var triangleAABB = new AABB();
var a = new Vec3();
var b = new Vec3();
var c = new Vec3();
var points = [a, b, c];
for (var i = 0; i < this.indices.length / 3; i++) {
//this.getTriangleVertices(i, a, b, c);
// Get unscaled triangle verts
var i3 = i * 3;
this._getUnscaledVertex(this.indices[i3], a);
this._getUnscaledVertex(this.indices[i3 + 1], b);
this._getUnscaledVertex(this.indices[i3 + 2], c);
triangleAABB.setFromPoints(points);
tree.insert(triangleAABB, i);
}
tree.removeEmptyNodes();
};
var unscaledAABB = new AABB();
/**
* Get triangles in a local AABB from the trimesh.
* @method getTrianglesInAABB
* @param {AABB} aabb
* @param {array} result An array of integers, referencing the queried triangles.
*/
Trimesh.prototype.getTrianglesInAABB = function(aabb, result){
unscaledAABB.copy(aabb);
// Scale it to local
var scale = this.scale;
var isx = scale.x;
var isy = scale.y;
var isz = scale.z;
var l = unscaledAABB.lowerBound;
var u = unscaledAABB.upperBound;
l.x /= isx;
l.y /= isy;
l.z /= isz;
u.x /= isx;
u.y /= isy;
u.z /= isz;
return this.tree.aabbQuery(unscaledAABB, result);
};
/**
* @method setScale
* @param {Vec3} scale
*/
Trimesh.prototype.setScale = function(scale){
var wasUniform = this.scale.x === this.scale.y === this.scale.z;
var isUniform = scale.x === scale.y === scale.z;
if(!(wasUniform && isUniform)){
// Non-uniform scaling. Need to update normals.
this.updateNormals();
}
this.scale.copy(scale);
this.updateAABB();
this.updateBoundingSphereRadius();
};
/**
* Compute the normals of the faces. Will save in the .normals array.
* @method updateNormals
*/
Trimesh.prototype.updateNormals = function(){
var n = computeNormals_n;
// Generate normals
var normals = this.normals;
for(var i=0; i < this.indices.length / 3; i++){
var i3 = i * 3;
var a = this.indices[i3],
b = this.indices[i3 + 1],
c = this.indices[i3 + 2];
this.getVertex(a, va);
this.getVertex(b, vb);
this.getVertex(c, vc);
Trimesh.computeNormal(vb, va, vc, n);
normals[i3] = n.x;
normals[i3 + 1] = n.y;
normals[i3 + 2] = n.z;
}
};
/**
* Update the .edges property
* @method updateEdges
*/
Trimesh.prototype.updateEdges = function(){
var edges = {};
var add = function(indexA, indexB){
var key = a < b ? a + '_' + b : b + '_' + a;
edges[key] = true;
};
for(var i=0; i < this.indices.length / 3; i++){
var i3 = i * 3;
var a = this.indices[i3],
b = this.indices[i3 + 1],
c = this.indices[i3 + 2];
add(a,b);
add(b,c);
add(c,a);
}
var keys = Object.keys(edges);
this.edges = new Int16Array(keys.length * 2);
for (var i = 0; i < keys.length; i++) {
var indices = keys[i].split('_');
this.edges[2 * i] = parseInt(indices[0], 10);
this.edges[2 * i + 1] = parseInt(indices[1], 10);
}
};
/**
* Get an edge vertex
* @method getEdgeVertex
* @param {number} edgeIndex
* @param {number} firstOrSecond 0 or 1, depending on which one of the vertices you need.
* @param {Vec3} vertexStore Where to store the result
*/
Trimesh.prototype.getEdgeVertex = function(edgeIndex, firstOrSecond, vertexStore){
var vertexIndex = this.edges[edgeIndex * 2 + (firstOrSecond ? 1 : 0)];
this.getVertex(vertexIndex, vertexStore);
};
var getEdgeVector_va = new Vec3();
var getEdgeVector_vb = new Vec3();
/**
* Get a vector along an edge.
* @method getEdgeVector
* @param {number} edgeIndex
* @param {Vec3} vectorStore
*/
Trimesh.prototype.getEdgeVector = function(edgeIndex, vectorStore){
var va = getEdgeVector_va;
var vb = getEdgeVector_vb;
this.getEdgeVertex(edgeIndex, 0, va);
this.getEdgeVertex(edgeIndex, 1, vb);
vb.vsub(va, vectorStore);
};
/**
* Get face normal given 3 vertices
* @static
* @method computeNormal
* @param {Vec3} va
* @param {Vec3} vb
* @param {Vec3} vc
* @param {Vec3} target
*/
var cb = new Vec3();
var ab = new Vec3();
Trimesh.computeNormal = function ( va, vb, vc, target ) {
vb.vsub(va,ab);
vc.vsub(vb,cb);
cb.cross(ab,target);
if ( !target.isZero() ) {
target.normalize();
}
};
var va = new Vec3();
var vb = new Vec3();
var vc = new Vec3();
/**
* Get vertex i.
* @method getVertex
* @param {number} i
* @param {Vec3} out
* @return {Vec3} The "out" vector object
*/
Trimesh.prototype.getVertex = function(i, out){
var scale = this.scale;
this._getUnscaledVertex(i, out);
out.x *= scale.x;
out.y *= scale.y;
out.z *= scale.z;
return out;
};
/**
* Get raw vertex i
* @private
* @method _getUnscaledVertex
* @param {number} i
* @param {Vec3} out
* @return {Vec3} The "out" vector object
*/
Trimesh.prototype._getUnscaledVertex = function(i, out){
var i3 = i * 3;
var vertices = this.vertices;
return out.set(
vertices[i3],
vertices[i3 + 1],
vertices[i3 + 2]
);
};
/**
* Get a vertex from the trimesh,transformed by the given position and quaternion.
* @method getWorldVertex
* @param {number} i
* @param {Vec3} pos
* @param {Quaternion} quat
* @param {Vec3} out
* @return {Vec3} The "out" vector object
*/
Trimesh.prototype.getWorldVertex = function(i, pos, quat, out){
this.getVertex(i, out);
Transform.pointToWorldFrame(pos, quat, out, out);
return out;
};
/**
* Get the three vertices for triangle i.
* @method getTriangleVertices
* @param {number} i
* @param {Vec3} a
* @param {Vec3} b
* @param {Vec3} c
*/
Trimesh.prototype.getTriangleVertices = function(i, a, b, c){
var i3 = i * 3;
this.getVertex(this.indices[i3], a);
this.getVertex(this.indices[i3 + 1], b);
this.getVertex(this.indices[i3 + 2], c);
};
/**
* Compute the normal of triangle i.
* @method getNormal
* @param {Number} i
* @param {Vec3} target
* @return {Vec3} The "target" vector object
*/
Trimesh.prototype.getNormal = function(i, target){
var i3 = i * 3;
return target.set(
this.normals[i3],
this.normals[i3 + 1],
this.normals[i3 + 2]
);
};
var cli_aabb = new AABB();
/**
* @method calculateLocalInertia
* @param {Number} mass
* @param {Vec3} target
* @return {Vec3} The "target" vector object
*/
Trimesh.prototype.calculateLocalInertia = function(mass,target){
// Approximate with box inertia
// Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it
this.computeLocalAABB(cli_aabb);
var x = cli_aabb.upperBound.x - cli_aabb.lowerBound.x,
y = cli_aabb.upperBound.y - cli_aabb.lowerBound.y,
z = cli_aabb.upperBound.z - cli_aabb.lowerBound.z;
return target.set(
1.0 / 12.0 * mass * ( 2*y*2*y + 2*z*2*z ),
1.0 / 12.0 * mass * ( 2*x*2*x + 2*z*2*z ),
1.0 / 12.0 * mass * ( 2*y*2*y + 2*x*2*x )
);
};
var computeLocalAABB_worldVert = new Vec3();
/**
* Compute the local AABB for the trimesh
* @method computeLocalAABB
* @param {AABB} aabb
*/
Trimesh.prototype.computeLocalAABB = function(aabb){
var l = aabb.lowerBound,
u = aabb.upperBound,
n = this.vertices.length,
vertices = this.vertices,
v = computeLocalAABB_worldVert;
this.getVertex(0, v);
l.copy(v);
u.copy(v);
for(var i=0; i !== n; i++){
this.getVertex(i, v);
if(v.x < l.x){
l.x = v.x;
} else if(v.x > u.x){
u.x = v.x;
}
if(v.y < l.y){
l.y = v.y;
} else if(v.y > u.y){
u.y = v.y;
}
if(v.z < l.z){
l.z = v.z;
} else if(v.z > u.z){
u.z = v.z;
}
}
};
/**
* Update the .aabb property
* @method updateAABB
*/
Trimesh.prototype.updateAABB = function(){
this.computeLocalAABB(this.aabb);
};
/**
* Will update the .boundingSphereRadius property
* @method updateBoundingSphereRadius
*/
Trimesh.prototype.updateBoundingSphereRadius = function(){
// Assume points are distributed with local (0,0,0) as center
var max2 = 0;
var vertices = this.vertices;
var v = new Vec3();
for(var i=0, N=vertices.length / 3; i !== N; i++) {
this.getVertex(i, v);
var norm2 = v.norm2();
if(norm2 > max2){
max2 = norm2;
}
}
this.boundingSphereRadius = Math.sqrt(max2);
};
var tempWorldVertex = new Vec3();
var calculateWorldAABB_frame = new Transform();
var calculateWorldAABB_aabb = new AABB();
/**
* @method calculateWorldAABB
* @param {Vec3} pos
* @param {Quaternion} quat
* @param {Vec3} min
* @param {Vec3} max
*/
Trimesh.prototype.calculateWorldAABB = function(pos,quat,min,max){
/*
var n = this.vertices.length / 3,
verts = this.vertices;
var minx,miny,minz,maxx,maxy,maxz;
var v = tempWorldVertex;
for(var i=0; i<n; i++){
this.getVertex(i, v);
quat.vmult(v, v);
pos.vadd(v, v);
if (v.x < minx || minx===undefined){
minx = v.x;
} else if(v.x > maxx || maxx===undefined){
maxx = v.x;
}
if (v.y < miny || miny===undefined){
miny = v.y;
} else if(v.y > maxy || maxy===undefined){
maxy = v.y;
}
if (v.z < minz || minz===undefined){
minz = v.z;
} else if(v.z > maxz || maxz===undefined){
maxz = v.z;
}
}
min.set(minx,miny,minz);
max.set(maxx,maxy,maxz);
*/
// Faster approximation using local AABB
var frame = calculateWorldAABB_frame;
var result = calculateWorldAABB_aabb;
frame.position = pos;
frame.quaternion = quat;
this.aabb.toWorldFrame(frame, result);
min.copy(result.lowerBound);
max.copy(result.upperBound);
};
/**
* Get approximate volume
* @method volume
* @return {Number}
*/
Trimesh.prototype.volume = function(){
return 4.0 * Math.PI * this.boundingSphereRadius / 3.0;
};
/**
* Create a Trimesh instance, shaped as a torus.
* @static
* @method createTorus
* @param {number} [radius=1]
* @param {number} [tube=0.5]
* @param {number} [radialSegments=8]
* @param {number} [tubularSegments=6]
* @param {number} [arc=6.283185307179586]
* @return {Trimesh} A torus
*/
Trimesh.createTorus = function (radius, tube, radialSegments, tubularSegments, arc) {
radius = radius || 1;
tube = tube || 0.5;
radialSegments = radialSegments || 8;
tubularSegments = tubularSegments || 6;
arc = arc || Math.PI * 2;
var vertices = [];
var indices = [];
for ( var j = 0; j <= radialSegments; j ++ ) {
for ( var i = 0; i <= tubularSegments; i ++ ) {
var u = i / tubularSegments * arc;
var v = j / radialSegments * Math.PI * 2;
var x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
var y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
var z = tube * Math.sin( v );
vertices.push( x, y, z );
}
}
for ( var j = 1; j <= radialSegments; j ++ ) {
for ( var i = 1; i <= tubularSegments; i ++ ) {
var a = ( tubularSegments + 1 ) * j + i - 1;
var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
var d = ( tubularSegments + 1 ) * j + i;
indices.push(a, b, d);
indices.push(b, c, d);
}
}
return new Trimesh(vertices, indices);
};
},{"../collision/AABB":46,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../utils/Octree":94,"./Shape":87}],90:[function(require,module,exports){
module.exports = GSSolver;
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Solver = require('./Solver');
/**
* Constraint equation Gauss-Seidel solver.
* @class GSSolver
* @constructor
* @todo The spook parameters should be specified for each constraint, not globally.
* @author schteppe / https://github.com/schteppe
* @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf
* @extends Solver
*/
function GSSolver(){
Solver.call(this);
/**
* The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations.
* @property iterations
* @type {Number}
* @todo write more about solver and iterations in the wiki
*/
this.iterations = 10;
/**
* When tolerance is reached, the system is assumed to be converged.
* @property tolerance
* @type {Number}
*/
this.tolerance = 1e-7;
}
GSSolver.prototype = new Solver();
var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve.
var GSSolver_solve_invCs = [];
var GSSolver_solve_Bs = [];
GSSolver.prototype.solve = function(dt,world){
var iter = 0,
maxIter = this.iterations,
tolSquared = this.tolerance*this.tolerance,
equations = this.equations,
Neq = equations.length,
bodies = world.bodies,
Nbodies = bodies.length,
h = dt,
q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj;
// Update solve mass
if(Neq !== 0){
for(var i=0; i!==Nbodies; i++){
bodies[i].updateSolveMassProperties();
}
}
// Things that does not change during iteration can be computed once
var invCs = GSSolver_solve_invCs,
Bs = GSSolver_solve_Bs,
lambda = GSSolver_solve_lambda;
invCs.length = Neq;
Bs.length = Neq;
lambda.length = Neq;
for(var i=0; i!==Neq; i++){
var c = equations[i];
lambda[i] = 0.0;
Bs[i] = c.computeB(h);
invCs[i] = 1.0 / c.computeC();
}
if(Neq !== 0){
// Reset vlambda
for(var i=0; i!==Nbodies; i++){
var b=bodies[i],
vlambda=b.vlambda,
wlambda=b.wlambda;
vlambda.set(0,0,0);
wlambda.set(0,0,0);
}
// Iterate over equations
for(iter=0; iter!==maxIter; iter++){
// Accumulate the total error for each iteration.
deltalambdaTot = 0.0;
for(var j=0; j!==Neq; j++){
var c = equations[j];
// Compute iteration
B = Bs[j];
invC = invCs[j];
lambdaj = lambda[j];
GWlambda = c.computeGWlambda();
deltalambda = invC * ( B - GWlambda - c.eps * lambdaj );
// Clamp if we are not within the min/max interval
if(lambdaj + deltalambda < c.minForce){
deltalambda = c.minForce - lambdaj;
} else if(lambdaj + deltalambda > c.maxForce){
deltalambda = c.maxForce - lambdaj;
}
lambda[j] += deltalambda;
deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda)
c.addToWlambda(deltalambda);
}
// If the total error is small enough - stop iterate
if(deltalambdaTot*deltalambdaTot < tolSquared){
break;
}
}
// Add result to velocity
for(var i=0; i!==Nbodies; i++){
var b=bodies[i],
v=b.velocity,
w=b.angularVelocity;
b.vlambda.vmul(b.linearFactor, b.vlambda);
v.vadd(b.vlambda, v);
b.wlambda.vmul(b.angularFactor, b.wlambda);
w.vadd(b.wlambda, w);
}
// Set the .multiplier property of each equation
var l = equations.length;
var invDt = 1 / h;
while(l--){
equations[l].multiplier = lambda[l] * invDt;
}
}
return iter;
};
},{"../math/Quaternion":72,"../math/Vec3":74,"./Solver":91}],91:[function(require,module,exports){
module.exports = Solver;
/**
* Constraint equation solver base class.
* @class Solver
* @constructor
* @author schteppe / https://github.com/schteppe
*/
function Solver(){
/**
* All equations to be solved
* @property {Array} equations
*/
this.equations = [];
}
/**
* Should be implemented in subclasses!
* @method solve
* @param {Number} dt
* @param {World} world
*/
Solver.prototype.solve = function(dt,world){
// Should return the number of iterations done!
return 0;
};
/**
* Add an equation
* @method addEquation
* @param {Equation} eq
*/
Solver.prototype.addEquation = function(eq){
if (eq.enabled) {
this.equations.push(eq);
}
};
/**
* Remove an equation
* @method removeEquation
* @param {Equation} eq
*/
Solver.prototype.removeEquation = function(eq){
var eqs = this.equations;
var i = eqs.indexOf(eq);
if(i !== -1){
eqs.splice(i,1);
}
};
/**
* Add all equations
* @method removeAllEquations
*/
Solver.prototype.removeAllEquations = function(){
this.equations.length = 0;
};
},{}],92:[function(require,module,exports){
module.exports = SplitSolver;
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var Solver = require('./Solver');
var Body = require('../objects/Body');
/**
* Splits the equations into islands and solves them independently. Can improve performance.
* @class SplitSolver
* @constructor
* @extends Solver
* @param {Solver} subsolver
*/
function SplitSolver(subsolver){
Solver.call(this);
this.iterations = 10;
this.tolerance = 1e-7;
this.subsolver = subsolver;
this.nodes = [];
this.nodePool = [];
// Create needed nodes, reuse if possible
while(this.nodePool.length < 128){
this.nodePool.push(this.createNode());
}
}
SplitSolver.prototype = new Solver();
// Returns the number of subsystems
var SplitSolver_solve_nodes = []; // All allocated node objects
var SplitSolver_solve_nodePool = []; // All allocated node objects
var SplitSolver_solve_eqs = []; // Temp array
var SplitSolver_solve_bds = []; // Temp array
var SplitSolver_solve_dummyWorld = {bodies:[]}; // Temp object
var STATIC = Body.STATIC;
function getUnvisitedNode(nodes){
var Nnodes = nodes.length;
for(var i=0; i!==Nnodes; i++){
var node = nodes[i];
if(!node.visited && !(node.body.type & STATIC)){
return node;
}
}
return false;
}
var queue = [];
function bfs(root,visitFunc,bds,eqs){
queue.push(root);
root.visited = true;
visitFunc(root,bds,eqs);
while(queue.length) {
var node = queue.pop();
// Loop over unvisited child nodes
var child;
while((child = getUnvisitedNode(node.children))) {
child.visited = true;
visitFunc(child,bds,eqs);
queue.push(child);
}
}
}
function visitFunc(node,bds,eqs){
bds.push(node.body);
var Neqs = node.eqs.length;
for(var i=0; i!==Neqs; i++){
var eq = node.eqs[i];
if(eqs.indexOf(eq) === -1){
eqs.push(eq);
}
}
}
SplitSolver.prototype.createNode = function(){
return { body:null, children:[], eqs:[], visited:false };
};
/**
* Solve the subsystems
* @method solve
* @param {Number} dt
* @param {World} world
*/
SplitSolver.prototype.solve = function(dt,world){
var nodes=SplitSolver_solve_nodes,
nodePool=this.nodePool,
bodies=world.bodies,
equations=this.equations,
Neq=equations.length,
Nbodies=bodies.length,
subsolver=this.subsolver;
// Create needed nodes, reuse if possible
while(nodePool.length < Nbodies){
nodePool.push(this.createNode());
}
nodes.length = Nbodies;
for (var i = 0; i < Nbodies; i++) {
nodes[i] = nodePool[i];
}
// Reset node values
for(var i=0; i!==Nbodies; i++){
var node = nodes[i];
node.body = bodies[i];
node.children.length = 0;
node.eqs.length = 0;
node.visited = false;
}
for(var k=0; k!==Neq; k++){
var eq=equations[k],
i=bodies.indexOf(eq.bi),
j=bodies.indexOf(eq.bj),
ni=nodes[i],
nj=nodes[j];
ni.children.push(nj);
ni.eqs.push(eq);
nj.children.push(ni);
nj.eqs.push(eq);
}
var child, n=0, eqs=SplitSolver_solve_eqs;
subsolver.tolerance = this.tolerance;
subsolver.iterations = this.iterations;
var dummyWorld = SplitSolver_solve_dummyWorld;
while((child = getUnvisitedNode(nodes))){
eqs.length = 0;
dummyWorld.bodies.length = 0;
bfs(child, visitFunc, dummyWorld.bodies, eqs);
var Neqs = eqs.length;
eqs = eqs.sort(sortById);
for(var i=0; i!==Neqs; i++){
subsolver.addEquation(eqs[i]);
}
var iter = subsolver.solve(dt,dummyWorld);
subsolver.removeAllEquations();
n++;
}
return n;
};
function sortById(a, b){
return b.id - a.id;
}
},{"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"./Solver":91}],93:[function(require,module,exports){
/**
* Base class for objects that dispatches events.
* @class EventTarget
* @constructor
*/
var EventTarget = function () {
};
module.exports = EventTarget;
EventTarget.prototype = {
constructor: EventTarget,
/**
* Add an event listener
* @method addEventListener
* @param {String} type
* @param {Function} listener
* @return {EventTarget} The self object, for chainability.
*/
addEventListener: function ( type, listener ) {
if ( this._listeners === undefined ){ this._listeners = {}; }
var listeners = this._listeners;
if ( listeners[ type ] === undefined ) {
listeners[ type ] = [];
}
if ( listeners[ type ].indexOf( listener ) === - 1 ) {
listeners[ type ].push( listener );
}
return this;
},
/**
* Check if an event listener is added
* @method hasEventListener
* @param {String} type
* @param {Function} listener
* @return {Boolean}
*/
hasEventListener: function ( type, listener ) {
if ( this._listeners === undefined ){ return false; }
var listeners = this._listeners;
if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
return true;
}
return false;
},
/**
* Check if any event listener of the given type is added
* @method hasAnyEventListener
* @param {String} type
* @return {Boolean}
*/
hasAnyEventListener: function ( type ) {
if ( this._listeners === undefined ){ return false; }
var listeners = this._listeners;
return ( listeners[ type ] !== undefined );
},
/**
* Remove an event listener
* @method removeEventListener
* @param {String} type
* @param {Function} listener
* @return {EventTarget} The self object, for chainability.
*/
removeEventListener: function ( type, listener ) {
if ( this._listeners === undefined ){ return this; }
var listeners = this._listeners;
if ( listeners[type] === undefined ){ return this; }
var index = listeners[ type ].indexOf( listener );
if ( index !== - 1 ) {
listeners[ type ].splice( index, 1 );
}
return this;
},
/**
* Emit an event.
* @method dispatchEvent
* @param {Object} event
* @param {String} event.type
* @return {EventTarget} The self object, for chainability.
*/
dispatchEvent: function ( event ) {
if ( this._listeners === undefined ){ return this; }
var listeners = this._listeners;
var listenerArray = listeners[ event.type ];
if ( listenerArray !== undefined ) {
event.target = this;
for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
listenerArray[ i ].call( this, event );
}
}
return this;
}
};
},{}],94:[function(require,module,exports){
var AABB = require('../collision/AABB');
var Vec3 = require('../math/Vec3');
module.exports = Octree;
/**
* @class OctreeNode
* @param {object} [options]
* @param {Octree} [options.root]
* @param {AABB} [options.aabb]
*/
function OctreeNode(options){
options = options || {};
/**
* The root node
* @property {OctreeNode} root
*/
this.root = options.root || null;
/**
* Boundary of this node
* @property {AABB} aabb
*/
this.aabb = options.aabb ? options.aabb.clone() : new AABB();
/**
* Contained data at the current node level.
* @property {Array} data
*/
this.data = [];
/**
* Children to this node
* @property {Array} children
*/
this.children = [];
}
/**
* @class Octree
* @param {AABB} aabb The total AABB of the tree
* @param {object} [options]
* @param {number} [options.maxDepth=8]
* @extends OctreeNode
*/
function Octree(aabb, options){
options = options || {};
options.root = null;
options.aabb = aabb;
OctreeNode.call(this, options);
/**
* Maximum subdivision depth
* @property {number} maxDepth
*/
this.maxDepth = typeof(options.maxDepth) !== 'undefined' ? options.maxDepth : 8;
}
Octree.prototype = new OctreeNode();
OctreeNode.prototype.reset = function(aabb, options){
this.children.length = this.data.length = 0;
};
/**
* Insert data into this node
* @method insert
* @param {AABB} aabb
* @param {object} elementData
* @return {boolean} True if successful, otherwise false
*/
OctreeNode.prototype.insert = function(aabb, elementData, level){
var nodeData = this.data;
level = level || 0;
// Ignore objects that do not belong in this node
if (!this.aabb.contains(aabb)){
return false; // object cannot be added
}
var children = this.children;
if(level < (this.maxDepth || this.root.maxDepth)){
// Subdivide if there are no children yet
var subdivided = false;
if (!children.length){
this.subdivide();
subdivided = true;
}
// add to whichever node will accept it
for (var i = 0; i !== 8; i++) {
if (children[i].insert(aabb, elementData, level + 1)){
return true;
}
}
if(subdivided){
// No children accepted! Might as well just remove em since they contain none
children.length = 0;
}
}
// Too deep, or children didnt want it. add it in current node
nodeData.push(elementData);
return true;
};
var halfDiagonal = new Vec3();
/**
* Create 8 equally sized children nodes and put them in the .children array.
* @method subdivide
*/
OctreeNode.prototype.subdivide = function() {
var aabb = this.aabb;
var l = aabb.lowerBound;
var u = aabb.upperBound;
var children = this.children;
children.push(
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,0) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,0) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,0) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,1) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,1) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,1) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,1) }) }),
new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,0) }) })
);
u.vsub(l, halfDiagonal);
halfDiagonal.scale(0.5, halfDiagonal);
var root = this.root || this;
for (var i = 0; i !== 8; i++) {
var child = children[i];
// Set current node as root
child.root = root;
// Compute bounds
var lowerBound = child.aabb.lowerBound;
lowerBound.x *= halfDiagonal.x;
lowerBound.y *= halfDiagonal.y;
lowerBound.z *= halfDiagonal.z;
lowerBound.vadd(l, lowerBound);
// Upper bound is always lower bound + halfDiagonal
lowerBound.vadd(halfDiagonal, child.aabb.upperBound);
}
};
/**
* Get all data, potentially within an AABB
* @method aabbQuery
* @param {AABB} aabb
* @param {array} result
* @return {array} The "result" object
*/
OctreeNode.prototype.aabbQuery = function(aabb, result) {
var nodeData = this.data;
// abort if the range does not intersect this node
// if (!this.aabb.overlaps(aabb)){
// return result;
// }
// Add objects at this level
// Array.prototype.push.apply(result, nodeData);
// Add child data
// @todo unwrap recursion into a queue / loop, that's faster in JS
var children = this.children;
// for (var i = 0, N = this.children.length; i !== N; i++) {
// children[i].aabbQuery(aabb, result);
// }
var queue = [this];
while (queue.length) {
var node = queue.pop();
if (node.aabb.overlaps(aabb)){
Array.prototype.push.apply(result, node.data);
}
Array.prototype.push.apply(queue, node.children);
}
return result;
};
var tmpAABB = new AABB();
/**
* Get all data, potentially intersected by a ray.
* @method rayQuery
* @param {Ray} ray
* @param {Transform} treeTransform
* @param {array} result
* @return {array} The "result" object
*/
OctreeNode.prototype.rayQuery = function(ray, treeTransform, result) {
// Use aabb query for now.
// @todo implement real ray query which needs less lookups
ray.getAABB(tmpAABB);
tmpAABB.toLocalFrame(treeTransform, tmpAABB);
this.aabbQuery(tmpAABB, result);
return result;
};
/**
* @method removeEmptyNodes
*/
OctreeNode.prototype.removeEmptyNodes = function() {
var queue = [this];
while (queue.length) {
var node = queue.pop();
for (var i = node.children.length - 1; i >= 0; i--) {
if(!node.children[i].data.length){
node.children.splice(i, 1);
}
}
Array.prototype.push.apply(queue, node.children);
}
};
},{"../collision/AABB":46,"../math/Vec3":74}],95:[function(require,module,exports){
module.exports = Pool;
/**
* For pooling objects that can be reused.
* @class Pool
* @constructor
*/
function Pool(){
/**
* The pooled objects
* @property {Array} objects
*/
this.objects = [];
/**
* Constructor of the objects
* @property {mixed} type
*/
this.type = Object;
}
/**
* Release an object after use
* @method release
* @param {Object} obj
*/
Pool.prototype.release = function(){
var Nargs = arguments.length;
for(var i=0; i!==Nargs; i++){
this.objects.push(arguments[i]);
}
return this;
};
/**
* Get an object
* @method get
* @return {mixed}
*/
Pool.prototype.get = function(){
if(this.objects.length===0){
return this.constructObject();
} else {
return this.objects.pop();
}
};
/**
* Construct an object. Should be implmented in each subclass.
* @method constructObject
* @return {mixed}
*/
Pool.prototype.constructObject = function(){
throw new Error("constructObject() not implemented in this Pool subclass yet!");
};
/**
* @method resize
* @param {number} size
* @return {Pool} Self, for chaining
*/
Pool.prototype.resize = function (size) {
var objects = this.objects;
while (objects.length > size) {
objects.pop();
}
while (objects.length < size) {
objects.push(this.constructObject());
}
return this;
};
},{}],96:[function(require,module,exports){
module.exports = TupleDictionary;
/**
* @class TupleDictionary
* @constructor
*/
function TupleDictionary() {
/**
* The data storage
* @property data
* @type {Object}
*/
this.data = { keys:[] };
}
/**
* @method get
* @param {Number} i
* @param {Number} j
* @return {Number}
*/
TupleDictionary.prototype.get = function(i, j) {
if (i > j) {
// swap
var temp = j;
j = i;
i = temp;
}
return this.data[i+'-'+j];
};
/**
* @method set
* @param {Number} i
* @param {Number} j
* @param {Number} value
*/
TupleDictionary.prototype.set = function(i, j, value) {
if (i > j) {
var temp = j;
j = i;
i = temp;
}
var key = i+'-'+j;
// Check if key already exists
if(!this.get(i,j)){
this.data.keys.push(key);
}
this.data[key] = value;
};
/**
* @method reset
*/
TupleDictionary.prototype.reset = function() {
var data = this.data,
keys = data.keys;
while(keys.length > 0){
var key = keys.pop();
delete data[key];
}
};
},{}],97:[function(require,module,exports){
function Utils(){}
module.exports = Utils;
/**
* Extend an options object with default values.
* @static
* @method defaults
* @param {object} options The options object. May be falsy: in this case, a new object is created and returned.
* @param {object} defaults An object containing default values.
* @return {object} The modified options object.
*/
Utils.defaults = function(options, defaults){
options = options || {};
for(var key in defaults){
if(!(key in options)){
options[key] = defaults[key];
}
}
return options;
};
},{}],98:[function(require,module,exports){
module.exports = Vec3Pool;
var Vec3 = require('../math/Vec3');
var Pool = require('./Pool');
/**
* @class Vec3Pool
* @constructor
* @extends Pool
*/
function Vec3Pool(){
Pool.call(this);
this.type = Vec3;
}
Vec3Pool.prototype = new Pool();
/**
* Construct a vector
* @method constructObject
* @return {Vec3}
*/
Vec3Pool.prototype.constructObject = function(){
return new Vec3();
};
},{"../math/Vec3":74,"./Pool":95}],99:[function(require,module,exports){
module.exports = Narrowphase;
var AABB = require('../collision/AABB');
var Body = require('../objects/Body');
var Shape = require('../shapes/Shape');
var Ray = require('../collision/Ray');
var Vec3 = require('../math/Vec3');
var Transform = require('../math/Transform');
var ConvexPolyhedron = require('../shapes/ConvexPolyhedron');
var Quaternion = require('../math/Quaternion');
var Solver = require('../solver/Solver');
var Vec3Pool = require('../utils/Vec3Pool');
var ContactEquation = require('../equations/ContactEquation');
var FrictionEquation = require('../equations/FrictionEquation');
/**
* Helper class for the World. Generates ContactEquations.
* @class Narrowphase
* @constructor
* @todo Sphere-ConvexPolyhedron contacts
* @todo Contact reduction
* @todo should move methods to prototype
*/
function Narrowphase(world){
/**
* Internal storage of pooled contact points.
* @property {Array} contactPointPool
*/
this.contactPointPool = [];
this.frictionEquationPool = [];
this.result = [];
this.frictionResult = [];
/**
* Pooled vectors.
* @property {Vec3Pool} v3pool
*/
this.v3pool = new Vec3Pool();
this.world = world;
this.currentContactMaterial = null;
/**
* @property {Boolean} enableFrictionReduction
*/
this.enableFrictionReduction = false;
}
/**
* Make a contact object, by using the internal pool or creating a new one.
* @method createContactEquation
* @param {Body} bi
* @param {Body} bj
* @param {Shape} si
* @param {Shape} sj
* @param {Shape} overrideShapeA
* @param {Shape} overrideShapeB
* @return {ContactEquation}
*/
Narrowphase.prototype.createContactEquation = function(bi, bj, si, sj, overrideShapeA, overrideShapeB){
var c;
if(this.contactPointPool.length){
c = this.contactPointPool.pop();
c.bi = bi;
c.bj = bj;
} else {
c = new ContactEquation(bi, bj);
}
c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse;
var cm = this.currentContactMaterial;
c.restitution = cm.restitution;
c.setSpookParams(
cm.contactEquationStiffness,
cm.contactEquationRelaxation,
this.world.dt
);
var matA = si.material || bi.material;
var matB = sj.material || bj.material;
if(matA && matB && matA.restitution >= 0 && matB.restitution >= 0){
c.restitution = matA.restitution * matB.restitution;
}
c.si = overrideShapeA || si;
c.sj = overrideShapeB || sj;
return c;
};
Narrowphase.prototype.createFrictionEquationsFromContact = function(contactEquation, outArray){
var bodyA = contactEquation.bi;
var bodyB = contactEquation.bj;
var shapeA = contactEquation.si;
var shapeB = contactEquation.sj;
var world = this.world;
var cm = this.currentContactMaterial;
// If friction or restitution were specified in the material, use them
var friction = cm.friction;
var matA = shapeA.material || bodyA.material;
var matB = shapeB.material || bodyB.material;
if(matA && matB && matA.friction >= 0 && matB.friction >= 0){
friction = matA.friction * matB.friction;
}
if(friction > 0){
// Create 2 tangent equations
var mug = friction * world.gravity.length();
var reducedMass = (bodyA.invMass + bodyB.invMass);
if(reducedMass > 0){
reducedMass = 1/reducedMass;
}
var pool = this.frictionEquationPool;
var c1 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass);
var c2 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass);
c1.bi = c2.bi = bodyA;
c1.bj = c2.bj = bodyB;
c1.minForce = c2.minForce = -mug*reducedMass;
c1.maxForce = c2.maxForce = mug*reducedMass;
// Copy over the relative vectors
c1.ri.copy(contactEquation.ri);
c1.rj.copy(contactEquation.rj);
c2.ri.copy(contactEquation.ri);
c2.rj.copy(contactEquation.rj);
// Construct tangents
contactEquation.ni.tangents(c1.t, c2.t);
// Set spook params
c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt);
c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt);
c1.enabled = c2.enabled = contactEquation.enabled;
outArray.push(c1, c2);
return true;
}
return false;
};
var averageNormal = new Vec3();
var averageContactPointA = new Vec3();
var averageContactPointB = new Vec3();
// Take the average N latest contact point on the plane.
Narrowphase.prototype.createFrictionFromAverage = function(numContacts){
// The last contactEquation
var c = this.result[this.result.length - 1];
// Create the result: two "average" friction equations
if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) {
return;
}
var f1 = this.frictionResult[this.frictionResult.length - 2];
var f2 = this.frictionResult[this.frictionResult.length - 1];
averageNormal.setZero();
averageContactPointA.setZero();
averageContactPointB.setZero();
var bodyA = c.bi;
var bodyB = c.bj;
for(var i=0; i!==numContacts; i++){
c = this.result[this.result.length - 1 - i];
if(c.bodyA !== bodyA){
averageNormal.vadd(c.ni, averageNormal);
averageContactPointA.vadd(c.ri, averageContactPointA);
averageContactPointB.vadd(c.rj, averageContactPointB);
} else {
averageNormal.vsub(c.ni, averageNormal);
averageContactPointA.vadd(c.rj, averageContactPointA);
averageContactPointB.vadd(c.ri, averageContactPointB);
}
}
var invNumContacts = 1 / numContacts;
averageContactPointA.scale(invNumContacts, f1.ri);
averageContactPointB.scale(invNumContacts, f1.rj);
f2.ri.copy(f1.ri); // Should be the same
f2.rj.copy(f1.rj);
averageNormal.normalize();
averageNormal.tangents(f1.t, f2.t);
// return eq;
};
var tmpVec1 = new Vec3();
var tmpVec2 = new Vec3();
var tmpQuat1 = new Quaternion();
var tmpQuat2 = new Quaternion();
/**
* Generate all contacts between a list of body pairs
* @method getContacts
* @param {array} p1 Array of body indices
* @param {array} p2 Array of body indices
* @param {World} world
* @param {array} result Array to store generated contacts
* @param {array} oldcontacts Optional. Array of reusable contact objects
*/
Narrowphase.prototype.getContacts = function(p1, p2, world, result, oldcontacts, frictionResult, frictionPool){
// Save old contact objects
this.contactPointPool = oldcontacts;
this.frictionEquationPool = frictionPool;
this.result = result;
this.frictionResult = frictionResult;
var qi = tmpQuat1;
var qj = tmpQuat2;
var xi = tmpVec1;
var xj = tmpVec2;
for(var k=0, N=p1.length; k!==N; k++){
// Get current collision bodies
var bi = p1[k],
bj = p2[k];
// Get contact material
var bodyContactMaterial = null;
if(bi.material && bj.material){
bodyContactMaterial = world.getContactMaterial(bi.material,bj.material) || null;
}
var justTest = (
(
(bi.type & Body.KINEMATIC) && (bj.type & Body.STATIC)
) || (
(bi.type & Body.STATIC) && (bj.type & Body.KINEMATIC)
) || (
(bi.type & Body.KINEMATIC) && (bj.type & Body.KINEMATIC)
)
);
for (var i = 0; i < bi.shapes.length; i++) {
bi.quaternion.mult(bi.shapeOrientations[i], qi);
bi.quaternion.vmult(bi.shapeOffsets[i], xi);
xi.vadd(bi.position, xi);
var si = bi.shapes[i];
for (var j = 0; j < bj.shapes.length; j++) {
// Compute world transform of shapes
bj.quaternion.mult(bj.shapeOrientations[j], qj);
bj.quaternion.vmult(bj.shapeOffsets[j], xj);
xj.vadd(bj.position, xj);
var sj = bj.shapes[j];
if(!((si.collisionFilterMask & sj.collisionFilterGroup) && (sj.collisionFilterMask & si.collisionFilterGroup))){
continue;
}
if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
continue;
}
// Get collision material
var shapeContactMaterial = null;
if(si.material && sj.material){
shapeContactMaterial = world.getContactMaterial(si.material,sj.material) || null;
}
this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial;
// Get contacts
var resolver = this[si.type | sj.type];
if(resolver){
var retval = false;
if (si.type < sj.type) {
retval = resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest);
} else {
retval = resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj, justTest);
}
if(retval && justTest){
// Register overlap
world.shapeOverlapKeeper.set(si.id, sj.id);
world.bodyOverlapKeeper.set(bi.id, bj.id);
}
}
}
}
}
};
var numWarnings = 0;
var maxWarnings = 10;
function warn(msg){
if(numWarnings > maxWarnings){
return;
}
numWarnings++;
console.warn(msg);
}
Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] =
Narrowphase.prototype.boxBox = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
si.convexPolyhedronRepresentation.material = si.material;
sj.convexPolyhedronRepresentation.material = sj.material;
si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse;
return this.convexConvex(si.convexPolyhedronRepresentation,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj,justTest);
};
Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] =
Narrowphase.prototype.boxConvex = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
si.convexPolyhedronRepresentation.material = si.material;
si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
return this.convexConvex(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest);
};
Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] =
Narrowphase.prototype.boxParticle = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
si.convexPolyhedronRepresentation.material = si.material;
si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
return this.convexParticle(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest);
};
/**
* @method sphereSphere
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.SPHERE] =
Narrowphase.prototype.sphereSphere = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
if(justTest){
return xi.distanceSquared(xj) < Math.pow(si.radius + sj.radius, 2);
}
// We will have only one contact in this case
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
// Contact normal
xj.vsub(xi, r.ni);
r.ni.normalize();
// Contact point locations
r.ri.copy(r.ni);
r.rj.copy(r.ni);
r.ri.mult(si.radius, r.ri);
r.rj.mult(-sj.radius, r.rj);
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
};
/**
* @method planeTrimesh
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
var planeTrimesh_normal = new Vec3();
var planeTrimesh_relpos = new Vec3();
var planeTrimesh_projected = new Vec3();
Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] =
Narrowphase.prototype.planeTrimesh = function(
planeShape,
trimeshShape,
planePos,
trimeshPos,
planeQuat,
trimeshQuat,
planeBody,
trimeshBody,
rsi,
rsj,
justTest
){
// Make contacts!
var v = new Vec3();
var normal = planeTrimesh_normal;
normal.set(0,0,1);
planeQuat.vmult(normal,normal); // Turn normal according to plane
for(var i=0; i<trimeshShape.vertices.length / 3; i++){
// Get world vertex from trimesh
trimeshShape.getVertex(i, v);
// Safe up
var v2 = new Vec3();
v2.copy(v);
Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v);
// Check plane side
var relpos = planeTrimesh_relpos;
v.vsub(planePos, relpos);
var dot = normal.dot(relpos);
if(dot <= 0.0){
if(justTest){
return true;
}
var r = this.createContactEquation(planeBody,trimeshBody,planeShape,trimeshShape,rsi,rsj);
r.ni.copy(normal); // Contact normal is the plane normal
// Get vertex position projected on plane
var projected = planeTrimesh_projected;
normal.scale(relpos.dot(normal), projected);
v.vsub(projected,projected);
// ri is the projected world position minus plane position
r.ri.copy(projected);
r.ri.vsub(planeBody.position, r.ri);
r.rj.copy(v);
r.rj.vsub(trimeshBody.position, r.rj);
// Store result
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
};
/**
* @method sphereTrimesh
* @param {Shape} sphereShape
* @param {Shape} trimeshShape
* @param {Vec3} spherePos
* @param {Vec3} trimeshPos
* @param {Quaternion} sphereQuat
* @param {Quaternion} trimeshQuat
* @param {Body} sphereBody
* @param {Body} trimeshBody
*/
var sphereTrimesh_normal = new Vec3();
var sphereTrimesh_relpos = new Vec3();
var sphereTrimesh_projected = new Vec3();
var sphereTrimesh_v = new Vec3();
var sphereTrimesh_v2 = new Vec3();
var sphereTrimesh_edgeVertexA = new Vec3();
var sphereTrimesh_edgeVertexB = new Vec3();
var sphereTrimesh_edgeVector = new Vec3();
var sphereTrimesh_edgeVectorUnit = new Vec3();
var sphereTrimesh_localSpherePos = new Vec3();
var sphereTrimesh_tmp = new Vec3();
var sphereTrimesh_va = new Vec3();
var sphereTrimesh_vb = new Vec3();
var sphereTrimesh_vc = new Vec3();
var sphereTrimesh_localSphereAABB = new AABB();
var sphereTrimesh_triangles = [];
Narrowphase.prototype[Shape.types.SPHERE | Shape.types.TRIMESH] =
Narrowphase.prototype.sphereTrimesh = function (
sphereShape,
trimeshShape,
spherePos,
trimeshPos,
sphereQuat,
trimeshQuat,
sphereBody,
trimeshBody,
rsi,
rsj,
justTest
) {
var edgeVertexA = sphereTrimesh_edgeVertexA;
var edgeVertexB = sphereTrimesh_edgeVertexB;
var edgeVector = sphereTrimesh_edgeVector;
var edgeVectorUnit = sphereTrimesh_edgeVectorUnit;
var localSpherePos = sphereTrimesh_localSpherePos;
var tmp = sphereTrimesh_tmp;
var localSphereAABB = sphereTrimesh_localSphereAABB;
var v2 = sphereTrimesh_v2;
var relpos = sphereTrimesh_relpos;
var triangles = sphereTrimesh_triangles;
// Convert sphere position to local in the trimesh
Transform.pointToLocalFrame(trimeshPos, trimeshQuat, spherePos, localSpherePos);
// Get the aabb of the sphere locally in the trimesh
var sphereRadius = sphereShape.radius;
localSphereAABB.lowerBound.set(
localSpherePos.x - sphereRadius,
localSpherePos.y - sphereRadius,
localSpherePos.z - sphereRadius
);
localSphereAABB.upperBound.set(
localSpherePos.x + sphereRadius,
localSpherePos.y + sphereRadius,
localSpherePos.z + sphereRadius
);
trimeshShape.getTrianglesInAABB(localSphereAABB, triangles);
//for (var i = 0; i < trimeshShape.indices.length / 3; i++) triangles.push(i); // All
// Vertices
var v = sphereTrimesh_v;
var radiusSquared = sphereShape.radius * sphereShape.radius;
for(var i=0; i<triangles.length; i++){
for (var j = 0; j < 3; j++) {
trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], v);
// Check vertex overlap in sphere
v.vsub(localSpherePos, relpos);
if(relpos.norm2() <= radiusSquared){
// Safe up
v2.copy(v);
Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v);
v.vsub(spherePos, relpos);
if(justTest){
return true;
}
var r = this.createContactEquation(sphereBody,trimeshBody,sphereShape,trimeshShape,rsi,rsj);
r.ni.copy(relpos);
r.ni.normalize();
// ri is the vector from sphere center to the sphere surface
r.ri.copy(r.ni);
r.ri.scale(sphereShape.radius, r.ri);
r.ri.vadd(spherePos, r.ri);
r.ri.vsub(sphereBody.position, r.ri);
r.rj.copy(v);
r.rj.vsub(trimeshBody.position, r.rj);
// Store result
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
}
// Check all edges
for(var i=0; i<triangles.length; i++){
for (var j = 0; j < 3; j++) {
trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], edgeVertexA);
trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + ((j+1)%3)], edgeVertexB);
edgeVertexB.vsub(edgeVertexA, edgeVector);
// Project sphere position to the edge
localSpherePos.vsub(edgeVertexB, tmp);
var positionAlongEdgeB = tmp.dot(edgeVector);
localSpherePos.vsub(edgeVertexA, tmp);
var positionAlongEdgeA = tmp.dot(edgeVector);
if(positionAlongEdgeA > 0 && positionAlongEdgeB < 0){
// Now check the orthogonal distance from edge to sphere center
localSpherePos.vsub(edgeVertexA, tmp);
edgeVectorUnit.copy(edgeVector);
edgeVectorUnit.normalize();
positionAlongEdgeA = tmp.dot(edgeVectorUnit);
edgeVectorUnit.scale(positionAlongEdgeA, tmp);
tmp.vadd(edgeVertexA, tmp);
// tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame
var dist = tmp.distanceTo(localSpherePos);
if(dist < sphereShape.radius){
if(justTest){
return true;
}
var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape,rsi,rsj);
tmp.vsub(localSpherePos, r.ni);
r.ni.normalize();
r.ni.scale(sphereShape.radius, r.ri);
Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp);
tmp.vsub(trimeshBody.position, r.rj);
Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni);
Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
}
}
// Triangle faces
var va = sphereTrimesh_va;
var vb = sphereTrimesh_vb;
var vc = sphereTrimesh_vc;
var normal = sphereTrimesh_normal;
for(var i=0, N = triangles.length; i !== N; i++){
trimeshShape.getTriangleVertices(triangles[i], va, vb, vc);
trimeshShape.getNormal(triangles[i], normal);
localSpherePos.vsub(va, tmp);
var dist = tmp.dot(normal);
normal.scale(dist, tmp);
localSpherePos.vsub(tmp, tmp);
// tmp is now the sphere position projected to the triangle plane
dist = tmp.distanceTo(localSpherePos);
if(Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius){
if(justTest){
return true;
}
var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape,rsi,rsj);
tmp.vsub(localSpherePos, r.ni);
r.ni.normalize();
r.ni.scale(sphereShape.radius, r.ri);
Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp);
tmp.vsub(trimeshBody.position, r.rj);
Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni);
Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
triangles.length = 0;
};
var point_on_plane_to_sphere = new Vec3();
var plane_to_sphere_ortho = new Vec3();
/**
* @method spherePlane
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] =
Narrowphase.prototype.spherePlane = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
// We will have one contact in this case
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
// Contact normal
r.ni.set(0,0,1);
qj.vmult(r.ni, r.ni);
r.ni.negate(r.ni); // body i is the sphere, flip normal
r.ni.normalize(); // Needed?
// Vector from sphere center to contact point
r.ni.mult(si.radius, r.ri);
// Project down sphere on plane
xi.vsub(xj, point_on_plane_to_sphere);
r.ni.mult(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho);
point_on_plane_to_sphere.vsub(plane_to_sphere_ortho,r.rj); // The sphere position projected to plane
if(-point_on_plane_to_sphere.dot(r.ni) <= si.radius){
if(justTest){
return true;
}
// Make it relative to the body
var ri = r.ri;
var rj = r.rj;
ri.vadd(xi, ri);
ri.vsub(bi.position, ri);
rj.vadd(xj, rj);
rj.vsub(bj.position, rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
};
// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html
var pointInPolygon_edge = new Vec3();
var pointInPolygon_edge_x_normal = new Vec3();
var pointInPolygon_vtp = new Vec3();
function pointInPolygon(verts, normal, p){
var positiveResult = null;
var N = verts.length;
for(var i=0; i!==N; i++){
var v = verts[i];
// Get edge to the next vertex
var edge = pointInPolygon_edge;
verts[(i+1) % (N)].vsub(v,edge);
// Get cross product between polygon normal and the edge
var edge_x_normal = pointInPolygon_edge_x_normal;
//var edge_x_normal = new Vec3();
edge.cross(normal,edge_x_normal);
// Get vector between point and current vertex
var vertex_to_p = pointInPolygon_vtp;
p.vsub(v,vertex_to_p);
// This dot product determines which side of the edge the point is
var r = edge_x_normal.dot(vertex_to_p);
// If all such dot products have same sign, we are inside the polygon.
if(positiveResult===null || (r>0 && positiveResult===true) || (r<=0 && positiveResult===false)){
if(positiveResult===null){
positiveResult = r>0;
}
continue;
} else {
return false; // Encountered some other sign. Exit.
}
}
// If we got here, all dot products were of the same sign.
return true;
}
var box_to_sphere = new Vec3();
var sphereBox_ns = new Vec3();
var sphereBox_ns1 = new Vec3();
var sphereBox_ns2 = new Vec3();
var sphereBox_sides = [new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3()];
var sphereBox_sphere_to_corner = new Vec3();
var sphereBox_side_ns = new Vec3();
var sphereBox_side_ns1 = new Vec3();
var sphereBox_side_ns2 = new Vec3();
/**
* @method sphereBox
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] =
Narrowphase.prototype.sphereBox = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
var v3pool = this.v3pool;
// we refer to the box as body j
var sides = sphereBox_sides;
xi.vsub(xj,box_to_sphere);
sj.getSideNormals(sides,qj);
var R = si.radius;
var penetrating_sides = [];
// Check side (plane) intersections
var found = false;
// Store the resulting side penetration info
var side_ns = sphereBox_side_ns;
var side_ns1 = sphereBox_side_ns1;
var side_ns2 = sphereBox_side_ns2;
var side_h = null;
var side_penetrations = 0;
var side_dot1 = 0;
var side_dot2 = 0;
var side_distance = null;
for(var idx=0,nsides=sides.length; idx!==nsides && found===false; idx++){
// Get the plane side normal (ns)
var ns = sphereBox_ns;
ns.copy(sides[idx]);
var h = ns.norm();
ns.normalize();
// The normal/distance dot product tells which side of the plane we are
var dot = box_to_sphere.dot(ns);
if(dot<h+R && dot>0){
// Intersects plane. Now check the other two dimensions
var ns1 = sphereBox_ns1;
var ns2 = sphereBox_ns2;
ns1.copy(sides[(idx+1)%3]);
ns2.copy(sides[(idx+2)%3]);
var h1 = ns1.norm();
var h2 = ns2.norm();
ns1.normalize();
ns2.normalize();
var dot1 = box_to_sphere.dot(ns1);
var dot2 = box_to_sphere.dot(ns2);
if(dot1<h1 && dot1>-h1 && dot2<h2 && dot2>-h2){
var dist = Math.abs(dot-h-R);
if(side_distance===null || dist < side_distance){
side_distance = dist;
side_dot1 = dot1;
side_dot2 = dot2;
side_h = h;
side_ns.copy(ns);
side_ns1.copy(ns1);
side_ns2.copy(ns2);
side_penetrations++;
if(justTest){
return true;
}
}
}
}
}
if(side_penetrations){
found = true;
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
side_ns.mult(-R,r.ri); // Sphere r
r.ni.copy(side_ns);
r.ni.negate(r.ni); // Normal should be out of sphere
side_ns.mult(side_h,side_ns);
side_ns1.mult(side_dot1,side_ns1);
side_ns.vadd(side_ns1,side_ns);
side_ns2.mult(side_dot2,side_ns2);
side_ns.vadd(side_ns2,r.rj);
// Make relative to bodies
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
// Check corners
var rj = v3pool.get();
var sphere_to_corner = sphereBox_sphere_to_corner;
for(var j=0; j!==2 && !found; j++){
for(var k=0; k!==2 && !found; k++){
for(var l=0; l!==2 && !found; l++){
rj.set(0,0,0);
if(j){
rj.vadd(sides[0],rj);
} else {
rj.vsub(sides[0],rj);
}
if(k){
rj.vadd(sides[1],rj);
} else {
rj.vsub(sides[1],rj);
}
if(l){
rj.vadd(sides[2],rj);
} else {
rj.vsub(sides[2],rj);
}
// World position of corner
xj.vadd(rj,sphere_to_corner);
sphere_to_corner.vsub(xi,sphere_to_corner);
if(sphere_to_corner.norm2() < R*R){
if(justTest){
return true;
}
found = true;
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
r.ri.copy(sphere_to_corner);
r.ri.normalize();
r.ni.copy(r.ri);
r.ri.mult(R,r.ri);
r.rj.copy(rj);
// Make relative to bodies
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
}
}
v3pool.release(rj);
rj = null;
// Check edges
var edgeTangent = v3pool.get();
var edgeCenter = v3pool.get();
var r = v3pool.get(); // r = edge center to sphere center
var orthogonal = v3pool.get();
var dist = v3pool.get();
var Nsides = sides.length;
for(var j=0; j!==Nsides && !found; j++){
for(var k=0; k!==Nsides && !found; k++){
if(j%3 !== k%3){
// Get edge tangent
sides[k].cross(sides[j],edgeTangent);
edgeTangent.normalize();
sides[j].vadd(sides[k], edgeCenter);
r.copy(xi);
r.vsub(edgeCenter,r);
r.vsub(xj,r);
var orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction
edgeTangent.mult(orthonorm,orthogonal); // Vector from edge center to sphere center in the tangent direction
// Find the third side orthogonal to this one
var l = 0;
while(l===j%3 || l===k%3){
l++;
}
// vec from edge center to sphere projected to the plane orthogonal to the edge tangent
dist.copy(xi);
dist.vsub(orthogonal,dist);
dist.vsub(edgeCenter,dist);
dist.vsub(xj,dist);
// Distances in tangent direction and distance in the plane orthogonal to it
var tdist = Math.abs(orthonorm);
var ndist = dist.norm();
if(tdist < sides[l].norm() && ndist<R){
if(justTest){
return true;
}
found = true;
var res = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
edgeCenter.vadd(orthogonal,res.rj); // box rj
res.rj.copy(res.rj);
dist.negate(res.ni);
res.ni.normalize();
res.ri.copy(res.rj);
res.ri.vadd(xj,res.ri);
res.ri.vsub(xi,res.ri);
res.ri.normalize();
res.ri.mult(R,res.ri);
// Make relative to bodies
res.ri.vadd(xi, res.ri);
res.ri.vsub(bi.position, res.ri);
res.rj.vadd(xj, res.rj);
res.rj.vsub(bj.position, res.rj);
this.result.push(res);
this.createFrictionEquationsFromContact(res, this.frictionResult);
}
}
}
}
v3pool.release(edgeTangent,edgeCenter,r,orthogonal,dist);
};
var convex_to_sphere = new Vec3();
var sphereConvex_edge = new Vec3();
var sphereConvex_edgeUnit = new Vec3();
var sphereConvex_sphereToCorner = new Vec3();
var sphereConvex_worldCorner = new Vec3();
var sphereConvex_worldNormal = new Vec3();
var sphereConvex_worldPoint = new Vec3();
var sphereConvex_worldSpherePointClosestToPlane = new Vec3();
var sphereConvex_penetrationVec = new Vec3();
var sphereConvex_sphereToWorldPoint = new Vec3();
/**
* @method sphereConvex
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.SPHERE | Shape.types.CONVEXPOLYHEDRON] =
Narrowphase.prototype.sphereConvex = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
var v3pool = this.v3pool;
xi.vsub(xj,convex_to_sphere);
var normals = sj.faceNormals;
var faces = sj.faces;
var verts = sj.vertices;
var R = si.radius;
var penetrating_sides = [];
// if(convex_to_sphere.norm2() > si.boundingSphereRadius + sj.boundingSphereRadius){
// return;
// }
// Check corners
for(var i=0; i!==verts.length; i++){
var v = verts[i];
// World position of corner
var worldCorner = sphereConvex_worldCorner;
qj.vmult(v,worldCorner);
xj.vadd(worldCorner,worldCorner);
var sphere_to_corner = sphereConvex_sphereToCorner;
worldCorner.vsub(xi, sphere_to_corner);
if(sphere_to_corner.norm2() < R * R){
if(justTest){
return true;
}
found = true;
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
r.ri.copy(sphere_to_corner);
r.ri.normalize();
r.ni.copy(r.ri);
r.ri.mult(R,r.ri);
worldCorner.vsub(xj,r.rj);
// Should be relative to the body.
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
// Should be relative to the body.
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
return;
}
}
// Check side (plane) intersections
var found = false;
for(var i=0, nfaces=faces.length; i!==nfaces && found===false; i++){
var normal = normals[i];
var face = faces[i];
// Get world-transformed normal of the face
var worldNormal = sphereConvex_worldNormal;
qj.vmult(normal,worldNormal);
// Get a world vertex from the face
var worldPoint = sphereConvex_worldPoint;
qj.vmult(verts[face[0]],worldPoint);
worldPoint.vadd(xj,worldPoint);
// Get a point on the sphere, closest to the face normal
var worldSpherePointClosestToPlane = sphereConvex_worldSpherePointClosestToPlane;
worldNormal.mult(-R, worldSpherePointClosestToPlane);
xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane);
// Vector from a face point to the closest point on the sphere
var penetrationVec = sphereConvex_penetrationVec;
worldSpherePointClosestToPlane.vsub(worldPoint,penetrationVec);
// The penetration. Negative value means overlap.
var penetration = penetrationVec.dot(worldNormal);
var worldPointToSphere = sphereConvex_sphereToWorldPoint;
xi.vsub(worldPoint, worldPointToSphere);
if(penetration < 0 && worldPointToSphere.dot(worldNormal)>0){
// Intersects plane. Now check if the sphere is inside the face polygon
var faceVerts = []; // Face vertices, in world coords
for(var j=0, Nverts=face.length; j!==Nverts; j++){
var worldVertex = v3pool.get();
qj.vmult(verts[face[j]], worldVertex);
xj.vadd(worldVertex,worldVertex);
faceVerts.push(worldVertex);
}
if(pointInPolygon(faceVerts,worldNormal,xi)){ // Is the sphere center in the face polygon?
if(justTest){
return true;
}
found = true;
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
worldNormal.mult(-R, r.ri); // Contact offset, from sphere center to contact
worldNormal.negate(r.ni); // Normal pointing out of sphere
var penetrationVec2 = v3pool.get();
worldNormal.mult(-penetration, penetrationVec2);
var penetrationSpherePoint = v3pool.get();
worldNormal.mult(-R, penetrationSpherePoint);
//xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj);
xi.vsub(xj,r.rj);
r.rj.vadd(penetrationSpherePoint,r.rj);
r.rj.vadd(penetrationVec2 , r.rj);
// Should be relative to the body.
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
// Should be relative to the body.
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
v3pool.release(penetrationVec2);
v3pool.release(penetrationSpherePoint);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
// Release world vertices
for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
v3pool.release(faceVerts[j]);
}
return; // We only expect *one* face contact
} else {
// Edge?
for(var j=0; j!==face.length; j++){
// Get two world transformed vertices
var v1 = v3pool.get();
var v2 = v3pool.get();
qj.vmult(verts[face[(j+1)%face.length]], v1);
qj.vmult(verts[face[(j+2)%face.length]], v2);
xj.vadd(v1, v1);
xj.vadd(v2, v2);
// Construct edge vector
var edge = sphereConvex_edge;
v2.vsub(v1,edge);
// Construct the same vector, but normalized
var edgeUnit = sphereConvex_edgeUnit;
edge.unit(edgeUnit);
// p is xi projected onto the edge
var p = v3pool.get();
var v1_to_xi = v3pool.get();
xi.vsub(v1, v1_to_xi);
var dot = v1_to_xi.dot(edgeUnit);
edgeUnit.mult(dot, p);
p.vadd(v1, p);
// Compute a vector from p to the center of the sphere
var xi_to_p = v3pool.get();
p.vsub(xi, xi_to_p);
// Collision if the edge-sphere distance is less than the radius
// AND if p is in between v1 and v2
if(dot > 0 && dot*dot<edge.norm2() && xi_to_p.norm2() < R*R){ // Collision if the edge-sphere distance is less than the radius
// Edge contact!
if(justTest){
return true;
}
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
p.vsub(xj,r.rj);
p.vsub(xi,r.ni);
r.ni.normalize();
r.ni.mult(R,r.ri);
// Should be relative to the body.
r.rj.vadd(xj, r.rj);
r.rj.vsub(bj.position, r.rj);
// Should be relative to the body.
r.ri.vadd(xi, r.ri);
r.ri.vsub(bi.position, r.ri);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
// Release world vertices
for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
v3pool.release(faceVerts[j]);
}
v3pool.release(v1);
v3pool.release(v2);
v3pool.release(p);
v3pool.release(xi_to_p);
v3pool.release(v1_to_xi);
return;
}
v3pool.release(v1);
v3pool.release(v2);
v3pool.release(p);
v3pool.release(xi_to_p);
v3pool.release(v1_to_xi);
}
}
// Release world vertices
for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
v3pool.release(faceVerts[j]);
}
}
}
};
var planeBox_normal = new Vec3();
var plane_to_corner = new Vec3();
/**
* @method planeBox
* @param {Array} result
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.PLANE | Shape.types.BOX] =
Narrowphase.prototype.planeBox = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
sj.convexPolyhedronRepresentation.material = sj.material;
sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse;
sj.convexPolyhedronRepresentation.id = sj.id;
return this.planeConvex(si,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj,justTest);
};
var planeConvex_v = new Vec3();
var planeConvex_normal = new Vec3();
var planeConvex_relpos = new Vec3();
var planeConvex_projected = new Vec3();
/**
* @method planeConvex
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.PLANE | Shape.types.CONVEXPOLYHEDRON] =
Narrowphase.prototype.planeConvex = function(
planeShape,
convexShape,
planePosition,
convexPosition,
planeQuat,
convexQuat,
planeBody,
convexBody,
si,
sj,
justTest
){
// Simply return the points behind the plane.
var worldVertex = planeConvex_v,
worldNormal = planeConvex_normal;
worldNormal.set(0,0,1);
planeQuat.vmult(worldNormal,worldNormal); // Turn normal according to plane orientation
var numContacts = 0;
var relpos = planeConvex_relpos;
for(var i = 0; i !== convexShape.vertices.length; i++){
// Get world convex vertex
worldVertex.copy(convexShape.vertices[i]);
convexQuat.vmult(worldVertex, worldVertex);
convexPosition.vadd(worldVertex, worldVertex);
worldVertex.vsub(planePosition, relpos);
var dot = worldNormal.dot(relpos);
if(dot <= 0.0){
if(justTest){
return true;
}
var r = this.createContactEquation(planeBody, convexBody, planeShape, convexShape, si, sj);
// Get vertex position projected on plane
var projected = planeConvex_projected;
worldNormal.mult(worldNormal.dot(relpos),projected);
worldVertex.vsub(projected, projected);
projected.vsub(planePosition, r.ri); // From plane to vertex projected on plane
r.ni.copy(worldNormal); // Contact normal is the plane normal out from plane
// rj is now just the vector from the convex center to the vertex
worldVertex.vsub(convexPosition, r.rj);
// Make it relative to the body
r.ri.vadd(planePosition, r.ri);
r.ri.vsub(planeBody.position, r.ri);
r.rj.vadd(convexPosition, r.rj);
r.rj.vsub(convexBody.position, r.rj);
this.result.push(r);
numContacts++;
if(!this.enableFrictionReduction){
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
}
if(this.enableFrictionReduction && numContacts){
this.createFrictionFromAverage(numContacts);
}
};
var convexConvex_sepAxis = new Vec3();
var convexConvex_q = new Vec3();
/**
* @method convexConvex
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON] =
Narrowphase.prototype.convexConvex = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest,faceListA,faceListB){
var sepAxis = convexConvex_sepAxis;
if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
return;
}
if(si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){
var res = [];
var q = convexConvex_q;
si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res);
var numContacts = 0;
for(var j = 0; j !== res.length; j++){
if(justTest){
return true;
}
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj),
ri = r.ri,
rj = r.rj;
sepAxis.negate(r.ni);
res[j].normal.negate(q);
q.mult(res[j].depth, q);
res[j].point.vadd(q, ri);
rj.copy(res[j].point);
// Contact points are in world coordinates. Transform back to relative
ri.vsub(xi,ri);
rj.vsub(xj,rj);
// Make relative to bodies
ri.vadd(xi, ri);
ri.vsub(bi.position, ri);
rj.vadd(xj, rj);
rj.vsub(bj.position, rj);
this.result.push(r);
numContacts++;
if(!this.enableFrictionReduction){
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
}
if(this.enableFrictionReduction && numContacts){
this.createFrictionFromAverage(numContacts);
}
}
};
/**
* @method convexTrimesh
* @param {Array} result
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
// Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] =
// Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){
// var sepAxis = convexConvex_sepAxis;
// if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
// return;
// }
// // Construct a temp hull for each triangle
// var hullB = new ConvexPolyhedron();
// hullB.faces = [[0,1,2]];
// var va = new Vec3();
// var vb = new Vec3();
// var vc = new Vec3();
// hullB.vertices = [
// va,
// vb,
// vc
// ];
// for (var i = 0; i < sj.indices.length / 3; i++) {
// var triangleNormal = new Vec3();
// sj.getNormal(i, triangleNormal);
// hullB.faceNormals = [triangleNormal];
// sj.getTriangleVertices(i, va, vb, vc);
// var d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj);
// if(!d){
// triangleNormal.scale(-1, triangleNormal);
// d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj);
// if(!d){
// continue;
// }
// }
// var res = [];
// var q = convexConvex_q;
// si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res);
// for(var j = 0; j !== res.length; j++){
// var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj),
// ri = r.ri,
// rj = r.rj;
// r.ni.copy(triangleNormal);
// r.ni.negate(r.ni);
// res[j].normal.negate(q);
// q.mult(res[j].depth, q);
// res[j].point.vadd(q, ri);
// rj.copy(res[j].point);
// // Contact points are in world coordinates. Transform back to relative
// ri.vsub(xi,ri);
// rj.vsub(xj,rj);
// // Make relative to bodies
// ri.vadd(xi, ri);
// ri.vsub(bi.position, ri);
// rj.vadd(xj, rj);
// rj.vsub(bj.position, rj);
// result.push(r);
// }
// }
// };
var particlePlane_normal = new Vec3();
var particlePlane_relpos = new Vec3();
var particlePlane_projected = new Vec3();
/**
* @method particlePlane
* @param {Array} result
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] =
Narrowphase.prototype.planeParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){
var normal = particlePlane_normal;
normal.set(0,0,1);
bj.quaternion.vmult(normal,normal); // Turn normal according to plane orientation
var relpos = particlePlane_relpos;
xi.vsub(bj.position,relpos);
var dot = normal.dot(relpos);
if(dot <= 0.0){
if(justTest){
return true;
}
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
r.ni.copy(normal); // Contact normal is the plane normal
r.ni.negate(r.ni);
r.ri.set(0,0,0); // Center of particle
// Get particle position projected on plane
var projected = particlePlane_projected;
normal.mult(normal.dot(xi),projected);
xi.vsub(projected,projected);
//projected.vadd(bj.position,projected);
// rj is now the projected world position minus plane position
r.rj.copy(projected);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
};
var particleSphere_normal = new Vec3();
/**
* @method particleSphere
* @param {Array} result
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] =
Narrowphase.prototype.sphereParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){
// The normal is the unit vector from sphere center to particle center
var normal = particleSphere_normal;
normal.set(0,0,1);
xi.vsub(xj,normal);
var lengthSquared = normal.norm2();
if(lengthSquared <= sj.radius * sj.radius){
if(justTest){
return true;
}
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
normal.normalize();
r.rj.copy(normal);
r.rj.mult(sj.radius,r.rj);
r.ni.copy(normal); // Contact normal
r.ni.negate(r.ni);
r.ri.set(0,0,0); // Center of particle
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
}
};
// WIP
var cqj = new Quaternion();
var convexParticle_local = new Vec3();
var convexParticle_normal = new Vec3();
var convexParticle_penetratedFaceNormal = new Vec3();
var convexParticle_vertexToParticle = new Vec3();
var convexParticle_worldPenetrationVec = new Vec3();
/**
* @method convexParticle
* @param {Array} result
* @param {Shape} si
* @param {Shape} sj
* @param {Vec3} xi
* @param {Vec3} xj
* @param {Quaternion} qi
* @param {Quaternion} qj
* @param {Body} bi
* @param {Body} bj
*/
Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] =
Narrowphase.prototype.convexParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){
var penetratedFaceIndex = -1;
var penetratedFaceNormal = convexParticle_penetratedFaceNormal;
var worldPenetrationVec = convexParticle_worldPenetrationVec;
var minPenetration = null;
var numDetectedFaces = 0;
// Convert particle position xi to local coords in the convex
var local = convexParticle_local;
local.copy(xi);
local.vsub(xj,local); // Convert position to relative the convex origin
qj.conjugate(cqj);
cqj.vmult(local,local);
if(sj.pointIsInside(local)){
if(sj.worldVerticesNeedsUpdate){
sj.computeWorldVertices(xj,qj);
}
if(sj.worldFaceNormalsNeedsUpdate){
sj.computeWorldFaceNormals(qj);
}
// For each world polygon in the polyhedra
for(var i=0,nfaces=sj.faces.length; i!==nfaces; i++){
// Construct world face vertices
var verts = [ sj.worldVertices[ sj.faces[i][0] ] ];
var normal = sj.worldFaceNormals[i];
// Check how much the particle penetrates the polygon plane.
xi.vsub(verts[0],convexParticle_vertexToParticle);
var penetration = -normal.dot(convexParticle_vertexToParticle);
if(minPenetration===null || Math.abs(penetration)<Math.abs(minPenetration)){
if(justTest){
return true;
}
minPenetration = penetration;
penetratedFaceIndex = i;
penetratedFaceNormal.copy(normal);
numDetectedFaces++;
}
}
if(penetratedFaceIndex!==-1){
// Setup contact
var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj);
penetratedFaceNormal.mult(minPenetration, worldPenetrationVec);
// rj is the particle position projected to the face
worldPenetrationVec.vadd(xi,worldPenetrationVec);
worldPenetrationVec.vsub(xj,worldPenetrationVec);
r.rj.copy(worldPenetrationVec);
//var projectedToFace = xi.vsub(xj).vadd(worldPenetrationVec);
//projectedToFace.copy(r.rj);
//qj.vmult(r.rj,r.rj);
penetratedFaceNormal.negate( r.ni ); // Contact normal
r.ri.set(0,0,0); // Center of particle
var ri = r.ri,
rj = r.rj;
// Make relative to bodies
ri.vadd(xi, ri);
ri.vsub(bi.position, ri);
rj.vadd(xj, rj);
rj.vsub(bj.position, rj);
this.result.push(r);
this.createFrictionEquationsFromContact(r, this.frictionResult);
} else {
console.warn("Point found inside convex, but did not find penetrating face!");
}
}
};
Narrowphase.prototype[Shape.types.BOX | Shape.types.HEIGHTFIELD] =
Narrowphase.prototype.boxHeightfield = function (si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){
si.convexPolyhedronRepresentation.material = si.material;
si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
return this.convexHeightfield(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest);
};
var convexHeightfield_tmp1 = new Vec3();
var convexHeightfield_tmp2 = new Vec3();
var convexHeightfield_faceList = [0];
/**
* @method convexHeightfield
*/
Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.HEIGHTFIELD] =
Narrowphase.prototype.convexHeightfield = function (
convexShape,
hfShape,
convexPos,
hfPos,
convexQuat,
hfQuat,
convexBody,
hfBody,
rsi,
rsj,
justTest
){
var data = hfShape.data,
w = hfShape.elementSize,
radius = convexShape.boundingSphereRadius,
worldPillarOffset = convexHeightfield_tmp2,
faceList = convexHeightfield_faceList;
// Get sphere position to heightfield local!
var localConvexPos = convexHeightfield_tmp1;
Transform.pointToLocalFrame(hfPos, hfQuat, convexPos, localConvexPos);
// Get the index of the data points to test against
var iMinX = Math.floor((localConvexPos.x - radius) / w) - 1,
iMaxX = Math.ceil((localConvexPos.x + radius) / w) + 1,
iMinY = Math.floor((localConvexPos.y - radius) / w) - 1,
iMaxY = Math.ceil((localConvexPos.y + radius) / w) + 1;
// Bail out if we are out of the terrain
if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMinY > data[0].length){
return;
}
// Clamp index to edges
if(iMinX < 0){ iMinX = 0; }
if(iMaxX < 0){ iMaxX = 0; }
if(iMinY < 0){ iMinY = 0; }
if(iMaxY < 0){ iMaxY = 0; }
if(iMinX >= data.length){ iMinX = data.length - 1; }
if(iMaxX >= data.length){ iMaxX = data.length - 1; }
if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; }
if(iMinY >= data[0].length){ iMinY = data[0].length - 1; }
var minMax = [];
hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
var min = minMax[0];
var max = minMax[1];
// Bail out if we're cant touch the bounding height box
if(localConvexPos.z - radius > max || localConvexPos.z + radius < min){
return;
}
for(var i = iMinX; i < iMaxX; i++){
for(var j = iMinY; j < iMaxY; j++){
var intersecting = false;
// Lower triangle
hfShape.getConvexTrianglePillar(i, j, false);
Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) {
intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null);
}
if(justTest && intersecting){
return true;
}
// Upper triangle
hfShape.getConvexTrianglePillar(i, j, true);
Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) {
intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null);
}
if(justTest && intersecting){
return true;
}
}
}
};
var sphereHeightfield_tmp1 = new Vec3();
var sphereHeightfield_tmp2 = new Vec3();
/**
* @method sphereHeightfield
*/
Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] =
Narrowphase.prototype.sphereHeightfield = function (
sphereShape,
hfShape,
spherePos,
hfPos,
sphereQuat,
hfQuat,
sphereBody,
hfBody,
rsi,
rsj,
justTest
){
var data = hfShape.data,
radius = sphereShape.radius,
w = hfShape.elementSize,
worldPillarOffset = sphereHeightfield_tmp2;
// Get sphere position to heightfield local!
var localSpherePos = sphereHeightfield_tmp1;
Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos);
// Get the index of the data points to test against
var iMinX = Math.floor((localSpherePos.x - radius) / w) - 1,
iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1,
iMinY = Math.floor((localSpherePos.y - radius) / w) - 1,
iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1;
// Bail out if we are out of the terrain
if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length){
return;
}
// Clamp index to edges
if(iMinX < 0){ iMinX = 0; }
if(iMaxX < 0){ iMaxX = 0; }
if(iMinY < 0){ iMinY = 0; }
if(iMaxY < 0){ iMaxY = 0; }
if(iMinX >= data.length){ iMinX = data.length - 1; }
if(iMaxX >= data.length){ iMaxX = data.length - 1; }
if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; }
if(iMinY >= data[0].length){ iMinY = data[0].length - 1; }
var minMax = [];
hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
var min = minMax[0];
var max = minMax[1];
// Bail out if we're cant touch the bounding height box
if(localSpherePos.z - radius > max || localSpherePos.z + radius < min){
return;
}
var result = this.result;
for(var i = iMinX; i < iMaxX; i++){
for(var j = iMinY; j < iMaxY; j++){
var numContactsBefore = result.length;
var intersecting = false;
// Lower triangle
hfShape.getConvexTrianglePillar(i, j, false);
Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) {
intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest);
}
if(justTest && intersecting){
return true;
}
// Upper triangle
hfShape.getConvexTrianglePillar(i, j, true);
Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) {
intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest);
}
if(justTest && intersecting){
return true;
}
var numContacts = result.length - numContactsBefore;
if(numContacts > 2){
return;
}
/*
// Skip all but 1
for (var k = 0; k < numContacts - 1; k++) {
result.pop();
}
*/
}
}
};
},{"../collision/AABB":46,"../collision/Ray":53,"../equations/ContactEquation":63,"../equations/FrictionEquation":65,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../objects/Body":75,"../shapes/ConvexPolyhedron":82,"../shapes/Shape":87,"../solver/Solver":91,"../utils/Vec3Pool":98}],100:[function(require,module,exports){
/* global performance */
module.exports = World;
var Shape = require('../shapes/Shape');
var Vec3 = require('../math/Vec3');
var Quaternion = require('../math/Quaternion');
var GSSolver = require('../solver/GSSolver');
var ContactEquation = require('../equations/ContactEquation');
var FrictionEquation = require('../equations/FrictionEquation');
var Narrowphase = require('./Narrowphase');
var EventTarget = require('../utils/EventTarget');
var ArrayCollisionMatrix = require('../collision/ArrayCollisionMatrix');
var OverlapKeeper = require('../collision/OverlapKeeper');
var Material = require('../material/Material');
var ContactMaterial = require('../material/ContactMaterial');
var Body = require('../objects/Body');
var TupleDictionary = require('../utils/TupleDictionary');
var RaycastResult = require('../collision/RaycastResult');
var AABB = require('../collision/AABB');
var Ray = require('../collision/Ray');
var NaiveBroadphase = require('../collision/NaiveBroadphase');
/**
* The physics world
* @class World
* @constructor
* @extends EventTarget
* @param {object} [options]
* @param {Vec3} [options.gravity]
* @param {boolean} [options.allowSleep]
* @param {Broadphase} [options.broadphase]
* @param {Solver} [options.solver]
* @param {boolean} [options.quatNormalizeFast]
* @param {number} [options.quatNormalizeSkip]
*/
function World(options){
options = options || {};
EventTarget.apply(this);
/**
* Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks.
* @property {Number} dt
*/
this.dt = -1;
/**
* Makes bodies go to sleep when they've been inactive
* @property allowSleep
* @type {Boolean}
* @default false
*/
this.allowSleep = !!options.allowSleep;
/**
* All the current contacts (instances of ContactEquation) in the world.
* @property contacts
* @type {Array}
*/
this.contacts = [];
this.frictionEquations = [];
/**
* How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong).
* @property quatNormalizeSkip
* @type {Number}
* @default 0
*/
this.quatNormalizeSkip = options.quatNormalizeSkip !== undefined ? options.quatNormalizeSkip : 0;
/**
* Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false.
* @property quatNormalizeFast
* @type {Boolean}
* @see Quaternion.normalizeFast
* @see Quaternion.normalize
* @default false
*/
this.quatNormalizeFast = options.quatNormalizeFast !== undefined ? options.quatNormalizeFast : false;
/**
* The wall-clock time since simulation start
* @property time
* @type {Number}
*/
this.time = 0.0;
/**
* Number of timesteps taken since start
* @property stepnumber
* @type {Number}
*/
this.stepnumber = 0;
/// Default and last timestep sizes
this.default_dt = 1/60;
this.nextId = 0;
/**
* @property gravity
* @type {Vec3}
*/
this.gravity = new Vec3();
if(options.gravity){
this.gravity.copy(options.gravity);
}
/**
* The broadphase algorithm to use. Default is NaiveBroadphase
* @property broadphase
* @type {Broadphase}
*/
this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase();
/**
* @property bodies
* @type {Array}
*/
this.bodies = [];
/**
* The solver algorithm to use. Default is GSSolver
* @property solver
* @type {Solver}
*/
this.solver = options.solver !== undefined ? options.solver : new GSSolver();
/**
* @property constraints
* @type {Array}
*/
this.constraints = [];
/**
* @property narrowphase
* @type {Narrowphase}
*/
this.narrowphase = new Narrowphase(this);
/**
* @property {ArrayCollisionMatrix} collisionMatrix
* @type {ArrayCollisionMatrix}
*/
this.collisionMatrix = new ArrayCollisionMatrix();
/**
* CollisionMatrix from the previous step.
* @property {ArrayCollisionMatrix} collisionMatrixPrevious
* @type {ArrayCollisionMatrix}
*/
this.collisionMatrixPrevious = new ArrayCollisionMatrix();
this.bodyOverlapKeeper = new OverlapKeeper();
this.shapeOverlapKeeper = new OverlapKeeper();
/**
* All added materials
* @property materials
* @type {Array}
*/
this.materials = [];
/**
* @property contactmaterials
* @type {Array}
*/
this.contactmaterials = [];
/**
* Used to look up a ContactMaterial given two instances of Material.
* @property {TupleDictionary} contactMaterialTable
*/
this.contactMaterialTable = new TupleDictionary();
this.defaultMaterial = new Material("default");
/**
* This contact material is used if no suitable contactmaterial is found for a contact.
* @property defaultContactMaterial
* @type {ContactMaterial}
*/
this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 });
/**
* @property doProfiling
* @type {Boolean}
*/
this.doProfiling = false;
/**
* @property profile
* @type {Object}
*/
this.profile = {
solve:0,
makeContactConstraints:0,
broadphase:0,
integrate:0,
narrowphase:0,
};
/**
* Time accumulator for interpolation. See http://gafferongames.com/game-physics/fix-your-timestep/
* @property {Number} accumulator
*/
this.accumulator = 0;
/**
* @property subsystems
* @type {Array}
*/
this.subsystems = [];
/**
* Dispatched after a body has been added to the world.
* @event addBody
* @param {Body} body The body that has been added to the world.
*/
this.addBodyEvent = {
type:"addBody",
body : null
};
/**
* Dispatched after a body has been removed from the world.
* @event removeBody
* @param {Body} body The body that has been removed from the world.
*/
this.removeBodyEvent = {
type:"removeBody",
body : null
};
this.idToBodyMap = {};
this.broadphase.setWorld(this);
}
World.prototype = new EventTarget();
// Temp stuff
var tmpAABB1 = new AABB();
var tmpArray1 = [];
var tmpRay = new Ray();
/**
* Get the contact material between materials m1 and m2
* @method getContactMaterial
* @param {Material} m1
* @param {Material} m2
* @return {ContactMaterial} The contact material if it was found.
*/
World.prototype.getContactMaterial = function(m1,m2){
return this.contactMaterialTable.get(m1.id,m2.id); //this.contactmaterials[this.mats2cmat[i+j*this.materials.length]];
};
/**
* Get number of objects in the world.
* @method numObjects
* @return {Number}
* @deprecated
*/
World.prototype.numObjects = function(){
return this.bodies.length;
};
/**
* Store old collision state info
* @method collisionMatrixTick
*/
World.prototype.collisionMatrixTick = function(){
var temp = this.collisionMatrixPrevious;
this.collisionMatrixPrevious = this.collisionMatrix;
this.collisionMatrix = temp;
this.collisionMatrix.reset();
this.bodyOverlapKeeper.tick();
this.shapeOverlapKeeper.tick();
};
/**
* Add a rigid body to the simulation.
* @method add
* @param {Body} body
* @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case.
* @todo Adding an array of bodies should be possible. This would save some loops too
* @deprecated Use .addBody instead
*/
World.prototype.add = World.prototype.addBody = function(body){
if(this.bodies.indexOf(body) !== -1){
return;
}
body.index = this.bodies.length;
this.bodies.push(body);
body.world = this;
body.initPosition.copy(body.position);
body.initVelocity.copy(body.velocity);
body.timeLastSleepy = this.time;
if(body instanceof Body){
body.initAngularVelocity.copy(body.angularVelocity);
body.initQuaternion.copy(body.quaternion);
}
this.collisionMatrix.setNumObjects(this.bodies.length);
this.addBodyEvent.body = body;
this.idToBodyMap[body.id] = body;
this.dispatchEvent(this.addBodyEvent);
};
/**
* Add a constraint to the simulation.
* @method addConstraint
* @param {Constraint} c
*/
World.prototype.addConstraint = function(c){
this.constraints.push(c);
};
/**
* Removes a constraint
* @method removeConstraint
* @param {Constraint} c
*/
World.prototype.removeConstraint = function(c){
var idx = this.constraints.indexOf(c);
if(idx!==-1){
this.constraints.splice(idx,1);
}
};
/**
* Raycast test
* @method rayTest
* @param {Vec3} from
* @param {Vec3} to
* @param {RaycastResult} result
* @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead.
*/
World.prototype.rayTest = function(from, to, result){
if(result instanceof RaycastResult){
// Do raycastclosest
this.raycastClosest(from, to, {
skipBackfaces: true
}, result);
} else {
// Do raycastAll
this.raycastAll(from, to, {
skipBackfaces: true
}, result);
}
};
/**
* Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument.
* @method raycastAll
* @param {Vec3} from
* @param {Vec3} to
* @param {Object} options
* @param {number} [options.collisionFilterMask=-1]
* @param {number} [options.collisionFilterGroup=-1]
* @param {boolean} [options.skipBackfaces=false]
* @param {boolean} [options.checkCollisionResponse=true]
* @param {Function} callback
* @return {boolean} True if any body was hit.
*/
World.prototype.raycastAll = function(from, to, options, callback){
options.mode = Ray.ALL;
options.from = from;
options.to = to;
options.callback = callback;
return tmpRay.intersectWorld(this, options);
};
/**
* Ray cast, and stop at the first result. Note that the order is random - but the method is fast.
* @method raycastAny
* @param {Vec3} from
* @param {Vec3} to
* @param {Object} options
* @param {number} [options.collisionFilterMask=-1]
* @param {number} [options.collisionFilterGroup=-1]
* @param {boolean} [options.skipBackfaces=false]
* @param {boolean} [options.checkCollisionResponse=true]
* @param {RaycastResult} result
* @return {boolean} True if any body was hit.
*/
World.prototype.raycastAny = function(from, to, options, result){
options.mode = Ray.ANY;
options.from = from;
options.to = to;
options.result = result;
return tmpRay.intersectWorld(this, options);
};
/**
* Ray cast, and return information of the closest hit.
* @method raycastClosest
* @param {Vec3} from
* @param {Vec3} to
* @param {Object} options
* @param {number} [options.collisionFilterMask=-1]
* @param {number} [options.collisionFilterGroup=-1]
* @param {boolean} [options.skipBackfaces=false]
* @param {boolean} [options.checkCollisionResponse=true]
* @param {RaycastResult} result
* @return {boolean} True if any body was hit.
*/
World.prototype.raycastClosest = function(from, to, options, result){
options.mode = Ray.CLOSEST;
options.from = from;
options.to = to;
options.result = result;
return tmpRay.intersectWorld(this, options);
};
/**
* Remove a rigid body from the simulation.
* @method remove
* @param {Body} body
* @deprecated Use .removeBody instead
*/
World.prototype.remove = function(body){
body.world = null;
var n = this.bodies.length - 1,
bodies = this.bodies,
idx = bodies.indexOf(body);
if(idx !== -1){
bodies.splice(idx, 1); // Todo: should use a garbage free method
// Recompute index
for(var i=0; i!==bodies.length; i++){
bodies[i].index = i;
}
this.collisionMatrix.setNumObjects(n);
this.removeBodyEvent.body = body;
delete this.idToBodyMap[body.id];
this.dispatchEvent(this.removeBodyEvent);
}
};
/**
* Remove a rigid body from the simulation.
* @method removeBody
* @param {Body} body
*/
World.prototype.removeBody = World.prototype.remove;
World.prototype.getBodyById = function(id){
return this.idToBodyMap[id];
};
// TODO Make a faster map
World.prototype.getShapeById = function(id){
var bodies = this.bodies;
for(var i=0, bl = bodies.length; i<bl; i++){
var shapes = bodies[i].shapes;
for (var j = 0, sl = shapes.length; j < sl; j++) {
var shape = shapes[j];
if(shape.id === id){
return shape;
}
}
}
};
/**
* Adds a material to the World.
* @method addMaterial
* @param {Material} m
* @todo Necessary?
*/
World.prototype.addMaterial = function(m){
this.materials.push(m);
};
/**
* Adds a contact material to the World
* @method addContactMaterial
* @param {ContactMaterial} cmat
*/
World.prototype.addContactMaterial = function(cmat) {
// Add contact material
this.contactmaterials.push(cmat);
// Add current contact material to the material table
this.contactMaterialTable.set(cmat.materials[0].id,cmat.materials[1].id,cmat);
};
// performance.now()
if(typeof performance === 'undefined'){
performance = {};
}
if(!performance.now){
var nowOffset = Date.now();
if (performance.timing && performance.timing.navigationStart){
nowOffset = performance.timing.navigationStart;
}
performance.now = function(){
return Date.now() - nowOffset;
};
}
var step_tmp1 = new Vec3();
/**
* Step the physics world forward in time.
*
* There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.
*
* @method step
* @param {Number} dt The fixed time step size to use.
* @param {Number} [timeSinceLastCalled] The time elapsed since the function was last called.
* @param {Number} [maxSubSteps=10] Maximum number of fixed steps to take per function call.
*
* @example
* // fixed timestepping without interpolation
* world.step(1/60);
*
* @see http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
*/
World.prototype.step = function(dt, timeSinceLastCalled, maxSubSteps){
maxSubSteps = maxSubSteps || 10;
timeSinceLastCalled = timeSinceLastCalled || 0;
if(timeSinceLastCalled === 0){ // Fixed, simple stepping
this.internalStep(dt);
// Increment time
this.time += dt;
} else {
this.accumulator += timeSinceLastCalled;
var substeps = 0;
while (this.accumulator >= dt && substeps < maxSubSteps) {
// Do fixed steps to catch up
this.internalStep(dt);
this.accumulator -= dt;
substeps++;
}
var t = (this.accumulator % dt) / dt;
for(var j=0; j !== this.bodies.length; j++){
var b = this.bodies[j];
b.previousPosition.lerp(b.position, t, b.interpolatedPosition);
b.previousQuaternion.slerp(b.quaternion, t, b.interpolatedQuaternion);
b.previousQuaternion.normalize();
}
this.time += timeSinceLastCalled;
}
};
var
/**
* Dispatched after the world has stepped forward in time.
* @event postStep
*/
World_step_postStepEvent = {type:"postStep"}, // Reusable event objects to save memory
/**
* Dispatched before the world steps forward in time.
* @event preStep
*/
World_step_preStepEvent = {type:"preStep"},
World_step_collideEvent = {type:Body.COLLIDE_EVENT_NAME, body:null, contact:null },
World_step_oldContacts = [], // Pools for unused objects
World_step_frictionEquationPool = [],
World_step_p1 = [], // Reusable arrays for collision pairs
World_step_p2 = [],
World_step_gvec = new Vec3(), // Temporary vectors and quats
World_step_vi = new Vec3(),
World_step_vj = new Vec3(),
World_step_wi = new Vec3(),
World_step_wj = new Vec3(),
World_step_t1 = new Vec3(),
World_step_t2 = new Vec3(),
World_step_rixn = new Vec3(),
World_step_rjxn = new Vec3(),
World_step_step_q = new Quaternion(),
World_step_step_w = new Quaternion(),
World_step_step_wq = new Quaternion(),
invI_tau_dt = new Vec3();
World.prototype.internalStep = function(dt){
this.dt = dt;
var world = this,
that = this,
contacts = this.contacts,
p1 = World_step_p1,
p2 = World_step_p2,
N = this.numObjects(),
bodies = this.bodies,
solver = this.solver,
gravity = this.gravity,
doProfiling = this.doProfiling,
profile = this.profile,
DYNAMIC = Body.DYNAMIC,
profilingStart,
constraints = this.constraints,
frictionEquationPool = World_step_frictionEquationPool,
gnorm = gravity.norm(),
gx = gravity.x,
gy = gravity.y,
gz = gravity.z,
i=0;
if(doProfiling){
profilingStart = performance.now();
}
// Add gravity to all objects
for(i=0; i!==N; i++){
var bi = bodies[i];
if(bi.type === DYNAMIC){ // Only for dynamic bodies
var f = bi.force, m = bi.mass;
f.x += m*gx;
f.y += m*gy;
f.z += m*gz;
}
}
// Update subsystems
for(var i=0, Nsubsystems=this.subsystems.length; i!==Nsubsystems; i++){
this.subsystems[i].update();
}
// Collision detection
if(doProfiling){ profilingStart = performance.now(); }
p1.length = 0; // Clean up pair arrays from last step
p2.length = 0;
this.broadphase.collisionPairs(this,p1,p2);
if(doProfiling){ profile.broadphase = performance.now() - profilingStart; }
// Remove constrained pairs with collideConnected == false
var Nconstraints = constraints.length;
for(i=0; i!==Nconstraints; i++){
var c = constraints[i];
if(!c.collideConnected){
for(var j = p1.length-1; j>=0; j-=1){
if( (c.bodyA === p1[j] && c.bodyB === p2[j]) ||
(c.bodyB === p1[j] && c.bodyA === p2[j])){
p1.splice(j, 1);
p2.splice(j, 1);
}
}
}
}
this.collisionMatrixTick();
// Generate contacts
if(doProfiling){ profilingStart = performance.now(); }
var oldcontacts = World_step_oldContacts;
var NoldContacts = contacts.length;
for(i=0; i!==NoldContacts; i++){
oldcontacts.push(contacts[i]);
}
contacts.length = 0;
// Transfer FrictionEquation from current list to the pool for reuse
var NoldFrictionEquations = this.frictionEquations.length;
for(i=0; i!==NoldFrictionEquations; i++){
frictionEquationPool.push(this.frictionEquations[i]);
}
this.frictionEquations.length = 0;
this.narrowphase.getContacts(
p1,
p2,
this,
contacts,
oldcontacts, // To be reused
this.frictionEquations,
frictionEquationPool
);
if(doProfiling){
profile.narrowphase = performance.now() - profilingStart;
}
// Loop over all collisions
if(doProfiling){
profilingStart = performance.now();
}
// Add all friction eqs
for (var i = 0; i < this.frictionEquations.length; i++) {
solver.addEquation(this.frictionEquations[i]);
}
var ncontacts = contacts.length;
for(var k=0; k!==ncontacts; k++){
// Current contact
var c = contacts[k];
// Get current collision indeces
var bi = c.bi,
bj = c.bj,
si = c.si,
sj = c.sj;
// Get collision properties
var cm;
if(bi.material && bj.material){
cm = this.getContactMaterial(bi.material,bj.material) || this.defaultContactMaterial;
} else {
cm = this.defaultContactMaterial;
}
// c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse;
var mu = cm.friction;
// c.restitution = cm.restitution;
// If friction or restitution were specified in the material, use them
if(bi.material && bj.material){
if(bi.material.friction >= 0 && bj.material.friction >= 0){
mu = bi.material.friction * bj.material.friction;
}
if(bi.material.restitution >= 0 && bj.material.restitution >= 0){
c.restitution = bi.material.restitution * bj.material.restitution;
}
}
// c.setSpookParams(
// cm.contactEquationStiffness,
// cm.contactEquationRelaxation,
// dt
// );
solver.addEquation(c);
// // Add friction constraint equation
// if(mu > 0){
// // Create 2 tangent equations
// var mug = mu * gnorm;
// var reducedMass = (bi.invMass + bj.invMass);
// if(reducedMass > 0){
// reducedMass = 1/reducedMass;
// }
// var pool = frictionEquationPool;
// var c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass);
// var c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass);
// this.frictionEquations.push(c1, c2);
// c1.bi = c2.bi = bi;
// c1.bj = c2.bj = bj;
// c1.minForce = c2.minForce = -mug*reducedMass;
// c1.maxForce = c2.maxForce = mug*reducedMass;
// // Copy over the relative vectors
// c1.ri.copy(c.ri);
// c1.rj.copy(c.rj);
// c2.ri.copy(c.ri);
// c2.rj.copy(c.rj);
// // Construct tangents
// c.ni.tangents(c1.t, c2.t);
// // Set spook params
// c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt);
// c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt);
// c1.enabled = c2.enabled = c.enabled;
// // Add equations to solver
// solver.addEquation(c1);
// solver.addEquation(c2);
// }
if( bi.allowSleep &&
bi.type === Body.DYNAMIC &&
bi.sleepState === Body.SLEEPING &&
bj.sleepState === Body.AWAKE &&
bj.type !== Body.STATIC
){
var speedSquaredB = bj.velocity.norm2() + bj.angularVelocity.norm2();
var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2);
if(speedSquaredB >= speedLimitSquaredB*2){
bi._wakeUpAfterNarrowphase = true;
}
}
if( bj.allowSleep &&
bj.type === Body.DYNAMIC &&
bj.sleepState === Body.SLEEPING &&
bi.sleepState === Body.AWAKE &&
bi.type !== Body.STATIC
){
var speedSquaredA = bi.velocity.norm2() + bi.angularVelocity.norm2();
var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2);
if(speedSquaredA >= speedLimitSquaredA*2){
bj._wakeUpAfterNarrowphase = true;
}
}
// Now we know that i and j are in contact. Set collision matrix state
this.collisionMatrix.set(bi, bj, true);
if (!this.collisionMatrixPrevious.get(bi, bj)) {
// First contact!
// We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached.
World_step_collideEvent.body = bj;
World_step_collideEvent.contact = c;
bi.dispatchEvent(World_step_collideEvent);
World_step_collideEvent.body = bi;
bj.dispatchEvent(World_step_collideEvent);
}
this.bodyOverlapKeeper.set(bi.id, bj.id);
this.shapeOverlapKeeper.set(si.id, sj.id);
}
this.emitContactEvents();
if(doProfiling){
profile.makeContactConstraints = performance.now() - profilingStart;
profilingStart = performance.now();
}
// Wake up bodies
for(i=0; i!==N; i++){
var bi = bodies[i];
if(bi._wakeUpAfterNarrowphase){
bi.wakeUp();
bi._wakeUpAfterNarrowphase = false;
}
}
// Add user-added constraints
var Nconstraints = constraints.length;
for(i=0; i!==Nconstraints; i++){
var c = constraints[i];
c.update();
for(var j=0, Neq=c.equations.length; j!==Neq; j++){
var eq = c.equations[j];
solver.addEquation(eq);
}
}
// Solve the constrained system
solver.solve(dt,this);
if(doProfiling){
profile.solve = performance.now() - profilingStart;
}
// Remove all contacts from solver
solver.removeAllEquations();
// Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details
var pow = Math.pow;
for(i=0; i!==N; i++){
var bi = bodies[i];
if(bi.type & DYNAMIC){ // Only for dynamic bodies
var ld = pow(1.0 - bi.linearDamping,dt);
var v = bi.velocity;
v.mult(ld,v);
var av = bi.angularVelocity;
if(av){
var ad = pow(1.0 - bi.angularDamping,dt);
av.mult(ad,av);
}
}
}
this.dispatchEvent(World_step_preStepEvent);
// Invoke pre-step callbacks
for(i=0; i!==N; i++){
var bi = bodies[i];
if(bi.preStep){
bi.preStep.call(bi);
}
}
// Leap frog
// vnew = v + h*f/m
// xnew = x + h*vnew
if(doProfiling){
profilingStart = performance.now();
}
var stepnumber = this.stepnumber;
var quatNormalize = stepnumber % (this.quatNormalizeSkip + 1) === 0;
var quatNormalizeFast = this.quatNormalizeFast;
for(i=0; i!==N; i++){
bodies[i].integrate(dt, quatNormalize, quatNormalizeFast);
}
this.clearForces();
this.broadphase.dirty = true;
if(doProfiling){
profile.integrate = performance.now() - profilingStart;
}
// Update world time
this.time += dt;
this.stepnumber += 1;
this.dispatchEvent(World_step_postStepEvent);
// Invoke post-step callbacks
for(i=0; i!==N; i++){
var bi = bodies[i];
var postStep = bi.postStep;
if(postStep){
postStep.call(bi);
}
}
// Sleeping update
if(this.allowSleep){
for(i=0; i!==N; i++){
bodies[i].sleepTick(this.time);
}
}
};
World.prototype.emitContactEvents = (function(){
var additions = [];
var removals = [];
var beginContactEvent = {
type: 'beginContact',
bodyA: null,
bodyB: null
};
var endContactEvent = {
type: 'endContact',
bodyA: null,
bodyB: null
};
var beginShapeContactEvent = {
type: 'beginShapeContact',
bodyA: null,
bodyB: null,
shapeA: null,
shapeB: null
};
var endShapeContactEvent = {
type: 'endShapeContact',
bodyA: null,
bodyB: null,
shapeA: null,
shapeB: null
};
return function(){
var hasBeginContact = this.hasAnyEventListener('beginContact');
var hasEndContact = this.hasAnyEventListener('endContact');
if(hasBeginContact || hasEndContact){
this.bodyOverlapKeeper.getDiff(additions, removals);
}
if(hasBeginContact){
for (var i = 0, l = additions.length; i < l; i += 2) {
beginContactEvent.bodyA = this.getBodyById(additions[i]);
beginContactEvent.bodyB = this.getBodyById(additions[i+1]);
this.dispatchEvent(beginContactEvent);
}
beginContactEvent.bodyA = beginContactEvent.bodyB = null;
}
if(hasEndContact){
for (var i = 0, l = removals.length; i < l; i += 2) {
endContactEvent.bodyA = this.getBodyById(removals[i]);
endContactEvent.bodyB = this.getBodyById(removals[i+1]);
this.dispatchEvent(endContactEvent);
}
endContactEvent.bodyA = endContactEvent.bodyB = null;
}
additions.length = removals.length = 0;
var hasBeginShapeContact = this.hasAnyEventListener('beginShapeContact');
var hasEndShapeContact = this.hasAnyEventListener('endShapeContact');
if(hasBeginShapeContact || hasEndShapeContact){
this.shapeOverlapKeeper.getDiff(additions, removals);
}
if(hasBeginShapeContact){
for (var i = 0, l = additions.length; i < l; i += 2) {
var shapeA = this.getShapeById(additions[i]);
var shapeB = this.getShapeById(additions[i+1]);
beginShapeContactEvent.shapeA = shapeA;
beginShapeContactEvent.shapeB = shapeB;
beginShapeContactEvent.bodyA = shapeA.body;
beginShapeContactEvent.bodyB = shapeB.body;
this.dispatchEvent(beginShapeContactEvent);
}
beginShapeContactEvent.bodyA = beginShapeContactEvent.bodyB = beginShapeContactEvent.shapeA = beginShapeContactEvent.shapeB = null;
}
if(hasEndShapeContact){
for (var i = 0, l = removals.length; i < l; i += 2) {
var shapeA = this.getShapeById(removals[i]);
var shapeB = this.getShapeById(removals[i+1]);
endShapeContactEvent.shapeA = shapeA;
endShapeContactEvent.shapeB = shapeB;
endShapeContactEvent.bodyA = shapeA.body;
endShapeContactEvent.bodyB = shapeB.body;
this.dispatchEvent(endShapeContactEvent);
}
endShapeContactEvent.bodyA = endShapeContactEvent.bodyB = endShapeContactEvent.shapeA = endShapeContactEvent.shapeB = null;
}
};
})();
/**
* Sets all body forces in the world to zero.
* @method clearForces
*/
World.prototype.clearForces = function(){
var bodies = this.bodies;
var N = bodies.length;
for(var i=0; i !== N; i++){
var b = bodies[i],
force = b.force,
tau = b.torque;
b.force.set(0,0,0);
b.torque.set(0,0,0);
}
};
},{"../collision/AABB":46,"../collision/ArrayCollisionMatrix":47,"../collision/NaiveBroadphase":50,"../collision/OverlapKeeper":52,"../collision/Ray":53,"../collision/RaycastResult":54,"../equations/ContactEquation":63,"../equations/FrictionEquation":65,"../material/ContactMaterial":68,"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Shape":87,"../solver/GSSolver":90,"../utils/EventTarget":93,"../utils/TupleDictionary":96,"./Narrowphase":99}]},{},[1]);
!function t(e,i,o){function n(r,a){if(!i[r]){if(!e[r]){var h="function"==typeof require&&require;if(!a&&h)return h(r,!0);if(s)return s(r,!0);var c=new Error("Cannot find module '"+r+"'");throw c.code="MODULE_NOT_FOUND",c}var l=i[r]={exports:{}};e[r][0].call(l.exports,function(t){var i=e[r][1][t];return n(i?i:t)},l,l.exports,t,e,i,o)}return i[r].exports}for(var s="function"==typeof require&&require,r=0;r<o.length;r++)n(o[r]);return n}({1:[function(t,e,i){t("./").registerAll()},{"./":2}],2:[function(t,e,i){e.exports={controls:t("./src/controls"),loaders:t("./src/loaders"),math:t("./src/math"),misc:t("./src/misc"),physics:t("./src/physics"),primitives:t("./src/primitives"),shadows:t("./src/shadows"),registerAll:function(){this.controls.registerAll(),this.loaders.registerAll(),this.math.registerAll(),this.misc.registerAll(),this.physics.registerAll(),this.primitives.registerAll(),this.shadows.registerAll()}}},{"./src/controls":13,"./src/loaders":18,"./src/math":21,"./src/misc":26,"./src/physics":33,"./src/primitives":40,"./src/shadows":41}],3:[function(t,e,i){function o(t){var e,i,o,n,s=new THREE.BoundingBoxHelper(t);return s.update(),e=s.box,isFinite(e.min.lengthSq())?(i=new v.Box(new v.Vec3((e.max.x-e.min.x)/2,(e.max.y-e.min.y)/2,(e.max.z-e.min.z)/2)),t.updateMatrixWorld(),n=new THREE.Vector3,n.setFromMatrixPosition(t.matrixWorld),o=s.position.sub(n),o.lengthSq()&&(i.offset=o),i):null}function n(t){var e,i,o,n,s=1e-4,r=p(t);if(!r||!r.vertices.length)return null;for(e=0;e<r.vertices.length;e++)r.vertices[e].x+=(Math.random()-.5)*s,r.vertices[e].y+=(Math.random()-.5)*s,r.vertices[e].z+=(Math.random()-.5)*s;for(n=f(r),i=new Array(n.vertices.length),e=0;e<n.vertices.length;e++)i[e]=new v.Vec3(n.vertices[e].x,n.vertices[e].y,n.vertices[e].z);for(o=new Array(n.faces.length),e=0;e<n.faces.length;e++)o[e]=[n.faces[e].a,n.faces[e].b,n.faces[e].c];return new v.ConvexPolyhedron(i,o)}function s(t){var e=d(t);if(!e.length)return null;t.computeBoundingBox();var i=t.boundingBox;return new v.Box(new v.Vec3((i.max.x-i.min.x)/2,(i.max.y-i.min.y)/2,(i.max.z-i.min.z)/2))}function r(t){var e,i=t.metadata?t.metadata.parameters:t.parameters;return e=new v.Cylinder(i.radiusTop,i.radiusBottom,i.height,i.radialSegments),e.orientation=new v.Quaternion,e.orientation.setFromEuler(THREE.Math.degToRad(-90),0,0,"XYZ").normalize(),e}function a(t){t.computeBoundingBox();var e=t.boundingBox;return new v.Box(new v.Vec3((e.max.x-e.min.x)/2||.1,(e.max.y-e.min.y)/2||.1,(e.max.z-e.min.z)/2||.1))}function h(t){var e=t.metadata?t.metadata.parameters:t.parameters;return new v.Sphere(e.radius)}function c(t,e){return t.computeBoundingSphere(),new v.Sphere(e.sphereRadius||t.boundingSphere.radius)}function l(t){var e=new THREE.BufferGeometry;return e.fromGeometry(t),u(e)}function u(t){var e,i=d(t);return i.length?(e=Object.keys(i).map(Number),new v.Trimesh(i,e)):null}function p(t){var e,i,o=y(t),n=new THREE.Geometry,s=new THREE.Geometry;if(0===o.length)return null;if(1===o.length){var r=new THREE.Vector3,a=new THREE.Quaternion,h=new THREE.Vector3;return n=o[0].geometry.clone(),n.metadata=o[0].geometry.metadata,o[0].updateMatrixWorld(),o[0].matrixWorld.decompose(r,a,h),n.scale(h.x,h.y,h.z)}for(;i=o.pop();)i.updateMatrixWorld(),i.geometry instanceof THREE.BufferGeometry?(n.fromBufferGeometry(i.geometry),s.merge(n,i.matrixWorld)):s.merge(i.geometry,i.matrixWorld);return e=new THREE.Matrix4,e.scale(t.scale),s.applyMatrix(e),s}function d(t){return t.attributes||(t=(new THREE.BufferGeometry).fromGeometry(t)),t.attributes.position.array}function y(t){var e=[];return t.traverse(function(t){"Mesh"===t.type&&e.push(t)}),e}var v=t("cannon"),f=t("./THREE.quickhull"),m={BOX:"Box",SPHERE:"Sphere",HULL:"ConvexPolyhedron"};e.exports=v.mesh2shape=function(t,e){if(e=e||{},e.type===m.BOX)return o(t);if(e.type===m.SPHERE)return c(p(t),e);if(e.type===m.HULL)return n(t);if(e.type)throw new Error('[CANNON.mesh2shape] Invalid type "%s".',e.type);var i=p(t);if(!i)return null;var d=i.metadata?i.metadata.type:i.type;switch(d){case"BoxGeometry":case"BoxBufferGeometry":return s(i);case"CylinderGeometry":case"CylinderBufferGeometry":return r(i);case"PlaneGeometry":case"PlaneBufferGeometry":return a(i);case"SphereGeometry":case"SphereBufferGeometry":return h(i);case"TubeGeometry":return l(i);case"Geometry":case"BufferGeometry":return u(i);default:return console.warn('Unrecognized geometry: "%s". Using bounding box as shape.',i.type),s(i)}},v.mesh2shape.Type=m},{"./THREE.quickhull":8,cannon:45}],4:[function(t,e,i){var o=t("cannon");o.shape2mesh=function(t){for(var e=new THREE.Object3D,i=0;i<t.shapes.length;i++){var n,s=t.shapes[i];switch(s.type){case o.Shape.types.SPHERE:var r=new THREE.SphereGeometry(s.radius,8,8);n=new THREE.Mesh(r,this.currentMaterial);break;case o.Shape.types.PARTICLE:n=new THREE.Mesh(this.particleGeo,this.particleMaterial);var a=this.settings;n.scale.set(a.particleSize,a.particleSize,a.particleSize);break;case o.Shape.types.PLANE:var h=new THREE.PlaneGeometry(10,10,4,4);n=new THREE.Object3D;var c=new THREE.Object3D,l=new THREE.Mesh(h,this.currentMaterial);l.scale.set(100,100,100),c.add(l),l.castShadow=!0,l.receiveShadow=!0,n.add(c);break;case o.Shape.types.BOX:var u=new THREE.BoxGeometry(2*s.halfExtents.x,2*s.halfExtents.y,2*s.halfExtents.z);n=new THREE.Mesh(u,this.currentMaterial);break;case o.Shape.types.CONVEXPOLYHEDRON:for(var p=new THREE.Geometry,d=0;d<s.vertices.length;d++){var y=s.vertices[d];p.vertices.push(new THREE.Vector3(y.x,y.y,y.z))}for(var d=0;d<s.faces.length;d++)for(var v=s.faces[d],f=v[0],m=1;m<v.length-1;m++){var g=v[m],w=v[m+1];p.faces.push(new THREE.Face3(f,g,w))}p.computeBoundingSphere(),p.computeFaceNormals(),n=new THREE.Mesh(p,this.currentMaterial);break;case o.Shape.types.HEIGHTFIELD:for(var h=new THREE.Geometry,b=new o.Vec3,x=new o.Vec3,E=new o.Vec3,A=0;A<s.data.length-1;A++)for(var C=0;C<s.data[A].length-1;C++)for(var B=0;B<2;B++){s.getConvexTrianglePillar(A,C,0===B),b.copy(s.pillarConvex.vertices[0]),x.copy(s.pillarConvex.vertices[1]),E.copy(s.pillarConvex.vertices[2]),b.vadd(s.pillarOffset,b),x.vadd(s.pillarOffset,x),E.vadd(s.pillarOffset,E),h.vertices.push(new THREE.Vector3(b.x,b.y,b.z),new THREE.Vector3(x.x,x.y,x.z),new THREE.Vector3(E.x,E.y,E.z));var d=h.vertices.length-3;h.faces.push(new THREE.Face3(d,d+1,d+2))}h.computeBoundingSphere(),h.computeFaceNormals(),n=new THREE.Mesh(h,this.currentMaterial);break;case o.Shape.types.TRIMESH:for(var h=new THREE.Geometry,b=new o.Vec3,x=new o.Vec3,E=new o.Vec3,d=0;d<s.indices.length/3;d++){s.getTriangleVertices(d,b,x,E),h.vertices.push(new THREE.Vector3(b.x,b.y,b.z),new THREE.Vector3(x.x,x.y,x.z),new THREE.Vector3(E.x,E.y,E.z));var m=h.vertices.length-3;h.faces.push(new THREE.Face3(m,m+1,m+2))}h.computeBoundingSphere(),h.computeFaceNormals(),n=new THREE.Mesh(h,this.currentMaterial);break;default:throw"Visual type not recognized: "+s.type}if(n.receiveShadow=!0,n.castShadow=!0,n.children)for(var d=0;d<n.children.length;d++)if(n.children[d].castShadow=!0,n.children[d].receiveShadow=!0,n.children[d])for(var m=0;m<n.children[d].length;m++)n.children[d].children[m].castShadow=!0,n.children[d].children[m].receiveShadow=!0;var S=t.shapeOffsets[i],R=t.shapeOrientations[i];n.position.set(S.x,S.y,S.z),n.quaternion.set(R.x,R.y,R.z,R.w),e.add(n)}return e},e.exports=o.shape2mesh},{cannon:45}],5:[function(t,e,i){e.exports=Object.assign(function(){},{FACE_1:0,FACE_2:1,FACE_3:2,FACE_4:3,L_SHOULDER_1:4,R_SHOULDER_1:5,L_SHOULDER_2:6,R_SHOULDER_2:7,SELECT:8,START:9,DPAD_UP:12,DPAD_DOWN:13,DPAD_LEFT:14,DPAD_RIGHT:15,VENDOR:16})},{}],6:[function(t,e,i){function o(t,e,i){this.type=t,this.index=e,this.pressed=i.pressed,this.value=i.value}e.exports=o},{}],7:[function(t,e,i){e.exports=THREE.PLYLoader=function(t){this.manager=void 0!==t?t:THREE.DefaultLoadingManager,this.propertyNameMapping={}},THREE.PLYLoader.prototype={constructor:THREE.PLYLoader,load:function(t,e,i,o){var n=this,s=new THREE.XHRLoader(this.manager);s.setResponseType("arraybuffer"),s.load(t,function(t){e(n.parse(t))},i,o)},setPropertyNameMapping:function(t){this.propertyNameMapping=t},bin2str:function(t){for(var e=new Uint8Array(t),i="",o=0;o<t.byteLength;o++)i+=String.fromCharCode(e[o]);return i},isASCII:function(t){var e=this.parseHeader(this.bin2str(t));return"ascii"===e.format},parse:function(t){return t instanceof ArrayBuffer?this.isASCII(t)?this.parseASCII(this.bin2str(t)):this.parseBinary(t):this.parseASCII(t)},parseHeader:function(t){function e(t,e){var i={type:t[0]};return"list"===i.type?(i.name=t[3],i.countType=t[1],i.itemType=t[2]):i.name=t[1],i.name in e&&(i.name=e[i.name]),i}var i=/ply([\s\S]*)end_header\s/,o="",n=0,s=i.exec(t);null!==s&&(o=s[1],n=s[0].length);for(var r,a,h={comments:[],elements:[],headerLength:n},c=o.split("\n"),l=void 0,u=0;u<c.length;u++){var p=c[u];if(p=p.trim(),""!==p)switch(a=p.split(/\s+/),r=a.shift(),p=a.join(" "),r){case"format":h.format=a[0],h.version=a[1];break;case"comment":h.comments.push(p);break;case"element":void 0!==l&&h.elements.push(l),l=Object(),l.name=a[0],l.count=parseInt(a[1]),l.properties=[];break;case"property":l.properties.push(e(a,this.propertyNameMapping));break;default:console.log("unhandled",r,a)}}return void 0!==l&&h.elements.push(l),h},parseASCIINumber:function(t,e){switch(e){case"char":case"uchar":case"short":case"ushort":case"int":case"uint":case"int8":case"uint8":case"int16":case"uint16":case"int32":case"uint32":return parseInt(t);case"float":case"double":case"float32":case"float64":return parseFloat(t)}},parseASCIIElement:function(t,e){for(var i=e.split(/\s+/),o=Object(),n=0;n<t.length;n++)if("list"===t[n].type){for(var s=[],r=this.parseASCIINumber(i.shift(),t[n].countType),a=0;a<r;a++)s.push(this.parseASCIINumber(i.shift(),t[n].itemType));o[t[n].name]=s}else o[t[n].name]=this.parseASCIINumber(i.shift(),t[n].type);return o},parseASCII:function(t){var e,i=new THREE.Geometry,o=this.parseHeader(t),n=/end_header\s([\s\S]*)$/,s="";null!==(e=n.exec(t))&&(s=e[1]);var r=s.split("\n"),a=0,h=0;i.useColor=!1;for(var c=0;c<r.length;c++){var l=r[c];if(l=l.trim(),""!==l){h>=o.elements[a].count&&(a++,h=0);var u=this.parseASCIIElement(o.elements[a].properties,l);this.handleElement(i,o.elements[a].name,u),h++}}return this.postProcess(i)},postProcess:function(t){if(t.useColor){for(var e=0;e<t.faces.length;e++)t.faces[e].vertexColors=[t.colors[t.faces[e].a],t.colors[t.faces[e].b],t.colors[t.faces[e].c]];t.elementsNeedUpdate=!0}return t.computeBoundingSphere(),t},handleElement:function(t,e,i){if("vertex"===e){if(t.vertices.push(new THREE.Vector3(i.x,i.y,i.z)),"red"in i&&"green"in i&&"blue"in i){t.useColor=!0;var o=new THREE.Color;o.setRGB(i.red/255,i.green/255,i.blue/255),t.colors.push(o)}}else if("face"===e){var n=i.vertex_indices||i.vertex_index;3===n.length?t.faces.push(new THREE.Face3(n[0],n[1],n[2])):4===n.length&&t.faces.push(new THREE.Face3(n[0],n[1],n[3]),new THREE.Face3(n[1],n[2],n[3]))}},binaryRead:function(t,e,i,o){switch(i){case"int8":case"char":return[t.getInt8(e),1];case"uint8":case"uchar":return[t.getUint8(e),1];case"int16":case"short":return[t.getInt16(e,o),2];case"uint16":case"ushort":return[t.getUint16(e,o),2];case"int32":case"int":return[t.getInt32(e,o),4];case"uint32":case"uint":return[t.getUint32(e,o),4];case"float32":case"float":return[t.getFloat32(e,o),4];case"float64":case"double":return[t.getFloat64(e,o),8]}},binaryReadElement:function(t,e,i,o){for(var n,s=Object(),r=0,a=0;a<i.length;a++)if("list"===i[a].type){var h=[];n=this.binaryRead(t,e+r,i[a].countType,o);var c=n[0];r+=n[1];for(var l=0;l<c;l++)n=this.binaryRead(t,e+r,i[a].itemType,o),h.push(n[0]),r+=n[1];s[i[a].name]=h}else n=this.binaryRead(t,e+r,i[a].type,o),s[i[a].name]=n[0],r+=n[1];return[s,r]},parseBinary:function(t){for(var e,i=new THREE.Geometry,o=this.parseHeader(this.bin2str(t)),n="binary_little_endian"===o.format,s=new DataView(t,o.headerLength),r=0,a=0;a<o.elements.length;a++)for(var h=0;h<o.elements[a].count;h++){e=this.binaryReadElement(s,r,o.elements[a].properties,n),r+=e[1];var c=e[0];this.handleElement(i,o.elements[a].name,c)}return this.postProcess(i)}}},{}],8:[function(t,e,i){e.exports=function(){function t(){v=new THREE.Vector3,f=new THREE.Vector3,m=new THREE.Vector3,g=new THREE.Vector3,w=new THREE.Vector3,b=new THREE.Vector3,x=new THREE.Vector3,E=new THREE.Vector3,A=new THREE.Vector3,C=new THREE.Vector3}function e(t){for(;S.length>0;)n(S.shift(),t)}function i(t,e){if(void 0!==t.normal)return t.normal;var i=e[t[0]],o=e[t[1]],n=e[t[2]];return v.subVectors(o,i),f.subVectors(n,i),b.crossVectors(f,v),b.normalize(),t.normal=b.clone()}function o(t,e,o){var n=o[t[0]],s=[],r=i(t,o);e.sort(function(t,e){return s[t.x/3]=void 0!==s[t.x/3]?s[t.x/3]:r.dot(g.subVectors(t,n)),s[e.x/3]=void 0!==s[e.x/3]?s[e.x/3]:r.dot(w.subVectors(e,n)),s[t.x/3]-s[e.x/3]});var a=e.length;for(1===a&&(s[e[0].x/3]=r.dot(g.subVectors(e[0],n)));a-- >0&&s[e[a].x/3]>0;);a+1<e.length&&s[e[a+1].x/3]>0&&(t.visiblePoints=e.splice(a+1))}function n(t,e){for(var n,s,r=B.length,a=[t],h=e.indexOf(t.visiblePoints.pop());r-- >0;)s=B[r],s!==t&&(n=i(s,e).dot(x.subVectors(e[h],e[s[0]])),n>0&&a.push(s));var c,l,u,p,d=r=a.length,y=1===r,v=[],f=0,m=[];[a[0][0],a[0][1],a[0][1],a[0][2],a[0][2],a[0][0]];if(1===a.length)s=a[0],v=[s[0],s[1],s[1],s[2],s[2],s[0]],S.indexOf(s)>-1&&S.splice(S.indexOf(s),1),s.visiblePoints&&(m=m.concat(s.visiblePoints)),B.splice(B.indexOf(s),1);else for(;r-- >0;){s=a[r],S.indexOf(s)>-1&&S.splice(S.indexOf(s),1),s.visiblePoints&&(m=m.concat(s.visiblePoints)),B.splice(B.indexOf(s),1);var g;for(cEdgeIndex=0;cEdgeIndex<3;){for(g=!1,d=a.length,u=s[cEdgeIndex],p=s[(cEdgeIndex+1)%3];d-- >0&&!g;)if(c=a[d],f=0,c!==s)for(;f<3&&!g;)l=f+1,g=c[f]===u&&c[l%3]===p||c[f]===p&&c[l%3]===u,f++;g&&!y||(v.push(u),v.push(p)),cEdgeIndex++}}r=0;for(var w,b=v.length/2;r<b;)w=[v[2*r+1],h,v[2*r]],o(w,m,e),B.push(w),void 0!==w.visiblePoints&&S.push(w),r++}var s,r,a,h,c,l,u,p,d,y,v,f,m,g,w,b,x,E,A,C,B=[],S=[],R=0,M=function(){var t=new THREE.Vector3,e=new THREE.Vector3,i=new THREE.Vector3;return function(o,n,s){return t.subVectors(s,o),e.subVectors(n,o),i.crossVectors(t,e),i.normalize()}}(),T=function(){var t=new THREE.Vector3,e=new THREE.Vector3,i=new THREE.Vector3;return function(o,n,s){t.subVectors(n,o),e.subVectors(s,o),i.subVectors(s,n);var r=e.dot(t);if(r<0)return e.dot(e);var a=t.dot(t);return r>=a?i.dot(i):e.dot(e)-r*r/a}}();return function(i){for(t(),points=i.vertices,B=[],S=[],x=s=points.length,r=points.slice(0,6),R=0;x-- >0;)points[x].x<r[0].x&&(r[0]=points[x]),points[x].x>r[1].x&&(r[1]=points[x]),points[x].y<r[2].y&&(r[2]=points[x]),points[x].y<r[3].y&&(r[3]=points[x]),points[x].z<r[4].z&&(r[4]=points[x]),points[x].z<r[5].z&&(r[5]=points[x]);for(h=x=6;x-- >0;)for(h=x-1;h-- >0;)R<(a=r[x].distanceToSquared(r[h]))&&(R=a,c=r[x],l=r[h]);for(x=6,R=0;x-- >0;)a=T(c,l,r[x]),R<a&&(R=a,u=r[x]);for(d=M(c,l,u),y=d.dot(c),R=0,x=s;x-- >0;)a=Math.abs(points[x].dot(d)-y),R<a&&(R=a,p=points[x]);var n=points.indexOf(c),v=points.indexOf(l),f=points.indexOf(u),m=points.indexOf(p),g=[[f,v,n],[v,m,n],[f,m,v],[n,m,f]];E.subVectors(l,c).normalize(),A.subVectors(u,c).normalize(),C.subVectors(p,c).normalize();var w=C.dot((new THREE.Vector3).crossVectors(A,E));w<0&&(g[0].reverse(),g[1].reverse(),g[2].reverse(),g[3].reverse());var b=points.slice();b.splice(b.indexOf(c),1),b.splice(b.indexOf(l),1),b.splice(b.indexOf(u),1),b.splice(b.indexOf(p),1);for(var x=g.length;x-- >0;)o(g[x],b,points),void 0!==g[x].visiblePoints&&S.push(g[x]),B.push(g[x]);e(points);for(var z=B.length;z-- >0;)i.faces[z]=new THREE.Face3(B[z][2],B[z][1],B[z][0],B[z].normal);return i.normalsNeedUpdate=!0,i}}()},{}],9:[function(t,e,i){!function(t){function e(t,e){return String(t).indexOf(e)!==-1}function i(t,e,i){d!==e&&p!==e&&u!==e||Object.keys(i).forEach(function(e){t[e]=i[e]})}function o(t,e){var i={};return Object.keys(t).forEach(function(o){var n=t[o];e in n&&(i[n[e]]=n)}),i}function n(t){var e="keyCode"in t?t.keyCode:"which"in t?t.which:0,i=function(){if(b||"keyLocation"in t){var i=b?t.location:t.keyLocation;if(i&&e in f[i])return f[i][e]}return"keyIdentifier"in t&&t.keyIdentifier in v?v[t.keyIdentifier]:e in y?y[e]:null}();if(!i)return null;var o=function(){var e=m[i.code];return e?t.shiftKey&&"shiftKey"in e?e.shiftKey:e.key:i.code}();return{code:i.code,key:o,location:i.location,keyCap:i.keyCap}}function s(t,e){if(t=String(t),!w.hasOwnProperty(t))return"Undefined";if(e&&"en-us"!==String(e).toLowerCase())throw Error("Unsupported locale");var i=w[t];return i.keyCap||i.code||"Undefined"}var r="KeyboardEvent"in t;r||(t.KeyboardEvent=function(){throw TypeError("Illegal constructor")}),t.KeyboardEvent.DOM_KEY_LOCATION_STANDARD=0,t.KeyboardEvent.DOM_KEY_LOCATION_LEFT=1,t.KeyboardEvent.DOM_KEY_LOCATION_RIGHT=2,t.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD=3;var a=window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,h=window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,c=window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,l=window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD,u=function(){return e(navigator.platform,"Win")?"win":e(navigator.platform,"Mac")?"mac":e(navigator.platform,"CrOS")?"cros":e(navigator.platform,"Linux")?"linux":e(navigator.userAgent,"iPad")||e(navigator.platform,"iPod")||e(navigator.platform,"iPhone")?"ios":""}(),p=function(){return e(navigator.userAgent,"Chrome/")?"chrome":e(navigator.vendor,"Apple")?"safari":e(navigator.userAgent,"MSIE")?"ie":e(navigator.userAgent,"Gecko/")?"moz":e(navigator.userAgent,"Opera/")?"opera":""}(),d=p+"-"+u,y={3:{code:"Cancel"},6:{code:"Help"},8:{code:"Backspace"},9:{code:"Tab"},12:{code:"Clear"},13:{code:"Enter"},16:{code:"Shift"},17:{code:"Control"},18:{code:"Alt"},19:{code:"Pause"},20:{code:"CapsLock"},21:{code:"KanaMode"},22:{code:"HangulMode"},23:{code:"JunjaMode"},24:{code:"FinalMode"},25:{code:"KanjiMode"},27:{code:"Escape"},28:{code:"Convert"},29:{code:"NonConvert"},30:{code:"Accept"},31:{code:"ModeChange"},32:{code:"Space"},33:{code:"PageUp"},34:{code:"PageDown"},35:{code:"End"},36:{code:"Home"},37:{code:"ArrowLeft"},38:{code:"ArrowUp"},39:{code:"ArrowRight"},40:{code:"ArrowDown"},41:{code:"Select"},42:{code:"Print"},43:{code:"Execute"},44:{code:"PrintScreen"},45:{code:"Insert"},46:{code:"Delete"},47:{code:"Help"},48:{code:"Digit0",keyCap:"0"},49:{code:"Digit1",keyCap:"1"},50:{code:"Digit2",keyCap:"2"},51:{code:"Digit3",keyCap:"3"},52:{code:"Digit4",keyCap:"4"},53:{code:"Digit5",keyCap:"5"},54:{code:"Digit6",keyCap:"6"},55:{code:"Digit7",keyCap:"7"},56:{code:"Digit8",keyCap:"8"},57:{code:"Digit9",keyCap:"9"},65:{code:"KeyA",keyCap:"a"},66:{code:"KeyB",keyCap:"b"},67:{code:"KeyC",keyCap:"c"},68:{code:"KeyD",keyCap:"d"},69:{code:"KeyE",keyCap:"e"},70:{code:"KeyF",keyCap:"f"},71:{code:"KeyG",keyCap:"g"},72:{code:"KeyH",keyCap:"h"},73:{code:"KeyI",keyCap:"i"},74:{code:"KeyJ",keyCap:"j"},75:{code:"KeyK",keyCap:"k"},76:{code:"KeyL",keyCap:"l"},77:{code:"KeyM",keyCap:"m"},78:{code:"KeyN",keyCap:"n"},79:{code:"KeyO",keyCap:"o"},80:{code:"KeyP",keyCap:"p"},81:{code:"KeyQ",keyCap:"q"},82:{code:"KeyR",keyCap:"r"},83:{code:"KeyS",keyCap:"s"},84:{code:"KeyT",keyCap:"t"},85:{code:"KeyU",keyCap:"u"},86:{code:"KeyV",keyCap:"v"},87:{code:"KeyW",keyCap:"w"},88:{code:"KeyX",keyCap:"x"},89:{code:"KeyY",keyCap:"y"},90:{code:"KeyZ",keyCap:"z"},91:{code:"OSLeft",location:h},92:{code:"OSRight",location:c},93:{code:"ContextMenu"},95:{code:"Standby"},96:{code:"Numpad0",keyCap:"0",location:l},97:{code:"Numpad1",keyCap:"1",location:l},98:{code:"Numpad2",keyCap:"2",location:l},99:{code:"Numpad3",keyCap:"3",location:l},100:{code:"Numpad4",keyCap:"4",location:l},101:{code:"Numpad5",keyCap:"5",location:l},102:{code:"Numpad6",keyCap:"6",location:l},103:{code:"Numpad7",keyCap:"7",location:l},104:{code:"Numpad8",keyCap:"8",location:l},105:{code:"Numpad9",keyCap:"9",location:l},106:{code:"NumpadMultiply",keyCap:"*",location:l},107:{code:"NumpadAdd",keyCap:"+",location:l},108:{code:"NumpadComma",keyCap:",",location:l},109:{code:"NumpadSubtract",keyCap:"-",location:l},110:{code:"NumpadDecimal",keyCap:".",location:l},111:{code:"NumpadDivide",keyCap:"/",location:l},112:{code:"F1"},113:{code:"F2"},114:{code:"F3"},115:{code:"F4"},116:{code:"F5"},117:{code:"F6"},118:{code:"F7"},119:{code:"F8"},120:{code:"F9"},121:{code:"F10"},122:{code:"F11"},123:{code:"F12"},124:{code:"F13"},125:{code:"F14"},126:{code:"F15"},127:{code:"F16"},128:{code:"F17"},129:{code:"F18"},130:{code:"F19"},131:{code:"F20"},132:{code:"F21"},133:{code:"F22"},134:{code:"F23"},135:{code:"F24"},144:{code:"NumLock",location:l},145:{code:"ScrollLock"},160:{code:"ShiftLeft",location:h},161:{code:"ShiftRight",location:c},162:{code:"ControlLeft",location:h},163:{code:"ControlRight",location:c},164:{code:"AltLeft",location:h},165:{code:"AltRight",location:c},166:{code:"BrowserBack"},167:{code:"BrowserForward"},168:{code:"BrowserRefresh"},169:{code:"BrowserStop"},170:{code:"BrowserSearch"},171:{code:"BrowserFavorites"},172:{code:"BrowserHome"},173:{code:"VolumeMute"},174:{code:"VolumeDown"},175:{code:"VolumeUp"},176:{code:"MediaTrackNext"},177:{code:"MediaTrackPrevious"},178:{code:"MediaStop"},179:{code:"MediaPlayPause"},180:{code:"LaunchMail"},181:{code:"MediaSelect"},182:{code:"LaunchApp1"},183:{code:"LaunchApp2"},186:{code:"Semicolon",keyCap:";"},187:{code:"Equal",keyCap:"="},188:{code:"Comma",keyCap:","},189:{code:"Minus",keyCap:"-"},190:{code:"Period",keyCap:"."},191:{code:"Slash",keyCap:"/"},192:{code:"Backquote",keyCap:"`"},219:{code:"BracketLeft",keyCap:"["},220:{code:"Backslash",keyCap:"\\"},221:{code:"BracketRight",keyCap:"]"},222:{code:"Quote",keyCap:"'"},226:{code:"IntlBackslash",keyCap:"\\"},229:{code:"Process"},246:{code:"Attn"},247:{code:"CrSel"},248:{code:"ExSel"},249:{code:"EraseEof"},250:{code:"Play"},251:{code:"ZoomToggle"},254:{code:"Clear"}};i(y,"moz",{59:{code:"Semicolon",keyCap:";"},61:{code:"Equal",keyCap:"="},107:{code:"Equal",keyCap:"="},109:{code:"Minus",keyCap:"-"},187:{code:"NumpadAdd",keyCap:"+",location:l},189:{code:"NumpadSubtract",keyCap:"-",location:l}}),i(y,"moz-mac",{12:{code:"NumLock",location:l},173:{code:"Minus",keyCap:"-"}}),i(y,"moz-win",{173:{code:"Minus",keyCap:"-"}}),i(y,"chrome-mac",{93:{code:"OSRight",location:c}}),i(y,"safari",{3:{code:"Enter"},25:{code:"Tab"}}),i(y,"ios",{10:{code:"Enter",location:a}}),i(y,"safari-mac",{91:{code:"OSLeft",location:h},93:{code:"OSRight",location:c},229:{code:"KeyQ",keyCap:"Q"}});var v={};"cros"===u&&(v["U+00A0"]={code:"ShiftLeft",location:h},v["U+00A1"]={code:"ShiftRight",location:c},v["U+00A2"]={code:"ControlLeft",location:h},v["U+00A3"]={code:"ControlRight",location:c},v["U+00A4"]={code:"AltLeft",location:h},v["U+00A5"]={code:"AltRight",location:c}),"chrome-mac"===d&&(v["U+0010"]={code:"ContextMenu"}),"safari-mac"===d&&(v["U+0010"]={code:"ContextMenu"}),"ios"===u&&(v["U+0010"]={code:"Function"},v["U+001C"]={code:"ArrowLeft"},v["U+001D"]={code:"ArrowRight"},v["U+001E"]={code:"ArrowUp"},v["U+001F"]={code:"ArrowDown"},v["U+0001"]={code:"Home"},v["U+0004"]={code:"End"},v["U+000B"]={code:"PageUp"},v["U+000C"]={code:"PageDown"});var f=[];f[h]={16:{code:"ShiftLeft",location:h},17:{code:"ControlLeft",location:h},18:{code:"AltLeft",location:h}},f[c]={16:{code:"ShiftRight",location:c},17:{code:"ControlRight",location:c},18:{code:"AltRight",location:c}},f[l]={13:{code:"NumpadEnter",location:l}},i(f[l],"moz",{109:{code:"NumpadSubtract",location:l},107:{code:"NumpadAdd",location:l}}),i(f[h],"moz-mac",{224:{code:"OSLeft",location:h}}),i(f[c],"moz-mac",{224:{code:"OSRight",location:c}}),i(f[c],"moz-win",{91:{code:"OSRight",location:c}}),i(f[c],"mac",{93:{code:"OSRight",location:c}}),i(f[l],"chrome-mac",{12:{code:"NumLock",location:l}}),i(f[l],"safari-mac",{12:{code:"NumLock",location:l},187:{code:"NumpadAdd",location:l},189:{code:"NumpadSubtract",location:l},190:{code:"NumpadDecimal",location:l},191:{code:"NumpadDivide",location:l}});var m={ShiftLeft:{key:"Shift"},ShiftRight:{key:"Shift"},ControlLeft:{key:"Control"},ControlRight:{key:"Control"},AltLeft:{key:"Alt"},AltRight:{key:"Alt"},OSLeft:{key:"OS"},OSRight:{key:"OS"},NumpadEnter:{key:"Enter"},Space:{key:" "},Digit0:{key:"0",shiftKey:")"},Digit1:{key:"1",shiftKey:"!"},Digit2:{key:"2",shiftKey:"@"},Digit3:{key:"3",shiftKey:"#"},Digit4:{key:"4",shiftKey:"$"},Digit5:{key:"5",shiftKey:"%"},Digit6:{key:"6",shiftKey:"^"},Digit7:{key:"7",shiftKey:"&"},Digit8:{key:"8",shiftKey:"*"},Digit9:{key:"9",shiftKey:"("},KeyA:{key:"a",shiftKey:"A"},KeyB:{key:"b",shiftKey:"B"},KeyC:{key:"c",shiftKey:"C"},KeyD:{key:"d",shiftKey:"D"},KeyE:{key:"e",shiftKey:"E"},KeyF:{key:"f",shiftKey:"F"},KeyG:{key:"g",shiftKey:"G"},KeyH:{key:"h",shiftKey:"H"},KeyI:{key:"i",shiftKey:"I"},KeyJ:{key:"j",shiftKey:"J"},KeyK:{key:"k",shiftKey:"K"},KeyL:{key:"l",shiftKey:"L"},KeyM:{key:"m",shiftKey:"M"},KeyN:{key:"n",shiftKey:"N"},KeyO:{key:"o",shiftKey:"O"},KeyP:{key:"p",shiftKey:"P"},KeyQ:{key:"q",shiftKey:"Q"},KeyR:{key:"r",shiftKey:"R"},KeyS:{key:"s",shiftKey:"S"},KeyT:{key:"t",shiftKey:"T"},KeyU:{key:"u",shiftKey:"U"},KeyV:{key:"v",shiftKey:"V"},KeyW:{key:"w",shiftKey:"W"},KeyX:{key:"x",shiftKey:"X"},KeyY:{key:"y",shiftKey:"Y"},KeyZ:{key:"z",shiftKey:"Z"},Numpad0:{key:"0"},Numpad1:{key:"1"},Numpad2:{key:"2"},Numpad3:{key:"3"},Numpad4:{key:"4"},Numpad5:{key:"5"},Numpad6:{key:"6"},Numpad7:{key:"7"},Numpad8:{key:"8"},Numpad9:{key:"9"},NumpadMultiply:{key:"*"},NumpadAdd:{key:"+"},NumpadComma:{key:","},NumpadSubtract:{key:"-"},NumpadDecimal:{key:"."},NumpadDivide:{key:"/"},Semicolon:{key:";",shiftKey:":"},Equal:{key:"=",shiftKey:"+"},Comma:{key:",",shiftKey:"<"},Minus:{key:"-",shiftKey:"_"},Period:{key:".",shiftKey:">"},Slash:{key:"/",shiftKey:"?"},Backquote:{key:"`",shiftKey:"~"},BracketLeft:{key:"[",shiftKey:"{"},Backslash:{key:"\\",shiftKey:"|"},BracketRight:{key:"]",shiftKey:"}"},Quote:{key:"'",shiftKey:'"'},IntlBackslash:{key:"\\",shiftKey:"|"}};i(m,"mac",{OSLeft:{key:"Meta"},OSRight:{key:"Meta"}});var g={Esc:"Escape",Nonconvert:"NonConvert",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Menu:"ContextMenu",MediaNextTrack:"MediaTrackNext",MediaPreviousTrack:"MediaTrackPrevious",SelectMedia:"MediaSelect",HalfWidth:"Hankaku",FullWidth:"Zenkaku",RomanCharacters:"Romaji",Crsel:"CrSel",Exsel:"ExSel",Zoom:"ZoomToggle"},w=o(y,"code");try{var b=r&&"location"in new KeyboardEvent("")}catch(x){}"KeyboardEvent"in t&&"defineProperty"in Object&&!function(){function t(t,e,i){e in t||Object.defineProperty(t,e,i)}if(t(KeyboardEvent.prototype,"code",{get:function(){var t=n(this);return t?t.code:""}}),"key"in KeyboardEvent.prototype){var e=Object.getOwnPropertyDescriptor(KeyboardEvent.prototype,"key");Object.defineProperty(KeyboardEvent.prototype,"key",{get:function(){var t=e.get.call(this);return g.hasOwnProperty(t)?g[t]:t}})}t(KeyboardEvent.prototype,"key",{get:function(){var t=n(this);return t&&"key"in t?t.key:"Unidentified"}}),t(KeyboardEvent.prototype,"location",{get:function(){var t=n(this);return t&&"location"in t?t.location:a}}),t(KeyboardEvent.prototype,"locale",{get:function(){return""}})}(),"queryKeyCap"in t.KeyboardEvent||(t.KeyboardEvent.queryKeyCap=s),t.identifyKey=function(t){if(!("code"in t)){var e=n(t);t.code=e?e.code:"",t.key=e&&"key"in e?e.key:"Unidentified",t.location="location"in t?t.location:"keyLocation"in t?t.keyLocation:e&&"location"in e?e.location:a,t.locale=""}}}(window)},{}],10:[function(t,e,i){var o=.1;e.exports={schema:{enabled:{"default":!0},mode:{"default":"teleport",oneOf:["teleport","animate"]},animateSpeed:{"default":3}},init:function(){this.active=!0,this.checkpoint=null,this.offset=new THREE.Vector3,this.position=new THREE.Vector3,this.targetPosition=new THREE.Vector3},play:function(){this.active=!0},pause:function(){this.active=!1},setCheckpoint:function(t){this.active&&(this.checkpoint=t,"teleport"===this.data.mode&&(this.sync(),this.el.setAttribute("position",this.targetPosition)))},isVelocityActive:function(){return!(!this.active||!this.checkpoint)},getVelocity:function(){if(this.active){var t=this.data,e=this.offset,i=this.position,n=this.targetPosition;return this.sync(),i.distanceTo(n)<o?(this.checkpoint=null,e.set(0,0,0)):(e.setLength(t.animateSpeed),e)}},sync:function(){var t=this.offset,e=this.position,i=this.targetPosition;e.copy(this.el.getComputedAttribute("position")),i.copy(this.checkpoint.getComputedAttribute("position")),i.y=e.y,t.copy(i).sub(e)}}},{}],11:[function(t,e,i){var o=t("../../lib/GamepadButton"),n=t("../../lib/GamepadButtonEvent"),s=.2;e.exports={GamepadButton:o,schema:{controller:{"default":0,oneOf:[0,1,2,3]},enabled:{"default":!0},debug:{"default":!1}},init:function(){var t=this.el.sceneEl;this.prevTime=window.performance.now(),this.buttons={},t.addBehavior(this),this.getGamepad()||console.warn("Gamepad #%d not found. Connect controller and press any button to continue.",this.data.controller)},update:function(){this.tick()},tick:function(){this.updateButtonState()},remove:function(){},isVelocityActive:function(){if(!this.data.enabled||!this.isConnected())return!1;var t=this.getDpad(),e=this.getJoystick(0),i=t.x||e.x,o=t.y||e.y;return Math.abs(i)>s||Math.abs(o)>s},getVelocityDelta:function(){var t=this.getDpad(),e=this.getJoystick(0),i=t.x||e.x,o=t.y||e.y,n=new THREE.Vector3;return Math.abs(i)>s&&(n.x+=i),Math.abs(o)>s&&(n.z+=o),n},isRotationActive:function(){if(!this.data.enabled||!this.isConnected())return!1;var t=this.getJoystick(1);return Math.abs(t.x)>s||Math.abs(t.y)>s},getRotationDelta:function(){var t=this.getJoystick(1);return Math.abs(t.x)<=s&&(t.x=0),Math.abs(t.y)<=s&&(t.y=0),t},updateButtonState:function(){var t=this.getGamepad();if(this.data.enabled&&t)for(var e=0;e<t.buttons.length;e++)t.buttons[e].pressed&&!this.buttons[e]?this.emit(new n("gamepadbuttondown",e,t.buttons[e])):!t.buttons[e].pressed&&this.buttons[e]&&this.emit(new n("gamepadbuttonup",e,t.buttons[e])),this.buttons[e]=t.buttons[e].pressed;else Object.keys(this.buttons)&&(this.buttons={})},emit:function(t){this.el.emit(t.type,t),this.el.emit(t.type+":"+t.index,new n(t.type,t.index,t))},getGamepad:function(){var t=navigator.getGamepads&&navigator.getGamepads()[this.data.controller],e=this.el.sceneEl.components["proxy-controls"],i=e&&e.isConnected()&&e.getGamepad(this.data.controller);return i||t},getButton:function(t){return this.getGamepad().buttons[t]},getAxis:function(t){return this.getGamepad().axes[t]},getJoystick:function(t){var e=this.getGamepad();switch(t){case 0:return new THREE.Vector2(e.axes[0],e.axes[1]);case 1:return new THREE.Vector2(e.axes[2],e.axes[3]);default:throw new Error('Unexpected joystick index "%d".',t)}},getDpad:function(){var t=this.getGamepad();return t.buttons[o.DPAD_RIGHT]?new THREE.Vector2((t.buttons[o.DPAD_RIGHT].pressed?1:0)+(t.buttons[o.DPAD_LEFT].pressed?-1:0),(t.buttons[o.DPAD_UP].pressed?-1:0)+(t.buttons[o.DPAD_DOWN].pressed?1:0)):new THREE.Vector2},isConnected:function(){var t=this.getGamepad();return!(!t||!t.connected)},getID:function(){return this.getGamepad().id}}},{"../../lib/GamepadButton":5,"../../lib/GamepadButtonEvent":6}],12:[function(t,e,i){function o(t){return 0===t.x&&0===t.y&&0===t.z}var n=THREE.Math.radToDeg,s=!1;e.exports={schema:{enabled:{"default":!0},standing:{"default":!0}},init:function(){this.isPositionCalibrated=!1,this.dolly=new THREE.Object3D,this.hmdEuler=new THREE.Euler,this.previousHMDPosition=new THREE.Vector3,this.deltaHMDPosition=new THREE.Vector3,this.vrControls=new THREE.VRControls(this.dolly),this.rotation=new THREE.Vector3},update:function(){var t=this.data,e=this.vrControls;e.standing=t.standing,e.update()},tick:function(){this.vrControls.update()},remove:function(){this.vrControls.dispose()},isRotationActive:function(){var t=this.hmdEuler;return!(!this.data.enabled||!this.el.sceneEl.is("vr-mode")&&!s)&&(t.setFromQuaternion(this.dolly.quaternion,"YXZ"),!o(t))},getRotation:function(){var t=this.hmdEuler;return this.rotation.set(n(t.x),n(t.y),n(t.z))},isVelocityActive:function(){var t=this.deltaHMDPosition,e=this.previousHMDPosition,i=this.calculateHMDPosition();return this.isPositionCalibrated=this.isPositionCalibrated||!o(e),!(!this.data.enabled||!this.el.sceneEl.is("vr-mode")||s)&&(t.copy(i).sub(e),
e.copy(i),this.isPositionCalibrated&&!o(t))},getPositionDelta:function(){return this.deltaHMDPosition},calculateHMDPosition:function(){var t=this.dolly,e=new THREE.Vector3;return t.updateMatrix(),e.setFromMatrixPosition(t.matrix),e}}},{}],13:[function(t,e,i){var o=t("../math");e.exports={"checkpoint-controls":t("./checkpoint-controls"),"gamepad-controls":t("./gamepad-controls"),"hmd-controls":t("./hmd-controls"),"keyboard-controls":t("./keyboard-controls"),"mouse-controls":t("./mouse-controls"),"touch-controls":t("./touch-controls"),"universal-controls":t("./universal-controls"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,o.registerAll(),t.components["checkpoint-controls"]||t.registerComponent("checkpoint-controls",this["checkpoint-controls"]),t.components["gamepad-controls"]||t.registerComponent("gamepad-controls",this["gamepad-controls"]),t.components["hmd-controls"]||t.registerComponent("hmd-controls",this["hmd-controls"]),t.components["keyboard-controls"]||t.registerComponent("keyboard-controls",this["keyboard-controls"]),t.components["mouse-controls"]||t.registerComponent("mouse-controls",this["mouse-controls"]),t.components["touch-controls"]||t.registerComponent("touch-controls",this["touch-controls"]),t.components["universal-controls"]||t.registerComponent("universal-controls",this["universal-controls"]),this._registered=!0)}}},{"../math":21,"./checkpoint-controls":10,"./gamepad-controls":11,"./hmd-controls":12,"./keyboard-controls":14,"./mouse-controls":15,"./touch-controls":16,"./universal-controls":17}],14:[function(t,e,i){t("../../lib/keyboard.polyfill");var o="__keyboard-controls-proxy",n=window.KeyboardEvent;e.exports={schema:{enabled:{"default":!0},debug:{"default":!1}},init:function(){this.dVelocity=new THREE.Vector3,this.localKeys={},this.listeners={keydown:this.onKeyDown.bind(this),keyup:this.onKeyUp.bind(this),blur:this.onBlur.bind(this)},this.attachEventListeners()},isVelocityActive:function(){return this.data.enabled&&!!Object.keys(this.getKeys()).length},getVelocityDelta:function(){var t=this.data,e=this.getKeys();return this.dVelocity.set(0,0,0),t.enabled&&((e.KeyW||e.ArrowUp)&&(this.dVelocity.z-=1),(e.KeyA||e.ArrowLeft)&&(this.dVelocity.x-=1),(e.KeyS||e.ArrowDown)&&(this.dVelocity.z+=1),(e.KeyD||e.ArrowRight)&&(this.dVelocity.x+=1)),this.dVelocity.clone()},play:function(){this.attachEventListeners()},pause:function(){this.removeEventListeners()},remove:function(){this.pause()},attachEventListeners:function(){window.addEventListener("keydown",this.listeners.keydown,!1),window.addEventListener("keyup",this.listeners.keyup,!1),window.addEventListener("blur",this.listeners.blur,!1)},removeEventListeners:function(){window.removeEventListener("keydown",this.listeners.keydown),window.removeEventListener("keyup",this.listeners.keyup),window.removeEventListener("blur",this.listeners.blur)},onKeyDown:function(t){this.localKeys[t.code]=!0,this.emit(t)},onKeyUp:function(t){delete this.localKeys[t.code],this.emit(t)},onBlur:function(){for(var t in this.localKeys)this.localKeys.hasOwnProperty(t)&&delete this.localKeys[t]},emit:function(t){o in t&&this.el.emit(t.type,t),this.el.emit(t.type+":"+t.code,new n(t.type,t)),this.data.debug&&console.log(t.type+":"+t.code)},isPressed:function(t){return t in this.getKeys()},getKeys:function(){return this.isProxied()?this.el.sceneEl.components["proxy-controls"].getKeyboard():this.localKeys},isProxied:function(){var t=this.el.sceneEl.components["proxy-controls"];return t&&t.isConnected()}}},{"../../lib/keyboard.polyfill":9}],15:[function(t,e,i){e.exports={schema:{enabled:{"default":!0},pointerlockEnabled:{"default":!0},sensitivity:{"default":.04}},init:function(){this.mouseDown=!1,this.pointerLocked=!1,this.lookVector=new THREE.Vector2,this.bindMethods()},play:function(){this.addEventListeners()},pause:function(){this.removeEventListeners(),this.lookVector.set(0,0)},remove:function(){this.pause()},bindMethods:function(){this.onMouseDown=this.onMouseDown.bind(this),this.onMouseMove=this.onMouseMove.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onPointerLockChange=this.onPointerLockChange.bind(this),this.onPointerLockChange=this.onPointerLockChange.bind(this),this.onPointerLockChange=this.onPointerLockChange.bind(this)},addEventListeners:function(){var t=this.el.sceneEl,e=t.canvas,i=this.data;return e?(e.addEventListener("mousedown",this.onMouseDown,!1),e.addEventListener("mousemove",this.onMouseMove,!1),e.addEventListener("mouseup",this.onMouseUp,!1),e.addEventListener("mouseout",this.onMouseUp,!1),void(i.pointerlockEnabled&&(document.addEventListener("pointerlockchange",this.onPointerLockChange,!1),document.addEventListener("mozpointerlockchange",this.onPointerLockChange,!1),document.addEventListener("pointerlockerror",this.onPointerLockError,!1)))):void t.addEventListener("render-target-loaded",this.addEventListeners.bind(this))},removeEventListeners:function(){var t=this.el.sceneEl&&this.el.sceneEl.canvas;t&&(t.removeEventListener("mousedown",this.onMouseDown,!1),t.removeEventListener("mousemove",this.onMouseMove,!1),t.removeEventListener("mouseup",this.onMouseUp,!1),t.removeEventListener("mouseout",this.onMouseUp,!1)),document.removeEventListener("pointerlockchange",this.onPointerLockChange,!1),document.removeEventListener("mozpointerlockchange",this.onPointerLockChange,!1),document.removeEventListener("pointerlockerror",this.onPointerLockError,!1)},isRotationActive:function(){return this.data.enabled&&(this.mouseDown||this.pointerLocked)},getRotationDelta:function(){var t=this.lookVector.clone().multiplyScalar(this.data.sensitivity);return this.lookVector.set(0,0),t},onMouseMove:function(t){var e=this.previousMouseEvent;if(this.data.enabled&&(this.mouseDown||this.pointerLocked)){var i=t.movementX||t.mozMovementX||0,o=t.movementY||t.mozMovementY||0;this.pointerLocked||(i=t.screenX-e.screenX,o=t.screenY-e.screenY),this.lookVector.x+=i,this.lookVector.y+=o,this.previousMouseEvent=t}},onMouseDown:function(t){var e=this.el.sceneEl.canvas;this.mouseDown=!0,this.previousMouseEvent=t,this.data.pointerlockEnabled&&!this.pointerLocked&&(e.requestPointerLock?e.requestPointerLock():e.mozRequestPointerLock&&e.mozRequestPointerLock())},onMouseUp:function(){this.mouseDown=!1},onPointerLockChange:function(){this.pointerLocked=!(!document.pointerLockElement&&!document.mozPointerLockElement)},onPointerLockError:function(){this.pointerLocked=!1}}},{}],16:[function(t,e,i){e.exports={schema:{enabled:{"default":!0}},init:function(){this.dVelocity=new THREE.Vector3,this.bindMethods()},play:function(){this.addEventListeners()},pause:function(){this.removeEventListeners(),this.dVelocity.set(0,0,0)},remove:function(){this.pause()},addEventListeners:function(){var t=this.el.sceneEl,e=t.canvas;return e?(e.addEventListener("touchstart",this.onTouchStart),void e.addEventListener("touchend",this.onTouchEnd)):void t.addEventListener("render-target-loaded",this.addEventListeners.bind(this))},removeEventListeners:function(){var t=this.el.sceneEl&&this.el.sceneEl.canvas;t&&(t.removeEventListener("touchstart",this.onTouchStart),t.removeEventListener("touchend",this.onTouchEnd))},isVelocityActive:function(){return this.data.enabled&&this.isMoving},getVelocityDelta:function(){return this.dVelocity.z=this.isMoving?-1:0,this.dVelocity.clone()},bindMethods:function(){this.onTouchStart=this.onTouchStart.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this)},onTouchStart:function(t){this.isMoving=!0,t.preventDefault()},onTouchEnd:function(t){this.isMoving=!1,t.preventDefault()}}},{}],17:[function(t,e,i){var o="-controls",n=.2,s=Math.PI/2;e.exports={dependencies:["velocity","rotation"],schema:{enabled:{"default":!0},movementEnabled:{"default":!0},movementControls:{"default":["gamepad","keyboard","touch","hmd"]},rotationEnabled:{"default":!0},rotationControls:{"default":["hmd","gamepad","mouse"]},movementSpeed:{"default":5},movementEasing:{"default":15},movementAcceleration:{"default":80},rotationSensitivity:{"default":.05}},init:function(){this.velocity=new THREE.Vector3,this.pitch=new THREE.Object3D,this.yaw=new THREE.Object3D,this.yaw.position.y=10,this.yaw.add(this.pitch),this.heading=new THREE.Euler(0,0,0,"YXZ"),this.el.sceneEl.hasLoaded?this.injectControls():this.el.sceneEl.addEventListener("loaded",this.injectControls.bind(this))},update:function(){this.el.sceneEl.hasLoaded&&this.injectControls()},injectControls:function(){var t,e,i=this.data;for(t=0;t<i.movementControls.length;t++)e=i.movementControls[t]+o,this.el.components[e]||this.el.setAttribute(e,"");for(t=0;t<i.rotationControls.length;t++)e=i.rotationControls[t]+o,this.el.components[e]||this.el.setAttribute(e,"")},tick:function(t,e){e&&(this.data.rotationEnabled&&this.updateRotation(e),this.data.movementEnabled&&e/1e3>n?(this.velocity.set(0,0,0),this.el.setAttribute("velocity",this.velocity)):this.updateVelocity(e))},updateRotation:function(t){for(var e,i,n=this.data,r=0,a=n.rotationControls.length;r<a;r++)if(e=this.el.components[n.rotationControls[r]+o],e&&e.isRotationActive()){if(e.getRotationDelta)i=e.getRotationDelta(t),i.multiplyScalar(n.rotationSensitivity),this.yaw.rotation.y-=i.x,this.pitch.rotation.x-=i.y,this.pitch.rotation.x=Math.max(-s,Math.min(s,this.pitch.rotation.x)),this.el.setAttribute("rotation",{x:THREE.Math.radToDeg(this.pitch.rotation.x),y:THREE.Math.radToDeg(this.yaw.rotation.y),z:0});else{if(!e.getRotation)throw new Error("Incompatible rotation controls: %s",n.rotationControls[r]);this.el.setAttribute("rotation",e.getRotation())}break}},updateVelocity:function(t){var e,i,n=this.velocity,s=this.data;if(s.movementEnabled)for(var r=0,a=s.movementControls.length;r<a;r++)if(e=this.el.components[s.movementControls[r]+o],e&&e.isVelocityActive()){if(!e.getVelocityDelta){if(e.getVelocity)return void this.el.setAttribute("velocity",e.getVelocity());if(e.getPositionDelta)return n.copy(e.getPositionDelta(t).multiplyScalar(1e3/t)),void this.el.setAttribute("velocity",n);throw new Error("Incompatible movement controls: ",s.movementControls[r])}i=e.getVelocityDelta(t);break}if(n.copy(this.el.getComputedAttribute("velocity")),n.x-=n.x*s.movementEasing*t/1e3,n.z-=n.z*s.movementEasing*t/1e3,i&&s.movementEnabled){i.length()>1?i.setLength(this.data.movementAcceleration*t/1e3):i.multiplyScalar(this.data.movementAcceleration*t/1e3);var h=this.el.getAttribute("rotation");h&&(this.heading.set(0,THREE.Math.degToRad(h.y),0),i.applyEuler(this.heading)),n.add(i)}this.el.setAttribute("velocity",n)}}},{}],18:[function(t,e,i){e.exports={"ply-model":t("./ply-model"),"three-model":t("./three-model"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,t.systems["ply-model"]||t.registerSystem("ply-model",this["ply-model"].System),t.components["ply-model"]||t.registerComponent("ply-model",this["ply-model"].Component),t.components["three-model"]||t.registerComponent("three-model",this["three-model"]),this._registered=!0)}}},{"./ply-model":19,"./three-model":20}],19:[function(t,e,i){function o(t){return new THREE.Mesh(t,new THREE.MeshPhongMaterial({color:16777215,shading:THREE.FlatShading,vertexColors:THREE.VertexColors,shininess:0}))}THREE.PLYLoader=t("../../lib/PLYLoader"),e.exports.System={init:function(){this.cache={}},getOrLoadGeometry:function(t,e){var i=this.cache,o=i[t];return!e&&o?o:(i[t]=new Promise(function(e){var i=new THREE.PLYLoader;i.load(t,function(t){e(t)})}),i[t])}},e.exports.Component={schema:{skipCache:{type:"boolean","default":!1},src:{type:"src"}},init:function(){this.model=null},update:function(){var t=this.data,e=this.el;return t.src?void this.system.getOrLoadGeometry(t.src,t.skipCache).then(function(t){var i=o(t);e.setObject3D("mesh",i),e.emit("model-loaded",{format:"ply",model:i})}):void console.warn("[%s] `src` property is required.",this.name)},remove:function(){this.model&&this.el.removeObject3D("mesh")}}},{"../../lib/PLYLoader":7}],20:[function(t,e,i){e.exports={schema:{src:{type:"src"},loader:{"default":"object",oneOf:["object","json"]},animation:{"default":""}},init:function(){this.model=null,this.mixer=null},update:function(t){var e,i=this.data;if(i.src)if(t)i.animation!==t.animation&&this.model.activeAction&&(this.model.activeAction.stop(),this.playAnimation());else if(this.remove(),"object"===i.loader)e=new THREE.ObjectLoader,e.load(i.src,this.load.bind(this));else{if("json"!==i.loader)throw new Error('[three-model] Invalid mode "%s".',i.mode);e=new THREE.JSONLoader,e.load(i.src,function(t,e){this.load(new THREE.Mesh(t,new THREE.MeshFaceMaterial(e)))}.bind(this))}},load:function(t){this.model=t,this.mixer=new THREE.AnimationMixer(this.model),this.el.setObject3D("mesh",t),this.el.emit("model-loaded",{format:"three",model:t}),this.data.animation&&this.playAnimation()},playAnimation:function(){var t=this.data,e=this.model.animations||this.model.geometry.animations,i=THREE.AnimationClip.findByName(e,t.animation);this.model.activeAction=this.mixer.clipAction(i,this.model).play()},remove:function(){this.mixer&&this.mixer.stopAllAction(),this.model&&this.el.removeObject3D("mesh")},tick:function(t,e){this.mixer&&!isNaN(e)&&this.mixer.update(e/1e3)}}},{}],21:[function(t,e,i){e.exports={velocity:t("./velocity"),quaternion:t("./quaternion"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,t.components.velocity||t.registerComponent("velocity",this.velocity),t.components.quaternion||t.registerComponent("quaternion",this.quaternion),this._registered=!0)}}},{"./quaternion":22,"./velocity":23}],22:[function(t,e,i){e.exports={schema:{type:"vec4"},play:function(){var t=this.el,e=t.object3D.quaternion;t.hasAttribute("rotation")&&(t.components.rotation.update(),t.setAttribute("quaternion",{x:e.x,y:e.y,z:e.z,w:e.w}),t.removeAttribute("rotation"),this.update())},update:function(){var t=this.data;this.el.object3D.quaternion.set(t.x,t.y,t.z,t.w)}}},{}],23:[function(t,e,i){e.exports={schema:{type:"vec3"},init:function(){this.system=this.el.sceneEl.systems.physics,this.system&&this.system.addBehavior(this,this.system.Phase.RENDER)},remove:function(){this.system&&this.system.removeBehavior(this,this.system.Phase.RENDER)},tick:function(t,e){e&&(this.system||this.step(t,e))},step:function(t,e){if(e){var i=this.el.sceneEl.systems.physics||{data:{maxInterval:1/60}},o=this.el.getAttribute("velocity")||{x:0,y:0,z:0},n=this.el.getAttribute("position")||{x:0,y:0,z:0};e=Math.min(e,1e3*i.data.maxInterval),this.el.setAttribute("position",{x:n.x+o.x*e/1e3,y:n.y+o.y*e/1e3,z:n.z+o.z*e/1e3})}}}},{}],24:[function(t,e,i){e.exports={schema:{defaultRotation:{type:"vec3"},enableDefaultRotation:{"default":!1}},init:function(){this.active=!1,this.targetEl=null,this.fire=this.fire.bind(this)},play:function(){this.el.addEventListener("click",this.fire)},pause:function(){this.el.addEventListener("click",this.fire)},remove:function(){this.pause()},fire:function(){var t=this.el.sceneEl.querySelector("[checkpoint-controls]");if(!t)throw new Error("No `checkpoint-controls` component found.");t.components["checkpoint-controls"].setCheckpoint(this.el)}}},{}],25:[function(t,e,i){e.exports={init:function(){this.GRABBED_STATE="grabbed",this.grabbing=!1,this.hitEl=null,this.physics=this.el.sceneEl.systems.physics,this.constraint=null,this.onHit=this.onHit.bind(this),this.onGripOpen=this.onGripOpen.bind(this),this.onGripClose=this.onGripClose.bind(this)},play:function(){var t=this.el;t.addEventListener("hit",this.onHit),t.addEventListener("gripdown",this.onGripClose),t.addEventListener("gripup",this.onGripOpen),t.addEventListener("trackpaddown",this.onGripClose),t.addEventListener("trackpadup",this.onGripOpen),t.addEventListener("triggerdown",this.onGripClose),t.addEventListener("triggerup",this.onGripOpen)},pause:function(){var t=this.el;t.removeEventListener("hit",this.onHit),t.removeEventListener("gripdown",this.onGripClose),t.removeEventListener("gripup",this.onGripOpen),t.removeEventListener("trackpaddown",this.onGripClose),t.removeEventListener("trackpadup",this.onGripOpen),t.removeEventListener("triggerdown",this.onGripClose),t.removeEventListener("triggerup",this.onGripOpen)},onGripClose:function(t){this.grabbing=!0},onGripOpen:function(t){var e=this.hitEl;this.grabbing=!1,e&&(e.removeState(this.GRABBED_STATE),this.hitEl=void 0,this.physics.world.removeConstraint(this.constraint),this.constraint=null)},onHit:function(t){var e=t.detail.el;e&&!e.is(this.GRABBED_STATE)&&this.grabbing&&!this.hitEl&&(e.addState(this.GRABBED_STATE),this.hitEl=e,this.constraint=new CANNON.LockConstraint(this.el.body,e.body),this.physics.world.addConstraint(this.constraint))}}},{}],26:[function(t,e,i){var o=t("../math"),n=t("../physics");e.exports={checkpoint:t("./checkpoint"),grab:t("./grab"),"jump-ability":t("./jump-ability"),"sphere-collider":t("./sphere-collider"),"toggle-velocity":t("./toggle-velocity"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,o.registerAll(),n.registerAll(),t.components.checkpoint||t.registerComponent("checkpoint",this.checkpoint),t.components.grab||t.registerComponent("grab",this.grab),t.components["jump-ability"]||t.registerComponent("jump-ability",this["jump-ability"]),t.components["sphere-collider"]||t.registerComponent("sphere-collider",this["sphere-collider"]),t.components["toggle-velocity"]||t.registerComponent("toggle-velocity",this["toggle-velocity"]),this._registered=!0)}}},{"../math":21,"../physics":33,"./checkpoint":24,"./grab":25,"./jump-ability":27,"./sphere-collider":28,"./toggle-velocity":29}],27:[function(t,e,i){var o=-9.8,n=-15;e.exports={dependencies:["velocity"],schema:{on:{"default":"keydown:Space gamepadbuttondown:0"},playerHeight:{"default":1.764},enableDoubleJump:{"default":!1},distance:{"default":5},soundJump:{"default":""},soundLand:{"default":""},debug:{"default":!1}},init:function(){this.isOnObject=!0,this.velocity=0,this.numJumps=0;var t=this.beginJump.bind(this),e=this.data.on.split(" ");this.bindings={};for(var i=0;i<e.length;i++)this.bindings[e[i]]=t,this.el.addEventListener(e[i],t)},remove:function(){for(var t in this.bindings)this.bindings.hasOwnProperty(t)&&(this.el.removeEventListener(t,this.bindings[t]),delete this.bindings[t])},beginJump:function(){if(this.isOnObject||this.data.enableDoubleJump&&1===this.numJumps){var t=this.data,e=Math.sqrt(-2*t.distance*(o+n)),i=this.el.getAttribute("velocity");this.el.setAttribute("velocity",{x:i.x,y:e,z:i.z}),this.numJumps++}}}},{}],28:[function(t,e,i){e.exports={schema:{objects:{"default":""},state:{"default":"collided"},radius:{"default":.05}},init:function(){this.els=[],this.collisions=[]},update:function(){var t,e=this.data;t=e.objects?this.el.sceneEl.querySelectorAll(e.objects):this.el.sceneEl.children,this.els=Array.prototype.slice.call(t)},tick:function(){var t=new THREE.Vector3,e=new THREE.Vector3;return function(){function i(i){var o,n=i.getObject3D("mesh");n&&(n.getWorldPosition(e),n.geometry.computeBoundingSphere(),o=n.geometry.boundingSphere.radius,t.distanceTo(e)<o+s.radius&&a.push(i))}function o(t){t.emit("hit"),t.addState(s.state),n.emit("hit",{el:t})}var n=this.el,s=this.data,r=n.getObject3D("mesh"),a=[];r&&(t.copy(n.getComputedAttribute("position")),this.els.forEach(i),a.forEach(o),0===a.length&&n.emit("hit",{el:null}),this.collisions.filter(function(t){return a.indexOf(t)===-1}).forEach(function(t){t.removeState(s.state)}),this.collisions=a)}}()}},{}],29:[function(t,e,i){e.exports={dependencies:["velocity"],schema:{axis:{"default":"x",oneOf:["x","y","z"]},min:{"default":0},max:{"default":0},speed:{"default":1}},init:function(){var t={x:0,y:0,z:0};t[this.data.axis]=this.data.speed,this.el.setAttribute("velocity",t),this.el.sceneEl.addBehavior&&this.el.sceneEl.addBehavior(this)},remove:function(){},update:function(){this.tick()},tick:function(){var t=this.data,e=this.el.getAttribute("velocity"),i=this.el.getAttribute("position");e[t.axis]>0&&i[t.axis]>t.max?(e[t.axis]=-t.speed,this.el.setAttribute("velocity",e)):e[t.axis]<0&&i[t.axis]<t.min&&(e[t.axis]=t.speed,this.el.setAttribute("velocity",e))}}},{}],30:[function(t,e,i){var o=t("cannon"),n=t("../../lib/CANNON-mesh2shape");t("../../lib/CANNON-shape2mesh"),e.exports={schema:{shape:{"default":"auto",oneOf:["auto","box","sphere","hull"]},sphereRadius:{"default":NaN}},init:function(){this.system=this.el.sceneEl.systems.physics,this.el.sceneEl.hasLoaded?this.initBody():this.el.sceneEl.addEventListener("loaded",this.initBody.bind(this))},initBody:function(){var t,e=this.el,i=this.data,s=e.getComputedAttribute("position"),r="auto"===i.shape?void 0:{type:n.Type[i.shape.toUpperCase()],sphereRadius:i.sphereRadius};if(this.el.object3D.updateMatrixWorld(!0),t=n(this.el.object3D,r),!t)return void this.el.addEventListener("model-loaded",this.initBody.bind(this));this.body=new o.Body({mass:i.mass||0,material:this.system.material,position:new o.Vec3(s.x,s.y,s.z),linearDamping:i.linearDamping,angularDamping:i.angularDamping}),this.body.addShape(t,t.offset,t.orientation);var a=e.getComputedAttribute("rotation");this.body.quaternion.setFromEuler(THREE.Math.degToRad(a.x),THREE.Math.degToRad(a.y),THREE.Math.degToRad(a.z),"XYZ").normalize(),this.system.debug&&this.createWireframe(this.body,t),this.el.body=this.body,this.body.el=this.el,this.isLoaded=!0,this.isPlaying&&this._play(),this.el.emit("body-loaded",{body:this.el.body})},play:function(){this.isLoaded&&this._play()},_play:function(){this.system.addBehavior(this,this.system.Phase.SIMULATE),this.system.addBody(this.body),this.wireframe&&this.el.sceneEl.object3D.add(this.wireframe),this.syncToPhysics()},pause:function(){this.isLoaded&&(this.system.removeBehavior(this,this.system.Phase.SIMULATE),this.system.removeBody(this.body),this.wireframe&&this.el.sceneEl.object3D.remove(this.wireframe))},remove:function(){this.pause(),delete this.body.el,delete this.body,delete this.el.body,delete this.wireframe},createWireframe:function(t,e){var i=e.offset,n=e.orientation,s=o.shape2mesh(t).children[0];this.wireframe=new THREE.EdgesHelper(s,16711680),i&&(this.wireframe.offset=i.clone()),n&&(n.inverse(n),this.wireframe.orientation=new THREE.Quaternion(n.x,n.y,n.z,n.w)),this.syncWireframe()},syncWireframe:function(){var t,e=this.wireframe;this.wireframe&&(e.quaternion.copy(this.body.quaternion),e.orientation&&e.quaternion.multiply(e.orientation),e.position.copy(this.body.position),e.offset&&(t=e.offset.clone().applyQuaternion(e.quaternion),e.position.add(t)),e.updateMatrix())},syncToPhysics:function(){var t=new THREE.Quaternion,e=new THREE.Vector3;return function(){var i=this.el,o=i.parentEl,n=this.body;n&&(i.components.velocity&&n.velocity.copy(i.getComputedAttribute("velocity")),o.isScene?(n.quaternion.copy(i.object3D.quaternion),n.position.copy(i.object3D.position)):(i.object3D.getWorldQuaternion(t),n.quaternion.copy(t),i.object3D.getWorldPosition(e),n.position.copy(e)),this.wireframe&&this.syncWireframe())}}(),syncFromPhysics:function(){var t=new THREE.Vector3,e=new THREE.Quaternion,i=new THREE.Quaternion;return function(){var o=this.el,n=o.parentEl,s=this.body;s&&(n.isScene?(o.setAttribute("quaternion",s.quaternion),o.setAttribute("position",s.position)):(e.copy(s.quaternion),n.object3D.getWorldQuaternion(i),e.multiply(i.inverse()),o.setAttribute("quaternion",{x:e.x,y:e.y,z:e.z,w:e.w}),t.copy(s.position),n.object3D.worldToLocal(t),o.setAttribute("position",{x:t.x,y:t.y,z:t.z})),this.wireframe&&this.syncWireframe())}}()}},{"../../lib/CANNON-mesh2shape":3,"../../lib/CANNON-shape2mesh":4,cannon:45}],31:[function(t,e,i){e.exports={GRAVITY:-9.8,MAX_INTERVAL:4/60,ITERATIONS:10,CONTACT_MATERIAL:{friction:.01,restitution:.3,contactEquationStiffness:1e8,contactEquationRelaxation:3,frictionEquationStiffness:1e8,frictionEquationRegularization:3}}},{}],32:[function(t,e,i){var o=t("./body");e.exports=AFRAME.utils.extend({},o,{dependencies:["quaternion","velocity"],schema:AFRAME.utils.extend({},o.schema,{mass:{"default":5},linearDamping:{"default":.01},angularDamping:{"default":.01}}),step:function(){this.syncFromPhysics()}})},{"./body":30}],33:[function(t,e,i){var o=t("cannon"),n=t("../math");e.exports={"dynamic-body":t("./dynamic-body"),"kinematic-body":t("./kinematic-body"),"static-body":t("./static-body"),system:t("./system/physics"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,n.registerAll(),t.systems.physics||t.registerSystem("physics",this.system),t.components["dynamic-body"]||t.registerComponent("dynamic-body",this["dynamic-body"]),t.components["kinematic-body"]||t.registerComponent("kinematic-body",this["kinematic-body"]),t.components["static-body"]||t.registerComponent("static-body",this["static-body"]),this._registered=!0)}},window.CANNON=window.CANNON||o},{"../math":21,"./dynamic-body":32,"./kinematic-body":34,"./static-body":35,"./system/physics":36,cannon:45}],34:[function(t,e,i){var o=t("cannon"),n=1e-6;e.exports={dependencies:["velocity"],schema:{mass:{"default":5},radius:{"default":1.3},height:{"default":1.764},linearDamping:{"default":.05},enableSlopes:{"default":!0}},init:function(){this.system=this.el.sceneEl.systems.physics,this.system.addBehavior(this,this.system.Phase.SIMULATE);var t=this.el,e=this.data,i=(new o.Vec3).copy(t.getAttribute("position"));this.body=new o.Body({material:this.system.material,position:i,mass:e.mass,linearDamping:e.linearDamping,fixedRotation:!0}),this.body.addShape(new o.Sphere(e.radius),new o.Vec3(0,e.radius-e.height,0)),this.body.el=this.el,this.system.addBody(this.body)},remove:function(){this.system.removeBody(this.body),this.system.removeBehavior(this,this.system.Phase.SIMULATE)},step:function(){var t=new THREE.Vector3,e=new THREE.Vector3,i=new THREE.Vector3,o=new THREE.Vector3;return function(s,r){if(r){var a,h,c=this.body,l=this.data,u=!1,p=-(1/0);r=Math.min(r,1e3*this.system.data.maxInterval),o.set(0,0,0),t.copy(this.el.getAttribute("velocity")),c.velocity.copy(t),c.position.copy(this.el.getAttribute("position"));for(var d,y=0;d=this.system.world.contacts[y];y++){if(c.id===d.bi.id)d.ni.negate(i);else{if(c.id!==d.bj.id)continue;i.copy(d.ni)}u=c.velocity.dot(i)<-n,u&&i.y<=.5?t=t.projectOnPlane(i):i.y>.5&&(a=c.id===d.bi.id?Math.abs(d.rj.y+d.bj.position.y):Math.abs(d.ri.y+d.bi.position.y),a>p&&(p=a,o.copy(i),h=c.id===d.bi.id?d.bj:d.bi))}if(e.copy(t).normalize(),h&&e.y<.5?(l.enableSlopes?o.y<1-n&&(o=this.raycastToGround(h,o)):o.set(0,1,0),t=t.projectOnPlane(o)):t.add(this.system.world.gravity.scale(4*r/1e3)),h&&h.el&&h.el.components.velocity){var v=h.el.getComputedAttribute("velocity");c.position.copy({x:c.position.x+v.x*r/1e3,y:c.position.y+v.y*r/1e3,z:c.position.z+v.z*r/1e3}),this.el.setAttribute("position",c.position)}c.velocity.copy(t),this.el.setAttribute("velocity",t)}}}(),raycastToGround:function(t,e){var i,n,s=this.body.position,r=this.body.position.clone();return r.y-=this.data.height,i=new o.Ray(s,r),i._updateDirection(),i.intersectBody(t),i.hasHit?(n=i.result.hitNormalWorld,Math.abs(n.y)>Math.abs(e.y)?n:e):e}}},{cannon:45}],35:[function(t,e,i){var o=t("./body");e.exports=AFRAME.utils.extend({},o,{step:function(){this.syncToPhysics()}})},{"./body":30}],36:[function(t,e,i){var o=t("cannon"),n=t("../constants"),s=n.GRAVITY,r=n.CONTACT_MATERIAL;e.exports={schema:{gravity:{"default":s},friction:{"default":r.friction},restitution:{"default":r.restitution},contactEquationStiffness:{"default":r.contactEquationStiffness},contactEquationRelaxation:{"default":r.contactEquationRelaxation},frictionEquationStiffness:{"default":r.frictionEquationStiffness},frictionEquationRegularization:{"default":r.frictionEquationRegularization},maxInterval:{"default":4/60},debug:{"default":!1}},Phase:{SIMULATE:"sim",RENDER:"render"},init:function(){var t=this.data;this.debug=t.debug,this.children={},this.children[this.Phase.SIMULATE]=[],this.children[this.Phase.RENDER]=[],this.listeners={},this.world=new o.World,this.world.quatNormalizeSkip=0,this.world.quatNormalizeFast=!1,this.world.solver.iterations=n.ITERATIONS,this.world.gravity.set(0,t.gravity,0),this.world.broadphase=new o.NaiveBroadphase,this.material=new o.Material({name:"defaultMaterial"}),this.contactMaterial=new o.ContactMaterial(this.material,this.material,{friction:t.friction,restitution:t.restitution,contactEquationStiffness:t.contactEquationStiffness,contactEquationRelaxation:t.contactEquationRelaxation,frictionEquationStiffness:t.frictionEquationStiffness,frictionEquationRegularization:t.frictionEquationRegularization}),this.world.addContactMaterial(this.contactMaterial)},tick:function(t,e){if(e){this.world.step(Math.min(e/1e3,this.data.maxInterval));var i;for(i=0;i<this.children[this.Phase.SIMULATE].length;i++)this.children[this.Phase.SIMULATE][i].step(t,e);for(i=0;i<this.children[this.Phase.RENDER].length;i++)this.children[this.Phase.RENDER][i].step(t,e)}},addBody:function(t){this.listeners[t.id]=function(e){t.el.emit("collide",e)},t.addEventListener("collide",this.listeners[t.id]),this.world.addBody(t)},removeBody:function(t){t.removeEventListener("collide",this.listeners[t.id]),delete this.listeners[t.id],this.world.removeBody(t)},addBehavior:function(t,e){this.children[e].push(t)},removeBehavior:function(t,e){this.children[e].splice(this.children[e].indexOf(t),1)},update:function(t){var e=this.data;e.debug!==t.debug&&console.warn("[physics] `debug` cannot be changed dynamically."),e.maxInterval!==t.maxInterval,e.gravity!==t.gravity&&this.world.gravity.set(0,e.gravity,0),this.contactMaterial.friction=e.friction,this.contactMaterial.restitution=e.restitution,this.contactMaterial.contactEquationStiffness=e.contactEquationStiffness,this.contactMaterial.contactEquationRelaxation=e.contactEquationRelaxation,this.contactMaterial.frictionEquationStiffness=e.frictionEquationStiffness,this.contactMaterial.frictionEquationRegularization=e.frictionEquationRegularization}}},{"../constants":31,cannon:45}],37:[function(t,e,i){e.exports={defaultComponents:{geometry:{primitive:"plane",width:75,height:75},rotation:{x:-90,y:0,z:0},material:{src:"url(https://cdn.rawgit.com/donmccurdy/aframe-extras/v1.16.3/assets/grid.png)",repeat:"75 75"}},mappings:{width:"geometry.width",depth:"geometry.depth",src:"material.src"}}},{}],38:[function(t,e,i){e.exports.Primitive={defaultComponents:{ocean:{},rotation:{x:-90,y:0,z:0}},mappings:{width:"ocean.width",depth:"ocean.depth",density:"ocean.density",color:"ocean.color",opacity:"ocean.opacity"}},e.exports.Component={schema:{width:{"default":10,min:0},depth:{"default":10,min:0},density:{"default":10},amplitude:{"default":.1},amplitudeVariance:{"default":.3},speed:{"default":1},speedVariance:{"default":2},color:{"default":8049399},opacity:{"default":.8}},play:function(){var t=this.el,e=this.data,i=t.components.material,o=new THREE.PlaneGeometry(e.width,e.depth,e.density,e.density);o.mergeVertices(),this.waves=[];for(var n,s=0,r=o.vertices.length;s<r;s++)n=o.vertices[s],this.waves.push({z:n.z,ang:Math.random()*Math.PI*2,amp:e.amplitude+Math.random()*e.amplitudeVariance,speed:(e.speed+Math.random()*e.speedVariance)/1e3});i||(i={},i.material=new THREE.MeshPhongMaterial({color:e.color,transparent:e.opacity<1,opacity:e.opacity,shading:THREE.FlatShading})),this.mesh=new THREE.Mesh(o,i.material),t.object3D.add(this.mesh)},remove:function(){this.el.object3D.remove(this.mesh)},tick:function(t,e){if(e){for(var i,o,n=this.mesh.geometry.vertices,s=0;i=n[s];s++)o=this.waves[s],i.z=o.z+Math.sin(o.ang)*o.amp,o.ang+=o.speed*e;this.mesh.geometry.verticesNeedUpdate=!0}}}},{}],39:[function(t,e,i){e.exports.Primitive={defaultComponents:{tube:{}},mappings:{path:"tube.path",segments:"tube.segments",radius:"tube.radius",radialSegments:"tube.radialSegments",closed:"tube.closed"}},e.exports.Component={schema:{path:{"default":[]},segments:{"default":64},radius:{"default":1},radialSegments:{"default":8},closed:{"default":!1}},init:function(){var t=this.el,e=this.data,i=t.components.material;if(!e.path.length)return void console.error("[a-tube] `path` property expected but not found.");
var o=new THREE.CatmullRomCurve3(e.path.map(function(t){return t=t.split(" "),new THREE.Vector3(Number(t[0]),Number(t[1]),Number(t[2]))})),n=new THREE.TubeGeometry(o,e.segments,e.radius,e.radialSegments,e.closed);i||(i={},i.material=new THREE.MeshPhongMaterial),this.mesh=new THREE.Mesh(n,i.material),this.el.setObject3D("mesh",this.mesh)},remove:function(){this.mesh&&this.el.removeObject3D("mesh")}}},{}],40:[function(t,e,i){e.exports={"a-grid":t("./a-grid"),"a-ocean":t("./a-ocean"),"a-tube":t("./a-tube"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,t.registerPrimitive("a-grid",this["a-grid"]),t.registerComponent("ocean",this["a-ocean"].Component),t.registerPrimitive("a-ocean",this["a-ocean"].Primitive),t.registerComponent("tube",this["a-tube"].Component),t.registerPrimitive("a-tube",this["a-tube"].Primitive),this._registered=!0)}}},{"./a-grid":37,"./a-ocean":38,"./a-tube":39}],41:[function(t,e,i){e.exports={shadow:t("./shadow"),"shadow-light":t("./shadow-light"),registerAll:function(t){this._registered||(t=t||window.AFRAME,t=t.aframeCore||t,t.components.shadow||t.registerComponent("shadow",this.shadow),t.components["shadow-light"]||t.registerComponent("shadow-light",this["shadow-light"]),this._registered=!0)}}},{"./shadow":43,"./shadow-light":42}],42:[function(t,e,i){e.exports={schema:{angle:{"default":Math.PI/3},castShadow:{"default":!1},color:{"default":"#FFF"},groundColor:{"default":"#FFF"},decay:{"default":1},distance:{"default":0},exponent:{"default":10},intensity:{"default":1},shadowBias:{"default":0},shadowCameraFar:{"default":5e3},shadowCameraFov:{"default":50},shadowCameraNear:{"default":.5},shadowDarkness:{"default":.5},shadowMapHeight:{"default":512},shadowMapWidth:{"default":512},type:{"default":"directional"}},init:function(){var t=this.el;this.light=this.getLight(),t.object3D.add(this.light),t.sceneEl.systems.light.registerLight(t),t.sceneEl.hasLoaded||t.sceneEl.addEventListener("loaded",this.play.bind(this))},update:function(t){t&&(this.el.object3D.remove(this.light),this.light=this.getLight(),this.el.object3D.add(this.light))},play:function(){var t=this.el,e=t.sceneEl.renderer;e&&!e.shadowMap.enabled&&(e.shadowMap.enabled=!0)},getLight:function(){var t=this.data,e=new THREE.Color(t.color).getHex(),i=t.intensity,o=t.type;switch(o&&(o=o.toLowerCase()),o){case"ambient":return new THREE.AmbientLight(e);case"directional":return this.setShadow(new THREE.DirectionalLight(e,i));case"hemisphere":return new THREE.HemisphereLight(e,t.groundColor,i);case"point":return this.setShadow(new THREE.PointLight(e,i,t.distance,t.decay));case"spot":return this.setShadow(new THREE.SpotLight(e,i,t.distance,t.angle,t.exponent,t.decay));default:return new THREE.AmbientLight(e)}},setShadow:function(t){var e=this.data;return e.castShadow?(t.castShadow=e.castShadow,t.shadow.camera.near=e.shadowCameraNear,t.shadow.camera.far=e.shadowCameraFar,t.shadow.camera.fov=e.shadowCameraFov,t.shadow.darkness=e.shadowDarkness,t.shadow.mapSize.height=e.shadowMapHeight,t.shadow.mapSize.width=e.shadowMapWidth,t):t}}},{}],43:[function(t,e,i){e.exports={schema:{cast:{"default":!1},receive:{"default":!1}},init:function(){this.el.addEventListener("model-loaded",this.update.bind(this))},update:function(){var t=this.data;this.el.object3D.traverse(function(e){e instanceof THREE.Mesh&&(e.castShadow=t.cast,e.receiveShadow=t.receive)})},remove:function(){}}},{}],44:[function(t,e,i){e.exports={name:"cannon",version:"0.6.2",description:"A lightweight 3D physics engine written in JavaScript.",homepage:"https://github.com/schteppe/cannon.js",author:"Stefan Hedman <schteppe@gmail.com> (http://steffe.se)",keywords:["cannon.js","cannon","physics","engine","3d"],main:"./src/Cannon.js",engines:{node:"*"},repository:{type:"git",url:"https://github.com/schteppe/cannon.js.git"},bugs:{url:"https://github.com/schteppe/cannon.js/issues"},licenses:[{type:"MIT"}],devDependencies:{jshint:"latest","uglify-js":"latest",nodeunit:"^0.9.0",grunt:"~0.4.0","grunt-contrib-jshint":"~0.1.1","grunt-contrib-nodeunit":"^0.4.1","grunt-contrib-concat":"~0.1.3","grunt-contrib-uglify":"^0.5.1","grunt-browserify":"^2.1.4","grunt-contrib-yuidoc":"^0.5.2",browserify:"*"},dependencies:{}}},{}],45:[function(t,e,i){e.exports={version:t("../package.json").version,AABB:t("./collision/AABB"),ArrayCollisionMatrix:t("./collision/ArrayCollisionMatrix"),Body:t("./objects/Body"),Box:t("./shapes/Box"),Broadphase:t("./collision/Broadphase"),Constraint:t("./constraints/Constraint"),ContactEquation:t("./equations/ContactEquation"),Narrowphase:t("./world/Narrowphase"),ConeTwistConstraint:t("./constraints/ConeTwistConstraint"),ContactMaterial:t("./material/ContactMaterial"),ConvexPolyhedron:t("./shapes/ConvexPolyhedron"),Cylinder:t("./shapes/Cylinder"),DistanceConstraint:t("./constraints/DistanceConstraint"),Equation:t("./equations/Equation"),EventTarget:t("./utils/EventTarget"),FrictionEquation:t("./equations/FrictionEquation"),GSSolver:t("./solver/GSSolver"),GridBroadphase:t("./collision/GridBroadphase"),Heightfield:t("./shapes/Heightfield"),HingeConstraint:t("./constraints/HingeConstraint"),LockConstraint:t("./constraints/LockConstraint"),Mat3:t("./math/Mat3"),Material:t("./material/Material"),NaiveBroadphase:t("./collision/NaiveBroadphase"),ObjectCollisionMatrix:t("./collision/ObjectCollisionMatrix"),Pool:t("./utils/Pool"),Particle:t("./shapes/Particle"),Plane:t("./shapes/Plane"),PointToPointConstraint:t("./constraints/PointToPointConstraint"),Quaternion:t("./math/Quaternion"),Ray:t("./collision/Ray"),RaycastVehicle:t("./objects/RaycastVehicle"),RaycastResult:t("./collision/RaycastResult"),RigidVehicle:t("./objects/RigidVehicle"),RotationalEquation:t("./equations/RotationalEquation"),RotationalMotorEquation:t("./equations/RotationalMotorEquation"),SAPBroadphase:t("./collision/SAPBroadphase"),SPHSystem:t("./objects/SPHSystem"),Shape:t("./shapes/Shape"),Solver:t("./solver/Solver"),Sphere:t("./shapes/Sphere"),SplitSolver:t("./solver/SplitSolver"),Spring:t("./objects/Spring"),Transform:t("./math/Transform"),Trimesh:t("./shapes/Trimesh"),Vec3:t("./math/Vec3"),Vec3Pool:t("./utils/Vec3Pool"),World:t("./world/World")}},{"../package.json":44,"./collision/AABB":46,"./collision/ArrayCollisionMatrix":47,"./collision/Broadphase":48,"./collision/GridBroadphase":49,"./collision/NaiveBroadphase":50,"./collision/ObjectCollisionMatrix":51,"./collision/Ray":53,"./collision/RaycastResult":54,"./collision/SAPBroadphase":55,"./constraints/ConeTwistConstraint":56,"./constraints/Constraint":57,"./constraints/DistanceConstraint":58,"./constraints/HingeConstraint":59,"./constraints/LockConstraint":60,"./constraints/PointToPointConstraint":61,"./equations/ContactEquation":63,"./equations/Equation":64,"./equations/FrictionEquation":65,"./equations/RotationalEquation":66,"./equations/RotationalMotorEquation":67,"./material/ContactMaterial":68,"./material/Material":69,"./math/Mat3":71,"./math/Quaternion":72,"./math/Transform":73,"./math/Vec3":74,"./objects/Body":75,"./objects/RaycastVehicle":76,"./objects/RigidVehicle":77,"./objects/SPHSystem":78,"./objects/Spring":79,"./shapes/Box":81,"./shapes/ConvexPolyhedron":82,"./shapes/Cylinder":83,"./shapes/Heightfield":84,"./shapes/Particle":85,"./shapes/Plane":86,"./shapes/Shape":87,"./shapes/Sphere":88,"./shapes/Trimesh":89,"./solver/GSSolver":90,"./solver/Solver":91,"./solver/SplitSolver":92,"./utils/EventTarget":93,"./utils/Pool":95,"./utils/Vec3Pool":98,"./world/Narrowphase":99,"./world/World":100}],46:[function(t,e,i){function o(t){t=t||{},this.lowerBound=new n,t.lowerBound&&this.lowerBound.copy(t.lowerBound),this.upperBound=new n,t.upperBound&&this.upperBound.copy(t.upperBound)}var n=t("../math/Vec3");t("../utils/Utils");e.exports=o;var s=new n;o.prototype.setFromPoints=function(t,e,i,o){var n=this.lowerBound,r=this.upperBound,a=i;n.copy(t[0]),a&&a.vmult(n,n),r.copy(n);for(var h=1;h<t.length;h++){var c=t[h];a&&(a.vmult(c,s),c=s),c.x>r.x&&(r.x=c.x),c.x<n.x&&(n.x=c.x),c.y>r.y&&(r.y=c.y),c.y<n.y&&(n.y=c.y),c.z>r.z&&(r.z=c.z),c.z<n.z&&(n.z=c.z)}return e&&(e.vadd(n,n),e.vadd(r,r)),o&&(n.x-=o,n.y-=o,n.z-=o,r.x+=o,r.y+=o,r.z+=o),this},o.prototype.copy=function(t){return this.lowerBound.copy(t.lowerBound),this.upperBound.copy(t.upperBound),this},o.prototype.clone=function(){return(new o).copy(this)},o.prototype.extend=function(t){this.lowerBound.x=Math.min(this.lowerBound.x,t.lowerBound.x),this.upperBound.x=Math.max(this.upperBound.x,t.upperBound.x),this.lowerBound.y=Math.min(this.lowerBound.y,t.lowerBound.y),this.upperBound.y=Math.max(this.upperBound.y,t.upperBound.y),this.lowerBound.z=Math.min(this.lowerBound.z,t.lowerBound.z),this.upperBound.z=Math.max(this.upperBound.z,t.upperBound.z)},o.prototype.overlaps=function(t){var e=this.lowerBound,i=this.upperBound,o=t.lowerBound,n=t.upperBound,s=o.x<=i.x&&i.x<=n.x||e.x<=n.x&&n.x<=i.x,r=o.y<=i.y&&i.y<=n.y||e.y<=n.y&&n.y<=i.y,a=o.z<=i.z&&i.z<=n.z||e.z<=n.z&&n.z<=i.z;return s&&r&&a},o.prototype.volume=function(){var t=this.lowerBound,e=this.upperBound;return(e.x-t.x)*(e.y-t.y)*(e.z-t.z)},o.prototype.contains=function(t){var e=this.lowerBound,i=this.upperBound,o=t.lowerBound,n=t.upperBound;return e.x<=o.x&&i.x>=n.x&&e.y<=o.y&&i.y>=n.y&&e.z<=o.z&&i.z>=n.z},o.prototype.getCorners=function(t,e,i,o,n,s,r,a){var h=this.lowerBound,c=this.upperBound;t.copy(h),e.set(c.x,h.y,h.z),i.set(c.x,c.y,h.z),o.set(h.x,c.y,c.z),n.set(c.x,h.y,h.z),s.set(h.x,c.y,h.z),r.set(h.x,h.y,c.z),a.copy(c)};var r=[new n,new n,new n,new n,new n,new n,new n,new n];o.prototype.toLocalFrame=function(t,e){var i=r,o=i[0],n=i[1],s=i[2],a=i[3],h=i[4],c=i[5],l=i[6],u=i[7];this.getCorners(o,n,s,a,h,c,l,u);for(var p=0;8!==p;p++){var d=i[p];t.pointToLocal(d,d)}return e.setFromPoints(i)},o.prototype.toWorldFrame=function(t,e){var i=r,o=i[0],n=i[1],s=i[2],a=i[3],h=i[4],c=i[5],l=i[6],u=i[7];this.getCorners(o,n,s,a,h,c,l,u);for(var p=0;8!==p;p++){var d=i[p];t.pointToWorld(d,d)}return e.setFromPoints(i)},o.prototype.overlapsRay=function(t){var e=1/t._direction.x,i=1/t._direction.y,o=1/t._direction.z,n=(this.lowerBound.x-t.from.x)*e,s=(this.upperBound.x-t.from.x)*e,r=(this.lowerBound.y-t.from.y)*i,a=(this.upperBound.y-t.from.y)*i,h=(this.lowerBound.z-t.from.z)*o,c=(this.upperBound.z-t.from.z)*o,l=Math.max(Math.max(Math.min(n,s),Math.min(r,a)),Math.min(h,c)),u=Math.min(Math.min(Math.max(n,s),Math.max(r,a)),Math.max(h,c));return!(u<0)&&!(l>u)}},{"../math/Vec3":74,"../utils/Utils":97}],47:[function(t,e,i){function o(){this.matrix=[]}e.exports=o,o.prototype.get=function(t,e){if(t=t.index,e=e.index,e>t){var i=e;e=t,t=i}return this.matrix[(t*(t+1)>>1)+e-1]},o.prototype.set=function(t,e,i){if(t=t.index,e=e.index,e>t){var o=e;e=t,t=o}this.matrix[(t*(t+1)>>1)+e-1]=i?1:0},o.prototype.reset=function(){for(var t=0,e=this.matrix.length;t!==e;t++)this.matrix[t]=0},o.prototype.setNumObjects=function(t){this.matrix.length=t*(t-1)>>1}},{}],48:[function(t,e,i){function o(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}var n=t("../objects/Body"),s=t("../math/Vec3"),r=t("../math/Quaternion");t("../shapes/Shape"),t("../shapes/Plane");e.exports=o,o.prototype.collisionPairs=function(t,e,i){throw new Error("collisionPairs not implemented for this BroadPhase class!")},o.prototype.needBroadphaseCollision=function(t,e){return 0!==(t.collisionFilterGroup&e.collisionFilterMask)&&0!==(e.collisionFilterGroup&t.collisionFilterMask)&&(0===(t.type&n.STATIC)&&t.sleepState!==n.SLEEPING||0===(e.type&n.STATIC)&&e.sleepState!==n.SLEEPING)},o.prototype.intersectionTest=function(t,e,i,o){this.useBoundingBoxes?this.doBoundingBoxBroadphase(t,e,i,o):this.doBoundingSphereBroadphase(t,e,i,o)};var a=new s;new s,new r,new s;o.prototype.doBoundingSphereBroadphase=function(t,e,i,o){var n=a;e.position.vsub(t.position,n);var s=Math.pow(t.boundingRadius+e.boundingRadius,2),r=n.norm2();r<s&&(i.push(t),o.push(e))},o.prototype.doBoundingBoxBroadphase=function(t,e,i,o){t.aabbNeedsUpdate&&t.computeAABB(),e.aabbNeedsUpdate&&e.computeAABB(),t.aabb.overlaps(e.aabb)&&(i.push(t),o.push(e))};var h={keys:[]},c=[],l=[];o.prototype.makePairsUnique=function(t,e){for(var i=h,o=c,n=l,s=t.length,r=0;r!==s;r++)o[r]=t[r],n[r]=e[r];t.length=0,e.length=0;for(var r=0;r!==s;r++){var a=o[r].id,u=n[r].id,p=a<u?a+","+u:u+","+a;i[p]=r,i.keys.push(p)}for(var r=0;r!==i.keys.length;r++){var p=i.keys.pop(),d=i[p];t.push(o[d]),e.push(n[d]),delete i[p]}},o.prototype.setWorld=function(t){};var u=new s;o.boundingSphereCheck=function(t,e){var i=u;return t.position.vsub(e.position,i),Math.pow(t.shape.boundingSphereRadius+e.shape.boundingSphereRadius,2)>i.norm2()},o.prototype.aabbQuery=function(t,e,i){return console.warn(".aabbQuery is not implemented in this Broadphase subclass."),[]}},{"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Plane":86,"../shapes/Shape":87}],49:[function(t,e,i){function o(t,e,i,o,r){n.apply(this),this.nx=i||10,this.ny=o||10,this.nz=r||10,this.aabbMin=t||new s(100,100,100),this.aabbMax=e||new s((-100),(-100),(-100));var a=this.nx*this.ny*this.nz;if(a<=0)throw"GridBroadphase: Each dimension's n must be >0";this.bins=[],this.binLengths=[],this.bins.length=a,this.binLengths.length=a;for(var h=0;h<a;h++)this.bins[h]=[],this.binLengths[h]=0}e.exports=o;var n=t("./Broadphase"),s=t("../math/Vec3"),r=t("../shapes/Shape");o.prototype=new n,o.prototype.constructor=o;var a=new s;new s;o.prototype.collisionPairs=function(t,e,i){function o(t,e,i,o,n,s,r){var a=(t-w)*E|0,h=(e-b)*A|0,c=(i-x)*C|0,f=F((o-w)*E),m=F((n-b)*A),g=F((s-x)*C);a<0?a=0:a>=l&&(a=l-1),h<0?h=0:h>=u&&(h=u-1),c<0?c=0:c>=p&&(c=p-1),f<0?f=0:f>=l&&(f=l-1),m<0?m=0:m>=u&&(m=u-1),g<0?g=0:g>=p&&(g=p-1),a*=d,h*=y,c*=v,f*=d,m*=y,g*=v;for(var B=a;B<=f;B+=d)for(var S=h;S<=m;S+=y)for(var R=c;R<=g;R+=v){var M=B+S+R;L[M][P[M]++]=r}}for(var n=t.numObjects(),s=t.bodies,h=this.aabbMax,c=this.aabbMin,l=this.nx,u=this.ny,p=this.nz,d=u*p,y=p,v=1,f=h.x,m=h.y,g=h.z,w=c.x,b=c.y,x=c.z,E=l/(f-w),A=u/(m-b),C=p/(g-x),B=(f-w)/l,S=(m-b)/u,R=(g-x)/p,M=.5*Math.sqrt(B*B+S*S+R*R),T=r.types,z=T.SPHERE,k=T.PLANE,L=(T.BOX,T.COMPOUND,T.CONVEXPOLYHEDRON,this.bins),P=this.binLengths,q=this.bins.length,V=0;V!==q;V++)P[V]=0;for(var F=Math.ceil,c=Math.min,h=Math.max,V=0;V!==n;V++){var N=s[V],I=N.shape;switch(I.type){case z:var O=N.position.x,H=N.position.y,j=N.position.z,D=I.radius;o(O-D,H-D,j-D,O+D,H+D,j+D,N);break;case k:I.worldNormalNeedsUpdate&&I.computeWorldNormal(N.quaternion);var W=I.worldNormal,K=w+.5*B-N.position.x,_=b+.5*S-N.position.y,U=x+.5*R-N.position.z,G=a;G.set(K,_,U);for(var X=0,Y=0;X!==l;X++,Y+=d,G.y=_,G.x+=B)for(var Q=0,Z=0;Q!==u;Q++,Z+=y,G.z=U,G.y+=S)for(var J=0,$=0;J!==p;J++,$+=v,G.z+=R)if(G.dot(W)<M){var tt=Y+Z+$;L[tt][P[tt]++]=N}break;default:N.aabbNeedsUpdate&&N.computeAABB(),o(N.aabb.lowerBound.x,N.aabb.lowerBound.y,N.aabb.lowerBound.z,N.aabb.upperBound.x,N.aabb.upperBound.y,N.aabb.upperBound.z,N)}}for(var V=0;V!==q;V++){var et=P[V];if(et>1)for(var it=L[V],X=0;X!==et;X++)for(var N=it[X],Q=0;Q!==X;Q++){var ot=it[Q];this.needBroadphaseCollision(N,ot)&&this.intersectionTest(N,ot,e,i)}}this.makePairsUnique(e,i)}},{"../math/Vec3":74,"../shapes/Shape":87,"./Broadphase":48}],50:[function(t,e,i){function o(){n.apply(this)}e.exports=o;var n=t("./Broadphase"),s=t("./AABB");o.prototype=new n,o.prototype.constructor=o,o.prototype.collisionPairs=function(t,e,i){var o,n,s,r,a=t.bodies,h=a.length;for(o=0;o!==h;o++)for(n=0;n!==o;n++)s=a[o],r=a[n],this.needBroadphaseCollision(s,r)&&this.intersectionTest(s,r,e,i)};new s;o.prototype.aabbQuery=function(t,e,i){i=i||[];for(var o=0;o<t.bodies.length;o++){var n=t.bodies[o];n.aabbNeedsUpdate&&n.computeAABB(),n.aabb.overlaps(e)&&i.push(n)}return i}},{"./AABB":46,"./Broadphase":48}],51:[function(t,e,i){function o(){this.matrix={}}e.exports=o,o.prototype.get=function(t,e){if(t=t.id,e=e.id,e>t){var i=e;e=t,t=i}return t+"-"+e in this.matrix},o.prototype.set=function(t,e,i){if(t=t.id,e=e.id,e>t){var o=e;e=t,t=o}i?this.matrix[t+"-"+e]=!0:delete this.matrix[t+"-"+e]},o.prototype.reset=function(){this.matrix={}},o.prototype.setNumObjects=function(t){}},{}],52:[function(t,e,i){function o(){this.current=[],this.previous=[]}function n(t,e){t.push((4294901760&e)>>16,65535&e)}e.exports=o,o.prototype.getKey=function(t,e){if(e<t){var i=e;e=t,t=i}return t<<16|e},o.prototype.set=function(t,e){for(var i=this.getKey(t,e),o=this.current,n=0;i>o[n];)n++;if(i!==o[n]){for(var e=o.length-1;e>=n;e--)o[e+1]=o[e];o[n]=i}},o.prototype.tick=function(){var t=this.current;this.current=this.previous,this.previous=t,this.current.length=0},o.prototype.getDiff=function(t,e){for(var i=this.current,o=this.previous,s=i.length,r=o.length,a=0,h=0;h<s;h++){for(var c=!1,l=i[h];l>o[a];)a++;c=l===o[a],c||n(t,l)}a=0;for(var h=0;h<r;h++){for(var c=!1,u=o[h];u>i[a];)a++;c=i[a]===u,c||n(e,u)}}},{}],53:[function(t,e,i){function o(t,e){this.from=t?t.clone():new r,this.to=e?e.clone():new r,this._direction=new r,this.precision=1e-4,this.checkCollisionResponse=!0,this.skipBackfaces=!1,this.collisionFilterMask=-1,this.collisionFilterGroup=-1,this.mode=o.ANY,this.result=new c,this.hasHit=!1,this.callback=function(t){}}function n(t,e,i,o){o.vsub(e,I),i.vsub(e,y),t.vsub(e,v);var n,s,r=I.dot(I),a=I.dot(y),h=I.dot(v),c=y.dot(y),l=y.dot(v);return(n=c*h-a*l)>=0&&(s=r*l-a*h)>=0&&n+s<r*c-a*a}function s(t,e,i){i.vsub(t,I);var o=I.dot(e);e.mult(o,O),O.vadd(t,O);var n=i.distanceTo(O);return n}e.exports=o;var r=t("../math/Vec3"),a=t("../math/Quaternion"),h=t("../math/Transform"),c=(t("../shapes/ConvexPolyhedron"),t("../shapes/Box"),t("../collision/RaycastResult")),l=t("../shapes/Shape"),u=t("../collision/AABB");o.prototype.constructor=o,o.CLOSEST=1,o.ANY=2,o.ALL=4;var p=new u,d=[];o.prototype.intersectWorld=function(t,e){return this.mode=e.mode||o.ANY,this.result=e.result||new c,this.skipBackfaces=!!e.skipBackfaces,this.collisionFilterMask="undefined"!=typeof e.collisionFilterMask?e.collisionFilterMask:-1,this.collisionFilterGroup="undefined"!=typeof e.collisionFilterGroup?e.collisionFilterGroup:-1,e.from&&this.from.copy(e.from),e.to&&this.to.copy(e.to),this.callback=e.callback||function(){},this.hasHit=!1,this.result.reset(),this._updateDirection(),this.getAABB(p),d.length=0,t.broadphase.aabbQuery(t,p,d),this.intersectBodies(d),this.hasHit};var y=new r,v=new r;o.pointInTriangle=n;var f=new r,m=new a;o.prototype.intersectBody=function(t,e){e&&(this.result=e,this._updateDirection());var i=this.checkCollisionResponse;if((!i||t.collisionResponse)&&0!==(this.collisionFilterGroup&t.collisionFilterMask)&&0!==(t.collisionFilterGroup&this.collisionFilterMask))for(var o=f,n=m,s=0,r=t.shapes.length;s<r;s++){var a=t.shapes[s];if((!i||a.collisionResponse)&&(t.quaternion.mult(t.shapeOrientations[s],n),t.quaternion.vmult(t.shapeOffsets[s],o),o.vadd(t.position,o),this.intersectShape(a,n,o,t),this.result._shouldStop))break}},o.prototype.intersectBodies=function(t,e){e&&(this.result=e,this._updateDirection());for(var i=0,o=t.length;!this.result._shouldStop&&i<o;i++)this.intersectBody(t[i])},o.prototype._updateDirection=function(){this.to.vsub(this.from,this._direction),this._direction.normalize()},o.prototype.intersectShape=function(t,e,i,o){var n=this.from,r=s(n,this._direction,i);if(!(r>t.boundingSphereRadius)){var a=this[t.type];a&&a.call(this,t,e,i,o,t)}};var g=(new r,new r,new r),w=new r,b=new r,x=new r;new r,new c;o.prototype.intersectBox=function(t,e,i,o,n){return this.intersectConvex(t.convexPolyhedronRepresentation,e,i,o,n)},o.prototype[l.types.BOX]=o.prototype.intersectBox,o.prototype.intersectPlane=function(t,e,i,o,n){var s=this.from,a=this.to,h=this._direction,c=new r(0,0,1);e.vmult(c,c);var l=new r;s.vsub(i,l);var u=l.dot(c);a.vsub(i,l);var p=l.dot(c);if(!(u*p>0||s.distanceTo(a)<u)){var d=c.dot(h);if(!(Math.abs(d)<this.precision)){var y=new r,v=new r,f=new r;s.vsub(i,y);var m=-c.dot(y)/d;h.scale(m,v),s.vadd(v,f),this.reportIntersection(c,f,n,o,-1)}}},o.prototype[l.types.PLANE]=o.prototype.intersectPlane,o.prototype.getAABB=function(t){var e=this.to,i=this.from;t.lowerBound.x=Math.min(e.x,i.x),t.lowerBound.y=Math.min(e.y,i.y),t.lowerBound.z=Math.min(e.z,i.z),t.upperBound.x=Math.max(e.x,i.x),t.upperBound.y=Math.max(e.y,i.y),t.upperBound.z=Math.max(e.z,i.z)};var E={faceList:[0]},A=new r,C=new o,B=[];o.prototype.intersectHeightfield=function(t,e,i,o,n){var s=(t.data,t.elementSize,C);s.from.copy(this.from),s.to.copy(this.to),h.pointToLocalFrame(i,e,s.from,s.from),h.pointToLocalFrame(i,e,s.to,s.to),s._updateDirection();var r,a,c,l,p=B;r=a=0,c=l=t.data.length-1;var d=new u;s.getAABB(d),t.getIndexOfPosition(d.lowerBound.x,d.lowerBound.y,p,!0),r=Math.max(r,p[0]),a=Math.max(a,p[1]),t.getIndexOfPosition(d.upperBound.x,d.upperBound.y,p,!0),c=Math.min(c,p[0]+1),l=Math.min(l,p[1]+1);for(var y=r;y<c;y++)for(var v=a;v<l;v++){if(this.result._shouldStop)return;if(t.getAabbAtIndex(y,v,d),d.overlapsRay(s)){if(t.getConvexTrianglePillar(y,v,!1),h.pointToWorldFrame(i,e,t.pillarOffset,A),this.intersectConvex(t.pillarConvex,e,A,o,n,E),this.result._shouldStop)return;t.getConvexTrianglePillar(y,v,!0),h.pointToWorldFrame(i,e,t.pillarOffset,A),this.intersectConvex(t.pillarConvex,e,A,o,n,E)}}},o.prototype[l.types.HEIGHTFIELD]=o.prototype.intersectHeightfield;var S=new r,R=new r;o.prototype.intersectSphere=function(t,e,i,o,n){var s=this.from,r=this.to,a=t.radius,h=Math.pow(r.x-s.x,2)+Math.pow(r.y-s.y,2)+Math.pow(r.z-s.z,2),c=2*((r.x-s.x)*(s.x-i.x)+(r.y-s.y)*(s.y-i.y)+(r.z-s.z)*(s.z-i.z)),l=Math.pow(s.x-i.x,2)+Math.pow(s.y-i.y,2)+Math.pow(s.z-i.z,2)-Math.pow(a,2),u=Math.pow(c,2)-4*h*l,p=S,d=R;if(!(u<0))if(0===u)s.lerp(r,u,p),p.vsub(i,d),d.normalize(),this.reportIntersection(d,p,n,o,-1);else{var y=(-c-Math.sqrt(u))/(2*h),v=(-c+Math.sqrt(u))/(2*h);if(y>=0&&y<=1&&(s.lerp(r,y,p),p.vsub(i,d),d.normalize(),this.reportIntersection(d,p,n,o,-1)),this.result._shouldStop)return;v>=0&&v<=1&&(s.lerp(r,v,p),p.vsub(i,d),d.normalize(),this.reportIntersection(d,p,n,o,-1))}},o.prototype[l.types.SPHERE]=o.prototype.intersectSphere;var M=new r,T=(new r,new r,new r);o.prototype.intersectConvex=function(t,e,i,o,s,r){for(var a=M,h=T,c=r&&r.faceList||null,l=t.faces,u=t.vertices,p=t.faceNormals,d=this._direction,y=this.from,v=this.to,f=y.distanceTo(v),m=c?c.length:l.length,E=this.result,A=0;!E._shouldStop&&A<m;A++){var C=c?c[A]:A,B=l[C],S=p[C],R=e,z=i;h.copy(u[B[0]]),R.vmult(h,h),h.vadd(z,h),h.vsub(y,h),R.vmult(S,a);var k=d.dot(a);if(!(Math.abs(k)<this.precision)){var L=a.dot(h)/k;if(!(L<0)){d.mult(L,g),g.vadd(y,g),w.copy(u[B[0]]),R.vmult(w,w),z.vadd(w,w);for(var P=1;!E._shouldStop&&P<B.length-1;P++){b.copy(u[B[P]]),x.copy(u[B[P+1]]),R.vmult(b,b),R.vmult(x,x),z.vadd(b,b),z.vadd(x,x);var q=g.distanceTo(y);!n(g,w,b,x)&&!n(g,b,w,x)||q>f||this.reportIntersection(a,g,s,o,C)}}}}},o.prototype[l.types.CONVEXPOLYHEDRON]=o.prototype.intersectConvex;var z=new r,k=new r,L=new r,P=new r,q=new r,V=new r,F=(new u,[]),N=new h;o.prototype.intersectTrimesh=function(t,e,i,o,s,r){var a=z,c=F,l=N,u=T,p=k,d=L,y=P,v=V,f=q,m=(r&&r.faceList||null,t.indices),E=(t.vertices,t.faceNormals,this.from),A=this.to,C=this._direction;l.position.copy(i),l.quaternion.copy(e),h.vectorToLocalFrame(i,e,C,p),h.pointToLocalFrame(i,e,E,d),h.pointToLocalFrame(i,e,A,y),y.x*=t.scale.x,y.y*=t.scale.y,y.z*=t.scale.z,d.x*=t.scale.x,d.y*=t.scale.y,d.z*=t.scale.z,y.vsub(d,p),p.normalize();var B=d.distanceSquared(y);t.tree.rayQuery(this,l,c);for(var S=0,R=c.length;!this.result._shouldStop&&S!==R;S++){var M=c[S];t.getNormal(M,a),t.getVertex(m[3*M],w),w.vsub(d,u);var I=p.dot(a),O=a.dot(u)/I;if(!(O<0)){p.scale(O,g),g.vadd(d,g),t.getVertex(m[3*M+1],b),t.getVertex(m[3*M+2],x);var H=g.distanceSquared(d);!n(g,b,w,x)&&!n(g,w,b,x)||H>B||(h.vectorToWorldFrame(e,a,f),h.pointToWorldFrame(i,e,g,v),this.reportIntersection(f,v,s,o,M))}}c.length=0},o.prototype[l.types.TRIMESH]=o.prototype.intersectTrimesh,o.prototype.reportIntersection=function(t,e,i,n,s){var r=this.from,a=this.to,h=r.distanceTo(e),c=this.result;if(!(this.skipBackfaces&&t.dot(this._direction)>0))switch(c.hitFaceIndex="undefined"!=typeof s?s:-1,this.mode){case o.ALL:this.hasHit=!0,c.set(r,a,t,e,i,n,h),c.hasHit=!0,this.callback(c);break;case o.CLOSEST:(h<c.distance||!c.hasHit)&&(this.hasHit=!0,c.hasHit=!0,c.set(r,a,t,e,i,n,h));break;case o.ANY:this.hasHit=!0,c.hasHit=!0,c.set(r,a,t,e,i,n,h),c._shouldStop=!0}};var I=new r,O=new r},{"../collision/AABB":46,"../collision/RaycastResult":54,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../shapes/Box":81,"../shapes/ConvexPolyhedron":82,"../shapes/Shape":87}],54:[function(t,e,i){function o(){this.rayFromWorld=new n,this.rayToWorld=new n,this.hitNormalWorld=new n,this.hitPointWorld=new n,this.hasHit=!1,this.shape=null,this.body=null,this.hitFaceIndex=-1,this.distance=-1,this._shouldStop=!1}var n=t("../math/Vec3");e.exports=o,o.prototype.reset=function(){this.rayFromWorld.setZero(),this.rayToWorld.setZero(),this.hitNormalWorld.setZero(),this.hitPointWorld.setZero(),this.hasHit=!1,this.shape=null,this.body=null,this.hitFaceIndex=-1,this.distance=-1,this._shouldStop=!1},o.prototype.abort=function(){this._shouldStop=!0},o.prototype.set=function(t,e,i,o,n,s,r){this.rayFromWorld.copy(t),this.rayToWorld.copy(e),this.hitNormalWorld.copy(i),this.hitPointWorld.copy(o),this.shape=n,this.body=s,this.distance=r}},{"../math/Vec3":74}],55:[function(t,e,i){function o(t){n.apply(this),this.axisList=[],this.world=null,this.axisIndex=0;var e=this.axisList;this._addBodyHandler=function(t){e.push(t.body)},this._removeBodyHandler=function(t){var i=e.indexOf(t.body);i!==-1&&e.splice(i,1)},t&&this.setWorld(t)}var n=(t("../shapes/Shape"),t("../collision/Broadphase"));e.exports=o,o.prototype=new n,o.prototype.setWorld=function(t){this.axisList.length=0;for(var e=0;e<t.bodies.length;e++)this.axisList.push(t.bodies[e]);t.removeEventListener("addBody",this._addBodyHandler),t.removeEventListener("removeBody",this._removeBodyHandler),t.addEventListener("addBody",this._addBodyHandler),t.addEventListener("removeBody",this._removeBodyHandler),this.world=t,this.dirty=!0},o.insertionSortX=function(t){for(var e=1,i=t.length;e<i;e++){for(var o=t[e],n=e-1;n>=0&&!(t[n].aabb.lowerBound.x<=o.aabb.lowerBound.x);n--)t[n+1]=t[n];t[n+1]=o}return t},o.insertionSortY=function(t){for(var e=1,i=t.length;e<i;e++){for(var o=t[e],n=e-1;n>=0&&!(t[n].aabb.lowerBound.y<=o.aabb.lowerBound.y);n--)t[n+1]=t[n];t[n+1]=o}return t},o.insertionSortZ=function(t){for(var e=1,i=t.length;e<i;e++){for(var o=t[e],n=e-1;n>=0&&!(t[n].aabb.lowerBound.z<=o.aabb.lowerBound.z);n--)t[n+1]=t[n];t[n+1]=o}return t},o.prototype.collisionPairs=function(t,e,i){var n,s,r=this.axisList,a=r.length,h=this.axisIndex;for(this.dirty&&(this.sortList(),this.dirty=!1),n=0;n!==a;n++){var c=r[n];for(s=n+1;s<a;s++){var l=r[s];if(this.needBroadphaseCollision(c,l)){if(!o.checkBounds(c,l,h))break;this.intersectionTest(c,l,e,i)}}}},o.prototype.sortList=function(){for(var t=this.axisList,e=this.axisIndex,i=t.length,n=0;n!==i;n++){var s=t[n];s.aabbNeedsUpdate&&s.computeAABB()}0===e?o.insertionSortX(t):1===e?o.insertionSortY(t):2===e&&o.insertionSortZ(t)},o.checkBounds=function(t,e,i){var o,n;0===i?(o=t.position.x,n=e.position.x):1===i?(o=t.position.y,n=e.position.y):2===i&&(o=t.position.z,n=e.position.z);var s=t.boundingRadius,r=e.boundingRadius,a=o+s,h=n-r;return h<a},o.prototype.autoDetectAxis=function(){for(var t=0,e=0,i=0,o=0,n=0,s=0,r=this.axisList,a=r.length,h=1/a,c=0;c!==a;c++){var l=r[c],u=l.position.x;t+=u,e+=u*u;var p=l.position.y;i+=p,o+=p*p;var d=l.position.z;n+=d,s+=d*d}var y=e-t*t*h,v=o-i*i*h,f=s-n*n*h;y>v?y>f?this.axisIndex=0:this.axisIndex=2:v>f?this.axisIndex=1:this.axisIndex=2},o.prototype.aabbQuery=function(t,e,i){i=i||[],this.dirty&&(this.sortList(),this.dirty=!1);var o=this.axisIndex,n="x";1===o&&(n="y"),2===o&&(n="z");for(var s=this.axisList,r=(e.lowerBound[n],e.upperBound[n],0);r<s.length;r++){var a=s[r];a.aabbNeedsUpdate&&a.computeAABB(),a.aabb.overlaps(e)&&i.push(a)}return i}},{"../collision/Broadphase":48,"../shapes/Shape":87}],56:[function(t,e,i){function o(t,e,i){i=i||{};var o="undefined"!=typeof i.maxForce?i.maxForce:1e6,h=i.pivotA?i.pivotA.clone():new a,c=i.pivotB?i.pivotB.clone():new a;this.axisA=i.axisA?i.axisA.clone():new a,this.axisB=i.axisB?i.axisB.clone():new a,n.call(this,t,h,e,c,o),this.collideConnected=!!i.collideConnected,this.angle="undefined"!=typeof i.angle?i.angle:0;var l=this.coneEquation=new s(t,e,i),u=this.twistEquation=new r(t,e,i);this.twistAngle="undefined"!=typeof i.twistAngle?i.twistAngle:0,l.maxForce=0,l.minForce=-o,u.maxForce=0,u.minForce=-o,this.equations.push(l,u)}e.exports=o;var n=(t("./Constraint"),t("./PointToPointConstraint")),s=t("../equations/ConeEquation"),r=t("../equations/RotationalEquation"),a=(t("../equations/ContactEquation"),t("../math/Vec3"));o.prototype=new n,o.constructor=o;new a,new a;o.prototype.update=function(){var t=this.bodyA,e=this.bodyB,i=this.coneEquation,o=this.twistEquation;n.prototype.update.call(this),t.vectorToWorldFrame(this.axisA,i.axisA),e.vectorToWorldFrame(this.axisB,i.axisB),this.axisA.tangents(o.axisA,o.axisA),t.vectorToWorldFrame(o.axisA,o.axisA),this.axisB.tangents(o.axisB,o.axisB),e.vectorToWorldFrame(o.axisB,o.axisB),i.angle=this.angle,o.maxAngle=this.twistAngle}},{"../equations/ConeEquation":62,"../equations/ContactEquation":63,"../equations/RotationalEquation":66,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],57:[function(t,e,i){function o(t,e,i){i=n.defaults(i,{collideConnected:!0,wakeUpBodies:!0}),this.equations=[],this.bodyA=t,this.bodyB=e,this.id=o.idCounter++,this.collideConnected=i.collideConnected,i.wakeUpBodies&&(t&&t.wakeUp(),e&&e.wakeUp())}e.exports=o;var n=t("../utils/Utils");o.prototype.update=function(){throw new Error("method update() not implmemented in this Constraint subclass!")},o.prototype.enable=function(){for(var t=this.equations,e=0;e<t.length;e++)t[e].enabled=!0},o.prototype.disable=function(){for(var t=this.equations,e=0;e<t.length;e++)t[e].enabled=!1},o.idCounter=0},{"../utils/Utils":97}],58:[function(t,e,i){function o(t,e,i,o){n.call(this,t,e),"undefined"==typeof i&&(i=t.position.distanceTo(e.position)),"undefined"==typeof o&&(o=1e6),this.distance=i;var r=this.distanceEquation=new s(t,e);this.equations.push(r),r.minForce=-o,r.maxForce=o}e.exports=o;var n=t("./Constraint"),s=t("../equations/ContactEquation");o.prototype=new n,o.prototype.update=function(){var t=this.bodyA,e=this.bodyB,i=this.distanceEquation,o=.5*this.distance,n=i.ni;e.position.vsub(t.position,n),n.normalize(),n.mult(o,i.ri),n.mult(-o,i.rj)}},{"../equations/ContactEquation":63,"./Constraint":57}],59:[function(t,e,i){function o(t,e,i){i=i||{};var o="undefined"!=typeof i.maxForce?i.maxForce:1e6,h=i.pivotA?i.pivotA.clone():new a,c=i.pivotB?i.pivotB.clone():new a;n.call(this,t,h,e,c,o);var l=this.axisA=i.axisA?i.axisA.clone():new a(1,0,0);l.normalize();var u=this.axisB=i.axisB?i.axisB.clone():new a(1,0,0);u.normalize();var p=this.rotationalEquation1=new s(t,e,i),d=this.rotationalEquation2=new s(t,e,i),y=this.motorEquation=new r(t,e,o);y.enabled=!1,this.equations.push(p,d,y)}e.exports=o;var n=(t("./Constraint"),t("./PointToPointConstraint")),s=t("../equations/RotationalEquation"),r=t("../equations/RotationalMotorEquation"),a=(t("../equations/ContactEquation"),t("../math/Vec3"));o.prototype=new n,o.constructor=o,o.prototype.enableMotor=function(){this.motorEquation.enabled=!0},o.prototype.disableMotor=function(){this.motorEquation.enabled=!1},o.prototype.setMotorSpeed=function(t){this.motorEquation.targetVelocity=t},o.prototype.setMotorMaxForce=function(t){this.motorEquation.maxForce=t,this.motorEquation.minForce=-t};var h=new a,c=new a;o.prototype.update=function(){var t=this.bodyA,e=this.bodyB,i=this.motorEquation,o=this.rotationalEquation1,s=this.rotationalEquation2,r=h,a=c,l=this.axisA,u=this.axisB;n.prototype.update.call(this),t.quaternion.vmult(l,r),e.quaternion.vmult(u,a),r.tangents(o.axisA,s.axisA),o.axisB.copy(a),s.axisB.copy(a),this.motorEquation.enabled&&(t.quaternion.vmult(this.axisA,i.axisA),e.quaternion.vmult(this.axisB,i.axisB))}},{"../equations/ContactEquation":63,
"../equations/RotationalEquation":66,"../equations/RotationalMotorEquation":67,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],60:[function(t,e,i){function o(t,e,i){i=i||{};var o="undefined"!=typeof i.maxForce?i.maxForce:1e6,a=new r,h=new r,c=new r;t.position.vadd(e.position,c),c.scale(.5,c),e.pointToLocalFrame(c,h),t.pointToLocalFrame(c,a),n.call(this,t,a,e,h,o),this.xA=t.vectorToLocalFrame(r.UNIT_X),this.xB=e.vectorToLocalFrame(r.UNIT_X),this.yA=t.vectorToLocalFrame(r.UNIT_Y),this.yB=e.vectorToLocalFrame(r.UNIT_Y),this.zA=t.vectorToLocalFrame(r.UNIT_Z),this.zB=e.vectorToLocalFrame(r.UNIT_Z);var l=this.rotationalEquation1=new s(t,e,i),u=this.rotationalEquation2=new s(t,e,i),p=this.rotationalEquation3=new s(t,e,i);this.equations.push(l,u,p)}e.exports=o;var n=(t("./Constraint"),t("./PointToPointConstraint")),s=t("../equations/RotationalEquation"),r=(t("../equations/RotationalMotorEquation"),t("../equations/ContactEquation"),t("../math/Vec3"));o.prototype=new n,o.constructor=o;new r,new r;o.prototype.update=function(){var t=this.bodyA,e=this.bodyB,i=(this.motorEquation,this.rotationalEquation1),o=this.rotationalEquation2,s=this.rotationalEquation3;n.prototype.update.call(this),t.vectorToWorldFrame(this.xA,i.axisA),e.vectorToWorldFrame(this.yB,i.axisB),t.vectorToWorldFrame(this.yA,o.axisA),e.vectorToWorldFrame(this.zB,o.axisB),t.vectorToWorldFrame(this.zA,s.axisA),e.vectorToWorldFrame(this.xB,s.axisB)}},{"../equations/ContactEquation":63,"../equations/RotationalEquation":66,"../equations/RotationalMotorEquation":67,"../math/Vec3":74,"./Constraint":57,"./PointToPointConstraint":61}],61:[function(t,e,i){function o(t,e,i,o,a){n.call(this,t,i),a="undefined"!=typeof a?a:1e6,this.pivotA=e?e.clone():new r,this.pivotB=o?o.clone():new r;var h=this.equationX=new s(t,i),c=this.equationY=new s(t,i),l=this.equationZ=new s(t,i);this.equations.push(h,c,l),h.minForce=c.minForce=l.minForce=-a,h.maxForce=c.maxForce=l.maxForce=a,h.ni.set(1,0,0),c.ni.set(0,1,0),l.ni.set(0,0,1)}e.exports=o;var n=t("./Constraint"),s=t("../equations/ContactEquation"),r=t("../math/Vec3");o.prototype=new n,o.prototype.update=function(){var t=this.bodyA,e=this.bodyB,i=this.equationX,o=this.equationY,n=this.equationZ;t.quaternion.vmult(this.pivotA,i.ri),e.quaternion.vmult(this.pivotB,i.rj),o.ri.copy(i.ri),o.rj.copy(i.rj),n.ri.copy(i.ri),n.rj.copy(i.rj)}},{"../equations/ContactEquation":63,"../math/Vec3":74,"./Constraint":57}],62:[function(t,e,i){function o(t,e,i){i=i||{};var o="undefined"!=typeof i.maxForce?i.maxForce:1e6;s.call(this,t,e,-o,o),this.axisA=i.axisA?i.axisA.clone():new n(1,0,0),this.axisB=i.axisB?i.axisB.clone():new n(0,1,0),this.angle="undefined"!=typeof i.angle?i.angle:0}e.exports=o;var n=t("../math/Vec3"),s=(t("../math/Mat3"),t("./Equation"));o.prototype=new s,o.prototype.constructor=o;var r=new n,a=new n;o.prototype.computeB=function(t){var e=this.a,i=this.b,o=this.axisA,n=this.axisB,s=r,h=a,c=this.jacobianElementA,l=this.jacobianElementB;o.cross(n,s),n.cross(o,h),c.rotational.copy(h),l.rotational.copy(s);var u=Math.cos(this.angle)-o.dot(n),p=this.computeGW(),d=this.computeGiMf(),y=-u*e-p*i-t*d;return y}},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],63:[function(t,e,i){function o(t,e,i){i="undefined"!=typeof i?i:1e6,n.call(this,t,e,0,i),this.restitution=0,this.ri=new s,this.rj=new s,this.ni=new s}e.exports=o;var n=t("./Equation"),s=t("../math/Vec3");t("../math/Mat3");o.prototype=new n,o.prototype.constructor=o;var r=new s,a=new s,h=new s;o.prototype.computeB=function(t){var e=this.a,i=this.b,o=this.bi,n=this.bj,s=this.ri,c=this.rj,l=r,u=a,p=o.velocity,d=o.angularVelocity,y=(o.force,o.torque,n.velocity),v=n.angularVelocity,f=(n.force,n.torque,h),m=this.jacobianElementA,g=this.jacobianElementB,w=this.ni;s.cross(w,l),c.cross(w,u),w.negate(m.spatial),l.negate(m.rotational),g.spatial.copy(w),g.rotational.copy(u),f.copy(n.position),f.vadd(c,f),f.vsub(o.position,f),f.vsub(s,f);var b=w.dot(f),x=this.restitution+1,E=x*y.dot(w)-x*p.dot(w)+v.dot(u)-d.dot(l),A=this.computeGiMf(),C=-b*e-E*i-t*A;return C};var c=new s,l=new s,u=new s,p=new s,d=new s;o.prototype.getImpactVelocityAlongNormal=function(){var t=c,e=l,i=u,o=p,n=d;return this.bi.position.vadd(this.ri,i),this.bj.position.vadd(this.rj,o),this.bi.getVelocityAtWorldPoint(i,t),this.bj.getVelocityAtWorldPoint(o,e),t.vsub(e,n),this.ni.dot(n)}},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],64:[function(t,e,i){function o(t,e,i,s){this.id=o.id++,this.minForce="undefined"==typeof i?-1e6:i,this.maxForce="undefined"==typeof s?1e6:s,this.bi=t,this.bj=e,this.a=0,this.b=0,this.eps=0,this.jacobianElementA=new n,this.jacobianElementB=new n,this.enabled=!0,this.multiplier=0,this.setSpookParams(1e7,4,1/60)}e.exports=o;var n=t("../math/JacobianElement"),s=t("../math/Vec3");o.prototype.constructor=o,o.id=0,o.prototype.setSpookParams=function(t,e,i){var o=e,n=t,s=i;this.a=4/(s*(1+4*o)),this.b=4*o/(1+4*o),this.eps=4/(s*s*n*(1+4*o))},o.prototype.computeB=function(t,e,i){var o=this.computeGW(),n=this.computeGq(),s=this.computeGiMf();return-n*t-o*e-s*i},o.prototype.computeGq=function(){var t=this.jacobianElementA,e=this.jacobianElementB,i=this.bi,o=this.bj,n=i.position,s=o.position;return t.spatial.dot(n)+e.spatial.dot(s)};new s;o.prototype.computeGW=function(){var t=this.jacobianElementA,e=this.jacobianElementB,i=this.bi,o=this.bj,n=i.velocity,s=o.velocity,r=i.angularVelocity,a=o.angularVelocity;return t.multiplyVectors(n,r)+e.multiplyVectors(s,a)},o.prototype.computeGWlambda=function(){var t=this.jacobianElementA,e=this.jacobianElementB,i=this.bi,o=this.bj,n=i.vlambda,s=o.vlambda,r=i.wlambda,a=o.wlambda;return t.multiplyVectors(n,r)+e.multiplyVectors(s,a)};var r=new s,a=new s,h=new s,c=new s;o.prototype.computeGiMf=function(){var t=this.jacobianElementA,e=this.jacobianElementB,i=this.bi,o=this.bj,n=i.force,s=i.torque,l=o.force,u=o.torque,p=i.invMassSolve,d=o.invMassSolve;return n.scale(p,r),l.scale(d,a),i.invInertiaWorldSolve.vmult(s,h),o.invInertiaWorldSolve.vmult(u,c),t.multiplyVectors(r,h)+e.multiplyVectors(a,c)};var l=new s;o.prototype.computeGiMGt=function(){var t=this.jacobianElementA,e=this.jacobianElementB,i=this.bi,o=this.bj,n=i.invMassSolve,s=o.invMassSolve,r=i.invInertiaWorldSolve,a=o.invInertiaWorldSolve,h=n+s;return r.vmult(t.rotational,l),h+=l.dot(t.rotational),a.vmult(e.rotational,l),h+=l.dot(e.rotational)};var u=new s;new s,new s,new s,new s,new s;o.prototype.addToWlambda=function(t){var e=this.jacobianElementA,i=this.jacobianElementB,o=this.bi,n=this.bj,s=u;o.vlambda.addScaledVector(o.invMassSolve*t,e.spatial,o.vlambda),n.vlambda.addScaledVector(n.invMassSolve*t,i.spatial,n.vlambda),o.invInertiaWorldSolve.vmult(e.rotational,s),o.wlambda.addScaledVector(t,s,o.wlambda),n.invInertiaWorldSolve.vmult(i.rotational,s),n.wlambda.addScaledVector(t,s,n.wlambda)},o.prototype.computeC=function(){return this.computeGiMGt()+this.eps}},{"../math/JacobianElement":70,"../math/Vec3":74}],65:[function(t,e,i){function o(t,e,i){n.call(this,t,e,-i,i),this.ri=new s,this.rj=new s,this.t=new s}e.exports=o;var n=t("./Equation"),s=t("../math/Vec3");t("../math/Mat3");o.prototype=new n,o.prototype.constructor=o;var r=new s,a=new s;o.prototype.computeB=function(t){var e=(this.a,this.b),i=(this.bi,this.bj,this.ri),o=this.rj,n=r,s=a,h=this.t;i.cross(h,n),o.cross(h,s);var c=this.jacobianElementA,l=this.jacobianElementB;h.negate(c.spatial),n.negate(c.rotational),l.spatial.copy(h),l.rotational.copy(s);var u=this.computeGW(),p=this.computeGiMf(),d=-u*e-t*p;return d}},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],66:[function(t,e,i){function o(t,e,i){i=i||{};var o="undefined"!=typeof i.maxForce?i.maxForce:1e6;s.call(this,t,e,-o,o),this.axisA=i.axisA?i.axisA.clone():new n(1,0,0),this.axisB=i.axisB?i.axisB.clone():new n(0,1,0),this.maxAngle=Math.PI/2}e.exports=o;var n=t("../math/Vec3"),s=(t("../math/Mat3"),t("./Equation"));o.prototype=new s,o.prototype.constructor=o;var r=new n,a=new n;o.prototype.computeB=function(t){var e=this.a,i=this.b,o=this.axisA,n=this.axisB,s=r,h=a,c=this.jacobianElementA,l=this.jacobianElementB;o.cross(n,s),n.cross(o,h),c.rotational.copy(h),l.rotational.copy(s);var u=Math.cos(this.maxAngle)-o.dot(n),p=this.computeGW(),d=this.computeGiMf(),y=-u*e-p*i-t*d;return y}},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],67:[function(t,e,i){function o(t,e,i){i="undefined"!=typeof i?i:1e6,s.call(this,t,e,-i,i),this.axisA=new n,this.axisB=new n,this.targetVelocity=0}e.exports=o;var n=t("../math/Vec3"),s=(t("../math/Mat3"),t("./Equation"));o.prototype=new s,o.prototype.constructor=o,o.prototype.computeB=function(t){var e=(this.a,this.b),i=(this.bi,this.bj,this.axisA),o=this.axisB,n=this.jacobianElementA,s=this.jacobianElementB;n.rotational.copy(i),o.negate(s.rotational);var r=this.computeGW()-this.targetVelocity,a=this.computeGiMf(),h=-r*e-t*a;return h}},{"../math/Mat3":71,"../math/Vec3":74,"./Equation":64}],68:[function(t,e,i){function o(t,e,i){i=n.defaults(i,{friction:.3,restitution:.3,contactEquationStiffness:1e7,contactEquationRelaxation:3,frictionEquationStiffness:1e7,frictionEquationRelaxation:3}),this.id=o.idCounter++,this.materials=[t,e],this.friction=i.friction,this.restitution=i.restitution,this.contactEquationStiffness=i.contactEquationStiffness,this.contactEquationRelaxation=i.contactEquationRelaxation,this.frictionEquationStiffness=i.frictionEquationStiffness,this.frictionEquationRelaxation=i.frictionEquationRelaxation}var n=t("../utils/Utils");e.exports=o,o.idCounter=0},{"../utils/Utils":97}],69:[function(t,e,i){function o(t){var e="";t=t||{},"string"==typeof t?(e=t,t={}):"object"==typeof t&&(e=""),this.name=e,this.id=o.idCounter++,this.friction="undefined"!=typeof t.friction?t.friction:-1,this.restitution="undefined"!=typeof t.restitution?t.restitution:-1}e.exports=o,o.idCounter=0},{}],70:[function(t,e,i){function o(){this.spatial=new n,this.rotational=new n}e.exports=o;var n=t("./Vec3");o.prototype.multiplyElement=function(t){return t.spatial.dot(this.spatial)+t.rotational.dot(this.rotational)},o.prototype.multiplyVectors=function(t,e){return t.dot(this.spatial)+e.dot(this.rotational)}},{"./Vec3":74}],71:[function(t,e,i){function o(t){t?this.elements=t:this.elements=[0,0,0,0,0,0,0,0,0]}e.exports=o;var n=t("./Vec3");o.prototype.identity=function(){var t=this.elements;t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1},o.prototype.setZero=function(){var t=this.elements;t[0]=0,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t[8]=0},o.prototype.setTrace=function(t){var e=this.elements;e[0]=t.x,e[4]=t.y,e[8]=t.z},o.prototype.getTrace=function(t){var t=t||new n,e=this.elements;t.x=e[0],t.y=e[4],t.z=e[8]},o.prototype.vmult=function(t,e){e=e||new n;var i=this.elements,o=t.x,s=t.y,r=t.z;return e.x=i[0]*o+i[1]*s+i[2]*r,e.y=i[3]*o+i[4]*s+i[5]*r,e.z=i[6]*o+i[7]*s+i[8]*r,e},o.prototype.smult=function(t){for(var e=0;e<this.elements.length;e++)this.elements[e]*=t},o.prototype.mmult=function(t,e){for(var i=e||new o,n=0;n<3;n++)for(var s=0;s<3;s++){for(var r=0,a=0;a<3;a++)r+=t.elements[n+3*a]*this.elements[a+3*s];i.elements[n+3*s]=r}return i},o.prototype.scale=function(t,e){e=e||new o;for(var i=this.elements,n=e.elements,s=0;3!==s;s++)n[3*s+0]=t.x*i[3*s+0],n[3*s+1]=t.y*i[3*s+1],n[3*s+2]=t.z*i[3*s+2];return e},o.prototype.solve=function(t,e){e=e||new n;for(var i=3,o=4,s=[],r=0;r<i*o;r++)s.push(0);var r,a;for(r=0;r<3;r++)for(a=0;a<3;a++)s[r+o*a]=this.elements[r+3*a];s[3]=t.x,s[7]=t.y,s[11]=t.z;var h,c,l=3,u=l,p=4;do{if(r=u-l,0===s[r+o*r])for(a=r+1;a<u;a++)if(0!==s[r+o*a]){h=p;do c=p-h,s[c+o*r]+=s[c+o*a];while(--h);break}if(0!==s[r+o*r])for(a=r+1;a<u;a++){var d=s[r+o*a]/s[r+o*r];h=p;do c=p-h,s[c+o*a]=c<=r?0:s[c+o*a]-s[c+o*r]*d;while(--h)}}while(--l);if(e.z=s[2*o+3]/s[2*o+2],e.y=(s[1*o+3]-s[1*o+2]*e.z)/s[1*o+1],e.x=(s[0*o+3]-s[0*o+2]*e.z-s[0*o+1]*e.y)/s[0*o+0],isNaN(e.x)||isNaN(e.y)||isNaN(e.z)||e.x===1/0||e.y===1/0||e.z===1/0)throw"Could not solve equation! Got x=["+e.toString()+"], b=["+t.toString()+"], A=["+this.toString()+"]";return e},o.prototype.e=function(t,e,i){return void 0===i?this.elements[e+3*t]:void(this.elements[e+3*t]=i)},o.prototype.copy=function(t){for(var e=0;e<t.elements.length;e++)this.elements[e]=t.elements[e];return this},o.prototype.toString=function(){for(var t="",e=",",i=0;i<9;i++)t+=this.elements[i]+e;return t},o.prototype.reverse=function(t){t=t||new o;for(var e=3,i=6,n=[],s=0;s<e*i;s++)n.push(0);var s,r;for(s=0;s<3;s++)for(r=0;r<3;r++)n[s+i*r]=this.elements[s+3*r];n[3]=1,n[9]=0,n[15]=0,n[4]=0,n[10]=1,n[16]=0,n[5]=0,n[11]=0,n[17]=1;var a,h,c=3,l=c,u=i;do{if(s=l-c,0===n[s+i*s])for(r=s+1;r<l;r++)if(0!==n[s+i*r]){a=u;do h=u-a,n[h+i*s]+=n[h+i*r];while(--a);break}if(0!==n[s+i*s])for(r=s+1;r<l;r++){var p=n[s+i*r]/n[s+i*s];a=u;do h=u-a,n[h+i*r]=h<=s?0:n[h+i*r]-n[h+i*s]*p;while(--a)}}while(--c);s=2;do{r=s-1;do{var p=n[s+i*r]/n[s+i*s];a=i;do h=i-a,n[h+i*r]=n[h+i*r]-n[h+i*s]*p;while(--a)}while(r--)}while(--s);s=2;do{var p=1/n[s+i*s];a=i;do h=i-a,n[h+i*s]=n[h+i*s]*p;while(--a)}while(s--);s=2;do{r=2;do{if(h=n[e+r+i*s],isNaN(h)||h===1/0)throw"Could not reverse! A=["+this.toString()+"]";t.e(s,r,h)}while(r--)}while(s--);return t},o.prototype.setRotationFromQuaternion=function(t){var e=t.x,i=t.y,o=t.z,n=t.w,s=e+e,r=i+i,a=o+o,h=e*s,c=e*r,l=e*a,u=i*r,p=i*a,d=o*a,y=n*s,v=n*r,f=n*a,m=this.elements;return m[0]=1-(u+d),m[1]=c-f,m[2]=l+v,m[3]=c+f,m[4]=1-(h+d),m[5]=p-y,m[6]=l-v,m[7]=p+y,m[8]=1-(h+u),this},o.prototype.transpose=function(t){t=t||new o;for(var e=t.elements,i=this.elements,n=0;3!==n;n++)for(var s=0;3!==s;s++)e[3*n+s]=i[3*s+n];return t}},{"./Vec3":74}],72:[function(t,e,i){function o(t,e,i,o){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0,this.z=void 0!==i?i:0,this.w=void 0!==o?o:1}e.exports=o;var n=t("./Vec3");o.prototype.set=function(t,e,i,o){return this.x=t,this.y=e,this.z=i,this.w=o,this},o.prototype.toString=function(){return this.x+","+this.y+","+this.z+","+this.w},o.prototype.toArray=function(){return[this.x,this.y,this.z,this.w]},o.prototype.setFromAxisAngle=function(t,e){var i=Math.sin(.5*e);return this.x=t.x*i,this.y=t.y*i,this.z=t.z*i,this.w=Math.cos(.5*e),this},o.prototype.toAxisAngle=function(t){t=t||new n,this.normalize();var e=2*Math.acos(this.w),i=Math.sqrt(1-this.w*this.w);return i<.001?(t.x=this.x,t.y=this.y,t.z=this.z):(t.x=this.x/i,t.y=this.y/i,t.z=this.z/i),[t,e]};var s=new n,r=new n;o.prototype.setFromVectors=function(t,e){if(t.isAntiparallelTo(e)){var i=s,o=r;t.tangents(i,o),this.setFromAxisAngle(i,Math.PI)}else{var n=t.cross(e);this.x=n.x,this.y=n.y,this.z=n.z,this.w=Math.sqrt(Math.pow(t.norm(),2)*Math.pow(e.norm(),2))+t.dot(e),this.normalize()}return this};new n,new n,new n;o.prototype.mult=function(t,e){e=e||new o;var i=this.x,n=this.y,s=this.z,r=this.w,a=t.x,h=t.y,c=t.z,l=t.w;return e.x=i*l+r*a+n*c-s*h,e.y=n*l+r*h+s*a-i*c,e.z=s*l+r*c+i*h-n*a,e.w=r*l-i*a-n*h-s*c,e},o.prototype.inverse=function(t){var e=this.x,i=this.y,n=this.z,s=this.w;t=t||new o,this.conjugate(t);var r=1/(e*e+i*i+n*n+s*s);return t.x*=r,t.y*=r,t.z*=r,t.w*=r,t},o.prototype.conjugate=function(t){return t=t||new o,t.x=-this.x,t.y=-this.y,t.z=-this.z,t.w=this.w,t},o.prototype.normalize=function(){var t=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);return 0===t?(this.x=0,this.y=0,this.z=0,this.w=0):(t=1/t,this.x*=t,this.y*=t,this.z*=t,this.w*=t),this},o.prototype.normalizeFast=function(){var t=(3-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2;return 0===t?(this.x=0,this.y=0,this.z=0,this.w=0):(this.x*=t,this.y*=t,this.z*=t,this.w*=t),this},o.prototype.vmult=function(t,e){e=e||new n;var i=t.x,o=t.y,s=t.z,r=this.x,a=this.y,h=this.z,c=this.w,l=c*i+a*s-h*o,u=c*o+h*i-r*s,p=c*s+r*o-a*i,d=-r*i-a*o-h*s;return e.x=l*c+d*-r+u*-h-p*-a,e.y=u*c+d*-a+p*-r-l*-h,e.z=p*c+d*-h+l*-a-u*-r,e},o.prototype.copy=function(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=t.w,this},o.prototype.toEuler=function(t,e){e=e||"YZX";var i,o,n,s=this.x,r=this.y,a=this.z,h=this.w;switch(e){case"YZX":var c=s*r+a*h;if(c>.499&&(i=2*Math.atan2(s,h),o=Math.PI/2,n=0),c<-.499&&(i=-2*Math.atan2(s,h),o=-Math.PI/2,n=0),isNaN(i)){var l=s*s,u=r*r,p=a*a;i=Math.atan2(2*r*h-2*s*a,1-2*u-2*p),o=Math.asin(2*c),n=Math.atan2(2*s*h-2*r*a,1-2*l-2*p)}break;default:throw new Error("Euler order "+e+" not supported yet.")}t.y=i,t.z=o,t.x=n},o.prototype.setFromEuler=function(t,e,i,o){o=o||"XYZ";var n=Math.cos(t/2),s=Math.cos(e/2),r=Math.cos(i/2),a=Math.sin(t/2),h=Math.sin(e/2),c=Math.sin(i/2);return"XYZ"===o?(this.x=a*s*r+n*h*c,this.y=n*h*r-a*s*c,this.z=n*s*c+a*h*r,this.w=n*s*r-a*h*c):"YXZ"===o?(this.x=a*s*r+n*h*c,this.y=n*h*r-a*s*c,this.z=n*s*c-a*h*r,this.w=n*s*r+a*h*c):"ZXY"===o?(this.x=a*s*r-n*h*c,this.y=n*h*r+a*s*c,this.z=n*s*c+a*h*r,this.w=n*s*r-a*h*c):"ZYX"===o?(this.x=a*s*r-n*h*c,this.y=n*h*r+a*s*c,this.z=n*s*c-a*h*r,this.w=n*s*r+a*h*c):"YZX"===o?(this.x=a*s*r+n*h*c,this.y=n*h*r+a*s*c,this.z=n*s*c-a*h*r,this.w=n*s*r-a*h*c):"XZY"===o&&(this.x=a*s*r-n*h*c,this.y=n*h*r-a*s*c,this.z=n*s*c+a*h*r,this.w=n*s*r+a*h*c),this},o.prototype.clone=function(){return new o(this.x,this.y,this.z,this.w)},o.prototype.slerp=function(t,e,i){i=i||new o;var n,s,r,a,h,c=this.x,l=this.y,u=this.z,p=this.w,d=t.x,y=t.y,v=t.z,f=t.w;return s=c*d+l*y+u*v+p*f,s<0&&(s=-s,d=-d,y=-y,v=-v,f=-f),1-s>1e-6?(n=Math.acos(s),r=Math.sin(n),a=Math.sin((1-e)*n)/r,h=Math.sin(e*n)/r):(a=1-e,h=e),i.x=a*c+h*d,i.y=a*l+h*y,i.z=a*u+h*v,i.w=a*p+h*f,i},o.prototype.integrate=function(t,e,i,n){n=n||new o;var s=t.x*i.x,r=t.y*i.y,a=t.z*i.z,h=this.x,c=this.y,l=this.z,u=this.w,p=.5*e;return n.x+=p*(s*u+r*l-a*c),n.y+=p*(r*u+a*h-s*l),n.z+=p*(a*u+s*c-r*h),n.w+=p*(-s*h-r*c-a*l),n}},{"./Vec3":74}],73:[function(t,e,i){function o(t){t=t||{},this.position=new n,t.position&&this.position.copy(t.position),this.quaternion=new s,t.quaternion&&this.quaternion.copy(t.quaternion)}var n=t("./Vec3"),s=t("./Quaternion");e.exports=o;var r=new s;o.pointToLocalFrame=function(t,e,i,o){var o=o||new n;return i.vsub(t,o),e.conjugate(r),r.vmult(o,o),o},o.prototype.pointToLocal=function(t,e){return o.pointToLocalFrame(this.position,this.quaternion,t,e)},o.pointToWorldFrame=function(t,e,i,o){var o=o||new n;return e.vmult(i,o),o.vadd(t,o),o},o.prototype.pointToWorld=function(t,e){return o.pointToWorldFrame(this.position,this.quaternion,t,e)},o.prototype.vectorToWorldFrame=function(t,e){var e=e||new n;return this.quaternion.vmult(t,e),e},o.vectorToWorldFrame=function(t,e,i){return t.vmult(e,i),i},o.vectorToLocalFrame=function(t,e,i,o){var o=o||new n;return e.w*=-1,e.vmult(i,o),e.w*=-1,o}},{"./Quaternion":72,"./Vec3":74}],74:[function(t,e,i){function o(t,e,i){this.x=t||0,this.y=e||0,this.z=i||0}e.exports=o;var n=t("./Mat3");o.ZERO=new o(0,0,0),o.UNIT_X=new o(1,0,0),o.UNIT_Y=new o(0,1,0),o.UNIT_Z=new o(0,0,1),o.prototype.cross=function(t,e){var i=t.x,n=t.y,s=t.z,r=this.x,a=this.y,h=this.z;return e=e||new o,e.x=a*s-h*n,e.y=h*i-r*s,e.z=r*n-a*i,e},o.prototype.set=function(t,e,i){return this.x=t,this.y=e,this.z=i,this},o.prototype.setZero=function(){this.x=this.y=this.z=0},o.prototype.vadd=function(t,e){return e?(e.x=t.x+this.x,e.y=t.y+this.y,e.z=t.z+this.z,void 0):new o(this.x+t.x,this.y+t.y,this.z+t.z)},o.prototype.vsub=function(t,e){return e?(e.x=this.x-t.x,e.y=this.y-t.y,e.z=this.z-t.z,void 0):new o(this.x-t.x,this.y-t.y,this.z-t.z)},o.prototype.crossmat=function(){return new n([0,-this.z,this.y,this.z,0,-this.x,-this.y,this.x,0])},o.prototype.normalize=function(){var t=this.x,e=this.y,i=this.z,o=Math.sqrt(t*t+e*e+i*i);if(o>0){var n=1/o;this.x*=n,this.y*=n,this.z*=n}else this.x=0,this.y=0,this.z=0;return o},o.prototype.unit=function(t){t=t||new o;var e=this.x,i=this.y,n=this.z,s=Math.sqrt(e*e+i*i+n*n);return s>0?(s=1/s,t.x=e*s,t.y=i*s,t.z=n*s):(t.x=1,t.y=0,t.z=0),t},o.prototype.norm=function(){var t=this.x,e=this.y,i=this.z;return Math.sqrt(t*t+e*e+i*i)},o.prototype.length=o.prototype.norm,o.prototype.norm2=function(){return this.dot(this)},o.prototype.lengthSquared=o.prototype.norm2,o.prototype.distanceTo=function(t){var e=this.x,i=this.y,o=this.z,n=t.x,s=t.y,r=t.z;return Math.sqrt((n-e)*(n-e)+(s-i)*(s-i)+(r-o)*(r-o))},o.prototype.distanceSquared=function(t){var e=this.x,i=this.y,o=this.z,n=t.x,s=t.y,r=t.z;return(n-e)*(n-e)+(s-i)*(s-i)+(r-o)*(r-o)},o.prototype.mult=function(t,e){e=e||new o;var i=this.x,n=this.y,s=this.z;return e.x=t*i,e.y=t*n,e.z=t*s,e},o.prototype.vmul=function(t,e){return e=e||new o,e.x=t.x*this.x,e.y=t.y*this.y,e.z=t.z*this.z,e},o.prototype.scale=o.prototype.mult,o.prototype.addScaledVector=function(t,e,i){return i=i||new o,i.x=this.x+t*e.x,i.y=this.y+t*e.y,i.z=this.z+t*e.z,i},o.prototype.dot=function(t){return this.x*t.x+this.y*t.y+this.z*t.z},o.prototype.isZero=function(){return 0===this.x&&0===this.y&&0===this.z},o.prototype.negate=function(t){return t=t||new o,t.x=-this.x,t.y=-this.y,t.z=-this.z,t};var s=new o,r=new o;o.prototype.tangents=function(t,e){var i=this.norm();if(i>0){var o=s,n=1/i;o.set(this.x*n,this.y*n,this.z*n);var a=r;Math.abs(o.x)<.9?(a.set(1,0,0),o.cross(a,t)):(a.set(0,1,0),o.cross(a,t)),o.cross(t,e)}else t.set(1,0,0),e.set(0,1,0)},o.prototype.toString=function(){return this.x+","+this.y+","+this.z},o.prototype.toArray=function(){return[this.x,this.y,this.z]},o.prototype.copy=function(t){return this.x=t.x,this.y=t.y,this.z=t.z,this},o.prototype.lerp=function(t,e,i){var o=this.x,n=this.y,s=this.z;i.x=o+(t.x-o)*e,i.y=n+(t.y-n)*e,i.z=s+(t.z-s)*e},o.prototype.almostEquals=function(t,e){return void 0===e&&(e=1e-6),!(Math.abs(this.x-t.x)>e||Math.abs(this.y-t.y)>e||Math.abs(this.z-t.z)>e)},o.prototype.almostZero=function(t){return void 0===t&&(t=1e-6),!(Math.abs(this.x)>t||Math.abs(this.y)>t||Math.abs(this.z)>t)};var a=new o;o.prototype.isAntiparallelTo=function(t,e){return this.negate(a),a.almostEquals(t,e)},o.prototype.clone=function(){return new o(this.x,this.y,this.z)}},{"./Mat3":71}],75:[function(t,e,i){function o(t){t=t||{},n.apply(this),this.id=o.idCounter++,this.world=null,this.preStep=null,this.postStep=null,this.vlambda=new s,this.collisionFilterGroup="number"==typeof t.collisionFilterGroup?t.collisionFilterGroup:1,this.collisionFilterMask="number"==typeof t.collisionFilterMask?t.collisionFilterMask:-1,this.collisionResponse=!0,this.position=new s,this.previousPosition=new s,this.interpolatedPosition=new s,this.initPosition=new s,t.position&&(this.position.copy(t.position),this.previousPosition.copy(t.position),this.interpolatedPosition.copy(t.position),this.initPosition.copy(t.position)),this.velocity=new s,t.velocity&&this.velocity.copy(t.velocity),this.initVelocity=new s,this.force=new s;var e="number"==typeof t.mass?t.mass:0;this.mass=e,this.invMass=e>0?1/e:0,this.material=t.material||null,this.linearDamping="number"==typeof t.linearDamping?t.linearDamping:.01,this.type=e<=0?o.STATIC:o.DYNAMIC,typeof t.type==typeof o.STATIC&&(this.type=t.type),this.allowSleep="undefined"==typeof t.allowSleep||t.allowSleep,this.sleepState=0,this.sleepSpeedLimit="undefined"!=typeof t.sleepSpeedLimit?t.sleepSpeedLimit:.1,this.sleepTimeLimit="undefined"!=typeof t.sleepTimeLimit?t.sleepTimeLimit:1,this.timeLastSleepy=0,this._wakeUpAfterNarrowphase=!1,this.torque=new s,this.quaternion=new a,this.initQuaternion=new a,this.previousQuaternion=new a,this.interpolatedQuaternion=new a,t.quaternion&&(this.quaternion.copy(t.quaternion),this.initQuaternion.copy(t.quaternion),this.previousQuaternion.copy(t.quaternion),this.interpolatedQuaternion.copy(t.quaternion)),this.angularVelocity=new s,t.angularVelocity&&this.angularVelocity.copy(t.angularVelocity),this.initAngularVelocity=new s,this.shapes=[],this.shapeOffsets=[],this.shapeOrientations=[],this.inertia=new s,this.invInertia=new s,this.invInertiaWorld=new r,this.invMassSolve=0,this.invInertiaSolve=new s,this.invInertiaWorldSolve=new r,this.fixedRotation="undefined"!=typeof t.fixedRotation&&t.fixedRotation,this.angularDamping="undefined"!=typeof t.angularDamping?t.angularDamping:.01,this.linearFactor=new s(1,1,1),t.linearFactor&&this.linearFactor.copy(t.linearFactor),this.angularFactor=new s(1,1,1),t.angularFactor&&this.angularFactor.copy(t.angularFactor),this.aabb=new h,this.aabbNeedsUpdate=!0,this.boundingRadius=0,this.wlambda=new s,t.shape&&this.addShape(t.shape),this.updateMassProperties()}e.exports=o;var n=t("../utils/EventTarget"),s=(t("../shapes/Shape"),t("../math/Vec3")),r=t("../math/Mat3"),a=t("../math/Quaternion"),h=(t("../material/Material"),t("../collision/AABB")),c=t("../shapes/Box");o.prototype=new n,o.prototype.constructor=o,o.COLLIDE_EVENT_NAME="collide",o.DYNAMIC=1,o.STATIC=2,o.KINEMATIC=4,o.AWAKE=0,o.SLEEPY=1,o.SLEEPING=2,o.idCounter=0,o.wakeupEvent={type:"wakeup"},o.prototype.wakeUp=function(){var t=this.sleepState;this.sleepState=0,this._wakeUpAfterNarrowphase=!1,t===o.SLEEPING&&this.dispatchEvent(o.wakeupEvent)},o.prototype.sleep=function(){this.sleepState=o.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0),this._wakeUpAfterNarrowphase=!1},o.sleepyEvent={type:"sleepy"},o.sleepEvent={type:"sleep"},o.prototype.sleepTick=function(t){if(this.allowSleep){var e=this.sleepState,i=this.velocity.norm2()+this.angularVelocity.norm2(),n=Math.pow(this.sleepSpeedLimit,2);e===o.AWAKE&&i<n?(this.sleepState=o.SLEEPY,this.timeLastSleepy=t,this.dispatchEvent(o.sleepyEvent)):e===o.SLEEPY&&i>n?this.wakeUp():e===o.SLEEPY&&t-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(o.sleepEvent))}},o.prototype.updateSolveMassProperties=function(){this.sleepState===o.SLEEPING||this.type===o.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))},o.prototype.pointToLocalFrame=function(t,e){var e=e||new s;return t.vsub(this.position,e),this.quaternion.conjugate().vmult(e,e),e},o.prototype.vectorToLocalFrame=function(t,e){var e=e||new s;return this.quaternion.conjugate().vmult(t,e),e},o.prototype.pointToWorldFrame=function(t,e){var e=e||new s;return this.quaternion.vmult(t,e),e.vadd(this.position,e),e},o.prototype.vectorToWorldFrame=function(t,e){var e=e||new s;return this.quaternion.vmult(t,e),e};var l=new s,u=new a;o.prototype.addShape=function(t,e,i){var o=new s,n=new a;return e&&o.copy(e),i&&n.copy(i),this.shapes.push(t),this.shapeOffsets.push(o),this.shapeOrientations.push(n),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,t.body=this,this},o.prototype.updateBoundingRadius=function(){for(var t=this.shapes,e=this.shapeOffsets,i=t.length,o=0,n=0;n!==i;n++){var s=t[n];s.updateBoundingSphereRadius();var r=e[n].norm(),a=s.boundingSphereRadius;r+a>o&&(o=r+a)}this.boundingRadius=o};var p=new h;o.prototype.computeAABB=function(){for(var t=this.shapes,e=this.shapeOffsets,i=this.shapeOrientations,o=t.length,n=l,s=u,r=this.quaternion,a=this.aabb,h=p,c=0;c!==o;c++){var d=t[c];r.vmult(e[c],n),n.vadd(this.position,n),i[c].mult(r,s),d.calculateWorldAABB(n,s,h.lowerBound,h.upperBound),0===c?a.copy(h):a.extend(h)}this.aabbNeedsUpdate=!1};var d=new r,y=new r;new r;o.prototype.updateInertiaWorld=function(t){var e=this.invInertia;if(e.x!==e.y||e.y!==e.z||t){var i=d,o=y;i.setRotationFromQuaternion(this.quaternion),i.transpose(o),i.scale(e,i),i.mmult(o,this.invInertiaWorld)}else;};var v=(new s,new s);o.prototype.applyForce=function(t,e){if(this.type===o.DYNAMIC){var i=v;e.cross(t,i),this.force.vadd(t,this.force),this.torque.vadd(i,this.torque)}};var f=new s,m=new s;o.prototype.applyLocalForce=function(t,e){if(this.type===o.DYNAMIC){var i=f,n=m;this.vectorToWorldFrame(t,i),this.vectorToWorldFrame(e,n),this.applyForce(i,n)}};var g=(new s,new s),w=new s;o.prototype.applyImpulse=function(t,e){if(this.type===o.DYNAMIC){var i=e,n=g;n.copy(t),n.mult(this.invMass,n),this.velocity.vadd(n,this.velocity);var s=w;i.cross(t,s),this.invInertiaWorld.vmult(s,s),this.angularVelocity.vadd(s,this.angularVelocity)}};var b=new s,x=new s;o.prototype.applyLocalImpulse=function(t,e){if(this.type===o.DYNAMIC){var i=b,n=x;this.vectorToWorldFrame(t,i),this.vectorToWorldFrame(e,n),this.applyImpulse(i,n)}};var E=new s;o.prototype.updateMassProperties=function(){var t=E;this.invMass=this.mass>0?1/this.mass:0;var e=this.inertia,i=this.fixedRotation;this.computeAABB(),t.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),c.calculateInertia(t,this.mass,e),this.invInertia.set(e.x>0&&!i?1/e.x:0,e.y>0&&!i?1/e.y:0,e.z>0&&!i?1/e.z:0),this.updateInertiaWorld(!0)},o.prototype.getVelocityAtWorldPoint=function(t,e){var i=new s;return t.vsub(this.position,i),this.angularVelocity.cross(i,e),this.velocity.vadd(e,e),e};new s,new s,new a,new a;o.prototype.integrate=function(t,e,i){if(this.previousPosition.copy(this.position),this.previousQuaternion.copy(this.quaternion),(this.type===o.DYNAMIC||this.type===o.KINEMATIC)&&this.sleepState!==o.SLEEPING){var n=this.velocity,s=this.angularVelocity,r=this.position,a=this.force,h=this.torque,c=this.quaternion,l=this.invMass,u=this.invInertiaWorld,p=this.linearFactor,d=l*t;n.x+=a.x*d*p.x,n.y+=a.y*d*p.y,n.z+=a.z*d*p.z;var y=u.elements,v=this.angularFactor,f=h.x*v.x,m=h.y*v.y,g=h.z*v.z;s.x+=t*(y[0]*f+y[1]*m+y[2]*g),s.y+=t*(y[3]*f+y[4]*m+y[5]*g),s.z+=t*(y[6]*f+y[7]*m+y[8]*g),r.x+=n.x*t,r.y+=n.y*t,r.z+=n.z*t,c.integrate(this.angularVelocity,t,this.angularFactor,c),e&&(i?c.normalizeFast():c.normalize()),this.aabbNeedsUpdate=!0,this.updateInertiaWorld()}}},{"../collision/AABB":46,"../material/Material":69,"../math/Mat3":71,"../math/Quaternion":72,"../math/Vec3":74,"../shapes/Box":81,"../shapes/Shape":87,"../utils/EventTarget":93}],76:[function(t,e,i){function o(t){this.chassisBody=t.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis="undefined"!=typeof t.indexRightAxis?t.indexRightAxis:1,this.indexForwardAxis="undefined"!=typeof t.indexForwardAxis?t.indexForwardAxis:0,this.indexUpAxis="undefined"!=typeof t.indexUpAxis?t.indexUpAxis:2}function n(t,e,i,o,n){var r=0,a=i,h=x,c=E,l=A;t.getVelocityAtWorldPoint(a,h),e.getVelocityAtWorldPoint(a,c),h.vsub(c,l);var u=o.dot(l),p=s(t,i,o),d=s(e,i,o),y=1,v=y/(p+d);return r=-u*v,n<r&&(r=n),r<-n&&(r=-n),r}function s(t,e,i){var o=C,n=B,s=S,r=R;return e.vsub(t.position,o),o.cross(i,n),t.invInertiaWorld.vmult(n,r),r.cross(o,s),t.invMass+i.dot(s)}function r(t,e,i,o,n,s){var r=n.norm2();if(r>1.1)return 0;var a=M,h=T,c=z;t.getVelocityAtWorldPoint(e,a),i.getVelocityAtWorldPoint(o,h),a.vsub(h,c);var l=n.dot(c),u=.2,p=1/(t.invMass+i.invMass),s=-u*l*p;return s}var a=(t("./Body"),t("../math/Vec3")),h=t("../math/Quaternion"),c=(t("../collision/RaycastResult"),t("../collision/Ray")),l=t("../objects/WheelInfo");e.exports=o;var u=(new a,new a,new a,new a),p=new a,d=new a;new c;o.prototype.addWheel=function(t){t=t||{};var e=new l(t),i=this.wheelInfos.length;return this.wheelInfos.push(e),i},o.prototype.setSteeringValue=function(t,e){var i=this.wheelInfos[e];i.steering=t};new a;o.prototype.applyEngineForce=function(t,e){this.wheelInfos[e].engineForce=t},o.prototype.setBrake=function(t,e){this.wheelInfos[e].brake=t},o.prototype.addToWorld=function(t){this.constraints;t.addBody(this.chassisBody);var e=this;this.preStepCallback=function(){e.updateVehicle(t.dt)},t.addEventListener("preStep",this.preStepCallback),this.world=t},o.prototype.getVehicleAxisWorld=function(t,e){e.set(0===t?1:0,1===t?1:0,2===t?1:0),this.chassisBody.vectorToWorldFrame(e,e)},o.prototype.updateVehicle=function(t){for(var e=this.wheelInfos,i=e.length,o=this.chassisBody,n=0;n<i;n++)this.updateWheelTransform(n);this.currentVehicleSpeedKmHour=3.6*o.velocity.norm();var s=new a;this.getVehicleAxisWorld(this.indexForwardAxis,s),s.dot(o.velocity)<0&&(this.currentVehicleSpeedKmHour*=-1);for(var n=0;n<i;n++)this.castRay(e[n]);this.updateSuspension(t);for(var r=new a,h=new a,n=0;n<i;n++){var c=e[n],l=c.suspensionForce;l>c.maxSuspensionForce&&(l=c.maxSuspensionForce),c.raycastResult.hitNormalWorld.scale(l*t,r),c.raycastResult.hitPointWorld.vsub(o.position,h),o.applyImpulse(r,h);
}this.updateFriction(t);var u=new a,p=new a,d=new a;for(n=0;n<i;n++){var c=e[n];o.getVelocityAtWorldPoint(c.chassisConnectionPointWorld,d);var y=1;switch(this.indexUpAxis){case 1:y=-1}if(c.isInContact){this.getVehicleAxisWorld(this.indexForwardAxis,p);var v=p.dot(c.raycastResult.hitNormalWorld);c.raycastResult.hitNormalWorld.scale(v,u),p.vsub(u,p);var f=p.dot(d);c.deltaRotation=y*f*t/c.radius}!c.sliding&&c.isInContact||0===c.engineForce||!c.useCustomSlidingRotationalSpeed||(c.deltaRotation=(c.engineForce>0?1:-1)*c.customSlidingRotationalSpeed*t),Math.abs(c.brake)>Math.abs(c.engineForce)&&(c.deltaRotation=0),c.rotation+=c.deltaRotation,c.deltaRotation*=.99}},o.prototype.updateSuspension=function(t){for(var e=this.chassisBody,i=e.mass,o=this.wheelInfos,n=o.length,s=0;s<n;s++){var r=o[s];if(r.isInContact){var a,h=r.suspensionRestLength,c=r.suspensionLength,l=h-c;a=r.suspensionStiffness*l*r.clippedInvContactDotSuspension;var u,p=r.suspensionRelativeVelocity;u=p<0?r.dampingCompression:r.dampingRelaxation,a-=u*p,r.suspensionForce=a*i,r.suspensionForce<0&&(r.suspensionForce=0)}else r.suspensionForce=0}},o.prototype.removeFromWorld=function(t){this.constraints;t.remove(this.chassisBody),t.removeEventListener("preStep",this.preStepCallback),this.world=null};var y=new a,v=new a;o.prototype.castRay=function(t){var e=y,i=v;this.updateWheelTransformWorld(t);var o=this.chassisBody,n=-1,s=t.suspensionRestLength+t.radius;t.directionWorld.scale(s,e);var r=t.chassisConnectionPointWorld;r.vadd(e,i);var h=t.raycastResult;h.reset();var c=o.collisionResponse;o.collisionResponse=!1,this.world.rayTest(r,i,h),o.collisionResponse=c;var l=h.body;if(t.raycastResult.groundObject=0,l){n=h.distance,t.raycastResult.hitNormalWorld=h.hitNormalWorld,t.isInContact=!0;var u=h.distance;t.suspensionLength=u-t.radius;var p=t.suspensionRestLength-t.maxSuspensionTravel,d=t.suspensionRestLength+t.maxSuspensionTravel;t.suspensionLength<p&&(t.suspensionLength=p),t.suspensionLength>d&&(t.suspensionLength=d,t.raycastResult.reset());var f=t.raycastResult.hitNormalWorld.dot(t.directionWorld),m=new a;o.getVelocityAtWorldPoint(t.raycastResult.hitPointWorld,m);var g=t.raycastResult.hitNormalWorld.dot(m);if(f>=-.1)t.suspensionRelativeVelocity=0,t.clippedInvContactDotSuspension=10;else{var w=-1/f;t.suspensionRelativeVelocity=g*w,t.clippedInvContactDotSuspension=w}}else t.suspensionLength=t.suspensionRestLength+0*t.maxSuspensionTravel,t.suspensionRelativeVelocity=0,t.directionWorld.scale(-1,t.raycastResult.hitNormalWorld),t.clippedInvContactDotSuspension=1;return n},o.prototype.updateWheelTransformWorld=function(t){t.isInContact=!1;var e=this.chassisBody;e.pointToWorldFrame(t.chassisConnectionPointLocal,t.chassisConnectionPointWorld),e.vectorToWorldFrame(t.directionLocal,t.directionWorld),e.vectorToWorldFrame(t.axleLocal,t.axleWorld)},o.prototype.updateWheelTransform=function(t){var e=u,i=p,o=d,n=this.wheelInfos[t];this.updateWheelTransformWorld(n),n.directionLocal.scale(-1,e),i.copy(n.axleLocal),e.cross(i,o),o.normalize(),i.normalize();var s=n.steering,r=new h;r.setFromAxisAngle(e,s);var a=new h;a.setFromAxisAngle(i,n.rotation);var c=n.worldTransform.quaternion;this.chassisBody.quaternion.mult(r,c),c.mult(a,c),c.normalize();var l=n.worldTransform.position;l.copy(n.directionWorld),l.scale(n.suspensionLength,l),l.vadd(n.chassisConnectionPointWorld,l)};var f=[new a(1,0,0),new a(0,1,0),new a(0,0,1)];o.prototype.getWheelTransformWorld=function(t){return this.wheelInfos[t].worldTransform};var m=new a,g=[],w=[],b=1;o.prototype.updateFriction=function(t){for(var e=m,i=this.wheelInfos,o=i.length,s=this.chassisBody,h=w,c=g,l=0,u=0;u<o;u++){var p=i[u],d=p.raycastResult.body;d&&l++,p.sideImpulse=0,p.forwardImpulse=0,h[u]||(h[u]=new a),c[u]||(c[u]=new a)}for(var u=0;u<o;u++){var p=i[u],d=p.raycastResult.body;if(d){var y=c[u],v=this.getWheelTransformWorld(u);v.vectorToWorldFrame(f[this.indexRightAxis],y);var x=p.raycastResult.hitNormalWorld,E=y.dot(x);x.scale(E,e),y.vsub(e,y),y.normalize(),x.cross(y,h[u]),h[u].normalize(),p.sideImpulse=r(s,p.raycastResult.hitPointWorld,d,p.raycastResult.hitPointWorld,y),p.sideImpulse*=b}}var A=1,C=.5;this.sliding=!1;for(var u=0;u<o;u++){var p=i[u],d=p.raycastResult.body,B=0;if(p.slipInfo=1,d){var S=0,R=p.brake?p.brake:S;B=n(s,d,p.raycastResult.hitPointWorld,h[u],R),B+=p.engineForce*t;var M=R/B;p.slipInfo*=M}if(p.forwardImpulse=0,p.skidInfo=1,d){p.skidInfo=1;var T=p.suspensionForce*t*p.frictionSlip,z=T,k=T*z;p.forwardImpulse=B;var L=p.forwardImpulse*C,P=p.sideImpulse*A,q=L*L+P*P;if(p.sliding=!1,q>k){this.sliding=!0,p.sliding=!0;var M=T/Math.sqrt(q);p.skidInfo*=M}}}if(this.sliding)for(var u=0;u<o;u++){var p=i[u];0!==p.sideImpulse&&p.skidInfo<1&&(p.forwardImpulse*=p.skidInfo,p.sideImpulse*=p.skidInfo)}for(var u=0;u<o;u++){var p=i[u],V=new a;if(p.raycastResult.hitPointWorld.vsub(s.position,V),0!==p.forwardImpulse){var F=new a;h[u].scale(p.forwardImpulse,F),s.applyImpulse(F,V)}if(0!==p.sideImpulse){var d=p.raycastResult.body,N=new a;p.raycastResult.hitPointWorld.vsub(d.position,N);var I=new a;c[u].scale(p.sideImpulse,I),s.vectorToLocalFrame(V,V),V["xyz"[this.indexUpAxis]]*=p.rollInfluence,s.vectorToWorldFrame(V,V),s.applyImpulse(I,V),I.scale(-1,I),d.applyImpulse(I,N)}}};var x=new a,E=new a,A=new a,C=new a,B=new a,S=new a,R=new a,M=new a,T=new a,z=new a},{"../collision/Ray":53,"../collision/RaycastResult":54,"../math/Quaternion":72,"../math/Vec3":74,"../objects/WheelInfo":80,"./Body":75}],77:[function(t,e,i){function o(t){if(this.wheelBodies=[],this.coordinateSystem="undefined"==typeof t.coordinateSystem?new a(1,2,3):t.coordinateSystem.clone(),this.chassisBody=t.chassisBody,!this.chassisBody){var e=new r(new a(5,2,.5));this.chassisBody=new n(1,e)}this.constraints=[],this.wheelAxes=[],this.wheelForces=[]}var n=t("./Body"),s=t("../shapes/Sphere"),r=t("../shapes/Box"),a=t("../math/Vec3"),h=t("../constraints/HingeConstraint");e.exports=o,o.prototype.addWheel=function(t){t=t||{};var e=t.body;e||(e=new n(1,new s(1.2))),this.wheelBodies.push(e),this.wheelForces.push(0);var i=(new a,"undefined"!=typeof t.position?t.position.clone():new a),o=new a;this.chassisBody.pointToWorldFrame(i,o),e.position.set(o.x,o.y,o.z);var r="undefined"!=typeof t.axis?t.axis.clone():new a(0,1,0);this.wheelAxes.push(r);var c=new h(this.chassisBody,e,{pivotA:i,axisA:r,pivotB:a.ZERO,axisB:r,collideConnected:!1});return this.constraints.push(c),this.wheelBodies.length-1},o.prototype.setSteeringValue=function(t,e){var i=this.wheelAxes[e],o=Math.cos(t),n=Math.sin(t),s=i.x,r=i.y;this.constraints[e].axisA.set(o*s-n*r,n*s+o*r,0)},o.prototype.setMotorSpeed=function(t,e){var i=this.constraints[e];i.enableMotor(),i.motorTargetVelocity=t},o.prototype.disableMotor=function(t){var e=this.constraints[t];e.disableMotor()};var c=new a;o.prototype.setWheelForce=function(t,e){this.wheelForces[e]=t},o.prototype.applyWheelForce=function(t,e){var i=this.wheelAxes[e],o=this.wheelBodies[e],n=o.torque;i.scale(t,c),o.vectorToWorldFrame(c,c),n.vadd(c,n)},o.prototype.addToWorld=function(t){for(var e=this.constraints,i=this.wheelBodies.concat([this.chassisBody]),o=0;o<i.length;o++)t.addBody(i[o]);for(var o=0;o<e.length;o++)t.addConstraint(e[o]);t.addEventListener("preStep",this._update.bind(this))},o.prototype._update=function(){for(var t=this.wheelForces,e=0;e<t.length;e++)this.applyWheelForce(t[e],e)},o.prototype.removeFromWorld=function(t){for(var e=this.constraints,i=this.wheelBodies.concat([this.chassisBody]),o=0;o<i.length;o++)t.remove(i[o]);for(var o=0;o<e.length;o++)t.removeConstraint(e[o])};var l=new a;o.prototype.getWheelSpeed=function(t){var e=this.wheelAxes[t],i=this.wheelBodies[t],o=i.angularVelocity;return this.chassisBody.vectorToWorldFrame(e,l),o.dot(l)}},{"../constraints/HingeConstraint":59,"../math/Vec3":74,"../shapes/Box":81,"../shapes/Sphere":88,"./Body":75}],78:[function(t,e,i){function o(){this.particles=[],this.density=1,this.smoothingRadius=1,this.speedOfSound=1,this.viscosity=.01,this.eps=1e-6,this.pressures=[],this.densities=[],this.neighbors=[]}e.exports=o;var n=(t("../shapes/Shape"),t("../math/Vec3"));t("../math/Quaternion"),t("../shapes/Particle"),t("../objects/Body"),t("../material/Material");o.prototype.add=function(t){this.particles.push(t),this.neighbors.length<this.particles.length&&this.neighbors.push([])},o.prototype.remove=function(t){var e=this.particles.indexOf(t);e!==-1&&(this.particles.splice(e,1),this.neighbors.length>this.particles.length&&this.neighbors.pop())};var s=new n;o.prototype.getNeighbors=function(t,e){for(var i=this.particles.length,o=t.id,n=this.smoothingRadius*this.smoothingRadius,r=s,a=0;a!==i;a++){var h=this.particles[a];h.position.vsub(t.position,r),o!==h.id&&r.norm2()<n&&e.push(h)}};var r=new n,a=new n,h=new n,c=new n,l=new n,u=new n;o.prototype.update=function(){for(var t=this.particles.length,e=r,i=this.speedOfSound,o=this.eps,n=0;n!==t;n++){var s=this.particles[n],p=this.neighbors[n];p.length=0,this.getNeighbors(s,p),p.push(this.particles[n]);for(var d=p.length,y=0,v=0;v!==d;v++){s.position.vsub(p[v].position,e);var f=e.norm(),m=this.w(f);y+=p[v].mass*m}this.densities[n]=y,this.pressures[n]=i*i*(this.densities[n]-this.density)}for(var g=a,w=h,b=c,x=l,E=u,n=0;n!==t;n++){var A=this.particles[n];g.set(0,0,0),w.set(0,0,0);for(var C,B,p=this.neighbors[n],d=p.length,v=0;v!==d;v++){var S=p[v];A.position.vsub(S.position,x);var R=x.norm();C=-S.mass*(this.pressures[n]/(this.densities[n]*this.densities[n]+o)+this.pressures[v]/(this.densities[v]*this.densities[v]+o)),this.gradw(x,b),b.mult(C,b),g.vadd(b,g),S.velocity.vsub(A.velocity,E),E.mult(1/(1e-4+this.densities[n]*this.densities[v])*this.viscosity*S.mass,E),B=this.nablaw(R),E.mult(B,E),w.vadd(E,w)}w.mult(A.mass,w),g.mult(A.mass,g),A.force.vadd(w,A.force),A.force.vadd(g,A.force)}},o.prototype.w=function(t){var e=this.smoothingRadius;return 315/(64*Math.PI*Math.pow(e,9))*Math.pow(e*e-t*t,3)},o.prototype.gradw=function(t,e){var i=t.norm(),o=this.smoothingRadius;t.mult(945/(32*Math.PI*Math.pow(o,9))*Math.pow(o*o-i*i,2),e)},o.prototype.nablaw=function(t){var e=this.smoothingRadius,i=945/(32*Math.PI*Math.pow(e,9))*(e*e-t*t)*(7*t*t-3*e*e);return i}},{"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Particle":85,"../shapes/Shape":87}],79:[function(t,e,i){function o(t,e,i){i=i||{},this.restLength="number"==typeof i.restLength?i.restLength:1,this.stiffness=i.stiffness||100,this.damping=i.damping||1,this.bodyA=t,this.bodyB=e,this.localAnchorA=new n,this.localAnchorB=new n,i.localAnchorA&&this.localAnchorA.copy(i.localAnchorA),i.localAnchorB&&this.localAnchorB.copy(i.localAnchorB),i.worldAnchorA&&this.setWorldAnchorA(i.worldAnchorA),i.worldAnchorB&&this.setWorldAnchorB(i.worldAnchorB)}var n=t("../math/Vec3");e.exports=o,o.prototype.setWorldAnchorA=function(t){this.bodyA.pointToLocalFrame(t,this.localAnchorA)},o.prototype.setWorldAnchorB=function(t){this.bodyB.pointToLocalFrame(t,this.localAnchorB)},o.prototype.getWorldAnchorA=function(t){this.bodyA.pointToWorldFrame(this.localAnchorA,t)},o.prototype.getWorldAnchorB=function(t){this.bodyB.pointToWorldFrame(this.localAnchorB,t)};var s=new n,r=new n,a=new n,h=new n,c=new n,l=new n,u=new n,p=new n,d=new n,y=new n,v=new n;o.prototype.applyForce=function(){var t=this.stiffness,e=this.damping,i=this.restLength,o=this.bodyA,n=this.bodyB,f=s,m=r,g=a,w=h,b=v,x=c,E=l,A=u,C=p,B=d,S=y;this.getWorldAnchorA(x),this.getWorldAnchorB(E),x.vsub(o.position,A),E.vsub(n.position,C),E.vsub(x,f);var R=f.norm();m.copy(f),m.normalize(),n.velocity.vsub(o.velocity,g),n.angularVelocity.cross(C,b),g.vadd(b,g),o.angularVelocity.cross(A,b),g.vsub(b,g),m.mult(-t*(R-i)-e*g.dot(m),w),o.force.vsub(w,o.force),n.force.vadd(w,n.force),A.cross(w,B),C.cross(w,S),o.torque.vsub(B,o.torque),n.torque.vadd(S,n.torque)}},{"../math/Vec3":74}],80:[function(t,e,i){function o(t){t=a.defaults(t,{chassisConnectionPointLocal:new n,chassisConnectionPointWorld:new n,directionLocal:new n,directionWorld:new n,axleLocal:new n,axleWorld:new n,suspensionRestLength:1,suspensionMaxLength:2,radius:1,suspensionStiffness:100,dampingCompression:10,dampingRelaxation:10,frictionSlip:1e4,steering:0,rotation:0,deltaRotation:0,rollInfluence:.01,maxSuspensionForce:Number.MAX_VALUE,isFrontWheel:!0,clippedInvContactDotSuspension:1,suspensionRelativeVelocity:0,suspensionForce:0,skidInfo:0,suspensionLength:0,maxSuspensionTravel:1,useCustomSlidingRotationalSpeed:!1,customSlidingRotationalSpeed:-.1}),this.maxSuspensionTravel=t.maxSuspensionTravel,this.customSlidingRotationalSpeed=t.customSlidingRotationalSpeed,this.useCustomSlidingRotationalSpeed=t.useCustomSlidingRotationalSpeed,this.sliding=!1,this.chassisConnectionPointLocal=t.chassisConnectionPointLocal.clone(),this.chassisConnectionPointWorld=t.chassisConnectionPointWorld.clone(),this.directionLocal=t.directionLocal.clone(),this.directionWorld=t.directionWorld.clone(),this.axleLocal=t.axleLocal.clone(),this.axleWorld=t.axleWorld.clone(),this.suspensionRestLength=t.suspensionRestLength,this.suspensionMaxLength=t.suspensionMaxLength,this.radius=t.radius,this.suspensionStiffness=t.suspensionStiffness,this.dampingCompression=t.dampingCompression,this.dampingRelaxation=t.dampingRelaxation,this.frictionSlip=t.frictionSlip,this.steering=0,this.rotation=0,this.deltaRotation=0,this.rollInfluence=t.rollInfluence,this.maxSuspensionForce=t.maxSuspensionForce,this.engineForce=0,this.brake=0,this.isFrontWheel=t.isFrontWheel,this.clippedInvContactDotSuspension=1,this.suspensionRelativeVelocity=0,this.suspensionForce=0,this.skidInfo=0,this.suspensionLength=0,this.sideImpulse=0,this.forwardImpulse=0,this.raycastResult=new r,this.worldTransform=new s,this.isInContact=!1}var n=t("../math/Vec3"),s=t("../math/Transform"),r=t("../collision/RaycastResult"),a=t("../utils/Utils");e.exports=o;var h=new n,c=new n,h=new n;o.prototype.updateWheel=function(t){var e=this.raycastResult;if(this.isInContact){var i=e.hitNormalWorld.dot(e.directionWorld);e.hitPointWorld.vsub(t.position,c),t.getVelocityAtWorldPoint(c,h);var o=e.hitNormalWorld.dot(h);if(i>=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{var n=-1/i;this.suspensionRelativeVelocity=o*n,this.clippedInvContactDotSuspension=n}}else e.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,e.directionWorld.scale(-1,e.hitNormalWorld),this.clippedInvContactDotSuspension=1}},{"../collision/RaycastResult":54,"../math/Transform":73,"../math/Vec3":74,"../utils/Utils":97}],81:[function(t,e,i){function o(t){n.call(this,{type:n.types.BOX}),this.halfExtents=t,this.convexPolyhedronRepresentation=null,this.updateConvexPolyhedronRepresentation(),this.updateBoundingSphereRadius()}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3"),r=t("./ConvexPolyhedron");o.prototype=new n,o.prototype.constructor=o,o.prototype.updateConvexPolyhedronRepresentation=function(){var t=this.halfExtents.x,e=this.halfExtents.y,i=this.halfExtents.z,o=s,n=[new o((-t),(-e),(-i)),new o(t,(-e),(-i)),new o(t,e,(-i)),new o((-t),e,(-i)),new o((-t),(-e),i),new o(t,(-e),i),new o(t,e,i),new o((-t),e,i)],a=[[3,2,1,0],[4,5,6,7],[5,4,0,1],[2,3,7,6],[0,4,7,3],[1,2,6,5]],h=([new o(0,0,1),new o(0,1,0),new o(1,0,0)],new r(n,a));this.convexPolyhedronRepresentation=h,h.material=this.material},o.prototype.calculateLocalInertia=function(t,e){return e=e||new s,o.calculateInertia(this.halfExtents,t,e),e},o.calculateInertia=function(t,e,i){var o=t;i.x=1/12*e*(2*o.y*2*o.y+2*o.z*2*o.z),i.y=1/12*e*(2*o.x*2*o.x+2*o.z*2*o.z),i.z=1/12*e*(2*o.y*2*o.y+2*o.x*2*o.x)},o.prototype.getSideNormals=function(t,e){var i=t,o=this.halfExtents;if(i[0].set(o.x,0,0),i[1].set(0,o.y,0),i[2].set(0,0,o.z),i[3].set(-o.x,0,0),i[4].set(0,-o.y,0),i[5].set(0,0,-o.z),void 0!==e)for(var n=0;n!==i.length;n++)e.vmult(i[n],i[n]);return i},o.prototype.volume=function(){return 8*this.halfExtents.x*this.halfExtents.y*this.halfExtents.z},o.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.halfExtents.norm()};var a=new s;new s;o.prototype.forEachWorldCorner=function(t,e,i){for(var o=this.halfExtents,n=[[o.x,o.y,o.z],[-o.x,o.y,o.z],[-o.x,-o.y,o.z],[-o.x,-o.y,-o.z],[o.x,-o.y,-o.z],[o.x,o.y,-o.z],[-o.x,o.y,-o.z],[o.x,-o.y,o.z]],s=0;s<n.length;s++)a.set(n[s][0],n[s][1],n[s][2]),e.vmult(a,a),t.vadd(a,a),i(a.x,a.y,a.z)};var h=[new s,new s,new s,new s,new s,new s,new s,new s];o.prototype.calculateWorldAABB=function(t,e,i,o){var n=this.halfExtents;h[0].set(n.x,n.y,n.z),h[1].set(-n.x,n.y,n.z),h[2].set(-n.x,-n.y,n.z),h[3].set(-n.x,-n.y,-n.z),h[4].set(n.x,-n.y,-n.z),h[5].set(n.x,n.y,-n.z),h[6].set(-n.x,n.y,-n.z),h[7].set(n.x,-n.y,n.z);var s=h[0];e.vmult(s,s),t.vadd(s,s),o.copy(s),i.copy(s);for(var r=1;r<8;r++){var s=h[r];e.vmult(s,s),t.vadd(s,s);var a=s.x,c=s.y,l=s.z;a>o.x&&(o.x=a),c>o.y&&(o.y=c),l>o.z&&(o.z=l),a<i.x&&(i.x=a),c<i.y&&(i.y=c),l<i.z&&(i.z=l)}}},{"../math/Vec3":74,"./ConvexPolyhedron":82,"./Shape":87}],82:[function(t,e,i){function o(t,e,i){n.call(this,{type:n.types.CONVEXPOLYHEDRON}),this.vertices=t||[],this.worldVertices=[],this.worldVerticesNeedsUpdate=!0,this.faces=e||[],this.faceNormals=[],this.computeNormals(),this.worldFaceNormalsNeedsUpdate=!0,this.worldFaceNormals=[],this.uniqueEdges=[],this.uniqueAxes=i?i.slice():null,this.computeEdges(),this.updateBoundingSphereRadius()}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3"),r=(t("../math/Quaternion"),t("../math/Transform"));o.prototype=new n,o.prototype.constructor=o;var a=new s;o.prototype.computeEdges=function(){var t=this.faces,e=this.vertices,i=(e.length,this.uniqueEdges);i.length=0;for(var o=a,n=0;n!==t.length;n++)for(var s=t[n],r=s.length,h=0;h!==r;h++){var c=(h+1)%r;e[s[h]].vsub(e[s[c]],o),o.normalize();for(var l=!1,u=0;u!==i.length;u++)if(i[u].almostEquals(o)||i[u].almostEquals(o)){l=!0;break}l||i.push(o.clone())}},o.prototype.computeNormals=function(){this.faceNormals.length=this.faces.length;for(var t=0;t<this.faces.length;t++){for(var e=0;e<this.faces[t].length;e++)if(!this.vertices[this.faces[t][e]])throw new Error("Vertex "+this.faces[t][e]+" not found!");var i=this.faceNormals[t]||new s;this.getFaceNormal(t,i),i.negate(i),this.faceNormals[t]=i;var o=this.vertices[this.faces[t][0]];if(i.dot(o)<0){console.error(".faceNormals["+t+"] = Vec3("+i.toString()+") looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.");for(var e=0;e<this.faces[t].length;e++)console.warn(".vertices["+this.faces[t][e]+"] = Vec3("+this.vertices[this.faces[t][e]].toString()+")")}}};var h=new s,c=new s;o.computeNormal=function(t,e,i,o){e.vsub(t,c),i.vsub(e,h),h.cross(c,o),o.isZero()||o.normalize()},o.prototype.getFaceNormal=function(t,e){var i=this.faces[t],n=this.vertices[i[0]],s=this.vertices[i[1]],r=this.vertices[i[2]];return o.computeNormal(n,s,r,e)};var l=new s;o.prototype.clipAgainstHull=function(t,e,i,o,n,r,a,h,c){for(var u=l,p=-1,d=-Number.MAX_VALUE,y=0;y<i.faces.length;y++){u.copy(i.faceNormals[y]),n.vmult(u,u);var v=u.dot(r);v>d&&(d=v,p=y)}for(var f=[],m=i.faces[p],g=m.length,w=0;w<g;w++){var b=i.vertices[m[w]],x=new s;x.copy(b),n.vmult(x,x),o.vadd(x,x),f.push(x)}p>=0&&this.clipFaceAgainstHull(r,t,e,f,a,h,c)};var u=new s,p=new s,d=new s,y=new s,v=new s,f=new s;o.prototype.findSeparatingAxis=function(t,e,i,o,n,s,r,a){var h=u,c=p,l=d,m=y,g=v,w=f,b=Number.MAX_VALUE,x=this,E=0;if(x.uniqueAxes)for(var A=0;A!==x.uniqueAxes.length;A++){i.vmult(x.uniqueAxes[A],h);var C=x.testSepAxis(h,t,e,i,o,n);if(C===!1)return!1;C<b&&(b=C,s.copy(h))}else for(var B=r?r.length:x.faces.length,A=0;A<B;A++){var S=r?r[A]:A;h.copy(x.faceNormals[S]),i.vmult(h,h);var C=x.testSepAxis(h,t,e,i,o,n);if(C===!1)return!1;C<b&&(b=C,s.copy(h))}if(t.uniqueAxes)for(var A=0;A!==t.uniqueAxes.length;A++){n.vmult(t.uniqueAxes[A],c),E++;var C=x.testSepAxis(c,t,e,i,o,n);if(C===!1)return!1;C<b&&(b=C,s.copy(c))}else for(var R=a?a.length:t.faces.length,A=0;A<R;A++){var S=a?a[A]:A;c.copy(t.faceNormals[S]),n.vmult(c,c),E++;var C=x.testSepAxis(c,t,e,i,o,n);if(C===!1)return!1;C<b&&(b=C,s.copy(c))}for(var M=0;M!==x.uniqueEdges.length;M++){i.vmult(x.uniqueEdges[M],m);for(var T=0;T!==t.uniqueEdges.length;T++)if(n.vmult(t.uniqueEdges[T],g),m.cross(g,w),!w.almostZero()){w.normalize();var z=x.testSepAxis(w,t,e,i,o,n);if(z===!1)return!1;z<b&&(b=z,s.copy(w))}}return o.vsub(e,l),l.dot(s)>0&&s.negate(s),!0};var m=[],g=[];o.prototype.testSepAxis=function(t,e,i,n,s,r){var a=this;o.project(a,t,i,n,m),o.project(e,t,s,r,g);var h=m[0],c=m[1],l=g[0],u=g[1];if(h<u||l<c)return!1;var p=h-u,d=l-c,y=p<d?p:d;return y};var w=new s,b=new s;o.prototype.calculateLocalInertia=function(t,e){this.computeLocalAABB(w,b);var i=b.x-w.x,o=b.y-w.y,n=b.z-w.z;e.x=1/12*t*(2*o*2*o+2*n*2*n),e.y=1/12*t*(2*i*2*i+2*n*2*n),e.z=1/12*t*(2*o*2*o+2*i*2*i)},o.prototype.getPlaneConstantOfFace=function(t){var e=this.faces[t],i=this.faceNormals[t],o=this.vertices[e[0]],n=-i.dot(o);return n};var x=new s,E=new s,A=new s,C=new s,B=new s,S=new s,R=new s,M=new s;o.prototype.clipFaceAgainstHull=function(t,e,i,o,n,s,r){for(var a=x,h=E,c=A,l=C,u=B,p=S,d=R,y=M,v=this,f=[],m=o,g=f,w=-1,b=Number.MAX_VALUE,T=0;T<v.faces.length;T++){a.copy(v.faceNormals[T]),i.vmult(a,a);var z=a.dot(t);z<b&&(b=z,w=T)}if(!(w<0)){var k=v.faces[w];k.connectedFaces=[];for(var L=0;L<v.faces.length;L++)for(var P=0;P<v.faces[L].length;P++)k.indexOf(v.faces[L][P])!==-1&&L!==w&&k.connectedFaces.indexOf(L)===-1&&k.connectedFaces.push(L);for(var q=(m.length,k.length),V=0;V<q;V++){var F=v.vertices[k[V]],N=v.vertices[k[(V+1)%q]];F.vsub(N,h),c.copy(h),i.vmult(c,c),e.vadd(c,c),l.copy(this.faceNormals[w]),i.vmult(l,l),e.vadd(l,l),c.cross(l,u),u.negate(u),p.copy(F),i.vmult(p,p),e.vadd(p,p);var I,O=(-p.dot(u),k.connectedFaces[V]);d.copy(this.faceNormals[O]);var H=this.getPlaneConstantOfFace(O);y.copy(d),i.vmult(y,y);var I=H-y.dot(e);for(this.clipFaceAgainstPlane(m,g,y,I);m.length;)m.shift();for(;g.length;)m.push(g.shift())}d.copy(this.faceNormals[w]);var H=this.getPlaneConstantOfFace(w);y.copy(d),i.vmult(y,y);for(var I=H-y.dot(e),L=0;L<m.length;L++){var j=y.dot(m[L])+I;if(j<=n&&(console.log("clamped: depth="+j+" to minDist="+(n+"")),j=n),j<=s){var D=m[L];if(j<=0){var W={point:D,normal:y,depth:j};r.push(W)}}}}},o.prototype.clipFaceAgainstPlane=function(t,e,i,o){var n,r,a=t.length;if(a<2)return e;var h=t[t.length-1],c=t[0];n=i.dot(h)+o;for(var l=0;l<a;l++){if(c=t[l],r=i.dot(c)+o,n<0)if(r<0){var u=new s;u.copy(c),e.push(u)}else{var u=new s;h.lerp(c,n/(n-r),u),e.push(u)}else if(r<0){var u=new s;h.lerp(c,n/(n-r),u),e.push(u),e.push(c)}h=c,n=r}return e},o.prototype.computeWorldVertices=function(t,e){for(var i=this.vertices.length;this.worldVertices.length<i;)this.worldVertices.push(new s);for(var o=this.vertices,n=this.worldVertices,r=0;r!==i;r++)e.vmult(o[r],n[r]),t.vadd(n[r],n[r]);this.worldVerticesNeedsUpdate=!1};new s;o.prototype.computeLocalAABB=function(t,e){var i=this.vertices.length,o=this.vertices;t.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE),e.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE);for(var n=0;n<i;n++){var s=o[n];s.x<t.x?t.x=s.x:s.x>e.x&&(e.x=s.x),s.y<t.y?t.y=s.y:s.y>e.y&&(e.y=s.y),s.z<t.z?t.z=s.z:s.z>e.z&&(e.z=s.z)}},o.prototype.computeWorldFaceNormals=function(t){for(var e=this.faceNormals.length;this.worldFaceNormals.length<e;)this.worldFaceNormals.push(new s);for(var i=this.faceNormals,o=this.worldFaceNormals,n=0;n!==e;n++)t.vmult(i[n],o[n]);this.worldFaceNormalsNeedsUpdate=!1},o.prototype.updateBoundingSphereRadius=function(){for(var t=0,e=this.vertices,i=0,o=e.length;i!==o;i++){var n=e[i].norm2();n>t&&(t=n)}this.boundingSphereRadius=Math.sqrt(t)};var T=new s;o.prototype.calculateWorldAABB=function(t,e,i,o){for(var n,s,r,a,h,c,l=this.vertices.length,u=this.vertices,p=0;p<l;p++){T.copy(u[p]),e.vmult(T,T),t.vadd(T,T);var d=T;d.x<n||void 0===n?n=d.x:(d.x>a||void 0===a)&&(a=d.x),d.y<s||void 0===s?s=d.y:(d.y>h||void 0===h)&&(h=d.y),d.z<r||void 0===r?r=d.z:(d.z>c||void 0===c)&&(c=d.z)}i.set(n,s,r),o.set(a,h,c)},o.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},o.prototype.getAveragePointLocal=function(t){t=t||new s;for(var e=this.vertices.length,i=this.vertices,o=0;o<e;o++)t.vadd(i[o],t);return t.mult(1/e,t),t},o.prototype.transformAllPoints=function(t,e){var i=this.vertices.length,o=this.vertices;if(e){for(var n=0;n<i;n++){var s=o[n];e.vmult(s,s)}for(var n=0;n<this.faceNormals.length;n++){var s=this.faceNormals[n];e.vmult(s,s)}}if(t)for(var n=0;n<i;n++){var s=o[n];s.vadd(t,s)}};var z=new s,k=new s,L=new s;o.prototype.pointIsInside=function(t){var e=this.vertices.length,i=this.vertices,o=this.faces,n=this.faceNormals,s=null,r=this.faces.length,a=z;this.getAveragePointLocal(a);for(var h=0;h<r;h++){var e=(this.faces[h].length,n[h]),c=i[o[h][0]],l=k;t.vsub(c,l);var u=e.dot(l),p=L;a.vsub(c,p);var d=e.dot(p);if(u<0&&d>0||u>0&&d<0)return!1}return s?1:-1};var P=(new s,new s),q=new s;o.project=function(t,e,i,o,n){var s=t.vertices.length,a=P,h=0,c=0,l=q,u=t.vertices;l.setZero(),r.vectorToLocalFrame(i,o,e,a),r.pointToLocalFrame(i,o,l,l);var p=l.dot(a);c=h=u[0].dot(a);for(var d=1;d<s;d++){var y=u[d].dot(a);y>h&&(h=y),y<c&&(c=y)}if(c-=p,h-=p,c>h){var v=c;c=h,h=v}n[0]=h,n[1]=c}},{"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"./Shape":87}],83:[function(t,e,i){function o(t,e,i,o){var r=o,a=[],h=[],c=[],l=[],u=[],p=Math.cos,d=Math.sin;a.push(new n(e*p(0),e*d(0),.5*-i)),l.push(0),a.push(new n(t*p(0),t*d(0),.5*i)),u.push(1);for(var y=0;y<r;y++){var v=2*Math.PI/r*(y+1),f=2*Math.PI/r*(y+.5);y<r-1?(a.push(new n(e*p(v),e*d(v),.5*-i)),l.push(2*y+2),a.push(new n(t*p(v),t*d(v),.5*i)),u.push(2*y+3),c.push([2*y+2,2*y+3,2*y+1,2*y])):c.push([0,1,2*y+1,2*y]),(r%2===1||y<r/2)&&h.push(new n(p(f),d(f),0))}c.push(u),h.push(new n(0,0,1));for(var m=[],y=0;y<l.length;y++)m.push(l[l.length-y-1]);c.push(m),s.call(this,a,c,h)}e.exports=o;var n=(t("./Shape"),t("../math/Vec3")),s=(t("../math/Quaternion"),t("./ConvexPolyhedron"));o.prototype=new s},{"../math/Quaternion":72,"../math/Vec3":74,"./ConvexPolyhedron":82,"./Shape":87}],84:[function(t,e,i){function o(t,e){e=h.defaults(e,{maxValue:null,minValue:null,elementSize:1}),this.data=t,this.maxValue=e.maxValue,this.minValue=e.minValue,this.elementSize=e.elementSize,null===e.minValue&&this.updateMinValue(),null===e.maxValue&&this.updateMaxValue(),this.cacheEnabled=!0,s.call(this,{type:s.types.HEIGHTFIELD}),this.pillarConvex=new r,this.pillarOffset=new a,this.updateBoundingSphereRadius(),this._cachedPillars={}}function n(t,e,i,o,n,s,r,a,h){h.x=((s-a)*(t-r)+(r-n)*(e-a))/((s-a)*(i-r)+(r-n)*(o-a)),h.y=((a-o)*(t-r)+(i-r)*(e-a))/((s-a)*(i-r)+(r-n)*(o-a)),h.z=1-h.x-h.y}var s=t("./Shape"),r=t("./ConvexPolyhedron"),a=t("../math/Vec3"),h=t("../utils/Utils");e.exports=o,o.prototype=new s,o.prototype.update=function(){this._cachedPillars={}},o.prototype.updateMinValue=function(){for(var t=this.data,e=t[0][0],i=0;i!==t.length;i++)for(var o=0;o!==t[i].length;o++){var n=t[i][o];n<e&&(e=n)}this.minValue=e},o.prototype.updateMaxValue=function(){for(var t=this.data,e=t[0][0],i=0;i!==t.length;i++)for(var o=0;o!==t[i].length;o++){var n=t[i][o];n>e&&(e=n)}this.maxValue=e},o.prototype.setHeightValueAtIndex=function(t,e,i){var o=this.data;o[t][e]=i,this.clearCachedConvexTrianglePillar(t,e,!1),t>0&&(this.clearCachedConvexTrianglePillar(t-1,e,!0),this.clearCachedConvexTrianglePillar(t-1,e,!1)),e>0&&(this.clearCachedConvexTrianglePillar(t,e-1,!0),this.clearCachedConvexTrianglePillar(t,e-1,!1)),e>0&&t>0&&this.clearCachedConvexTrianglePillar(t-1,e-1,!0)},o.prototype.getRectMinMax=function(t,e,i,o,n){n=n||[];for(var s=this.data,r=this.minValue,a=t;a<=i;a++)for(var h=e;h<=o;h++){var c=s[a][h];c>r&&(r=c)}n[0]=this.minValue,n[1]=r},o.prototype.getIndexOfPosition=function(t,e,i,o){var n=this.elementSize,s=this.data,r=Math.floor(t/n),a=Math.floor(e/n);return i[0]=r,i[1]=a,o&&(r<0&&(r=0),a<0&&(a=0),r>=s.length-1&&(r=s.length-1),a>=s[0].length-1&&(a=s[0].length-1)),!(r<0||a<0||r>=s.length-1||a>=s[0].length-1)};var c=[],l=new a,u=new a,p=new a,d=new a;o.prototype.getTriangleAt=function(t,e,i,o,n,s){var r=c;this.getIndexOfPosition(t,e,r,i);var a=r[0],h=r[1],l=this.data;i&&(a=Math.min(l.length-2,Math.max(0,a)),h=Math.min(l[0].length-2,Math.max(0,h)));var u=this.elementSize,p=Math.pow(t/u-a,2)+Math.pow(e/u-h,2),d=Math.pow(t/u-(a+1),2)+Math.pow(e/u-(h+1),2),y=p>d;return this.getTriangle(a,h,y,o,n,s),y};var y=new a,v=new a,f=new a,m=new a,g=new a;o.prototype.getNormalAt=function(t,e,i,o){var n=y,s=v,r=f,a=m,h=g;this.getTriangleAt(t,e,i,n,s,r),s.vsub(n,a),r.vsub(n,h),a.cross(h,o),o.normalize()},o.prototype.getAabbAtIndex=function(t,e,i){var o=this.data,n=this.elementSize;i.lowerBound.set(t*n,e*n,o[t][e]),i.upperBound.set((t+1)*n,(e+1)*n,o[t+1][e+1])},o.prototype.getHeightAt=function(t,e,i){var o=this.data,s=u,r=p,a=d,h=c;this.getIndexOfPosition(t,e,h,i);var y=h[0],v=h[1];i&&(y=Math.min(o.length-2,Math.max(0,y)),v=Math.min(o[0].length-2,Math.max(0,v)));var f=this.getTriangleAt(t,e,i,s,r,a);n(t,e,s.x,s.y,r.x,r.y,a.x,a.y,l);var m=l;return f?o[y+1][v+1]*m.x+o[y][v+1]*m.y+o[y+1][v]*m.z:o[y][v]*m.x+o[y+1][v]*m.y+o[y][v+1]*m.z},o.prototype.getCacheConvexTrianglePillarKey=function(t,e,i){return t+"_"+e+"_"+(i?1:0)},o.prototype.getCachedConvexTrianglePillar=function(t,e,i){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(t,e,i)]},o.prototype.setCachedConvexTrianglePillar=function(t,e,i,o,n){this._cachedPillars[this.getCacheConvexTrianglePillarKey(t,e,i)]={convex:o,offset:n}},o.prototype.clearCachedConvexTrianglePillar=function(t,e,i){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(t,e,i)]},o.prototype.getTriangle=function(t,e,i,o,n,s){var r=this.data,a=this.elementSize;i?(o.set((t+1)*a,(e+1)*a,r[t+1][e+1]),n.set(t*a,(e+1)*a,r[t][e+1]),s.set((t+1)*a,e*a,r[t+1][e])):(o.set(t*a,e*a,r[t][e]),n.set((t+1)*a,e*a,r[t+1][e]),s.set(t*a,(e+1)*a,r[t][e+1]))},o.prototype.getConvexTrianglePillar=function(t,e,i){var o=this.pillarConvex,n=this.pillarOffset;if(this.cacheEnabled){var s=this.getCachedConvexTrianglePillar(t,e,i);if(s)return this.pillarConvex=s.convex,void(this.pillarOffset=s.offset);o=new r,n=new a,this.pillarConvex=o,this.pillarOffset=n}var s=this.data,h=this.elementSize,c=o.faces;o.vertices.length=6;for(var l=0;l<6;l++)o.vertices[l]||(o.vertices[l]=new a);c.length=5;for(var l=0;l<5;l++)c[l]||(c[l]=[]);var u=o.vertices,p=(Math.min(s[t][e],s[t+1][e],s[t][e+1],s[t+1][e+1])-this.minValue)/2+this.minValue;i?(n.set((t+.75)*h,(e+.75)*h,p),u[0].set(.25*h,.25*h,s[t+1][e+1]-p),u[1].set(-.75*h,.25*h,s[t][e+1]-p),u[2].set(.25*h,-.75*h,s[t+1][e]-p),u[3].set(.25*h,.25*h,-p-1),u[4].set(-.75*h,.25*h,-p-1),u[5].set(.25*h,-.75*h,-p-1),c[0][0]=0,c[0][1]=1,c[0][2]=2,c[1][0]=5,c[1][1]=4,c[1][2]=3,c[2][0]=2,c[2][1]=5,c[2][2]=3,c[2][3]=0,c[3][0]=3,c[3][1]=4,c[3][2]=1,c[3][3]=0,c[4][0]=1,c[4][1]=4,c[4][2]=5,c[4][3]=2):(n.set((t+.25)*h,(e+.25)*h,p),u[0].set(-.25*h,-.25*h,s[t][e]-p),u[1].set(.75*h,-.25*h,s[t+1][e]-p),u[2].set(-.25*h,.75*h,s[t][e+1]-p),u[3].set(-.25*h,-.25*h,-p-1),u[4].set(.75*h,-.25*h,-p-1),u[5].set(-.25*h,.75*h,-p-1),c[0][0]=0,c[0][1]=1,c[0][2]=2,c[1][0]=5,c[1][1]=4,c[1][2]=3,c[2][0]=0,c[2][1]=2,c[2][2]=5,c[2][3]=3,c[3][0]=1,c[3][1]=0,c[3][2]=3,c[3][3]=4,c[4][0]=4,c[4][1]=5,c[4][2]=2,c[4][3]=1),o.computeNormals(),o.computeEdges(),o.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(t,e,i,o,n)},o.prototype.calculateLocalInertia=function(t,e){return e=e||new a,e.set(0,0,0),e},o.prototype.volume=function(){return Number.MAX_VALUE},o.prototype.calculateWorldAABB=function(t,e,i,o){i.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),o.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)},o.prototype.updateBoundingSphereRadius=function(){var t=this.data,e=this.elementSize;this.boundingSphereRadius=new a(t.length*e,t[0].length*e,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).norm()},o.prototype.setHeightsFromImage=function(t,e){var i=document.createElement("canvas");i.width=t.width,i.height=t.height;var o=i.getContext("2d");o.drawImage(t,0,0);var n=o.getImageData(0,0,t.width,t.height),s=this.data;
s.length=0,this.elementSize=Math.abs(e.x)/n.width;for(var r=0;r<n.height;r++){for(var a=[],h=0;h<n.width;h++){var c=n.data[4*(r*n.height+h)],l=n.data[4*(r*n.height+h)+1],u=n.data[4*(r*n.height+h)+2],p=(c+l+u)/4/255*e.z;e.x<0?a.push(p):a.unshift(p)}e.y<0?s.unshift(a):s.push(a)}this.updateMaxValue(),this.updateMinValue(),this.update()}},{"../math/Vec3":74,"../utils/Utils":97,"./ConvexPolyhedron":82,"./Shape":87}],85:[function(t,e,i){function o(){n.call(this,{type:n.types.PARTICLE})}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3");o.prototype=new n,o.prototype.constructor=o,o.prototype.calculateLocalInertia=function(t,e){return e=e||new s,e.set(0,0,0),e},o.prototype.volume=function(){return 0},o.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=0},o.prototype.calculateWorldAABB=function(t,e,i,o){i.copy(t),o.copy(t)}},{"../math/Vec3":74,"./Shape":87}],86:[function(t,e,i){function o(){n.call(this,{type:n.types.PLANE}),this.worldNormal=new s,this.worldNormalNeedsUpdate=!0,this.boundingSphereRadius=Number.MAX_VALUE}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3");o.prototype=new n,o.prototype.constructor=o,o.prototype.computeWorldNormal=function(t){var e=this.worldNormal;e.set(0,0,1),t.vmult(e,e),this.worldNormalNeedsUpdate=!1},o.prototype.calculateLocalInertia=function(t,e){return e=e||new s},o.prototype.volume=function(){return Number.MAX_VALUE};var r=new s;o.prototype.calculateWorldAABB=function(t,e,i,o){r.set(0,0,1),e.vmult(r,r);var n=Number.MAX_VALUE;i.set(-n,-n,-n),o.set(n,n,n),1===r.x&&(o.x=t.x),1===r.y&&(o.y=t.y),1===r.z&&(o.z=t.z),r.x===-1&&(i.x=t.x),r.y===-1&&(i.y=t.y),r.z===-1&&(i.z=t.z)},o.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=Number.MAX_VALUE}},{"../math/Vec3":74,"./Shape":87}],87:[function(t,e,i){function o(t){t=t||{},this.id=o.idCounter++,this.type=t.type||0,this.boundingSphereRadius=0,this.collisionResponse=!t.collisionResponse||t.collisionResponse,this.collisionFilterGroup=void 0!==t.collisionFilterGroup?t.collisionFilterGroup:1,this.collisionFilterMask=void 0!==t.collisionFilterMask?t.collisionFilterMask:-1,this.material=t.material?t.material:null,this.body=null}e.exports=o;var o=t("./Shape");t("../math/Vec3"),t("../math/Quaternion"),t("../material/Material");o.prototype.constructor=o,o.prototype.updateBoundingSphereRadius=function(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type},o.prototype.volume=function(){throw"volume() not implemented for shape type "+this.type},o.prototype.calculateLocalInertia=function(t,e){throw"calculateLocalInertia() not implemented for shape type "+this.type},o.idCounter=0,o.types={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256}},{"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"./Shape":87}],88:[function(t,e,i){function o(t){if(n.call(this,{type:n.types.SPHERE}),this.radius=void 0!==t?t:1,this.radius<0)throw new Error("The sphere radius cannot be negative.");this.updateBoundingSphereRadius()}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3");o.prototype=new n,o.prototype.constructor=o,o.prototype.calculateLocalInertia=function(t,e){e=e||new s;var i=2*t*this.radius*this.radius/5;return e.x=i,e.y=i,e.z=i,e},o.prototype.volume=function(){return 4*Math.PI*this.radius/3},o.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.radius},o.prototype.calculateWorldAABB=function(t,e,i,o){for(var n=this.radius,s=["x","y","z"],r=0;r<s.length;r++){var a=s[r];i[a]=t[a]-n,o[a]=t[a]+n}}},{"../math/Vec3":74,"./Shape":87}],89:[function(t,e,i){function o(t,e){n.call(this,{type:n.types.TRIMESH}),this.vertices=new Float32Array(t),this.indices=new Int16Array(e),this.normals=new Float32Array(e.length),this.aabb=new a,this.edges=null,this.scale=new s(1,1,1),this.tree=new h,this.updateEdges(),this.updateNormals(),this.updateAABB(),this.updateBoundingSphereRadius(),this.updateTree()}e.exports=o;var n=t("./Shape"),s=t("../math/Vec3"),r=(t("../math/Quaternion"),t("../math/Transform")),a=t("../collision/AABB"),h=t("../utils/Octree");o.prototype=new n,o.prototype.constructor=o;var c=new s;o.prototype.updateTree=function(){var t=this.tree;t.reset(),t.aabb.copy(this.aabb);var e=this.scale;t.aabb.lowerBound.x*=1/e.x,t.aabb.lowerBound.y*=1/e.y,t.aabb.lowerBound.z*=1/e.z,t.aabb.upperBound.x*=1/e.x,t.aabb.upperBound.y*=1/e.y,t.aabb.upperBound.z*=1/e.z;for(var i=new a,o=new s,n=new s,r=new s,h=[o,n,r],c=0;c<this.indices.length/3;c++){var l=3*c;this._getUnscaledVertex(this.indices[l],o),this._getUnscaledVertex(this.indices[l+1],n),this._getUnscaledVertex(this.indices[l+2],r),i.setFromPoints(h),t.insert(i,c)}t.removeEmptyNodes()};var l=new a;o.prototype.getTrianglesInAABB=function(t,e){l.copy(t);var i=this.scale,o=i.x,n=i.y,s=i.z,r=l.lowerBound,a=l.upperBound;return r.x/=o,r.y/=n,r.z/=s,a.x/=o,a.y/=n,a.z/=s,this.tree.aabbQuery(l,e)},o.prototype.setScale=function(t){var e=this.scale.x===this.scale.y===this.scale.z,i=t.x===t.y===t.z;e&&i||this.updateNormals(),this.scale.copy(t),this.updateAABB(),this.updateBoundingSphereRadius()},o.prototype.updateNormals=function(){for(var t=c,e=this.normals,i=0;i<this.indices.length/3;i++){var n=3*i,s=this.indices[n],r=this.indices[n+1],a=this.indices[n+2];this.getVertex(s,v),this.getVertex(r,f),this.getVertex(a,m),o.computeNormal(f,v,m,t),e[n]=t.x,e[n+1]=t.y,e[n+2]=t.z}},o.prototype.updateEdges=function(){for(var t={},e=function(e,i){var o=n<s?n+"_"+s:s+"_"+n;t[o]=!0},i=0;i<this.indices.length/3;i++){var o=3*i,n=this.indices[o],s=this.indices[o+1],r=this.indices[o+2];e(n,s),e(s,r),e(r,n)}var a=Object.keys(t);this.edges=new Int16Array(2*a.length);for(var i=0;i<a.length;i++){var h=a[i].split("_");this.edges[2*i]=parseInt(h[0],10),this.edges[2*i+1]=parseInt(h[1],10)}},o.prototype.getEdgeVertex=function(t,e,i){var o=this.edges[2*t+(e?1:0)];this.getVertex(o,i)};var u=new s,p=new s;o.prototype.getEdgeVector=function(t,e){var i=u,o=p;this.getEdgeVertex(t,0,i),this.getEdgeVertex(t,1,o),o.vsub(i,e)};var d=new s,y=new s;o.computeNormal=function(t,e,i,o){e.vsub(t,y),i.vsub(e,d),d.cross(y,o),o.isZero()||o.normalize()};var v=new s,f=new s,m=new s;o.prototype.getVertex=function(t,e){var i=this.scale;return this._getUnscaledVertex(t,e),e.x*=i.x,e.y*=i.y,e.z*=i.z,e},o.prototype._getUnscaledVertex=function(t,e){var i=3*t,o=this.vertices;return e.set(o[i],o[i+1],o[i+2])},o.prototype.getWorldVertex=function(t,e,i,o){return this.getVertex(t,o),r.pointToWorldFrame(e,i,o,o),o},o.prototype.getTriangleVertices=function(t,e,i,o){var n=3*t;this.getVertex(this.indices[n],e),this.getVertex(this.indices[n+1],i),this.getVertex(this.indices[n+2],o)},o.prototype.getNormal=function(t,e){var i=3*t;return e.set(this.normals[i],this.normals[i+1],this.normals[i+2])};var g=new a;o.prototype.calculateLocalInertia=function(t,e){this.computeLocalAABB(g);var i=g.upperBound.x-g.lowerBound.x,o=g.upperBound.y-g.lowerBound.y,n=g.upperBound.z-g.lowerBound.z;return e.set(1/12*t*(2*o*2*o+2*n*2*n),1/12*t*(2*i*2*i+2*n*2*n),1/12*t*(2*o*2*o+2*i*2*i))};var w=new s;o.prototype.computeLocalAABB=function(t){var e=t.lowerBound,i=t.upperBound,o=this.vertices.length,n=(this.vertices,w);this.getVertex(0,n),e.copy(n),i.copy(n);for(var s=0;s!==o;s++)this.getVertex(s,n),n.x<e.x?e.x=n.x:n.x>i.x&&(i.x=n.x),n.y<e.y?e.y=n.y:n.y>i.y&&(i.y=n.y),n.z<e.z?e.z=n.z:n.z>i.z&&(i.z=n.z)},o.prototype.updateAABB=function(){this.computeLocalAABB(this.aabb)},o.prototype.updateBoundingSphereRadius=function(){for(var t=0,e=this.vertices,i=new s,o=0,n=e.length/3;o!==n;o++){this.getVertex(o,i);var r=i.norm2();r>t&&(t=r)}this.boundingSphereRadius=Math.sqrt(t)};var b=(new s,new r),x=new a;o.prototype.calculateWorldAABB=function(t,e,i,o){var n=b,s=x;n.position=t,n.quaternion=e,this.aabb.toWorldFrame(n,s),i.copy(s.lowerBound),o.copy(s.upperBound)},o.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},o.createTorus=function(t,e,i,n,s){t=t||1,e=e||.5,i=i||8,n=n||6,s=s||2*Math.PI;for(var r=[],a=[],h=0;h<=i;h++)for(var c=0;c<=n;c++){var l=c/n*s,u=h/i*Math.PI*2,p=(t+e*Math.cos(u))*Math.cos(l),d=(t+e*Math.cos(u))*Math.sin(l),y=e*Math.sin(u);r.push(p,d,y)}for(var h=1;h<=i;h++)for(var c=1;c<=n;c++){var v=(n+1)*h+c-1,f=(n+1)*(h-1)+c-1,m=(n+1)*(h-1)+c,g=(n+1)*h+c;a.push(v,f,g),a.push(f,m,g)}return new o(r,a)}},{"../collision/AABB":46,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../utils/Octree":94,"./Shape":87}],90:[function(t,e,i){function o(){n.call(this),this.iterations=10,this.tolerance=1e-7}e.exports=o;var n=(t("../math/Vec3"),t("../math/Quaternion"),t("./Solver"));o.prototype=new n;var s=[],r=[],a=[];o.prototype.solve=function(t,e){var i,o,n,h,c,l,u=0,p=this.iterations,d=this.tolerance*this.tolerance,y=this.equations,v=y.length,f=e.bodies,m=f.length,g=t;if(0!==v)for(var w=0;w!==m;w++)f[w].updateSolveMassProperties();var b=r,x=a,E=s;b.length=v,x.length=v,E.length=v;for(var w=0;w!==v;w++){var A=y[w];E[w]=0,x[w]=A.computeB(g),b[w]=1/A.computeC()}if(0!==v){for(var w=0;w!==m;w++){var C=f[w],B=C.vlambda,S=C.wlambda;B.set(0,0,0),S.set(0,0,0)}for(u=0;u!==p;u++){h=0;for(var R=0;R!==v;R++){var A=y[R];i=x[R],o=b[R],l=E[R],c=A.computeGWlambda(),n=o*(i-c-A.eps*l),l+n<A.minForce?n=A.minForce-l:l+n>A.maxForce&&(n=A.maxForce-l),E[R]+=n,h+=n>0?n:-n,A.addToWlambda(n)}if(h*h<d)break}for(var w=0;w!==m;w++){var C=f[w],M=C.velocity,T=C.angularVelocity;C.vlambda.vmul(C.linearFactor,C.vlambda),M.vadd(C.vlambda,M),C.wlambda.vmul(C.angularFactor,C.wlambda),T.vadd(C.wlambda,T)}for(var z=y.length,k=1/g;z--;)y[z].multiplier=E[z]*k}return u}},{"../math/Quaternion":72,"../math/Vec3":74,"./Solver":91}],91:[function(t,e,i){function o(){this.equations=[]}e.exports=o,o.prototype.solve=function(t,e){return 0},o.prototype.addEquation=function(t){t.enabled&&this.equations.push(t)},o.prototype.removeEquation=function(t){var e=this.equations,i=e.indexOf(t);i!==-1&&e.splice(i,1)},o.prototype.removeAllEquations=function(){this.equations.length=0}},{}],92:[function(t,e,i){function o(t){for(h.call(this),this.iterations=10,this.tolerance=1e-7,this.subsolver=t,this.nodes=[],this.nodePool=[];this.nodePool.length<128;)this.nodePool.push(this.createNode())}function n(t){for(var e=t.length,i=0;i!==e;i++){var o=t[i];if(!(o.visited||o.body.type&d))return o}return!1}function s(t,e,i,o){for(y.push(t),t.visited=!0,e(t,i,o);y.length;)for(var s,r=y.pop();s=n(r.children);)s.visited=!0,e(s,i,o),y.push(s)}function r(t,e,i){e.push(t.body);for(var o=t.eqs.length,n=0;n!==o;n++){var s=t.eqs[n];i.indexOf(s)===-1&&i.push(s)}}function a(t,e){return e.id-t.id}e.exports=o;var h=(t("../math/Vec3"),t("../math/Quaternion"),t("./Solver")),c=t("../objects/Body");o.prototype=new h;var l=[],u=[],p={bodies:[]},d=c.STATIC,y=[];o.prototype.createNode=function(){return{body:null,children:[],eqs:[],visited:!1}},o.prototype.solve=function(t,e){for(var i=l,o=this.nodePool,h=e.bodies,c=this.equations,d=c.length,y=h.length,v=this.subsolver;o.length<y;)o.push(this.createNode());i.length=y;for(var f=0;f<y;f++)i[f]=o[f];for(var f=0;f!==y;f++){var m=i[f];m.body=h[f],m.children.length=0,m.eqs.length=0,m.visited=!1}for(var g=0;g!==d;g++){var w=c[g],f=h.indexOf(w.bi),b=h.indexOf(w.bj),x=i[f],E=i[b];x.children.push(E),x.eqs.push(w),E.children.push(x),E.eqs.push(w)}var A,C=0,B=u;v.tolerance=this.tolerance,v.iterations=this.iterations;for(var S=p;A=n(i);){B.length=0,S.bodies.length=0,s(A,r,S.bodies,B);var R=B.length;B=B.sort(a);for(var f=0;f!==R;f++)v.addEquation(B[f]);v.solve(t,S);v.removeAllEquations(),C++}return C}},{"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"./Solver":91}],93:[function(t,e,i){var o=function(){};e.exports=o,o.prototype={constructor:o,addEventListener:function(t,e){void 0===this._listeners&&(this._listeners={});var i=this._listeners;return void 0===i[t]&&(i[t]=[]),i[t].indexOf(e)===-1&&i[t].push(e),this},hasEventListener:function(t,e){if(void 0===this._listeners)return!1;var i=this._listeners;return void 0!==i[t]&&i[t].indexOf(e)!==-1},hasAnyEventListener:function(t){if(void 0===this._listeners)return!1;var e=this._listeners;return void 0!==e[t]},removeEventListener:function(t,e){if(void 0===this._listeners)return this;var i=this._listeners;if(void 0===i[t])return this;var o=i[t].indexOf(e);return o!==-1&&i[t].splice(o,1),this},dispatchEvent:function(t){if(void 0===this._listeners)return this;var e=this._listeners,i=e[t.type];if(void 0!==i){t.target=this;for(var o=0,n=i.length;o<n;o++)i[o].call(this,t)}return this}}},{}],94:[function(t,e,i){function o(t){t=t||{},this.root=t.root||null,this.aabb=t.aabb?t.aabb.clone():new s,this.data=[],this.children=[]}function n(t,e){e=e||{},e.root=null,e.aabb=t,o.call(this,e),this.maxDepth="undefined"!=typeof e.maxDepth?e.maxDepth:8}var s=t("../collision/AABB"),r=t("../math/Vec3");e.exports=n,n.prototype=new o,o.prototype.reset=function(t,e){this.children.length=this.data.length=0},o.prototype.insert=function(t,e,i){var o=this.data;if(i=i||0,!this.aabb.contains(t))return!1;var n=this.children;if(i<(this.maxDepth||this.root.maxDepth)){var s=!1;n.length||(this.subdivide(),s=!0);for(var r=0;8!==r;r++)if(n[r].insert(t,e,i+1))return!0;s&&(n.length=0)}return o.push(e),!0};var a=new r;o.prototype.subdivide=function(){var t=this.aabb,e=t.lowerBound,i=t.upperBound,n=this.children;n.push(new o({aabb:new s({lowerBound:new r(0,0,0)})}),new o({aabb:new s({lowerBound:new r(1,0,0)})}),new o({aabb:new s({lowerBound:new r(1,1,0)})}),new o({aabb:new s({lowerBound:new r(1,1,1)})}),new o({aabb:new s({lowerBound:new r(0,1,1)})}),new o({aabb:new s({lowerBound:new r(0,0,1)})}),new o({aabb:new s({lowerBound:new r(1,0,1)})}),new o({aabb:new s({lowerBound:new r(0,1,0)})})),i.vsub(e,a),a.scale(.5,a);for(var h=this.root||this,c=0;8!==c;c++){var l=n[c];l.root=h;var u=l.aabb.lowerBound;u.x*=a.x,u.y*=a.y,u.z*=a.z,u.vadd(e,u),u.vadd(a,l.aabb.upperBound)}},o.prototype.aabbQuery=function(t,e){for(var i=(this.data,this.children,[this]);i.length;){var o=i.pop();o.aabb.overlaps(t)&&Array.prototype.push.apply(e,o.data),Array.prototype.push.apply(i,o.children)}return e};var h=new s;o.prototype.rayQuery=function(t,e,i){return t.getAABB(h),h.toLocalFrame(e,h),this.aabbQuery(h,i),i},o.prototype.removeEmptyNodes=function(){for(var t=[this];t.length;){for(var e=t.pop(),i=e.children.length-1;i>=0;i--)e.children[i].data.length||e.children.splice(i,1);Array.prototype.push.apply(t,e.children)}}},{"../collision/AABB":46,"../math/Vec3":74}],95:[function(t,e,i){function o(){this.objects=[],this.type=Object}e.exports=o,o.prototype.release=function(){for(var t=arguments.length,e=0;e!==t;e++)this.objects.push(arguments[e]);return this},o.prototype.get=function(){return 0===this.objects.length?this.constructObject():this.objects.pop()},o.prototype.constructObject=function(){throw new Error("constructObject() not implemented in this Pool subclass yet!")},o.prototype.resize=function(t){for(var e=this.objects;e.length>t;)e.pop();for(;e.length<t;)e.push(this.constructObject());return this}},{}],96:[function(t,e,i){function o(){this.data={keys:[]}}e.exports=o,o.prototype.get=function(t,e){if(t>e){var i=e;e=t,t=i}return this.data[t+"-"+e]},o.prototype.set=function(t,e,i){if(t>e){var o=e;e=t,t=o}var n=t+"-"+e;this.get(t,e)||this.data.keys.push(n),this.data[n]=i},o.prototype.reset=function(){for(var t=this.data,e=t.keys;e.length>0;){var i=e.pop();delete t[i]}}},{}],97:[function(t,e,i){function o(){}e.exports=o,o.defaults=function(t,e){t=t||{};for(var i in e)i in t||(t[i]=e[i]);return t}},{}],98:[function(t,e,i){function o(){s.call(this),this.type=n}e.exports=o;var n=t("../math/Vec3"),s=t("./Pool");o.prototype=new s,o.prototype.constructObject=function(){return new n}},{"../math/Vec3":74,"./Pool":95}],99:[function(t,e,i){function o(t){this.contactPointPool=[],this.frictionEquationPool=[],this.result=[],this.frictionResult=[],this.v3pool=new p,this.world=t,this.currentContactMaterial=null,this.enableFrictionReduction=!1}function n(t,e,i){for(var o=null,n=t.length,s=0;s!==n;s++){var r=t[s],a=D;t[(s+1)%n].vsub(r,a);var h=W;a.cross(e,h);var c=K;i.vsub(r,c);var l=h.dot(c);if(!(null===o||l>0&&o===!0||l<=0&&o===!1))return!1;null===o&&(o=l>0)}return!0}e.exports=o;var s=t("../collision/AABB"),r=t("../objects/Body"),a=t("../shapes/Shape"),h=t("../collision/Ray"),c=t("../math/Vec3"),l=t("../math/Transform"),u=(t("../shapes/ConvexPolyhedron"),t("../math/Quaternion")),p=(t("../solver/Solver"),t("../utils/Vec3Pool")),d=t("../equations/ContactEquation"),y=t("../equations/FrictionEquation");o.prototype.createContactEquation=function(t,e,i,o,n,s){var r;this.contactPointPool.length?(r=this.contactPointPool.pop(),r.bi=t,r.bj=e):r=new d(t,e),r.enabled=t.collisionResponse&&e.collisionResponse&&i.collisionResponse&&o.collisionResponse;var a=this.currentContactMaterial;r.restitution=a.restitution,r.setSpookParams(a.contactEquationStiffness,a.contactEquationRelaxation,this.world.dt);var h=i.material||t.material,c=o.material||e.material;return h&&c&&h.restitution>=0&&c.restitution>=0&&(r.restitution=h.restitution*c.restitution),r.si=n||i,r.sj=s||o,r},o.prototype.createFrictionEquationsFromContact=function(t,e){var i=t.bi,o=t.bj,n=t.si,s=t.sj,r=this.world,a=this.currentContactMaterial,h=a.friction,c=n.material||i.material,l=s.material||o.material;if(c&&l&&c.friction>=0&&l.friction>=0&&(h=c.friction*l.friction),h>0){var u=h*r.gravity.length(),p=i.invMass+o.invMass;p>0&&(p=1/p);var d=this.frictionEquationPool,v=d.length?d.pop():new y(i,o,u*p),f=d.length?d.pop():new y(i,o,u*p);return v.bi=f.bi=i,v.bj=f.bj=o,v.minForce=f.minForce=-u*p,v.maxForce=f.maxForce=u*p,v.ri.copy(t.ri),v.rj.copy(t.rj),f.ri.copy(t.ri),f.rj.copy(t.rj),t.ni.tangents(v.t,f.t),v.setSpookParams(a.frictionEquationStiffness,a.frictionEquationRelaxation,r.dt),f.setSpookParams(a.frictionEquationStiffness,a.frictionEquationRelaxation,r.dt),v.enabled=f.enabled=t.enabled,e.push(v,f),!0}return!1};var v=new c,f=new c,m=new c;o.prototype.createFrictionFromAverage=function(t){var e=this.result[this.result.length-1];if(this.createFrictionEquationsFromContact(e,this.frictionResult)&&1!==t){var i=this.frictionResult[this.frictionResult.length-2],o=this.frictionResult[this.frictionResult.length-1];v.setZero(),f.setZero(),m.setZero();for(var n=e.bi,s=(e.bj,0);s!==t;s++)e=this.result[this.result.length-1-s],e.bodyA!==n?(v.vadd(e.ni,v),f.vadd(e.ri,f),m.vadd(e.rj,m)):(v.vsub(e.ni,v),f.vadd(e.rj,f),m.vadd(e.ri,m));var r=1/t;f.scale(r,i.ri),m.scale(r,i.rj),o.ri.copy(i.ri),o.rj.copy(i.rj),v.normalize(),v.tangents(i.t,o.t)}};var g=new c,w=new c,b=new u,x=new u;o.prototype.getContacts=function(t,e,i,o,n,s,a){this.contactPointPool=n,this.frictionEquationPool=a,this.result=o,this.frictionResult=s;for(var h=b,c=x,l=g,u=w,p=0,d=t.length;p!==d;p++){var y=t[p],v=e[p],f=null;y.material&&v.material&&(f=i.getContactMaterial(y.material,v.material)||null);for(var m=y.type&r.KINEMATIC&&v.type&r.STATIC||y.type&r.STATIC&&v.type&r.KINEMATIC||y.type&r.KINEMATIC&&v.type&r.KINEMATIC,E=0;E<y.shapes.length;E++){y.quaternion.mult(y.shapeOrientations[E],h),y.quaternion.vmult(y.shapeOffsets[E],l),l.vadd(y.position,l);for(var A=y.shapes[E],C=0;C<v.shapes.length;C++){v.quaternion.mult(v.shapeOrientations[C],c),v.quaternion.vmult(v.shapeOffsets[C],u),u.vadd(v.position,u);var B=v.shapes[C];if(A.collisionFilterMask&B.collisionFilterGroup&&B.collisionFilterMask&A.collisionFilterGroup&&!(l.distanceTo(u)>A.boundingSphereRadius+B.boundingSphereRadius)){var S=null;A.material&&B.material&&(S=i.getContactMaterial(A.material,B.material)||null),this.currentContactMaterial=S||f||i.defaultContactMaterial;var R=this[A.type|B.type];if(R){var M=!1;M=A.type<B.type?R.call(this,A,B,l,u,h,c,y,v,A,B,m):R.call(this,B,A,u,l,c,h,v,y,A,B,m),M&&m&&(i.shapeOverlapKeeper.set(A.id,B.id),i.bodyOverlapKeeper.set(y.id,v.id))}}}}}};o.prototype[a.types.BOX|a.types.BOX]=o.prototype.boxBox=function(t,e,i,o,n,s,r,a,h,c,l){return t.convexPolyhedronRepresentation.material=t.material,e.convexPolyhedronRepresentation.material=e.material,t.convexPolyhedronRepresentation.collisionResponse=t.collisionResponse,e.convexPolyhedronRepresentation.collisionResponse=e.collisionResponse,this.convexConvex(t.convexPolyhedronRepresentation,e.convexPolyhedronRepresentation,i,o,n,s,r,a,t,e,l)},o.prototype[a.types.BOX|a.types.CONVEXPOLYHEDRON]=o.prototype.boxConvex=function(t,e,i,o,n,s,r,a,h,c,l){return t.convexPolyhedronRepresentation.material=t.material,t.convexPolyhedronRepresentation.collisionResponse=t.collisionResponse,this.convexConvex(t.convexPolyhedronRepresentation,e,i,o,n,s,r,a,t,e,l)},o.prototype[a.types.BOX|a.types.PARTICLE]=o.prototype.boxParticle=function(t,e,i,o,n,s,r,a,h,c,l){return t.convexPolyhedronRepresentation.material=t.material,t.convexPolyhedronRepresentation.collisionResponse=t.collisionResponse,this.convexParticle(t.convexPolyhedronRepresentation,e,i,o,n,s,r,a,t,e,l)},o.prototype[a.types.SPHERE]=o.prototype.sphereSphere=function(t,e,i,o,n,s,r,a,h,c,l){if(l)return i.distanceSquared(o)<Math.pow(t.radius+e.radius,2);var u=this.createContactEquation(r,a,t,e,h,c);o.vsub(i,u.ni),u.ni.normalize(),u.ri.copy(u.ni),u.rj.copy(u.ni),u.ri.mult(t.radius,u.ri),u.rj.mult(-e.radius,u.rj),u.ri.vadd(i,u.ri),u.ri.vsub(r.position,u.ri),u.rj.vadd(o,u.rj),u.rj.vsub(a.position,u.rj),this.result.push(u),this.createFrictionEquationsFromContact(u,this.frictionResult)};var E=new c,A=new c,C=new c;o.prototype[a.types.PLANE|a.types.TRIMESH]=o.prototype.planeTrimesh=function(t,e,i,o,n,s,r,a,h,u,p){var d=new c,y=E;y.set(0,0,1),n.vmult(y,y);for(var v=0;v<e.vertices.length/3;v++){e.getVertex(v,d);var f=new c;f.copy(d),l.pointToWorldFrame(o,s,f,d);var m=A;d.vsub(i,m);var g=y.dot(m);if(g<=0){if(p)return!0;var w=this.createContactEquation(r,a,t,e,h,u);w.ni.copy(y);var b=C;y.scale(m.dot(y),b),d.vsub(b,b),w.ri.copy(b),w.ri.vsub(r.position,w.ri),w.rj.copy(d),w.rj.vsub(a.position,w.rj),this.result.push(w),this.createFrictionEquationsFromContact(w,this.frictionResult)}}};var B=new c,S=new c,R=(new c,new c),M=new c,T=new c,z=new c,k=new c,L=new c,P=new c,q=new c,V=new c,F=new c,N=new c,I=new s,O=[];o.prototype[a.types.SPHERE|a.types.TRIMESH]=o.prototype.sphereTrimesh=function(t,e,i,o,n,s,r,a,c,u,p){var d=T,y=z,v=k,f=L,m=P,g=q,w=I,b=M,x=S,E=O;l.pointToLocalFrame(o,s,i,m);var A=t.radius;w.lowerBound.set(m.x-A,m.y-A,m.z-A),w.upperBound.set(m.x+A,m.y+A,m.z+A),e.getTrianglesInAABB(w,E);for(var C=R,H=t.radius*t.radius,j=0;j<E.length;j++)for(var D=0;D<3;D++)if(e.getVertex(e.indices[3*E[j]+D],C),C.vsub(m,x),x.norm2()<=H){if(b.copy(C),l.pointToWorldFrame(o,s,b,C),C.vsub(i,x),p)return!0;var W=this.createContactEquation(r,a,t,e,c,u);W.ni.copy(x),W.ni.normalize(),W.ri.copy(W.ni),W.ri.scale(t.radius,W.ri),W.ri.vadd(i,W.ri),W.ri.vsub(r.position,W.ri),W.rj.copy(C),W.rj.vsub(a.position,W.rj),this.result.push(W),this.createFrictionEquationsFromContact(W,this.frictionResult)}for(var j=0;j<E.length;j++)for(var D=0;D<3;D++){e.getVertex(e.indices[3*E[j]+D],d),e.getVertex(e.indices[3*E[j]+(D+1)%3],y),y.vsub(d,v),m.vsub(y,g);var K=g.dot(v);m.vsub(d,g);var _=g.dot(v);if(_>0&&K<0){m.vsub(d,g),f.copy(v),f.normalize(),_=g.dot(f),f.scale(_,g),g.vadd(d,g);var U=g.distanceTo(m);if(U<t.radius){if(p)return!0;var W=this.createContactEquation(r,a,t,e,c,u);g.vsub(m,W.ni),W.ni.normalize(),W.ni.scale(t.radius,W.ri),l.pointToWorldFrame(o,s,g,g),g.vsub(a.position,W.rj),l.vectorToWorldFrame(s,W.ni,W.ni),l.vectorToWorldFrame(s,W.ri,W.ri),this.result.push(W),this.createFrictionEquationsFromContact(W,this.frictionResult)}}}for(var G=V,X=F,Y=N,Q=B,j=0,Z=E.length;j!==Z;j++){e.getTriangleVertices(E[j],G,X,Y),e.getNormal(E[j],Q),m.vsub(G,g);var U=g.dot(Q);if(Q.scale(U,g),m.vsub(g,g),U=g.distanceTo(m),h.pointInTriangle(g,G,X,Y)&&U<t.radius){if(p)return!0;var W=this.createContactEquation(r,a,t,e,c,u);g.vsub(m,W.ni),W.ni.normalize(),W.ni.scale(t.radius,W.ri),l.pointToWorldFrame(o,s,g,g),g.vsub(a.position,W.rj),l.vectorToWorldFrame(s,W.ni,W.ni),l.vectorToWorldFrame(s,W.ri,W.ri),this.result.push(W),this.createFrictionEquationsFromContact(W,this.frictionResult)}}E.length=0};var H=new c,j=new c;o.prototype[a.types.SPHERE|a.types.PLANE]=o.prototype.spherePlane=function(t,e,i,o,n,s,r,a,h,c,l){var u=this.createContactEquation(r,a,t,e,h,c);if(u.ni.set(0,0,1),s.vmult(u.ni,u.ni),u.ni.negate(u.ni),u.ni.normalize(),u.ni.mult(t.radius,u.ri),i.vsub(o,H),u.ni.mult(u.ni.dot(H),j),H.vsub(j,u.rj),-H.dot(u.ni)<=t.radius){if(l)return!0;var p=u.ri,d=u.rj;p.vadd(i,p),p.vsub(r.position,p),d.vadd(o,d),d.vsub(a.position,d),this.result.push(u),this.createFrictionEquationsFromContact(u,this.frictionResult)}};var D=new c,W=new c,K=new c,_=new c,U=new c,G=new c,X=new c,Y=[new c,new c,new c,new c,new c,new c],Q=new c,Z=new c,J=new c,$=new c;o.prototype[a.types.SPHERE|a.types.BOX]=o.prototype.sphereBox=function(t,e,i,o,n,s,r,a,h,c,l){var u=this.v3pool,p=Y;i.vsub(o,_),e.getSideNormals(p,s);for(var d=t.radius,y=!1,v=Z,f=J,m=$,g=null,w=0,b=0,x=0,E=null,A=0,C=p.length;A!==C&&y===!1;A++){var B=U;B.copy(p[A]);var S=B.norm();B.normalize();var R=_.dot(B);if(R<S+d&&R>0){var M=G,T=X;M.copy(p[(A+1)%3]),T.copy(p[(A+2)%3]);var z=M.norm(),k=T.norm();M.normalize(),T.normalize();var L=_.dot(M),P=_.dot(T);if(L<z&&L>-z&&P<k&&P>-k){var q=Math.abs(R-S-d);if((null===E||q<E)&&(E=q,b=L,x=P,g=S,v.copy(B),f.copy(M),m.copy(T),w++,l))return!0}}}if(w){y=!0;var V=this.createContactEquation(r,a,t,e,h,c);v.mult(-d,V.ri),V.ni.copy(v),V.ni.negate(V.ni),v.mult(g,v),f.mult(b,f),v.vadd(f,v),m.mult(x,m),v.vadd(m,V.rj),V.ri.vadd(i,V.ri),V.ri.vsub(r.position,V.ri),V.rj.vadd(o,V.rj),V.rj.vsub(a.position,V.rj),this.result.push(V),this.createFrictionEquationsFromContact(V,this.frictionResult)}for(var F=u.get(),N=Q,I=0;2!==I&&!y;I++)for(var O=0;2!==O&&!y;O++)for(var H=0;2!==H&&!y;H++)if(F.set(0,0,0),I?F.vadd(p[0],F):F.vsub(p[0],F),O?F.vadd(p[1],F):F.vsub(p[1],F),H?F.vadd(p[2],F):F.vsub(p[2],F),o.vadd(F,N),N.vsub(i,N),N.norm2()<d*d){if(l)return!0;y=!0;var V=this.createContactEquation(r,a,t,e,h,c);V.ri.copy(N),V.ri.normalize(),V.ni.copy(V.ri),V.ri.mult(d,V.ri),V.rj.copy(F),V.ri.vadd(i,V.ri),V.ri.vsub(r.position,V.ri),V.rj.vadd(o,V.rj),V.rj.vsub(a.position,V.rj),this.result.push(V),this.createFrictionEquationsFromContact(V,this.frictionResult)}u.release(F),F=null;for(var j=u.get(),D=u.get(),V=u.get(),W=u.get(),q=u.get(),K=p.length,I=0;I!==K&&!y;I++)for(var O=0;O!==K&&!y;O++)if(I%3!==O%3){p[O].cross(p[I],j),j.normalize(),p[I].vadd(p[O],D),V.copy(i),V.vsub(D,V),V.vsub(o,V);var tt=V.dot(j);j.mult(tt,W);for(var H=0;H===I%3||H===O%3;)H++;q.copy(i),q.vsub(W,q),q.vsub(D,q),q.vsub(o,q);var et=Math.abs(tt),it=q.norm();if(et<p[H].norm()&&it<d){if(l)return!0;y=!0;var ot=this.createContactEquation(r,a,t,e,h,c);D.vadd(W,ot.rj),ot.rj.copy(ot.rj),q.negate(ot.ni),ot.ni.normalize(),ot.ri.copy(ot.rj),ot.ri.vadd(o,ot.ri),ot.ri.vsub(i,ot.ri),ot.ri.normalize(),ot.ri.mult(d,ot.ri),ot.ri.vadd(i,ot.ri),ot.ri.vsub(r.position,ot.ri),ot.rj.vadd(o,ot.rj),ot.rj.vsub(a.position,ot.rj),this.result.push(ot),this.createFrictionEquationsFromContact(ot,this.frictionResult)}}u.release(j,D,V,W,q)};var tt=new c,et=new c,it=new c,ot=new c,nt=new c,st=new c,rt=new c,at=new c,ht=new c,ct=new c;o.prototype[a.types.SPHERE|a.types.CONVEXPOLYHEDRON]=o.prototype.sphereConvex=function(t,e,i,o,s,r,a,h,c,l,u){var p=this.v3pool;i.vsub(o,tt);for(var d=e.faceNormals,y=e.faces,v=e.vertices,f=t.radius,m=0;m!==v.length;m++){var g=v[m],w=nt;r.vmult(g,w),o.vadd(w,w);var b=ot;if(w.vsub(i,b),b.norm2()<f*f){if(u)return!0;E=!0;var x=this.createContactEquation(a,h,t,e,c,l);return x.ri.copy(b),x.ri.normalize(),x.ni.copy(x.ri),x.ri.mult(f,x.ri),w.vsub(o,x.rj),x.ri.vadd(i,x.ri),x.ri.vsub(a.position,x.ri),x.rj.vadd(o,x.rj),x.rj.vsub(h.position,x.rj),this.result.push(x),void this.createFrictionEquationsFromContact(x,this.frictionResult)}}for(var E=!1,m=0,A=y.length;m!==A&&E===!1;m++){var C=d[m],B=y[m],S=st;r.vmult(C,S);var R=rt;r.vmult(v[B[0]],R),R.vadd(o,R);var M=at;S.mult(-f,M),i.vadd(M,M);var T=ht;M.vsub(R,T);var z=T.dot(S),k=ct;if(i.vsub(R,k),z<0&&k.dot(S)>0){for(var L=[],P=0,q=B.length;P!==q;P++){var V=p.get();r.vmult(v[B[P]],V),o.vadd(V,V),L.push(V)}if(n(L,S,i)){if(u)return!0;E=!0;var x=this.createContactEquation(a,h,t,e,c,l);S.mult(-f,x.ri),S.negate(x.ni);var F=p.get();S.mult(-z,F);var N=p.get();S.mult(-f,N),i.vsub(o,x.rj),x.rj.vadd(N,x.rj),x.rj.vadd(F,x.rj),x.rj.vadd(o,x.rj),x.rj.vsub(h.position,x.rj),x.ri.vadd(i,x.ri),x.ri.vsub(a.position,x.ri),p.release(F),p.release(N),this.result.push(x),this.createFrictionEquationsFromContact(x,this.frictionResult);for(var P=0,I=L.length;P!==I;P++)p.release(L[P]);return}for(var P=0;P!==B.length;P++){var O=p.get(),H=p.get();r.vmult(v[B[(P+1)%B.length]],O),r.vmult(v[B[(P+2)%B.length]],H),o.vadd(O,O),o.vadd(H,H);var j=et;H.vsub(O,j);var D=it;j.unit(D);var W=p.get(),K=p.get();i.vsub(O,K);var _=K.dot(D);D.mult(_,W),W.vadd(O,W);var U=p.get();if(W.vsub(i,U),_>0&&_*_<j.norm2()&&U.norm2()<f*f){if(u)return!0;var x=this.createContactEquation(a,h,t,e,c,l);W.vsub(o,x.rj),W.vsub(i,x.ni),x.ni.normalize(),x.ni.mult(f,x.ri),x.rj.vadd(o,x.rj),x.rj.vsub(h.position,x.rj),x.ri.vadd(i,x.ri),x.ri.vsub(a.position,x.ri),this.result.push(x),this.createFrictionEquationsFromContact(x,this.frictionResult);for(var P=0,I=L.length;P!==I;P++)p.release(L[P]);return p.release(O),p.release(H),p.release(W),p.release(U),void p.release(K)}p.release(O),p.release(H),p.release(W),p.release(U),p.release(K)}for(var P=0,I=L.length;P!==I;P++)p.release(L[P])}}};new c,new c;o.prototype[a.types.PLANE|a.types.BOX]=o.prototype.planeBox=function(t,e,i,o,n,s,r,a,h,c,l){return e.convexPolyhedronRepresentation.material=e.material,e.convexPolyhedronRepresentation.collisionResponse=e.collisionResponse,e.convexPolyhedronRepresentation.id=e.id,this.planeConvex(t,e.convexPolyhedronRepresentation,i,o,n,s,r,a,t,e,l)};var lt=new c,ut=new c,pt=new c,dt=new c;o.prototype[a.types.PLANE|a.types.CONVEXPOLYHEDRON]=o.prototype.planeConvex=function(t,e,i,o,n,s,r,a,h,c,l){var u=lt,p=ut;p.set(0,0,1),n.vmult(p,p);for(var d=0,y=pt,v=0;v!==e.vertices.length;v++){u.copy(e.vertices[v]),s.vmult(u,u),o.vadd(u,u),u.vsub(i,y);var f=p.dot(y);if(f<=0){if(l)return!0;var m=this.createContactEquation(r,a,t,e,h,c),g=dt;p.mult(p.dot(y),g),u.vsub(g,g),g.vsub(i,m.ri),m.ni.copy(p),u.vsub(o,m.rj),m.ri.vadd(i,m.ri),m.ri.vsub(r.position,m.ri),m.rj.vadd(o,m.rj),m.rj.vsub(a.position,m.rj),this.result.push(m),d++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(m,this.frictionResult)}}this.enableFrictionReduction&&d&&this.createFrictionFromAverage(d)};var yt=new c,vt=new c;o.prototype[a.types.CONVEXPOLYHEDRON]=o.prototype.convexConvex=function(t,e,i,o,n,s,r,a,h,c,l,u,p){var d=yt;if(!(i.distanceTo(o)>t.boundingSphereRadius+e.boundingSphereRadius)&&t.findSeparatingAxis(e,i,n,o,s,d,u,p)){var y=[],v=vt;t.clipAgainstHull(i,n,e,o,s,d,-100,100,y);for(var f=0,m=0;m!==y.length;m++){if(l)return!0;var g=this.createContactEquation(r,a,t,e,h,c),w=g.ri,b=g.rj;d.negate(g.ni),y[m].normal.negate(v),v.mult(y[m].depth,v),y[m].point.vadd(v,w),b.copy(y[m].point),w.vsub(i,w),b.vsub(o,b),w.vadd(i,w),w.vsub(r.position,w),b.vadd(o,b),b.vsub(a.position,b),this.result.push(g),f++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(g,this.frictionResult)}this.enableFrictionReduction&&f&&this.createFrictionFromAverage(f)}};var ft=new c,mt=new c,gt=new c;o.prototype[a.types.PLANE|a.types.PARTICLE]=o.prototype.planeParticle=function(t,e,i,o,n,s,r,a,h,c,l){var u=ft;u.set(0,0,1),r.quaternion.vmult(u,u);var p=mt;o.vsub(r.position,p);var d=u.dot(p);if(d<=0){if(l)return!0;var y=this.createContactEquation(a,r,e,t,h,c);y.ni.copy(u),y.ni.negate(y.ni),y.ri.set(0,0,0);var v=gt;u.mult(u.dot(o),v),o.vsub(v,v),y.rj.copy(v),this.result.push(y),this.createFrictionEquationsFromContact(y,this.frictionResult)}};var wt=new c;o.prototype[a.types.PARTICLE|a.types.SPHERE]=o.prototype.sphereParticle=function(t,e,i,o,n,s,r,a,h,c,l){var u=wt;u.set(0,0,1),o.vsub(i,u);var p=u.norm2();if(p<=t.radius*t.radius){if(l)return!0;var d=this.createContactEquation(a,r,e,t,h,c);u.normalize(),d.rj.copy(u),d.rj.mult(t.radius,d.rj),d.ni.copy(u),
d.ni.negate(d.ni),d.ri.set(0,0,0),this.result.push(d),this.createFrictionEquationsFromContact(d,this.frictionResult)}};var bt=new u,xt=new c,Et=(new c,new c),At=new c,Ct=new c;o.prototype[a.types.PARTICLE|a.types.CONVEXPOLYHEDRON]=o.prototype.convexParticle=function(t,e,i,o,n,s,r,a,h,c,l){var u=-1,p=Et,d=Ct,y=null,v=0,f=xt;if(f.copy(o),f.vsub(i,f),n.conjugate(bt),bt.vmult(f,f),t.pointIsInside(f)){t.worldVerticesNeedsUpdate&&t.computeWorldVertices(i,n),t.worldFaceNormalsNeedsUpdate&&t.computeWorldFaceNormals(n);for(var m=0,g=t.faces.length;m!==g;m++){var w=[t.worldVertices[t.faces[m][0]]],b=t.worldFaceNormals[m];o.vsub(w[0],At);var x=-b.dot(At);if(null===y||Math.abs(x)<Math.abs(y)){if(l)return!0;y=x,u=m,p.copy(b),v++}}if(u!==-1){var E=this.createContactEquation(a,r,e,t,h,c);p.mult(y,d),d.vadd(o,d),d.vsub(i,d),E.rj.copy(d),p.negate(E.ni),E.ri.set(0,0,0);var A=E.ri,C=E.rj;A.vadd(o,A),A.vsub(a.position,A),C.vadd(i,C),C.vsub(r.position,C),this.result.push(E),this.createFrictionEquationsFromContact(E,this.frictionResult)}else console.warn("Point found inside convex, but did not find penetrating face!")}},o.prototype[a.types.BOX|a.types.HEIGHTFIELD]=o.prototype.boxHeightfield=function(t,e,i,o,n,s,r,a,h,c,l){return t.convexPolyhedronRepresentation.material=t.material,t.convexPolyhedronRepresentation.collisionResponse=t.collisionResponse,this.convexHeightfield(t.convexPolyhedronRepresentation,e,i,o,n,s,r,a,t,e,l)};var Bt=new c,St=new c,Rt=[0];o.prototype[a.types.CONVEXPOLYHEDRON|a.types.HEIGHTFIELD]=o.prototype.convexHeightfield=function(t,e,i,o,n,s,r,a,h,c,u){var p=e.data,d=e.elementSize,y=t.boundingSphereRadius,v=St,f=Rt,m=Bt;l.pointToLocalFrame(o,s,i,m);var g=Math.floor((m.x-y)/d)-1,w=Math.ceil((m.x+y)/d)+1,b=Math.floor((m.y-y)/d)-1,x=Math.ceil((m.y+y)/d)+1;if(!(w<0||x<0||g>p.length||b>p[0].length)){g<0&&(g=0),w<0&&(w=0),b<0&&(b=0),x<0&&(x=0),g>=p.length&&(g=p.length-1),w>=p.length&&(w=p.length-1),x>=p[0].length&&(x=p[0].length-1),b>=p[0].length&&(b=p[0].length-1);var E=[];e.getRectMinMax(g,b,w,x,E);var A=E[0],C=E[1];if(!(m.z-y>C||m.z+y<A))for(var B=g;B<w;B++)for(var S=b;S<x;S++){var R=!1;if(e.getConvexTrianglePillar(B,S,!1),l.pointToWorldFrame(o,s,e.pillarOffset,v),i.distanceTo(v)<e.pillarConvex.boundingSphereRadius+t.boundingSphereRadius&&(R=this.convexConvex(t,e.pillarConvex,i,v,n,s,r,a,null,null,u,f,null)),u&&R)return!0;if(e.getConvexTrianglePillar(B,S,!0),l.pointToWorldFrame(o,s,e.pillarOffset,v),i.distanceTo(v)<e.pillarConvex.boundingSphereRadius+t.boundingSphereRadius&&(R=this.convexConvex(t,e.pillarConvex,i,v,n,s,r,a,null,null,u,f,null)),u&&R)return!0}}};var Mt=new c,Tt=new c;o.prototype[a.types.SPHERE|a.types.HEIGHTFIELD]=o.prototype.sphereHeightfield=function(t,e,i,o,n,s,r,a,h,c,u){var p=e.data,d=t.radius,y=e.elementSize,v=Tt,f=Mt;l.pointToLocalFrame(o,s,i,f);var m=Math.floor((f.x-d)/y)-1,g=Math.ceil((f.x+d)/y)+1,w=Math.floor((f.y-d)/y)-1,b=Math.ceil((f.y+d)/y)+1;if(!(g<0||b<0||m>p.length||b>p[0].length)){m<0&&(m=0),g<0&&(g=0),w<0&&(w=0),b<0&&(b=0),m>=p.length&&(m=p.length-1),g>=p.length&&(g=p.length-1),b>=p[0].length&&(b=p[0].length-1),w>=p[0].length&&(w=p[0].length-1);var x=[];e.getRectMinMax(m,w,g,b,x);var E=x[0],A=x[1];if(!(f.z-d>A||f.z+d<E))for(var C=this.result,B=m;B<g;B++)for(var S=w;S<b;S++){var R=C.length,M=!1;if(e.getConvexTrianglePillar(B,S,!1),l.pointToWorldFrame(o,s,e.pillarOffset,v),i.distanceTo(v)<e.pillarConvex.boundingSphereRadius+t.boundingSphereRadius&&(M=this.sphereConvex(t,e.pillarConvex,i,v,n,s,r,a,t,e,u)),u&&M)return!0;if(e.getConvexTrianglePillar(B,S,!0),l.pointToWorldFrame(o,s,e.pillarOffset,v),i.distanceTo(v)<e.pillarConvex.boundingSphereRadius+t.boundingSphereRadius&&(M=this.sphereConvex(t,e.pillarConvex,i,v,n,s,r,a,t,e,u)),u&&M)return!0;var T=C.length-R;if(T>2)return}}}},{"../collision/AABB":46,"../collision/Ray":53,"../equations/ContactEquation":63,"../equations/FrictionEquation":65,"../math/Quaternion":72,"../math/Transform":73,"../math/Vec3":74,"../objects/Body":75,"../shapes/ConvexPolyhedron":82,"../shapes/Shape":87,"../solver/Solver":91,"../utils/Vec3Pool":98}],100:[function(t,e,i){function o(t){t=t||{},h.apply(this),this.dt=-1,this.allowSleep=!!t.allowSleep,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=void 0!==t.quatNormalizeSkip?t.quatNormalizeSkip:0,this.quatNormalizeFast=void 0!==t.quatNormalizeFast&&t.quatNormalizeFast,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.nextId=0,this.gravity=new n,t.gravity&&this.gravity.copy(t.gravity),this.broadphase=void 0!==t.broadphase?t.broadphase:new g,this.bodies=[],this.solver=void 0!==t.solver?t.solver:new r,this.constraints=[],this.narrowphase=new a(this),this.collisionMatrix=new c,this.collisionMatrixPrevious=new c,this.bodyOverlapKeeper=new l,this.shapeOverlapKeeper=new l,this.materials=[],this.contactmaterials=[],this.contactMaterialTable=new y,this.defaultMaterial=new u("default"),this.defaultContactMaterial=new p(this.defaultMaterial,this.defaultMaterial,{friction:.3,restitution:0}),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},this.accumulator=0,this.subsystems=[],this.addBodyEvent={type:"addBody",body:null},this.removeBodyEvent={type:"removeBody",body:null},this.idToBodyMap={},this.broadphase.setWorld(this)}e.exports=o;var n=(t("../shapes/Shape"),t("../math/Vec3")),s=t("../math/Quaternion"),r=t("../solver/GSSolver"),a=(t("../equations/ContactEquation"),t("../equations/FrictionEquation"),t("./Narrowphase")),h=t("../utils/EventTarget"),c=t("../collision/ArrayCollisionMatrix"),l=t("../collision/OverlapKeeper"),u=t("../material/Material"),p=t("../material/ContactMaterial"),d=t("../objects/Body"),y=t("../utils/TupleDictionary"),v=t("../collision/RaycastResult"),f=t("../collision/AABB"),m=t("../collision/Ray"),g=t("../collision/NaiveBroadphase");o.prototype=new h;var w=(new f,new m);if(o.prototype.getContactMaterial=function(t,e){return this.contactMaterialTable.get(t.id,e.id)},o.prototype.numObjects=function(){return this.bodies.length},o.prototype.collisionMatrixTick=function(){var t=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=t,this.collisionMatrix.reset(),this.bodyOverlapKeeper.tick(),this.shapeOverlapKeeper.tick()},o.prototype.add=o.prototype.addBody=function(t){this.bodies.indexOf(t)===-1&&(t.index=this.bodies.length,this.bodies.push(t),t.world=this,t.initPosition.copy(t.position),t.initVelocity.copy(t.velocity),t.timeLastSleepy=this.time,t instanceof d&&(t.initAngularVelocity.copy(t.angularVelocity),t.initQuaternion.copy(t.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=t,this.idToBodyMap[t.id]=t,this.dispatchEvent(this.addBodyEvent))},o.prototype.addConstraint=function(t){this.constraints.push(t)},o.prototype.removeConstraint=function(t){var e=this.constraints.indexOf(t);e!==-1&&this.constraints.splice(e,1)},o.prototype.rayTest=function(t,e,i){i instanceof v?this.raycastClosest(t,e,{skipBackfaces:!0},i):this.raycastAll(t,e,{skipBackfaces:!0},i)},o.prototype.raycastAll=function(t,e,i,o){return i.mode=m.ALL,i.from=t,i.to=e,i.callback=o,w.intersectWorld(this,i)},o.prototype.raycastAny=function(t,e,i,o){return i.mode=m.ANY,i.from=t,i.to=e,i.result=o,w.intersectWorld(this,i)},o.prototype.raycastClosest=function(t,e,i,o){return i.mode=m.CLOSEST,i.from=t,i.to=e,i.result=o,w.intersectWorld(this,i)},o.prototype.remove=function(t){t.world=null;var e=this.bodies.length-1,i=this.bodies,o=i.indexOf(t);if(o!==-1){i.splice(o,1);for(var n=0;n!==i.length;n++)i[n].index=n;this.collisionMatrix.setNumObjects(e),this.removeBodyEvent.body=t,delete this.idToBodyMap[t.id],this.dispatchEvent(this.removeBodyEvent)}},o.prototype.removeBody=o.prototype.remove,o.prototype.getBodyById=function(t){return this.idToBodyMap[t]},o.prototype.getShapeById=function(t){for(var e=this.bodies,i=0,o=e.length;i<o;i++)for(var n=e[i].shapes,s=0,r=n.length;s<r;s++){var a=n[s];if(a.id===t)return a}},o.prototype.addMaterial=function(t){this.materials.push(t)},o.prototype.addContactMaterial=function(t){this.contactmaterials.push(t),this.contactMaterialTable.set(t.materials[0].id,t.materials[1].id,t)},"undefined"==typeof performance&&(performance={}),!performance.now){var b=Date.now();performance.timing&&performance.timing.navigationStart&&(b=performance.timing.navigationStart),performance.now=function(){return Date.now()-b}}new n;o.prototype.step=function(t,e,i){if(i=i||10,e=e||0,0===e)this.internalStep(t),this.time+=t;else{this.accumulator+=e;for(var o=0;this.accumulator>=t&&o<i;)this.internalStep(t),this.accumulator-=t,o++;for(var n=this.accumulator%t/t,s=0;s!==this.bodies.length;s++){var r=this.bodies[s];r.previousPosition.lerp(r.position,n,r.interpolatedPosition),r.previousQuaternion.slerp(r.quaternion,n,r.interpolatedQuaternion),r.previousQuaternion.normalize()}this.time+=e}};var x={type:"postStep"},E={type:"preStep"},A={type:d.COLLIDE_EVENT_NAME,body:null,contact:null},C=[],B=[],S=[],R=[];new n,new n,new n,new n,new n,new n,new n,new n,new n,new s,new s,new s,new n;o.prototype.internalStep=function(t){this.dt=t;var e,i=this.contacts,o=S,n=R,s=this.numObjects(),r=this.bodies,a=this.solver,h=this.gravity,c=this.doProfiling,l=this.profile,u=d.DYNAMIC,p=this.constraints,y=B,v=(h.norm(),h.x),f=h.y,m=h.z,g=0;for(c&&(e=performance.now()),g=0;g!==s;g++){var w=r[g];if(w.type===u){var b=w.force,M=w.mass;b.x+=M*v,b.y+=M*f,b.z+=M*m}}for(var g=0,T=this.subsystems.length;g!==T;g++)this.subsystems[g].update();c&&(e=performance.now()),o.length=0,n.length=0,this.broadphase.collisionPairs(this,o,n),c&&(l.broadphase=performance.now()-e);var z=p.length;for(g=0;g!==z;g++){var k=p[g];if(!k.collideConnected)for(var L=o.length-1;L>=0;L-=1)(k.bodyA===o[L]&&k.bodyB===n[L]||k.bodyB===o[L]&&k.bodyA===n[L])&&(o.splice(L,1),n.splice(L,1))}this.collisionMatrixTick(),c&&(e=performance.now());var P=C,q=i.length;for(g=0;g!==q;g++)P.push(i[g]);i.length=0;var V=this.frictionEquations.length;for(g=0;g!==V;g++)y.push(this.frictionEquations[g]);this.frictionEquations.length=0,this.narrowphase.getContacts(o,n,this,i,P,this.frictionEquations,y),c&&(l.narrowphase=performance.now()-e),c&&(e=performance.now());for(var g=0;g<this.frictionEquations.length;g++)a.addEquation(this.frictionEquations[g]);for(var F=i.length,N=0;N!==F;N++){var I,k=i[N],w=k.bi,O=k.bj,H=k.si,j=k.sj;I=w.material&&O.material?this.getContactMaterial(w.material,O.material)||this.defaultContactMaterial:this.defaultContactMaterial;var D=I.friction;if(w.material&&O.material&&(w.material.friction>=0&&O.material.friction>=0&&(D=w.material.friction*O.material.friction),w.material.restitution>=0&&O.material.restitution>=0&&(k.restitution=w.material.restitution*O.material.restitution)),a.addEquation(k),w.allowSleep&&w.type===d.DYNAMIC&&w.sleepState===d.SLEEPING&&O.sleepState===d.AWAKE&&O.type!==d.STATIC){var W=O.velocity.norm2()+O.angularVelocity.norm2(),K=Math.pow(O.sleepSpeedLimit,2);W>=2*K&&(w._wakeUpAfterNarrowphase=!0)}if(O.allowSleep&&O.type===d.DYNAMIC&&O.sleepState===d.SLEEPING&&w.sleepState===d.AWAKE&&w.type!==d.STATIC){var _=w.velocity.norm2()+w.angularVelocity.norm2(),U=Math.pow(w.sleepSpeedLimit,2);_>=2*U&&(O._wakeUpAfterNarrowphase=!0)}this.collisionMatrix.set(w,O,!0),this.collisionMatrixPrevious.get(w,O)||(A.body=O,A.contact=k,w.dispatchEvent(A),A.body=w,O.dispatchEvent(A)),this.bodyOverlapKeeper.set(w.id,O.id),this.shapeOverlapKeeper.set(H.id,j.id)}for(this.emitContactEvents(),c&&(l.makeContactConstraints=performance.now()-e,e=performance.now()),g=0;g!==s;g++){var w=r[g];w._wakeUpAfterNarrowphase&&(w.wakeUp(),w._wakeUpAfterNarrowphase=!1)}var z=p.length;for(g=0;g!==z;g++){var k=p[g];k.update();for(var L=0,G=k.equations.length;L!==G;L++){var X=k.equations[L];a.addEquation(X)}}a.solve(t,this),c&&(l.solve=performance.now()-e),a.removeAllEquations();var Y=Math.pow;for(g=0;g!==s;g++){var w=r[g];if(w.type&u){var Q=Y(1-w.linearDamping,t),Z=w.velocity;Z.mult(Q,Z);var J=w.angularVelocity;if(J){var $=Y(1-w.angularDamping,t);J.mult($,J)}}}for(this.dispatchEvent(E),g=0;g!==s;g++){var w=r[g];w.preStep&&w.preStep.call(w)}c&&(e=performance.now());var tt=this.stepnumber,et=tt%(this.quatNormalizeSkip+1)===0,it=this.quatNormalizeFast;for(g=0;g!==s;g++)r[g].integrate(t,et,it);for(this.clearForces(),this.broadphase.dirty=!0,c&&(l.integrate=performance.now()-e),this.time+=t,this.stepnumber+=1,this.dispatchEvent(x),g=0;g!==s;g++){var w=r[g],ot=w.postStep;ot&&ot.call(w)}if(this.allowSleep)for(g=0;g!==s;g++)r[g].sleepTick(this.time)},o.prototype.emitContactEvents=function(){var t=[],e=[],i={type:"beginContact",bodyA:null,bodyB:null},o={type:"endContact",bodyA:null,bodyB:null},n={type:"beginShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null},s={type:"endShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null};return function(){var r=this.hasAnyEventListener("beginContact"),a=this.hasAnyEventListener("endContact");if((r||a)&&this.bodyOverlapKeeper.getDiff(t,e),r){for(var h=0,c=t.length;h<c;h+=2)i.bodyA=this.getBodyById(t[h]),i.bodyB=this.getBodyById(t[h+1]),this.dispatchEvent(i);i.bodyA=i.bodyB=null}if(a){for(var h=0,c=e.length;h<c;h+=2)o.bodyA=this.getBodyById(e[h]),o.bodyB=this.getBodyById(e[h+1]),this.dispatchEvent(o);o.bodyA=o.bodyB=null}t.length=e.length=0;var l=this.hasAnyEventListener("beginShapeContact"),u=this.hasAnyEventListener("endShapeContact");if((l||u)&&this.shapeOverlapKeeper.getDiff(t,e),l){for(var h=0,c=t.length;h<c;h+=2){var p=this.getShapeById(t[h]),d=this.getShapeById(t[h+1]);n.shapeA=p,n.shapeB=d,n.bodyA=p.body,n.bodyB=d.body,this.dispatchEvent(n)}n.bodyA=n.bodyB=n.shapeA=n.shapeB=null}if(u){for(var h=0,c=e.length;h<c;h+=2){var p=this.getShapeById(e[h]),d=this.getShapeById(e[h+1]);s.shapeA=p,s.shapeB=d,s.bodyA=p.body,s.bodyB=d.body,this.dispatchEvent(s)}s.bodyA=s.bodyB=s.shapeA=s.shapeB=null}}}(),o.prototype.clearForces=function(){for(var t=this.bodies,e=t.length,i=0;i!==e;i++){var o=t[i];o.force,o.torque;o.force.set(0,0,0),o.torque.set(0,0,0)}}},{"../collision/AABB":46,"../collision/ArrayCollisionMatrix":47,"../collision/NaiveBroadphase":50,"../collision/OverlapKeeper":52,"../collision/Ray":53,"../collision/RaycastResult":54,"../equations/ContactEquation":63,"../equations/FrictionEquation":65,"../material/ContactMaterial":68,"../material/Material":69,"../math/Quaternion":72,"../math/Vec3":74,"../objects/Body":75,"../shapes/Shape":87,"../solver/GSSolver":90,"../utils/EventTarget":93,"../utils/TupleDictionary":96,"./Narrowphase":99}]},{},[1]);
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no,user-scalable=no,maximum-scale=1">
<title>Examples • Vive</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r81/three.min.js"></script>
<script src="aframe.min.js"></script>
<script src="aframe-extras.js"></script>
</head>
<body>
<a-scene physics="debug: true">
<!-- Player -->
<a-entity camera look-controls></a-entity>
<a-entity static-body="shape: sphere; sphereRadius: 0.02;" vive-controls="hand: left" sphere-collider="objects: .cube;" grab></a-entity>
<a-entity static-body="shape: sphere; sphereRadius: 0.02;" vive-controls="hand: right" sphere-collider="objects: .cube;" grab></a-entity>
<a-box class="cube" dynamic-body position="0 0.25 -1" width="0.5" height="0.5" depth="0.5" color="red"></a-box>
<a-box class="cube" dynamic-body position="-1 0.25 -1" width="0.5" height="0.5" depth="0.5" color="green"></a-box>
<a-box class="cube" dynamic-body position="1 0.25 -1" width="0.5" height="0.5" depth="0.5" color="blue"></a-box>
<!-- Terrain -->
<a-grid static-body></a-grid>
<!-- Lighting -->
<a-entity light="type: point; color: #f4f4f4; intensity: 0.2; distance: 0" position="8 10 18"></a-entity>
<a-entity light="type: point; color: #f4f4f4; intensity: 0.6; distance: 0" position="-8 10 -18"></a-entity>
<a-entity light="type: ambient; color: #f4f4f4; intensity: 0.4;" position="-8 10 -18"></a-entity>
</a-scene>
</body>
</html>
This file has been truncated, but you can view the full file.
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.AFRAME=e()}}(function(){var t;return function i(e,t,r){function n(o,s){if(!t[o]){if(!e[o]){var c="function"==typeof require&&require;if(!s&&c)return c(o,!0);if(a)return a(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var l=t[o]={exports:{}};e[o][0].call(l.exports,function(t){var i=e[o][1][t];return n(i?i:t)},l,l.exports,i,e,t,r)}return t[o].exports}for(var a="function"==typeof require&&require,o=0;o<r.length;o++)n(r[o]);return n}({1:[function(e,t){"use strict";t.exports={createLink:function(e,t){var i=document.head||document.getElementsByTagName("head")[0],r=document.createElement("link");r.href=e,r.rel="stylesheet";for(var n in t)if(t.hasOwnProperty(n)){var a=t[n];r.setAttribute("data-"+n,a)}i.appendChild(r)},createStyle:function(e,t){var i=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css";for(var n in t)if(t.hasOwnProperty(n)){var a=t[n];r.setAttribute("data-"+n,a)}r.sheet?(r.innerHTML=e,r.sheet.cssText=e,i.appendChild(r)):r.styleSheet?(i.appendChild(r),r.styleSheet.cssText=e):(r.appendChild(document.createTextNode(e)),i.appendChild(r))}}},{}],2:[function(e,t,i){function r(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31}function n(){var e=arguments,t=this.useColors;if(e[0]=(t?"%c":"")+this.namespace+(t?" %c":" ")+e[0]+(t?"%c ":" ")+"+"+i.humanize(this.diff),!t)return e;var r="color: "+this.color;e=[e[0],r,"color: inherit"].concat(Array.prototype.slice.call(e,1));var n=0,a=0;return e[0].replace(/%[a-z%]/g,function(e){"%%"!==e&&(n++,"%c"===e&&(a=n))}),e.splice(a,0,r),e}function a(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function o(e){try{null==e?i.storage.removeItem("debug"):i.storage.debug=e}catch(t){}}function s(){var e;try{e=i.storage.debug}catch(t){}return e}function c(){try{return window.localStorage}catch(e){}}i=t.exports=e("./debug"),i.log=a,i.formatArgs=n,i.save=o,i.load=s,i.useColors=r,i.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:c(),i.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],i.formatters.j=function(e){return JSON.stringify(e)},i.enable(s())},{"./debug":3}],3:[function(e,t,i){function r(){return i.colors[l++%i.colors.length]}function n(e){function t(){}function n(){var e=n,t=+new Date,a=t-(u||t);e.diff=a,e.prev=u,e.curr=t,u=t,null==e.useColors&&(e.useColors=i.useColors()),null==e.color&&e.useColors&&(e.color=r());var o=Array.prototype.slice.call(arguments);o[0]=i.coerce(o[0]),"string"!=typeof o[0]&&(o=["%o"].concat(o));var s=0;o[0]=o[0].replace(/%([a-z%])/g,function(t,r){if("%%"===t)return t;s++;var n=i.formatters[r];if("function"==typeof n){var a=o[s];t=n.call(e,a),o.splice(s,1),s--}return t}),"function"==typeof i.formatArgs&&(o=i.formatArgs.apply(e,o));var c=n.log||i.log||console.log.bind(console);c.apply(e,o)}t.enabled=!1,n.enabled=!0;var a=i.enabled(e)?n:t;return a.namespace=e,a}function a(e){i.save(e);for(var t=(e||"").split(/[\s,]+/),r=t.length,n=0;r>n;n++)t[n]&&(e=t[n].replace(/\*/g,".*?"),"-"===e[0]?i.skips.push(new RegExp("^"+e.substr(1)+"$")):i.names.push(new RegExp("^"+e+"$")))}function o(){i.enable("")}function s(e){var t,r;for(t=0,r=i.skips.length;r>t;t++)if(i.skips[t].test(e))return!1;for(t=0,r=i.names.length;r>t;t++)if(i.names[t].test(e))return!0;return!1}function c(e){return e instanceof Error?e.stack||e.message:e}i=t.exports=n,i.coerce=c,i.disable=o,i.enable=a,i.enabled=s,i.humanize=e("ms"),i.names=[],i.skips=[],i.formatters={};var u,l=0},{ms:7}],4:[function(e,t){"use strict";function i(e){if(null===e||void 0===e)throw new TypeError("Sources cannot be null or undefined");return Object(e)}function r(e,t,i){var r=t[i];if(void 0!==r&&null!==r){if(o.call(e,i)&&(void 0===e[i]||null===e[i]))throw new TypeError("Cannot convert undefined or null to object ("+i+")");e[i]=o.call(e,i)&&a(r)?n(Object(e[i]),t[i]):r}}function n(e,t){if(e===t)return e;t=Object(t);for(var i in t)o.call(t,i)&&r(e,t,i);if(Object.getOwnPropertySymbols)for(var n=Object.getOwnPropertySymbols(t),a=0;a<n.length;a++)s.call(t,n[a])&&r(e,t,n[a]);return e}var a=e("is-obj"),o=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable;t.exports=function(e){e=i(e);for(var t=1;t<arguments.length;t++)n(e,arguments[t]);return e}},{"is-obj":6}],5:[function(){!function(t,i,r,n){"use strict";function a(e,t){for(var i=0,r=e.length;r>i;i++)v(e[i],t)}function o(e){for(var t,i=0,r=e.length;r>i;i++)t=e[i],b(t,P[c(t)])}function s(e){return function(t){nt(t)&&(v(t,e),a(t.querySelectorAll(F),e))}}function c(e){var t=st.call(e,"is"),i=e.nodeName.toUpperCase(),r=V.call(U,t?k+t.toUpperCase():z+i);return t&&r>-1&&!u(i,t)?-1:r}function u(e,t){return-1<F.indexOf(e+'[is="'+t+'"]')}function l(e){var t=e.currentTarget,i=e.attrChange,r=e.attrName,n=e.target;vt&&(!n||n===t)&&t.attributeChangedCallback&&"style"!==r&&e.prevValue!==e.newValue&&t.attributeChangedCallback(r,i===e[S]?null:e.prevValue,i===e[I]?null:e.newValue)}function h(e){var t=s(e);return function(e){y.push(t,e.target)}}function d(e){gt&&(gt=!1,e.currentTarget.removeEventListener(_,d)),a((e.target||i).querySelectorAll(F),e.detail===N?N:L),rt&&m()}function p(e,t){var i=this;ct.call(i,e,t),M.call(i,{target:i})}function f(e,t){et(e,t),w?w.observe(e,ht):(mt&&(e.setAttribute=p,e[T]=x(e),e.addEventListener(O,M)),e.addEventListener(j,l)),e.createdCallback&&vt&&(e.created=!0,e.createdCallback(),e.created=!1)}function m(){for(var e,t=0,i=at.length;i>t;t++)e=at[t],G.contains(e)||(i--,at.splice(t--,1),v(e,N))}function g(e){throw new Error("A "+e+" type is already registered")}function v(e,t){var i,r=c(e);r>-1&&(E(e,P[r]),r=0,t!==L||e[L]?t===N&&!e[N]&&(e[L]=!1,e[N]=!0,r=1):(e[N]=!1,e[L]=!0,r=1,rt&&V.call(at,e)<0&&at.push(e)),r&&(i=e[t+"Callback"])&&i.call(e))}if(!(n in i)){var y,M,A,x,w,E,b,T="__"+n+(1e5*Math.random()>>0),L="attached",N="detached",D="extends",S="ADDITION",C="MODIFICATION",I="REMOVAL",j="DOMAttrModified",_="DOMContentLoaded",O="DOMSubtreeModified",z="<",k="=",R=/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,B=["ANNOTATION-XML","COLOR-PROFILE","FONT-FACE","FONT-FACE-SRC","FONT-FACE-URI","FONT-FACE-FORMAT","FONT-FACE-NAME","MISSING-GLYPH"],U=[],P=[],F="",G=i.documentElement,V=U.indexOf||function(e){for(var t=this.length;t--&&this[t]!==e;);return t},Q=r.prototype,Y=Q.hasOwnProperty,H=Q.isPrototypeOf,W=r.defineProperty,X=r.getOwnPropertyDescriptor,q=r.getOwnPropertyNames,Z=r.getPrototypeOf,K=r.setPrototypeOf,J=!!r.__proto__,$=r.create||function yt(e){return e?(yt.prototype=e,new yt):this},et=K||(J?function(e,t){return e.__proto__=t,e}:q&&X?function(){function e(e,t){for(var i,r=q(t),n=0,a=r.length;a>n;n++)i=r[n],Y.call(e,i)||W(e,i,X(t,i))}return function(t,i){do e(t,i);while((i=Z(i))&&!H.call(i,t));return t}}():function(e,t){for(var i in t)e[i]=t[i];return e}),tt=t.MutationObserver||t.WebKitMutationObserver,it=(t.HTMLElement||t.Element||t.Node).prototype,rt=!H.call(it,G),nt=rt?function(e){return 1===e.nodeType}:function(e){return H.call(it,e)},at=rt&&[],ot=it.cloneNode,st=it.getAttribute,ct=it.setAttribute,ut=it.removeAttribute,lt=i.createElement,ht=tt&&{attributes:!0,characterData:!0,attributeOldValue:!0},dt=tt||function(){mt=!1,G.removeEventListener(j,dt)},pt=t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.msRequestAnimationFrame||function(e){setTimeout(e,10)},ft=!1,mt=!0,gt=!0,vt=!0;K||J?(E=function(e,t){H.call(t,e)||f(e,t)},b=f):(E=function(e,t){e[T]||(e[T]=r(!0),f(e,t))},b=E),rt?(mt=!1,function(){var t=X(it,"addEventListener"),i=t.value,r=function(e){var t=new CustomEvent(j,{bubbles:!0});t.attrName=e,t.prevValue=st.call(this,e),t.newValue=null,t[I]=t.attrChange=2,ut.call(this,e),this.dispatchEvent(t)},n=function(t,i){var r=this.hasAttribute(t),n=r&&st.call(this,t);e=new CustomEvent(j,{bubbles:!0}),ct.call(this,t,i),e.attrName=t,e.prevValue=r?n:null,e.newValue=i,r?e[C]=e.attrChange=1:e[S]=e.attrChange=0,this.dispatchEvent(e)},a=function(e){var t,i=e.currentTarget,r=i[T],n=e.propertyName;r.hasOwnProperty(n)&&(r=r[n],t=new CustomEvent(j,{bubbles:!0}),t.attrName=r.name,t.prevValue=r.value||null,t.newValue=r.value=i[n]||null,null==t.prevValue?t[S]=t.attrChange=0:t[C]=t.attrChange=1,i.dispatchEvent(t))};t.value=function(e,t,o){e===j&&this.attributeChangedCallback&&this.setAttribute!==n&&(this[T]={className:{name:"class",value:this.className}},this.setAttribute=n,this.removeAttribute=r,i.call(this,"propertychange",a)),i.call(this,e,t,o)},W(it,"addEventListener",t)}()):tt||(G.addEventListener(j,dt),G.setAttribute(T,1),G.removeAttribute(T),mt&&(M=function(e){var t,i,r,n=this;if(n===e.target){t=n[T],n[T]=i=x(n);for(r in i){if(!(r in t))return A(0,n,r,t[r],i[r],S);if(i[r]!==t[r])return A(1,n,r,t[r],i[r],C)}for(r in t)if(!(r in i))return A(2,n,r,t[r],i[r],I)}},A=function(e,t,i,r,n,a){var o={attrChange:e,currentTarget:t,attrName:i,prevValue:r,newValue:n};o[a]=e,l(o)},x=function(e){for(var t,i,r={},n=e.attributes,a=0,o=n.length;o>a;a++)t=n[a],i=t.name,"setAttribute"!==i&&(r[i]=t.value);return r})),i[n]=function(e,t){if(r=e.toUpperCase(),ft||(ft=!0,tt?(w=function(e,t){function i(e,t){for(var i=0,r=e.length;r>i;t(e[i++]));}return new tt(function(r){for(var n,a,o,s=0,c=r.length;c>s;s++)n=r[s],"childList"===n.type?(i(n.addedNodes,e),i(n.removedNodes,t)):(a=n.target,vt&&a.attributeChangedCallback&&"style"!==n.attributeName&&(o=st.call(a,n.attributeName),o!==n.oldValue&&a.attributeChangedCallback(n.attributeName,n.oldValue,o)))})}(s(L),s(N)),w.observe(i,{childList:!0,subtree:!0})):(y=[],pt(function v(){for(;y.length;)y.shift().call(null,y.shift());pt(v)}),i.addEventListener("DOMNodeInserted",h(L)),i.addEventListener("DOMNodeRemoved",h(N))),i.addEventListener(_,d),i.addEventListener("readystatechange",d),i.createElement=function(e,t){var r=lt.apply(i,arguments),n=""+e,a=V.call(U,(t?k:z)+(t||n).toUpperCase()),o=a>-1;return t&&(r.setAttribute("is",t=t.toLowerCase()),o&&(o=u(n.toUpperCase(),t))),vt=!i.createElement.innerHTMLHelper,o&&b(r,P[a]),r},it.cloneNode=function(e){var t=ot.call(this,!!e),i=c(t);return i>-1&&b(t,P[i]),e&&o(t.querySelectorAll(F)),t}),-2<V.call(U,k+r)+V.call(U,z+r)&&g(e),!R.test(r)||-1<V.call(B,r))throw new Error("The type "+e+" is invalid");var r,n,l=function(){return f?i.createElement(m,r):i.createElement(m)},p=t||Q,f=Y.call(p,D),m=f?t[D].toUpperCase():r;return f&&-1<V.call(U,z+m)&&g(m),n=U.push((f?k:z)+r)-1,F=F.concat(F.length?",":"",f?m+'[is="'+e.toLowerCase()+'"]':m),l.prototype=P[n]=Y.call(p,"prototype")?p.prototype:$(it),a(i.querySelectorAll(F),L),l}}}(window,document,Object,"registerElement")},{}],6:[function(e,t){"use strict";t.exports=function(e){var t=typeof e;return null!==e&&("object"===t||"function"===t)}},{}],7:[function(e,t){function i(e){if(e=""+e,!(e.length>1e4)){var t=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var i=parseFloat(t[1]),r=(t[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"yrs":case"yr":case"y":return i*l;case"days":case"day":case"d":return i*u;case"hours":case"hour":case"hrs":case"hr":case"h":return i*c;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*o;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i}}}}function r(e){return e>=u?Math.round(e/u)+"d":e>=c?Math.round(e/c)+"h":e>=s?Math.round(e/s)+"m":e>=o?Math.round(e/o)+"s":e+"ms"}function n(e){return a(e,u,"day")||a(e,c,"hour")||a(e,s,"minute")||a(e,o,"second")||e+" ms"}function a(e,t,i){return t>e?void 0:1.5*t>e?Math.floor(e/t)+" "+i:Math.ceil(e/t)+" "+i+"s"}var o=1e3,s=60*o,c=60*s,u=24*c,l=365.25*u;t.exports=function(e,t){return t=t||{},"string"==typeof e?i(e):t.long?n(e):r(e)}},{}],8:[function(e,t){"use strict";function i(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}function r(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},i=0;10>i;i++)t["_"+String.fromCharCode(i)]=i;var r=Object.getOwnPropertyNames(t).map(function(e){return t[e]});if("0123456789"!==r.join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(e){n[e]=e}),"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},n)).join("")?!1:!0}catch(a){return!1}}var n=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;t.exports=r()?Object.assign:function(e){for(var t,r,o=i(e),s=1;s<arguments.length;s++){t=Object(arguments[s]);for(var c in t)n.call(t,c)&&(o[c]=t[c]);if(Object.getOwnPropertySymbols){r=Object.getOwnPropertySymbols(t);for(var u=0;u<r.length;u++)a.call(t,r[u])&&(o[r[u]]=t[r[u]])}}return o}},{}],9:[function(e,t){(function(e){var i=e.performance||{},r=function(){for(var e=["now","webkitNow","msNow","mozNow","oNow"];e.length;){var t=e.shift();if(t in i)return i[t].bind(i)}var r=Date.now||function(){return(new Date).getTime()},n=(i.timing||{}).navigationStart||r();return function(){return r()-n}}();r.performanceNow=i.now,r.noConflict=function(){i.now=r.performanceNow},r.conflict=function(){i.now=r},r.conflict(),t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],10:[function(e,t){function i(){h&&u&&(h=!1,u.length?l=u.concat(l):d=-1,l.length&&r())}function r(){if(!h){var e=o(i);h=!0;for(var t=l.length;t;){for(u=l,l=[];++d<t;)u&&u[d].run();d=-1,t=l.length}u=null,h=!1,s(e)}}function n(e,t){this.fun=e,this.array=t}function a(){}var o,s,c=t.exports={};!function(){try{o=setTimeout}catch(e){o=function(){throw new Error("setTimeout is not defined")}}try{s=clearTimeout}catch(e){s=function(){throw new Error("clearTimeout is not defined")}}}();var u,l=[],h=!1,d=-1;c.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var i=1;i<arguments.length;i++)t[i-1]=arguments[i];l.push(new n(e,t)),1!==l.length||h||o(r,0)},n.prototype.run=function(){this.fun.apply(null,this.array)},c.title="browser",c.browser=!0,c.env={},c.argv=[],c.version="",c.versions={},c.on=a,c.addListener=a,c.once=a,c.off=a,c.removeListener=a,c.removeAllListeners=a,c.emit=a,c.binding=function(){throw new Error("process.binding is not supported")},c.cwd=function(){return"/"},c.chdir=function(){throw new Error("process.chdir is not supported")},c.umask=function(){return 0}},{}],11:[function(e,t){!function(e){function i(e,t){return function(){e.apply(t,arguments)}}function r(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],u(e,i(a,this),i(o,this))}function n(e){var t=this;return null===this._state?(this._deferreds.push(e),void 0):(h(function(){var i=t._state?e.onFulfilled:e.onRejected;if(null===i)return(t._state?e.resolve:e.reject)(t._value),void 0;var r;try{r=i(t._value)}catch(n){return e.reject(n),void 0}e.resolve(r)}),void 0)}function a(e){try{if(e===this)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var t=e.then;if("function"==typeof t)return u(i(t,e),i(a,this),i(o,this)),void 0}this._state=!0,this._value=e,s.call(this)}catch(r){o.call(this,r)}}function o(e){this._state=!1,this._value=e,s.call(this)}function s(){for(var e=0,t=this._deferreds.length;t>e;e++)n.call(this,this._deferreds[e]);this._deferreds=null}function c(e,t,i,r){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=i,this.reject=r}function u(e,t,i){var r=!1;try{e(function(e){r||(r=!0,t(e))},function(e){r||(r=!0,i(e))})}catch(n){if(r)return;r=!0,i(n)}}var l=setTimeout,h="function"==typeof setImmediate&&setImmediate||function(e){l(e,1)},d=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};r.prototype["catch"]=function(e){return this.then(null,e)},r.prototype.then=function(e,t){var i=this;return new r(function(r,a){n.call(i,new c(e,t,r,a))})},r.all=function(){var e=Array.prototype.slice.call(1===arguments.length&&d(arguments[0])?arguments[0]:arguments);return new r(function(t,i){function r(a,o){try{if(o&&("object"==typeof o||"function"==typeof o)){var s=o.then;if("function"==typeof s)return s.call(o,function(e){r(a,e)},i),void 0}e[a]=o,0===--n&&t(e)}catch(c){i(c)}}if(0===e.length)return t([]);for(var n=e.length,a=0;a<e.length;a++)r(a,e[a])})},r.resolve=function(e){return e&&"object"==typeof e&&e.constructor===r?e:new r(function(t){t(e)})},r.reject=function(e){return new r(function(t,i){i(e)})},r.race=function(e){return new r(function(t,i){for(var r=0,n=e.length;n>r;r++)e[r].then(t,i)})},r._setImmediateFn=function(e){h=e},"undefined"!=typeof t&&t.exports?t.exports=r:e.Promise||(e.Promise=r)}(this)},{}],12:[function(e,t){function i(e){var t=function(e){return e.trim()},i={};return r(e).map(t).filter(Boolean).forEach(function(e){var t=e.indexOf(":"),r=e.substr(0,t).trim(),n=e.substr(t+1).trim();i[r]=n}),i}function r(e){for(var t,i=[],r=0,n=";",a=/url\([^\)]+$/,o="";r<e.length;)t=e.indexOf(n,r),-1===t&&(t=e.length),o+=e.substring(r,t),a.test(o)?(o+=";",r=t+1):(i.push(o),o="",r=t+1);return i}function n(e){return Object.keys(e).map(function(t){return t+":"+e[t]}).join(";")}function a(e){return n(i(e))}t.exports.parse=i,t.exports.stringify=n,t.exports.normalize=a},{}],13:[function(){THREE.BlendCharacter=function(){this.weightSchedule=[],this.warpSchedule=[],this.load=function(e,t){var i=this,r=new THREE.ObjectLoader;r.load(e,function(r){if(r.traverse(function(e){e instanceof THREE.SkinnedMesh&&(i.skinnedMesh=e)}),THREE.SkinnedMesh.call(i,i.skinnedMesh.geometry,i.skinnedMesh.material),void 0==i.skinnedMesh)return console.log("unable to find skinned mesh in "+e),void 0;i.material.skinning=!0,i.mixer=new THREE.AnimationMixer(i),i.mixer=i.mixer;for(var n=0;n<i.geometry.animations.length;++n)i.mixer.clipAction(i.geometry.animations[n]);void 0!==t&&t()})},this.update=function(e){this.mixer.update(e)},this.play=function(e,t){return this.mixer.clipAction(e).setEffectiveWeight(t).play()},this.crossfade=function(e,t,i){this.mixer.stopAllAction();var r=this.play(e,1),n=this.play(t,1);r.crossFadeTo(n,i,!1)},this.warp=function(e,t,i){this.mixer.stopAllAction();var r=this.play(e,1),n=this.play(t,1);r.crossFadeTo(n,i,!0)},this.applyWeight=function(e,t){this.mixer.clipAction(e).setEffectiveWeight(t)},this.getWeight=function(e){return this.mixer.clipAction(e).getEffectiveWeight()},this.pauseAll=function(){this.mixer.timeScale=0},this.unPauseAll=function(){this.mixer.timeScale=1},this.stopAll=function(){this.mixer.stopAllAction()},this.showModel=function(e){this.visible=e}},THREE.BlendCharacter.prototype=Object.create(THREE.SkinnedMesh.prototype),THREE.BlendCharacter.prototype.constructor=THREE.BlendCharacter,THREE.BlendCharacter.prototype.getForward=function(){var e=new THREE.Vector3;return function(){return e.set(-this.matrix.elements[8],-this.matrix.elements[9],-this.matrix.elements[10]),e}}},{}],14:[function(){THREE.ColladaLoader=function(){function e(e,i,r,n){var a=0;if(document.implementation&&document.implementation.createDocument){var o=new XMLHttpRequest;o.onreadystatechange=function(){4===o.readyState?0===o.status||200===o.status?o.response?(Bt=i,t(o.response,void 0,e)):n?n({type:"error",url:e}):console.error("ColladaLoader: Empty or non-existing file ("+e+")"):n?n({type:"error",url:e}):console.error("ColladaLoader: Couldn't load \""+e+'" ('+o.status+")"):3===o.readyState&&r&&(0===a&&(a=o.getResponseHeader("Content-Length")),r({total:a,loaded:o.responseText.length}))},o.open("GET",e,!0),o.send(null)}else alert("Don't know how to parse XML!")}function t(e,t,i){if(kt=(new DOMParser).parseFromString(e,"text/xml"),t=t||Bt,void 0!==i){var c=i.split("/");c.pop(),_t=(c.length<1?".":c.join("/"))+"/"}r(),At(),Pt=n("library_images image",L,"image"),Qt=n("library_materials material",H,"material"),Yt=n("library_effects effect",K,"effect"),Vt=n("library_geometries geometry",k,"geometry"),Ht=n("library_cameras camera",rt,"camera"),Wt=n("library_lights light",at,"light"),Gt=n("library_controllers controller",N,"controller"),Ft=n("library_animations animation",$,"animation"),It=n("library_visual_scenes visual_scene",C,"visual_scene"),jt=n("library_kinematics_models kinematics_model",st,"kinematics_model"),Ot=[],zt=[],Nt=a(),Rt=new THREE.Group;for(var u=0;u<Nt.nodes.length;u++)Rt.add(v(Nt.nodes[u]));Rt.scale.multiplyScalar(Zt),s(),Dt=o(),g();var l={scene:Rt,morphs:Ot,skins:zt,animations:St,kinematics:Ct,dae:{images:Pt,materials:Qt,cameras:Ht,lights:Wt,effects:Yt,geometries:Vt,controllers:Gt,animations:Ft,visualScenes:It,visualScene:Nt,scene:Nt,kinematicsModels:jt,kinematicsModel:Dt}};return t&&t(l),l}function i(e){Xt=e}function r(){var e=kt.querySelectorAll("asset"),t=e[0];if(t&&t.childNodes)for(var i=0;i<t.childNodes.length;i++){var r=t.childNodes[i];switch(r.nodeName){case"unit":var n=r.getAttribute("meter");n&&(Zt=parseFloat(n));break;case"up_axis":Kt=r.textContent.charAt(0)}}}function n(e,t,i){for(var r=kt.querySelectorAll(e),n={},a=0,o=r.length,s=0;o>s;s++){var c=r[s],u=(new t).parse(c);u.id&&0!==u.id.length||(u.id=i+a++),n[u.id]=u}return n}function a(){var e=kt.querySelectorAll("scene instance_visual_scene")[0];if(e){var t=e.getAttribute("url").replace(/^#/,"");return It[t.length>0?t:"visual_scene0"]}return null}function o(){var e=kt.querySelectorAll("instance_kinematics_model")[0];if(e){var t=e.getAttribute("url").replace(/^#/,"");return jt[t.length>0?t:"kinematics_model0"]}return null}function s(){St=[],c(Rt)}function c(e){var t=Nt.getChildById(e.colladaId,!0),i=null;if(t&&t.keys){i={fps:60,hierarchy:[{node:t,keys:t.keys,sids:t.sids}],node:e,name:"animation_"+e.name,length:0},St.push(i);for(var r=0,n=t.keys.length;n>r;r++)i.length=Math.max(i.length,t.keys[r].time)}else i={hierarchy:[{keys:[],sids:[]}]};for(var r=0,n=e.children.length;n>r;r++)for(var a=c(e.children[r]),o=0,s=a.hierarchy.length;s>o;o++)i.hierarchy.push({keys:[],sids:[]});return i}function u(){var e,t=1e6,i=-t,r=0;for(var n in Ft){var a=Ft[n];e=e||a.id;for(var o=0;o<a.sampler.length;o++){var s=a.sampler[o];s.create(),t=Math.min(t,s.startTime),i=Math.max(i,s.endTime),r=Math.max(r,s.input.length)}}return{start:t,end:i,frames:r,ID:e}}function l(e,t){var i=t instanceof _?Gt[t.url]:t;if(!i||!i.morph)return console.log("could not find morph controller!"),void 0;for(var r=i.morph,n=0;n<r.targets.length;n++){var a=r.targets[n],o=Vt[a];if(o.mesh&&o.mesh.primitives&&o.mesh.primitives.length){var s=o.mesh.primitives[0].geometry;s.vertices.length===e.vertices.length&&e.morphTargets.push({name:"target_1",vertices:s.vertices})}}e.morphTargets.push({name:"target_Z",vertices:e.vertices})}function h(e,t,i,r){if(e.world=e.world||new THREE.Matrix4,e.localworld=e.localworld||new THREE.Matrix4,e.world.copy(e.matrix),e.localworld.copy(e.matrix),e.channels&&e.channels.length){var n=e.channels[0],a=n.sampler.output[i];a instanceof THREE.Matrix4&&(e.world.copy(a),e.localworld.copy(a),0===i&&e.matrix.copy(a))}r&&e.world.multiplyMatrices(r,e.world),t.push(e);for(var o=0;o<e.nodes.length;o++)h(e.nodes[o],t,i,e.world)}function d(e,t){for(var i=0;i<e.length;i++){var r=e[i],n=-1;if("JOINT"==r.type){for(var a=0;a<t.joints.length;a++)if(r.sid===t.joints[a]){n=a;break}if(n>=0){var o=t.invBindMatrices[n];r.invBindMatrix=o,r.skinningMatrix=new THREE.Matrix4,r.skinningMatrix.multiplyMatrices(r.world,o),r.animatrix=new THREE.Matrix4,r.animatrix.copy(r.localworld),r.weights=[];for(var a=0;a<t.weights.length;a++)for(var s=0;s<t.weights[a].length;s++){var c=t.weights[a][s];c.joint===n&&r.weights.push(c)}}else console.warn("ColladaLoader: Could not find joint '"+r.sid+"'."),r.skinningMatrix=new THREE.Matrix4,r.weights=[]}}}function p(e){var t=[],i=function(e,t,r){var n={};n.name=t.sid,n.parent=e,n.matrix=t.matrix;var a=[new THREE.Vector3,new THREE.Quaternion,new THREE.Vector3];n.matrix.decompose(a[0],a[1],a[2]),n.pos=[a[0].x,a[0].y,a[0].z],n.scl=[a[2].x,a[2].y,a[2].z],n.rotq=[a[1].x,a[1].y,a[1].z,a[1].w],r.push(n);for(var o in t.nodes)i(t.sid,t.nodes[o],r)};return i(-1,e,t),t}function f(e,t,i){var r=[];h(t,r,-1),d(r,i.skin);for(var n=new THREE.Vector3,a=[],o=0;o<e.vertices.length;o++)a.push(new THREE.Vector3);for(o=0;o<r.length;o++)if("JOINT"==r[o].type)for(var s=0;s<r[o].weights.length;s++){var c=r[o].weights[s],u=c.index,l=c.weight,p=e.vertices[u],f=a[u];n.x=p.x,n.y=p.y,n.z=p.z,n.applyMatrix4(r[o].skinningMatrix),f.x+=n.x*l,f.y+=n.y*l,f.z+=n.z*l}for(var o=0;o<e.vertices.length;o++)e.vertices[o]=a[o]}function m(e,t,i){var r=Gt[t.url];if(i=void 0!==i?i:40,!r||!r.skin)return console.log("ColladaLoader: Could not find skin controller."),void 0;if(!t.skeleton||!t.skeleton.length)return console.log("ColladaLoader: Could not find the skeleton for the skin. "),void 0;for(var n=u(),a=Nt.getChildById(t.skeleton[0],!0)||Nt.getChildBySid(t.skeleton[0],!0),o=p(a),s=r.skin.joints,c=[],l=0;l<s.length;l++)for(var m=0;m<o.length;m++)o[m].name===s[l]&&(c[l]=o[m]);for(var l=0;l<c.length;l++)for(var m=0;m<c.length;m++)c[l].parent===c[m].name&&(c[l].parent=m);{var l,m,g;new THREE.Vector3}for(l=0;l<e.vertices.length;l++)e.vertices[l].applyMatrix4(r.skin.bindShapeMatrix);for(var v=[],y=[],M=r.skin.weights,l=0;l<M.length;l++){var A=new THREE.Vector4(M[l][0]?M[l][0].joint:0,M[l][1]?M[l][1].joint:0,M[l][2]?M[l][2].joint:0,M[l][3]?M[l][3].joint:0),g=new THREE.Vector4(M[l][0]?M[l][0].weight:0,M[l][1]?M[l][1].weight:0,M[l][2]?M[l][2].weight:0,M[l][3]?M[l][3].weight:0);v.push(A),y.push(g)}e.skinIndices=v,e.skinWeights=y,e.bones=c;for(var x={name:n.ID,fps:30,length:n.frames/30,hierarchy:[]},m=0;m<c.length;m++)x.hierarchy.push({parent:c[m].parent,name:c[m].name,keys:[]});for(console.log("ColladaLoader:",n.ID+" has "+c.length+" bones."),f(e,a,r),i=0;i<n.frames;i++){var w=[];h(a,w,i),d(w,r.skin);for(var l=0;l<w.length;l++)for(var m=0;m<x.hierarchy.length;m++)if(x.hierarchy[m].name===w[l].sid){var E={};E.time=i/30,E.matrix=w[l].animatrix,0===i&&(w[l].matrix=E.matrix);var b=[new THREE.Vector3,new THREE.Quaternion,new THREE.Vector3];E.matrix.decompose(b[0],b[1],b[2]),E.pos=[b[0].x,b[0].y,b[0].z],E.scl=[b[2].x,b[2].y,b[2].z],E.rot=b[1],x.hierarchy[m].keys.push(E)}e.animation=x}}function g(){if(Dt&&0===Dt.joints.length)return Ct=void 0,void 0;var e={},t=function(t,i){var r=i.getAttribute("id"),n=Nt.getChildById(r,!0),a=Dt.joints[t];Rt.traverse(function(i){i.colladaId==r&&(e[t]={node:i,transforms:n.transforms,joint:a,position:a.zeroPosition})})};Ct={joints:Dt&&Dt.joints,getJointValue:function(t){var i=e[t];return i?i.position:(console.log("getJointValue: joint "+t+" doesn't exist"),void 0)},setJointValue:function(t,i){var n=e[t];if(n){var a=n.joint;if(i>a.limits.max||i<a.limits.min)console.log("setJointValue: joint "+t+" value "+i+" outside of limits (min: "+a.limits.min+", max: "+a.limits.max+")");else if(a.static)console.log("setJointValue: joint "+t+" is static");else{var o=n.node,s=a.axis,c=n.transforms,u=new THREE.Matrix4;for(r=0;r<c.length;r++){var l=c[r];if(l.sid&&-1!==l.sid.indexOf("joint"+t))switch(a.type){case"revolute":u.multiply(h.makeRotationAxis(s,THREE.Math.degToRad(i)));break;case"prismatic":u.multiply(h.makeTranslation(s.x*i,s.y*i,s.z*i));break;default:console.warn("setJointValue: unknown joint type: "+a.type)}else{var h=new THREE.Matrix4;switch(l.type){case"matrix":u.multiply(l.obj);break;case"translate":u.multiply(h.makeTranslation(l.obj.x,l.obj.y,l.obj.z));break;case"rotate":u.multiply(h.makeRotationAxis(l.obj,l.angle))}}}var d=u.elements,p=Array.prototype.slice.call(d),f=[p[0],p[4],p[8],p[12],p[1],p[5],p[9],p[13],p[2],p[6],p[10],p[14],p[3],p[7],p[11],p[15]];o.matrix.set.apply(o.matrix,f),o.matrix.decompose(o.position,o.quaternion,o.scale)}}else console.log("setJointValue: joint "+t+" doesn't exist")}};var i=kt.querySelector("scene instance_kinematics_scene");if(i)for(var r=0;r<i.childNodes.length;r++){var n=i.childNodes[r];if(1==n.nodeType)switch(n.nodeName){case"bind_joint_axis":var a=n.getAttribute("target").split("/").pop(),o=n.querySelector("axis param").textContent,s=parseInt(o.split("joint").pop().split(".")[0]),c=kt.querySelector('[sid="'+a+'"]');if(c){var u=c.parentElement;t(s,u)}}}}function v(e){var t,i,r,n,a=new THREE.Object3D,o=!1;for(r=0;r<e.controllers.length;r++){var s=Gt[e.controllers[r].url];switch(s.type){case"skin":if(Vt[s.skin.source]){var c=new z;c.url=s.skin.source,c.instance_material=e.controllers[r].instance_material,e.geometries.push(c),o=!0,t=e.controllers[r]}else if(Gt[s.skin.source]){var u=Gt[s.skin.source];if(i=u,u.morph&&Vt[u.morph.source]){var c=new z;c.url=u.morph.source,c.instance_material=e.controllers[r].instance_material,e.geometries.push(c)}}break;case"morph":if(Vt[s.morph.source]){var c=new z;c.url=s.morph.source,c.instance_material=e.controllers[r].instance_material,e.geometries.push(c),i=e.controllers[r]}console.log("ColladaLoader: Morph-controller partially supported.")}}var h={};for(r=0;r<e.geometries.length;r++){var d,p=e.geometries[r],f=p.instance_material,g=Vt[p.url],y={},M=[],A=0;if(g){if(!g.mesh||!g.mesh.primitives)continue;if(0===a.name.length&&(a.name=g.id),f)for(n=0;n<f.length;n++){var x=f[n],w=Qt[x.target],E=w.instance_effect.url,b=Yt[E].shader,T=b.material;if(g.doubleSided){if(!(x.symbol in h)){var L=T.clone();L.side=THREE.DoubleSide,h[x.symbol]=L}T=h[x.symbol]}T.opacity=T.opacity?T.opacity:1,y[x.symbol]=A,M.push(T),d=T,d.name=null===w.name||""===w.name?w.id:w.name,A++}var N,D=d||new THREE.MeshLambertMaterial({color:14540253,side:g.doubleSided?THREE.DoubleSide:THREE.FrontSide}),S=g.mesh.geometry3js;if(A>1)for(D=new THREE.MultiMaterial(M),n=0;n<S.faces.length;n++){var C=S.faces[n];C.materialIndex=y[C.daeMaterial]}void 0!==t?(m(S,t),S.morphTargets.length>0?(D.morphTargets=!0,D.skinning=!1):(D.morphTargets=!1,D.skinning=!0),N=new THREE.SkinnedMesh(S,D,!1),N.name="skin_"+zt.length,zt.push(N)):void 0!==i?(l(S,i),D.morphTargets=!0,N=new THREE.Mesh(S,D),N.name="morph_"+Ot.length,Ot.push(N)):N=S.isLineStrip===!0?new THREE.Line(S):new THREE.Mesh(S,D),a.add(N)}}for(r=0;r<e.cameras.length;r++){var I=e.cameras[r],j=Ht[I.url],_=new THREE.PerspectiveCamera(j.yfov,parseFloat(j.aspect_ratio),parseFloat(j.znear),parseFloat(j.zfar));a.add(_)}for(r=0;r<e.lights.length;r++){var O=null,k=e.lights[r],R=Wt[k.url];if(R&&R.technique){var B=R.color.getHex(),U=R.intensity,P=R.distance,F=R.falloff_angle;switch(R.technique){case"directional":O=new THREE.DirectionalLight(B,U,P),O.position.set(0,0,1);break;case"point":O=new THREE.PointLight(B,U,P);break;case"spot":O=new THREE.SpotLight(B,U,P,F),O.position.set(0,0,1);break;case"ambient":O=new THREE.AmbientLight(B)}}O&&a.add(O)}if(a.name=e.name||e.id||"",a.colladaId=e.id||"",a.layer=e.layer||"",a.matrix=e.matrix,a.matrix.decompose(a.position,a.quaternion,a.scale),qt.centerGeometry&&a.geometry){var G=a.geometry.center();G.multiply(a.scale),G.applyQuaternion(a.quaternion),a.position.sub(G)}for(r=0;r<e.nodes.length;r++)a.add(v(e.nodes[r],e));return a}function y(e){for(var t=kt.querySelectorAll("library_nodes node"),i=0;i<t.length;i++){var r=t[i].attributes.getNamedItem("id");if(r&&r.value===e)return t[i]}return void 0}function M(e){var t=[],i=1e6,r=-1e6;for(var n in Ft)for(var a=Ft[n],o=0;o<a.channel.length;o++){var s=a.channel[o],c=a.sampler[o],n=s.target.split("/")[0];n==e.id&&(c.create(),s.sampler=c,i=Math.min(i,c.startTime),r=Math.max(r,c.endTime),t.push(s))}return t.length&&(e.startTime=i,e.endTime=r),t}function A(e){if(e.channels&&e.channels.length){for(var t=[],i=[],r=0,n=e.channels.length;n>r;r++){var a,o=e.channels[r],s=o.fullSid,c=o.sampler,u=c.input,l=e.getTransformBySid(o.sid);
if(o.arrIndices){a=[];for(var h=0,d=o.arrIndices.length;d>h;h++)a[h]=Tt(o.arrIndices[h])}else a=Lt(o.member);if(l){-1===i.indexOf(s)&&i.push(s);for(var h=0,d=u.length;d>h;h++){var p=u[h],f=c.getData(l.type,h,a),m=x(t,p);if(!m){m=new it(p);var g=w(t,p);t.splice(-1===g?t.length:g,0,m)}m.addTarget(s,l,a,f)}}else console.log('Could not find transform "'+o.sid+'" in node '+e.id)}for(var r=0;r<i.length;r++)for(var v=i[r],h=0;h<t.length;h++){var m=t[h];m.hasTarget(v)||E(t,m,h,v)}e.keys=t,e.sids=i}}function x(e,t){for(var i=null,r=0,n=e.length;n>r&&null===i;r++){var a=e[r];if(a.time===t)i=a;else if(a.time>t)break}return i}function w(e,t){for(var i=-1,r=0,n=e.length;n>r&&-1===i;r++){var a=e[r];a.time>=t&&(i=r)}return i}function E(e,t,i,r){var n=T(e,r,i?i-1:0),a=b(e,r,i+1);if(n&&a){var o,s=(t.time-n.time)/(a.time-n.time),c=n.getTarget(r),u=a.getTarget(r).data,l=c.data;if("matrix"===c.type)o=l;else if(l.length){o=[];for(var h=0;h<l.length;++h)o[h]=l[h]+(u[h]-l[h])*s}else o=l+(u-l)*s;t.addTarget(r,c.transform,c.member,o)}}function b(e,t,i){for(;i<e.length;i++){var r=e[i];if(r.hasTarget(t))return r}return null}function T(e,t,i){for(i=i>=0?i:i+e.length;i>=0;i--){var r=e[i];if(r.hasTarget(t))return r}return null}function L(){this.id="",this.init_from=""}function N(){this.id="",this.name="",this.type="",this.skin=null,this.morph=null}function D(){this.method=null,this.source=null,this.targets=null,this.weights=null}function S(){this.source="",this.bindShapeMatrix=null,this.invBindMatrices=[],this.joints=[],this.weights=[]}function C(){this.id="",this.name="",this.nodes=[],this.scene=new THREE.Group}function I(){this.id="",this.name="",this.sid="",this.nodes=[],this.controllers=[],this.transforms=[],this.geometries=[],this.channels=[],this.matrix=new THREE.Matrix4}function j(){this.sid="",this.type="",this.data=[],this.obj=null}function _(){this.url="",this.skeleton=[],this.instance_material=[]}function O(){this.symbol="",this.target=""}function z(){this.url="",this.instance_material=[]}function k(){this.id="",this.mesh=null}function R(e){this.geometry=e.id,this.primitives=[],this.vertices=null,this.geometry3js=null}function B(){this.material="",this.count=0,this.inputs=[],this.vcount=null,this.p=[],this.geometry=new THREE.Geometry}function U(){B.call(this),this.vcount=[]}function P(){B.call(this),this.vcount=1}function F(){B.call(this),this.vcount=3}function G(){this.source="",this.count=0,this.stride=0,this.params=[]}function V(){this.input={}}function Q(){this.semantic="",this.offset=0,this.source="",this.set=0}function Y(e){this.id=e,this.type=null}function H(){this.id="",this.name="",this.instance_effect=null}function W(){this.color=new THREE.Color,this.color.setRGB(Math.random(),Math.random(),Math.random()),this.color.a=1,this.texture=null,this.texcoord=null,this.texOpts=null}function X(e,t){this.type=e,this.effect=t,this.material=null}function q(e){this.effect=e,this.init_from=null,this.format=null}function Z(e){this.effect=e,this.source=null,this.wrap_s=null,this.wrap_t=null,this.minfilter=null,this.magfilter=null,this.mipfilter=null}function K(){this.id="",this.name="",this.shader=null,this.surface={},this.sampler={}}function J(){this.url=""}function $(){this.id="",this.name="",this.source={},this.sampler=[],this.channel=[]}function et(e){this.animation=e,this.source="",this.target="",this.fullSid=null,this.sid=null,this.dotSyntax=null,this.arrSyntax=null,this.arrIndices=null,this.member=null}function tt(e){this.id="",this.animation=e,this.inputs=[],this.input=null,this.output=null,this.strideOut=null,this.interpolation=null,this.startTime=null,this.endTime=null,this.duration=0}function it(e){this.targets=[],this.time=e}function rt(){this.id="",this.name="",this.technique=""}function nt(){this.url=""}function at(){this.id="",this.name="",this.technique=""}function ot(){this.url=""}function st(){this.id="",this.name="",this.joints=[],this.links=[]}function ct(){this.sid="",this.name="",this.axis=new THREE.Vector3,this.limits={min:0,max:0},this.type="",this.static=!1,this.zeroPosition=0,this.middlePosition=0}function ut(){this.sid="",this.name="",this.transforms=[],this.attachments=[]}function lt(){this.joint="",this.transforms=[],this.links=[]}function ht(e){var t=e.getAttribute("id");return void 0!=Ut[t]?Ut[t]:(Ut[t]=new Y(t).parse(e),Ut[t])}function dt(e){for(var t=mt(e),i=[],r=0,n=t.length;n>r;r++)i.push("true"===t[r]||"1"===t[r]?!0:!1);return i}function pt(e){for(var t=mt(e),i=[],r=0,n=t.length;n>r;r++)i.push(parseFloat(t[r]));return i}function ft(e){for(var t=mt(e),i=[],r=0,n=t.length;n>r;r++)i.push(parseInt(t[r],10));return i}function mt(e){return e.length>0?gt(e).split(/\s+/):[]}function gt(e){return e.replace(/^\s+/,"").replace(/\s+$/,"")}function vt(e,t,i){return e.hasAttribute(t)?parseInt(e.getAttribute(t),10):i}function yt(e,t){var i=new THREE.ImageLoader;i.load(t,function(t){e.image=t,e.needsUpdate=!0})}function Mt(e,t){e.doubleSided=!1;var i=t.querySelectorAll("extra double_sided")[0];i&&i&&1===parseInt(i.textContent,10)&&(e.doubleSided=!0)}function At(){if(qt.convertUpAxis!==!0||Kt===qt.upAxis)Jt=null;else switch(Kt){case"X":Jt="Y"===qt.upAxis?"XtoY":"XtoZ";break;case"Y":Jt="X"===qt.upAxis?"YtoX":"YtoZ";break;case"Z":Jt="X"===qt.upAxis?"ZtoX":"ZtoY"}}function xt(e,t){if(qt.convertUpAxis===!0&&Kt!==qt.upAxis)switch(Jt){case"XtoY":var i=e[0];e[0]=t*e[1],e[1]=i;break;case"XtoZ":var i=e[2];e[2]=e[1],e[1]=e[0],e[0]=i;break;case"YtoX":var i=e[0];e[0]=e[1],e[1]=t*i;break;case"YtoZ":var i=e[1];e[1]=t*e[2],e[2]=i;break;case"ZtoX":var i=e[0];e[0]=e[1],e[1]=e[2],e[2]=i;break;case"ZtoY":var i=e[1];e[1]=e[2],e[2]=t*i}}function wt(e,t){if(qt.convertUpAxis!==!0||Kt===qt.upAxis)return t;switch(e){case"X":t="XtoY"===Jt?-1*t:t;break;case"Y":t="YtoZ"===Jt||"YtoX"===Jt?-1*t:t;break;case"Z":t="ZtoY"===Jt?-1*t:t}return t}function Et(e,t){var i=[e[t],e[t+1],e[t+2]];return xt(i,-1),new THREE.Vector3(i[0],i[1],i[2])}function bt(e){if(qt.convertUpAxis){var t=[e[0],e[4],e[8]];xt(t,-1),e[0]=t[0],e[4]=t[1],e[8]=t[2],t=[e[1],e[5],e[9]],xt(t,-1),e[1]=t[0],e[5]=t[1],e[9]=t[2],t=[e[2],e[6],e[10]],xt(t,-1),e[2]=t[0],e[6]=t[1],e[10]=t[2],t=[e[0],e[1],e[2]],xt(t,-1),e[0]=t[0],e[1]=t[1],e[2]=t[2],t=[e[4],e[5],e[6]],xt(t,-1),e[4]=t[0],e[5]=t[1],e[6]=t[2],t=[e[8],e[9],e[10]],xt(t,-1),e[8]=t[0],e[9]=t[1],e[10]=t[2],t=[e[3],e[7],e[11]],xt(t,-1),e[3]=t[0],e[7]=t[1],e[11]=t[2]}return(new THREE.Matrix4).set(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14],e[15])}function Tt(e){if(e>-1&&3>e){var t=["X","Y","Z"],i={X:0,Y:1,Z:2};e=Lt(t[e]),e=i[e]}return e}function Lt(e){if(qt.convertUpAxis)switch(e){case"X":switch(Jt){case"XtoY":case"XtoZ":case"YtoX":e="Y";break;case"ZtoX":e="Z"}break;case"Y":switch(Jt){case"XtoY":case"YtoX":case"ZtoX":e="X";break;case"XtoZ":case"YtoZ":case"ZtoY":e="Z"}break;case"Z":switch(Jt){case"XtoZ":e="X";break;case"YtoZ":case"ZtoX":case"ZtoY":e="Y"}}return e}var Nt,Dt,St,Ct,It,jt,_t,Ot,zt,kt=null,Rt=null,Bt=null,Ut={},Pt={},Ft={},Gt={},Vt={},Qt={},Yt={},Ht={},Wt={},Xt=THREE.SmoothShading,qt={centerGeometry:!1,convertUpAxis:!1,subdivideFaces:!0,upAxis:"Y",defaultEnvMap:null},Zt=1,Kt="Y",Jt=null;return L.prototype.parse=function(e){this.id=e.getAttribute("id");for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];"init_from"===i.nodeName&&(this.init_from=i.textContent)}return this},N.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name"),this.type="none";for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"skin":this.skin=(new S).parse(i),this.type=i.nodeName;break;case"morph":this.morph=(new D).parse(i),this.type=i.nodeName}}return this},D.prototype.parse=function(e){var t,i={},r=[];for(this.method=e.getAttribute("method"),this.source=e.getAttribute("source").replace(/^#/,""),t=0;t<e.childNodes.length;t++){var n=e.childNodes[t];if(1==n.nodeType)switch(n.nodeName){case"source":var a=(new Y).parse(n);i[a.id]=a;break;case"targets":r=this.parseInputs(n);break;default:console.log(n.nodeName)}}for(t=0;t<r.length;t++){var o=r[t],a=i[o.source];switch(o.semantic){case"MORPH_TARGET":this.targets=a.read();break;case"MORPH_WEIGHT":this.weights=a.read()}}return this},D.prototype.parseInputs=function(e){for(var t=[],i=0;i<e.childNodes.length;i++){var r=e.childNodes[i];if(1==r.nodeType)switch(r.nodeName){case"input":t.push((new Q).parse(r))}}return t},S.prototype.parse=function(e){var t,i,r={};this.source=e.getAttribute("source").replace(/^#/,""),this.invBindMatrices=[],this.joints=[],this.weights=[];for(var n=0;n<e.childNodes.length;n++){var a=e.childNodes[n];if(1==a.nodeType)switch(a.nodeName){case"bind_shape_matrix":var o=pt(a.textContent);this.bindShapeMatrix=bt(o);break;case"source":var s=(new Y).parse(a);r[s.id]=s;break;case"joints":t=a;break;case"vertex_weights":i=a;break;default:console.log(a.nodeName)}}return this.parseJoints(t,r),this.parseWeights(i,r),this},S.prototype.parseJoints=function(e,t){for(var i=0;i<e.childNodes.length;i++){var r=e.childNodes[i];if(1==r.nodeType)switch(r.nodeName){case"input":var n=(new Q).parse(r),a=t[n.source];"JOINT"===n.semantic?this.joints=a.read():"INV_BIND_MATRIX"===n.semantic&&(this.invBindMatrices=a.read())}}},S.prototype.parseWeights=function(e,t){for(var i,r,n=[],a=0;a<e.childNodes.length;a++){var o=e.childNodes[a];if(1==o.nodeType)switch(o.nodeName){case"input":n.push((new Q).parse(o));break;case"v":i=ft(o.textContent);break;case"vcount":r=ft(o.textContent)}}for(var s=0,a=0;a<r.length;a++){for(var c=r[a],u=[],l=0;c>l;l++){for(var h={},d=0;d<n.length;d++){var p=n[d],f=i[s+p.offset];switch(p.semantic){case"JOINT":h.joint=f;break;case"WEIGHT":h.weight=t[p.source].data[f]}}u.push(h),s+=n.length}for(var l=0;l<u.length;l++)u[l].index=a;this.weights.push(u)}},C.prototype.getChildById=function(e,t){for(var i=0;i<this.nodes.length;i++){var r=this.nodes[i].getChildById(e,t);if(r)return r}return null},C.prototype.getChildBySid=function(e,t){for(var i=0;i<this.nodes.length;i++){var r=this.nodes[i].getChildBySid(e,t);if(r)return r}return null},C.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name"),this.nodes=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"node":this.nodes.push((new I).parse(i))}}return this},I.prototype.getChannelForTransform=function(e){for(var t=0;t<this.channels.length;t++){var i,r,n=this.channels[t],a=n.target.split("/"),o=(a.shift(),a.shift()),s=o.indexOf(".")>=0,c=o.indexOf("(")>=0;if(s)a=o.split("."),o=a.shift(),r=a.shift();else if(c){i=o.split("("),o=i.shift();for(var u=0;u<i.length;u++)i[u]=parseInt(i[u].replace(/\)/,""))}if(o===e)return n.info={sid:o,dotSyntax:s,arrSyntax:c,arrIndices:i},n}return null},I.prototype.getChildById=function(e,t){if(this.id===e)return this;if(t)for(var i=0;i<this.nodes.length;i++){var r=this.nodes[i].getChildById(e,t);if(r)return r}return null},I.prototype.getChildBySid=function(e,t){if(this.sid===e)return this;if(t)for(var i=0;i<this.nodes.length;i++){var r=this.nodes[i].getChildBySid(e,t);if(r)return r}return null},I.prototype.getTransformBySid=function(e){for(var t=0;t<this.transforms.length;t++)if(this.transforms[t].sid===e)return this.transforms[t];return null},I.prototype.parse=function(e){var t;this.id=e.getAttribute("id"),this.sid=e.getAttribute("sid"),this.name=e.getAttribute("name"),this.type=e.getAttribute("type"),this.layer=e.getAttribute("layer"),this.type="JOINT"===this.type?this.type:"NODE",this.nodes=[],this.transforms=[],this.geometries=[],this.cameras=[],this.lights=[],this.controllers=[],this.matrix=new THREE.Matrix4;for(var i=0;i<e.childNodes.length;i++){var r=e.childNodes[i];if(1==r.nodeType)switch(r.nodeName){case"node":this.nodes.push((new I).parse(r));break;case"instance_camera":this.cameras.push((new nt).parse(r));break;case"instance_controller":this.controllers.push((new _).parse(r));break;case"instance_geometry":this.geometries.push((new z).parse(r));break;case"instance_light":this.lights.push((new ot).parse(r));break;case"instance_node":t=r.getAttribute("url").replace(/^#/,"");var n=y(t);n&&this.nodes.push((new I).parse(n));break;case"rotate":case"translate":case"scale":case"matrix":case"lookat":case"skew":this.transforms.push((new j).parse(r));break;case"extra":break;default:console.log(r.nodeName)}}return this.channels=M(this),A(this),this.updateMatrix(),this},I.prototype.updateMatrix=function(){this.matrix.identity();for(var e=0;e<this.transforms.length;e++)this.transforms[e].apply(this.matrix)},j.prototype.parse=function(e){return this.sid=e.getAttribute("sid"),this.type=e.nodeName,this.data=pt(e.textContent),this.convert(),this},j.prototype.convert=function(){switch(this.type){case"matrix":this.obj=bt(this.data);break;case"rotate":this.angle=THREE.Math.degToRad(this.data[3]);case"translate":xt(this.data,-1),this.obj=new THREE.Vector3(this.data[0],this.data[1],this.data[2]);break;case"scale":xt(this.data,1),this.obj=new THREE.Vector3(this.data[0],this.data[1],this.data[2]);break;default:console.log("Can not convert Transform of type "+this.type)}},j.prototype.apply=function(){var e=new THREE.Matrix4;return function(t){switch(this.type){case"matrix":t.multiply(this.obj);break;case"translate":t.multiply(e.makeTranslation(this.obj.x,this.obj.y,this.obj.z));break;case"rotate":t.multiply(e.makeRotationAxis(this.obj,this.angle));break;case"scale":t.scale(this.obj)}}}(),j.prototype.update=function(e,t){var i=["X","Y","Z","ANGLE"];switch(this.type){case"matrix":if(t)if(1===t.length)switch(t[0]){case 0:this.obj.n11=e[0],this.obj.n21=e[1],this.obj.n31=e[2],this.obj.n41=e[3];break;case 1:this.obj.n12=e[0],this.obj.n22=e[1],this.obj.n32=e[2],this.obj.n42=e[3];break;case 2:this.obj.n13=e[0],this.obj.n23=e[1],this.obj.n33=e[2],this.obj.n43=e[3];break;case 3:this.obj.n14=e[0],this.obj.n24=e[1],this.obj.n34=e[2],this.obj.n44=e[3]}else if(2===t.length){var r="n"+(t[0]+1)+(t[1]+1);this.obj[r]=e}else console.log("Incorrect addressing of matrix in transform.");else this.obj.copy(e);break;case"translate":case"scale":switch("[object Array]"===Object.prototype.toString.call(t)&&(t=i[t[0]]),t){case"X":this.obj.x=e;break;case"Y":this.obj.y=e;break;case"Z":this.obj.z=e;break;default:this.obj.x=e[0],this.obj.y=e[1],this.obj.z=e[2]}break;case"rotate":switch("[object Array]"===Object.prototype.toString.call(t)&&(t=i[t[0]]),t){case"X":this.obj.x=e;break;case"Y":this.obj.y=e;break;case"Z":this.obj.z=e;break;case"ANGLE":this.angle=THREE.Math.degToRad(e);break;default:this.obj.x=e[0],this.obj.y=e[1],this.obj.z=e[2],this.angle=THREE.Math.degToRad(e[3])}}},_.prototype.parse=function(e){this.url=e.getAttribute("url").replace(/^#/,""),this.skeleton=[],this.instance_material=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1===i.nodeType)switch(i.nodeName){case"skeleton":this.skeleton.push(i.textContent.replace(/^#/,""));break;case"bind_material":for(var r=i.querySelectorAll("instance_material"),n=0;n<r.length;n++){var a=r[n];this.instance_material.push((new O).parse(a))}break;case"extra":}}return this},O.prototype.parse=function(e){return this.symbol=e.getAttribute("symbol"),this.target=e.getAttribute("target").replace(/^#/,""),this},z.prototype.parse=function(e){this.url=e.getAttribute("url").replace(/^#/,""),this.instance_material=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType&&"bind_material"===i.nodeName){for(var r=i.querySelectorAll("instance_material"),n=0;n<r.length;n++){var a=r[n];this.instance_material.push((new O).parse(a))}break}}return this},k.prototype.parse=function(e){this.id=e.getAttribute("id"),Mt(this,e);for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"mesh":this.mesh=new R(this).parse(i);break;case"extra":}}return this},R.prototype.parse=function(e){this.primitives=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"source":ht(i);break;case"vertices":this.vertices=(new V).parse(i);break;case"linestrips":this.primitives.push((new P).parse(i));break;case"triangles":this.primitives.push((new F).parse(i));break;case"polygons":this.primitives.push((new B).parse(i));break;case"polylist":this.primitives.push((new U).parse(i))}}if(this.geometry3js=new THREE.Geometry,null===this.vertices)return this;for(var r=Ut[this.vertices.input.POSITION.source].data,t=0;t<r.length;t+=3)this.geometry3js.vertices.push(Et(r,t).clone());for(var t=0;t<this.primitives.length;t++){var n=this.primitives[t];n.setVertices(this.vertices),this.handlePrimitive(n,this.geometry3js)}return this.geometry3js.calcNormals&&(this.geometry3js.computeVertexNormals(),delete this.geometry3js.calcNormals),this},R.prototype.handlePrimitive=function(e,t){if(e instanceof P)return t.isLineStrip=!0,void 0;var i,r,n,a,o,s,c,u=e.p,l=e.inputs,h=0,d=3,p=0,f=[];for(i=0;i<l.length;i++){n=l[i];var m=n.offset+1;switch(p=m>p?m:p,n.semantic){case"TEXCOORD":f.push(n.set)}}for(var g=0;g<u.length;++g)for(var v=u[g],y=0;y<v.length;){var M=[],A=[],x=null,w=[];for(d=e.vcount?e.vcount.length?e.vcount[h++]:e.vcount:v.length/p,i=0;d>i;i++)for(r=0;r<l.length;r++)switch(n=l[r],s=Ut[n.source],a=v[y+i*p+n.offset],c=s.accessor.params.length,o=a*c,n.semantic){case"VERTEX":M.push(a);break;case"NORMAL":A.push(Et(s.data,o));break;case"TEXCOORD":x=x||{},void 0===x[n.set]&&(x[n.set]=[]),x[n.set].push(new THREE.Vector2(s.data[o],s.data[o+1]));break;case"COLOR":w.push((new THREE.Color).setRGB(s.data[o],s.data[o+1],s.data[o+2]))}if(0===A.length)if(n=this.vertices.input.NORMAL){s=Ut[n.source],c=s.accessor.params.length;for(var E=0,b=M.length;b>E;E++)A.push(Et(s.data,M[E]*c))}else t.calcNormals=!0;if(!x&&(x={},n=this.vertices.input.TEXCOORD)){f.push(n.set),s=Ut[n.source],c=s.accessor.params.length;for(var E=0,b=M.length;b>E;E++)o=M[E]*c,void 0===x[n.set]&&(x[n.set]=[]),x[n.set].push(new THREE.Vector2(s.data[o],1-s.data[o+1]))}if(0===w.length&&(n=this.vertices.input.COLOR)){s=Ut[n.source],c=s.accessor.params.length;for(var E=0,b=M.length;b>E;E++)o=M[E]*c,w.push((new THREE.Color).setRGB(s.data[o],s.data[o+1],s.data[o+2]))}var T,L,N=null,D=[];if(3===d)D.push(new THREE.Face3(M[0],M[1],M[2],A,w.length?w:new THREE.Color));else if(4===d)D.push(new THREE.Face3(M[0],M[1],M[3],A.length?[A[0].clone(),A[1].clone(),A[3].clone()]:[],w.length?[w[0],w[1],w[3]]:new THREE.Color)),D.push(new THREE.Face3(M[1],M[2],M[3],A.length?[A[1].clone(),A[2].clone(),A[3].clone()]:[],w.length?[w[1],w[2],w[3]]:new THREE.Color));else if(d>4&&qt.subdivideFaces){var S=w.length?w:new THREE.Color;for(r=1;d-1>r;)D.push(new THREE.Face3(M[0],M[r],M[r+1],A.length?[A[0].clone(),A[r++].clone(),A[r].clone()]:[],S))}if(D.length)for(var E=0,b=D.length;b>E;E++)for(N=D[E],N.daeMaterial=e.material,t.faces.push(N),r=0;r<f.length;r++)T=x[f[r]],L=d>4?[T[0],T[E+1],T[E+2]]:4===d?0===E?[T[0],T[1],T[3]]:[T[1].clone(),T[2],T[3].clone()]:[T[0],T[1],T[2]],void 0===t.faceVertexUvs[r]&&(t.faceVertexUvs[r]=[]),t.faceVertexUvs[r].push(L);else console.log("dropped face with vcount "+d+" for geometry with id: "+t.id);y+=p*d}},B.prototype.setVertices=function(e){for(var t=0;t<this.inputs.length;t++)this.inputs[t].source===e.id&&(this.inputs[t].source=e.input.POSITION.source)},B.prototype.parse=function(e){this.material=e.getAttribute("material"),this.count=vt(e,"count",0);for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"input":this.inputs.push((new Q).parse(e.childNodes[t]));break;case"vcount":this.vcount=ft(i.textContent);break;case"p":this.p.push(ft(i.textContent));break;case"ph":console.warn("polygon holes not yet supported!")}}return this},U.prototype=Object.create(B.prototype),U.prototype.constructor=U,P.prototype=Object.create(B.prototype),P.prototype.constructor=P,F.prototype=Object.create(B.prototype),F.prototype.constructor=F,G.prototype.parse=function(e){this.params=[],this.source=e.getAttribute("source"),this.count=vt(e,"count",0),this.stride=vt(e,"stride",0);for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if("param"===i.nodeName){var r={};r.name=i.getAttribute("name"),r.type=i.getAttribute("type"),this.params.push(r)}}return this},V.prototype.parse=function(e){this.id=e.getAttribute("id");for(var t=0;t<e.childNodes.length;t++)if("input"===e.childNodes[t].nodeName){var i=(new Q).parse(e.childNodes[t]);this.input[i.semantic]=i}return this},Q.prototype.parse=function(e){return this.semantic=e.getAttribute("semantic"),this.source=e.getAttribute("source").replace(/^#/,""),this.set=vt(e,"set",-1),this.offset=vt(e,"offset",0),"TEXCOORD"===this.semantic&&this.set<0&&(this.set=0),this},Y.prototype.parse=function(e){this.id=e.getAttribute("id");for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"bool_array":this.data=dt(i.textContent),this.type=i.nodeName;break;case"float_array":this.data=pt(i.textContent),this.type=i.nodeName;break;case"int_array":this.data=ft(i.textContent),this.type=i.nodeName;break;case"IDREF_array":case"Name_array":this.data=mt(i.textContent),this.type=i.nodeName;break;case"technique_common":for(var r=0;r<i.childNodes.length;r++)if("accessor"===i.childNodes[r].nodeName){this.accessor=(new G).parse(i.childNodes[r]);break}}}return this},Y.prototype.read=function(){var e=[],t=this.accessor.params[0];switch(t.type){case"IDREF":case"Name":case"name":case"float":return this.data;case"float4x4":for(var i=0;i<this.data.length;i+=16){var r=this.data.slice(i,i+16),n=bt(r);e.push(n)}break;default:console.log("ColladaLoader: Source: Read dont know how to read "+t.type+".")}return e},H.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name");for(var t=0;t<e.childNodes.length;t++)if("instance_effect"===e.childNodes[t].nodeName){this.instance_effect=(new J).parse(e.childNodes[t]);break}return this},W.prototype.isColor=function(){return null===this.texture},W.prototype.isTexture=function(){return null!=this.texture},W.prototype.parse=function(e){"transparent"===e.nodeName&&(this.opaque=e.getAttribute("opaque"));for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"color":var r=pt(i.textContent);this.color=new THREE.Color,this.color.setRGB(r[0],r[1],r[2]),this.color.a=r[3];break;case"texture":this.texture=i.getAttribute("texture"),this.texcoord=i.getAttribute("texcoord"),this.texOpts={offsetU:0,offsetV:0,repeatU:1,repeatV:1,wrapU:1,wrapV:1},this.parseTexture(i)}}return this},W.prototype.parseTexture=function(e){if(!e.childNodes)return this;e.childNodes[1]&&"extra"===e.childNodes[1].nodeName&&(e=e.childNodes[1],e.childNodes[1]&&"technique"===e.childNodes[1].nodeName&&(e=e.childNodes[1]));for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"offsetU":case"offsetV":case"repeatU":case"repeatV":this.texOpts[i.nodeName]=parseFloat(i.textContent);break;case"wrapU":case"wrapV":this.texOpts[i.nodeName]="TRUE"===i.textContent.toUpperCase()?1:parseInt(i.textContent);break;default:this.texOpts[i.nodeName]=i.textContent}}return this},X.prototype.parse=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"emission":case"diffuse":case"specular":case"transparent":this[i.nodeName]=(new W).parse(i);break;case"bump":var r=i.getAttribute("bumptype");r?"heightfield"===r.toLowerCase()?this.bump=(new W).parse(i):"normalmap"===r.toLowerCase()?this.normal=(new W).parse(i):(console.error("Shader.prototype.parse: Invalid value for attribute 'bumptype' ("+r+") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'"),this.bump=(new W).parse(i)):(console.warn("Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'"),this.bump=(new W).parse(i));break;case"shininess":case"reflectivity":case"index_of_refraction":case"transparency":var n=i.querySelectorAll("float");n.length>0&&(this[i.nodeName]=parseFloat(n[0].textContent))}}return this.create(),this},X.prototype.create=function(){var e={},t=!1;if(void 0!==this.transparency&&void 0!==this.transparent){var i=(this.transparent,(this.transparent.color.r+this.transparent.color.g+this.transparent.color.b)/3*this.transparency);i>0&&(t=!0,e.transparent=!0,e.opacity=1-i)}var r={diffuse:"map",ambient:"lightMap",specular:"specularMap",emission:"emissionMap",bump:"bumpMap",normal:"normalMap"};for(var n in this)switch(n){case"ambient":case"emission":case"diffuse":case"specular":case"bump":case"normal":var a=this[n];if(a instanceof W)if(a.isTexture()){var o=a.texture,s=this.effect.sampler[o];if(void 0!==s&&void 0!==s.source){var c=this.effect.surface[s.source];if(void 0!==c){var u=Pt[c.init_from];if(u){var l,h=_t+u.init_from,d=THREE.Loader.Handlers.get(h);null!==d?l=d.load(h):(l=new THREE.Texture,yt(l,h)),l.wrapS=a.texOpts.wrapU?THREE.RepeatWrapping:THREE.ClampToEdgeWrapping,l.wrapT=a.texOpts.wrapV?THREE.RepeatWrapping:THREE.ClampToEdgeWrapping,l.offset.x=a.texOpts.offsetU,l.offset.y=a.texOpts.offsetV,l.repeat.x=a.texOpts.repeatU,l.repeat.y=a.texOpts.repeatV,e[r[n]]=l,"emission"===n&&(e.emissive=16777215)}}}}else"diffuse"!==n&&t||("emission"===n?e.emissive=a.color.getHex():e[n]=a.color.getHex());break;case"shininess":e[n]=this[n];break;case"reflectivity":e[n]=this[n],e[n]>0&&(e.envMap=qt.defaultEnvMap),e.combine=THREE.MixOperation;break;case"index_of_refraction":e.refractionRatio=this[n],1!==this[n]&&(e.envMap=qt.defaultEnvMap);break;case"transparency":}switch(e.shading=Xt,e.side=this.effect.doubleSided?THREE.DoubleSide:THREE.FrontSide,void 0!==e.diffuse&&(e.color=e.diffuse,delete e.diffuse),this.type){case"constant":void 0!=e.emissive&&(e.color=e.emissive),this.material=new THREE.MeshBasicMaterial(e);break;case"phong":case"blinn":this.material=new THREE.MeshPhongMaterial(e);break;case"lambert":default:this.material=new THREE.MeshLambertMaterial(e)}return this.material},q.prototype.parse=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"init_from":this.init_from=i.textContent;break;case"format":this.format=i.textContent;break;default:console.log("unhandled Surface prop: "+i.nodeName)}}return this},Z.prototype.parse=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"source":this.source=i.textContent;break;case"minfilter":this.minfilter=i.textContent;break;case"magfilter":this.magfilter=i.textContent;break;case"mipfilter":this.mipfilter=i.textContent;break;case"wrap_s":this.wrap_s=i.textContent;break;case"wrap_t":this.wrap_t=i.textContent;break;default:console.log("unhandled Sampler2D prop: "+i.nodeName)}}return this},K.prototype.create=function(){return null===this.shader?null:void 0},K.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name"),Mt(this,e),this.shader=null;for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"profile_COMMON":this.parseTechnique(this.parseProfileCOMMON(i))}}return this},K.prototype.parseNewparam=function(e){for(var t=e.getAttribute("sid"),i=0;i<e.childNodes.length;i++){var r=e.childNodes[i];if(1==r.nodeType)switch(r.nodeName){case"surface":this.surface[t]=new q(this).parse(r);break;case"sampler2D":this.sampler[t]=new Z(this).parse(r);break;case"extra":break;default:console.log(r.nodeName)}}},K.prototype.parseProfileCOMMON=function(e){for(var t,i=0;i<e.childNodes.length;i++){var r=e.childNodes[i];if(1==r.nodeType)switch(r.nodeName){case"profile_COMMON":this.parseProfileCOMMON(r);break;case"technique":t=r;break;case"newparam":this.parseNewparam(r);break;case"image":var n=(new L).parse(r);Pt[n.id]=n;break;case"extra":break;default:console.log(r.nodeName)}}return t},K.prototype.parseTechnique=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"constant":case"lambert":case"blinn":case"phong":this.shader=new X(i.nodeName,this).parse(i);break;case"extra":this.parseExtra(i)}}},K.prototype.parseExtra=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"technique":this.parseExtraTechnique(i)}}},K.prototype.parseExtraTechnique=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"bump":this.shader.parse(e)}}},J.prototype.parse=function(e){return this.url=e.getAttribute("url").replace(/^#/,""),this},$.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name"),this.source={};for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"animation":var r=(new $).parse(i);for(var n in r.source)this.source[n]=r.source[n];for(var a=0;a<r.channel.length;a++)this.channel.push(r.channel[a]),this.sampler.push(r.sampler[a]);break;case"source":var n=(new Y).parse(i);this.source[n.id]=n;break;case"sampler":this.sampler.push(new tt(this).parse(i));break;case"channel":this.channel.push(new et(this).parse(i))}}return this},et.prototype.parse=function(e){this.source=e.getAttribute("source").replace(/^#/,""),this.target=e.getAttribute("target");var t=this.target.split("/"),i=(t.shift(),t.shift()),r=i.indexOf(".")>=0,n=i.indexOf("(")>=0;if(r)t=i.split("."),this.sid=t.shift(),this.member=t.shift();else if(n){var a=i.split("(");this.sid=a.shift();for(var o=0;o<a.length;o++)a[o]=parseInt(a[o].replace(/\)/,""));this.arrIndices=a}else this.sid=i;return this.fullSid=i,this.dotSyntax=r,this.arrSyntax=n,this},tt.prototype.parse=function(e){this.id=e.getAttribute("id"),this.inputs=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"input":this.inputs.push((new Q).parse(i))}}return this},tt.prototype.create=function(){for(var e=0;e<this.inputs.length;e++){var t=this.inputs[e],i=this.animation.source[t.source];switch(t.semantic){case"INPUT":this.input=i.read();break;case"OUTPUT":this.output=i.read(),this.strideOut=i.accessor.stride;break;case"INTERPOLATION":this.interpolation=i.read();break;case"IN_TANGENT":break;case"OUT_TANGENT":break;default:console.log(t.semantic)}}if(this.startTime=0,this.endTime=0,this.duration=0,this.input.length){this.startTime=1e8,this.endTime=-1e8;for(var e=0;e<this.input.length;e++)this.startTime=Math.min(this.startTime,this.input[e]),this.endTime=Math.max(this.endTime,this.input[e]);this.duration=this.endTime-this.startTime}},tt.prototype.getData=function(e,t,i){var r;if("matrix"===e&&16===this.strideOut)r=this.output[t];else if(this.strideOut>1){r=[],t*=this.strideOut;for(var n=0;n<this.strideOut;++n)r[n]=this.output[t+n];if(3===this.strideOut)switch(e){case"rotate":case"translate":xt(r,-1);break;case"scale":xt(r,1)}else 4===this.strideOut&&"matrix"===e&&xt(r,-1)}else r=this.output[t],i&&"translate"===e&&(r=wt(i,r));return r},it.prototype.addTarget=function(e,t,i,r){this.targets.push({sid:e,member:i,transform:t,data:r})},it.prototype.apply=function(e){for(var t=0;t<this.targets.length;++t){var i=this.targets[t];e&&i.sid!==e||i.transform.update(i.data,i.member)}},it.prototype.getTarget=function(e){for(var t=0;t<this.targets.length;++t)if(this.targets[t].sid===e)return this.targets[t];return null},it.prototype.hasTarget=function(e){for(var t=0;t<this.targets.length;++t)if(this.targets[t].sid===e)return!0;return!1},it.prototype.interpolate=function(e,t){for(var i=0,r=this.targets.length;r>i;i++){var n,a=this.targets[i],o=e.getTarget(a.sid);if("matrix"!==a.transform.type&&o){var s=(t-this.time)/(e.time-this.time),c=o.data,u=a.data;if(0>s&&(s=0),s>1&&(s=1),u.length){n=[];for(var l=0;l<u.length;++l)n[l]=u[l]+(c[l]-u[l])*s}else n=u+(c-u)*s}else n=a.data;a.transform.update(n,a.member)}},rt.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name");for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"optics":this.parseOptics(i)}}return this},rt.prototype.parseOptics=function(e){for(var t=0;t<e.childNodes.length;t++)if("technique_common"===e.childNodes[t].nodeName)for(var i=e.childNodes[t],r=0;r<i.childNodes.length;r++)if(this.technique=i.childNodes[r].nodeName,"perspective"===this.technique)for(var n=i.childNodes[r],a=0;a<n.childNodes.length;a++){var o=n.childNodes[a];
switch(o.nodeName){case"yfov":this.yfov=o.textContent;break;case"xfov":this.xfov=o.textContent;break;case"znear":this.znear=o.textContent;break;case"zfar":this.zfar=o.textContent;break;case"aspect_ratio":this.aspect_ratio=o.textContent}}else if("orthographic"===this.technique)for(var s=i.childNodes[r],a=0;a<s.childNodes.length;a++){var o=s.childNodes[a];switch(o.nodeName){case"xmag":this.xmag=o.textContent;break;case"ymag":this.ymag=o.textContent;break;case"znear":this.znear=o.textContent;break;case"zfar":this.zfar=o.textContent;break;case"aspect_ratio":this.aspect_ratio=o.textContent}}return this},nt.prototype.parse=function(e){return this.url=e.getAttribute("url").replace(/^#/,""),this},at.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name");for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"technique_common":this.parseCommon(i);break;case"technique":this.parseTechnique(i)}}return this},at.prototype.parseCommon=function(e){for(var t=0;t<e.childNodes.length;t++)switch(e.childNodes[t].nodeName){case"directional":case"point":case"spot":case"ambient":this.technique=e.childNodes[t].nodeName;for(var i=e.childNodes[t],r=0;r<i.childNodes.length;r++){var n=i.childNodes[r];switch(n.nodeName){case"color":var a=pt(n.textContent);this.color=new THREE.Color(0),this.color.setRGB(a[0],a[1],a[2]),this.color.a=a[3];break;case"falloff_angle":this.falloff_angle=parseFloat(n.textContent);break;case"quadratic_attenuation":var o=parseFloat(n.textContent);this.distance=o?Math.sqrt(1/o):0}}}return this},at.prototype.parseTechnique=function(e){this.profile=e.getAttribute("profile");for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];switch(i.nodeName){case"intensity":this.intensity=parseFloat(i.textContent)}}return this},ot.prototype.parse=function(e){return this.url=e.getAttribute("url").replace(/^#/,""),this},st.prototype.parse=function(e){this.id=e.getAttribute("id"),this.name=e.getAttribute("name"),this.joints=[],this.links=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"technique_common":this.parseCommon(i)}}return this},st.prototype.parseCommon=function(e){for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(e.childNodes[t].nodeName){case"joint":this.joints.push((new ct).parse(i));break;case"link":this.links.push((new ut).parse(i))}}return this},ct.prototype.parse=function(e){this.sid=e.getAttribute("sid"),this.name=e.getAttribute("name"),this.axis=new THREE.Vector3,this.limits={min:0,max:0},this.type="",this.static=!1,this.zeroPosition=0,this.middlePosition=0;var t=e.querySelector("axis"),i=pt(t.textContent);this.axis=Et(i,0);var r=e.querySelector("limits min")?parseFloat(e.querySelector("limits min").textContent):-360,n=e.querySelector("limits max")?parseFloat(e.querySelector("limits max").textContent):360;this.limits={min:r,max:n};for(var a=["prismatic","revolute"],o=0;o<a.length;o++){var s=a[o],c=e.querySelector(s);c&&(this.type=s)}return this.limits.min>=this.limits.max&&(this.static=!0),this.middlePosition=(this.limits.min+this.limits.max)/2,this},ut.prototype.parse=function(e){this.sid=e.getAttribute("sid"),this.name=e.getAttribute("name"),this.transforms=[],this.attachments=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"attachment_full":this.attachments.push((new lt).parse(i));break;case"rotate":case"translate":case"matrix":this.transforms.push((new j).parse(i))}}return this},lt.prototype.parse=function(e){this.joint=e.getAttribute("joint").split("/").pop(),this.links=[];for(var t=0;t<e.childNodes.length;t++){var i=e.childNodes[t];if(1==i.nodeType)switch(i.nodeName){case"link":this.links.push((new ut).parse(i));break;case"rotate":case"translate":case"matrix":this.transforms.push((new j).parse(i))}}return this},{load:e,parse:t,setPreferredShading:i,applySkin:m,geometries:Vt,options:qt}}},{}],15:[function(){THREE.MTLLoader=function(e){this.manager=void 0!==e?e:THREE.DefaultLoadingManager},THREE.MTLLoader.prototype={constructor:THREE.MTLLoader,load:function(e,t,i,r){var n=this,a=new THREE.XHRLoader(this.manager);a.setPath(this.path),a.load(e,function(e){t(n.parse(e))},i,r)},setPath:function(e){this.path=e},setBaseUrl:function(e){this.baseUrl=e},setCrossOrigin:function(e){this.crossOrigin=e},setMaterialOptions:function(e){this.materialOptions=e},parse:function(e){for(var t=e.split("\n"),i={},r=/\s+/,n={},a=0;a<t.length;a++){var o=t[a];if(o=o.trim(),0!==o.length&&"#"!==o.charAt(0)){var s=o.indexOf(" "),c=s>=0?o.substring(0,s):o;c=c.toLowerCase();var u=s>=0?o.substring(s+1):"";if(u=u.trim(),"newmtl"===c)i={name:u},n[u]=i;else if(i)if("ka"===c||"kd"===c||"ks"===c){var l=u.split(r,3);i[c]=[parseFloat(l[0]),parseFloat(l[1]),parseFloat(l[2])]}else i[c]=u}}var h=new THREE.MTLLoader.MaterialCreator(this.baseUrl,this.materialOptions);return h.setCrossOrigin(this.crossOrigin),h.setManager(this.manager),h.setMaterials(n),h}},THREE.MTLLoader.MaterialCreator=function(e,t){this.baseUrl=e,this.options=t,this.materialsInfo={},this.materials={},this.materialsArray=[],this.nameLookup={},this.side=this.options&&this.options.side?this.options.side:THREE.FrontSide,this.wrap=this.options&&this.options.wrap?this.options.wrap:THREE.RepeatWrapping},THREE.MTLLoader.MaterialCreator.prototype={constructor:THREE.MTLLoader.MaterialCreator,setCrossOrigin:function(e){this.crossOrigin=e},setManager:function(e){this.manager=e},setMaterials:function(e){this.materialsInfo=this.convert(e),this.materials={},this.materialsArray=[],this.nameLookup={}},convert:function(e){if(!this.options)return e;var t={};for(var i in e){var r=e[i],n={};t[i]=n;for(var a in r){var o=!0,s=r[a],c=a.toLowerCase();switch(c){case"kd":case"ka":case"ks":this.options&&this.options.normalizeRGB&&(s=[s[0]/255,s[1]/255,s[2]/255]),this.options&&this.options.ignoreZeroRGBs&&0===s[0]&&0===s[1]&&0===s[1]&&(o=!1)}o&&(n[c]=s)}}return t},preload:function(){for(var e in this.materialsInfo)this.create(e)},getIndex:function(e){return this.nameLookup[e]},getAsArray:function(){var e=0;for(var t in this.materialsInfo)this.materialsArray[e]=this.create(t),this.nameLookup[t]=e,e++;return this.materialsArray},create:function(e){return void 0===this.materials[e]&&this.createMaterial_(e),this.materials[e]},createMaterial_:function(e){var t=this.materialsInfo[e],i={name:e,side:this.side};for(var r in t){var n=t[r];if(""!==n)switch(r.toLowerCase()){case"kd":i.color=(new THREE.Color).fromArray(n);break;case"ks":i.specular=(new THREE.Color).fromArray(n);break;case"map_kd":i.map=this.loadTexture(this.baseUrl+n),i.map.wrapS=this.wrap,i.map.wrapT=this.wrap;break;case"ns":i.shininess=parseFloat(n);break;case"d":1>n&&(i.opacity=n,i.transparent=!0);break;case"Tr":n>0&&(i.opacity=1-n,i.transparent=!0);break;case"map_bump":case"bump":if(i.bumpMap)break;i.bumpMap=this.loadTexture(this.baseUrl+n),i.bumpMap.wrapS=this.wrap,i.bumpMap.wrapT=this.wrap}}return this.materials[e]=new THREE.MeshPhongMaterial(i),this.materials[e]},loadTexture:function(e,t,i,r,n){var a,o=THREE.Loader.Handlers.get(e),s=void 0!==this.manager?this.manager:THREE.DefaultLoadingManager;return null===o&&(o=new THREE.TextureLoader(s)),o.setCrossOrigin&&o.setCrossOrigin(this.crossOrigin),a=o.load(e,i,r,n),void 0!==t&&(a.mapping=t),a}},THREE.EventDispatcher.prototype.apply(THREE.MTLLoader.prototype)},{}],16:[function(){THREE.OBJLoader=function(e){this.manager=void 0!==e?e:THREE.DefaultLoadingManager,this.materials=null,this.regexp={vertex_pattern:/^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,normal_pattern:/^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,uv_pattern:/^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/,face_vertex:/^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/,face_vertex_uv:/^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/,face_vertex_uv_normal:/^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/,face_vertex_normal:/^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/,object_pattern:/^[og]\s*(.+)?/,smoothing_pattern:/^s\s+(\d+|on|off)/,material_library_pattern:/^mtllib /,material_use_pattern:/^usemtl /}},THREE.OBJLoader.prototype={constructor:THREE.OBJLoader,load:function(e,t,i,r){var n=this,a=new THREE.XHRLoader(n.manager);a.setPath(this.path),a.load(e,function(e){t(n.parse(e))},i,r)},setPath:function(e){this.path=e},setMaterials:function(e){this.materials=e},_createParserState:function(){var e={objects:[],object:{},vertices:[],normals:[],uvs:[],materialLibraries:[],startObject:function(e,t){return this.object&&this.object.fromDeclaration===!1?(this.object.name=e,this.object.fromDeclaration=t!==!1,void 0):(this.object={name:e||"",geometry:{vertices:[],normals:[],uvs:[]},material:{name:"",smooth:!0},fromDeclaration:t!==!1},this.objects.push(this.object),void 0)},parseVertexIndex:function(e,t){var i=parseInt(e,10);return 3*(i>=0?i-1:i+t/3)},parseNormalIndex:function(e,t){var i=parseInt(e,10);return 3*(i>=0?i-1:i+t/3)},parseUVIndex:function(e,t){var i=parseInt(e,10);return 2*(i>=0?i-1:i+t/2)},addVertex:function(e,t,i){var r=this.vertices,n=this.object.geometry.vertices;n.push(r[e+0]),n.push(r[e+1]),n.push(r[e+2]),n.push(r[t+0]),n.push(r[t+1]),n.push(r[t+2]),n.push(r[i+0]),n.push(r[i+1]),n.push(r[i+2])},addVertexLine:function(e){var t=this.vertices,i=this.object.geometry.vertices;i.push(t[e+0]),i.push(t[e+1]),i.push(t[e+2])},addNormal:function(e,t,i){var r=this.normals,n=this.object.geometry.normals;n.push(r[e+0]),n.push(r[e+1]),n.push(r[e+2]),n.push(r[t+0]),n.push(r[t+1]),n.push(r[t+2]),n.push(r[i+0]),n.push(r[i+1]),n.push(r[i+2])},addUV:function(e,t,i){var r=this.uvs,n=this.object.geometry.uvs;n.push(r[e+0]),n.push(r[e+1]),n.push(r[t+0]),n.push(r[t+1]),n.push(r[i+0]),n.push(r[i+1])},addUVLine:function(e){var t=this.uvs,i=this.object.geometry.uvs;i.push(t[e+0]),i.push(t[e+1])},addFace:function(e,t,i,r,n,a,o,s,c,u,l,h){var d,p=this.vertices.length,f=this.parseVertexIndex(e,p),m=this.parseVertexIndex(t,p),g=this.parseVertexIndex(i,p);if(void 0===r?this.addVertex(f,m,g):(d=this.parseVertexIndex(r,p),this.addVertex(f,m,d),this.addVertex(m,g,d)),void 0!==n){var v=this.uvs.length;f=this.parseUVIndex(n,v),m=this.parseUVIndex(a,v),g=this.parseUVIndex(o,v),void 0===r?this.addUV(f,m,g):(d=this.parseUVIndex(s,v),this.addUV(f,m,d),this.addUV(m,g,d))}if(void 0!==c){var y=this.normals.length;f=this.parseNormalIndex(c,y),m=c===u?f:this.parseNormalIndex(u,y),g=c===l?f:this.parseNormalIndex(l,y),void 0===r?this.addNormal(f,m,g):(d=this.parseNormalIndex(h,y),this.addNormal(f,m,d),this.addNormal(m,g,d))}},addLineGeometry:function(e,t){this.object.geometry.type="Line";for(var i=this.vertices.length,r=this.uvs.length,n=0,a=e.length;a>n;n++)this.addVertexLine(this.parseVertexIndex(e[n],i));for(var o=0,a=t.length;a>o;o++)this.addUVLine(this.parseUVIndex(t[o],r))}};return e.startObject("",!1),e},parse:function(e){console.time("OBJLoader");var t=this._createParserState();-1!==e.indexOf("\r\n")&&(e=e.replace("\r\n","\n"));for(var i=e.split("\n"),r="",n="",a="",o=0,s=[],c="function"==typeof"".trimLeft,u=0,l=i.length;l>u;u++)if(r=i[u],r=c?r.trimLeft():r.trim(),o=r.length,0!==o&&(n=r.charAt(0),"#"!==n))if("v"===n)if(a=r.charAt(1)," "===a&&null!==(s=this.regexp.vertex_pattern.exec(r)))t.vertices.push(parseFloat(s[1]),parseFloat(s[2]),parseFloat(s[3]));else if("n"===a&&null!==(s=this.regexp.normal_pattern.exec(r)))t.normals.push(parseFloat(s[1]),parseFloat(s[2]),parseFloat(s[3]));else{if("t"!==a||null===(s=this.regexp.uv_pattern.exec(r)))throw new Error("Unexpected vertex/normal/uv line: '"+r+"'");t.uvs.push(parseFloat(s[1]),parseFloat(s[2]))}else if("f"===n)if(null!==(s=this.regexp.face_vertex_uv_normal.exec(r)))t.addFace(s[1],s[4],s[7],s[10],s[2],s[5],s[8],s[11],s[3],s[6],s[9],s[12]);else if(null!==(s=this.regexp.face_vertex_uv.exec(r)))t.addFace(s[1],s[3],s[5],s[7],s[2],s[4],s[6],s[8]);else if(null!==(s=this.regexp.face_vertex_normal.exec(r)))t.addFace(s[1],s[3],s[5],s[7],void 0,void 0,void 0,void 0,s[2],s[4],s[6],s[8]);else{if(null===(s=this.regexp.face_vertex.exec(r)))throw new Error("Unexpected face line: '"+r+"'");t.addFace(s[1],s[2],s[3],s[4])}else if("l"===n){var h=r.substring(1).trim().split(" "),d=[],p=[];if(-1===r.indexOf("/"))d=h;else for(var f=0,m=h.length;m>f;f++){var g=h[f].split("/");""!==g[0]&&d.push(g[0]),""!==g[1]&&p.push(g[1])}t.addLineGeometry(d,p)}else if(null!==(s=this.regexp.object_pattern.exec(r))){var v=s[0].substr(1).trim();t.startObject(v)}else if(this.regexp.material_use_pattern.test(r))t.object.material.name=r.substring(7).trim();else if(this.regexp.material_library_pattern.test(r))t.materialLibraries.push(r.substring(7).trim());else{if(null===(s=this.regexp.smoothing_pattern.exec(r))){if("\x00"===r)continue;throw new Error("Unexpected line: '"+r+"'")}var y=s[1].trim().toLowerCase();t.object.material.smooth="1"===y||"on"===y}var M=new THREE.Group;M.materialLibraries=[].concat(t.materialLibraries);for(var u=0,l=t.objects.length;l>u;u++){var A=t.objects[u],x=A.geometry,w="Line"===x.type;if(0!==x.vertices.length){var E=new THREE.BufferGeometry;E.addAttribute("position",new THREE.BufferAttribute(new Float32Array(x.vertices),3)),x.normals.length>0?E.addAttribute("normal",new THREE.BufferAttribute(new Float32Array(x.normals),3)):E.computeVertexNormals(),x.uvs.length>0&&E.addAttribute("uv",new THREE.BufferAttribute(new Float32Array(x.uvs),2));var b;if(null!==this.materials&&(b=this.materials.create(A.material.name),w&&b&&!(b instanceof THREE.LineBasicMaterial))){var T=new THREE.LineBasicMaterial;T.copy(b),b=T}b||(b=w?new THREE.LineBasicMaterial:new THREE.MeshPhongMaterial,b.name=A.material.name),b.shading=A.material.smooth?THREE.SmoothShading:THREE.FlatShading;var L=w?new THREE.Line(E,b):new THREE.Mesh(E,b);L.name=A.name,M.add(L)}}return console.timeEnd("OBJLoader"),M}}},{}],17:[function(e,i,r){var n={REVISION:"76"};"function"==typeof t&&t.amd?t("three",n):"undefined"!=typeof r&&"undefined"!=typeof i&&(i.exports=n),void 0===Number.EPSILON&&(Number.EPSILON=Math.pow(2,-52)),void 0===Math.sign&&(Math.sign=function(e){return 0>e?-1:e>0?1:+e}),void 0===Function.prototype.name&&void 0!==Object.defineProperty&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1]}}),void 0===Object.assign&&Object.defineProperty(Object,"assign",{writable:!0,configurable:!0,value:function(e){"use strict";if(void 0===e||null===e)throw new TypeError("Cannot convert first argument to object");for(var t=Object(e),i=1,r=arguments.length;i!==r;++i){var n=arguments[i];if(void 0!==n&&null!==n){n=Object(n);for(var a=Object.keys(n),o=0,s=a.length;o!==s;++o){var c=a[o],u=Object.getOwnPropertyDescriptor(n,c);void 0!==u&&u.enumerable&&(t[c]=n[c])}}}return t}}),n.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2},n.CullFaceNone=0,n.CullFaceBack=1,n.CullFaceFront=2,n.CullFaceFrontBack=3,n.FrontFaceDirectionCW=0,n.FrontFaceDirectionCCW=1,n.BasicShadowMap=0,n.PCFShadowMap=1,n.PCFSoftShadowMap=2,n.FrontSide=0,n.BackSide=1,n.DoubleSide=2,n.FlatShading=1,n.SmoothShading=2,n.NoColors=0,n.FaceColors=1,n.VertexColors=2,n.NoBlending=0,n.NormalBlending=1,n.AdditiveBlending=2,n.SubtractiveBlending=3,n.MultiplyBlending=4,n.CustomBlending=5,n.AddEquation=100,n.SubtractEquation=101,n.ReverseSubtractEquation=102,n.MinEquation=103,n.MaxEquation=104,n.ZeroFactor=200,n.OneFactor=201,n.SrcColorFactor=202,n.OneMinusSrcColorFactor=203,n.SrcAlphaFactor=204,n.OneMinusSrcAlphaFactor=205,n.DstAlphaFactor=206,n.OneMinusDstAlphaFactor=207,n.DstColorFactor=208,n.OneMinusDstColorFactor=209,n.SrcAlphaSaturateFactor=210,n.NeverDepth=0,n.AlwaysDepth=1,n.LessDepth=2,n.LessEqualDepth=3,n.EqualDepth=4,n.GreaterEqualDepth=5,n.GreaterDepth=6,n.NotEqualDepth=7,n.MultiplyOperation=0,n.MixOperation=1,n.AddOperation=2,n.NoToneMapping=0,n.LinearToneMapping=1,n.ReinhardToneMapping=2,n.Uncharted2ToneMapping=3,n.CineonToneMapping=4,n.UVMapping=300,n.CubeReflectionMapping=301,n.CubeRefractionMapping=302,n.EquirectangularReflectionMapping=303,n.EquirectangularRefractionMapping=304,n.SphericalReflectionMapping=305,n.CubeUVReflectionMapping=306,n.CubeUVRefractionMapping=307,n.RepeatWrapping=1e3,n.ClampToEdgeWrapping=1001,n.MirroredRepeatWrapping=1002,n.NearestFilter=1003,n.NearestMipMapNearestFilter=1004,n.NearestMipMapLinearFilter=1005,n.LinearFilter=1006,n.LinearMipMapNearestFilter=1007,n.LinearMipMapLinearFilter=1008,n.UnsignedByteType=1009,n.ByteType=1010,n.ShortType=1011,n.UnsignedShortType=1012,n.IntType=1013,n.UnsignedIntType=1014,n.FloatType=1015,n.HalfFloatType=1025,n.UnsignedShort4444Type=1016,n.UnsignedShort5551Type=1017,n.UnsignedShort565Type=1018,n.AlphaFormat=1019,n.RGBFormat=1020,n.RGBAFormat=1021,n.LuminanceFormat=1022,n.LuminanceAlphaFormat=1023,n.RGBEFormat=n.RGBAFormat,n.DepthFormat=1026,n.RGB_S3TC_DXT1_Format=2001,n.RGBA_S3TC_DXT1_Format=2002,n.RGBA_S3TC_DXT3_Format=2003,n.RGBA_S3TC_DXT5_Format=2004,n.RGB_PVRTC_4BPPV1_Format=2100,n.RGB_PVRTC_2BPPV1_Format=2101,n.RGBA_PVRTC_4BPPV1_Format=2102,n.RGBA_PVRTC_2BPPV1_Format=2103,n.RGB_ETC1_Format=2151,n.LoopOnce=2200,n.LoopRepeat=2201,n.LoopPingPong=2202,n.InterpolateDiscrete=2300,n.InterpolateLinear=2301,n.InterpolateSmooth=2302,n.ZeroCurvatureEnding=2400,n.ZeroSlopeEnding=2401,n.WrapAroundEnding=2402,n.TrianglesDrawMode=0,n.TriangleStripDrawMode=1,n.TriangleFanDrawMode=2,n.LinearEncoding=3e3,n.sRGBEncoding=3001,n.GammaEncoding=3007,n.RGBEEncoding=3002,n.LogLuvEncoding=3003,n.RGBM7Encoding=3004,n.RGBM16Encoding=3005,n.RGBDEncoding=3006,n.BasicDepthPacking=3200,n.RGBADepthPacking=3201,n.Color=function(e){return 3===arguments.length?this.fromArray(arguments):this.set(e)},n.Color.prototype={constructor:n.Color,r:1,g:1,b:1,set:function(e){return e instanceof n.Color?this.copy(e):"number"==typeof e?this.setHex(e):"string"==typeof e&&this.setStyle(e),this},setScalar:function(e){this.r=e,this.g=e,this.b=e},setHex:function(e){return e=Math.floor(e),this.r=(e>>16&255)/255,this.g=(e>>8&255)/255,this.b=(255&e)/255,this},setRGB:function(e,t,i){return this.r=e,this.g=t,this.b=i,this},setHSL:function(){function e(e,t,i){return 0>i&&(i+=1),i>1&&(i-=1),1/6>i?e+6*(t-e)*i:.5>i?t:2/3>i?e+6*(t-e)*(2/3-i):e}return function(t,i,r){if(t=n.Math.euclideanModulo(t,1),i=n.Math.clamp(i,0,1),r=n.Math.clamp(r,0,1),0===i)this.r=this.g=this.b=r;else{var a=.5>=r?r*(1+i):r+i-r*i,o=2*r-a;this.r=e(o,a,t+1/3),this.g=e(o,a,t),this.b=e(o,a,t-1/3)}return this}}(),setStyle:function(e){function t(t){void 0!==t&&parseFloat(t)<1&&console.warn("THREE.Color: Alpha component of "+e+" will be ignored.")}var i;if(i=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(e)){var r,a=i[1],o=i[2];switch(a){case"rgb":case"rgba":if(r=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(o))return this.r=Math.min(255,parseInt(r[1],10))/255,this.g=Math.min(255,parseInt(r[2],10))/255,this.b=Math.min(255,parseInt(r[3],10))/255,t(r[5]),this;if(r=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(o))return this.r=Math.min(100,parseInt(r[1],10))/100,this.g=Math.min(100,parseInt(r[2],10))/100,this.b=Math.min(100,parseInt(r[3],10))/100,t(r[5]),this;break;case"hsl":case"hsla":if(r=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(o)){var s=parseFloat(r[1])/360,c=parseInt(r[2],10)/100,u=parseInt(r[3],10)/100;return t(r[5]),this.setHSL(s,c,u)}}}else if(i=/^\#([A-Fa-f0-9]+)$/.exec(e)){var l=i[1],h=l.length;if(3===h)return this.r=parseInt(l.charAt(0)+l.charAt(0),16)/255,this.g=parseInt(l.charAt(1)+l.charAt(1),16)/255,this.b=parseInt(l.charAt(2)+l.charAt(2),16)/255,this;if(6===h)return this.r=parseInt(l.charAt(0)+l.charAt(1),16)/255,this.g=parseInt(l.charAt(2)+l.charAt(3),16)/255,this.b=parseInt(l.charAt(4)+l.charAt(5),16)/255,this}if(e&&e.length>0){var l=n.ColorKeywords[e];void 0!==l?this.setHex(l):console.warn("THREE.Color: Unknown color "+e)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(e){return this.r=e.r,this.g=e.g,this.b=e.b,this},copyGammaToLinear:function(e,t){return void 0===t&&(t=2),this.r=Math.pow(e.r,t),this.g=Math.pow(e.g,t),this.b=Math.pow(e.b,t),this},copyLinearToGamma:function(e,t){void 0===t&&(t=2);var i=t>0?1/t:1;return this.r=Math.pow(e.r,i),this.g=Math.pow(e.g,i),this.b=Math.pow(e.b,i),this},convertGammaToLinear:function(){var e=this.r,t=this.g,i=this.b;return this.r=e*e,this.g=t*t,this.b=i*i,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(e){var t,i,r=e||{h:0,s:0,l:0},n=this.r,a=this.g,o=this.b,s=Math.max(n,a,o),c=Math.min(n,a,o),u=(c+s)/2;if(c===s)t=0,i=0;else{var l=s-c;switch(i=.5>=u?l/(s+c):l/(2-s-c),s){case n:t=(a-o)/l+(o>a?6:0);break;case a:t=(o-n)/l+2;break;case o:t=(n-a)/l+4}t/=6}return r.h=t,r.s=i,r.l=u,r},getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},offsetHSL:function(e,t,i){var r=this.getHSL();return r.h+=e,r.s+=t,r.l+=i,this.setHSL(r.h,r.s,r.l),this},add:function(e){return this.r+=e.r,this.g+=e.g,this.b+=e.b,this},addColors:function(e,t){return this.r=e.r+t.r,this.g=e.g+t.g,this.b=e.b+t.b,this},addScalar:function(e){return this.r+=e,this.g+=e,this.b+=e,this},multiply:function(e){return this.r*=e.r,this.g*=e.g,this.b*=e.b,this},multiplyScalar:function(e){return this.r*=e,this.g*=e,this.b*=e,this},lerp:function(e,t){return this.r+=(e.r-this.r)*t,this.g+=(e.g-this.g)*t,this.b+=(e.b-this.b)*t,this},equals:function(e){return e.r===this.r&&e.g===this.g&&e.b===this.b},fromArray:function(e,t){return void 0===t&&(t=0),this.r=e[t],this.g=e[t+1],this.b=e[t+2],this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this.r,e[t+1]=this.g,e[t+2]=this.b,e}},n.ColorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},n.Quaternion=function(e,t,i,r){this._x=e||0,this._y=t||0,this._z=i||0,this._w=void 0!==r?r:1},n.Quaternion.prototype={constructor:n.Quaternion,get x(){return this._x},set x(e){this._x=e,this.onChangeCallback()},get y(){return this._y},set y(e){this._y=e,this.onChangeCallback()},get z(){return this._z},set z(e){this._z=e,this.onChangeCallback()},get w(){return this._w},set w(e){this._w=e,this.onChangeCallback()},set:function(e,t,i,r){return this._x=e,this._y=t,this._z=i,this._w=r,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(e){return this._x=e.x,this._y=e.y,this._z=e.z,this._w=e.w,this.onChangeCallback(),this},setFromEuler:function(e,t){if(e instanceof n.Euler==!1)throw new Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var i=Math.cos(e._x/2),r=Math.cos(e._y/2),a=Math.cos(e._z/2),o=Math.sin(e._x/2),s=Math.sin(e._y/2),c=Math.sin(e._z/2),u=e.order;return"XYZ"===u?(this._x=o*r*a+i*s*c,this._y=i*s*a-o*r*c,this._z=i*r*c+o*s*a,this._w=i*r*a-o*s*c):"YXZ"===u?(this._x=o*r*a+i*s*c,this._y=i*s*a-o*r*c,this._z=i*r*c-o*s*a,this._w=i*r*a+o*s*c):"ZXY"===u?(this._x=o*r*a-i*s*c,this._y=i*s*a+o*r*c,this._z=i*r*c+o*s*a,this._w=i*r*a-o*s*c):"ZYX"===u?(this._x=o*r*a-i*s*c,this._y=i*s*a+o*r*c,this._z=i*r*c-o*s*a,this._w=i*r*a+o*s*c):"YZX"===u?(this._x=o*r*a+i*s*c,this._y=i*s*a+o*r*c,this._z=i*r*c-o*s*a,this._w=i*r*a-o*s*c):"XZY"===u&&(this._x=o*r*a-i*s*c,this._y=i*s*a-o*r*c,this._z=i*r*c+o*s*a,this._w=i*r*a+o*s*c),t!==!1&&this.onChangeCallback(),this},setFromAxisAngle:function(e,t){var i=t/2,r=Math.sin(i);return this._x=e.x*r,this._y=e.y*r,this._z=e.z*r,this._w=Math.cos(i),this.onChangeCallback(),this},setFromRotationMatrix:function(e){var t,i=e.elements,r=i[0],n=i[4],a=i[8],o=i[1],s=i[5],c=i[9],u=i[2],l=i[6],h=i[10],d=r+s+h;return d>0?(t=.5/Math.sqrt(d+1),this._w=.25/t,this._x=(l-c)*t,this._y=(a-u)*t,this._z=(o-n)*t):r>s&&r>h?(t=2*Math.sqrt(1+r-s-h),this._w=(l-c)/t,this._x=.25*t,this._y=(n+o)/t,this._z=(a+u)/t):s>h?(t=2*Math.sqrt(1+s-r-h),this._w=(a-u)/t,this._x=(n+o)/t,this._y=.25*t,this._z=(c+l)/t):(t=2*Math.sqrt(1+h-r-s),this._w=(o-n)/t,this._x=(a+u)/t,this._y=(c+l)/t,this._z=.25*t),this.onChangeCallback(),this},setFromUnitVectors:function(){var e,t,i=1e-6;return function(r,a){return void 0===e&&(e=new n.Vector3),t=r.dot(a)+1,i>t?(t=0,Math.abs(r.x)>Math.abs(r.z)?e.set(-r.y,r.x,0):e.set(0,-r.z,r.y)):e.crossVectors(r,a),this._x=e.x,this._y=e.y,this._z=e.z,this._w=t,this.normalize(),this}}(),inverse:function(){return this.conjugate().normalize(),this},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(e){return this._x*e._x+this._y*e._y+this._z*e._z+this._w*e._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var e=this.length();return 0===e?(this._x=0,this._y=0,this._z=0,this._w=1):(e=1/e,this._x=this._x*e,this._y=this._y*e,this._z=this._z*e,this._w=this._w*e),this.onChangeCallback(),this},multiply:function(e,t){return void 0!==t?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(e,t)):this.multiplyQuaternions(this,e)},multiplyQuaternions:function(e,t){var i=e._x,r=e._y,n=e._z,a=e._w,o=t._x,s=t._y,c=t._z,u=t._w;return this._x=i*u+a*o+r*c-n*s,this._y=r*u+a*s+n*o-i*c,this._z=n*u+a*c+i*s-r*o,this._w=a*u-i*o-r*s-n*c,this.onChangeCallback(),this},slerp:function(e,t){if(0===t)return this;if(1===t)return this.copy(e);var i=this._x,r=this._y,n=this._z,a=this._w,o=a*e._w+i*e._x+r*e._y+n*e._z;if(0>o?(this._w=-e._w,this._x=-e._x,this._y=-e._y,this._z=-e._z,o=-o):this.copy(e),o>=1)return this._w=a,this._x=i,this._y=r,this._z=n,this;var s=Math.sqrt(1-o*o);if(Math.abs(s)<.001)return this._w=.5*(a+this._w),this._x=.5*(i+this._x),this._y=.5*(r+this._y),this._z=.5*(n+this._z),this;var c=Math.atan2(s,o),u=Math.sin((1-t)*c)/s,l=Math.sin(t*c)/s;return this._w=a*u+this._w*l,this._x=i*u+this._x*l,this._y=r*u+this._y*l,this._z=n*u+this._z*l,this.onChangeCallback(),this},equals:function(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._w===this._w},fromArray:function(e,t){return void 0===t&&(t=0),this._x=e[t],this._y=e[t+1],this._z=e[t+2],this._w=e[t+3],this.onChangeCallback(),this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._w,e},onChange:function(e){return this.onChangeCallback=e,this},onChangeCallback:function(){}},Object.assign(n.Quaternion,{slerp:function(e,t,i,r){return i.copy(e).slerp(t,r)},slerpFlat:function(e,t,i,r,n,a,o){var s=i[r+0],c=i[r+1],u=i[r+2],l=i[r+3],h=n[a+0],d=n[a+1],p=n[a+2],f=n[a+3];if(l!==f||s!==h||c!==d||u!==p){var m=1-o,g=s*h+c*d+u*p+l*f,v=g>=0?1:-1,y=1-g*g;if(y>Number.EPSILON){var M=Math.sqrt(y),A=Math.atan2(M,g*v);m=Math.sin(m*A)/M,o=Math.sin(o*A)/M}var x=o*v;if(s=s*m+h*x,c=c*m+d*x,u=u*m+p*x,l=l*m+f*x,m===1-o){var w=1/Math.sqrt(s*s+c*c+u*u+l*l);s*=w,c*=w,u*=w,l*=w}}e[t]=s,e[t+1]=c,e[t+2]=u,e[t+3]=l}}),n.Vector2=function(e,t){this.x=e||0,this.y=t||0},n.Vector2.prototype={constructor:n.Vector2,get width(){return this.x},set width(e){this.x=e},get height(){return this.y},set height(e){this.y=e},set:function(e,t){return this.x=e,this.y=t,this},setScalar:function(e){return this.x=e,this.y=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;default:throw new Error("index is out of range: "+e)}},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(e){return this.x=e.x,this.y=e.y,this},add:function(e,t){return void 0!==t?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this)},addScalar:function(e){return this.x+=e,this.y+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this},sub:function(e,t){return void 0!==t?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this)},subScalar:function(e){return this.x-=e,this.y-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this},multiply:function(e){return this.x*=e.x,this.y*=e.y,this},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e):(this.x=0,this.y=0),this},divide:function(e){return this.x/=e.x,this.y/=e.y,this},divideScalar:function(e){return this.multiplyScalar(1/e)},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this},clampScalar:function(){var e,t;
return function(i,r){return void 0===e&&(e=new n.Vector2,t=new n.Vector2),e.set(i,i),t.set(r,r),this.clamp(e,t)}}(),clampLength:function(e,t){var i=this.length();return this.multiplyScalar(Math.max(e,Math.min(t,i))/i),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(e){return this.x*e.x+this.y*e.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},angle:function(){var e=Math.atan2(this.y,this.x);return 0>e&&(e+=2*Math.PI),e},distanceTo:function(e){return Math.sqrt(this.distanceToSquared(e))},distanceToSquared:function(e){var t=this.x-e.x,i=this.y-e.y;return t*t+i*i},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e),this},equals:function(e){return e.x===this.x&&e.y===this.y},fromArray:function(e,t){return void 0===t&&(t=0),this.x=e[t],this.y=e[t+1],this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this.x,e[t+1]=this.y,e},fromAttribute:function(e,t,i){return void 0===i&&(i=0),t=t*e.itemSize+i,this.x=e.array[t],this.y=e.array[t+1],this},rotateAround:function(e,t){var i=Math.cos(t),r=Math.sin(t),n=this.x-e.x,a=this.y-e.y;return this.x=n*i-a*r+e.x,this.y=n*r+a*i+e.y,this}},n.Vector3=function(e,t,i){this.x=e||0,this.y=t||0,this.z=i||0},n.Vector3.prototype={constructor:n.Vector3,set:function(e,t,i){return this.x=e,this.y=t,this.z=i,this},setScalar:function(e){return this.x=e,this.y=e,this.z=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setZ:function(e){return this.z=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;default:throw new Error("index is out of range: "+e)}},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this},add:function(e,t){return void 0!==t?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this.z+=e.z,this)},addScalar:function(e){return this.x+=e,this.y+=e,this.z+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this},sub:function(e,t){return void 0!==t?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this.z-=e.z,this)},subScalar:function(e){return this.x-=e,this.y-=e,this.z-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this},multiply:function(e,t){return void 0!==t?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(e,t)):(this.x*=e.x,this.y*=e.y,this.z*=e.z,this)},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e,this.z*=e):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(e,t){return this.x=e.x*t.x,this.y=e.y*t.y,this.z=e.z*t.z,this},applyEuler:function(){var e;return function(t){return t instanceof n.Euler==!1&&console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."),void 0===e&&(e=new n.Quaternion),this.applyQuaternion(e.setFromEuler(t)),this}}(),applyAxisAngle:function(){var e;return function(t,i){return void 0===e&&(e=new n.Quaternion),this.applyQuaternion(e.setFromAxisAngle(t,i)),this}}(),applyMatrix3:function(e){var t=this.x,i=this.y,r=this.z,n=e.elements;return this.x=n[0]*t+n[3]*i+n[6]*r,this.y=n[1]*t+n[4]*i+n[7]*r,this.z=n[2]*t+n[5]*i+n[8]*r,this},applyMatrix4:function(e){var t=this.x,i=this.y,r=this.z,n=e.elements;return this.x=n[0]*t+n[4]*i+n[8]*r+n[12],this.y=n[1]*t+n[5]*i+n[9]*r+n[13],this.z=n[2]*t+n[6]*i+n[10]*r+n[14],this},applyProjection:function(e){var t=this.x,i=this.y,r=this.z,n=e.elements,a=1/(n[3]*t+n[7]*i+n[11]*r+n[15]);return this.x=(n[0]*t+n[4]*i+n[8]*r+n[12])*a,this.y=(n[1]*t+n[5]*i+n[9]*r+n[13])*a,this.z=(n[2]*t+n[6]*i+n[10]*r+n[14])*a,this},applyQuaternion:function(e){var t=this.x,i=this.y,r=this.z,n=e.x,a=e.y,o=e.z,s=e.w,c=s*t+a*r-o*i,u=s*i+o*t-n*r,l=s*r+n*i-a*t,h=-n*t-a*i-o*r;return this.x=c*s+h*-n+u*-o-l*-a,this.y=u*s+h*-a+l*-n-c*-o,this.z=l*s+h*-o+c*-a-u*-n,this},project:function(){var e;return function(t){return void 0===e&&(e=new n.Matrix4),e.multiplyMatrices(t.projectionMatrix,e.getInverse(t.matrixWorld)),this.applyProjection(e)}}(),unproject:function(){var e;return function(t){return void 0===e&&(e=new n.Matrix4),e.multiplyMatrices(t.matrixWorld,e.getInverse(t.projectionMatrix)),this.applyProjection(e)}}(),transformDirection:function(e){var t=this.x,i=this.y,r=this.z,n=e.elements;return this.x=n[0]*t+n[4]*i+n[8]*r,this.y=n[1]*t+n[5]*i+n[9]*r,this.z=n[2]*t+n[6]*i+n[10]*r,this.normalize(),this},divide:function(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this},divideScalar:function(e){return this.multiplyScalar(1/e)},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this},clampScalar:function(){var e,t;return function(i,r){return void 0===e&&(e=new n.Vector3,t=new n.Vector3),e.set(i,i,i),t.set(r,r,r),this.clamp(e,t)}}(),clampLength:function(e,t){var i=this.length();return this.multiplyScalar(Math.max(e,Math.min(t,i))/i),this},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e),this},cross:function(e,t){if(void 0!==t)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(e,t);var i=this.x,r=this.y,n=this.z;return this.x=r*e.z-n*e.y,this.y=n*e.x-i*e.z,this.z=i*e.y-r*e.x,this},crossVectors:function(e,t){var i=e.x,r=e.y,n=e.z,a=t.x,o=t.y,s=t.z;return this.x=r*s-n*o,this.y=n*a-i*s,this.z=i*o-r*a,this},projectOnVector:function(){var e,t;return function(i){return void 0===e&&(e=new n.Vector3),e.copy(i).normalize(),t=this.dot(e),this.copy(e).multiplyScalar(t)}}(),projectOnPlane:function(){var e;return function(t){return void 0===e&&(e=new n.Vector3),e.copy(this).projectOnVector(t),this.sub(e)}}(),reflect:function(){var e;return function(t){return void 0===e&&(e=new n.Vector3),this.sub(e.copy(t).multiplyScalar(2*this.dot(t)))}}(),angleTo:function(e){var t=this.dot(e)/Math.sqrt(this.lengthSq()*e.lengthSq());return Math.acos(n.Math.clamp(t,-1,1))},distanceTo:function(e){return Math.sqrt(this.distanceToSquared(e))},distanceToSquared:function(e){var t=this.x-e.x,i=this.y-e.y,r=this.z-e.z;return t*t+i*i+r*r},setFromSpherical:function(e){var t=Math.sin(e.phi)*e.radius;return this.x=t*Math.sin(e.theta),this.y=Math.cos(e.phi)*e.radius,this.z=t*Math.cos(e.theta),this},setFromMatrixPosition:function(e){return this.setFromMatrixColumn(e,3)},setFromMatrixScale:function(e){var t=this.setFromMatrixColumn(e,0).length(),i=this.setFromMatrixColumn(e,1).length(),r=this.setFromMatrixColumn(e,2).length();return this.x=t,this.y=i,this.z=r,this},setFromMatrixColumn:function(e,t){return"number"==typeof e&&(console.warn("THREE.Vector3: setFromMatrixColumn now expects ( matrix, index )."),e=arguments[1],t=arguments[0]),this.fromArray(e.elements,4*t)},equals:function(e){return e.x===this.x&&e.y===this.y&&e.z===this.z},fromArray:function(e,t){return void 0===t&&(t=0),this.x=e[t],this.y=e[t+1],this.z=e[t+2],this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e},fromAttribute:function(e,t,i){return void 0===i&&(i=0),t=t*e.itemSize+i,this.x=e.array[t],this.y=e.array[t+1],this.z=e.array[t+2],this}},n.Vector4=function(e,t,i,r){this.x=e||0,this.y=t||0,this.z=i||0,this.w=void 0!==r?r:1},n.Vector4.prototype={constructor:n.Vector4,set:function(e,t,i,r){return this.x=e,this.y=t,this.z=i,this.w=r,this},setScalar:function(e){return this.x=e,this.y=e,this.z=e,this.w=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setZ:function(e){return this.z=e,this},setW:function(e){return this.w=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;case 3:this.w=t;break;default:throw new Error("index is out of range: "+e)}},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=void 0!==e.w?e.w:1,this},add:function(e,t){return void 0!==t?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this.z+=e.z,this.w+=e.w,this)},addScalar:function(e){return this.x+=e,this.y+=e,this.z+=e,this.w+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this.w=e.w+t.w,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this.w+=e.w*t,this},sub:function(e,t){return void 0!==t?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this.z-=e.z,this.w-=e.w,this)},subScalar:function(e){return this.x-=e,this.y-=e,this.z-=e,this.w-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this.w=e.w-t.w,this},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e,this.z*=e,this.w*=e):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(e){var t=this.x,i=this.y,r=this.z,n=this.w,a=e.elements;return this.x=a[0]*t+a[4]*i+a[8]*r+a[12]*n,this.y=a[1]*t+a[5]*i+a[9]*r+a[13]*n,this.z=a[2]*t+a[6]*i+a[10]*r+a[14]*n,this.w=a[3]*t+a[7]*i+a[11]*r+a[15]*n,this},divideScalar:function(e){return this.multiplyScalar(1/e)},setAxisAngleFromQuaternion:function(e){this.w=2*Math.acos(e.w);var t=Math.sqrt(1-e.w*e.w);return 1e-4>t?(this.x=1,this.y=0,this.z=0):(this.x=e.x/t,this.y=e.y/t,this.z=e.z/t),this},setAxisAngleFromRotationMatrix:function(e){var t,i,r,n,a=.01,o=.1,s=e.elements,c=s[0],u=s[4],l=s[8],h=s[1],d=s[5],p=s[9],f=s[2],m=s[6],g=s[10];if(Math.abs(u-h)<a&&Math.abs(l-f)<a&&Math.abs(p-m)<a){if(Math.abs(u+h)<o&&Math.abs(l+f)<o&&Math.abs(p+m)<o&&Math.abs(c+d+g-3)<o)return this.set(1,0,0,0),this;t=Math.PI;var v=(c+1)/2,y=(d+1)/2,M=(g+1)/2,A=(u+h)/4,x=(l+f)/4,w=(p+m)/4;return v>y&&v>M?a>v?(i=0,r=.707106781,n=.707106781):(i=Math.sqrt(v),r=A/i,n=x/i):y>M?a>y?(i=.707106781,r=0,n=.707106781):(r=Math.sqrt(y),i=A/r,n=w/r):a>M?(i=.707106781,r=.707106781,n=0):(n=Math.sqrt(M),i=x/n,r=w/n),this.set(i,r,n,t),this}var E=Math.sqrt((m-p)*(m-p)+(l-f)*(l-f)+(h-u)*(h-u));return Math.abs(E)<.001&&(E=1),this.x=(m-p)/E,this.y=(l-f)/E,this.z=(h-u)/E,this.w=Math.acos((c+d+g-1)/2),this},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this.w=Math.min(this.w,e.w),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this.w=Math.max(this.w,e.w),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this.w=Math.max(e.w,Math.min(t.w,this.w)),this},clampScalar:function(){var e,t;return function(i,r){return void 0===e&&(e=new n.Vector4,t=new n.Vector4),e.set(i,i,i,i),t.set(r,r,r,r),this.clamp(e,t)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z+this.w*e.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this.w+=(e.w-this.w)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e),this},equals:function(e){return e.x===this.x&&e.y===this.y&&e.z===this.z&&e.w===this.w},fromArray:function(e,t){return void 0===t&&(t=0),this.x=e[t],this.y=e[t+1],this.z=e[t+2],this.w=e[t+3],this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e[t+3]=this.w,e},fromAttribute:function(e,t,i){return void 0===i&&(i=0),t=t*e.itemSize+i,this.x=e.array[t],this.y=e.array[t+1],this.z=e.array[t+2],this.w=e.array[t+3],this}},n.Euler=function(e,t,i,r){this._x=e||0,this._y=t||0,this._z=i||0,this._order=r||n.Euler.DefaultOrder},n.Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],n.Euler.DefaultOrder="XYZ",n.Euler.prototype={constructor:n.Euler,get x(){return this._x},set x(e){this._x=e,this.onChangeCallback()},get y(){return this._y},set y(e){this._y=e,this.onChangeCallback()},get z(){return this._z},set z(e){this._z=e,this.onChangeCallback()},get order(){return this._order},set order(e){this._order=e,this.onChangeCallback()},set:function(e,t,i,r){return this._x=e,this._y=t,this._z=i,this._order=r||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(e){return this._x=e._x,this._y=e._y,this._z=e._z,this._order=e._order,this.onChangeCallback(),this},setFromRotationMatrix:function(e,t,i){var r=n.Math.clamp,a=e.elements,o=a[0],s=a[4],c=a[8],u=a[1],l=a[5],h=a[9],d=a[2],p=a[6],f=a[10];return t=t||this._order,"XYZ"===t?(this._y=Math.asin(r(c,-1,1)),Math.abs(c)<.99999?(this._x=Math.atan2(-h,f),this._z=Math.atan2(-s,o)):(this._x=Math.atan2(p,l),this._z=0)):"YXZ"===t?(this._x=Math.asin(-r(h,-1,1)),Math.abs(h)<.99999?(this._y=Math.atan2(c,f),this._z=Math.atan2(u,l)):(this._y=Math.atan2(-d,o),this._z=0)):"ZXY"===t?(this._x=Math.asin(r(p,-1,1)),Math.abs(p)<.99999?(this._y=Math.atan2(-d,f),this._z=Math.atan2(-s,l)):(this._y=0,this._z=Math.atan2(u,o))):"ZYX"===t?(this._y=Math.asin(-r(d,-1,1)),Math.abs(d)<.99999?(this._x=Math.atan2(p,f),this._z=Math.atan2(u,o)):(this._x=0,this._z=Math.atan2(-s,l))):"YZX"===t?(this._z=Math.asin(r(u,-1,1)),Math.abs(u)<.99999?(this._x=Math.atan2(-h,l),this._y=Math.atan2(-d,o)):(this._x=0,this._y=Math.atan2(c,f))):"XZY"===t?(this._z=Math.asin(-r(s,-1,1)),Math.abs(s)<.99999?(this._x=Math.atan2(p,l),this._y=Math.atan2(c,o)):(this._x=Math.atan2(-h,f),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+t),this._order=t,i!==!1&&this.onChangeCallback(),this},setFromQuaternion:function(){var e;return function(t,i,r){return void 0===e&&(e=new n.Matrix4),e.makeRotationFromQuaternion(t),this.setFromRotationMatrix(e,i,r),this}}(),setFromVector3:function(e,t){return this.set(e.x,e.y,e.z,t||this._order)},reorder:function(){var e=new n.Quaternion;return function(t){e.setFromEuler(this),this.setFromQuaternion(e,t)}}(),equals:function(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._order===this._order},fromArray:function(e){return this._x=e[0],this._y=e[1],this._z=e[2],void 0!==e[3]&&(this._order=e[3]),this.onChangeCallback(),this},toArray:function(e,t){return void 0===e&&(e=[]),void 0===t&&(t=0),e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._order,e},toVector3:function(e){return e?e.set(this._x,this._y,this._z):new n.Vector3(this._x,this._y,this._z)},onChange:function(e){return this.onChangeCallback=e,this},onChangeCallback:function(){}},n.Line3=function(e,t){this.start=void 0!==e?e:new n.Vector3,this.end=void 0!==t?t:new n.Vector3},n.Line3.prototype={constructor:n.Line3,set:function(e,t){return this.start.copy(e),this.end.copy(t),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.start.copy(e.start),this.end.copy(e.end),this},center:function(e){var t=e||new n.Vector3;return t.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(e){var t=e||new n.Vector3;return t.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(e,t){var i=t||new n.Vector3;return this.delta(i).multiplyScalar(e).add(this.start)},closestPointToPointParameter:function(){var e=new n.Vector3,t=new n.Vector3;return function(i,r){e.subVectors(i,this.start),t.subVectors(this.end,this.start);var a=t.dot(t),o=t.dot(e),s=o/a;return r&&(s=n.Math.clamp(s,0,1)),s}}(),closestPointToPoint:function(e,t,i){var r=this.closestPointToPointParameter(e,t),a=i||new n.Vector3;return this.delta(a).multiplyScalar(r).add(this.start)},applyMatrix4:function(e){return this.start.applyMatrix4(e),this.end.applyMatrix4(e),this},equals:function(e){return e.start.equals(this.start)&&e.end.equals(this.end)}},n.Box2=function(e,t){this.min=void 0!==e?e:new n.Vector2(+1/0,+1/0),this.max=void 0!==t?t:new n.Vector2(-1/0,-1/0)},n.Box2.prototype={constructor:n.Box2,set:function(e,t){return this.min.copy(e),this.max.copy(t),this},setFromPoints:function(e){this.makeEmpty();for(var t=0,i=e.length;i>t;t++)this.expandByPoint(e[t]);return this},setFromCenterAndSize:function(){var e=new n.Vector2;return function(t,i){var r=e.copy(i).multiplyScalar(.5);return this.min.copy(t).sub(r),this.max.copy(t).add(r),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.min.copy(e.min),this.max.copy(e.max),this},makeEmpty:function(){return this.min.x=this.min.y=+1/0,this.max.x=this.max.y=-1/0,this},isEmpty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(e){var t=e||new n.Vector2;return t.addVectors(this.min,this.max).multiplyScalar(.5)},size:function(e){var t=e||new n.Vector2;return t.subVectors(this.max,this.min)},expandByPoint:function(e){return this.min.min(e),this.max.max(e),this},expandByVector:function(e){return this.min.sub(e),this.max.add(e),this},expandByScalar:function(e){return this.min.addScalar(-e),this.max.addScalar(e),this},containsPoint:function(e){return e.x<this.min.x||e.x>this.max.x||e.y<this.min.y||e.y>this.max.y?!1:!0},containsBox:function(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y?!0:!1},getParameter:function(e,t){var i=t||new n.Vector2;return i.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(e){return e.max.x<this.min.x||e.min.x>this.max.x||e.max.y<this.min.y||e.min.y>this.max.y?!1:!0},clampPoint:function(e,t){var i=t||new n.Vector2;return i.copy(e).clamp(this.min,this.max)},distanceToPoint:function(){var e=new n.Vector2;return function(t){var i=e.copy(t).clamp(this.min,this.max);return i.sub(t).length()}}(),intersect:function(e){return this.min.max(e.min),this.max.min(e.max),this},union:function(e){return this.min.min(e.min),this.max.max(e.max),this},translate:function(e){return this.min.add(e),this.max.add(e),this},equals:function(e){return e.min.equals(this.min)&&e.max.equals(this.max)}},n.Box3=function(e,t){this.min=void 0!==e?e:new n.Vector3(+1/0,+1/0,+1/0),this.max=void 0!==t?t:new n.Vector3(-1/0,-1/0,-1/0)},n.Box3.prototype={constructor:n.Box3,set:function(e,t){return this.min.copy(e),this.max.copy(t),this},setFromArray:function(e){for(var t=+1/0,i=+1/0,r=+1/0,n=-1/0,a=-1/0,o=-1/0,s=0,c=e.length;c>s;s+=3){var u=e[s],l=e[s+1],h=e[s+2];t>u&&(t=u),i>l&&(i=l),r>h&&(r=h),u>n&&(n=u),l>a&&(a=l),h>o&&(o=h)}this.min.set(t,i,r),this.max.set(n,a,o)},setFromPoints:function(e){this.makeEmpty();for(var t=0,i=e.length;i>t;t++)this.expandByPoint(e[t]);return this},setFromCenterAndSize:function(){var e=new n.Vector3;return function(t,i){var r=e.copy(i).multiplyScalar(.5);return this.min.copy(t).sub(r),this.max.copy(t).add(r),this}}(),setFromObject:function(){var e=new n.Vector3;return function(t){var i=this;return t.updateMatrixWorld(!0),this.makeEmpty(),t.traverse(function(t){var r=t.geometry;if(void 0!==r)if(r instanceof n.Geometry)for(var a=r.vertices,o=0,s=a.length;s>o;o++)e.copy(a[o]),e.applyMatrix4(t.matrixWorld),i.expandByPoint(e);else if(r instanceof n.BufferGeometry&&void 0!==r.attributes.position)for(var c=r.attributes.position.array,o=0,s=c.length;s>o;o+=3)e.fromArray(c,o),e.applyMatrix4(t.matrixWorld),i.expandByPoint(e)}),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.min.copy(e.min),this.max.copy(e.max),this},makeEmpty:function(){return this.min.x=this.min.y=this.min.z=+1/0,this.max.x=this.max.y=this.max.z=-1/0,this},isEmpty:function(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(e){var t=e||new n.Vector3;return t.addVectors(this.min,this.max).multiplyScalar(.5)},size:function(e){var t=e||new n.Vector3;return t.subVectors(this.max,this.min)},expandByPoint:function(e){return this.min.min(e),this.max.max(e),this},expandByVector:function(e){return this.min.sub(e),this.max.add(e),this},expandByScalar:function(e){return this.min.addScalar(-e),this.max.addScalar(e),this},containsPoint:function(e){return e.x<this.min.x||e.x>this.max.x||e.y<this.min.y||e.y>this.max.y||e.z<this.min.z||e.z>this.max.z?!1:!0},containsBox:function(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y&&this.min.z<=e.min.z&&e.max.z<=this.max.z?!0:!1},getParameter:function(e,t){var i=t||new n.Vector3;return i.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y),(e.z-this.min.z)/(this.max.z-this.min.z))},intersectsBox:function(e){return e.max.x<this.min.x||e.min.x>this.max.x||e.max.y<this.min.y||e.min.y>this.max.y||e.max.z<this.min.z||e.min.z>this.max.z?!1:!0},intersectsSphere:function(){var e;return function(t){return void 0===e&&(e=new n.Vector3),this.clampPoint(t.center,e),e.distanceToSquared(t.center)<=t.radius*t.radius}}(),intersectsPlane:function(e){var t,i;return e.normal.x>0?(t=e.normal.x*this.min.x,i=e.normal.x*this.max.x):(t=e.normal.x*this.max.x,i=e.normal.x*this.min.x),e.normal.y>0?(t+=e.normal.y*this.min.y,i+=e.normal.y*this.max.y):(t+=e.normal.y*this.max.y,i+=e.normal.y*this.min.y),e.normal.z>0?(t+=e.normal.z*this.min.z,i+=e.normal.z*this.max.z):(t+=e.normal.z*this.max.z,i+=e.normal.z*this.min.z),t<=e.constant&&i>=e.constant},clampPoint:function(e,t){var i=t||new n.Vector3;return i.copy(e).clamp(this.min,this.max)},distanceToPoint:function(){var e=new n.Vector3;return function(t){var i=e.copy(t).clamp(this.min,this.max);return i.sub(t).length()}}(),getBoundingSphere:function(){var e=new n.Vector3;return function(t){var i=t||new n.Sphere;return i.center=this.center(),i.radius=.5*this.size(e).length(),i}}(),intersect:function(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this},union:function(e){return this.min.min(e.min),this.max.max(e.max),this},applyMatrix4:function(){var e=[new n.Vector3,new n.Vector3,new n.Vector3,new n.Vector3,new n.Vector3,new n.Vector3,new n.Vector3,new n.Vector3];return function(t){return this.isEmpty()?this:(e[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),e[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),e[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),e[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),e[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),e[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),e[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),e[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(e),this)}}(),translate:function(e){return this.min.add(e),this.max.add(e),this},equals:function(e){return e.min.equals(this.min)&&e.max.equals(this.max)}},n.Matrix3=function(){this.elements=new Float32Array([1,0,0,0,1,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")},n.Matrix3.prototype={constructor:n.Matrix3,set:function(e,t,i,r,n,a,o,s,c){var u=this.elements;return u[0]=e,u[1]=r,u[2]=o,u[3]=t,u[4]=n,u[5]=s,u[6]=i,u[7]=a,u[8]=c,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(e){var t=e.elements;return this.set(t[0],t[3],t[6],t[1],t[4],t[7],t[2],t[5],t[8]),this},setFromMatrix4:function(e){var t=e.elements;return this.set(t[0],t[4],t[8],t[1],t[5],t[9],t[2],t[6],t[10]),this},applyToVector3Array:function(){var e;return function(t,i,r){void 0===e&&(e=new n.Vector3),void 0===i&&(i=0),void 0===r&&(r=t.length);for(var a=0,o=i;r>a;a+=3,o+=3)e.fromArray(t,o),e.applyMatrix3(this),e.toArray(t,o);return t}}(),applyToBuffer:function(){var e;return function(t,i,r){void 0===e&&(e=new n.Vector3),void 0===i&&(i=0),void 0===r&&(r=t.length/t.itemSize);for(var a=0,o=i;r>a;a++,o++)e.x=t.getX(o),e.y=t.getY(o),e.z=t.getZ(o),e.applyMatrix3(this),t.setXYZ(e.x,e.y,e.z);return t}}(),multiplyScalar:function(e){var t=this.elements;return t[0]*=e,t[3]*=e,t[6]*=e,t[1]*=e,t[4]*=e,t[7]*=e,t[2]*=e,t[5]*=e,t[8]*=e,this},determinant:function(){var e=this.elements,t=e[0],i=e[1],r=e[2],n=e[3],a=e[4],o=e[5],s=e[6],c=e[7],u=e[8];return t*a*u-t*o*c-i*n*u+i*o*s+r*n*c-r*a*s},getInverse:function(e,t){e instanceof n.Matrix4&&console.error("THREE.Matrix3.getInverse no longer takes a Matrix4 argument.");var i=e.elements,r=this.elements,a=i[0],o=i[1],s=i[2],c=i[3],u=i[4],l=i[5],h=i[6],d=i[7],p=i[8],f=p*u-l*d,m=l*h-p*c,g=d*c-u*h,v=a*f+o*m+s*g;if(0===v){var y="THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0";if(t)throw new Error(y);return console.warn(y),this.identity()}return r[0]=f,r[1]=s*d-p*o,r[2]=l*o-s*u,r[3]=m,r[4]=p*a-s*h,r[5]=s*c-l*a,r[6]=g,r[7]=o*h-d*a,r[8]=u*a-o*c,this.multiplyScalar(1/v)},transpose:function(){var e,t=this.elements;return e=t[1],t[1]=t[3],t[3]=e,e=t[2],t[2]=t[6],t[6]=e,e=t[5],t[5]=t[7],t[7]=e,this},flattenToArrayOffset:function(e,t){return console.warn("THREE.Matrix3: .flattenToArrayOffset is deprecated - just use .toArray instead."),this.toArray(e,t)},getNormalMatrix:function(e){return this.setFromMatrix4(e).getInverse(this).transpose()},transposeIntoArray:function(e){var t=this.elements;return e[0]=t[0],e[1]=t[3],e[2]=t[6],e[3]=t[1],e[4]=t[4],e[5]=t[7],e[6]=t[2],e[7]=t[5],e[8]=t[8],this},fromArray:function(e){return this.elements.set(e),this},toArray:function(e,t){void 0===e&&(e=[]),void 0===t&&(t=0);var i=this.elements;return e[t]=i[0],e[t+1]=i[1],e[t+2]=i[2],e[t+3]=i[3],e[t+4]=i[4],e[t+5]=i[5],e[t+6]=i[6],e[t+7]=i[7],e[t+8]=i[8],e}},n.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")},n.Matrix4.prototype={constructor:n.Matrix4,set:function(e,t,i,r,n,a,o,s,c,u,l,h,d,p,f,m){var g=this.elements;return g[0]=e,g[4]=t,g[8]=i,g[12]=r,g[1]=n,g[5]=a,g[9]=o,g[13]=s,g[2]=c,g[6]=u,g[10]=l,g[14]=h,g[3]=d,g[7]=p,g[11]=f,g[15]=m,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new n.Matrix4).fromArray(this.elements)},copy:function(e){return this.elements.set(e.elements),this},copyPosition:function(e){var t=this.elements,i=e.elements;return t[12]=i[12],t[13]=i[13],t[14]=i[14],this},extractBasis:function(e,t,i){return e.setFromMatrixColumn(this,0),t.setFromMatrixColumn(this,1),i.setFromMatrixColumn(this,2),this},makeBasis:function(e,t,i){return this.set(e.x,t.x,i.x,0,e.y,t.y,i.y,0,e.z,t.z,i.z,0,0,0,0,1),this},extractRotation:function(){var e;return function(t){void 0===e&&(e=new n.Vector3);var i=this.elements,r=t.elements,a=1/e.setFromMatrixColumn(t,0).length(),o=1/e.setFromMatrixColumn(t,1).length(),s=1/e.setFromMatrixColumn(t,2).length();return i[0]=r[0]*a,i[1]=r[1]*a,i[2]=r[2]*a,i[4]=r[4]*o,i[5]=r[5]*o,i[6]=r[6]*o,i[8]=r[8]*s,i[9]=r[9]*s,i[10]=r[10]*s,this}}(),makeRotationFromEuler:function(e){e instanceof n.Euler==!1&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var t=this.elements,i=e.x,r=e.y,a=e.z,o=Math.cos(i),s=Math.sin(i),c=Math.cos(r),u=Math.sin(r),l=Math.cos(a),h=Math.sin(a);if("XYZ"===e.order){var d=o*l,p=o*h,f=s*l,m=s*h;t[0]=c*l,t[4]=-c*h,t[8]=u,t[1]=p+f*u,t[5]=d-m*u,t[9]=-s*c,t[2]=m-d*u,t[6]=f+p*u,t[10]=o*c}else if("YXZ"===e.order){var g=c*l,v=c*h,y=u*l,M=u*h;t[0]=g+M*s,t[4]=y*s-v,t[8]=o*u,t[1]=o*h,t[5]=o*l,t[9]=-s,t[2]=v*s-y,t[6]=M+g*s,t[10]=o*c}else if("ZXY"===e.order){var g=c*l,v=c*h,y=u*l,M=u*h;t[0]=g-M*s,t[4]=-o*h,t[8]=y+v*s,t[1]=v+y*s,t[5]=o*l,t[9]=M-g*s,t[2]=-o*u,t[6]=s,t[10]=o*c}else if("ZYX"===e.order){var d=o*l,p=o*h,f=s*l,m=s*h;t[0]=c*l,t[4]=f*u-p,t[8]=d*u+m,t[1]=c*h,t[5]=m*u+d,t[9]=p*u-f,t[2]=-u,t[6]=s*c,t[10]=o*c}else if("YZX"===e.order){var A=o*c,x=o*u,w=s*c,E=s*u;t[0]=c*l,t[4]=E-A*h,t[8]=w*h+x,t[1]=h,t[5]=o*l,t[9]=-s*l,t[2]=-u*l,t[6]=x*h+w,t[10]=A-E*h}else if("XZY"===e.order){var A=o*c,x=o*u,w=s*c,E=s*u;t[0]=c*l,t[4]=-h,t[8]=u*l,t[1]=A*h+E,t[5]=o*l,t[9]=x*h-w,t[2]=w*h-x,t[6]=s*l,t[10]=E*h+A}return t[3]=0,t[7]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this},makeRotationFromQuaternion:function(e){var t=this.elements,i=e.x,r=e.y,n=e.z,a=e.w,o=i+i,s=r+r,c=n+n,u=i*o,l=i*s,h=i*c,d=r*s,p=r*c,f=n*c,m=a*o,g=a*s,v=a*c;
return t[0]=1-(d+f),t[4]=l-v,t[8]=h+g,t[1]=l+v,t[5]=1-(u+f),t[9]=p-m,t[2]=h-g,t[6]=p+m,t[10]=1-(u+d),t[3]=0,t[7]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this},lookAt:function(){var e,t,i;return function(r,a,o){void 0===e&&(e=new n.Vector3),void 0===t&&(t=new n.Vector3),void 0===i&&(i=new n.Vector3);var s=this.elements;return i.subVectors(r,a).normalize(),0===i.lengthSq()&&(i.z=1),e.crossVectors(o,i).normalize(),0===e.lengthSq()&&(i.x+=1e-4,e.crossVectors(o,i).normalize()),t.crossVectors(i,e),s[0]=e.x,s[4]=t.x,s[8]=i.x,s[1]=e.y,s[5]=t.y,s[9]=i.y,s[2]=e.z,s[6]=t.z,s[10]=i.z,this}}(),multiply:function(e,t){return void 0!==t?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(e,t)):this.multiplyMatrices(this,e)},premultiply:function(e){return this.multiplyMatrices(e,this)},multiplyMatrices:function(e,t){var i=e.elements,r=t.elements,n=this.elements,a=i[0],o=i[4],s=i[8],c=i[12],u=i[1],l=i[5],h=i[9],d=i[13],p=i[2],f=i[6],m=i[10],g=i[14],v=i[3],y=i[7],M=i[11],A=i[15],x=r[0],w=r[4],E=r[8],b=r[12],T=r[1],L=r[5],N=r[9],D=r[13],S=r[2],C=r[6],I=r[10],j=r[14],_=r[3],O=r[7],z=r[11],k=r[15];return n[0]=a*x+o*T+s*S+c*_,n[4]=a*w+o*L+s*C+c*O,n[8]=a*E+o*N+s*I+c*z,n[12]=a*b+o*D+s*j+c*k,n[1]=u*x+l*T+h*S+d*_,n[5]=u*w+l*L+h*C+d*O,n[9]=u*E+l*N+h*I+d*z,n[13]=u*b+l*D+h*j+d*k,n[2]=p*x+f*T+m*S+g*_,n[6]=p*w+f*L+m*C+g*O,n[10]=p*E+f*N+m*I+g*z,n[14]=p*b+f*D+m*j+g*k,n[3]=v*x+y*T+M*S+A*_,n[7]=v*w+y*L+M*C+A*O,n[11]=v*E+y*N+M*I+A*z,n[15]=v*b+y*D+M*j+A*k,this},multiplyToArray:function(e,t,i){var r=this.elements;return this.multiplyMatrices(e,t),i[0]=r[0],i[1]=r[1],i[2]=r[2],i[3]=r[3],i[4]=r[4],i[5]=r[5],i[6]=r[6],i[7]=r[7],i[8]=r[8],i[9]=r[9],i[10]=r[10],i[11]=r[11],i[12]=r[12],i[13]=r[13],i[14]=r[14],i[15]=r[15],this},multiplyScalar:function(e){var t=this.elements;return t[0]*=e,t[4]*=e,t[8]*=e,t[12]*=e,t[1]*=e,t[5]*=e,t[9]*=e,t[13]*=e,t[2]*=e,t[6]*=e,t[10]*=e,t[14]*=e,t[3]*=e,t[7]*=e,t[11]*=e,t[15]*=e,this},applyToVector3Array:function(){var e;return function(t,i,r){void 0===e&&(e=new n.Vector3),void 0===i&&(i=0),void 0===r&&(r=t.length);for(var a=0,o=i;r>a;a+=3,o+=3)e.fromArray(t,o),e.applyMatrix4(this),e.toArray(t,o);return t}}(),applyToBuffer:function(){var e;return function(t,i,r){void 0===e&&(e=new n.Vector3),void 0===i&&(i=0),void 0===r&&(r=t.length/t.itemSize);for(var a=0,o=i;r>a;a++,o++)e.x=t.getX(o),e.y=t.getY(o),e.z=t.getZ(o),e.applyMatrix4(this),t.setXYZ(e.x,e.y,e.z);return t}}(),determinant:function(){var e=this.elements,t=e[0],i=e[4],r=e[8],n=e[12],a=e[1],o=e[5],s=e[9],c=e[13],u=e[2],l=e[6],h=e[10],d=e[14],p=e[3],f=e[7],m=e[11],g=e[15];return p*(+n*s*l-r*c*l-n*o*h+i*c*h+r*o*d-i*s*d)+f*(+t*s*d-t*c*h+n*a*h-r*a*d+r*c*u-n*s*u)+m*(+t*c*l-t*o*d-n*a*l+i*a*d+n*o*u-i*c*u)+g*(-r*o*u-t*s*l+t*o*h+r*a*l-i*a*h+i*s*u)},transpose:function(){var e,t=this.elements;return e=t[1],t[1]=t[4],t[4]=e,e=t[2],t[2]=t[8],t[8]=e,e=t[6],t[6]=t[9],t[9]=e,e=t[3],t[3]=t[12],t[12]=e,e=t[7],t[7]=t[13],t[13]=e,e=t[11],t[11]=t[14],t[14]=e,this},flattenToArrayOffset:function(e,t){return console.warn("THREE.Matrix3: .flattenToArrayOffset is deprecated - just use .toArray instead."),this.toArray(e,t)},getPosition:function(){var e;return function(){return void 0===e&&(e=new n.Vector3),console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."),e.setFromMatrixColumn(this,3)}}(),setPosition:function(e){var t=this.elements;return t[12]=e.x,t[13]=e.y,t[14]=e.z,this},getInverse:function(e,t){var i=this.elements,r=e.elements,n=r[0],a=r[1],o=r[2],s=r[3],c=r[4],u=r[5],l=r[6],h=r[7],d=r[8],p=r[9],f=r[10],m=r[11],g=r[12],v=r[13],y=r[14],M=r[15],A=p*y*h-v*f*h+v*l*m-u*y*m-p*l*M+u*f*M,x=g*f*h-d*y*h-g*l*m+c*y*m+d*l*M-c*f*M,w=d*v*h-g*p*h+g*u*m-c*v*m-d*u*M+c*p*M,E=g*p*l-d*v*l-g*u*f+c*v*f+d*u*y-c*p*y,b=n*A+a*x+o*w+s*E;if(0===b){var T="THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";if(t)throw new Error(T);return console.warn(T),this.identity()}return i[0]=A,i[1]=v*f*s-p*y*s-v*o*m+a*y*m+p*o*M-a*f*M,i[2]=u*y*s-v*l*s+v*o*h-a*y*h-u*o*M+a*l*M,i[3]=p*l*s-u*f*s-p*o*h+a*f*h+u*o*m-a*l*m,i[4]=x,i[5]=d*y*s-g*f*s+g*o*m-n*y*m-d*o*M+n*f*M,i[6]=g*l*s-c*y*s-g*o*h+n*y*h+c*o*M-n*l*M,i[7]=c*f*s-d*l*s+d*o*h-n*f*h-c*o*m+n*l*m,i[8]=w,i[9]=g*p*s-d*v*s-g*a*m+n*v*m+d*a*M-n*p*M,i[10]=c*v*s-g*u*s+g*a*h-n*v*h-c*a*M+n*u*M,i[11]=d*u*s-c*p*s-d*a*h+n*p*h+c*a*m-n*u*m,i[12]=E,i[13]=d*v*o-g*p*o+g*a*f-n*v*f-d*a*y+n*p*y,i[14]=g*u*o-c*v*o-g*a*l+n*v*l+c*a*y-n*u*y,i[15]=c*p*o-d*u*o+d*a*l-n*p*l-c*a*f+n*u*f,this.multiplyScalar(1/b)},scale:function(e){var t=this.elements,i=e.x,r=e.y,n=e.z;return t[0]*=i,t[4]*=r,t[8]*=n,t[1]*=i,t[5]*=r,t[9]*=n,t[2]*=i,t[6]*=r,t[10]*=n,t[3]*=i,t[7]*=r,t[11]*=n,this},getMaxScaleOnAxis:function(){var e=this.elements,t=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],i=e[4]*e[4]+e[5]*e[5]+e[6]*e[6],r=e[8]*e[8]+e[9]*e[9]+e[10]*e[10];return Math.sqrt(Math.max(t,i,r))},makeTranslation:function(e,t,i){return this.set(1,0,0,e,0,1,0,t,0,0,1,i,0,0,0,1),this},makeRotationX:function(e){var t=Math.cos(e),i=Math.sin(e);return this.set(1,0,0,0,0,t,-i,0,0,i,t,0,0,0,0,1),this},makeRotationY:function(e){var t=Math.cos(e),i=Math.sin(e);return this.set(t,0,i,0,0,1,0,0,-i,0,t,0,0,0,0,1),this},makeRotationZ:function(e){var t=Math.cos(e),i=Math.sin(e);return this.set(t,-i,0,0,i,t,0,0,0,0,1,0,0,0,0,1),this},makeRotationAxis:function(e,t){var i=Math.cos(t),r=Math.sin(t),n=1-i,a=e.x,o=e.y,s=e.z,c=n*a,u=n*o;return this.set(c*a+i,c*o-r*s,c*s+r*o,0,c*o+r*s,u*o+i,u*s-r*a,0,c*s-r*o,u*s+r*a,n*s*s+i,0,0,0,0,1),this},makeScale:function(e,t,i){return this.set(e,0,0,0,0,t,0,0,0,0,i,0,0,0,0,1),this},compose:function(e,t,i){return this.makeRotationFromQuaternion(t),this.scale(i),this.setPosition(e),this},decompose:function(){var e,t;return function(i,r,a){void 0===e&&(e=new n.Vector3),void 0===t&&(t=new n.Matrix4);var o=this.elements,s=e.set(o[0],o[1],o[2]).length(),c=e.set(o[4],o[5],o[6]).length(),u=e.set(o[8],o[9],o[10]).length(),l=this.determinant();0>l&&(s=-s),i.x=o[12],i.y=o[13],i.z=o[14],t.elements.set(this.elements);var h=1/s,d=1/c,p=1/u;return t.elements[0]*=h,t.elements[1]*=h,t.elements[2]*=h,t.elements[4]*=d,t.elements[5]*=d,t.elements[6]*=d,t.elements[8]*=p,t.elements[9]*=p,t.elements[10]*=p,r.setFromRotationMatrix(t),a.x=s,a.y=c,a.z=u,this}}(),makeFrustum:function(e,t,i,r,n,a){var o=this.elements,s=2*n/(t-e),c=2*n/(r-i),u=(t+e)/(t-e),l=(r+i)/(r-i),h=-(a+n)/(a-n),d=-2*a*n/(a-n);return o[0]=s,o[4]=0,o[8]=u,o[12]=0,o[1]=0,o[5]=c,o[9]=l,o[13]=0,o[2]=0,o[6]=0,o[10]=h,o[14]=d,o[3]=0,o[7]=0,o[11]=-1,o[15]=0,this},makePerspective:function(e,t,i,r){var a=i*Math.tan(n.Math.DEG2RAD*e*.5),o=-a,s=o*t,c=a*t;return this.makeFrustum(s,c,o,a,i,r)},makeOrthographic:function(e,t,i,r,n,a){var o=this.elements,s=1/(t-e),c=1/(i-r),u=1/(a-n),l=(t+e)*s,h=(i+r)*c,d=(a+n)*u;return o[0]=2*s,o[4]=0,o[8]=0,o[12]=-l,o[1]=0,o[5]=2*c,o[9]=0,o[13]=-h,o[2]=0,o[6]=0,o[10]=-2*u,o[14]=-d,o[3]=0,o[7]=0,o[11]=0,o[15]=1,this},equals:function(e){for(var t=this.elements,i=e.elements,r=0;16>r;r++)if(t[r]!==i[r])return!1;return!0},fromArray:function(e){return this.elements.set(e),this},toArray:function(e,t){void 0===e&&(e=[]),void 0===t&&(t=0);var i=this.elements;return e[t]=i[0],e[t+1]=i[1],e[t+2]=i[2],e[t+3]=i[3],e[t+4]=i[4],e[t+5]=i[5],e[t+6]=i[6],e[t+7]=i[7],e[t+8]=i[8],e[t+9]=i[9],e[t+10]=i[10],e[t+11]=i[11],e[t+12]=i[12],e[t+13]=i[13],e[t+14]=i[14],e[t+15]=i[15],e}},n.Ray=function(e,t){this.origin=void 0!==e?e:new n.Vector3,this.direction=void 0!==t?t:new n.Vector3},n.Ray.prototype={constructor:n.Ray,set:function(e,t){return this.origin.copy(e),this.direction.copy(t),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.origin.copy(e.origin),this.direction.copy(e.direction),this},at:function(e,t){var i=t||new n.Vector3;return i.copy(this.direction).multiplyScalar(e).add(this.origin)},lookAt:function(e){this.direction.copy(e).sub(this.origin).normalize()},recast:function(){var e=new n.Vector3;return function(t){return this.origin.copy(this.at(t,e)),this}}(),closestPointToPoint:function(e,t){var i=t||new n.Vector3;i.subVectors(e,this.origin);var r=i.dot(this.direction);return 0>r?i.copy(this.origin):i.copy(this.direction).multiplyScalar(r).add(this.origin)},distanceToPoint:function(e){return Math.sqrt(this.distanceSqToPoint(e))},distanceSqToPoint:function(){var e=new n.Vector3;return function(t){var i=e.subVectors(t,this.origin).dot(this.direction);return 0>i?this.origin.distanceToSquared(t):(e.copy(this.direction).multiplyScalar(i).add(this.origin),e.distanceToSquared(t))}}(),distanceSqToSegment:function(){var e=new n.Vector3,t=new n.Vector3,i=new n.Vector3;return function(r,n,a,o){e.copy(r).add(n).multiplyScalar(.5),t.copy(n).sub(r).normalize(),i.copy(this.origin).sub(e);var s,c,u,l,h=.5*r.distanceTo(n),d=-this.direction.dot(t),p=i.dot(this.direction),f=-i.dot(t),m=i.lengthSq(),g=Math.abs(1-d*d);if(g>0)if(s=d*f-p,c=d*p-f,l=h*g,s>=0)if(c>=-l)if(l>=c){var v=1/g;s*=v,c*=v,u=s*(s+d*c+2*p)+c*(d*s+c+2*f)+m}else c=h,s=Math.max(0,-(d*c+p)),u=-s*s+c*(c+2*f)+m;else c=-h,s=Math.max(0,-(d*c+p)),u=-s*s+c*(c+2*f)+m;else-l>=c?(s=Math.max(0,-(-d*h+p)),c=s>0?-h:Math.min(Math.max(-h,-f),h),u=-s*s+c*(c+2*f)+m):l>=c?(s=0,c=Math.min(Math.max(-h,-f),h),u=c*(c+2*f)+m):(s=Math.max(0,-(d*h+p)),c=s>0?h:Math.min(Math.max(-h,-f),h),u=-s*s+c*(c+2*f)+m);else c=d>0?-h:h,s=Math.max(0,-(d*c+p)),u=-s*s+c*(c+2*f)+m;return a&&a.copy(this.direction).multiplyScalar(s).add(this.origin),o&&o.copy(t).multiplyScalar(c).add(e),u}}(),intersectSphere:function(){var e=new n.Vector3;return function(t,i){e.subVectors(t.center,this.origin);var r=e.dot(this.direction),n=e.dot(e)-r*r,a=t.radius*t.radius;if(n>a)return null;var o=Math.sqrt(a-n),s=r-o,c=r+o;return 0>s&&0>c?null:0>s?this.at(c,i):this.at(s,i)}}(),intersectsSphere:function(e){return this.distanceToPoint(e.center)<=e.radius},distanceToPlane:function(e){var t=e.normal.dot(this.direction);if(0===t)return 0===e.distanceToPoint(this.origin)?0:null;var i=-(this.origin.dot(e.normal)+e.constant)/t;return i>=0?i:null},intersectPlane:function(e,t){var i=this.distanceToPlane(e);return null===i?null:this.at(i,t)},intersectsPlane:function(e){var t=e.distanceToPoint(this.origin);if(0===t)return!0;var i=e.normal.dot(this.direction);return 0>i*t?!0:!1},intersectBox:function(e,t){var i,r,n,a,o,s,c=1/this.direction.x,u=1/this.direction.y,l=1/this.direction.z,h=this.origin;return c>=0?(i=(e.min.x-h.x)*c,r=(e.max.x-h.x)*c):(i=(e.max.x-h.x)*c,r=(e.min.x-h.x)*c),u>=0?(n=(e.min.y-h.y)*u,a=(e.max.y-h.y)*u):(n=(e.max.y-h.y)*u,a=(e.min.y-h.y)*u),i>a||n>r?null:((n>i||i!==i)&&(i=n),(r>a||r!==r)&&(r=a),l>=0?(o=(e.min.z-h.z)*l,s=(e.max.z-h.z)*l):(o=(e.max.z-h.z)*l,s=(e.min.z-h.z)*l),i>s||o>r?null:((o>i||i!==i)&&(i=o),(r>s||r!==r)&&(r=s),0>r?null:this.at(i>=0?i:r,t)))},intersectsBox:function(){var e=new n.Vector3;return function(t){return null!==this.intersectBox(t,e)}}(),intersectTriangle:function(){var e=new n.Vector3,t=new n.Vector3,i=new n.Vector3,r=new n.Vector3;return function(n,a,o,s,c){t.subVectors(a,n),i.subVectors(o,n),r.crossVectors(t,i);var u,l=this.direction.dot(r);if(l>0){if(s)return null;u=1}else{if(!(0>l))return null;u=-1,l=-l}e.subVectors(this.origin,n);var h=u*this.direction.dot(i.crossVectors(e,i));if(0>h)return null;var d=u*this.direction.dot(t.cross(e));if(0>d)return null;if(h+d>l)return null;var p=-u*e.dot(r);return 0>p?null:this.at(p/l,c)}}(),applyMatrix4:function(e){return this.direction.add(this.origin).applyMatrix4(e),this.origin.applyMatrix4(e),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(e){return e.origin.equals(this.origin)&&e.direction.equals(this.direction)}},n.Sphere=function(e,t){this.center=void 0!==e?e:new n.Vector3,this.radius=void 0!==t?t:0},n.Sphere.prototype={constructor:n.Sphere,set:function(e,t){return this.center.copy(e),this.radius=t,this},setFromPoints:function(){var e=new n.Box3;return function(t,i){var r=this.center;void 0!==i?r.copy(i):e.setFromPoints(t).center(r);for(var n=0,a=0,o=t.length;o>a;a++)n=Math.max(n,r.distanceToSquared(t[a]));return this.radius=Math.sqrt(n),this}}(),clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.center.copy(e.center),this.radius=e.radius,this},empty:function(){return this.radius<=0},containsPoint:function(e){return e.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(e){return e.distanceTo(this.center)-this.radius},intersectsSphere:function(e){var t=this.radius+e.radius;return e.center.distanceToSquared(this.center)<=t*t},intersectsBox:function(e){return e.intersectsSphere(this)},intersectsPlane:function(e){return Math.abs(this.center.dot(e.normal)-e.constant)<=this.radius},clampPoint:function(e,t){var i=this.center.distanceToSquared(e),r=t||new n.Vector3;return r.copy(e),i>this.radius*this.radius&&(r.sub(this.center).normalize(),r.multiplyScalar(this.radius).add(this.center)),r},getBoundingBox:function(e){var t=e||new n.Box3;return t.set(this.center,this.center),t.expandByScalar(this.radius),t},applyMatrix4:function(e){return this.center.applyMatrix4(e),this.radius=this.radius*e.getMaxScaleOnAxis(),this},translate:function(e){return this.center.add(e),this},equals:function(e){return e.center.equals(this.center)&&e.radius===this.radius}},n.Frustum=function(e,t,i,r,a,o){this.planes=[void 0!==e?e:new n.Plane,void 0!==t?t:new n.Plane,void 0!==i?i:new n.Plane,void 0!==r?r:new n.Plane,void 0!==a?a:new n.Plane,void 0!==o?o:new n.Plane]},n.Frustum.prototype={constructor:n.Frustum,set:function(e,t,i,r,n,a){var o=this.planes;return o[0].copy(e),o[1].copy(t),o[2].copy(i),o[3].copy(r),o[4].copy(n),o[5].copy(a),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){for(var t=this.planes,i=0;6>i;i++)t[i].copy(e.planes[i]);return this},setFromMatrix:function(e){var t=this.planes,i=e.elements,r=i[0],n=i[1],a=i[2],o=i[3],s=i[4],c=i[5],u=i[6],l=i[7],h=i[8],d=i[9],p=i[10],f=i[11],m=i[12],g=i[13],v=i[14],y=i[15];return t[0].setComponents(o-r,l-s,f-h,y-m).normalize(),t[1].setComponents(o+r,l+s,f+h,y+m).normalize(),t[2].setComponents(o+n,l+c,f+d,y+g).normalize(),t[3].setComponents(o-n,l-c,f-d,y-g).normalize(),t[4].setComponents(o-a,l-u,f-p,y-v).normalize(),t[5].setComponents(o+a,l+u,f+p,y+v).normalize(),this},intersectsObject:function(){var e=new n.Sphere;return function(t){var i=t.geometry;return null===i.boundingSphere&&i.computeBoundingSphere(),e.copy(i.boundingSphere),e.applyMatrix4(t.matrixWorld),this.intersectsSphere(e)}}(),intersectsSphere:function(e){for(var t=this.planes,i=e.center,r=-e.radius,n=0;6>n;n++){var a=t[n].distanceToPoint(i);if(r>a)return!1}return!0},intersectsBox:function(){var e=new n.Vector3,t=new n.Vector3;return function(i){for(var r=this.planes,n=0;6>n;n++){var a=r[n];e.x=a.normal.x>0?i.min.x:i.max.x,t.x=a.normal.x>0?i.max.x:i.min.x,e.y=a.normal.y>0?i.min.y:i.max.y,t.y=a.normal.y>0?i.max.y:i.min.y,e.z=a.normal.z>0?i.min.z:i.max.z,t.z=a.normal.z>0?i.max.z:i.min.z;var o=a.distanceToPoint(e),s=a.distanceToPoint(t);if(0>o&&0>s)return!1}return!0}}(),containsPoint:function(e){for(var t=this.planes,i=0;6>i;i++)if(t[i].distanceToPoint(e)<0)return!1;retur
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment