Skip to content

Instantly share code, notes, and snippets.

@z3t0
Last active August 29, 2015 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 z3t0/f945cb625333241c9eec to your computer and use it in GitHub Desktop.
Save z3t0/f945cb625333241c9eec to your computer and use it in GitHub Desktop.
Voxel Game using GameJS for controls
This file has been truncated, but you can view the full file.
(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){
},{}],2:[function(require,module,exports){
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
var base64 = require('base64-js')
var ieee754 = require('ieee754')
var isArray = require('is-array')
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50
Buffer.poolSize = 8192 // not used by this implementation
var kMaxLength = 0x3fffffff
var rootParent = {}
/**
* If `Buffer.TYPED_ARRAY_SUPPORT`:
* === true Use Uint8Array implementation (fastest)
* === false Use Object implementation (most compatible, even IE6)
*
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* Note:
*
* - Implementation must support adding new properties to `Uint8Array` instances.
* Firefox 4-29 lacked support, fixed in Firefox 30+.
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
*
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
*
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
* incorrect length in some situations.
*
* We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will
* get the Object implementation, which is slower but will work correctly.
*/
Buffer.TYPED_ARRAY_SUPPORT = (function () {
try {
var buf = new ArrayBuffer(0)
var arr = new Uint8Array(buf)
arr.foo = function () { return 42 }
return arr.foo() === 42 && // typed array instances can be augmented
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
} catch (e) {
return false
}
})()
/**
* Class: Buffer
* =============
*
* The Buffer constructor returns instances of `Uint8Array` that are augmented
* with function properties for all the node `Buffer` API functions. We use
* `Uint8Array` so that square bracket notation works as expected -- it returns
* a single octet.
*
* By augmenting the instances, we can avoid modifying the `Uint8Array`
* prototype.
*/
function Buffer (subject, encoding) {
var self = this
if (!(self instanceof Buffer)) return new Buffer(subject, encoding)
var type = typeof subject
var length
if (type === 'number') {
length = +subject
} else if (type === 'string') {
length = Buffer.byteLength(subject, encoding)
} else if (type === 'object' && subject !== null) {
// assume object is array-like
if (subject.type === 'Buffer' && isArray(subject.data)) subject = subject.data
length = +subject.length
} else {
throw new TypeError('must start with number, buffer, array or string')
}
if (length > kMaxLength) {
throw new RangeError('Attempt to allocate Buffer larger than maximum size: 0x' +
kMaxLength.toString(16) + ' bytes')
}
if (length < 0) length = 0
else length >>>= 0 // coerce to uint32
if (Buffer.TYPED_ARRAY_SUPPORT) {
// Preferred: Return an augmented `Uint8Array` instance for best performance
self = Buffer._augment(new Uint8Array(length)) // eslint-disable-line consistent-this
} else {
// Fallback: Return THIS instance of Buffer (created by `new`)
self.length = length
self._isBuffer = true
}
var i
if (Buffer.TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
// Speed optimization -- use set if we're copying from a typed array
self._set(subject)
} else if (isArrayish(subject)) {
// Treat array-ish objects as a byte array
if (Buffer.isBuffer(subject)) {
for (i = 0; i < length; i++) {
self[i] = subject.readUInt8(i)
}
} else {
for (i = 0; i < length; i++) {
self[i] = ((subject[i] % 256) + 256) % 256
}
}
} else if (type === 'string') {
self.write(subject, 0, encoding)
} else if (type === 'number' && !Buffer.TYPED_ARRAY_SUPPORT) {
for (i = 0; i < length; i++) {
self[i] = 0
}
}
if (length > 0 && length <= Buffer.poolSize) self.parent = rootParent
return self
}
function SlowBuffer (subject, encoding) {
if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding)
var buf = new Buffer(subject, encoding)
delete buf.parent
return buf
}
Buffer.isBuffer = function isBuffer (b) {
return !!(b != null && b._isBuffer)
}
Buffer.compare = function compare (a, b) {
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
throw new TypeError('Arguments must be Buffers')
}
if (a === b) return 0
var x = a.length
var y = b.length
for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
if (i !== len) {
x = a[i]
y = b[i]
}
if (x < y) return -1
if (y < x) return 1
return 0
}
Buffer.isEncoding = function isEncoding (encoding) {
switch (String(encoding).toLowerCase()) {
case 'hex':
case 'utf8':
case 'utf-8':
case 'ascii':
case 'binary':
case 'base64':
case 'raw':
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return true
default:
return false
}
}
Buffer.concat = function concat (list, totalLength) {
if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.')
if (list.length === 0) {
return new Buffer(0)
} else if (list.length === 1) {
return list[0]
}
var i
if (totalLength === undefined) {
totalLength = 0
for (i = 0; i < list.length; i++) {
totalLength += list[i].length
}
}
var buf = new Buffer(totalLength)
var pos = 0
for (i = 0; i < list.length; i++) {
var item = list[i]
item.copy(buf, pos)
pos += item.length
}
return buf
}
Buffer.byteLength = function byteLength (str, encoding) {
var ret
str = str + ''
switch (encoding || 'utf8') {
case 'ascii':
case 'binary':
case 'raw':
ret = str.length
break
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = str.length * 2
break
case 'hex':
ret = str.length >>> 1
break
case 'utf8':
case 'utf-8':
ret = utf8ToBytes(str).length
break
case 'base64':
ret = base64ToBytes(str).length
break
default:
ret = str.length
}
return ret
}
// pre-set for values that may exist in the future
Buffer.prototype.length = undefined
Buffer.prototype.parent = undefined
// toString(encoding, start=0, end=buffer.length)
Buffer.prototype.toString = function toString (encoding, start, end) {
var loweredCase = false
start = start >>> 0
end = end === undefined || end === Infinity ? this.length : end >>> 0
if (!encoding) encoding = 'utf8'
if (start < 0) start = 0
if (end > this.length) end = this.length
if (end <= start) return ''
while (true) {
switch (encoding) {
case 'hex':
return hexSlice(this, start, end)
case 'utf8':
case 'utf-8':
return utf8Slice(this, start, end)
case 'ascii':
return asciiSlice(this, start, end)
case 'binary':
return binarySlice(this, start, end)
case 'base64':
return base64Slice(this, start, end)
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return utf16leSlice(this, start, end)
default:
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
encoding = (encoding + '').toLowerCase()
loweredCase = true
}
}
}
Buffer.prototype.equals = function equals (b) {
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
if (this === b) return true
return Buffer.compare(this, b) === 0
}
Buffer.prototype.inspect = function inspect () {
var str = ''
var max = exports.INSPECT_MAX_BYTES
if (this.length > 0) {
str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
if (this.length > max) str += ' ... '
}
return '<Buffer ' + str + '>'
}
Buffer.prototype.compare = function compare (b) {
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
if (this === b) return 0
return Buffer.compare(this, b)
}
Buffer.prototype.indexOf = function indexOf (val, byteOffset) {
if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff
else if (byteOffset < -0x80000000) byteOffset = -0x80000000
byteOffset >>= 0
if (this.length === 0) return -1
if (byteOffset >= this.length) return -1
// Negative offsets start from the end of the buffer
if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0)
if (typeof val === 'string') {
if (val.length === 0) return -1 // special case: looking for empty string always fails
return String.prototype.indexOf.call(this, val, byteOffset)
}
if (Buffer.isBuffer(val)) {
return arrayIndexOf(this, val, byteOffset)
}
if (typeof val === 'number') {
if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') {
return Uint8Array.prototype.indexOf.call(this, val, byteOffset)
}
return arrayIndexOf(this, [ val ], byteOffset)
}
function arrayIndexOf (arr, val, byteOffset) {
var foundIndex = -1
for (var i = 0; byteOffset + i < arr.length; i++) {
if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) {
if (foundIndex === -1) foundIndex = i
if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex
} else {
foundIndex = -1
}
}
return -1
}
throw new TypeError('val must be string, number or Buffer')
}
// `get` will be removed in Node 0.13+
Buffer.prototype.get = function get (offset) {
console.log('.get() is deprecated. Access using array indexes instead.')
return this.readUInt8(offset)
}
// `set` will be removed in Node 0.13+
Buffer.prototype.set = function set (v, offset) {
console.log('.set() is deprecated. Access using array indexes instead.')
return this.writeUInt8(v, offset)
}
function hexWrite (buf, string, offset, length) {
offset = Number(offset) || 0
var remaining = buf.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
// must be an even number of digits
var strLen = string.length
if (strLen % 2 !== 0) throw new Error('Invalid hex string')
if (length > strLen / 2) {
length = strLen / 2
}
for (var i = 0; i < length; i++) {
var parsed = parseInt(string.substr(i * 2, 2), 16)
if (isNaN(parsed)) throw new Error('Invalid hex string')
buf[offset + i] = parsed
}
return i
}
function utf8Write (buf, string, offset, length) {
var charsWritten = blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
return charsWritten
}
function asciiWrite (buf, string, offset, length) {
var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
return charsWritten
}
function binaryWrite (buf, string, offset, length) {
return asciiWrite(buf, string, offset, length)
}
function base64Write (buf, string, offset, length) {
var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
return charsWritten
}
function utf16leWrite (buf, string, offset, length) {
var charsWritten = blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
return charsWritten
}
Buffer.prototype.write = function write (string, offset, length, encoding) {
// Support both (string, offset, length, encoding)
// and the legacy (string, encoding, offset, length)
if (isFinite(offset)) {
if (!isFinite(length)) {
encoding = length
length = undefined
}
} else { // legacy
var swap = encoding
encoding = offset
offset = length
length = swap
}
offset = Number(offset) || 0
if (length < 0 || offset < 0 || offset > this.length) {
throw new RangeError('attempt to write outside buffer bounds')
}
var remaining = this.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}
encoding = String(encoding || 'utf8').toLowerCase()
var ret
switch (encoding) {
case 'hex':
ret = hexWrite(this, string, offset, length)
break
case 'utf8':
case 'utf-8':
ret = utf8Write(this, string, offset, length)
break
case 'ascii':
ret = asciiWrite(this, string, offset, length)
break
case 'binary':
ret = binaryWrite(this, string, offset, length)
break
case 'base64':
ret = base64Write(this, string, offset, length)
break
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
ret = utf16leWrite(this, string, offset, length)
break
default:
throw new TypeError('Unknown encoding: ' + encoding)
}
return ret
}
Buffer.prototype.toJSON = function toJSON () {
return {
type: 'Buffer',
data: Array.prototype.slice.call(this._arr || this, 0)
}
}
function base64Slice (buf, start, end) {
if (start === 0 && end === buf.length) {
return base64.fromByteArray(buf)
} else {
return base64.fromByteArray(buf.slice(start, end))
}
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end)
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return res + decodeUtf8Char(tmp)
}
function asciiSlice (buf, start, end) {
var ret = ''
end = Math.min(buf.length, end)
for (var i = start; i < end; i++) {
ret += String.fromCharCode(buf[i] & 0x7F)
}
return ret
}
function binarySlice (buf, start, end) {
var ret = ''
end = Math.min(buf.length, end)
for (var i = start; i < end; i++) {
ret += String.fromCharCode(buf[i])
}
return ret
}
function hexSlice (buf, start, end) {
var len = buf.length
if (!start || start < 0) start = 0
if (!end || end < 0 || end > len) end = len
var out = ''
for (var i = start; i < end; i++) {
out += toHex(buf[i])
}
return out
}
function utf16leSlice (buf, start, end) {
var bytes = buf.slice(start, end)
var res = ''
for (var i = 0; i < bytes.length; i += 2) {
res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
}
return res
}
Buffer.prototype.slice = function slice (start, end) {
var len = this.length
start = ~~start
end = end === undefined ? len : ~~end
if (start < 0) {
start += len
if (start < 0) start = 0
} else if (start > len) {
start = len
}
if (end < 0) {
end += len
if (end < 0) end = 0
} else if (end > len) {
end = len
}
if (end < start) end = start
var newBuf
if (Buffer.TYPED_ARRAY_SUPPORT) {
newBuf = Buffer._augment(this.subarray(start, end))
} else {
var sliceLen = end - start
newBuf = new Buffer(sliceLen, undefined)
for (var i = 0; i < sliceLen; i++) {
newBuf[i] = this[i + start]
}
}
if (newBuf.length) newBuf.parent = this.parent || this
return newBuf
}
/*
* Need to make sure that buffer isn't trying to write out of bounds.
*/
function checkOffset (offset, ext, length) {
if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
}
Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) checkOffset(offset, byteLength, this.length)
var val = this[offset]
var mul = 1
var i = 0
while (++i < byteLength && (mul *= 0x100)) {
val += this[offset + i] * mul
}
return val
}
Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) {
checkOffset(offset, byteLength, this.length)
}
var val = this[offset + --byteLength]
var mul = 1
while (byteLength > 0 && (mul *= 0x100)) {
val += this[offset + --byteLength] * mul
}
return val
}
Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
if (!noAssert) checkOffset(offset, 1, this.length)
return this[offset]
}
Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 2, this.length)
return this[offset] | (this[offset + 1] << 8)
}
Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 2, this.length)
return (this[offset] << 8) | this[offset + 1]
}
Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return ((this[offset]) |
(this[offset + 1] << 8) |
(this[offset + 2] << 16)) +
(this[offset + 3] * 0x1000000)
}
Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return (this[offset] * 0x1000000) +
((this[offset + 1] << 16) |
(this[offset + 2] << 8) |
this[offset + 3])
}
Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) checkOffset(offset, byteLength, this.length)
var val = this[offset]
var mul = 1
var i = 0
while (++i < byteLength && (mul *= 0x100)) {
val += this[offset + i] * mul
}
mul *= 0x80
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
return val
}
Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) checkOffset(offset, byteLength, this.length)
var i = byteLength
var mul = 1
var val = this[offset + --i]
while (i > 0 && (mul *= 0x100)) {
val += this[offset + --i] * mul
}
mul *= 0x80
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
return val
}
Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
if (!noAssert) checkOffset(offset, 1, this.length)
if (!(this[offset] & 0x80)) return (this[offset])
return ((0xff - this[offset] + 1) * -1)
}
Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 2, this.length)
var val = this[offset] | (this[offset + 1] << 8)
return (val & 0x8000) ? val | 0xFFFF0000 : val
}
Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 2, this.length)
var val = this[offset + 1] | (this[offset] << 8)
return (val & 0x8000) ? val | 0xFFFF0000 : val
}
Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return (this[offset]) |
(this[offset + 1] << 8) |
(this[offset + 2] << 16) |
(this[offset + 3] << 24)
}
Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return (this[offset] << 24) |
(this[offset + 1] << 16) |
(this[offset + 2] << 8) |
(this[offset + 3])
}
Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return ieee754.read(this, offset, true, 23, 4)
}
Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 4, this.length)
return ieee754.read(this, offset, false, 23, 4)
}
Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 8, this.length)
return ieee754.read(this, offset, true, 52, 8)
}
Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
if (!noAssert) checkOffset(offset, 8, this.length)
return ieee754.read(this, offset, false, 52, 8)
}
function checkInt (buf, value, offset, ext, max, min) {
if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance')
if (value > max || value < min) throw new RangeError('value is out of bounds')
if (offset + ext > buf.length) throw new RangeError('index out of range')
}
Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
value = +value
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
var mul = 1
var i = 0
this[offset] = value & 0xFF
while (++i < byteLength && (mul *= 0x100)) {
this[offset + i] = (value / mul) >>> 0 & 0xFF
}
return offset + byteLength
}
Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
value = +value
offset = offset >>> 0
byteLength = byteLength >>> 0
if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
var i = byteLength - 1
var mul = 1
this[offset + i] = value & 0xFF
while (--i >= 0 && (mul *= 0x100)) {
this[offset + i] = (value / mul) >>> 0 & 0xFF
}
return offset + byteLength
}
Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
this[offset] = value
return offset + 1
}
function objectWriteUInt16 (buf, value, offset, littleEndian) {
if (value < 0) value = 0xffff + value + 1
for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
(littleEndian ? i : 1 - i) * 8
}
}
Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset + 1] = (value >>> 8)
} else {
objectWriteUInt16(this, value, offset, true)
}
return offset + 2
}
Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 8)
this[offset + 1] = value
} else {
objectWriteUInt16(this, value, offset, false)
}
return offset + 2
}
function objectWriteUInt32 (buf, value, offset, littleEndian) {
if (value < 0) value = 0xffffffff + value + 1
for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
}
}
Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset + 3] = (value >>> 24)
this[offset + 2] = (value >>> 16)
this[offset + 1] = (value >>> 8)
this[offset] = value
} else {
objectWriteUInt32(this, value, offset, true)
}
return offset + 4
}
Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 24)
this[offset + 1] = (value >>> 16)
this[offset + 2] = (value >>> 8)
this[offset + 3] = value
} else {
objectWriteUInt32(this, value, offset, false)
}
return offset + 4
}
Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) {
checkInt(
this, value, offset, byteLength,
Math.pow(2, 8 * byteLength - 1) - 1,
-Math.pow(2, 8 * byteLength - 1)
)
}
var i = 0
var mul = 1
var sub = value < 0 ? 1 : 0
this[offset] = value & 0xFF
while (++i < byteLength && (mul *= 0x100)) {
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
}
return offset + byteLength
}
Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) {
checkInt(
this, value, offset, byteLength,
Math.pow(2, 8 * byteLength - 1) - 1,
-Math.pow(2, 8 * byteLength - 1)
)
}
var i = byteLength - 1
var mul = 1
var sub = value < 0 ? 1 : 0
this[offset + i] = value & 0xFF
while (--i >= 0 && (mul *= 0x100)) {
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
}
return offset + byteLength
}
Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
if (value < 0) value = 0xff + value + 1
this[offset] = value
return offset + 1
}
Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset + 1] = (value >>> 8)
} else {
objectWriteUInt16(this, value, offset, true)
}
return offset + 2
}
Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 8)
this[offset + 1] = value
} else {
objectWriteUInt16(this, value, offset, false)
}
return offset + 2
}
Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset + 1] = (value >>> 8)
this[offset + 2] = (value >>> 16)
this[offset + 3] = (value >>> 24)
} else {
objectWriteUInt32(this, value, offset, true)
}
return offset + 4
}
Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
value = +value
offset = offset >>> 0
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
if (value < 0) value = 0xffffffff + value + 1
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 24)
this[offset + 1] = (value >>> 16)
this[offset + 2] = (value >>> 8)
this[offset + 3] = value
} else {
objectWriteUInt32(this, value, offset, false)
}
return offset + 4
}
function checkIEEE754 (buf, value, offset, ext, max, min) {
if (value > max || value < min) throw new RangeError('value is out of bounds')
if (offset + ext > buf.length) throw new RangeError('index out of range')
if (offset < 0) throw new RangeError('index out of range')
}
function writeFloat (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
}
ieee754.write(buf, value, offset, littleEndian, 23, 4)
return offset + 4
}
Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
return writeFloat(this, value, offset, true, noAssert)
}
Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
return writeFloat(this, value, offset, false, noAssert)
}
function writeDouble (buf, value, offset, littleEndian, noAssert) {
if (!noAssert) {
checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
}
ieee754.write(buf, value, offset, littleEndian, 52, 8)
return offset + 8
}
Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
return writeDouble(this, value, offset, true, noAssert)
}
Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
return writeDouble(this, value, offset, false, noAssert)
}
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function copy (target, target_start, start, end) {
if (!start) start = 0
if (!end && end !== 0) end = this.length
if (target_start >= target.length) target_start = target.length
if (!target_start) target_start = 0
if (end > 0 && end < start) end = start
// Copy 0 bytes; we're done
if (end === start) return 0
if (target.length === 0 || this.length === 0) return 0
// Fatal error conditions
if (target_start < 0) {
throw new RangeError('targetStart out of bounds')
}
if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
if (end < 0) throw new RangeError('sourceEnd out of bounds')
// Are we oob?
if (end > this.length) end = this.length
if (target.length - target_start < end - start) {
end = target.length - target_start + start
}
var len = end - start
if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
for (var i = 0; i < len; i++) {
target[i + target_start] = this[i + start]
}
} else {
target._set(this.subarray(start, start + len), target_start)
}
return len
}
// fill(value, start=0, end=buffer.length)
Buffer.prototype.fill = function fill (value, start, end) {
if (!value) value = 0
if (!start) start = 0
if (!end) end = this.length
if (end < start) throw new RangeError('end < start')
// Fill 0 bytes; we're done
if (end === start) return
if (this.length === 0) return
if (start < 0 || start >= this.length) throw new RangeError('start out of bounds')
if (end < 0 || end > this.length) throw new RangeError('end out of bounds')
var i
if (typeof value === 'number') {
for (i = start; i < end; i++) {
this[i] = value
}
} else {
var bytes = utf8ToBytes(value.toString())
var len = bytes.length
for (i = start; i < end; i++) {
this[i] = bytes[i % len]
}
}
return this
}
/**
* Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
* Added in Node 0.12. Only available in browsers that support ArrayBuffer.
*/
Buffer.prototype.toArrayBuffer = function toArrayBuffer () {
if (typeof Uint8Array !== 'undefined') {
if (Buffer.TYPED_ARRAY_SUPPORT) {
return (new Buffer(this)).buffer
} else {
var buf = new Uint8Array(this.length)
for (var i = 0, len = buf.length; i < len; i += 1) {
buf[i] = this[i]
}
return buf.buffer
}
} else {
throw new TypeError('Buffer.toArrayBuffer not supported in this browser')
}
}
// HELPER FUNCTIONS
// ================
var BP = Buffer.prototype
/**
* Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
*/
Buffer._augment = function _augment (arr) {
arr.constructor = Buffer
arr._isBuffer = true
// save reference to original Uint8Array set method before overwriting
arr._set = arr.set
// deprecated, will be removed in node 0.13+
arr.get = BP.get
arr.set = BP.set
arr.write = BP.write
arr.toString = BP.toString
arr.toLocaleString = BP.toString
arr.toJSON = BP.toJSON
arr.equals = BP.equals
arr.compare = BP.compare
arr.indexOf = BP.indexOf
arr.copy = BP.copy
arr.slice = BP.slice
arr.readUIntLE = BP.readUIntLE
arr.readUIntBE = BP.readUIntBE
arr.readUInt8 = BP.readUInt8
arr.readUInt16LE = BP.readUInt16LE
arr.readUInt16BE = BP.readUInt16BE
arr.readUInt32LE = BP.readUInt32LE
arr.readUInt32BE = BP.readUInt32BE
arr.readIntLE = BP.readIntLE
arr.readIntBE = BP.readIntBE
arr.readInt8 = BP.readInt8
arr.readInt16LE = BP.readInt16LE
arr.readInt16BE = BP.readInt16BE
arr.readInt32LE = BP.readInt32LE
arr.readInt32BE = BP.readInt32BE
arr.readFloatLE = BP.readFloatLE
arr.readFloatBE = BP.readFloatBE
arr.readDoubleLE = BP.readDoubleLE
arr.readDoubleBE = BP.readDoubleBE
arr.writeUInt8 = BP.writeUInt8
arr.writeUIntLE = BP.writeUIntLE
arr.writeUIntBE = BP.writeUIntBE
arr.writeUInt16LE = BP.writeUInt16LE
arr.writeUInt16BE = BP.writeUInt16BE
arr.writeUInt32LE = BP.writeUInt32LE
arr.writeUInt32BE = BP.writeUInt32BE
arr.writeIntLE = BP.writeIntLE
arr.writeIntBE = BP.writeIntBE
arr.writeInt8 = BP.writeInt8
arr.writeInt16LE = BP.writeInt16LE
arr.writeInt16BE = BP.writeInt16BE
arr.writeInt32LE = BP.writeInt32LE
arr.writeInt32BE = BP.writeInt32BE
arr.writeFloatLE = BP.writeFloatLE
arr.writeFloatBE = BP.writeFloatBE
arr.writeDoubleLE = BP.writeDoubleLE
arr.writeDoubleBE = BP.writeDoubleBE
arr.fill = BP.fill
arr.inspect = BP.inspect
arr.toArrayBuffer = BP.toArrayBuffer
return arr
}
var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g
function base64clean (str) {
// Node strips out invalid characters like \n and \t from the string, base64-js does not
str = stringtrim(str).replace(INVALID_BASE64_RE, '')
// Node converts strings with length < 2 to ''
if (str.length < 2) return ''
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
while (str.length % 4 !== 0) {
str = str + '='
}
return str
}
function stringtrim (str) {
if (str.trim) return str.trim()
return str.replace(/^\s+|\s+$/g, '')
}
function isArrayish (subject) {
return isArray(subject) || Buffer.isBuffer(subject) ||
subject && typeof subject === 'object' &&
typeof subject.length === 'number'
}
function toHex (n) {
if (n < 16) return '0' + n.toString(16)
return n.toString(16)
}
function utf8ToBytes (string, units) {
units = units || Infinity
var codePoint
var length = string.length
var leadSurrogate = null
var bytes = []
var i = 0
for (; i < length; i++) {
codePoint = string.charCodeAt(i)
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
} else {
// valid surrogate pair
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
leadSurrogate = null
}
} else {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else if (i + 1 === length) {
// unpaired lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else {
// valid lead
leadSurrogate = codePoint
continue
}
}
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = null
}
// encode utf8
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
bytes.push(codePoint)
} else if (codePoint < 0x800) {
if ((units -= 2) < 0) break
bytes.push(
codePoint >> 0x6 | 0xC0,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x10000) {
if ((units -= 3) < 0) break
bytes.push(
codePoint >> 0xC | 0xE0,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x200000) {
if ((units -= 4) < 0) break
bytes.push(
codePoint >> 0x12 | 0xF0,
codePoint >> 0xC & 0x3F | 0x80,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else {
throw new Error('Invalid code point')
}
}
return bytes
}
function asciiToBytes (str) {
var byteArray = []
for (var i = 0; i < str.length; i++) {
// Node's code seems to be doing this and not & 0x7F..
byteArray.push(str.charCodeAt(i) & 0xFF)
}
return byteArray
}
function utf16leToBytes (str, units) {
var c, hi, lo
var byteArray = []
for (var i = 0; i < str.length; i++) {
if ((units -= 2) < 0) break
c = str.charCodeAt(i)
hi = c >> 8
lo = c % 256
byteArray.push(lo)
byteArray.push(hi)
}
return byteArray
}
function base64ToBytes (str) {
return base64.toByteArray(base64clean(str))
}
function blitBuffer (src, dst, offset, length) {
for (var i = 0; i < length; i++) {
if ((i + offset >= dst.length) || (i >= src.length)) break
dst[i + offset] = src[i]
}
return i
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
},{"base64-js":3,"ieee754":4,"is-array":5}],3:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
;(function (exports) {
'use strict';
var Arr = (typeof Uint8Array !== 'undefined')
? Uint8Array
: Array
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0)
var PLUS_URL_SAFE = '-'.charCodeAt(0)
var SLASH_URL_SAFE = '_'.charCodeAt(0)
function decode (elt) {
var code = elt.charCodeAt(0)
if (code === PLUS ||
code === PLUS_URL_SAFE)
return 62 // '+'
if (code === SLASH ||
code === SLASH_URL_SAFE)
return 63 // '/'
if (code < NUMBER)
return -1 //no match
if (code < NUMBER + 10)
return code - NUMBER + 26 + 26
if (code < UPPER + 26)
return code - UPPER
if (code < LOWER + 26)
return code - LOWER + 26
}
function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr
if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
// base64 is 4/3 + up to two characters of the original data
arr = new Arr(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0
function push (v) {
arr[L++] = v
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF)
}
if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF)
} else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF)
push(tmp & 0xFF)
}
return arr
}
function uint8ToBase64 (uint8) {
var i,
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
output = "",
temp, length
function encode (num) {
return lookup.charAt(num)
}
function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
}
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp)
}
// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F)
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F)
output += '='
break
}
return output
}
exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
},{}],4:[function(require,module,exports){
exports.read = function(buffer, offset, isLE, mLen, nBytes) {
var e, m,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
nBits = -7,
i = isLE ? (nBytes - 1) : 0,
d = isLE ? -1 : 1,
s = buffer[offset + i];
i += d;
e = s & ((1 << (-nBits)) - 1);
s >>= (-nBits);
nBits += eLen;
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
m = e & ((1 << (-nBits)) - 1);
e >>= (-nBits);
nBits += mLen;
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
if (e === 0) {
e = 1 - eBias;
} else if (e === eMax) {
return m ? NaN : ((s ? -1 : 1) * Infinity);
} else {
m = m + Math.pow(2, mLen);
e = e - eBias;
}
return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};
exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
var e, m, c,
eLen = nBytes * 8 - mLen - 1,
eMax = (1 << eLen) - 1,
eBias = eMax >> 1,
rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
i = isLE ? 0 : (nBytes - 1),
d = isLE ? 1 : -1,
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
value = Math.abs(value);
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0;
e = eMax;
} else {
e = Math.floor(Math.log(value) / Math.LN2);
if (value * (c = Math.pow(2, -e)) < 1) {
e--;
c *= 2;
}
if (e + eBias >= 1) {
value += rt / c;
} else {
value += rt * Math.pow(2, 1 - eBias);
}
if (value * c >= 2) {
e++;
c /= 2;
}
if (e + eBias >= eMax) {
m = 0;
e = eMax;
} else if (e + eBias >= 1) {
m = (value * c - 1) * Math.pow(2, mLen);
e = e + eBias;
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
e = 0;
}
}
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
e = (e << mLen) | m;
eLen += mLen;
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
buffer[offset + i - d] |= s * 128;
};
},{}],5:[function(require,module,exports){
/**
* isArray
*/
var isArray = Array.isArray;
/**
* toString
*/
var str = Object.prototype.toString;
/**
* Whether or not the given `val`
* is an array.
*
* example:
*
* isArray([]);
* // > true
* isArray(arguments);
* // > false
* isArray('');
* // > false
*
* @param {mixed} val
* @return {bool}
*/
module.exports = isArray || function (val) {
return !! val && '[object Array]' == str.call(val);
};
},{}],6:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
}
throw TypeError('Uncaught, unspecified "error" event.');
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
handler.apply(this, args);
}
} else if (isObject(handler)) {
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
var m;
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
var ret;
if (!emitter._events || !emitter._events[type])
ret = 0;
else if (isFunction(emitter._events[type]))
ret = 1;
else
ret = emitter._events[type].length;
return ret;
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
},{}],7:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],8:[function(require,module,exports){
module.exports = Array.isArray || function (arr) {
return Object.prototype.toString.call(arr) == '[object Array]';
};
},{}],9:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = parts.length - 1; i >= 0; i--) {
var last = parts[i];
if (last === '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var splitPath = function(filename) {
return splitPathRe.exec(filename).slice(1);
};
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : process.cwd();
// Skip empty and invalid entries
if (typeof path !== 'string') {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = exports.isAbsolute(path),
trailingSlash = substr(path, -1) === '/';
// Normalize the path
path = normalizeArray(filter(path.split('/'), function(p) {
return !!p;
}), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
exports.join = function() {
var paths = Array.prototype.slice.call(arguments, 0);
return exports.normalize(filter(paths, function(p, index) {
if (typeof p !== 'string') {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
}).join('/'));
};
// path.relative(from, to)
// posix version
exports.relative = function(from, to) {
from = exports.resolve(from).substr(1);
to = exports.resolve(to).substr(1);
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end - start + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.sep = '/';
exports.delimiter = ':';
exports.dirname = function(path) {
var result = splitPath(path),
root = result[0],
dir = result[1];
if (!root && !dir) {
// No dirname whatsoever
return '.';
}
if (dir) {
// It has a dirname, strip trailing slash
dir = dir.substr(0, dir.length - 1);
}
return root + dir;
};
exports.basename = function(path, ext) {
var f = splitPath(path)[2];
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function(path) {
return splitPath(path)[3];
};
function filter (xs, f) {
if (xs.filter) return xs.filter(f);
var res = [];
for (var i = 0; i < xs.length; i++) {
if (f(xs[i], i, xs)) res.push(xs[i]);
}
return res;
}
// String.prototype.substr - negative index don't work in IE8
var substr = 'ab'.substr(-1) === 'b'
? function (str, start, len) { return str.substr(start, len) }
: function (str, start, len) {
if (start < 0) start = str.length + start;
return str.substr(start, len);
}
;
}).call(this,require('_process'))
},{"_process":10}],10:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
var queue = [];
var draining = false;
function drainQueue() {
if (draining) {
return;
}
draining = true;
var currentQueue;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
var i = -1;
while (++i < len) {
currentQueue[i]();
}
len = queue.length;
}
draining = false;
}
process.nextTick = function (fun) {
queue.push(fun);
if (!draining) {
setTimeout(drainQueue, 0);
}
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],11:[function(require,module,exports){
module.exports = require("./lib/_stream_duplex.js")
},{"./lib/_stream_duplex.js":12}],12:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
module.exports = Duplex;
/*<replacement>*/
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
}
/*</replacement>*/
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
forEach(objectKeys(Writable.prototype), function(method) {
if (!Duplex.prototype[method])
Duplex.prototype[method] = Writable.prototype[method];
});
function Duplex(options) {
if (!(this instanceof Duplex))
return new Duplex(options);
Readable.call(this, options);
Writable.call(this, options);
if (options && options.readable === false)
this.readable = false;
if (options && options.writable === false)
this.writable = false;
this.allowHalfOpen = true;
if (options && options.allowHalfOpen === false)
this.allowHalfOpen = false;
this.once('end', onend);
}
// the no-half-open enforcer
function onend() {
// if we allow half-open state, or if the writable side ended,
// then we're ok.
if (this.allowHalfOpen || this._writableState.ended)
return;
// no more data can be written.
// But allow more writes to happen in this tick.
process.nextTick(this.end.bind(this));
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
}).call(this,require('_process'))
},{"./_stream_readable":14,"./_stream_writable":16,"_process":10,"core-util-is":17,"inherits":7}],13:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
module.exports = PassThrough;
var Transform = require('./_stream_transform');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(PassThrough, Transform);
function PassThrough(options) {
if (!(this instanceof PassThrough))
return new PassThrough(options);
Transform.call(this, options);
}
PassThrough.prototype._transform = function(chunk, encoding, cb) {
cb(null, chunk);
};
},{"./_stream_transform":15,"core-util-is":17,"inherits":7}],14:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
module.exports = Readable;
/*<replacement>*/
var isArray = require('isarray');
/*</replacement>*/
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Readable.ReadableState = ReadableState;
var EE = require('events').EventEmitter;
/*<replacement>*/
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
return emitter.listeners(type).length;
};
/*</replacement>*/
var Stream = require('stream');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var StringDecoder;
/*<replacement>*/
var debug = require('util');
if (debug && debug.debuglog) {
debug = debug.debuglog('stream');
} else {
debug = function () {};
}
/*</replacement>*/
util.inherits(Readable, Stream);
function ReadableState(options, stream) {
var Duplex = require('./_stream_duplex');
options = options || {};
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;
var defaultHwm = options.objectMode ? 16 : 16 * 1024;
this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.buffer = [];
this.length = 0;
this.pipes = null;
this.pipesCount = 0;
this.flowing = null;
this.ended = false;
this.endEmitted = false;
this.reading = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;
this.emittedReadable = false;
this.readableListening = false;
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;
if (stream instanceof Duplex)
this.objectMode = this.objectMode || !!options.readableObjectMode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// when piping, we only care about 'readable' events that happen
// after read()ing all the bytes and not getting any pushback.
this.ranOut = false;
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;
// if true, a maybeReadMore has been scheduled
this.readingMore = false;
this.decoder = null;
this.encoding = null;
if (options.encoding) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
function Readable(options) {
var Duplex = require('./_stream_duplex');
if (!(this instanceof Readable))
return new Readable(options);
this._readableState = new ReadableState(options, this);
// legacy
this.readable = true;
Stream.call(this);
}
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function(chunk, encoding) {
var state = this._readableState;
if (util.isString(chunk) && !state.objectMode) {
encoding = encoding || state.defaultEncoding;
if (encoding !== state.encoding) {
chunk = new Buffer(chunk, encoding);
encoding = '';
}
}
return readableAddChunk(this, state, chunk, encoding, false);
};
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function(chunk) {
var state = this._readableState;
return readableAddChunk(this, state, chunk, '', true);
};
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
var er = chunkInvalid(state, chunk);
if (er) {
stream.emit('error', er);
} else if (util.isNullOrUndefined(chunk)) {
state.reading = false;
if (!state.ended)
onEofChunk(stream, state);
} else if (state.objectMode || chunk && chunk.length > 0) {
if (state.ended && !addToFront) {
var e = new Error('stream.push() after EOF');
stream.emit('error', e);
} else if (state.endEmitted && addToFront) {
var e = new Error('stream.unshift() after end event');
stream.emit('error', e);
} else {
if (state.decoder && !addToFront && !encoding)
chunk = state.decoder.write(chunk);
if (!addToFront)
state.reading = false;
// if we want the data now, just emit it.
if (state.flowing && state.length === 0 && !state.sync) {
stream.emit('data', chunk);
stream.read(0);
} else {
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront)
state.buffer.unshift(chunk);
else
state.buffer.push(chunk);
if (state.needReadable)
emitReadable(stream);
}
maybeReadMore(stream, state);
}
} else if (!addToFront) {
state.reading = false;
}
return needMoreData(state);
}
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes. This is to work around cases where hwm=0,
// such as the repl. Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
return !state.ended &&
(state.needReadable ||
state.length < state.highWaterMark ||
state.length === 0);
}
// backwards compatibility.
Readable.prototype.setEncoding = function(enc) {
if (!StringDecoder)
StringDecoder = require('string_decoder/').StringDecoder;
this._readableState.decoder = new StringDecoder(enc);
this._readableState.encoding = enc;
return this;
};
// Don't raise the hwm > 128MB
var MAX_HWM = 0x800000;
function roundUpToNextPowerOf2(n) {
if (n >= MAX_HWM) {
n = MAX_HWM;
} else {
// Get the next highest power of 2
n--;
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
n++;
}
return n;
}
function howMuchToRead(n, state) {
if (state.length === 0 && state.ended)
return 0;
if (state.objectMode)
return n === 0 ? 0 : 1;
if (isNaN(n) || util.isNull(n)) {
// only flow one buffer at a time
if (state.flowing && state.buffer.length)
return state.buffer[0].length;
else
return state.length;
}
if (n <= 0)
return 0;
// If we're asking for more than the target buffer level,
// then raise the water mark. Bump up to the next highest
// power of 2, to prevent increasing it excessively in tiny
// amounts.
if (n > state.highWaterMark)
state.highWaterMark = roundUpToNextPowerOf2(n);
// don't have that much. return null, unless we've ended.
if (n > state.length) {
if (!state.ended) {
state.needReadable = true;
return 0;
} else
return state.length;
}
return n;
}
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function(n) {
debug('read', n);
var state = this._readableState;
var nOrig = n;
if (!util.isNumber(n) || n > 0)
state.emittedReadable = false;
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 &&
state.needReadable &&
(state.length >= state.highWaterMark || state.ended)) {
debug('read: emitReadable', state.length, state.ended);
if (state.length === 0 && state.ended)
endReadable(this);
else
emitReadable(this);
return null;
}
n = howMuchToRead(n, state);
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {
if (state.length === 0)
endReadable(this);
return null;
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;
debug('need readable', doRead);
// if we currently have less than the highWaterMark, then also read some
if (state.length === 0 || state.length - n < state.highWaterMark) {
doRead = true;
debug('length less than watermark', doRead);
}
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading) {
doRead = false;
debug('reading or ended', doRead);
}
if (doRead) {
debug('do read');
state.reading = true;
state.sync = true;
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0)
state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);
state.sync = false;
}
// If _read pushed data synchronously, then `reading` will be false,
// and we need to re-evaluate how much data we can return to the user.
if (doRead && !state.reading)
n = howMuchToRead(nOrig, state);
var ret;
if (n > 0)
ret = fromList(n, state);
else
ret = null;
if (util.isNull(ret)) {
state.needReadable = true;
n = 0;
}
state.length -= n;
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (state.length === 0 && !state.ended)
state.needReadable = true;
// If we tried to read() past the EOF, then emit end on the next tick.
if (nOrig !== n && state.ended && state.length === 0)
endReadable(this);
if (!util.isNull(ret))
this.emit('data', ret);
return ret;
};
function chunkInvalid(state, chunk) {
var er = null;
if (!util.isBuffer(chunk) &&
!util.isString(chunk) &&
!util.isNullOrUndefined(chunk) &&
!state.objectMode) {
er = new TypeError('Invalid non-string/buffer chunk');
}
return er;
}
function onEofChunk(stream, state) {
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
state.length += state.objectMode ? 1 : chunk.length;
}
}
state.ended = true;
// emit 'readable' now to make sure it gets picked up.
emitReadable(stream);
}
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow. This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
var state = stream._readableState;
state.needReadable = false;
if (!state.emittedReadable) {
debug('emitReadable', state.flowing);
state.emittedReadable = true;
if (state.sync)
process.nextTick(function() {
emitReadable_(stream);
});
else
emitReadable_(stream);
}
}
function emitReadable_(stream) {
debug('emit readable');
stream.emit('readable');
flow(stream);
}
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data. that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
if (!state.readingMore) {
state.readingMore = true;
process.nextTick(function() {
maybeReadMore_(stream, state);
});
}
}
function maybeReadMore_(stream, state) {
var len = state.length;
while (!state.reading && !state.flowing && !state.ended &&
state.length < state.highWaterMark) {
debug('maybeReadMore read 0');
stream.read(0);
if (len === state.length)
// didn't get any data, stop spinning.
break;
else
len = state.length;
}
state.readingMore = false;
}
// abstract method. to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function(n) {
this.emit('error', new Error('not implemented'));
};
Readable.prototype.pipe = function(dest, pipeOpts) {
var src = this;
var state = this._readableState;
switch (state.pipesCount) {
case 0:
state.pipes = dest;
break;
case 1:
state.pipes = [state.pipes, dest];
break;
default:
state.pipes.push(dest);
break;
}
state.pipesCount += 1;
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
dest !== process.stdout &&
dest !== process.stderr;
var endFn = doEnd ? onend : cleanup;
if (state.endEmitted)
process.nextTick(endFn);
else
src.once('end', endFn);
dest.on('unpipe', onunpipe);
function onunpipe(readable) {
debug('onunpipe');
if (readable === src) {
cleanup();
}
}
function onend() {
debug('onend');
dest.end();
}
// when the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
function cleanup() {
debug('cleanup');
// cleanup event handlers once the pipe is broken
dest.removeListener('close', onclose);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);
src.removeListener('data', ondata);
// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (state.awaitDrain &&
(!dest._writableState || dest._writableState.needDrain))
ondrain();
}
src.on('data', ondata);
function ondata(chunk) {
debug('ondata');
var ret = dest.write(chunk);
if (false === ret) {
debug('false write response, pause',
src._readableState.awaitDrain);
src._readableState.awaitDrain++;
src.pause();
}
}
// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
function onerror(er) {
debug('onerror', er);
unpipe();
dest.removeListener('error', onerror);
if (EE.listenerCount(dest, 'error') === 0)
dest.emit('error', er);
}
// This is a brutally ugly hack to make sure that our error handler
// is attached before any userland ones. NEVER DO THIS.
if (!dest._events || !dest._events.error)
dest.on('error', onerror);
else if (isArray(dest._events.error))
dest._events.error.unshift(onerror);
else
dest._events.error = [onerror, dest._events.error];
// Both close and finish should trigger unpipe, but only once.
function onclose() {
dest.removeListener('finish', onfinish);
unpipe();
}
dest.once('close', onclose);
function onfinish() {
debug('onfinish');
dest.removeListener('close', onclose);
unpipe();
}
dest.once('finish', onfinish);
function unpipe() {
debug('unpipe');
src.unpipe(dest);
}
// tell the dest that it's being piped to
dest.emit('pipe', src);
// start the flow if it hasn't been started already.
if (!state.flowing) {
debug('pipe resume');
src.resume();
}
return dest;
};
function pipeOnDrain(src) {
return function() {
var state = src._readableState;
debug('pipeOnDrain', state.awaitDrain);
if (state.awaitDrain)
state.awaitDrain--;
if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) {
state.flowing = true;
flow(src);
}
};
}
Readable.prototype.unpipe = function(dest) {
var state = this._readableState;
// if we're not piping anywhere, then do nothing.
if (state.pipesCount === 0)
return this;
// just one destination. most common case.
if (state.pipesCount === 1) {
// passed in one, but it's not the right one.
if (dest && dest !== state.pipes)
return this;
if (!dest)
dest = state.pipes;
// got a match.
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
if (dest)
dest.emit('unpipe', this);
return this;
}
// slow case. multiple pipe destinations.
if (!dest) {
// remove all.
var dests = state.pipes;
var len = state.pipesCount;
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
for (var i = 0; i < len; i++)
dests[i].emit('unpipe', this);
return this;
}
// try to find the right one.
var i = indexOf(state.pipes, dest);
if (i === -1)
return this;
state.pipes.splice(i, 1);
state.pipesCount -= 1;
if (state.pipesCount === 1)
state.pipes = state.pipes[0];
dest.emit('unpipe', this);
return this;
};
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function(ev, fn) {
var res = Stream.prototype.on.call(this, ev, fn);
// If listening to data, and it has not explicitly been paused,
// then call resume to start the flow of data on the next tick.
if (ev === 'data' && false !== this._readableState.flowing) {
this.resume();
}
if (ev === 'readable' && this.readable) {
var state = this._readableState;
if (!state.readableListening) {
state.readableListening = true;
state.emittedReadable = false;
state.needReadable = true;
if (!state.reading) {
var self = this;
process.nextTick(function() {
debug('readable nexttick read 0');
self.read(0);
});
} else if (state.length) {
emitReadable(this, state);
}
}
}
return res;
};
Readable.prototype.addListener = Readable.prototype.on;
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function() {
var state = this._readableState;
if (!state.flowing) {
debug('resume');
state.flowing = true;
if (!state.reading) {
debug('resume read 0');
this.read(0);
}
resume(this, state);
}
return this;
};
function resume(stream, state) {
if (!state.resumeScheduled) {
state.resumeScheduled = true;
process.nextTick(function() {
resume_(stream, state);
});
}
}
function resume_(stream, state) {
state.resumeScheduled = false;
stream.emit('resume');
flow(stream);
if (state.flowing && !state.reading)
stream.read(0);
}
Readable.prototype.pause = function() {
debug('call pause flowing=%j', this._readableState.flowing);
if (false !== this._readableState.flowing) {
debug('pause');
this._readableState.flowing = false;
this.emit('pause');
}
return this;
};
function flow(stream) {
var state = stream._readableState;
debug('flow', state.flowing);
if (state.flowing) {
do {
var chunk = stream.read();
} while (null !== chunk && state.flowing);
}
}
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function(stream) {
var state = this._readableState;
var paused = false;
var self = this;
stream.on('end', function() {
debug('wrapped end');
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length)
self.push(chunk);
}
self.push(null);
});
stream.on('data', function(chunk) {
debug('wrapped data');
if (state.decoder)
chunk = state.decoder.write(chunk);
if (!chunk || !state.objectMode && !chunk.length)
return;
var ret = self.push(chunk);
if (!ret) {
paused = true;
stream.pause();
}
});
// proxy all the other methods.
// important when wrapping filters and duplexes.
for (var i in stream) {
if (util.isFunction(stream[i]) && util.isUndefined(this[i])) {
this[i] = function(method) { return function() {
return stream[method].apply(stream, arguments);
}}(i);
}
}
// proxy certain important events.
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
forEach(events, function(ev) {
stream.on(ev, self.emit.bind(self, ev));
});
// when we try to consume some more bytes, simply unpause the
// underlying stream.
self._read = function(n) {
debug('wrapped _read', n);
if (paused) {
paused = false;
stream.resume();
}
};
return self;
};
// exposed for testing purposes only.
Readable._fromList = fromList;
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
var list = state.buffer;
var length = state.length;
var stringMode = !!state.decoder;
var objectMode = !!state.objectMode;
var ret;
// nothing in the list, definitely empty.
if (list.length === 0)
return null;
if (length === 0)
ret = null;
else if (objectMode)
ret = list.shift();
else if (!n || n >= length) {
// read it all, truncate the array.
if (stringMode)
ret = list.join('');
else
ret = Buffer.concat(list, length);
list.length = 0;
} else {
// read just some of it.
if (n < list[0].length) {
// just take a part of the first list item.
// slice is the same for buffers and strings.
var buf = list[0];
ret = buf.slice(0, n);
list[0] = buf.slice(n);
} else if (n === list[0].length) {
// first list is a perfect match
ret = list.shift();
} else {
// complex case.
// we have enough to cover it, but it spans past the first buffer.
if (stringMode)
ret = '';
else
ret = new Buffer(n);
var c = 0;
for (var i = 0, l = list.length; i < l && c < n; i++) {
var buf = list[0];
var cpy = Math.min(n - c, buf.length);
if (stringMode)
ret += buf.slice(0, cpy);
else
buf.copy(ret, c, 0, cpy);
if (cpy < buf.length)
list[0] = buf.slice(cpy);
else
list.shift();
c += cpy;
}
}
}
return ret;
}
function endReadable(stream) {
var state = stream._readableState;
// If we get here before consuming all the bytes, then that is a
// bug in node. Should never happen.
if (state.length > 0)
throw new Error('endReadable called on non-empty stream');
if (!state.endEmitted) {
state.ended = true;
process.nextTick(function() {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {
state.endEmitted = true;
stream.readable = false;
stream.emit('end');
}
});
}
}
function forEach (xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
function indexOf (xs, x) {
for (var i = 0, l = xs.length; i < l; i++) {
if (xs[i] === x) return i;
}
return -1;
}
}).call(this,require('_process'))
},{"./_stream_duplex":12,"_process":10,"buffer":2,"core-util-is":17,"events":6,"inherits":7,"isarray":8,"stream":22,"string_decoder/":23,"util":1}],15:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
module.exports = Transform;
var Duplex = require('./_stream_duplex');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(Transform, Duplex);
function TransformState(options, stream) {
this.afterTransform = function(er, data) {
return afterTransform(stream, er, data);
};
this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
}
function afterTransform(stream, er, data) {
var ts = stream._transformState;
ts.transforming = false;
var cb = ts.writecb;
if (!cb)
return stream.emit('error', new Error('no writecb in Transform class'));
ts.writechunk = null;
ts.writecb = null;
if (!util.isNullOrUndefined(data))
stream.push(data);
if (cb)
cb(er);
var rs = stream._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
}
}
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(options, this);
// when the writable side finishes, then flush out anything remaining.
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
this.once('prefinish', function() {
if (util.isFunction(this._flush))
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}
Transform.prototype.push = function(chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
};
// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side. You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk. If you pass
// an error, then that'll put the hurt on the whole operation. If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function(chunk, encoding, cb) {
throw new Error('not implemented');
};
Transform.prototype._write = function(chunk, encoding, cb) {
var ts = this._transformState;
ts.writecb = cb;
ts.writechunk = chunk;
ts.writeencoding = encoding;
if (!ts.transforming) {
var rs = this._readableState;
if (ts.needTransform ||
rs.needReadable ||
rs.length < rs.highWaterMark)
this._read(rs.highWaterMark);
}
};
// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function(n) {
var ts = this._transformState;
if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
};
function done(stream, er) {
if (er)
return stream.emit('error', er);
// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var ts = stream._transformState;
if (ws.length)
throw new Error('calling transform done when ws.length != 0');
if (ts.transforming)
throw new Error('calling transform done when still transforming');
return stream.push(null);
}
},{"./_stream_duplex":12,"core-util-is":17,"inherits":7}],16:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, cb), and it'll handle all
// the drain event emission and buffering.
module.exports = Writable;
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Writable.WritableState = WritableState;
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Stream = require('stream');
util.inherits(Writable, Stream);
function WriteReq(chunk, encoding, cb) {
this.chunk = chunk;
this.encoding = encoding;
this.callback = cb;
}
function WritableState(options, stream) {
var Duplex = require('./_stream_duplex');
options = options || {};
// the point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write()
var hwm = options.highWaterMark;
var defaultHwm = options.objectMode ? 16 : 16 * 1024;
this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm;
// object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!options.objectMode;
if (stream instanceof Duplex)
this.objectMode = this.objectMode || !!options.writableObjectMode;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.needDrain = false;
// at the start of calling end()
this.ending = false;
// when end() has been called, and returned
this.ended = false;
// when 'finish' is emitted
this.finished = false;
// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
var noDecode = options.decodeStrings === false;
this.decodeStrings = !noDecode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0;
// a flag to see when we're in the middle of a write.
this.writing = false;
// when true all writes will be buffered until .uncork() call
this.corked = 0;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// a flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false;
// the callback that's passed to _write(chunk,cb)
this.onwrite = function(er) {
onwrite(stream, er);
};
// the callback that the user supplies to write(chunk,encoding,cb)
this.writecb = null;
// the amount that is being written when _write is called.
this.writelen = 0;
this.buffer = [];
// number of pending user-supplied write callbacks
// this must be 0 before 'finish' can be emitted
this.pendingcb = 0;
// emit prefinish if the only thing we're waiting for is _write cbs
// This is relevant for synchronous Transform streams
this.prefinished = false;
// True if the error was already emitted and should not be thrown again
this.errorEmitted = false;
}
function Writable(options) {
var Duplex = require('./_stream_duplex');
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
if (!(this instanceof Writable) && !(this instanceof Duplex))
return new Writable(options);
this._writableState = new WritableState(options, this);
// legacy.
this.writable = true;
Stream.call(this);
}
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function() {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
function writeAfterEnd(stream, state, cb) {
var er = new Error('write after end');
// TODO: defer error events consistently everywhere, not just the cb
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
}
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
var valid = true;
if (!util.isBuffer(chunk) &&
!util.isString(chunk) &&
!util.isNullOrUndefined(chunk) &&
!state.objectMode) {
var er = new TypeError('Invalid non-string/buffer chunk');
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
valid = false;
}
return valid;
}
Writable.prototype.write = function(chunk, encoding, cb) {
var state = this._writableState;
var ret = false;
if (util.isFunction(encoding)) {
cb = encoding;
encoding = null;
}
if (util.isBuffer(chunk))
encoding = 'buffer';
else if (!encoding)
encoding = state.defaultEncoding;
if (!util.isFunction(cb))
cb = function() {};
if (state.ended)
writeAfterEnd(this, state, cb);
else if (validChunk(this, state, chunk, cb)) {
state.pendingcb++;
ret = writeOrBuffer(this, state, chunk, encoding, cb);
}
return ret;
};
Writable.prototype.cork = function() {
var state = this._writableState;
state.corked++;
};
Writable.prototype.uncork = function() {
var state = this._writableState;
if (state.corked) {
state.corked--;
if (!state.writing &&
!state.corked &&
!state.finished &&
!state.bufferProcessing &&
state.buffer.length)
clearBuffer(this, state);
}
};
function decodeChunk(state, chunk, encoding) {
if (!state.objectMode &&
state.decodeStrings !== false &&
util.isString(chunk)) {
chunk = new Buffer(chunk, encoding);
}
return chunk;
}
// if we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
chunk = decodeChunk(state, chunk, encoding);
if (util.isBuffer(chunk))
encoding = 'buffer';
var len = state.objectMode ? 1 : chunk.length;
state.length += len;
var ret = state.length < state.highWaterMark;
// we must ensure that previous needDrain will not be reset to false.
if (!ret)
state.needDrain = true;
if (state.writing || state.corked)
state.buffer.push(new WriteReq(chunk, encoding, cb));
else
doWrite(stream, state, false, len, chunk, encoding, cb);
return ret;
}
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
state.writelen = len;
state.writecb = cb;
state.writing = true;
state.sync = true;
if (writev)
stream._writev(chunk, state.onwrite);
else
stream._write(chunk, encoding, state.onwrite);
state.sync = false;
}
function onwriteError(stream, state, sync, er, cb) {
if (sync)
process.nextTick(function() {
state.pendingcb--;
cb(er);
});
else {
state.pendingcb--;
cb(er);
}
stream._writableState.errorEmitted = true;
stream.emit('error', er);
}
function onwriteStateUpdate(state) {
state.writing = false;
state.writecb = null;
state.length -= state.writelen;
state.writelen = 0;
}
function onwrite(stream, er) {
var state = stream._writableState;
var sync = state.sync;
var cb = state.writecb;
onwriteStateUpdate(state);
if (er)
onwriteError(stream, state, sync, er, cb);
else {
// Check if we're actually ready to finish, but don't emit yet
var finished = needFinish(stream, state);
if (!finished &&
!state.corked &&
!state.bufferProcessing &&
state.buffer.length) {
clearBuffer(stream, state);
}
if (sync) {
process.nextTick(function() {
afterWrite(stream, state, finished, cb);
});
} else {
afterWrite(stream, state, finished, cb);
}
}
}
function afterWrite(stream, state, finished, cb) {
if (!finished)
onwriteDrain(stream, state);
state.pendingcb--;
cb();
finishMaybe(stream, state);
}
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
if (state.length === 0 && state.needDrain) {
state.needDrain = false;
stream.emit('drain');
}
}
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
state.bufferProcessing = true;
if (stream._writev && state.buffer.length > 1) {
// Fast case, write everything using _writev()
var cbs = [];
for (var c = 0; c < state.buffer.length; c++)
cbs.push(state.buffer[c].callback);
// count the one we are adding, as well.
// TODO(isaacs) clean this up
state.pendingcb++;
doWrite(stream, state, true, state.length, state.buffer, '', function(err) {
for (var i = 0; i < cbs.length; i++) {
state.pendingcb--;
cbs[i](err);
}
});
// Clear buffer
state.buffer = [];
} else {
// Slow case, write chunks one-by-one
for (var c = 0; c < state.buffer.length; c++) {
var entry = state.buffer[c];
var chunk = entry.chunk;
var encoding = entry.encoding;
var cb = entry.callback;
var len = state.objectMode ? 1 : chunk.length;
doWrite(stream, state, false, len, chunk, encoding, cb);
// if we didn't call the onwrite immediately, then
// it means that we need to wait until it does.
// also, that means that the chunk and cb are currently
// being processed, so move the buffer counter past them.
if (state.writing) {
c++;
break;
}
}
if (c < state.buffer.length)
state.buffer = state.buffer.slice(c);
else
state.buffer.length = 0;
}
state.bufferProcessing = false;
}
Writable.prototype._write = function(chunk, encoding, cb) {
cb(new Error('not implemented'));
};
Writable.prototype._writev = null;
Writable.prototype.end = function(chunk, encoding, cb) {
var state = this._writableState;
if (util.isFunction(chunk)) {
cb = chunk;
chunk = null;
encoding = null;
} else if (util.isFunction(encoding)) {
cb = encoding;
encoding = null;
}
if (!util.isNullOrUndefined(chunk))
this.write(chunk, encoding);
// .end() fully uncorks
if (state.corked) {
state.corked = 1;
this.uncork();
}
// ignore unnecessary end() calls.
if (!state.ending && !state.finished)
endWritable(this, state, cb);
};
function needFinish(stream, state) {
return (state.ending &&
state.length === 0 &&
!state.finished &&
!state.writing);
}
function prefinish(stream, state) {
if (!state.prefinished) {
state.prefinished = true;
stream.emit('prefinish');
}
}
function finishMaybe(stream, state) {
var need = needFinish(stream, state);
if (need) {
if (state.pendingcb === 0) {
prefinish(stream, state);
state.finished = true;
stream.emit('finish');
} else
prefinish(stream, state);
}
return need;
}
function endWritable(stream, state, cb) {
state.ending = true;
finishMaybe(stream, state);
if (cb) {
if (state.finished)
process.nextTick(cb);
else
stream.once('finish', cb);
}
state.ended = true;
}
}).call(this,require('_process'))
},{"./_stream_duplex":12,"_process":10,"buffer":2,"core-util-is":17,"inherits":7,"stream":22}],17:[function(require,module,exports){
(function (Buffer){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) &&
(objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return Buffer.isBuffer(arg);
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
}).call(this,require("buffer").Buffer)
},{"buffer":2}],18:[function(require,module,exports){
module.exports = require("./lib/_stream_passthrough.js")
},{"./lib/_stream_passthrough.js":13}],19:[function(require,module,exports){
exports = module.exports = require('./lib/_stream_readable.js');
exports.Stream = require('stream');
exports.Readable = exports;
exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');
},{"./lib/_stream_duplex.js":12,"./lib/_stream_passthrough.js":13,"./lib/_stream_readable.js":14,"./lib/_stream_transform.js":15,"./lib/_stream_writable.js":16,"stream":22}],20:[function(require,module,exports){
module.exports = require("./lib/_stream_transform.js")
},{"./lib/_stream_transform.js":15}],21:[function(require,module,exports){
module.exports = require("./lib/_stream_writable.js")
},{"./lib/_stream_writable.js":16}],22:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
module.exports = Stream;
var EE = require('events').EventEmitter;
var inherits = require('inherits');
inherits(Stream, EE);
Stream.Readable = require('readable-stream/readable.js');
Stream.Writable = require('readable-stream/writable.js');
Stream.Duplex = require('readable-stream/duplex.js');
Stream.Transform = require('readable-stream/transform.js');
Stream.PassThrough = require('readable-stream/passthrough.js');
// Backwards-compat with node 0.4.x
Stream.Stream = Stream;
// old-style streams. Note that the pipe method (the only relevant
// part of this class) is overridden in the Readable class.
function Stream() {
EE.call(this);
}
Stream.prototype.pipe = function(dest, options) {
var source = this;
function ondata(chunk) {
if (dest.writable) {
if (false === dest.write(chunk) && source.pause) {
source.pause();
}
}
}
source.on('data', ondata);
function ondrain() {
if (source.readable && source.resume) {
source.resume();
}
}
dest.on('drain', ondrain);
// If the 'end' option is not supplied, dest.end() will be called when
// source gets the 'end' or 'close' events. Only dest.end() once.
if (!dest._isStdio && (!options || options.end !== false)) {
source.on('end', onend);
source.on('close', onclose);
}
var didOnEnd = false;
function onend() {
if (didOnEnd) return;
didOnEnd = true;
dest.end();
}
function onclose() {
if (didOnEnd) return;
didOnEnd = true;
if (typeof dest.destroy === 'function') dest.destroy();
}
// don't leave dangling pipes when there are errors.
function onerror(er) {
cleanup();
if (EE.listenerCount(this, 'error') === 0) {
throw er; // Unhandled stream error in pipe.
}
}
source.on('error', onerror);
dest.on('error', onerror);
// remove all the event listeners that were added.
function cleanup() {
source.removeListener('data', ondata);
dest.removeListener('drain', ondrain);
source.removeListener('end', onend);
source.removeListener('close', onclose);
source.removeListener('error', onerror);
dest.removeListener('error', onerror);
source.removeListener('end', cleanup);
source.removeListener('close', cleanup);
dest.removeListener('close', cleanup);
}
source.on('end', cleanup);
source.on('close', cleanup);
dest.on('close', cleanup);
dest.emit('pipe', source);
// Allow for unix-like usage: A.pipe(B).pipe(C)
return dest;
};
},{"events":6,"inherits":7,"readable-stream/duplex.js":11,"readable-stream/passthrough.js":18,"readable-stream/readable.js":19,"readable-stream/transform.js":20,"readable-stream/writable.js":21}],23:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
var Buffer = require('buffer').Buffer;
var isBufferEncoding = Buffer.isEncoding
|| function(encoding) {
switch (encoding && encoding.toLowerCase()) {
case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true;
default: return false;
}
}
function assertEncoding(encoding) {
if (encoding && !isBufferEncoding(encoding)) {
throw new Error('Unknown encoding: ' + encoding);
}
}
// StringDecoder provides an interface for efficiently splitting a series of
// buffers into a series of JS strings without breaking apart multi-byte
// characters. CESU-8 is handled as part of the UTF-8 encoding.
//
// @TODO Handling all encodings inside a single object makes it very difficult
// to reason about this code, so it should be split up in the future.
// @TODO There should be a utf8-strict encoding that rejects invalid UTF-8 code
// points as used by CESU-8.
var StringDecoder = exports.StringDecoder = function(encoding) {
this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');
assertEncoding(encoding);
switch (this.encoding) {
case 'utf8':
// CESU-8 represents each of Surrogate Pair by 3-bytes
this.surrogateSize = 3;
break;
case 'ucs2':
case 'utf16le':
// UTF-16 represents each of Surrogate Pair by 2-bytes
this.surrogateSize = 2;
this.detectIncompleteChar = utf16DetectIncompleteChar;
break;
case 'base64':
// Base-64 stores 3 bytes in 4 chars, and pads the remainder.
this.surrogateSize = 3;
this.detectIncompleteChar = base64DetectIncompleteChar;
break;
default:
this.write = passThroughWrite;
return;
}
// Enough space to store all bytes of a single character. UTF-8 needs 4
// bytes, but CESU-8 may require up to 6 (3 bytes per surrogate).
this.charBuffer = new Buffer(6);
// Number of bytes received for the current incomplete multi-byte character.
this.charReceived = 0;
// Number of bytes expected for the current incomplete multi-byte character.
this.charLength = 0;
};
// write decodes the given buffer and returns it as JS string that is
// guaranteed to not contain any partial multi-byte characters. Any partial
// character found at the end of the buffer is buffered up, and will be
// returned when calling write again with the remaining bytes.
//
// Note: Converting a Buffer containing an orphan surrogate to a String
// currently works, but converting a String to a Buffer (via `new Buffer`, or
// Buffer#write) will replace incomplete surrogates with the unicode
// replacement character. See https://codereview.chromium.org/121173009/ .
StringDecoder.prototype.write = function(buffer) {
var charStr = '';
// if our last write ended with an incomplete multibyte character
while (this.charLength) {
// determine how many remaining bytes this buffer has to offer for this char
var available = (buffer.length >= this.charLength - this.charReceived) ?
this.charLength - this.charReceived :
buffer.length;
// add the new bytes to the char buffer
buffer.copy(this.charBuffer, this.charReceived, 0, available);
this.charReceived += available;
if (this.charReceived < this.charLength) {
// still not enough chars in this buffer? wait for more ...
return '';
}
// remove bytes belonging to the current character from the buffer
buffer = buffer.slice(available, buffer.length);
// get the character that was split
charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);
// CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
var charCode = charStr.charCodeAt(charStr.length - 1);
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
this.charLength += this.surrogateSize;
charStr = '';
continue;
}
this.charReceived = this.charLength = 0;
// if there are no more bytes in this buffer, just emit our char
if (buffer.length === 0) {
return charStr;
}
break;
}
// determine and set charLength / charReceived
this.detectIncompleteChar(buffer);
var end = buffer.length;
if (this.charLength) {
// buffer the incomplete character bytes we got
buffer.copy(this.charBuffer, 0, buffer.length - this.charReceived, end);
end -= this.charReceived;
}
charStr += buffer.toString(this.encoding, 0, end);
var end = charStr.length - 1;
var charCode = charStr.charCodeAt(end);
// CESU-8: lead surrogate (D800-DBFF) is also the incomplete character
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
var size = this.surrogateSize;
this.charLength += size;
this.charReceived += size;
this.charBuffer.copy(this.charBuffer, size, 0, size);
buffer.copy(this.charBuffer, 0, 0, size);
return charStr.substring(0, end);
}
// or just emit the charStr
return charStr;
};
// detectIncompleteChar determines if there is an incomplete UTF-8 character at
// the end of the given buffer. If so, it sets this.charLength to the byte
// length that character, and sets this.charReceived to the number of bytes
// that are available for this character.
StringDecoder.prototype.detectIncompleteChar = function(buffer) {
// determine how many bytes we have to check at the end of this buffer
var i = (buffer.length >= 3) ? 3 : buffer.length;
// Figure out if one of the last i bytes of our buffer announces an
// incomplete char.
for (; i > 0; i--) {
var c = buffer[buffer.length - i];
// See http://en.wikipedia.org/wiki/UTF-8#Description
// 110XXXXX
if (i == 1 && c >> 5 == 0x06) {
this.charLength = 2;
break;
}
// 1110XXXX
if (i <= 2 && c >> 4 == 0x0E) {
this.charLength = 3;
break;
}
// 11110XXX
if (i <= 3 && c >> 3 == 0x1E) {
this.charLength = 4;
break;
}
}
this.charReceived = i;
};
StringDecoder.prototype.end = function(buffer) {
var res = '';
if (buffer && buffer.length)
res = this.write(buffer);
if (this.charReceived) {
var cr = this.charReceived;
var buf = this.charBuffer;
var enc = this.encoding;
res += buf.slice(0, cr).toString(enc);
}
return res;
};
function passThroughWrite(buffer) {
return buffer.toString(this.encoding);
}
function utf16DetectIncompleteChar(buffer) {
this.charReceived = buffer.length % 2;
this.charLength = this.charReceived ? 2 : 0;
}
function base64DetectIncompleteChar(buffer) {
this.charReceived = buffer.length % 3;
this.charLength = this.charReceived ? 3 : 0;
}
},{"buffer":2}],24:[function(require,module,exports){
function controls(){
var gamejs = require('gamejs');
gamejs.ready(function() {
gamejs.logging.info('I am ready!')
});
gamejs.event.onEvent(function(event) {
if (event.type === gamejs.event.MOUSE_DOWN) {
// mouse 0: Left mouse button
// mouse 1: Middle Mouse button
// mouse 2: Right Mouse Button
gamejs.logging.info(event.pos, event.button);
// console.log(event.button)
var position = block
switch(event.button){
case 0: // Break Block
if(game.getBlock(position) != 0){
game.setBlock(position, 0)
console.log("Destroyed: " + position);
}
else {
console.log("No Block Found")
}
break;
case 1:
console.log("Block ID" + game.getBlock(position))
break;
case 2:
position = adjacentBlock
console.log(adjacentBlock)
game.setBlock(position, 1);
console.log("Created Block: " + position);
break;
}
} else if (event.type === gamejs.event.KEY_DOWN) {
gamejs.logging.info(event.key);
// console.log(event.key)
}
});
}
controls();
var createGame = require('voxel-engine');
var highlight = require('voxel-highlight');
var game = createGame({
texturePath: './textures/',
generate: function(x, y, z) {
return y === 1 ? 1 : 0
},
materials: [['grass', 'dirt', 'grass_dirt'], 'brick', 'dirt'],
materialFlatColor: false,
chunkSize: 32,
chunkDistance: 2,
worldOrigin: [0, 0, 0],
controls: { discreteFire: true },
lightsDisabled: false,
fogDisabled: true,
// playerHeight: 1.62,
generateChunks : true,
keybindings: {
'W': 'forward'
, 'A': 'left'
, 'S': 'backward'
, 'D': 'right'
, '<up>': 'forward'
, '<left>': 'left'
, '<down>': 'backward'
, '<right>': 'right'
, '<mouse 0>': 'fire'
, '<mouse 1>': 'fire'
, '<mouse 2>': 'firealt'
, '<mouse 3>': 'alt' // left mouse button
, '<space>': 'jump'
, '<shift>': 'crouch'
, '<control>': 'alt'
}
});
var modal = require('voxel-modal')
var voxelConsole = require('voxel-console')
var commands = require('voxel-commands')
// Terrain
function generateTerrain(){
var terrain = require('voxel-perlin-terrain')
var chunkSize = 32
// initialize your noise with a seed, floor height, ceiling height and scale factor
var generateChunk = terrain('foo', 0, 40, 50)
// then hook it up to your game as such:
game.voxels.on('missingChunk', function(p) {
var voxels = generateChunk(p, chunkSize)
var chunk = {
position: p,
dims: [chunkSize, chunkSize, chunkSize],
voxels: voxels
}
game.showChunk(chunk)
})
}
// var createTree = require('voxel-forest');
// for (var i = 0; i < 250; i++) {
// createTree(game, { bark: 2, leaves: 3 });
// }
var adjacentBlock;
var block;
var highlight = require('voxel-highlight')
var highlighter = highlight(game, {
color: '#00CC00'
})
highlighter.on('highlight', function (voxelPos) { block = voxelPos })
highlighter.on('remove', function (voxelPos) { block = null })
highlighter.on('highlight-adjacent', function (voxelPos) { adjacentBlock = voxelPos })
highlighter.on('remove-adjacent', function (voxelPos) { })
// var createSky = require('voxel-sky')(game);
// var sky = createSky();
// game.on('tick', sky);
var container = document.body;
game.appendTo(container);
var createPlayer = require('voxel-player')(game);
var player = createPlayer('textures/player.png');
player.possess();
player.yaw.position.set(2, 14, 4);
player.position.set(0, 20, 0);
// Flying
var fly = require('voxel-fly');
var makeFly = fly(game);
makeFly(player);
makeFly(game.controls.target())
},{"gamejs":25,"voxel-commands":52,"voxel-console":59,"voxel-engine":66,"voxel-fly":107,"voxel-highlight":112,"voxel-modal":115,"voxel-perlin-terrain":119,"voxel-player":121}],25:[function(require,module,exports){
var matrix = require('./gamejs/math/matrix');
var objects = require('./gamejs/utils/objects');
var Callback = require('./gamejs/utils/callback').Callback;
/**
* @fileoverview `gamejs.ready()` is maybe the most important function as it kickstarts your app:
*
* var gamejs = require('gamejs');
* ready(function() {
* gamejs.logging.info('I am ready!')
* });
*
* If you use images or sounds preload all assets with `gamejs.preload(['./files/foo.png'])` before calling `ready()`.
*
* Also in this module is the `Rect` class which is generally useful when dealing with Surfaces and simple rectangles (e.g. for collisions).
*/
// preloading stuff
var gamejs = exports;
var RESOURCES = {};
/**
* @ignore
*/
exports.thread = require('./gamejs/thread');
/**
* ReadyFn is called once all modules and assets are loaded.
* @param {Function} callbackFunction the function to be called once gamejs finished loading
* @name ready
*/
if (gamejs.thread.inWorker === true) {
exports.ready = function(readyFn) {
require('./gamejs/thread')._ready();
gamejs.init();
readyFn();
};
} else {
exports.ready = function(readyFn) {
var getMixerProgress = null;
var getImageProgress = null;
// init time instantly - we need it for preloaders
gamejs.time.init();
// 2.
function _ready() {
if (!document.body) {
return window.setTimeout(_ready, 50);
}
getImageProgress = gamejs.image.preload(RESOURCES);
try {
getMixerProgress = gamejs.audio.preload(RESOURCES);
} catch (e) {
gamejs.debug('Error loading audio files ', e);
}
window.setTimeout(_readyResources, 50);
}
// 3.
function _readyResources() {
if (getImageProgress() < 1 || getMixerProgress() < 1) {
return window.setTimeout(_readyResources, 100);
}
gamejs.display.init();
gamejs.image.init();
gamejs.audio.init();
gamejs.event.init();
gamejs.math.random.init();
readyFn();
}
// 1.
window.setTimeout(_ready, 13);
function getLoadProgress() {
if (getImageProgress) {
return (0.5 * getImageProgress()) + (0.5 * getMixerProgress());
}
return 0.1;
}
return getLoadProgress;
};
}
/**
* Initialize all gamejs modules. This is automatically called
* by `gamejs.ready()`.
* @returns {Object} the properties of this objecte are the moduleIds that failed, they value are the exceptions
* @ignore
*/
exports.init = function() {
var errorModules = {};
['time', 'display', 'image', 'audio', 'event'].forEach(function(moduleName) {
try {
gamejs[moduleName].init();
} catch (e) {
errorModules[moduleName] = e.toString();
}
});
return errorModules;
};
var resourceBaseHref = function() {
return (window.$g && window.$g.resourceBaseHref) || document.location.href;
};
/**
* Preload resources.
* @param {Array} resources list of resources paths
* @name preload
*/
var preload = exports.preload = function(resources) {
var uri = require('./gamejs/utils/uri');
var baseHref = resourceBaseHref();
resources.forEach(function(res) {
RESOURCES[res] = uri.resolve(baseHref, res);
}, this);
return;
};
/**
* The function passed to `onTick` will continously be called at a
* frequency determined by the browser (typically between 1 and 60 times per second).
* @param {Function} callbackFunction the function you want to be called
* @param {Function} callbackScope optional scope for the function call
*/
exports.onTick = function(fn, scope) {
/** ignore **/
exports.time._CALLBACKS.push(new Callback(fn, scope));
};
/**
* Normalize various ways to specify a Rect into {left, top, width, height} form.
* @ignore
*
*/
var normalizeRectArguments = exports.normalizeRectArguments = function () {
var left = 0;
var top = 0;
var width = 0;
var height = 0;
if (arguments.length === 2) {
if (arguments[0] instanceof Array && arguments[1] instanceof Array) {
left = arguments[0][0];
top = arguments[0][1];
width = arguments[1][0];
height = arguments[1][1];
} else {
left = arguments[0];
top = arguments[1];
}
} else if (arguments.length === 1 && arguments[0] instanceof Array) {
left = arguments[0][0];
top = arguments[0][1];
width = arguments[0][2];
height = arguments[0][3];
} else if (arguments.length === 1 && arguments[0] instanceof Rect) {
left = arguments[0].left;
top = arguments[0].top;
width = arguments[0].width;
height = arguments[0].height;
} else if (arguments.length === 4) {
left = arguments[0];
top = arguments[1];
width = arguments[2];
height = arguments[3];
} else {
throw new Error('not a valid rectangle specification');
}
return {left: left || 0, top: top || 0, width: width || 0, height: height || 0};
};
/**
* Creates a Rect. Rects are used to hold rectangular areas. There are a couple
* of convinient ways to create Rects with different arguments and defaults.
*
* Any function that requires a `gamejs.Rect` argument also accepts any of the
* constructor value combinations `Rect` accepts.
*
* Rects are used a lot. They are good for collision detection, specifying
* an area on the screen (for blitting) or just to hold an objects position.
*
* The Rect object has several virtual attributes which can be used to move and align the Rect:
*
* top, left, bottom, right
* topleft, bottomleft, topright, bottomright
* center
* width, height
* w,h
*
* All of these attributes can be assigned to.
* Assigning to width or height changes the dimensions of the rectangle; all other
* assignments move the rectangle without resizing it. Notice that some attributes
* are Numbers and others are pairs of Numbers.
*
* @example
* new Rect([left, top]) // width & height default to 0
* new Rect(left, top) // width & height default to 0
* new Rect(left, top, width, height)
* new Rect([left, top], [width, height])
* new Rect(oldRect) // clone of oldRect is created
*
* @property {Number} right
* @property {Number} bottom
* @property {Number} center
* @constructor
* @param {Array|gamejs.Rect} position Array holding left and top coordinates
* @param {Array} dimensions Array holding width and height
*/
var Rect = exports.Rect = function() {
var args = normalizeRectArguments.apply(this, arguments);
/**
* Left, X coordinate
* @type Number
*/
this.left = args.left;
/**
* Top, Y coordinate
* @type Number
*/
this.top = args.top;
/**
* Width of rectangle
* @type Number
*/
this.width = args.width;
/**
* Height of rectangle
* @type Number
*/
this.height = args.height;
return this;
};
objects.accessors(Rect.prototype, {
/**
* Bottom, Y coordinate
* @name Rect.prototype.bottom
* @type Number
*/
'bottom': {
get: function() {
return this.top + this.height;
},
set: function(newValue) {
this.top = newValue - this.height;
return;
}
},
/**
* Right, X coordinate
* @name Rect.prototype.right
* @type Number
*/
'right': {
get: function() {
return this.left + this.width;
},
set: function(newValue) {
this.left = newValue - this.width;
}
},
/**
* Center Position. You can assign a rectangle form.
* @name Rect.prototype.center
* @type Array
*/
'center': {
get: function() {
return [this.left + (this.width / 2) | 0,
this.top + (this.height / 2) | 0
];
},
set: function() {
var args = normalizeRectArguments.apply(this, arguments);
this.left = args.left - (this.width / 2) | 0;
this.top = args.top - (this.height / 2) | 0;
return;
}
},
/**
* Top-left Position. You can assign a rectangle form.
* @name Rect.prototype.topleft
* @type Array
*/
'topleft': {
get: function() {
return [this.left, this.top];
},
set: function() {
var args = normalizeRectArguments.apply(this, arguments);
this.left = args.left;
this.top = args.top;
return;
}
},
/**
* Bottom-left Position. You can assign a rectangle form.
* @name Rect.prototype.bottomleft
* @type Array
*/
'bottomleft': {
get: function() {
return [this.left, this.bottom];
},
set: function() {
var args = normalizeRectArguments.apply(this, arguments);
this.left = args.left;
this.bottom = args.top;
return;
}
},
/**
* Top-right Position. You can assign a rectangle form.
* @name Rect.prototype.topright
* @type Array
*/
'topright': {
get: function() {
return [this.right, this.top];
},
set: function() {
var args = normalizeRectArguments.apply(this, arguments);
this.right = args.left;
this.top = args.top;
return;
}
},
/**
* Bottom-right Position. You can assign a rectangle form.
* @name Rect.prototype.bottomright
* @type Array
*/
'bottomright': {
get: function() {
return [this.right, this.bottom];
},
set: function() {
var args = normalizeRectArguments.apply(this, arguments);
this.right = args.left;
this.bottom = args.top;
return;
}
},
/**
* Position x value, alias for `left`.
* @name Rect.prototype.y
* @type Array
*/
'x': {
get: function() {
return this.left;
},
set: function(newValue) {
this.left = newValue;
return;
}
},
/**
* Position y value, alias for `top`.
* @name Rect.prototype.y
* @type Array
*/
'y': {
get: function() {
return this.top;
},
set: function(newValue) {
this.top = newValue;
return;
}
}
});
/**
* Move returns a new Rect, which is a version of this Rect
* moved by the given amounts. Accepts any rectangle form.
* as argument.
*
* @param {Number|gamejs.Rect} x amount to move on x axis
* @param {Number} y amount to move on y axis
*/
Rect.prototype.move = function() {
var args = normalizeRectArguments.apply(this, arguments);
return new Rect(this.left + args.left, this.top + args.top, this.width, this.height);
};
/**
* Move this Rect in place - not returning a new Rect like `move(x, y)` would.
*
* `moveIp(x,y)` or `moveIp([x,y])`
*
* @param {Number|gamejs.Rect} x amount to move on x axis
* @param {Number} y amount to move on y axis
*/
Rect.prototype.moveIp = function() {
var args = normalizeRectArguments.apply(this, arguments);
this.left += args.left;
this.top += args.top;
return;
};
/**
* Return the area in which this Rect and argument Rect overlap.
*
* @param {gamejs.Rect} Rect to clip this one into
* @returns {gamejs.Rect} new Rect which is completely inside the argument Rect,
* zero sized Rect if the two rectangles do not overlap
*/
Rect.prototype.clip = function(rect) {
if(!this.collideRect(rect)) {
return new Rect(0,0,0,0);
}
var x, y, width, height;
// Left
if ((this.left >= rect.left) && (this.left < rect.right)) {
x = this.left;
} else if ((rect.left >= this.left) && (rect.left < this.right)) {
x = rect.left;
}
// Right
if ((this.right > rect.left) && (this.right <= rect.right)) {
width = this.right - x;
} else if ((rect.right > this.left) && (rect.right <= this.right)) {
width = rect.right - x;
}
// Top
if ((this.top >= rect.top) && (this.top < rect.bottom)) {
y = this.top;
} else if ((rect.top >= this.top) && (rect.top < this.bottom)) {
y = rect.top;
}
// Bottom
if ((this.bottom > rect.top) && (this.bottom <= rect.bottom)) {
height = this.bottom - y;
} else if ((rect.bottom > this.top) && (rect.bottom <= this.bottom)) {
height = rect.bottom - y;
}
return new Rect(x, y, width, height);
};
/**
* Join two rectangles
*
* @param {gamejs.Rect} union with this rectangle
* @returns {gamejs.Rect} rectangle containing area of both rectangles
*/
Rect.prototype.union = function(rect) {
var x, y, width, height;
x = Math.min(this.left, rect.left);
y = Math.min(this.top, rect.top);
width = Math.max(this.right, rect.right) - x;
height = Math.max(this.bottom, rect.bottom) - y;
return new Rect(x, y, width, height);
};
/**
* Grow or shrink the rectangle size
*
* @param {Number} amount to change in the width
* @param {Number} amount to change in the height
* @returns {gamejs.Rect} inflated rectangle centered on the original rectangle's center
*/
Rect.prototype.inflate = function(x, y) {
var copy = this.clone();
copy.inflateIp(x, y);
return copy;
};
/**
* Grow or shrink this Rect in place - not returning a new Rect like `inflate(x, y)` would.
*
* @param {Number} amount to change in the width
* @param {Number} amount to change in the height
*/
Rect.prototype.inflateIp = function(x, y) {
// Use Math.floor here to deal with rounding of negative numbers the
// way this relies on.
this.left -= Math.floor(x / 2);
this.top -= Math.floor(y / 2);
this.width += x;
this.height += y;
};
/**
* Check for collision with a point.
*
* `collidePoint(x,y)` or `collidePoint([x,y])` or `collidePoint(new Rect(x,y))`
*
* @param {Array|gamejs.Rect} point the x and y coordinates of the point to test for collision
* @returns {Boolean} true if the point collides with this Rect
*/
Rect.prototype.collidePoint = function() {
var args = normalizeRectArguments.apply(this, arguments);
return (this.left <= args.left && args.left <= this.right) &&
(this.top <= args.top && args.top <= this.bottom);
};
/**
* Check for collision with a Rect.
* @param {gamejs.Rect} rect the Rect to test check for collision
* @returns {Boolean} true if the given Rect collides with this Rect
*/
Rect.prototype.collideRect = function(rect) {
return !(this.left > rect.right || this.right < rect.left ||
this.top > rect.bottom || this.bottom < rect.top);
};
/**
* @param {Array} pointA start point of the line
* @param {Array} pointB end point of the line
* @returns true if the line intersects with the rectangle
* @see http://stackoverflow.com/questions/99353/how-to-test-if-a-line-segment-intersects-an-axis-aligned-rectange-in-2d/293052#293052
*
*/
Rect.prototype.collideLine = function(p1, p2) {
var x1 = p1[0];
var y1 = p1[1];
var x2 = p2[0];
var y2 = p2[1];
function linePosition(point) {
var x = point[0];
var y = point[1];
return (y2 - y1) * x + (x1 - x2) * y + (x2 * y1 - x1 * y2);
}
var relPoses = [[this.left, this.top],
[this.left, this.bottom],
[this.right, this.top],
[this.right, this.bottom]
].map(linePosition);
var noNegative = true;
var noPositive = true;
var noZero = true;
relPoses.forEach(function(relPos) {
if (relPos > 0) {
noPositive = false;
} else if (relPos < 0) {
noNegative = false;
} else if (relPos === 0) {
noZero = false;
}
}, this);
if ( (noNegative || noPositive) && noZero) {
return false;
}
return !((x1 > this.right && x2 > this.right) ||
(x1 < this.left && x2 < this.left) ||
(y1 < this.top && y2 < this.top) ||
(y1 > this.bottom && y2 > this.bottom)
);
};
/**
* @returns {String} Like "[x, y][w, h]"
*/
Rect.prototype.toString = function() {
return ["[", this.left, ",", this.top, "]"," [",this.width, ",", this.height, "]"].join("");
};
/**
* @returns {gamejs.Rect} A new copy of this rect
*/
Rect.prototype.clone = function() {
return new Rect(this);
};
/**
* @ignore
*/
exports.event = require('./gamejs/event');
/**
* @ignore
*/
exports.font = require('./gamejs/font');
/**
* @ignore
*/
exports.http = require('./gamejs/http');
/**
* @ignore
*/
exports.image = require('./gamejs/image');
/**
* @ignore
*/
exports.audio = require('./gamejs/audio');
/**
* @ignore
*/
exports.graphics = require('./gamejs/graphics');
/**
* @ignore
*/
exports.logging = require('./gamejs/logging');
/**
* @ignore
*/
exports.math = {
matrix: require('./gamejs/math/matrix'),
vectors: require('./gamejs/math/vectors'),
angles: require('./gamejs/math/angles'),
binaryheap: require('./gamejs/math/binaryheap'),
random: require('./gamejs/math/random'),
noise: require('./gamejs/math/noise'),
};
/**
* @ignore
*/
exports.utils = {
arrays: require('./gamejs/utils/arrays'),
objects: require('./gamejs/utils/objects'),
uri: require('./gamejs/utils/uri'),
strings: require('./gamejs/utils/strings'),
xml: require('./gamejs/utils/xml'),
base64: require('./gamejs/utils/base64')
};
/**
* @ignore
*/
exports.display = require('./gamejs/display');
/**
* @ignore
*/
exports.pathfinding = require('./gamejs/pathfinding');
/**
* @ignore
*/
exports.tiledmap = require('./gamejs/tiledmap');
/**
* @ignore
*/
exports.time = require('./gamejs/time');
/**
* @ignore
*/
exports.pixelcollision = require('./gamejs/pixelcollision');
},{"./gamejs/audio":26,"./gamejs/display":27,"./gamejs/event":28,"./gamejs/font":29,"./gamejs/graphics":30,"./gamejs/http":31,"./gamejs/image":32,"./gamejs/logging":33,"./gamejs/math/angles":34,"./gamejs/math/binaryheap":35,"./gamejs/math/matrix":36,"./gamejs/math/noise":37,"./gamejs/math/random":38,"./gamejs/math/vectors":39,"./gamejs/pathfinding":40,"./gamejs/pixelcollision":41,"./gamejs/thread":42,"./gamejs/tiledmap":43,"./gamejs/time":44,"./gamejs/utils/arrays":45,"./gamejs/utils/base64":46,"./gamejs/utils/callback":47,"./gamejs/utils/objects":48,"./gamejs/utils/strings":49,"./gamejs/utils/uri":50,"./gamejs/utils/xml":51}],26:[function(require,module,exports){
var gamejs = require('../gamejs');
/**
* @fileoverview Playing sounds with the html5 audio tag. Audio files must be preloaded
* with the usual `gamejs.preload()` function. Ogg, wav and webm supported.
*
*/
var CACHE = {};
/**
* need to export preloading status for require
* @ignore
*/
var _PRELOADING = false;
/**
* @ignore
*/
var NUM_CHANNELS = 8;
/**
* Sets the number of available channels for the mixer. The default value is 8.
*/
exports.setNumChannels = function(count) {
NUM_CHANNELS = parseInt(count, 10) || NUM_CHANNELS;
};
exports.getNumChannels = function() {
return NUM_CHANNELS;
};
/**
* put all audios on page in cache
* if same domain as current page, remove common href-prefix
* @ignore
*/
exports.init = function() {
var audios = Array.prototype.slice.call(document.getElementsByTagName("audio"), 0);
addToCache(audios);
return;
};
/**
* Preload the audios into cache
* @param {String[]} List of audio URIs to load
* @returns {Function} which returns 0-1 for preload progress
* @ignore
*/
exports.preload = function(audioUrls, showProgressOrImage) {
var countTotal = 0;
var countLoaded = 0;
function incrementLoaded() {
countLoaded++;
if (countLoaded == countTotal) {
_PRELOADING = false;
}
}
function getProgress() {
return countTotal > 0 ? countLoaded / countTotal : 1;
}
function successHandler() {
addToCache(this);
incrementLoaded();
}
function errorHandler() {
incrementLoaded();
throw new Error('Error loading ' + this.src);
}
for (var key in audioUrls) {
if (key.indexOf('wav') == -1 && key.indexOf('ogg') == -1 && key.indexOf('webm') == -1) {
continue;
}
countTotal++;
var audio = new Audio();
audio.addEventListener('canplay', successHandler, true);
audio.addEventListener('error', errorHandler, true);
audio.src = audioUrls[key];
audio.gamejsKey = key;
audio.load();
}
if (countTotal > 0) {
_PRELOADING = true;
}
return getProgress;
};
/**
* @ignore
*/
exports.isPreloading = function() {
return _PRELOADING;
};
/**
* @param {dom.ImgElement} audios the <audio> elements to put into cache
* @ignore
*/
function addToCache(audios) {
if (!(audios instanceof Array)) {
audios = [audios];
}
var docLoc = document.location.href;
audios.forEach(function(audio) {
CACHE[audio.gamejsKey] = audio;
});
return;
}
/**
* Sounds can be played back.
* @constructor
* @param {String|dom.AudioElement} uriOrAudio the uri of <audio> dom element
* of the sound
*/
exports.Sound = function Sound(uriOrAudio) {
var cachedAudio;
if (typeof uriOrAudio === 'string') {
cachedAudio = CACHE[uriOrAudio];
} else {
cachedAudio = uriOrAudio;
}
if (!cachedAudio) {
// TODO sync audio loading
throw new Error('Missing "' + uriOrAudio + '", gamejs.preload() all audio files before loading');
}
var channels = [];
var i = NUM_CHANNELS;
while (i-->0) {
var audio = new Audio();
audio.preload = "auto";
audio.loop = false;
audio.src = cachedAudio.src;
channels.push(audio);
}
/**
* start the sound
* @param {Boolean} loop whether the audio should loop for ever or not
*/
this.play = function(loop) {
channels.some(function(audio) {
if (audio.ended || audio.paused) {
audio.loop = !!loop;
audio.play();
return true;
}
return false;
});
};
/**
* Stop the sound.
* This will stop the playback of this Sound on any active Channels.
*/
this.stop = function() {
channels.forEach(function(audio) {
audio.stop();
});
};
/**
* Set volume of this sound
* @param {Number} value volume from 0 to 1
*/
this.setVolume = function(value) {
channels.forEach(function(audio) {
audio.volume = value;
});
};
/**
* @returns {Number} the sound's volume from 0 to 1
*/
this.getVolume = function() {
return channels[0].volume;
};
/**
* @returns {Number} Duration of this sound in seconds
*/
this.getLength = function() {
return channels[0].duration;
};
return this;
};
},{"../gamejs":25}],27:[function(require,module,exports){
var Surface = require('./graphics').Surface;
/**
* @fileoverview Methods to create, access and manipulate the display Surface.
*
* You can just grab the canvas element whether it exists in the DOM or not (GameJs
* will create it if necessary):
*
* var display = gamejs.display.getSurface();
*
* If you need to resize the canvas - although it is recommended to style it with CSS - you can
* call the `setMode()` function, which conviniently returns the new display surface:
*
* newDisplay = gamejs.display.setMode([800, 600]);
*
* ### Browser window gets resized
*
* When the canvas size is configured with CSS, the display surface might change when
* the browser window is resized. GameJs will internally deal with this and recreate
* the the display surface with the new size.
*
* You will typically not have to worry about this but if you want to get informed
* about a display resize, you can register a callback with `gamejs.event.onDisplayResize`.
*
*
* ### Flags
*
* For advanced uses you can set a few modes which additionally change how the display
* behaves with regards to pixel smoothing and whether you want a fullscreen canvas with
* or withouth the mouse pointer locked inside the window (for endless mouse movement in
* all directions).
*
*
* `gamejs.display.setMode()` understands three flags:
*
* * gamejs.display.FULLSCREEN
* * gamejs.display.DISABLE_SMOOTHING
* * gamejs.display.POINTERLOCK (implies FULLSCREEN)
*
* For example:
* // disable smoothing
* gamejs.display.setMode([800, 600], gamejs.display.DISABLE_SMOOTHING);
* // disable smoothing and fullscreen
* gamejs.display.setMode(
[800, 600],
gamejs.display.DISABLE_SMOOTHING | gamejs.display.FULLSCREEN
);
*
* ### Fullscreen mode
*
* When `setMode()` is called with the fullscreen flag then the fullscreen mode can be enabled by the
* player by clicking on the DOM element with id "gjs-fullscreen-toggle". Browser security requires
* that a user enables fullscreen with a "gesture" (e.g., clicking a button) and we can not enable fullscreen
* in code.
*
* Fullscreen mode can be exited by many keys, e.g., anything window manager related (ALT-TAB) or ESC. A lot
* of keys will trigger a browser information popup explaining how fullscreen mode can be exited.
*
* The following keys are "whitelisted" in fullscreen mode and will not trigger such a browser popup:
*
* * left arrow, right arrow, up arrow, down arrow
* * space
* * shift, control, alt
* * page up, page down
* * home, end, tab, meta
*
*
* ### Relevant DOM node ids accessed by this module
*
* You can provide your own tags with those ids
*
* * gjs-canvas - the display surface
* * gjs-loader - loading bar
* * gjs-fullscreen-toggle a clickable element to enable fullscreen
* * gjs-canvas-wrapper this wrapper is added when in fullscreen mode
*
*/
var CANVAS_ID = "gjs-canvas";
var LOADER_ID = "gjs-loader";
var SURFACE = null;
/**
* Pass this flag to `gamejs.display.setMode(resolution, flags)` to disable
* pixel smoothing; this is, for example, useful for retro-style, low resolution graphics
* where you don't want the browser to smooth them when scaling & drawing.
*/
var DISABLE_SMOOTHING = exports.DISABLE_SMOOTHING = 2;
var FULLSCREEN = exports.FULLSCREEN = 4;
var POINTERLOCK = exports.POINTERLOCK = 8;
var _flags = 0;
/**
* @returns {document.Element} the canvas dom element
* @ignore
*/
var getCanvas = exports._getCanvas = function() {
var displayCanvas = document.getElementById(CANVAS_ID);
if (!displayCanvas) {
displayCanvas = document.createElement("canvas");
displayCanvas.setAttribute("id", CANVAS_ID);
document.body.appendChild(displayCanvas);
}
return displayCanvas;
};
var getFullScreenToggle = function() {
var fullScreenButton = document.getElementById('gjs-fullscreen-toggle');
if (!fullScreenButton) {
// before canvas
fullScreenButton = document.createElement('button');
fullScreenButton.innerHTML = 'Fullscreen';
fullScreenButton.id = 'gjs-fullscreen-toggle';
var canvas = getCanvas();
canvas.parentNode.insertBefore(fullScreenButton, canvas);
canvas.parentNode.insertBefore(document.createElement('br'), canvas);
}
return fullScreenButton;
};
var fullScreenChange = function(event) {
var gjsEvent ={
type: isFullScreen() ? require('./event').DISPLAY_FULLSCREEN_ENABLED :
require('./event').DISPLAY_FULLSCREEN_DISABLED
};
if (isFullScreen()) {
if (_flags & POINTERLOCK) {
enablePointerLock();
}
}
require('./event')._triggerCallbacks(gjsEvent);
};
exports.hasPointerLock = function() {
return !!(document.pointerLockElement ||
document.webkitFullscreenElement ||
document.mozFullscreenElement ||
document.mozFullScreenElement);
};
function onResize(event) {
var canvas = getCanvas();
SURFACE._canvas.width = canvas.clientWidth;
SURFACE._canvas.height = canvas.clientHeight;
require('./event')._triggerCallbacks({
type: require('./event').DISPLAY_RESIZE
});
}
/**
* Create the master Canvas plane.
* @ignore
*/
exports.init = function() {
// create canvas element if not yet present
var canvas = getCanvas();
if (!canvas.getAttribute('tabindex')) {
// to be focusable, tabindex must be set
canvas.setAttribute("tabindex", 1);
canvas.focus();
}
// remove loader if any;
var $loader = document.getElementById(LOADER_ID);
if ($loader) {
$loader.style.display = "none";
}
var $displaySurface = document.getElementById(CANVAS_ID);
if ($displaySurface) {
$displaySurface.style.display = 'block';
}
// hook into resize
window.addEventListener("resize", onResize, false);
return;
};
var isFullScreen = exports.isFullscreen = function() {
return (document.fullScreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.webkitDisplayingFullscreen);
};
/**
* Switches the display window normal browser mode and fullscreen.
* @ignore
* @returns {Boolean} true if operation was successfull, false otherwise
*/
var enableFullScreen = function(event) {
var wrapper = getCanvas();
wrapper.requestFullScreen = wrapper.requestFullScreen || wrapper.mozRequestFullScreen || wrapper.webkitRequestFullScreen;
if (!wrapper.requestFullScreen) {
return false;
}
// @xbrowser chrome allows keboard input onl if ask for it (why oh why?)
if (Element.ALLOW_KEYBOARD_INPUT) {
wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
wrapper.requestFullScreen();
}
return true;
};
var enablePointerLock = function() {
var wrapper = getCanvas();
wrapper.requestPointerLock = wrapper.requestPointerLock || wrapper.mozRequestPointerLock || wrapper.webkitRequestPointerLock;
if (wrapper.requestPointerLock) {
wrapper.requestPointerLock();
}
};
/** @ignore **/
exports._hasFocus = function() {
return document.activeElement == getCanvas();
};
/**
* Set the width and height of the Display. Conviniently this will
* return the actual display Surface - the same as calling [gamejs.display.getSurface()](#getSurface)
* later on.
* @param {Array} dimensions [width, height] of the display surface
* @param {Number} flags gamejs.display.DISABLE_SMOOTHING | gamejs.display.FULLSCREEN | gamejs.display.POINTERLOCK
*/
exports.setMode = function(dimensions, flags) {
SURFACE = null;
var canvas = getCanvas();
canvas.width = canvas.clientWidth = dimensions[0];
canvas.height = canvas.clientHeight = dimensions[1];
_flags = _flags || flags;
// @ xbrowser firefox allows pointerlock only if fullscreen
if (_flags & POINTERLOCK) {
_flags = _flags | FULLSCREEN;
}
if (_flags & FULLSCREEN) {
// attach fullscreen toggle checkbox
var fullScreenToggle = getFullScreenToggle();
fullScreenToggle.removeEventListener('click', enableFullScreen, false);
fullScreenToggle.addEventListener('click', enableFullScreen, false);
// @@ xbrowser
document.removeEventListener('fullScreenchange',fullScreenChange, false);
document.removeEventListener('webkitfullscreenchange',fullScreenChange, false);
document.removeEventListener('mozfullscreenchange',fullScreenChange, false);
document.addEventListener('fullscreenchange', fullScreenChange, false);
document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
document.addEventListener('mozfullscreenchange', fullScreenChange, false);
}
return getSurface(dimensions);
};
/**
* Set the Caption of the Display (document.title)
* @param {String} title the title of the app
* @param {gamejs.Image} icon FIXME implement favicon support
*/
exports.setCaption = function(title, icon) {
document.title = title;
};
/** @ignore **/
exports._isSmoothingEnabled = function() {
return !(_flags & DISABLE_SMOOTHING);
};
/**
* The Display (the canvas element) is most likely not in the top left corner
* of the browser due to CSS styling. To calculate the mouseposition within the
* canvas we need this offset.
* @see gamejs/event
* @ignore
*
* @returns {Array} [x, y] offset of the canvas
*/
exports._getCanvasOffset = function() {
var boundRect = getCanvas().getBoundingClientRect();
return [boundRect.left, boundRect.top];
};
/**
* Drawing on the Surface returned by `getSurface()` will draw on the screen.
* @returns {gamejs.Surface} the display Surface
*/
var getSurface = exports.getSurface = function(dimensions) {
if (SURFACE === null) {
var canvas = getCanvas();
if (dimensions === undefined) {
dimensions = [canvas.clientWidth, canvas.clientHeight];
}
SURFACE = new Surface(dimensions);
SURFACE._canvas = canvas;
SURFACE._canvas.width = dimensions[0];
SURFACE._canvas.height = dimensions[1];
SURFACE._context = canvas.getContext('2d');
if (!(_flags & DISABLE_SMOOTHING)) {
SURFACE._smooth();
} else {
SURFACE._noSmooth();
}
}
return SURFACE;
};
},{"./event":28,"./graphics":30}],28:[function(require,module,exports){
var display = require('./display');
var Callback = require('./utils/callback').Callback;
/**
* @fileoverview
* Deal with mouse and keyboard events.
*
* You can either handle all events in one callback with `gamejs.event.onEvent()`:
*
* gamejs.onEvent(function(event) {
* if (event.type === gamejs.event.MOUSE_UP) {
* gamejs.logging.info(event.pos, event.button);
* } else if (event.type === gamejs.event.KEY_UP) {
* gamejs.logging.info(event.key);
* }
* });
*
* Or recieve more specific callbacks, e.g. only for `KEY\_UP` with `gamejs.event.onKeyUp()`:
*
* gamejs.onKeyUp(function(event) {
* gamejs.logging.info(event.key);
* });
*
* All events passed to your callback are instances of `gamejs.event.Event` and have a `type` property to help
* you distinguish between the different events. This `type` property is set to one of those constants:
*
* * gamejs.event.MOUSE\_UP
* * gamejs.event.MOUSE\_MOTION
* * gamejs.event.MOUSE\_DOWN
* * gamejs.event.KEY\_UP
* * gamejs.event.KEY\_DOWN
* * gamejs.event.DISPLAY\_FULLSCREEN\_ENABLED
* * gamejs.event.DISPLAY\_FULLSCREEN\_DISABLED
* * gamejs.event.QUIT
* * gamejs.event.MOUSE_WHEEL
* * gamejs.event.TOUCH\_DOWN
* * gamejs.event.TOUCH\_UP
* * gamejs.event.TOUCH\_MOTION
*
* ### Keyboard constants
*
* There are also a lot of keyboard constants for ASCII. Those are all prefixed with `K\_`, e.g. `gamejs.event.K\_a` would be the "a"
* key and `gamejs.event.K_SPACE` is the spacebar.
*
* ## Touch events
*
* Touch events do not have a single position but for all `TOUCH\_*` events you get an array of
* `touches`, which each have their own `pos` attribute and a unique `identifier` for tracking
* this touch across multiple `TOUCH\_MOTION` events.
*
* ## User defined events
*
* All user defined events can have the value of `gamejs.event.USEREVENT` or higher.
* Make sure your custom event ids follow this system.
*
* @example
* gamejs.onEvent(function(event) {
* if (event.type === gamejs.event.MOUSE_UP) {
* gamejs.logging.log(event.pos, event.button);
* } else if (event.type === gamejs.event.KEY_UP) {
* gamejs.logging.log(event.key);
* }
* });
*
*/
var _CALLBACKS = [];
/** @ignore **/
var _triggerCallbacks = exports._triggerCallbacks = function() {
var args = arguments;
_CALLBACKS.forEach(function(cb) {
if (cb.type === 'all' || args[0].type === cb.type) {
cb.callback.apply(cb.scope, args);
}
});
};
/*
exports.onQuit(callback)
exports.onVisiblityChange(callback)
*/
/**
* Pass a callback function to be called when Fullscreen is enabled or disabled.
* Inspect `event.type` to distinguis between entering and exiting fullscreen.
*
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onFullscreen = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.DISPLAY_FULLSCREEN_ENABLED
});
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.DISPLAY_FULLSCREEN_DISABLED
});
};
/**
* The function passsed to `onEvent` will be called whenever
* any event (mouse, keyboard, etc) was triggered.
*
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onEvent = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: 'all'
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onDisplayResize = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
};
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.DISPLAY_RESIZE
});
}
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onMouseMotion = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.MOUSE_MOTION
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onMouseUp = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.MOUSE_UP
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onMouseDown = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.MOUSE_DOWN
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onTouchMotion = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.TOUCH_MOTION
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onTouchUp = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.TOUCH_UP
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onTouchDown = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.TOUCH_DOWN
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onKeyDown = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.KEY_DOWN
});
};
/**
* @param {Function} callback to be called
* @param {Object} scope within which the callback should be called. It's `this` during invocation. (optional)
*/
exports.onKeyUp = function(callback, scope) {
if (typeof(callback) !== 'function') {
throw new Error('Callback must be a function');
}
_CALLBACKS.push({
callback: callback,
scope: scope,
type: exports.KEY_UP
});
};
// key constants
exports.K_UP = 38;
exports.K_DOWN = 40;
exports.K_RIGHT = 39;
exports.K_LEFT = 37;
exports.K_SPACE = 32;
exports.K_BACKSPACE = 8;
exports.K_TAB = 9;
exports.K_ENTER = 13;
exports.K_SHIFT = 16;
exports.K_CTRL = 17;
exports.K_ALT = 18;
exports.K_ESC = 27;
exports.K_0 = 48;
exports.K_1 = 49;
exports.K_2 = 50;
exports.K_3 = 51;
exports.K_4 = 52;
exports.K_5 = 53;
exports.K_6 = 54;
exports.K_7 = 55;
exports.K_8 = 56;
exports.K_9 = 57;
exports.K_a = 65;
exports.K_b = 66;
exports.K_c = 67;
exports.K_d = 68;
exports.K_e = 69;
exports.K_f = 70;
exports.K_g = 71;
exports.K_h = 72;
exports.K_i = 73;
exports.K_j = 74;
exports.K_k = 75;
exports.K_l = 76;
exports.K_m = 77;
exports.K_n = 78;
exports.K_o = 79;
exports.K_p = 80;
exports.K_q = 81;
exports.K_r = 82;
exports.K_s = 83;
exports.K_t = 84;
exports.K_u = 85;
exports.K_v = 86;
exports.K_w = 87;
exports.K_x = 88;
exports.K_y = 89;
exports.K_z = 90;
exports.K_KP1 = 97;
exports.K_KP2 = 98;
exports.K_KP3 = 99;
exports.K_KP4 = 100;
exports.K_KP5 = 101;
exports.K_KP6 = 102;
exports.K_KP7 = 103;
exports.K_KP8 = 104;
exports.K_KP9 = 105;
// event type constants
exports.NOEVENT = 0;
exports.NUMEVENTS = 32000;
exports.DISPLAY_FULLSCREEN_ENABLED = 300;
exports.DISPLAY_FULLSCREEN_DISABLED = 301;
exports.DISPLAY_RESIZE = 302;
exports.QUIT = 0;
exports.KEY_DOWN = 1;
exports.KEY_UP = 2;
exports.MOUSE_MOTION = 3;
exports.MOUSE_UP = 4;
exports.MOUSE_DOWN = 5;
exports.MOUSE_WHEEL = 6;
exports.TOUCH_UP = 7;
exports.TOUCH_DOWN = 8;
exports.TOUCH_MOTION = 9;
exports.USEREVENT = 2000;
/**
* Properties of the `event` object argument passed to the callbacks.
* @class
*/
exports.Event = function() {
/**
* The type of the event. e.g., gamejs.event.QUIT, KEYDOWN, MOUSEUP.
*/
this.type = null;
/**
* key the keyCode of the key. compare with gamejs.event.K_a, gamejs.event.K_b,...
*/
this.key = null;
/**
* relative movement for a mousemove event
*/
this.rel = null;
/**
* the number of the mousebutton pressed
*/
this.button = null;
/**
* pos the position of the event for mouse events
*/
this.pos = null;
};
/**
* @ignore
*/
exports.init = function() {
var lastPos = [];
// anonymous functions as event handlers = memory leak, see MDC:elementAddEventListener
function onMouseDown (ev) {
var canvasOffset = display._getCanvasOffset();
_triggerCallbacks({
'type': exports.MOUSE_DOWN,
'pos': [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]],
'button': ev.button,
'shiftKey': ev.shiftKey,
'ctrlKey': ev.ctrlKey,
'metaKey': ev.metaKey
});
}
function onMouseUp (ev) {
var canvasOffset = display._getCanvasOffset();
_triggerCallbacks({
'type':exports.MOUSE_UP,
'pos': [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]],
'button': ev.button,
'shiftKey': ev.shiftKey,
'ctrlKey': ev.ctrlKey,
'metaKey': ev.metaKey
});
}
function onKeyDown (ev) {
var key = ev.keyCode || ev.which;
_triggerCallbacks({
'type': exports.KEY_DOWN,
'key': key,
'shiftKey': ev.shiftKey,
'ctrlKey': ev.ctrlKey,
'metaKey': ev.metaKey
});
// if the display has focus, we surpress default action
// for most keys
if (display._hasFocus() && (!ev.ctrlKey && !ev.metaKey &&
((key >= exports.K_LEFT && key <= exports.K_DOWN) ||
(key >= exports.K_0 && key <= exports.K_z) ||
(key >= exports.K_KP1 && key <= exports.K_KP9) ||
key === exports.K_SPACE ||
key === exports.K_TAB ||
key === exports.K_ENTER)) ||
key === exports.K_ALT ||
key === exports.K_BACKSPACE) {
ev.preventDefault();
}
}
function onKeyUp (ev) {
_triggerCallbacks({
'type': exports.KEY_UP,
'key': ev.keyCode,
'shiftKey': ev.shiftKey,
'ctrlKey': ev.ctrlKey,
'metaKey': ev.metaKey
});
}
function onMouseMove (ev) {
var canvasOffset = display._getCanvasOffset();
var currentPos = [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]];
var relativePos = [];
if (lastPos.length) {
relativePos = [
lastPos[0] - currentPos[0],
lastPos[1] - currentPos[1]
];
}
_triggerCallbacks({
'type': exports.MOUSE_MOTION,
'pos': currentPos,
'rel': relativePos,
'buttons': null, // FIXME, fixable?
'timestamp': ev.timeStamp,
'movement': [ev.movementX ||
ev.mozMovementX ||
ev.webkitMovementX || 0,
ev.movementY ||
ev.mozMovementY ||
ev.webkitMovementY || 0
]
});
lastPos = currentPos;
return;
}
function onMouseScroll(ev) {
var canvasOffset = display._getCanvasOffset();
var currentPos = [ev.clientX - canvasOffset[0], ev.clientY - canvasOffset[1]];
_triggerCallbacks({
type: exports.MOUSE_WHEEL,
pos: currentPos,
delta: ev.detail || (- ev.wheelDeltaY / 40)
});
return;
}
function onBeforeUnload (ev) {
_triggerCallbacks({
'type': exports.QUIT
});
return;
};
// convert a w3c touch event into gamejs event
function w3cTouchConvert(touchList) {
var canvasOffset = display._getCanvasOffset();
var tList = [];
for (var i = 0; i < touchList.length; i++) {
var touchEvent = touchList.item(i);
tList.push({
identifier: touchEvent.identifier,
pos: [touchEvent.clientX - canvasOffset[0], touchEvent.clientY - canvasOffset[1]]
});
}
return tList;
}
function onTouchDown(ev) {
var canvasOffset = display._getCanvasOffset();
var changedTouches = w3cTouchConvert(ev.changedTouches);
_triggerCallbacks({
'type': exports.TOUCH_DOWN,
'touches': changedTouches
});
};
function onTouchUp(ev) {
var changedTouches = w3cTouchConvert(ev.changedTouches);
_triggerCallbacks({
'type': exports.TOUCH_UP,
'touches': changedTouches,
});
}
function onTouchMotion(ev) {
var changedTouches = w3cTouchConvert(ev.changedTouches);
_triggerCallbacks({
'type': exports.TOUCH_MOTION,
'touches': changedTouches
});
ev.preventDefault();
}
// IE does not support addEventListener on document itself
// FX events don't reach body if mouse outside window or on menubar
var canvas = display._getCanvas();
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mouseup', onMouseUp, false);
document.addEventListener('keydown', onKeyDown, false);
document.addEventListener('keyup', onKeyUp, false);
document.addEventListener('mousemove', onMouseMove, false);
canvas.addEventListener('mousewheel', onMouseScroll, false);
// MOZFIX
// https://developer.mozilla.org/en/Code_snippets/Miscellaneous#Detecting_mouse_wheel_events
canvas.addEventListener('DOMMouseScroll', onMouseScroll, false);
canvas.addEventListener('beforeunload', onBeforeUnload, false);
// touchs
canvas.addEventListener("touchstart", onTouchDown, false);
canvas.addEventListener("touchend", onTouchUp, false);
canvas.addEventListener("touchcancel", onTouchUp, false);
canvas.addEventListener("touchleave", onTouchUp, false);
canvas.addEventListener("touchmove", onTouchMotion, false);
};
},{"./display":27,"./utils/callback":47}],29:[function(require,module,exports){
var Surface = require('./graphics').Surface;
var objects = require('./utils/objects');
/**
* @fileoverview Methods for creating Font objects which can render text
* to a Surface.
*
* @example
* var font = new Font('20px monospace');
* // render text - this returns a surface with the text written on it.
* var helloSurface = font.render('Hello World')
*/
/**
* Create a Font to draw on the screen. The Font allows you to
* `render()` text. Rendering text returns a Surface which
* in turn can be put on screen.
*
* @constructor
* @property {Number} fontHeight the line height of this Font
*
* @param {String} fontSettings a css font definition, e.g., "20px monospace"
* @param {STring} backgroundColor valid #rgb string, "#ff00cc"
*/
var Font = exports.Font = function(fontSettings, backgroundColor) {
/**
* @ignore
*/
this.sampleSurface = new Surface([10,10]);
this.sampleSurface.context.font = fontSettings;
this.sampleSurface.context.textAlign = 'start';
// http://diveintohtml5.org/canvas.html#text
this.sampleSurface.context.textBaseline = 'bottom';
this.backgroundColor = backgroundColor || false;
return this;
};
/**
* Returns a Surface with the given text on it.
* @param {String} text the text to render
* @param {String} color a valid #RGB String, "#ffcc00"
* @returns {gamejs.Surface} Surface with the rendered text on it.
*/
Font.prototype.render = function(text, color) {
var dims = this.size(text);
var surface = new Surface(dims);
var ctx = surface.context;
ctx.save();
if ( this.backgroundColor ) {
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, surface.rect.width, surface.rect.height);
}
ctx.font = this.sampleSurface.context.font;
ctx.textBaseline = this.sampleSurface.context.textBaseline;
ctx.textAlign = this.sampleSurface.context.textAlign;
ctx.fillStyle = ctx.strokeStyle = color || "#000000";
ctx.fillText(text, 0, surface.rect.height, surface.rect.width);
ctx.restore();
return surface;
};
/**
* Determine the width and height of the given text if rendered
* with this Font.
* @param {String} text the text to measure
* @returns {Array} the [width, height] of the text if rendered with this Font
*/
Font.prototype.size = function(text) {
var metrics = this.sampleSurface.context.measureText(text);
// FIXME measuretext is buggy, make extra wide
return [metrics.width, this.fontHeight];
};
/**
* Height of the font in pixels.
*/
objects.accessors(Font.prototype, {
'fontHeight': {
get: function() {
// Returns an approximate line height of the text
// »This version of the specification does not provide a way to obtain
// the bounding box dimensions of the text.«
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-measuretext
return this.sampleSurface.context.measureText('M').width * 1.5;
}
}
});
},{"./graphics":30,"./utils/objects":48}],30:[function(require,module,exports){
/**
* @fileoverview
* This module holds the important `Surface` class which is the general container for image data.
*
* var surface = new gamejs.graphics.Surface([width, height]);
*
* The functions
* to draw geometric lines like circles, lines, rectangles, etc. are also all in this module:
*
* gamejs.graphics.line(surface, '#ff0000', centerPoint, radius);
*
* Each Surface instance has methods to create *a new* rotated, flipped, scaled, etc. instance of itself:
*
* // the original `surface` remains untouched by the
* // filp operation. A new Surface instance
* // is returned by `flip()`.
* var horizontalFlippedSurface = surface.flip(true);
*
* If you want to put images (png, jpg) on the screen, also see the `gamejs.image` module and `gamejs.preload()`.
*
* There are several ways to specify colors. Whenever the docs says "valid #RGB string"
* you can pass in any of the following formats: `"#ff00ff"`, `"rgb(255, 0, 255)"` or `"rgba(255, 0, 255, 1)"`.
*
* @see gamejs/image
*/
var gamejs = require('../gamejs');
var Rect = gamejs.Rect;
var objects = require('./utils/objects');
/**
* transform functions
*/
var matrix = require('./math/matrix');
var vectors = require('./math/vectors');
/**
* A Surface represents a bitmap image with a fixed width and height. The
* most important feature of a Surface is that they can be `blitted`
* onto each other.
*
* @example
* new gamejs.graphics.Surface([width, height]);
* new gamejs.graphics.Surface(width, height);
* new gamejs.graphics.Surface(rect);
* @constructor
*
* @param {Array} dimensions Array holding width and height
*/
var Surface = exports.Surface = function() {
var args = gamejs.normalizeRectArguments.apply(this, arguments);
var width = args.left;
var height = args.top;
// unless argument is rect:
if (arguments.length == 1 && arguments[0] instanceof Rect) {
width = args.width;
height = args.height;
}
// only for rotatation & scale
/** @ignore */
this._matrix = matrix.identity();
/** @ignore */
this._canvas = document.createElement("canvas");
this._canvas.width = width;
this._canvas.height = height;
/** @ignore */
this._blitAlpha = 1.0;
/** @ignore */
this._context = this._canvas.getContext('2d');
// using exports is weird but avoids circular require
if (gamejs.display._isSmoothingEnabled()) {
this._smooth();
} else {
this._noSmooth();
}
return this;
};
/** @ignore */
Surface.prototype._noSmooth = function() {
// disable image scaling
// see https://developer.mozilla.org/en/Canvas_tutorial/Using_images#Controlling_image_scaling_behavior
// and https://github.com/jbuck/processing-js/commit/65de16a8340c694cee471a2db7634733370b941c
this.context.mozImageSmoothingEnabled = false;
this.context.webkitImageSmoothingEnabled = false;
return;
};
/** @ignore */
Surface.prototype._smooth = function() {
this.context.mozImageSmoothingEnabled = true;
this.context.webkitImageSmoothingEnabled = true;
};
/**
* Blits another Surface on this Surface. The destination where to blit to
* can be given (or it defaults to the top left corner) as well as the
* Area from the Surface which should be blitted (e.g., for cutting out parts of
* a Surface).
*
* @example
* // blit flower in top left corner of display
* displaySurface.blit(flowerSurface);
*
* // position flower at 10/10 of display
* displaySurface.blit(flowerSurface, [10, 10])
*
* // ... `dest` can also be a rect whose topleft position is taken:
* displaySurface.blit(flowerSurface, new gamejs.Rect([10, 10]);
*
* // only blit half of the flower onto the display
* var flowerRect = flowerSurface.rect;
* flowerRect = new gamejs.Rect([0,0], [flowerRect.width/2, flowerRect.height/2])
* displaySurface.blit(flowerSurface, [0,0], flowerRect);
*
* @param {gamejs.graphics.Surface} src The Surface which will be blitted onto this one
* @param {gamejs.Rect|Array} dst the Destination x, y position in this Surface.
* If a Rect is given, it's top and left values are taken. If this argument
* is not supplied the blit happens at [0,0].
* @param {gamesjs.Rect|Array} area the Area from the passed Surface which
* should be blitted onto this Surface.
* @param {Number} compositionOperation how the source and target surfaces are composited together; one of: source-atop, source-in, source-out, source-over (default), destination-atop, destination-in, destination-out, destination-over, lighter, copy, xor; for an explanation of these values see: http://dev.w3.org/html5/2dcontext/#dom-context-2d-globalcompositeoperation
* @returns {gamejs.Rect} Rect actually repainted FIXME actually return something?
*/
Surface.prototype.blit = function(src, dest, area, compositeOperation) {
var rDest, rArea;
if (dest instanceof Rect) {
rDest = dest.clone();
var srcSize = src.getSize();
if (!rDest.width) {
rDest.width = srcSize[0];
}
if (!rDest.height) {
rDest.height = srcSize[1];
}
} else if (dest && dest instanceof Array && dest.length == 2) {
rDest = new Rect(dest, src.getSize());
} else {
rDest = new Rect([0,0], src.getSize());
}
compositeOperation = compositeOperation || 'source-over';
// area within src to be drawn
if (area instanceof Rect) {
rArea = area;
} else if (area && area instanceof Array && area.length == 2) {
var size = src.getSize();
rArea = new Rect(area, [size[0] - area[0], size[1] - area[1]]);
} else {
rArea = new Rect([0,0], src.getSize());
}
if (isNaN(rDest.left) || isNaN(rDest.top) || isNaN(rDest.width) || isNaN(rDest.height)) {
throw new Error('[blit] bad parameters, destination is ' + rDest);
}
this.context.save();
this.context.globalCompositeOperation = compositeOperation;
this.context.globalAlpha = src._blitAlpha;
this.context.drawImage(src.canvas, rArea.left, rArea.top, rArea.width, rArea.height, rDest.left, rDest.top, rDest.width, rDest.height);
this.context.restore();
return;
};
/**
* @returns {Number[]} the width and height of the Surface
*/
Surface.prototype.getSize = function() {
return [this.canvas.width, this.canvas.height];
};
/**
* Obsolte, only here for compatibility.
* @deprecated
* @ignore
* @returns {gamejs.Rect} a Rect of the size of this Surface
*/
Surface.prototype.getRect = function() {
return new Rect([0,0], this.getSize());
};
/**
* Fills the whole Surface with a color. Usefull for erasing a Surface.
* @param {String} CSS color string, e.g. '#0d120a' or '#0f0' or 'rgba(255, 0, 0, 0.5)'
* @param {gamejs.Rect} a Rect of the area to fill (defauts to entire surface if not specified)
*/
Surface.prototype.fill = function(color, rect) {
this.context.save();
this.context.fillStyle = color || "#000000";
if (rect === undefined) {
rect = new Rect(0, 0, this.canvas.width, this.canvas.height);
}
this.context.fillRect(rect.left, rect.top, rect.width, rect.height);
this.context.restore();
return;
};
/**
* Clear the surface.
*/
Surface.prototype.clear = function(rect) {
var size = this.getSize();
rect = rect || new Rect(0, 0, size[0], size[1]);
this.context.clearRect(rect.left, rect.top, rect.width, rect.height);
return;
};
objects.accessors(Surface.prototype, {
/**
* @type gamejs.Rect
*/
'rect': {
get: function() {
return this.getRect();
}
},
/**
* @ignore
*/
'context': {
get: function() {
return this._context;
}
},
'canvas': {
get: function() {
return this._canvas;
}
}
});
/**
* @returns {gamejs.graphics.Surface} a clone of this surface
*/
Surface.prototype.clone = function() {
var newSurface = new Surface(this.getRect());
newSurface.blit(this);
return newSurface;
};
/**
* @returns {Number} current alpha value
*/
Surface.prototype.getAlpha = function() {
return (1 - this._blitAlpha);
};
/**
* Set the alpha value for the whole Surface. When blitting the Surface on
* a destination, the pixels will be drawn slightly transparent.
* @param {Number} alpha value in range 0.0 - 1.0
* @returns {Number} current alpha value
*/
Surface.prototype.setAlpha = function(alpha) {
if (isNaN(alpha) || alpha < 0 || alpha > 1) {
return;
}
this._blitAlpha = (1 - alpha);
return (1 - this._blitAlpha);
};
/**
* The data must be represented in left-to-right order, row by row top to bottom,
* starting with the top left, with each pixel's red, green, blue, and alpha components
* being given in that order for each pixel.
* @see http://dev.w3.org/html5/2dcontext/#canvaspixelarray
* @returns {ImageData} an object holding the pixel image data {data, width, height}
*/
Surface.prototype.getImageData = function() {
var size = this.getSize();
return this.context.getImageData(0, 0, size[0], size[1]);
};
// FIXME all draw functions must return a minimal rect containing the drawn shape
/**
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color valid #RGB string, e.g., "#ff0000"
* @param {Array} startPos [x, y] position of line start
* @param {Array} endPos [x, y] position of line end
* @param {Number} width of the line, defaults to 1
*/
exports.line = function(surface, color, startPos, endPos, width) {
var ctx = surface.context;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = width || 1;
ctx.moveTo(startPos[0], startPos[1]);
ctx.lineTo(endPos[0], endPos[1]);
ctx.stroke();
ctx.restore();
return;
};
/**
* Draw connected lines. Use this instead of indiviudal line() calls for
* better performance
*
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color a valid #RGB string, "#ff0000"
* @param {Boolean} closed if true the last and first point are connected
* @param {Array} pointlist holding array [x,y] arrays of points
* @param {Number} width width of the lines, defaults to 1
*/
exports.lines = function(surface, color, closed, pointlist, width) {
closed = closed || false;
var ctx = surface.context;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color;
ctx.lineWidth = width || 1;
pointlist.forEach(function(point, idx) {
if (idx === 0) {
ctx.moveTo(point[0], point[1]);
} else {
ctx.lineTo(point[0], point[1]);
}
});
if (closed) {
ctx.lineTo(pointlist[0][0], pointlist[0][1]);
}
ctx.stroke();
ctx.restore();
return;
};
/**
* Draw a circle on Surface
*
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color a valid #RGB String, #ff00cc
* @param {Array} pos [x, y] position of the circle center
* @param {Number} radius of the circle
* @param {Number} width width of the circle, if not given or 0 the circle is filled
*/
exports.circle = function(surface, color, pos, radius, width) {
if (isNaN(radius)) {
throw new Error('[circle] radius required argument');
}
if (!pos || !(pos instanceof Array)) {
throw new Error('[circle] pos must be given & array' + pos);
}
var ctx = surface.context;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color;
ctx.lineWidth = width || 1;
ctx.arc(pos[0], pos[1], radius, 0, 2*Math.PI, true);
if (width === undefined || width === 0) {
ctx.fill();
} else {
ctx.stroke();
}
ctx.restore();
return;
};
/**
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color a valid #RGB String, #ff00cc
* @param {gamejs.Rect} rect the position and dimension attributes of this Rect will be used
* @param {Number} width the width of line drawing the Rect, if 0 or not given the Rect is filled.
*/
exports.rect = function(surface, color, rect, width) {
var ctx =surface.context;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color;
if (isNaN(width) || width === 0) {
ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
} else {
ctx.lineWidth = width || 1;
ctx.strokeRect(rect.left, rect.top, rect.width, rect.height);
}
ctx.restore();
};
/**
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color a valid #RGB String, #ff00cc
* @param {Array} pos [x, y] position of the circle center
* @param {Number} startAngle, both angles in radians
* @param {Number} stopAngle
* @param {Number} radius
* @param {Number} width the width of line, if 0 or not given the arc is filled.
*/
exports.arc= function(surface, color, pos, startAngle, stopAngle, radius, width) {
var ctx = surface.context;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = ctx.fillStyle = color;
ctx.arc(pos[0], pos[1],
radius,
startAngle, stopAngle,
false
);
if (isNaN(width) || width === 0) {
ctx.fill();
} else {
ctx.lineWidth = width || 1;
ctx.stroke();
}
ctx.restore();
};
/**
* Draw a polygon on the surface. The pointlist argument are the vertices
* for the polygon.
*
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color a valid #RGB String, #ff00cc
* @param {Array} pointlist array of vertices [x, y] of the polygon
* @param {Number} width the width of line, if 0 or not given the polygon is filled.
*/
exports.polygon = function(surface, color, pointlist, width) {
var ctx = surface.context;
ctx.save();
ctx.fillStyle = ctx.strokeStyle = color;
ctx.beginPath();
pointlist.forEach(function(point, idx) {
if (idx === 0) {
ctx.moveTo(point[0], point[1]);
} else {
ctx.lineTo(point[0], point[1]);
}
});
ctx.closePath();
if (isNaN(width) || width === 0) {
ctx.fill();
} else {
ctx.lineWidth = width || 1;
ctx.stroke();
}
ctx.restore();
};
/**
* Draw a quadratic curve with one control point on the surface.
* The control point position defines the shape of the quadratic curve.
*
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color valid #RGB string, e.g., "#ff0000"
* @param {Array} startPos [x, y] the start position for the quadratic curve
* @param {Array} endPos [x, y] the end position for the quadratic curve
* @param {Array} controlPos [x, y] position of the control point
* @param {Number} width of the quadratic curve, defaults to 1
*/
exports.quadraticCurve = function(surface, color, startPos, endPos, controlPos, width) {
if (!startPos || !(startPos instanceof Array)) {
throw new Error('[quadratic_curve] startPos must be defined!');
}
if (!endPos || !(endPos instanceof Array)) {
throw new Error('[quadratic_curve] endPos must be defined!');
}
if (!controlPos || !(controlPos instanceof Array)) {
throw new Error('[quadratic_curve] controlPos must be defined!');
}
var ctx = surface.context;
ctx.save();
ctx.fillStyle = ctx.strokeStyle = color;
ctx.lineWidth = width || 1;
ctx.beginPath();
ctx.moveTo(startPos[0], startPos[1]);
ctx.quadraticCurveTo(controlPos[0], controlPos[1], endPos[0], endPos[1]);
ctx.stroke();
ctx.restore();
};
/**
* Draw a bezier curve with two control points on the surface.
* The control point positions define the shape of the bezier curve.
*
* @param {gamejs.graphics.Surface} surface the Surface to draw on
* @param {String} color valid #RGB string, e.g., "#ff0000"
* @param {Array} startPos [x, y] the start position for the bezier curve
* @param {Array} endPos [x, y] the end position for the bezier curve
* @param {Array} ct1Pos [x, y] position of the first control point
* @param {Array} ct2Pos [x, y] position of the second control point
* @param {Number} width of the bezier curve, defaults to 1
*/
exports.bezierCurve = function(surface, color, startPos, endPos, ct1Pos, ct2Pos, width) {
if (!startPos || !(startPos instanceof Array)) {
throw new Error('[bezier_curve] startPos must be defined!');
}
if (!endPos || !(endPos instanceof Array)) {
throw new Error('[bezier_curve] endPos must be defined!');
}
if (!ct1Pos || !(ct1Pos instanceof Array)) {
throw new Error('[bezier_curve] ct1Pos must be defined!');
}
if (!ct2Pos || !(ct2Pos instanceof Array)) {
throw new Error('[bezier_curve] ct2Pos must be defined!');
}
var ctx = surface.context;
ctx.save();
ctx.fillStyle = ctx.strokeStyle = color;
ctx.lineWidth = width || 1;
ctx.beginPath();
ctx.moveTo(startPos[0], startPos[1]);
ctx.bezierCurveTo(ct1Pos[0], ct1Pos[1], ct2Pos[0], ct2Pos[1], endPos[0], endPos[1]);
ctx.stroke();
ctx.restore();
};
/**
* Returns a new surface which holds this surface rotate by angle degrees.
* Unless rotating by 90 degree increments, the image will be padded larger to hold the new size.
* @param {angel} angle Clockwise angle by which to rotate
* @returns {Surface} new, rotated surface
*/
Surface.prototype.rotate = function (angle) {
var origSize = this.getSize();
var radians = (angle * Math.PI / 180);
var newSize = origSize;
// find new bounding box
if (angle % 360 !== 0) {
var rect = this.getRect();
var points = [
[-rect.width/2, rect.height/2],
[rect.width/2, rect.height/2],
[-rect.width/2, -rect.height/2],
[rect.width/2, -rect.height/2]
];
var rotPoints = points.map(function(p) {
return vectors.rotate(p, radians);
});
var xs = rotPoints.map(function(p) { return p[0]; });
var ys = rotPoints.map(function(p) { return p[1]; });
var left = Math.min.apply(Math, xs);
var right = Math.max.apply(Math, xs);
var bottom = Math.min.apply(Math, ys);
var top = Math.max.apply(Math, ys);
newSize = [right-left, top-bottom];
}
var newSurface = new Surface(newSize);
var m = matrix.translate(this._matrix, origSize[0]/2, origSize[1]/2);
m = matrix.rotate(m, radians);
m = matrix.translate(m, -origSize[0]/2, -origSize[1]/2);
var offset = [(newSize[0] - origSize[0]) / 2, (newSize[1] - origSize[1]) / 2];
newSurface.context.save();
newSurface.context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
newSurface.blit(this, offset);
newSurface.context.restore();
return newSurface;
};
/**
* Returns a new surface holding the scaled surface.
* @param {Array} dimensions new [width, height] of surface after scaling
* @returns {Surface} new, scaled surface
*/
Surface.prototype.scale = function(dims) {
var width = dims[0];
var height = dims[1];
if (width <= 0 || height <= 0) {
throw new Error('[gamejs.transform.scale] Invalid arguments for height and width', [width, height]);
}
var oldDims = this.getSize();
var ws = width / oldDims[0];
var hs = height / oldDims[1];
var newSurface = new Surface([width, height]);
var originalMatrix = this._matrix.slice(0);
var m = matrix.scale(matrix.identity(), [ws, hs]);
newSurface.context.save();
newSurface.context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
newSurface.blit(this);
newSurface.context.restore();
return newSurface;
};
/**
* Flip a Surface either vertically, horizontally or both. This returns
* a new Surface (i.e: nondestructive).
* @param {Boolean} flipHorizontal
* @param {Boolean} flipVertical
* @returns {Surface} new, flipped surface
*/
Surface.prototype.flip = function(flipHorizontal, flipVertical) {
var dims = this.getSize();
var newSurface = new Surface(dims);
var scaleX = 1;
var scaleY = 1;
var xPos = 0;
var yPos = 0;
if (flipHorizontal === true) {
scaleX = -1;
xPos = -dims[0];
}
if (flipVertical === true) {
scaleY = -1;
yPos = -dims[1];
}
newSurface.context.save();
newSurface.context.scale(scaleX, scaleY);
newSurface.context.drawImage(this.canvas, xPos, yPos);
newSurface.context.restore();
return newSurface;
};
/**
* Directly copy values from an array into a Surface.
*
* This is faster than blitting the `surface` property on a SurfaceArray
*
* The array must be the same dimensions as the Surface and will completely
* replace all pixel values.
* @param {gamejs.graphics.Surface} surface
* @param {gamejs.graphics.Surfacearray.SurfaceArray} surfaceArray
*/
exports.blitArray = function(surface, surfaceArray) {
surface.context.putImageData(surfaceArray.imageData, 0, 0);
return;
};
/**
* Fast pixel access. The SurfaceArray can be constructed with a surface whose values
* are then used to initialize the pixel array.
*
* The surface passed as argument is not modified by the SurfaceArray.
*
* If an array is used to construct SurfaceArray, the array must describe
* the dimensions of the SurfaceArray [width, height].
*
* @example
*
* // create array from display surface
* var srfArray = new SurfaceArray(display);
* // direct pixel access
* srfArray.set(50, 100, [255, 0, 0, 100]);
* console.log(srfArray.get(30, 50));
* // blit modified array back to display surface
* blitArray(display, srfArray);
*
* @param {gamejs.graphics.Surface|Array} surfaceOrDimensions
* @see http://dev.w3.org/html5/2dcontext/#pixel-manipulation
*/
var SurfaceArray = exports.SurfaceArray = function(surfaceOrDimensions) {
var size = null;
var data = null;
var imageData = null;
/**
* Set rgba value at position x, y.
*
* For performance reasons this function has only one signature
* being Number, Number, Array[4].
*
* @param {Number} x x position of pixel
* @param {Number} y y position of pixel
* @param {Array} rgba [red, green, blue, alpha] values [255, 255, 255, 255] (alpha, the last argument defaults to 255)
* @throws Error if x, y out of range
*/
this.set = function(x, y, rgba) {
var offset = (x * 4) + (y * size[0] * 4);
/** faster without
if (offset + 3 >= data.length || x < 0 || y < 0) {
throw new Error('x, y out of range', x, y);
}
**/
data[offset] = rgba[0];
data[offset+1] = rgba[1];
data[offset+2] = rgba[2];
data[offset+3] = rgba[3] === undefined ? 255 : rgba[3];
return;
};
/**
* Get rgba value at position xy,
* @param {Number} x
* @param {Number} y
* @returns {Array} [red, green, blue, alpha]
*/
this.get = function(x, y) {
var offset = (x * 4) + (y * size[0] * 4);
return [
data[offset],
data[offset+1],
data[offset+2],
data[offset+3]
];
};
/**
* a new gamejs.graphics.Surface on every access, representing
* the current state of the SurfaceArray.
* @type {gamejs.graphics.Surface}
*/
// for jsdoc only
this.surface = null;
objects.accessors(this, {
surface: {
get: function() {
var s = new gamejs.graphics.Surface(size);
s.context.putImageData(imageData, 0, 0);
return s;
}
},
imageData: {
get: function() {
return imageData;
}
}
});
this.getSize = function() {
return size;
};
/**
* constructor
*/
if (surfaceOrDimensions instanceof Array) {
size = surfaceOrDimensions;
imageData = gamejs.display.getSurface().context.createImageData(size[0], size[1]);
data = imageData.data;
} else {
size = surfaceOrDimensions.getSize();
imageData = surfaceOrDimensions.getImageData(0, 0, size[0], size[1]);
data = imageData.data;
}
return this;
};
},{"../gamejs":25,"./math/matrix":36,"./math/vectors":39,"./utils/objects":48}],31:[function(require,module,exports){
/**
* @fileoverview Make synchronous http requests to your game's serverside component.
*
* If you configure a ajax base URL you can make http requests to your
* server using those functions.
* The most high-level functions are `load()` and `save()` which take
* and return a JavaScript object, which they will send to / recieve from
* the server-side in JSON format.
*
*
*/
/**
* Response object returned by http functions `get` and `post`. This
* class is not instantiable.
*
* @param{String} responseText
* @param {String} responseXML
* @param {Number} status
* @param {String} statusText
*/
exports.Response = function() {
/**
* @param {String} header
*/
this.getResponseHeader = function(header) {
};
throw new Error('response class not instantiable');
};
/**
* Make http request to server-side
* @param {String} method http method
* @param {String} url
* @param {String|Object} data
* @param {String|Object} type "Accept" header value
* @return {Response} response
*/
var ajax = exports.ajax = function(method, url, data, type) {
data = data || null;
var response = new XMLHttpRequest();
response.open(method, url, false);
if (type) {
response.setRequestHeader("Accept", type);
}
if (data instanceof Object) {
data = JSON.stringify(data);
response.setRequestHeader('Content-Type', 'application/json');
}
response.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
response.send(data);
return response;
};
/**
* Make http GET request to server-side
* @param {String} url
*/
var get = exports.get = function(url) {
return ajax('GET', url);
};
/**
* Make http POST request to server-side
* @param {String} url
* @param {String|Object} data
* @param {String|Object} type "Accept" header value
* @returns {Response}
*/
var post = exports.post = function(url, data, type) {
return ajax('POST', url, data, type);
};
function stringify(response) {
/* jshint ignore:start */
return eval('(' + response.responseText + ')');
/* jshint ignore:end */
}
function ajaxBaseHref() {
return (window.$g && window.$g.ajaxBaseHref) || './';
}
/**
* Load an object from the server-side.
* @param {String} url
* @return {Object} the object loaded from the server
*/
exports.load = function(url) {
return stringify(get(ajaxBaseHref() + url));
};
/**
* Send an object to a server-side function.
* @param {String} url
* @param {String|Object} data
* @param {String|Object} type "Accept" header value
* @returns {Object} the response object
*/
exports.save = function(url, data, type) {
return stringify(post(ajaxBaseHref() + url, {payload: data}, type));
};
},{}],32:[function(require,module,exports){
var gamejs = require('../gamejs');
/**
* @fileoverview Load images as Surfaces.
*
* Sounds & Images are loaded relative to your game's html page
* (the html which includes the GameJs code) or relative to the
* property `window.$g.resourceBaseHref`
* if it is set.
*
*
*/
var CACHE = {};
/**
* need to export preloading status for require
* @ignore
*/
var _PRELOADING = false;
/**
* Load image and return it on a Surface.
*
* All images must be preloaded before they can be used.
* @example
* gamejs.preload(["./images/ship.png", "./images/sunflower.png"]);
* // ...later...
* display.blit(gamejs.image.load('images/ship.png'))
*
* @param {String|dom.Image} uriOrImage resource uri for image
* @returns {gamejs.graphics.Surface} surface with the image on it.
*/
exports.load = function(key) {
var img;
if (typeof key === 'string') {
img = CACHE[key];
if (!img) {
throw new Error('Missing "' + key + '", gamejs.preload() all images before trying to load them.');
}
} else {
img = key;
}
var canvas = document.createElement('canvas');
// IEFIX missing html5 feature naturalWidth/Height
canvas.width = img.naturalWidth || img.width;
canvas.height = img.naturalHeight || img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
img.getSize = function() { return [img.naturalWidth, img.naturalHeight]; };
var surface = new gamejs.graphics.Surface(img.getSize());
// NOTE hack setting protected _canvas directly
surface._canvas = canvas;
surface._context = context;
return surface;
};
/**
* add all images on the currrent page into cache
* @ignore
*/
exports.init = function() {
return;
};
/**
* preload the given img URIs
* @returns {Function} which returns 0-1 for preload progress
* @ignore
*/
exports.preload = function(imgIdents) {
var countLoaded = 0;
var countTotal = 0;
function incrementLoaded() {
countLoaded++;
if (countLoaded == countTotal) {
_PRELOADING = false;
}
if (countLoaded % 10 === 0) {
gamejs.logging.debug('gamejs.image: preloaded ' + countLoaded + ' of ' + countTotal);
}
}
function getProgress() {
return countTotal > 0 ? countLoaded / countTotal : 1;
}
function successHandler() {
addToCache(this);
incrementLoaded();
}
function errorHandler() {
incrementLoaded();
throw new Error('Error loading ' + this.src);
}
var key;
for (key in imgIdents) {
var lowerKey = key.toLowerCase();
if (lowerKey.indexOf('.png') == -1 &&
lowerKey.indexOf('.jpg') == -1 &&
lowerKey.indexOf('.jpeg') == -1 &&
lowerKey.indexOf('.svg') == -1 &&
lowerKey.indexOf('.gif') == -1) {
continue;
}
var img = new Image();
img.addEventListener('load', successHandler, true);
img.addEventListener('error', errorHandler, true);
img.src = imgIdents[key];
img.gamejsKey = key;
countTotal++;
}
if (countTotal > 0) {
_PRELOADING = true;
}
return getProgress;
};
/**
* add the given <img> dom elements into the cache.
* @private
*/
var addToCache = function(img) {
CACHE[img.gamejsKey] = img;
return;
};
},{"../gamejs":25}],33:[function(require,module,exports){
/**
* @fileoverview Static methods for logging and setting the log level. All logging functions (`info()`, `debug()`, etc.) take
* any number of arguments and will print them in one line.
*
*/
var DEBUG_LEVELS = ['debug', 'info', 'warn', 'error', 'fatal'];
var debugLevel = 2;
var gamejs = require('../gamejs');
/**
* set logLevel as string or number
* * 0 = info
* * 1 = warn
* * 2 = error
* * 3 = fatal
*
* @example
* gamejs.setLogLevel(0); // debug
* gamejs.setLogLevel('error'); // equal to setLogLevel(2)
*/
exports.setLogLevel = function(logLevel) {
if (typeof logLevel === 'string' && DEBUG_LEVELS.indexOf(logLevel)) {
debugLevel = DEBUG_LEVELS.indexOf(logLevel);
} else if (typeof logLevel === 'number') {
debugLevel = logLevel;
} else {
throw new Error('invalid logLevel ', logLevel, ' Must be one of: ', DEBUG_LEVELS);
}
return debugLevel;
};
/**
* Log a msg to the console if console is enable
* @param {String} message,... the msg to log
*/
var log = exports.log = function() {
if (gamejs.thread.inWorker === true) {
gamejs.thread._logMessage.apply(null, arguments);
return;
}
// IEFIX can't call apply on console
var args = Array.prototype.slice.apply(arguments, [0]);
args.unshift(Date.now());
if (window.console !== undefined && console.log.apply) {
console.log.apply(console, args);
}
};
/**
* @param {String} message,... to log
*/
exports.debug = function() {
if (debugLevel <= DEBUG_LEVELS.indexOf('debug')) {
log.apply(this, arguments);
}
};
/**
* @param {String} message,... to log
*/
exports.info = function() {
if (debugLevel <= DEBUG_LEVELS.indexOf('info')) {
log.apply(this, arguments);
}
};
/**
* @param {String} message,... to log
*/
exports.warn = function() {
if (debugLevel <= DEBUG_LEVELS.indexOf('warn')) {
log.apply(this, arguments);
}
};
/**
* @param {String} message,... to log
*/
exports.error = function() {
if (debugLevel <= DEBUG_LEVELS.indexOf('error')) {
log.apply(this, arguments);
}
};
/**
* @param {String} message to log
*/
exports.fatal = function() {
if (debugLevel <= DEBUG_LEVELS.indexOf('fatal')) {
log.apply(this, arguments);
}
};
},{"../gamejs":25}],34:[function(require,module,exports){
/**
* @fileoverview Degrees and radians.
*
*/
/**
*
* absolute angle to relative angle, in degrees
* @param {Number} absolute angle in degrees
* @returns {Number} relative angle in degrees
*/
exports.normaliseDegrees=function(degrees){
degrees=degrees % 360;
if(degrees<0) {
degrees+=360;
}
return degrees;
};
/**
*
* absolute angle to relative angle, in radians
* @param {Number} absolute angle in radians
* @returns {Number} relative angle in radians
*/
exports.normaliseRadians=function(radians){
radians=radians % (2*Math.PI);
if(radians<0) {
radians+=(2*Math.PI);
}
return radians;
};
/**
*
* convert radians to degrees
* @param {Number} radians
* @returns {Number} degrees
*/
exports.degrees=function(radians) {
return radians*(180/Math.PI);
};
/**
*
* convert degrees to radians
* @param {Number} degrees
* @returns {Number} radians
*/
exports.radians=function(degrees) {
return degrees*(Math.PI/180);
};
},{}],35:[function(require,module,exports){
/**
* @fileoverview Binary Heap implementation from Eloquent JavaScript
*
* @see http://eloquentjavascript.net/appendix2.html
*/
var BinaryHeap = exports.BinaryHeap = function(scoreFunction){
/**
* @ignore
*/
this.content = [];
/**
* @ignore
*/
this.scoreFunction = scoreFunction;
return this;
};
/**
* Add element to heap.
* @param {Object} element
*/
BinaryHeap.prototype.push = function(element) {
this.content.push(element);
this.sinkDown(this.content.length - 1);
return;
};
/**
* Return first element from heap.
* @param {Object} element
* @returns {Object} element
*/
BinaryHeap.prototype.pop = function() {
// Store the first element so we can return it later.
var result = this.content[0];
// Get the element at the end of the array.
var end = this.content.pop();
// If there are any elements left, put the end element at the
// start, and let it bubble up.
if (this.content.length > 0) {
this.content[0] = end;
this.bubbleUp(0);
}
return result;
};
/**
* Remove the given element from the heap.
* @param {Object} element
* @throws {Error} if node not found
* @returns true if the node was round and removed or fals otherwise
*/
BinaryHeap.prototype.remove = function(node) {
// To remove a value, we must search through the array to find
// it.
var isFound = this.content.some(function(cNode, idx) {
if (cNode == node) {
var end = this.content.pop();
if (idx != this.content.length) {
this.content[idx] = end;
if (this.scoreFunction(end) < this.scoreFunction(node)) {
this.sinkDown(idx);
} else {
this.bubbleUp(idx);
}
}
return true;
}
return false;
}, this);
return isFound;
};
/**
* Number of elements in heap.
*/
BinaryHeap.prototype.size = function() {
return this.content.length;
};
/**
* @ignore
*/
BinaryHeap.prototype.sinkDown = function(idx) {
// Fetch the element that has to be sunk
var element = this.content[idx];
// When at 0, an element can not sink any further.
while (idx > 0) {
// Compute the parent element's index, and fetch it.
var parentIdx = Math.floor((idx + 1) / 2) - 1;
var parent = this.content[parentIdx];
// Swap the elements if the parent is greater.
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
this.content[parentIdx] = element;
this.content[idx] = parent;
// Update 'n' to continue at the new position.
idx = parentIdx;
// Found a parent that is less, no need to sink any further.
} else {
break;
}
}
return;
};
/**
* @ignore
*/
BinaryHeap.prototype.bubbleUp = function(idx) {
// Look up the target element and its score.
var length = this.content.length;
var element = this.content[idx];
var elemScore = this.scoreFunction(element);
while(true) {
// Compute the indices of the child elements.
var child2Idx = (idx + 1) * 2;
var child1Idx= child2Idx - 1;
// This is used to store the new position of the element,
// if any.
var swapIdx = null;
// If the first child exists (is inside the array)...
var child1Score;
if (child1Idx < length) {
// Look it up and compute its score.
var child1 = this.content[child1Idx];
child1Score = this.scoreFunction(child1);
// If the score is less than our element's, we need to swap.
if (child1Score < elemScore) {
swapIdx = child1Idx;
}
}
// Do the same checks for the other child.
if (child2Idx < length) {
var child2 = this.content[child2Idx];
var child2Score = this.scoreFunction(child2);
if (child2Score < (swapIdx === null ? elemScore : child1Score)) {
swapIdx = child2Idx;
}
}
// If the element needs to be moved, swap it, and continue.
if (swapIdx !== null) {
this.content[idx] = this.content[swapIdx];
this.content[swapIdx] = element;
idx = swapIdx;
// Otherwise, we are done.
} else {
break;
}
}
return;
};
},{}],36:[function(require,module,exports){
/**
* @fileoverview Matrix manipulation, used by GameJs itself. You
* probably do not need this unless you manipulate a Context's transformation
* matrix yourself.
*/
// correct way to do scale, rotate, translate
// * gamejs.utils.matrix will be used in gamejs.transforms, modifing the surfaces.matrix
// * this matrix must be applied to the context in Surface.draw()
/**
* @returns {Array} [1, 0, 0, 1, 0, 0]
*/
var identiy = exports.identity = function () {
return [1, 0, 0, 1, 0, 0];
};
/**
* @param {Array} matrix
* @param {Array} matrix
* @returns {Array} matrix sum
*/
var add = exports.add = function(m1, m2) {
return [
m1[0] + m2[0],
m1[1] + m2[1],
m1[2] + m2[2],
m1[3] + m2[3],
m1[4] + m2[4],
m1[5] + m2[5],
m1[6] + m2[6]
];
};
/**
* @param {Array} matrix A
* @param {Array} matrix B
* @returns {Array} matrix product
*/
var multiply = exports.multiply = function(m1, m2) {
return [
m1[0] * m2[0] + m1[2] * m2[1],
m1[1] * m2[0] + m1[3] * m2[1],
m1[0] * m2[2] + m1[2] * m2[3],
m1[1] * m2[2] + m1[3] * m2[3],
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
];
};
/**
* @param {Array} matrix
* @param {Number} dx
* @param {Number} dy
* @returns {Array} translated matrix
*/
var translate = exports.translate = function(m1, dx, dy) {
return multiply(m1, [1, 0, 0, 1, dx, dy]);
};
/**
* @param {Array} matrix
* @param {Number} angle in radians
* @returns {Array} rotated matrix
*/
var rotate = exports.rotate = function(m1, angle) {
// radians
var sin = Math.sin(angle);
var cos = Math.cos(angle);
return multiply(m1, [cos, sin, -sin, cos, 0, 0]);
};
/**
* @param {Array} matrix
* @returns {Number} rotation in radians
*/
var rotation = exports.rotation = function(m1) {
return Math.atan2(m1[1], m1[0]);
};
/**
* @param {Array} matrix
* @param {Array} vector [a, b]
* @returns {Array} scaled matrix
*/
var scale = exports.scale = function(m1, svec) {
var sx = svec[0];
var sy = svec[1];
return multiply(m1, [sx, 0, 0, sy, 0, 0]);
};
},{}],37:[function(require,module,exports){
/**
* @fileoverview
* A noise generator comparable to Perlin noise, which is useful
* for generating procedural content.
*
* This implementation provides 2D and 3D noise:
*
* var simplex = new Simplex();
* simplex.get(2, 4);
* simple.get3d(1, 2, 4);
*
* You can optionally
* pass a seedable pseudo-random number generator to its constructor. This
* generator object is assumed to have a `random()` method; `Math` is used
* per default:
*
* var Alea = require('gamejs/math/random').Alea;
* var simplex = new Simplex(new Alea());
*
* Also see `gamejs/math/random` for a seedable pseudo random number generator
*
* @see gamejs/utils/prng
*/
// Ported to JS by by zz85 <https://github.com/zz85> from Stefan
// Gustavson's java implementation
// <http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf>
// Read Stefan's excellent paper for details on how this code works.
//
// Sean McCullough banksean@gmail.com
/**
* @param {Object} randomNumberGenerator the random number generator to use; most provide `random()` method
* @usage
* var simplex = new gamejs.noise.Simplex();
* simplex.get(x, y);
* // or for 3d noise
* simple.get(x, y, y);
*/
var Simplex = exports.Simplex = function(r) {
if (r === undefined) {
r = Math;
}
/** @ignore */
this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
/** @ignore */
this.p = [];
var i;
for (i=0; i<256; i++) {
this.p[i] = Math.floor(r.random()*256);
}
// To remove the need for index wrapping, double the permutation table length
/** @ignore */
this.perm = [];
for(i=0; i<512; i++) {
this.perm[i]=this.p[i & 255];
}
// A lookup table to traverse the simplex around a given point in 4D.
// Details can be found where this table is used, in the 4D noise method.
/** @ignore */
this.simplex = [
[0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0],
[0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0],
[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
[1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0],
[1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0],
[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
[2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0],
[2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]];
};
/** @ignore */
Simplex.prototype.dot = function(g, x, y) {
return g[0]*x + g[1]*y;
};
/**
* @param {Number} x
* @param {Number} y
* @returns {Number} noise for given position, in range [-1, 1]
*/
Simplex.prototype.get = function(xin, yin) {
var n0, n1, n2; // Noise contributions from the three corners
// Skew the input space to determine which simplex cell we're in
var F2 = 0.5*(Math.sqrt(3.0)-1.0);
var s = (xin+yin)*F2; // Hairy factor for 2D
var i = Math.floor(xin+s);
var j = Math.floor(yin+s);
var G2 = (3.0-Math.sqrt(3.0))/6.0;
var t = (i+j)*G2;
var X0 = i-t; // Unskew the cell origin back to (x,y) space
var Y0 = j-t;
var x0 = xin-X0; // The x,y distances from the cell origin
var y0 = yin-Y0;
// For the 2D case, the simplex shape is an equilateral triangle.
// Determine which simplex we are in.
var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1)
// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
// c = (3-sqrt(3))/6
var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
var y1 = y0 - j1 + G2;
var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
var y2 = y0 - 1.0 + 2.0 * G2;
// Work out the hashed gradient indices of the three simplex corners
var ii = i & 255;
var jj = j & 255;
var gi0 = this.perm[ii+this.perm[jj]] % 12;
var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12;
var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12;
// Calculate the contribution from the three corners
var t0 = 0.5 - x0*x0-y0*y0;
if(t0<0) {
n0 = 0.0;
} else {
t0 *= t0;
n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
}
var t1 = 0.5 - x1*x1-y1*y1;
if(t1<0) {
n1 = 0.0;
} else {
t1 *= t1;
n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1);
}
var t2 = 0.5 - x2*x2-y2*y2;
if(t2<0) {
n2 = 0.0;
} else {
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to return values in the interval [-1,1].
return 70.0 * (n0 + n1 + n2);
};
/**
* @param {Number} x
* @param {Number} y
* @param {Number} y
* @returns {Number} noise for given position, in range [-1, 1]
*/
Simplex.prototype.get3d = function(xin, yin, zin) {
var n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
var F3 = 1.0/3.0;
var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D
var i = Math.floor(xin+s);
var j = Math.floor(yin+s);
var k = Math.floor(zin+s);
var G3 = 1.0/6.0; // Very nice and simple unskew factor, too
var t = (i+j+k)*G3;
var X0 = i-t; // Unskew the cell origin back to (x,y,z) space
var Y0 = j-t;
var Z0 = k-t;
var x0 = xin-X0; // The x,y,z distances from the cell origin
var y0 = yin-Y0;
var z0 = zin-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
if(x0>=y0) {
if(y0>=z0)
{ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
var x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
var y1 = y0 - j1 + G3;
var z1 = z0 - k1 + G3;
var x2 = x0 - i2 + 2.0*G3; // Offsets for third corner in (x,y,z) coords
var y2 = y0 - j2 + 2.0*G3;
var z2 = z0 - k2 + 2.0*G3;
var x3 = x0 - 1.0 + 3.0*G3; // Offsets for last corner in (x,y,z) coords
var y3 = y0 - 1.0 + 3.0*G3;
var z3 = z0 - 1.0 + 3.0*G3;
// Work out the hashed gradient indices of the four simplex corners
var ii = i & 255;
var jj = j & 255;
var kk = k & 255;
var gi0 = this.perm[ii+this.perm[jj+this.perm[kk]]] % 12;
var gi1 = this.perm[ii+i1+this.perm[jj+j1+this.perm[kk+k1]]] % 12;
var gi2 = this.perm[ii+i2+this.perm[jj+j2+this.perm[kk+k2]]] % 12;
var gi3 = this.perm[ii+1+this.perm[jj+1+this.perm[kk+1]]] % 12;
// Calculate the contribution from the four corners
var t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
if(t0<0) {
n0 = 0.0;
} else {
t0 *= t0;
n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0, z0);
}
var t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
if(t1<0) {
n1 = 0.0;
} else {
t1 *= t1;
n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1, z1);
}
var t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
if(t2<0) {
n2 = 0.0;
} else {
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2, z2);
}
var t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
if(t3<0) {
n3 = 0.0;
} else {
t3 *= t3;
n3 = t3 * t3 * this.dot(this.grad3[gi3], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 32.0*(n0 + n1 + n2 + n3);
};
},{}],38:[function(require,module,exports){
/**
* @fileoverview A seedable random-number generator.
*
* A generator is initialized by GameJs and can be used with the
* static functions of this module:
*
* gamejs.random.choose([1,2,4]);
* // integet between and including 2 and 5
* gamejs.random.integer(2, 5);
*
* You can re-initialize this generator with a different seed by
* calling `gamejs.utils.prng.init(seed)` after which the static
* functions in this module will use the new seed.
*
* @usage
* var prng = require('gamejs/math/random');
* prng.random(); // 0.6765871671959758
* prng.integer(2, 10); // 5
* prng.choose([1,2,3,4,5]); // 3
*/
// From http://baagoe.com/en/RandomMusings/javascript/
// Johannes Baagøe <baagoe@baagoe.com>, 2010
// API modified by Simon Oberhammer <simon@nekapuzer.at>, 2012
// discussion of the used algorithms <http://baagoe.org/en/w/index.php/Better_random_numbers_for_javascript>
/** @ignore **/
var Mash = function Mash() {
var n = 0xefc8249d;
this.hash = function(data) {
data = data.toString();
var i;
for (i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
var h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
this.version = 'Mash 0.9';
return this;
};
/**
* A seedable pseudo-random number generator.
* @param {Number|String} seed the seed for generating the numbers
*
* @usage
* var prng = require('gamejs/math/random');
* var seed = 'gamejs';
* var alea = new prng.Alea(seed);
* alea.random(); // 0.6765871671959758
* alea.random(); // 0.15881546027958393
*
* // generator with the same seed will generate the same sequence
* // of numbers:
* var aleaTwo = new prng.Alea(seed);
* aleaTwo.random(); // 0.6765871671959758
* aleaTwo.random(); // 0.15881546027958393
*/
var Alea = exports.Alea = function Alea() {
var args = Array.prototype.slice.call(arguments);
var s0 = 0;
var s1 = 0;
var s2 = 0;
var c = 1;
if (args.length === 0 || !args[0]) {
args = [Date.now()];
}
var mash = new Mash();
s0 = mash.hash(' ');
s1 = mash.hash(' ');
s2 = mash.hash(' ');
var i;
for (i = 0; i < args.length; i++) {
s0 -= mash.hash(args[i]);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash.hash(args[i]);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash.hash(args[i]);
if (s2 < 0) {
s2 += 1;
}
}
mash = null;
/**
* @returns {Number} the next random number as determined by the seed
*/
this.random = function() {
var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
s2 = t - (c = t | 0);
return s2;
};
this.integer = function(min, max) {
return min + parseInt(this.random() * (max-min+1), 10);
};
this.vector = function(min, max) {
return [this.integer(min[0], max[0]), this.integer(min[1], max[1])];
};
this.choose = function(items) {
return items[this.integer(0, items.length-1)];
};
return this;
};
// alea instance per gamejs instance
var alea = null;
/**
* @param {Number} min
* @param {Number} max
* @returns {Number} random integer between min and max
*/
var integer = exports.integer = function(min, max){
return alea.integer(min, max);
};
/**
* @param {Array} minVector 2 integers, the minimum vector
* @param {Array} maxVector 2 integers, the maximum vector
* @returns {Array} a random vector [min[0]<=x<=max[0], min[1]<=y<=max[1]]
*/
exports.vector = function(min, max){
return alea.vector(min, max);
};
/**
* @param {Array} items
* @returns {Object} random item from items list
*/
exports.choose = function(items){
return alea.choose(items);
};
/**
* @returns {Number} next random float between 0 and 1
*/
exports.random = function() {
return alea.random();
};
/**
* Re-initialize the per instance random number generator used
* in the static functions on this module (e.g. vector())
* @param {Number|String} seed
*/
exports.init = function(seed) {
alea = new Alea(seed);
};
},{}],39:[function(require,module,exports){
/**
* Vector operations.
*/
var angles = require('./angles');
/**
* @param {Array} origin point [b0, b1]
* @param {Array} target point [b0, b1]
* @returns {Number} distance between two points
*/
exports.distance = function(a, b) {
return len(subtract(a, b));
};
/**
* subtracts vectors [a0, a1] - [a0, a1]
* @param {Array} a
* @param {Array} b
* @returns {Array} vector
*/
var subtract = exports.subtract = function(a, b) {
return [a[0] - b[0], a[1] - b[1]];
};
/**
* adds vectors [a0, a1] - [a0, a1]
* @param {Array} a vector
* @param {Array} b vector
* @returns {Array} vector
*/
var add = exports.add = function(a, b) {
return [a[0] + b[0], a[1] + b[1]];
};
/**
* multiply vector with scalar or other vector
* @param {Array} vector [v0, v1]
* @param {Number|Array} vector or number
* @returns {Number|Array} result
*/
var multiply = exports.multiply = function(a, s) {
if (typeof s === 'number') {
return [a[0] * s, a[1] * s];
}
return [a[0] * s[0], a[1] * s[1]];
};
/**
* @param {Array} a vector
* @param {Number} s
*/
exports.divide = function(a, s) {
if (typeof s === 'number') {
return [a[0] / s, a[1] / s];
}
throw new Error('only divide by scalar supported');
};
/**
* @param {Array} vector [v0, v1]
* @returns {Number} length of vector
*/
var len = exports.len = function(v) {
return Math.sqrt(v[0]*v[0] + v[1]*v[1]);
};
/**
*
* normalize vector to unit vector
* @param {Array} vector [v0, v1]
* @returns {Array} unit vector [v0, v1]
*/
var unit = exports.unit = function(v) {
var l = len(v);
if(l) {
return [v[0] / l, v[1] / l];
}
return [0, 0];
};
/**
*
* rotate vector
* @param {Array} vector [v0, v1]
* @param {Number} angle to rotate vector by, radians. can be negative
* @returns {Array} rotated vector [v0, v1]
*/
exports.rotate=function(v, angle){
angle=angles.normaliseRadians(angle);
return [v[0]* Math.cos(angle)-v[1]*Math.sin(angle),
v[0]* Math.sin(angle)+v[1]*Math.cos(angle)];
};
/**
*
* calculate vector dot product
* @param {Array} vector [v0, v1]
* @param {Array} vector [v0, v1]
* @returns {Number} dot product of v1 and v2
*/
var dot = exports.dot=function(v1, v2){
return (v1[0] * v2[0]) + (v1[1] * v2[1]);
};
/**
*
* calculate angle between vectors
* @param {Array} vector [v0, v1]
* @param {Array} vector [v0, v1]
* @returns {Number} angle between v1 and v2 in radians
*/
exports.angle=function(v1, v2){
var perpDot = v1[0] * v2[1] - v1[1] * v2[0];
return Math.atan2(perpDot, dot(v1,v2));
};
/**
* @returns {Array} vector with max length as specified.
*/
exports.truncate = function(v, maxLength) {
if (len(v) > maxLength) {
return multiply(unit(v), maxLength);
}
return v;
};
/**
* @returns the center of multipled 2d points
* @param {Array} first point
* @param {Array} second point
* @param {Array} ...
*/
exports.centroid = function() {
var args = Array.prototype.slice.apply(arguments, [0]);
var c = [0,0];
args.forEach(function(p) {
c[0] += parseInt(p[0], 10);
c[1] += parseInt(p[1], 10);
});
var len = args.length;
return [
c[0] / len,
c[1] / len
];
};
},{"./angles":34}],40:[function(require,module,exports){
/**
* @fileoverview
* A* path finding algorithm
*
* Use the `findRoute(map, from, to, [timeout])` function to get the linked list
* leading `from` a point `to` another on the given `map`.
*
* The map must implement the interface `gamejs.pathfinding.Map`. This
* class already holds an example implementation for debugging use.
*
* Optionally, the search is cancelled after `timeout` in millseconds.
*
* If there is no route `null` is returned.
*
* @see http://en.wikipedia.org/wiki/A*_search_algorithm
* @see http://eloquentjavascript.net/chapter7.html
*/
var BinaryHeap = require('./math/binaryheap').BinaryHeap;
/**
* helper function for A*
*/
function ReachedList(hashFn) {
var list = {};
this.store = function(point, route) {
list[hashFn(point)] = route;
return;
};
this.find = function(point) {
return list[hashFn(point)];
};
return this;
}
/** A* search function.
*
* This function expects a `Map` implementation and the origin and destination
* points given. If there is a path between the two it will return the optimal
* path as a linked list. If there is no path it will return null.
*
* The linked list is in reverse order: the first item is the destination and
* the path to the origin follows.
*
* @param {Map} map map instance, must follow interface defined in {Map}
* @param {Array} origin
* @param {Array} destination
* @param {Number} timeout milliseconds after which search should be canceled
* @returns {Object} the linked list leading from `to` to `from` (sic!).
**/
exports.findRoute = function(map, from, to, timeout) {
var open = new BinaryHeap(routeScore);
var hashFn = typeof map.hash === 'function' ? map.hash : defaultHash;
var reached = new ReachedList(hashFn);
function routeScore(route) {
if (route.score === undefined) {
route.score = map.estimatedDistance(route.point, to) + route.length;
}
return route.score;
}
function addOpenRoute(route) {
open.push(route);
reached.store(route.point, route);
}
function processNewPoints(direction) {
var known = reached.find(direction);
var newLength = route.length + map.actualDistance(route.point, direction);
if (!known || known.length > newLength){
if (known) {
open.remove(known);
}
addOpenRoute({
point: direction,
from: route,
length: newLength
});
}
}
var startMs = Date.now();
var route = null;
addOpenRoute({
point: from,
from: null,
length: 0
});
var equalsFn = typeof map.equals === 'function' ? map.equals : defaultEquals;
while (open.size() > 0 && (!timeout || Date.now() - startMs < timeout)) {
route = open.pop();
if (equalsFn(to, route.point)) {
return route;
}
map.adjacent(route.point).forEach(processNewPoints);
} // end while
return null;
};
var defaultEquals = function(a, b) {
return a[0] === b[0] && a[1] === b[1];
};
var defaultHash = function(a) {
return a[0] + '-' + a[1];
};
/**
* This is the interface for a Map that can be passed to the `findRoute()`
* function. `Map` is not instantiable - see the unit tests for an example
* implementation of Map.
*/
var Map = exports.Map = function() {
throw new Error('not instantiable, this is an interface');
};
/**
* @param {Array} origin
* @returns {Array} list of points accessible from given Point
*/
Map.prototype.adjacent = function(origin) {
};
/**
* @param {Object} a one of the points ot test for equality
* @param {Object} b ... the other point
* @returns Wheter the two points are equal.
*/
Map.prototype.equals = defaultEquals;
/**
* @param {Object} a point
* @returns {String} hash for the point
*/
Map.prototype.hash = defaultHash;
/**
* Estimated lower bound distance between two points.
* @param {Object} pointA
* @param {Object} pointB
* @returns {Number} the estimated distance between two points
*/
Map.prototype.estimatedDistance = function(pointA, pointB) {
return 1;
};
/**
* Actual distance between two points.
* @param {Object} pointA
* @param {Object} pointB
* @returns {Number} the actual distance between two points
*/
Map.prototype.actualDistance = function(pointA, pointB) {
return 1;
};
},{"./math/binaryheap":35}],41:[function(require,module,exports){
var gamejs = require('../gamejs');
var objects = require('./utils/objects');
/**
* @fileoverview Image mask. Usefull for pixel perfect collision detection:
*
* @example
* var unitMask = new Maks(unitSurface, collisionThresholdAlphaValue);
* var spearMask = new Maks(unitSurface, collisionThresholdAlphaValue);
* var collide = unitMask.overlap(spearMask);
*/
/**
* Creates an image mask from the given Surface. The alpha of each pixel is checked
* to see if it is greater than the given threshold. If it is greater then
* that pixel is set as not colliding.
*
* @param {Surface} surface used for image mask
* @param {Number} threshold 0 to 255. defaults to: 255, fully transparent
*
*/
var Mask = exports.Mask = function(surface, threshold) {
/**
* @ignore
*/
this._bits = [];
threshold = (threshold && (255 - threshold)) || 255;
var imgData = surface.getImageData().data;
var dims = surface.getSize();
/**
* @ignore
*/
this.width = dims[0];
/**
* @ignore
*/
this.height = dims[1];
var i,j;
for (i=0;i<this.width;i++) {
this._bits[i] = [];
for (j=0;j<this.height;j++) {
this._bits[i][j] = false;
}
}
for (i=0;i<imgData.length;i += 4) {
// y: pixel # / width
var y = parseInt((i / 4) / dims[0], 10);
// x: pixel # % width
var x = parseInt((i / 4) % dims[0], 10);
var alpha = imgData[i+3];
if (alpha >= threshold) {
this.setAt(x, y);
}
}
return;
};
/**
* @param {gamejs.mask.Mask} otherMask
* @param {Array} offset [x,y]
* @returns the overlapping rectangle or null if there is no overlap;
*/
Mask.prototype.overlapRect = function(otherMask, offset) {
var arect = this.rect;
var brect = otherMask.rect;
if (offset) {
brect.moveIp(offset);
}
// bounding box intersect
if (!brect.collideRect(arect)) {
return null;
}
var xStart = Math.max(arect.left, brect.left);
var xEnd = Math.min(arect.right, brect.right);
var yStart = Math.max(arect.top, brect.top);
var yEnd = Math.min(arect.bottom, brect.bottom);
return new gamejs.Rect([xStart, yStart], [xEnd - xStart, yEnd - yStart]);
};
/**
*
* @returns True if the otherMask overlaps with this map.
* @param {Mask} otherMask
* @param {Array} offset
*/
Mask.prototype.overlap = function(otherMask, offset) {
var overlapRect = this.overlapRect(otherMask, offset);
if (overlapRect === null) {
return false;
}
var arect = this.rect;
var brect = otherMask.rect;
if (offset) {
brect.moveIp(offset);
}
var count = 0;
var x,y;
for (y=overlapRect.top; y<=overlapRect.bottom; y++) {
for (x=overlapRect.left; x<=overlapRect.right; x++) {
if (this.getAt(x - arect.left, y - arect.top) &&
otherMask.getAt(x - brect.left, y - brect.top)) {
return true;
}
}
}
// NOTE this should not happen because either we bailed out
// long ago because the rects do not overlap or there is an
// overlap and we should not have gotten this far.
// throw new Error("Maks.overlap: overlap detected but could not create mask for it.");
return false;
};
/**
* @param {gamejs.mask.Mask} otherMask
* @param {Array} offset [x,y]
* @returns the number of overlapping pixels
*/
Mask.prototype.overlapArea = function(otherMask, offset) {
var overlapRect = this.overlapRect(otherMask, offset);
if (overlapRect === null) {
return 0;
}
var arect = this.rect;
var brect = otherMask.rect;
if (offset) {
brect.moveIp(offset);
}
var count = 0;
var x,y;
for (y=overlapRect.top; y<=overlapRect.bottom; y++) {
for (x=overlapRect.left; x<=overlapRect.right; x++) {
if (this.getAt(x - arect.left, y - arect.top) &&
otherMask.getAt(x - brect.left, y - brect.top)) {
count++;
}
}
}
return count;
};
/**
* @param {gamejs.mask.Mask} otherMask
* @param {Array} offset [x,y]
* @returns a mask of the overlapping pixels
*/
Mask.prototype.overlapMask = function(otherMask, offset) {
var overlapRect = this.overlapRect(otherMask, offset);
if (overlapRect === null) {
return 0;
}
var arect = this.rect;
var brect = otherMask.rect;
if (offset) {
brect.moveIp(offset);
}
var mask = new Mask(new gamejs.graphics.Surface([overlapRect.width, overlapRect.height]));
var x,y;
for (y=overlapRect.top; y<=overlapRect.bottom; y++) {
for (x=overlapRect.left; x<=overlapRect.right; x++) {
if (this.getAt(x - arect.left, y - arect.top) &&
otherMask.getAt(x - brect.left, y - brect.top)) {
mask.setAt(x, y);
}
}
}
return mask;
};
/**
* Set bit at position.
* @param {Number} x
* @param {Number} y
*/
Mask.prototype.setAt = function(x, y) {
this._bits[x][y] = true;
};
/**
* Get bit at position.
*
* @param {Number} x
* @param {Number} y
*/
Mask.prototype.getAt = function(x, y) {
x = parseInt(x, 10);
y = parseInt(y, 10);
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
return false;
}
return this._bits[x][y];
};
/**
* Flip the bits in this map.
*/
Mask.prototype.invert = function() {
this._bits = this._bits.map(function(row) {
return row.map(function(b) {
return !b;
});
});
};
/**
* @returns {Array} the dimensions of the map
*/
Mask.prototype.getSize = function() {
return [this.width, this.height];
};
objects.accessors(Mask.prototype, {
/**
* Rect of this Mask.
*/
'rect': {
get: function() {
return new gamejs.Rect([0, 0], [this.width, this.height]);
}
},
/**
* @returns {Number} number of set pixels in this mask.
*/
'length': {
get: function() {
var c = 0;
this._bits.forEach(function(row) {
row.forEach(function(b) {
if (b) {
c++;
}
});
});
return c;
}
}
});
},{"../gamejs":25,"./utils/objects":48}],42:[function(require,module,exports){
var gamejs = require('../gamejs');
var uri = require('./utils/uri');
var Callback = require('./utils/callback').Callback;
/**
* ignore
*/
var _EVENTS = exports._EVENTS = {
RESULT: 1001,
ALIVE: 1002,
LOG: 1004
};
/**
* @fileoverview
*
* gamejs.worker makes it more convinient to work with W3C WebWorkers by providing a way to run
* CommonJs modules inside of them. GameJs also provides the typically `gamejs.ready()` and
* event loop to facilitate communication between workers and the main application.
*
* See the `examples/workers` directory for a running example.
*
* Create a worker with the main module "foo-worker" (see below for how the worker's module looks like):
*
* var fooWorker = new Worker('./foo-worker');
* // Send a message to your worker.
* // The Message doesn't have to be a string but it
* // must be `JSON.stringify()`-able
* fooWorker.post("foobar");
*
* You can also recieve messages from the worker:
*
* // recieve events from the worker
* fooWorker.onEvent(function(event) {
* if(event.timestamp > ...)
* });
*
* And this is how the above referenced "foo-worker" module would looke like. As usual, we need a
* `gamejs.ready()` to get started and within that we bind an event handler:
*
* var gamejs = require('gamejs');
* gamejs.ready(function() {
* gamejs.event.onEvent(function(event) {
* var plaintext = fastCrack(event.password)
* ....
* });
* });
*
* Our event worker could do expensive calculations (seperate and not blocking the main game) when
* recieving an event. Once the result is caculated, it can be sent back to the main application
* with `gamejs.thread.post()`:
*
* gamejs.thread.post({
* info: "important message from worker",
* timestamp: 12232435234
* });
*
* The main application would in turn recieve an event posted like this from `fooWorker.onEvent`, as seen above.
*
* This module is useful for expensive algorithms where the result does not have to available instantiously
* (e.g., path-finding) or for continous logic which can be
* calculated seperately from the rendering loop, and which only needs to feed back into the model of the rendering every
* now and then (e.g. physics) The main draw back of the `Worker` model is that
* you can only communicate with them via text messages (typically JSON.stringify()ed messages).
*/
/**
* true if this GameJs instance is being executed within a WebWorker
* @type Boolean
*/
var inWorker = exports.inWorker = (this.importScripts !== undefined);
/**
* executed in scope of worker
* @ignore
*/
exports._ready = function() {
self.onmessage = function(event) {
gamejs.event._triggerCallbacks(event.data);
};
self.postMessage({
type: _EVENTS.ALIVE
});
};
/**
* Send an event back to the main script.
* @param {Object} data to be sent back to main script
*/
exports.post = function(data) {
if (inWorker) {
self.postMessage({
type: _EVENTS.RESULT,
data: data
});
} else {
throw new Error('gamejs.postMessage only available in a thread/worker module');
}
};
/**
* Send message to main context for logging
* @ignore
**/
exports._logMessage = function() {
var args = [];
Array.prototype.forEach.call(arguments, function(a) {
args.push(a);
});
self.postMessage({
type: _EVENTS.LOG,
arguments: args
});
};
/**
* executed in scope of worker before user's main module
* @ignore
*/
var workerPrefix = function workerPrefix() {
__scripts.forEach(function(script) {
try {
importScripts(script);
} catch (e) {
// can't help the worker
}
});
};
/**
* Setup a worker which has `require()` defined
* @ignore
**/
var create = function(workerModuleId) {
var moduleRoot = uri.resolve(document.location.href, window.require.getModuleRoot());
var initialScripts = [];
Array.prototype.slice.apply(document.getElementsByTagName('script'), [0]).forEach(function(script) {
if (script.src) {
initialScripts.push(script.src);
}
});
var URL = window.URL || window.webkitURL;
var prefixString = workerPrefix.toString();
// don't be afraid...
prefixString = prefixString.substring(prefixString.indexOf("{") + 1, prefixString.lastIndexOf("}"));
var blob = new Blob([
'var __scripts = ["' + initialScripts.join('","') + '"];',
prefixString,
';self.require.setModuleRoot("' + moduleRoot + '");',
'self.require.run("'+ workerModuleId +'");'
], {type: 'application\/javascript'});
var blobURL = URL.createObjectURL(blob);
return new Worker(blobURL);
};
/**
* The `Worker` constructor takes only one argument: a module id. This module
* will be executed inside the newly created Worker. It is effectively the
* main module of the Worker.
*
* Inside a Worker, you can use `require()` to import other scripts or
* GameJs modules.
*
* **Note:** A Worker does not have access to the browser's `document`. So
* a lot of GameJs modules - everything related to drawing to the canvas -
* do not work in the Worker.
*
* You can use `gamejs.time.*`, `gamejs.utils.*`, `gamejs.event.*` and probably others
* (as well as any module you write yourself for this purpose, of course).
*
* @param {String} moduleId The Worker's main module id. The main module will be executed in the worker
*/
exports.Worker = function(moduleId) {
// FIXME id should be unchangeable
/**
* Unique id of this worker
* @property {Number}
*/
var id = this.id = guid(moduleId);
var worker = create(moduleId);
var deadQueue = [];
var alive = false;
var self = this;
var _CALLBACKS = [];
var _ERROR_CALLBACKS = [];
function triggerCallbacks(callbacks, event) {
callbacks.forEach(function(c) {
c.trigger(event);
});
}
worker.onmessage = function(event) {
if (event.data.type === _EVENTS.ALIVE) {
// if worker says he is alive -> send him the event queue so far
alive = true;
deadQueue.forEach(function(data) {
self.post(data);
});
} else if (event.data.type === _EVENTS.LOG) {
gamejs.logging.log.apply(null, [id].concat(event.data.arguments));
} else {
triggerCallbacks(_CALLBACKS, event.data.data);
}
};
worker.onerror = function(event) {
gamejs.logging.error('Error in worker "' + id + '" line ' + event.lineno + ': ', event.message);
triggerCallbacks(_ERROR_CALLBACKS, {
data: event.data,
worker: self,
event: event
});
};
this.onEvent = function(fn, scope) {
_CALLBACKS.push(new Callback(fn, scope));
};
this.onError = function(fn, scope) {
_ERROR_CALLBACKS.push(new Callback(fn, scope));
};
/**
* Send a message to the worker
*
* @param {Object} data Payload object which gets sent to the Worker
*/
this.post = function(data) {
if (alive) {
worker.postMessage(data);
} else {
deadQueue.push(data);
}
};
return this;
};
/**
* not a real GUID
* @ignore
*/
function guid(moduleId) {
var S4 = function() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
return moduleId + '@' + (S4()+S4());
}
},{"../gamejs":25,"./utils/callback":47,"./utils/uri":50}],43:[function(require,module,exports){
var gamejs = require('../gamejs');
var objects = require('./utils/objects');
var xml = require('./utils/xml');
var base64 = require('./utils/base64');
var uri = require('./utils/uri');
/**
* @fileoverview
* This is a loader for the general purpose tile map editor "Tiled".
*
* This module can load all ".tmx" files even if additionally base64 encoded
* (can be configured in Tiled).
*
* This module loads the whole map definition, including the TileSets with
* all necessary images. For an example on how to render a map loaded with
* this module, see `examples/tiledmap`.
*
* You will typically create a Map instance with `Map(url)` and deal
* with the layers, tilesets, etc. through the Map instance
* instead of loading & creating them yourself.
*
* Only orthogonol maps are supported (no isometric maps).
*
* @see http://www.mapeditor.org/
* @see https://github.com/bjorn/tiled/wiki/TMX-Map-Format
*/
/**
* My code is inspired by:
* * https://bitbucket.org/maikg/tiled2cocos/
* * https://github.com/obiot/melonJS/
*
*/
/**
* A Tiled Map holds all layers defined in the tmx file as well
* as the necessary tiles to render the map.
* @param {String} url Relative or absolute URL to the tmx file
*/
var Map = exports.Map = function(url) {
url = uri.resolve(document.location.href, url);
var xmlDoc = xml.Document.fromURL(url);
var mapNode = xmlDoc.element('map');
/**
* Width of a single tile in pixels
* @type Number
*/
this.tileWidth = mapNode.attribute('tilewidth');
/**
* Height of a single tile in pixels
* @type Number
*/
this.tileHeight = mapNode.attribute('tileheight');
/**
* Width of the map in tiles
* @type Number
*/
this.width = mapNode.attribute('width');
/**
* Height of the map in tiles
* @type Number
*/
this.height = mapNode.attribute('height');
var orientation = mapNode.attribute('orientation');
if (orientation !== 'orthogonal') {
throw new Error('only orthogonol maps supported');
}
/**
* Custom properties of the map
*/
this.properties = {};
setProperties(this.properties, mapNode);
/**
* All tiles of this map.
* @type {TileSet}
*/
this.tiles = new TileSets(mapNode, url);
this.layers = loadLayers(mapNode);
return this;
};
/**
* A Tile. Can not be instantiated. Get a Tile by calling `getTile(gid)`
* on a `TileSets` instance.
*/
var Tile = exports.Tile = function() {
/**
* @type {gamejs.graphics.Surface}
*/
this.surface = null;
/**
* @type {Object}
*/
this.properties = null;
throw new Error('Can not be instantiated.');
};
/**
* A TileSets instance holds all tilesets of a map. This class
* makes it easy to get the image for a certain tile ID. You usually
* don't care about in which specific TileSet an image is so this
* class holds them all and deals with the lookup.
*
* You don't usually create a `TileSets` instance yourself, instead
* it is automatically created and attached to a `Map`.
*/
var TileSets = exports.TileSets = function(mapNode, mapUrl) {
var tileSets = [];
/**
* Retrieve the image for a tile ID (gid).
*
* @param {Number} gid global tile id to retrieve
* @returns {gamejs.graphics.Surface} the Surface for the gid
*/
this.getSurface = function(gid) {
var tile = this.getTile(gid);
return tile && tile.surface || null;
};
/**
* @param {Number} gid global tile id
* @returns {Object} the custom properties of this tile
*/
this.getProperties = function(gid) {
var tile = this.getTile(gid);
return tile && tile.properties || {};
};
/**
* @param {Number} gid global tile id
* @returns {Object} the Tile object for this gid
*/
this.getTile = function(gid) {
var tile = null;
tileSets.some(function(tileSet, idx) {
if (tileSet.firstGid <= gid) {
tile = tileSet.tiles[gid - tileSet.firstGid];
return true;
}
return false;
}, this);
return tile;
};
var loadTileSet = function(tileSetNode) {
var tiles = [];
var tileWidth = tileSetNode.attribute('tilewidth');
var tileHeight = tileSetNode.attribute('tileheight');
var spacing = tileSetNode.attribute('spacing') || 0;
// broken in tiled?
var margin = 0;
var imageNode = tileSetNode.element('image');
var imageAtlasFile = imageNode.attribute('source');
var imageUrl = uri.makeRelative(uri.resolve(mapUrl, imageAtlasFile));
var atlas = gamejs.image.load(imageUrl);
// FIXME set transparency if imageNode.attribute('trans') is set
var tileNodes = tileSetNode.elements('tile');
var dims = atlas.getSize();
var imgSize = new gamejs.Rect([0,0], [tileWidth, tileHeight]);
var idx = 0;
var y = 0;
while (y + tileHeight <= dims[1]) {
var x = 0;
while (x + tileWidth <= dims[0]) {
var tileImage = new gamejs.graphics.Surface(tileWidth, tileHeight);
var rect = new gamejs.Rect([x, y], [tileWidth, tileHeight]);
tileImage.blit(atlas, imgSize, rect);
var tileProperties = {};
/* jshint ignore:start */
// function within loop
tileNodes.some(function(tileNode) {
if (tileNode.attribute('id') === idx) {
setProperties(tileProperties, tileNode);
return true;
}
}, this);
/* jshint ignore:end */
tiles.push({
surface: tileImage,
properties: tileProperties
});
x += tileWidth + spacing;
idx++;
}
y += tileHeight + spacing;
}
return tiles;
};
/**
*
* constructor
**/
mapNode.elements('tileset').forEach(function(tileSetNode) {
var firstGid = tileSetNode.attribute('firstgid');
var externalSource = tileSetNode.attribute('source');
if (externalSource) {
var tileSetDocument = xml.Document.fromURL(uri.resolve(mapUrl, externalSource));
tileSetNode = tileSetDocument.element('tileset');
}
tileSets.push({
tiles: loadTileSet(tileSetNode),
firstGid: firstGid
});
});
tileSets.reverse();
return this;
};
/**
* loadLayers
*/
var H_FLIP = 0x80000000;
var V_FLIP = 0x40000000;
var loadLayers = function(mapNode) {
var layers = [];
var getGids = function(layerNode) {
var dataNode = layerNode.element('data');
var encoding = dataNode.attribute('encoding');
var compression = dataNode.attribute('compression');
var data = "";
dataNode.children().forEach(function(textNode) {
data += textNode.value();
});
var byteData = [];
if (encoding === 'base64') {
if (compression) {
throw new Error('Compression of map data unsupported');
}
byteData = base64.decodeAsArray(data, 4);
} else if (encoding === 'csv') {
data.trim().split('\n').forEach(function(row) {
row.split(',', width).forEach(function(entry) {
byteData.push(parseInt(entry, 10));
});
});
} else {
// FIXME individual XML tile elements
throw new Error('individual tile format not supported');
}
return byteData;
};
var width = mapNode.attribute('width');
var height = mapNode.attribute('height');
mapNode.elements('layer').forEach(function(layerNode) {
// create empty gid matrix
var gidMatrix = [];
var i = height;
while (i-->0) {
var j = width;
gidMatrix[i] = [];
while (j-->0) {
gidMatrix[i][j] = 0;
}
}
getGids(layerNode).forEach(function(gid, idx) {
// FIXME flipX/Y currently ignored
var flipX = gid & H_FLIP;
var flipY = gid & V_FLIP;
// clear flags
gid &= ~(H_FLIP | V_FLIP);
gidMatrix[parseInt(idx / width, 10)][parseInt(idx % width, 10)] = gid;
});
layers.push({
gids: gidMatrix,
opacity: layerNode.attribute('opacity'),
visible: layerNode.attribute('visible'),
properties: setProperties({}, layerNode)
});
});
return layers;
};
/**
* set generic <properties><property name="" value="">... on given object
*/
var setProperties = function(object, node) {
var props = node.element('properties');
if (!props) {
return;
}
props.elements('property').forEach(function(propertyNode) {
var name = propertyNode.attribute('name');
var value = propertyNode.attribute('value');
object[name] = value;
});
return object;
};
/**
* FIXME explain viewRect (change it to sroll!) and image, mapImage and how to update, redraw
*/
var MapView = exports.MapView = function(map) {
this.timeout = 0;
this.layerViews = map.layers.map(function(layer) {
return new LayerView(layer, {
tileWidth: map.tileWidth,
tileHeight: map.tileHeight,
width: map.width,
height: map.height,
tiles: map.tiles
});
});
this.viewRect = new gamejs.Rect([0,0], [map.width * map.tileWidth, map.height*map.tileWidth]);
this.image = new gamejs.graphics.Surface([this.viewRect.width, this.viewRect.height]);
this.mapImage = this.image.clone();
this.redraw();
return this;
};
MapView.prototype.redraw = function() {
this.layerViews.forEach(function(layer) {
layer.draw(this.mapImage);
}, this);
};
MapView.prototype.draw = function(display, offset) {
display.blit(this.mapImage, offset || [0,0], this.viewRect);
};
/**
* LayerView
* Renders the layer to a big surface.
*/
var LayerView = exports.LayerView = function(layer, opts) {
this.draw = function(display) {
display.blit(this.surface);
};
/**
* constructor
*/
this.surface = new gamejs.graphics.Surface(opts.width * opts.tileWidth, opts.height * opts.tileHeight);
this.surface.setAlpha(layer.opacity);
/**
* Note how below we look up the "gid" of the tile images in the TileSet from the Map
* ('opt.tiles') to get the actual Surfaces.
*/
layer.gids.forEach(function(row, i) {
row.forEach(function(gid, j) {
if (gid ===0) {
return;
}
var tileSurface = opts.tiles.getSurface(gid);
if (tileSurface) {
this.surface.blit(tileSurface,
new gamejs.Rect([j * opts.tileWidth, i * opts.tileHeight], [opts.tileWidth, opts.tileHeight])
);
} else {
gamejs.log('no gid ', gid, i, j, 'layer', i);
}
}, this);
}, this);
return this;
};
},{"../gamejs":25,"./utils/base64":46,"./utils/objects":48,"./utils/uri":50,"./utils/xml":51}],44:[function(require,module,exports){
/**
* @fileoverview
* Only used by GameJs internally to provide a game loop.
* @ignore
*/
var Callback = require('./utils/callback').Callback;
var TIMER_LASTCALL = null;
var STARTTIME = null;
/** @ignore **/
var _CALLBACKS = exports._CALLBACKS = [];
// `window` is not accessible in webworker (would lead to TypeError)
// @@ this cross-browser fuckery has to go away ASAP.
var reqAnimationFrame = typeof(window) != 'undefined' ?
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
null : null;
var reqAniFrameRecursive = function() {
perInterval();
reqAnimationFrame(reqAniFrameRecursive);
};
var triggerCallbacks = function(msDuration) {
_CALLBACKS.forEach(function(c) {
c.trigger(msDuration);
});
};
/**
* @ignore
*/
exports.init = function() {
STARTTIME = Date.now();
if (reqAnimationFrame) {
reqAnimationFrame(reqAniFrameRecursive);
} else {
setInterval(perInterval, 10);
}
return;
};
var perInterval = function() {
var msNow = Date.now();
triggerCallbacks(msNow - (TIMER_LASTCALL || msNow));
TIMER_LASTCALL = msNow;
return;
};
},{"./utils/callback":47}],45:[function(require,module,exports){
/**
* @fileoverview Utility functions for working with Obiects
* @param {Object} item
* @param {Array} array
* @param {Object} returns removed item or null
*/
exports.remove = function(item, array) {
var index = array.indexOf(item);
if (index !== -1) {
return array.splice(array.indexOf(item), 1);
}
return null;
};
/**
* Shuffles the array *in place*.
* @see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
*/
exports.shuffle = function(array) {
var len = array.length -1;
for (var i = len; i > 0; i--) {
var idx = parseInt(Math.random() * (i + 1), 10);
var item = array[i];
array[i] = array[idx];
array[idx] = item;
}
return array;
};
},{}],46:[function(require,module,exports){
/**
* @fileoverview
* Base64 encode / decode
* @author http://www.webtoolkit.info
*/
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/**
* Decodes a base64 encoded string to a string.
*/
var decode = exports.decode = function(input) {
var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(String.fromCharCode(chr1));
if (enc3 != 64) {
output.push(String.fromCharCode(chr2));
}
if (enc4 != 64) {
output.push(String.fromCharCode(chr3));
}
}
output = output.join('');
return output;
};
/**
* Decodes a base64 encoded string into a byte array
* @param {String} input
* @param {Array} bytes bytes per character, defaults to 1
*/
exports.decodeAsArray = function(input, bytes) {
bytes = bytes || 1;
var decoded = decode(input);
var len = decoded.length / bytes;
var array = [];
var i,j;
for (i=0; i< len; i++) {
array[i] = 0;
for (j = bytes - 1; j >=0; --j) {
array[i] += decoded.charCodeAt((i * bytes) + j) << (j <<3 );
}
}
return array;
}
;
},{}],47:[function(require,module,exports){
/**
* @fileoverview
* Manage a callback with invocation scope. This is used internally by GameJs but might be useful for others.
*/
/**
* @param {Function} callback
* @param {Object} scope with which the callback will be triggered
*/
var Callback = exports.Callback = function(fn, scope) {
this.fn = fn;
this.fnScope = scope || {};
return this;
};
/**
* Any arguments passed to `trigger` will be passed to the callback.
*/
Callback.prototype.trigger = function() {
this.fn.apply(this.fnScope, arguments);
};
},{}],48:[function(require,module,exports){
/**
* @fileoverview Utility functions for working with Objects
*/
/**
* Put a prototype into the prototype chain of another prototype.
* @param {Object} subClass
* @param {Object} superClass
*/
exports.extend = function(subClass, superClass) {
if (subClass === undefined) {
throw new Error('unknown subClass');
}
if (superClass === undefined) {
throw new Error('unknown superClass');
}
var F;
/* jshint ignore:start */
F = new Function();
/* jshint ignore:start */
F.prototype = superClass.prototype;
/* jshint ignore:end */
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superClass = superClass.prototype;
subClass.superConstructor = superClass;
return;
};
/**
* Creates a new object as the as the keywise union of the provided objects.
* Whenever a key exists in a later object that already existed in an earlier
* object, the according value of the earlier object takes precedence.
* @param {Object} obj... The objects to merge
*/
exports.merge = function() {
var result = {};
var i, property;
for (i = arguments.length; i > 0; --i) {
var obj = arguments[i - 1];
for (property in obj) {
result[property] = obj[property];
}
}
return result;
};
/**
* fallback for Object.keys
* @param {Object} obj
* @returns {Array} list of own properties
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
*/
var keys = exports.keys = function(obj) {
if (Object.keys) {
return Object.keys(obj);
}
var ret=[],p;
for (p in obj) {
if(Object.prototype.hasOwnProperty.call(obj, p)) {
ret.push(p);
}
}
return ret;
};
/**
* Create object accessors
* @param {Object} object The object on which to define the property
* @param {String} name name of the property
* @param {Function} get
* @param {Function} set
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
*/
var accessor = exports.accessor = function(object, name, get, set) {
// ECMA5
if (Object.defineProperty !== undefined) {
Object.defineProperty(object, name, {
get: get,
set: set
});
// non-standard
} else if (Object.prototype.__defineGetter__ !== undefined) {
object.__defineGetter__(name, get);
if (set) {
object.__defineSetter__(name, set);
}
}
return;
};
/**
* @param {Object} object The object on which to define or modify properties.
* @param {Object} props An object whose own enumerable properties constitute descriptors for the properties to be defined or modified.
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties
*/
exports.accessors = function(object, props) {
keys(props).forEach(function(propKey) {
accessor(object, propKey, props[propKey].get, props[propKey].set);
});
return;
};
},{}],49:[function(require,module,exports){
/**
* @fileoverview Working with strings
*/
/**
* Get the longest common segment that two strings
* have in common, starting at the beginning of the string
* @param {String} str1 a string
* @param {String} str2 another string
* @returns {String} the longest common segment
*/
exports.getCommonPrefix = function getCommonPrefix(str1, str2) {
if (str1 === null || str2 === null) {
return null;
} else if (str1.length > str2.length && str1.indexOf(str2) === 0) {
return str2;
} else if (str2.length > str1.length && str2.indexOf(str1) === 0) {
return str1;
}
var length = Math.min(str1.length, str2.length);
var i;
for (i = 0; i < length; i++) {
if (str1[i] != str2[i]) {
return str1.slice(0, i);
}
}
return str1.slice(0, length);
};
},{}],50:[function(require,module,exports){
/**
* @fileoverview Utilies for URI handling.
*
*/
var URI_REGEX = new RegExp(
'^' +
'(?:' +
'([^:/?#.]+)' + // scheme - ignore special characters
// used by other URL parts such as :,
// ?, /, #, and .
':)?' +
'(?://' +
'(?:([^/?#]*)@)?' + // userInfo
'([\\w\\d\\-\\u0100-\\uffff.%]*)' + // domain - restrict to letters,
// digits, dashes, dots, percent
// escapes, and unicode characters.
'(?::([0-9]+))?' + // port
')?' +
'([^?#]+)?' + // path
'(?:\\?([^#]*))?' + // query
'(?:#(.*))?' + // fragment
'$');
/**
* Resolve path against URI.
*
* @param {String} uri
* @param {String} path to resolve
*/
var resolve = exports.resolve = function(uri, path) {
var m = match(uri);
var n = match(path);
var host = m[1] + '://' + m[3];
if (n[1]) {
return path;
}
if (m[4]) {
host = host + ":" + m[4];
}
var absolutePath = m[5];
if (path.charAt(0) !== '/') {
var lastSlashIndex = absolutePath.lastIndexOf('/');
absolutePath = absolutePath.substr(0, lastSlashIndex + 1) + path;
} else {
absolutePath = path;
}
return host + removeDotSegments(absolutePath);
};
/**
* Try to match an URI against a regex returning the following
* capture groups:
* $1 = http scheme
* $2 = <undefined> userInfo -\
* $3 = www.ics.uci.edu domain | authority
* $4 = <undefined> port -/
* $5 = /pub/ietf/uri/ path
* $6 = <undefined> query without ?
* $7 = Related fragment without #
*
* @param {String} uri
*/
var match = exports.match = function(uri) {
return uri.match(URI_REGEX);
};
/**
* Make an absolute URI relative to document.location.href
* @param {String} uri
* @returns The relative URI or the unchanged URI if it's not
* possible to make it relative to the path of document.location.href.
*/
var makeRelative = exports.makeRelative = function(uri) {
var docLocPath = resolve(document.location.href, './');
if (uri.indexOf(docLocPath) === 0) {
uri = './' + uri.substring(docLocPath.length);
}
return uri;
};
/**
* Removes dot segments in given path component
*/
var removeDotSegments = function(path) {
if (path == '..' || path == '.') {
return '';
}
var leadingSlash = path.indexOf('/') > -1;
var segments = path.split('/');
var out = [];
var pos;
for (pos = 0; pos < segments.length; ) {
var segment = segments[pos++];
if (segment == '.') {
if (leadingSlash && pos == segments.length) {
out.push('');
}
} else if (segment == '..') {
if (out.length > 1 || out.length !== 1 && out[0] !== '') {
out.pop();
}
if (leadingSlash && pos == segments.length) {
out.push('');
}
} else {
out.push(segment);
leadingSlash = true;
}
}
return out.join('/');
};
},{}],51:[function(require,module,exports){
/**
* @fileoverview
*
* Provides facilities for parsing a xml String.
*
* You will typically get a `gamejs.xml.Document` instance
* by loading the data with one of the two static
* `Document.fromString(string)` or `Document.fromUrl(url)`.
* Querying for `elements(name)` or `children()` will return a
* new `gamejs.xml.Document` matching your result (or null).
*
* Use `attributes(name)` and `value()` to get the data stored
* in the XML Document.
*/
/**
* XMLParser
*/
var Parser = exports.Parser = function() {
var xmlDoc = null;
var parser = new DOMParser();
this.parseFromString = function(xmlString) {
xmlDoc = parser.parseFromString(xmlString, 'text/xml');
return xmlDoc;
};
return this;
};
/**
* Instantiate with the static functions `Document.fromString()` and `fromURL()`.
*/
var Document = exports.Document = function(xmlDocument) {
if (!xmlDocument || (!xmlDocument instanceof XMLDocument) ) {
throw new Error('Need a valid xmlDocument.');
}
/** @ignore **/
this._xmlDocument = xmlDocument;
return this;
};
/**
* Returns the first element in the current document whose tag-name matches
* the given 'name'.
* @returns gamejs.xml.Document
*/
Document.prototype.element = function(name) {
var elem = this._xmlDocument.getElementsByTagName(name)[0];
return elem && new Document(elem) || null;
};
/**
* Returns all elements in the current document whose tag-name matches
* the given 'name'.
* @returns an Array of gamejs.xml.Document
*/
Document.prototype.elements = function(name) {
var elems = this._xmlDocument.getElementsByTagName(name);
return Array.prototype.slice.apply(elems, [0]).map(function(elem) {
return new Document(elem);
});
};
/**
* Returns the attribute value of this document.
*
* @returns String
*/
Document.prototype.attribute = function(name) {
var attributeValue = this._xmlDocument.getAttribute(name);
attributeValue = attributeValue ? attributeValue.trim() : null;
if (attributeValue === null) {
return null;
}
if (attributeValue.toLowerCase() === 'true') {
return true;
}
if (attributeValue.toLowerCase() === 'false') {
return false;
}
var attributeIntValue = parseInt(attributeValue, 10);
var attributeFloatValue = parseFloat(attributeValue, 10);
if (!isNaN(attributeIntValue)) {
if (attributeFloatValue !== attributeIntValue) {
return attributeFloatValue;
}
return attributeIntValue;
}
return attributeValue;
};
/**
* Returns the nodevalue of the current xml document
* @returns String
*/
Document.prototype.value = function() {
return this._xmlDocument.nodeValue;
};
/**
* Returns all children of this xml document
* @returns Array of gamejs.xml.Document
*/
Document.prototype.children = function() {
return Array.prototype.slice.apply(this._xmlDocument.childNodes, [0]).map(function(cNode) {
return new Document(cNode);
});
};
/**
* @returns gamejs.xml.Document
*/
Document.fromString = function(xmlString) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xmlString, 'text/xml');
return new Document(xmlDoc);
};
/**
* @returns gamejs.xml.Document
*/
Document.fromURL = function(url) {
var response = new XMLHttpRequest();
response.open('GET', url, false);
response.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
response.setRequestHeader('Content-Type', 'text/xml');
response.overrideMimeType('text/xml');
response.send();
return new Document(response.responseXML);
};
},{}],52:[function(require,module,exports){
var CommandsPlugin, ItemPile, shellwords,
slice = [].slice,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
shellwords = require('shellwords');
ItemPile = require('itempile');
module.exports = function(game, opts) {
return new CommandsPlugin(game, opts);
};
module.exports.pluginInfo = {
loadAfter: ['voxel-console']
};
CommandsPlugin = (function() {
function CommandsPlugin(game1, opts) {
var ref, ref1;
this.game = game1;
this.console = (ref = this.game.plugins) != null ? ref.get('voxel-console') : void 0;
if (this.console == null) {
throw new Error('voxel-commands requires voxel-console');
}
this.registry = (ref1 = this.game.plugins) != null ? ref1.get('voxel-registry') : void 0;
if (this.registry == null) {
throw new Error('voxel-commands requires voxel-registry');
}
this.isConnectedToServer = false;
this.usages = {
pos: "x y z",
home: "",
item: "name [count [tags]]",
clear: "",
block: "name [data]",
plugins: "",
enable: "plugin",
disable: "plugin"
};
this.handlers = {
undefined: function() {
var args, command;
command = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return this.console.log("Invalid command " + command + " " + (args.join(' ')));
},
help: function() {
var name, ref2, results, usage;
this.console.log("Available commands:");
results = [];
for (name in this.usages) {
usage = (ref2 = this.usages[name]) != null ? ref2 : '';
results.push(this.console.log("." + name + " " + usage));
}
return results;
},
plugins: function() {
var list, ref2;
list = (ref2 = this.game.plugins) != null ? ref2.list() : void 0;
return this.console.log(("Enabled plugins (" + list.length + "): ") + list.join(' '));
},
enable: function(name) {
var ref2;
if ((ref2 = this.game.plugins) != null ? ref2.enable(name) : void 0) {
return this.console.log("Enabled plugin: " + name);
} else {
return this.console.log("Failed to enable plugin: " + name);
}
},
disable: function(name) {
if (this.game.plugins.disable(name)) {
return this.console.log("Disabled plugin: " + name);
} else {
return this.console.log("Failed to disable plugin: " + name);
}
},
pos: function(x, y, z) {
var player, ref2;
player = (ref2 = this.game.plugins) != null ? ref2.get('voxel-player') : void 0;
if (player) {
player.moveTo(x, y, z);
return this.console.log([player.position.x, player.position.y, player.position.z]);
}
},
home: function() {
var ref2, ref3;
return (ref2 = this.game.plugins) != null ? (ref3 = ref2.get('voxel-player')) != null ? ref3.home() : void 0 : void 0;
},
item: function(name, count, tagsStr) {
var carry, e, pile, props, ref2, tags;
props = this.registry.getItemProps(name);
if (props == null) {
this.console.log("No such item: " + name);
return;
}
if (tagsStr != null) {
try {
tags = JSON.parse(tagsStr);
} catch (_error) {
e = _error;
this.console.log("Invalid JSON " + tagsStr + ": " + e);
return;
}
} else {
tags = void 0;
}
if (count == null) {
count = 1;
}
count = parseInt(count, 10);
if (isNaN(count)) {
count = 1;
}
pile = new ItemPile(name, count, tags);
carry = (ref2 = this.game.plugins) != null ? ref2.get('voxel-carry') : void 0;
if (carry) {
carry.inventory.give(pile);
return this.console.log("Gave " + name + " x " + count + " " + (tags != null ? JSON.stringify(tags) : ''));
}
},
clear: function() {
var carry, ref2;
carry = (ref2 = this.game.plugins) != null ? ref2.get('voxel-carry') : void 0;
if (carry) {
carry.inventory.clear();
return this.console.log("Cleared inventory");
}
},
block: function(name, data) {
var blockdata, dataInfo, hit, index, oldData, oldIndex, oldName, reachDistance, ref2, ref3, x, y, z;
if (name != null) {
index = this.registry.getBlockIndex(name);
if (index == null) {
this.console.log("No such block: " + name);
return;
}
}
reachDistance = 8;
hit = this.game.raycastVoxels(this.game.cameraPosition(), this.game.cameraVector(), reachDistance);
if (!hit) {
this.console.log("No block targetted");
return;
}
ref2 = hit.voxel, x = ref2[0], y = ref2[1], z = ref2[2];
oldIndex = hit.value;
oldName = this.registry.getBlockName(oldIndex);
if (name != null) {
this.game.setBlock(hit.voxel, index);
}
blockdata = (ref3 = this.game.plugins) != null ? ref3.get('voxel-blockdata') : void 0;
if (blockdata != null) {
oldData = blockdata.get(x, y, z);
if (data != null) {
blockdata.set(x, y, z, data);
}
}
dataInfo = "";
if (oldData != null) {
dataInfo = (JSON.stringify(oldData)) + " -> ";
}
if (data == null) {
data = oldData;
}
if (oldData != null) {
dataInfo += JSON.stringify(data);
}
if (name == null) {
name = oldName;
}
if (index == null) {
index = oldIndex;
}
return this.console.log("Set (" + x + ", " + y + ", " + z + ") " + oldName + "/" + oldIndex + " -> " + name + "/" + index + " " + dataInfo);
}
};
this.handlers.p = this.handlers.position = this.handlers.tp = this.handlers.pos;
this.handlers.i = this.handlers.give = this.handlers.item;
this.handlers.b = this.handlers.setblock = this.handlers.set = this.handlers.block;
this.enable();
}
CommandsPlugin.prototype.process = function(input) {
var args, command, connection, handler, ref, ref1, words;
if (input.indexOf('.') !== 0) {
if (!this.isConnectedToServer) {
this.console.log(input);
connection = (ref = this.game.plugins) != null ? (ref1 = ref.get('voxel-client')) != null ? ref1.connection : void 0 : void 0;
if (connection != null) {
connection.emit('chat', {
message: input
});
} else {
this.console.log('Not connected to server. Type .help for commands');
}
}
return;
}
input = input.substring(1);
words = shellwords.split(input);
command = words[0], args = 2 <= words.length ? slice.call(words, 1) : [];
handler = this.handlers[command];
if (handler == null) {
handler = this.handlers.undefined;
args.unshift(command);
}
return handler.apply(this, args);
};
CommandsPlugin.prototype.enable = function() {
var ref, ref1, ref2;
if ((ref = this.console.widget) != null) {
ref.on('input', this.onInput = (function(_this) {
return function(input) {
return _this.process(input);
};
})(this));
}
return (ref1 = this.game.plugins) != null ? (ref2 = ref1.get('voxel-client')) != null ? ref2.connection.on('chat', this.onChat = (function(_this) {
return function(input) {
var ref3;
return _this.console.log((ref3 = input.message) != null ? ref3 : input);
};
})(this)) : void 0 : void 0;
};
CommandsPlugin.prototype.disable = function() {
var ref, ref1;
this.console.widget.removeListener('input', this.onInput);
return (ref = this.game.plugins) != null ? (ref1 = ref.get('voxel-client')) != null ? ref1.connection.removeListener('chat', this.onChat) : void 0 : void 0;
};
CommandsPlugin.prototype.registerCommand = function(name, handler, usage, help) {
if (indexOf.call(this.handlers, name) >= 0) {
throw new Error("voxel-commands duplicate command registration: " + name + " for " + handler);
}
this.handlers[name] = handler;
return this.usages[name] = usage + " -- " + help;
};
CommandsPlugin.prototype.unregisterCommand = function(name, handler) {
if (this.handlers[name] !== handler) {
throw new Error("voxel-commands attempted to unregister mismatched command: " + name + " was " + this.handlers[name] + " not " + handler);
}
delete this.handlers[name];
return delete this.usages[name];
};
return CommandsPlugin;
})();
},{"itempile":53,"shellwords":58}],53:[function(require,module,exports){
"use strict";
var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { _arr.push(_step.value); if (i && _arr.length === i) break; } return _arr; } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } };
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
var deepEqual = require("deep-equal");
var cloneObject = require("clone");
var ItemPile = (function () {
function ItemPile(item) {
var count = arguments[1] === undefined ? 1 : arguments[1];
var tags = arguments[2] === undefined ? {} : arguments[2];
_classCallCheck(this, ItemPile);
this.item = typeof item === "string" ? ItemPile.itemFromString(item) : item;
this.count = count;
this.tags = tags;
}
_createClass(ItemPile, {
clone: {
value: function clone() {
return new ItemPile(this.item, this.count, cloneObject(this.tags, false));
}
},
hasTags: {
value: function hasTags() {
return Object.keys(this.tags).length !== 0; // not "{}"
}
},
matchesType: {
value: function matchesType(itemPile) {
return this.item === itemPile.item;
}
},
matchesTypeAndCount: {
value: function matchesTypeAndCount(itemPile) {
return this.item === itemPile.item && this.count === itemPile.count;
}
},
matchesTypeAndTags: {
value: function matchesTypeAndTags(itemPile) {
return this.item === itemPile.item && deepEqual(this.tags, itemPile.tags, { strict: true });
}
},
matchesAll: {
value: function matchesAll(itemPile) {
return this.matchesTypeAndCount(itemPile) && deepEqual(this.tags, itemPile.tags, { strict: true });
}
},
canPileWith: {
// can this pile be merged with another?
value: function canPileWith(itemPile) {
if (itemPile.item !== this.item) {
return false;
}if (itemPile.count === 0 || this.count === 0) {
return true;
} // (special case: can always merge with 0-size pile of same item, regardless of tags - for placeholder slots)
if (itemPile.hasTags() || this.hasTags()) {
return false;
} // any tag data makes unpileable
return true;
}
},
mergePile: {
// combine two piles if possible, altering both this and argument pile
// returns count of items that didn't fit
value: function mergePile(itemPile) {
if (!this.canPileWith(itemPile)) {
return false;
}itemPile.count = this.increase(itemPile.count);
return itemPile.count;
}
},
increase: {
// increase count by argument, returning number of items that didn't fit
value: function increase(n) {
var _tryAdding = this.tryAdding(n);
var _tryAdding2 = _slicedToArray(_tryAdding, 2);
var newCount = _tryAdding2[0];
var excessCount = _tryAdding2[1];
this.count = newCount;
return excessCount;
}
},
decrease: {
// decrease count by argument, returning number of items removed
value: function decrease(n) {
var _trySubtracting = this.trySubtracting(n);
var _trySubtracting2 = _slicedToArray(_trySubtracting, 2);
var removedCount = _trySubtracting2[0];
var remainingCount = _trySubtracting2[1];
this.count = remainingCount;
return removedCount;
}
},
tryAdding: {
// try combining count of items up to max pile size, returns [newCount, excessCount]
value: function tryAdding(n) {
// special case: infinite incoming count sets pile to infinite, even though >maxPileSize
// TODO: option to disable infinite piles? might want to add only up to 64 etc. (ref GH-2)
if (n === Infinity) {
return [Infinity, 0];
}var sum = this.count + n;
if (sum > ItemPile.maxPileSize && this.count !== Infinity) {
// (special case: infinite destination piles never overflow)
return [ItemPile.maxPileSize, sum - ItemPile.maxPileSize]; // overflowing pile
} else {
return [sum, 0]; // added everything they wanted
}
}
},
trySubtracting: {
// try removing a finite count of items, returns [removedCount, remainingCount]
value: function trySubtracting(n) {
var difference = this.count - n;
if (difference < 0) {
return [this.count, n - this.count]; // didn't have enough
} else {
return [n, this.count - n]; // had enough, some remain
}
}
},
splitPile: {
// remove count of argument items, returning new pile of those items which were split off
value: function splitPile(n) {
if (n === 0) {
return false;
}if (n < 0) {
// negative count = all but n
n = this.count + n;
} else if (n < 1) {
// fraction = fraction
n = Math.ceil(this.count * n);
}
if (n > this.count) {
return false;
}if (n !== Infinity) this.count -= n; // (subtract, but avoid Infinity - Infinity = NaN)
return new ItemPile(this.item, n, cloneObject(this.tags, false));
}
},
toString: {
value: function toString() {
if (this.hasTags()) {
return "" + this.count + ":" + this.item + " " + JSON.stringify(this.tags);
} else {
return "" + this.count + ":" + this.item;
}
}
}
}, {
maxPileSize: {
// maximum size items should pile
get: function () {
return 64;
}
},
itemFromString: {
// convert item<->string; change these to use non-string items
value: function itemFromString(s) {
if (s instanceof ItemPile) {
return s;
}return !s ? "" : s;
}
},
itemToString: {
value: function itemToString(item) {
return "" + item;
}
},
fromString: {
value: function fromString(s) {
var a = s.match(/^([^:]+):([^ ]+) ?(.*)/); // assumptions: positive integral count, item name no spaces
if (!a) {
return undefined;
}
var _a = _slicedToArray(a, 4);
var _ = _a[0];
var countStr = _a[1];
var itemStr = _a[2];
var tagsStr = _a[3];
var count = undefined;
if (countStr === "Infinity") {
count = Infinity;
} else {
count = parseInt(countStr, 10);
}
var item = ItemPile.itemFromString(itemStr);
var tags = undefined;
if (tagsStr && tagsStr.length) {
tags = JSON.parse(tagsStr);
} else {
tags = {};
}
return new ItemPile(item, count, tags);
}
},
fromArray: {
value: function fromArray(_ref) {
var _ref2 = _slicedToArray(_ref, 3);
var item = _ref2[0];
var count = _ref2[1];
var tags = _ref2[2];
return new ItemPile(item, count, tags);
}
},
fromArrayIfArray: {
value: function fromArrayIfArray(a) {
if (Array.isArray(a)) {
return ItemPile.fromArray(a);
} else {
return a;
}
}
}
});
return ItemPile;
})();
module.exports = ItemPile;
},{"clone":54,"deep-equal":55}],54:[function(require,module,exports){
(function (Buffer){
var clone = (function() {
'use strict';
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
*/
function clone(parent, circular, depth, prototype) {
var filter;
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
filter = circular.filter;
circular = circular.circular
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth == 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
child = new Buffer(parent.length);
parent.copy(child);
return child;
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
for (var i in parent) {
var attrs;
if (proto) {
attrs = Object.getOwnPropertyDescriptor(proto, i);
}
if (attrs && attrs.set == null) {
continue;
}
child[i] = _clone(parent[i], depth - 1);
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
};
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
};
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
};
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
};
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
};
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
if (typeof module === 'object' && module.exports) {
module.exports = clone;
}
}).call(this,require("buffer").Buffer)
},{"buffer":2}],55:[function(require,module,exports){
var pSlice = Array.prototype.slice;
var objectKeys = require('./lib/keys.js');
var isArguments = require('./lib/is_arguments.js');
var deepEqual = module.exports = function (actual, expected, opts) {
if (!opts) opts = {};
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
// 7.3. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
return opts.strict ? actual === expected : actual == expected;
// 7.4. For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected, opts);
}
}
function isUndefinedOrNull(value) {
return value === null || value === undefined;
}
function isBuffer (x) {
if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;
if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
return false;
}
if (x.length > 0 && typeof x[0] !== 'number') return false;
return true;
}
function objEquiv(a, b, opts) {
var i, key;
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return deepEqual(a, b, opts);
}
if (isBuffer(a)) {
if (!isBuffer(b)) {
return false;
}
if (a.length !== b.length) return false;
for (i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
try {
var ka = objectKeys(a),
kb = objectKeys(b);
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!deepEqual(a[key], b[key], opts)) return false;
}
return typeof a === typeof b;
}
},{"./lib/is_arguments.js":56,"./lib/keys.js":57}],56:[function(require,module,exports){
var supportsArgumentsClass = (function(){
return Object.prototype.toString.call(arguments)
})() == '[object Arguments]';
exports = module.exports = supportsArgumentsClass ? supported : unsupported;
exports.supported = supported;
function supported(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
};
exports.unsupported = unsupported;
function unsupported(object){
return object &&
typeof object == 'object' &&
typeof object.length == 'number' &&
Object.prototype.hasOwnProperty.call(object, 'callee') &&
!Object.prototype.propertyIsEnumerable.call(object, 'callee') ||
false;
};
},{}],57:[function(require,module,exports){
exports = module.exports = typeof Object.keys === 'function'
? Object.keys : shim;
exports.shim = shim;
function shim (obj) {
var keys = [];
for (var key in obj) keys.push(key);
return keys;
}
},{}],58:[function(require,module,exports){
// Generated by CoffeeScript 1.3.3
(function() {
var scan;
scan = function(string, pattern, callback) {
var match, result;
result = "";
while (string.length > 0) {
match = string.match(pattern);
if (match) {
result += string.slice(0, match.index);
result += callback(match);
string = string.slice(match.index + match[0].length);
} else {
result += string;
string = "";
}
}
return result;
};
exports.split = function(line) {
var field, words;
if (line == null) {
line = "";
}
words = [];
field = "";
scan(line, /\s*(?:([^\s\\\'\"]+)|'((?:[^\'\\]|\\.)*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|$)?/, function(match) {
var dq, escape, garbage, raw, seperator, sq, word;
raw = match[0], word = match[1], sq = match[2], dq = match[3], escape = match[4], garbage = match[5], seperator = match[6];
if (garbage != null) {
throw new Error("Unmatched quote");
}
field += word || (sq || dq || escape).replace(/\\(?=.)/, "");
if (seperator != null) {
words.push(field);
return field = "";
}
});
if (field) {
words.push(field);
}
return words;
};
exports.escape = function(str) {
if (str == null) {
str = "";
}
if (str == null) {
return "''";
}
return str.replace(/([^A-Za-z0-9_\-.,:\/@\n])/g, "\\$1").replace(/\n/g, "'\n'");
};
}).call(this);
},{}],59:[function(require,module,exports){
var Console, ConsoleWidget, Modal,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
Modal = require('voxel-modal');
ConsoleWidget = require('console-widget');
module.exports = function(game, opts) {
return new Console(game, opts);
};
module.exports.pluginInfo = {
loadAfter: ['voxel-keys']
};
Console = (function(superClass) {
extend(Console, superClass);
function Console(game1, opts1) {
var base, ref, widgetOpts;
this.game = game1;
this.opts = opts1;
if (!this.game.isClient) {
return;
}
if ((base = this.opts).includeTextBindings == null) {
base.includeTextBindings = {
'console': void 0,
console2: '/',
console3: '.'
};
}
widgetOpts = this.opts;
widgetOpts.closeKeys = [];
this.widget = ConsoleWidget(widgetOpts);
this.keys = (function() {
if ((ref = game.plugins.get('voxel-keys')) != null) {
return ref;
} else {
throw new Error('voxel-console requires voxel-keys plugin');
}
})();
this.bindKeys();
Console.__super__.constructor.call(this, game, {
element: this.widget.containerNode
});
}
Console.prototype.bindKeys = function() {
return ['console', 'console2', 'console3'].forEach((function(_this) {
return function(binding) {
return _this.keys.down.on(binding, function() {
var initialText;
initialText = _this.opts.includeTextBindings[binding];
return _this.open(initialText);
});
};
})(this));
};
Console.prototype.open = function(initialText) {
if (initialText == null) {
initialText = void 0;
}
Console.__super__.open.call(this);
return this.widget.open(initialText);
};
Console.prototype.close = function() {
return Console.__super__.close.call(this);
};
Console.prototype.log = function(text) {
return this.widget.log(text);
};
Console.prototype.logNode = function(node) {
return this.widget.logNode(node);
};
return Console;
})(Modal);
},{"console-widget":60,"voxel-modal":62}],60:[function(require,module,exports){
var ConsoleWidget, EventEmitter, MAX_LINES, vkey,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
EventEmitter = (require('events')).EventEmitter;
vkey = require('vkey');
MAX_LINES = 999;
ConsoleWidget = (function(_super) {
__extends(ConsoleWidget, _super);
function ConsoleWidget(opts) {
var _base, _base1, _base2, _base3, _base4, _base5, _base6;
this.opts = opts;
if (this.opts == null) {
this.opts = {};
}
if ((_base = this.opts).widthPx == null) {
_base.widthPx = 500;
}
if ((_base1 = this.opts).rows == null) {
_base1.rows = 10;
}
if ((_base2 = this.opts).lineHeightPx == null) {
_base2.lineHeightPx = 20;
}
if ((_base3 = this.opts).font == null) {
_base3.font = '12pt Menlo, Courier, \'Courier New\', monospace';
}
if ((_base4 = this.opts).backgroundImage == null) {
_base4.backgroundImage = 'linear-gradient(rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.6) 100%)';
}
if ((_base5 = this.opts).closeKeys == null) {
_base5.closeKeys = ['<escape>'];
}
if ((_base6 = this.opts).hideTimeout == null) {
_base6.hideTimeout = 5000;
}
this.history = [];
this.historyCursor = this.history.length;
this.hideTimer = void 0;
this.createNodes();
}
ConsoleWidget.prototype.show = function() {
this.containerNode.style.visibility = '';
this.containerNode.style.transition = '';
this.containerNode.style.opacity = 1.0;
if (this.hideTimer != null) {
return clearTimeout(this.hideTimer);
}
};
ConsoleWidget.prototype.hide = function() {
return this.containerNode.style.visibility = 'hidden';
};
ConsoleWidget.prototype.open = function(text) {
if (text == null) {
text = void 0;
}
this.show();
if (text != null) {
this.setInput(text);
}
this.registerEvents();
return this.focusInput();
};
ConsoleWidget.prototype.close = function() {
this.unregisterEvents();
return this.hide();
};
ConsoleWidget.prototype.isOpen = function() {
return this.isShown();
};
ConsoleWidget.prototype.isShown = function() {
return this.containerNode.style.visibility !== 'hidden' && this.containerNode.style.opacity | 0 !== 0;
};
ConsoleWidget.prototype.log = function(text) {
return this.logNode(document.createTextNode(text));
};
ConsoleWidget.prototype.logNode = function(node) {
if (!this.isShown()) {
this.show();
this.hideTimer = setTimeout(this.fadeOut.bind(this), this.opts.hideTimeout);
}
this.outputNode.appendChild(node);
this.outputNode.appendChild(document.createElement('br'));
return this.scrollOutput();
};
ConsoleWidget.prototype.fadeOut = function() {
this.containerNode.style.transition = 'opacity linear 1s';
return this.containerNode.style.opacity = 0.0;
};
ConsoleWidget.prototype.focusInput = function() {
return this.inputNode.focus();
};
ConsoleWidget.prototype.setInput = function(text) {
this.inputNode.value = text;
return this.inputNode.setSelectionRange(text.length, text.length);
};
ConsoleWidget.prototype.scrollOutput = function() {
var _base;
return typeof (_base = this.outputNode).scrollByLines === "function" ? _base.scrollByLines(MAX_LINES + 1) : void 0;
};
ConsoleWidget.prototype.createNodes = function() {
this.containerNode = document.createElement('div');
this.containerNode.setAttribute('style', "width: " + this.opts.widthPx + "px; height: " + (this.opts.lineHeightPx * this.opts.rows) + "px; border: 1px solid white; color: white; visibility: hidden; bottom: 0px; position: absolute; font: " + this.opts.font + "; background-image: " + this.opts.backgroundImage + ";");
this.outputNode = document.createElement('div');
this.outputNode.setAttribute('style', "overflow-y: scroll; width: 100%; height: " + (this.opts.lineHeightPx * (this.opts.rows - 1)) + "px;");
this.inputNode = document.createElement('input');
this.inputNode.setAttribute('style', "width: 100%; height: " + this.opts.lineHeightPx + "px; padding: 0px; border: 1px dashed white; background-color: transparent; color: white; font: " + this.opts.font + ";");
this.containerNode.appendChild(this.outputNode);
this.containerNode.appendChild(this.inputNode);
return document.body.appendChild(this.containerNode);
};
ConsoleWidget.prototype.registerEvents = function() {
return document.body.addEventListener('keydown', this.onKeydown = (function(_this) {
return function(ev) {
var key, preventDefault, _base, _base1, _base2, _base3, _base4, _base5, _base6, _base7;
key = vkey[ev.keyCode];
preventDefault = true;
if (key === '<enter>') {
if (_this.inputNode.value.length === 0) {
return;
}
_this.history.push(_this.inputNode.value);
_this.historyCursor = _this.history.length - 1;
_this.emit('input', _this.inputNode.value);
_this.inputNode.value = '';
} else if (key === '<up>') {
if (ev.shiftKey) {
if (typeof (_base = _this.outputNode).scrollByLines === "function") {
_base.scrollByLines(-1);
}
} else {
if (_this.history[_this.historyCursor] != null) {
_this.inputNode.value = _this.history[_this.historyCursor];
}
_this.historyCursor -= 1;
if (_this.historyCursor < 0) {
_this.historyCursor = 0;
}
}
} else if (key === '<down>') {
if (ev.shiftKey) {
if (typeof (_base1 = _this.outputNode).scrollByLines === "function") {
_base1.scrollByLines(1);
}
} else {
if (_this.history[_this.historyCursor] != null) {
_this.inputNode.value = _this.history[_this.historyCursor];
}
_this.historyCursor += 1;
if (_this.historyCursor > _this.history.length - 1) {
_this.historyCursor = _this.history.length - 1;
}
}
} else if (key === '<page-up>') {
if (ev.shiftKey) {
if (typeof (_base2 = _this.outputNode).scrollByLines === "function") {
_base2.scrollByLines(-1);
}
} else if (ev.ctrlKey || ev.metaKey) {
if (typeof (_base3 = _this.outputNode).scrollByLines === "function") {
_base3.scrollByLines(-MAX_LINES);
}
} else {
if (typeof (_base4 = _this.outputNode).scrollByPages === "function") {
_base4.scrollByPages(-1);
}
}
} else if (key === '<page-down>') {
if (ev.shiftKey) {
if (typeof (_base5 = _this.outputNode).scrollByLines === "function") {
_base5.scrollByLines(1);
}
} else if (ev.ctrlKey || ev.metaKey) {
if (typeof (_base6 = _this.outputNode).scrollByLines === "function") {
_base6.scrollByLines(MAX_LINES);
}
} else {
if (typeof (_base7 = _this.outputNode).scrollByPages === "function") {
_base7.scrollByPages(1);
}
}
} else if (_this.opts.closeKeys.indexOf(key) !== -1) {
_this.close();
} else {
preventDefault = false;
}
if (preventDefault) {
return ev.preventDefault();
}
};
})(this));
};
ConsoleWidget.prototype.unregisterEvents = function() {
return document.body.removeEventListener('keydown', this.onKeydown);
};
return ConsoleWidget;
})(EventEmitter);
module.exports = function(opts) {
return new ConsoleWidget(opts);
};
},{"events":6,"vkey":61}],61:[function(require,module,exports){
var ua = typeof window !== 'undefined' ? window.navigator.userAgent : ''
, isOSX = /OS X/.test(ua)
, isOpera = /Opera/.test(ua)
, maybeFirefox = !/like Gecko/.test(ua) && !isOpera
var i, output = module.exports = {
0: isOSX ? '<menu>' : '<UNK>'
, 1: '<mouse 1>'
, 2: '<mouse 2>'
, 3: '<break>'
, 4: '<mouse 3>'
, 5: '<mouse 4>'
, 6: '<mouse 5>'
, 8: '<backspace>'
, 9: '<tab>'
, 12: '<clear>'
, 13: '<enter>'
, 16: '<shift>'
, 17: '<control>'
, 18: '<alt>'
, 19: '<pause>'
, 20: '<caps-lock>'
, 21: '<ime-hangul>'
, 23: '<ime-junja>'
, 24: '<ime-final>'
, 25: '<ime-kanji>'
, 27: '<escape>'
, 28: '<ime-convert>'
, 29: '<ime-nonconvert>'
, 30: '<ime-accept>'
, 31: '<ime-mode-change>'
, 27: '<escape>'
, 32: '<space>'
, 33: '<page-up>'
, 34: '<page-down>'
, 35: '<end>'
, 36: '<home>'
, 37: '<left>'
, 38: '<up>'
, 39: '<right>'
, 40: '<down>'
, 41: '<select>'
, 42: '<print>'
, 43: '<execute>'
, 44: '<snapshot>'
, 45: '<insert>'
, 46: '<delete>'
, 47: '<help>'
, 91: '<meta>' // meta-left -- no one handles left and right properly, so we coerce into one.
, 92: '<meta>' // meta-right
, 93: isOSX ? '<meta>' : '<menu>' // chrome,opera,safari all report this for meta-right (osx mbp).
, 95: '<sleep>'
, 106: '<num-*>'
, 107: '<num-+>'
, 108: '<num-enter>'
, 109: '<num-->'
, 110: '<num-.>'
, 111: '<num-/>'
, 144: '<num-lock>'
, 145: '<scroll-lock>'
, 160: '<shift-left>'
, 161: '<shift-right>'
, 162: '<control-left>'
, 163: '<control-right>'
, 164: '<alt-left>'
, 165: '<alt-right>'
, 166: '<browser-back>'
, 167: '<browser-forward>'
, 168: '<browser-refresh>'
, 169: '<browser-stop>'
, 170: '<browser-search>'
, 171: '<browser-favorites>'
, 172: '<browser-home>'
// ff/osx reports '<volume-mute>' for '-'
, 173: isOSX && maybeFirefox ? '-' : '<volume-mute>'
, 174: '<volume-down>'
, 175: '<volume-up>'
, 176: '<next-track>'
, 177: '<prev-track>'
, 178: '<stop>'
, 179: '<play-pause>'
, 180: '<launch-mail>'
, 181: '<launch-media-select>'
, 182: '<launch-app 1>'
, 183: '<launch-app 2>'
, 186: ';'
, 187: '='
, 188: ','
, 189: '-'
, 190: '.'
, 191: '/'
, 192: '`'
, 219: '['
, 220: '\\'
, 221: ']'
, 222: "'"
, 223: '<meta>'
, 224: '<meta>' // firefox reports meta here.
, 226: '<alt-gr>'
, 229: '<ime-process>'
, 231: isOpera ? '`' : '<unicode>'
, 246: '<attention>'
, 247: '<crsel>'
, 248: '<exsel>'
, 249: '<erase-eof>'
, 250: '<play>'
, 251: '<zoom>'
, 252: '<no-name>'
, 253: '<pa-1>'
, 254: '<clear>'
}
for(i = 58; i < 65; ++i) {
output[i] = String.fromCharCode(i)
}
// 0-9
for(i = 48; i < 58; ++i) {
output[i] = (i - 48)+''
}
// A-Z
for(i = 65; i < 91; ++i) {
output[i] = String.fromCharCode(i)
}
// num0-9
for(i = 96; i < 106; ++i) {
output[i] = '<num-'+(i - 96)+'>'
}
// F1-F24
for(i = 112; i < 136; ++i) {
output[i] = 'F'+(i-111)
}
},{}],62:[function(require,module,exports){
/*jshint globalstrict: true*/
'use strict';
module.exports = Modal;
var ever = require('ever');
function Modal(game, opts)
{
this.game = game;
opts = opts || {};
this.element = opts.element;
if (!this.element) throw new Error('voxel-modal requires "element" option');
// shortcut to close:
// ` (backquote) -- NOT escape due to pointer-lock https://github.com/deathcap/voxel-modal/issues/1
// if you don't have "`", alternative is to click the game canvas (closes automatically on attain)
this.escapeKeys = opts.escapeKeys || [192];
this.isOpen = false;
}
Modal.prototype.open = function() {
if (this.isOpen) return;
var self = this;
if (this.game.shell) {
// exit pointer lock so user can interact with the modal element
this.game.shell.pointerLock = false;
// but re-"want" it so clicking the canvas (outside the modal) will
// activate pointer lock (misleading assignment; requires user interaction)
this.game.shell.pointerLock = true;
// when user successfully acquires pointer lock by clicking the game-shell
// canvas, get out of the way
// TODO: ask game-shell to emit an event for us
var self = this;
ever(document).on('pointerlockchange', self.onPointerLockChange = function() {
if (document.pointerLockElement) {
// pointer lock was acquired - close ourselves, resume gameplay
self.close();
ever(document).removeListener('pointerlockchange', self.onPointerLockChange); // can't seem to use .once with ever (TypeError document removeListener)
}
});
// webkit prefix for older browsers (< Chrome 40)
ever(document).on('webkitpointerlockchange', self.onWKPointerLockChange = function() {
if (document.webkitPointerLockElement) {
self.close();
ever(document).removeListener('webkitpointerlockchange', self.onWKPointerLockChange);
}
});
// moz prefix for Firefox
ever(document).on('mozpointerlockchange', self.onMPointerLockChange = function() {
if (document.mozPointerLockElement) {
self.close();
ever(document).removeListener('mozpointerlockchange', self.onMPointerLockChange);
}
});
} else if (this.game.interact) {
this.game.interact.release();
this.game.interact.on('attain', this.onAttain = function() {
// clicked game, beyond dialog TODO: game-shell needs this, too!
self.close();
});
} else {
throw new Error('voxel-modal requires game-shell or interact');
}
ever(document.body).on('keydown', this.onKeydown = function(ev) {
if (self.escapeKeys.indexOf(ev.keyCode) !== -1) {
self.close();
ev.preventDefault();
}
});
this.element.style.visibility = '';
this.isOpen = true;
};
Modal.prototype.close = function() {
if (!this.isOpen) return;
ever(document.body).removeListener('keydown', this.onKeydown);
this.element.style.visibility = 'hidden';
// resume game interaction
if (this.game.shell) {
this.game.shell.pointerLock = true;
} else if (this.game.interact) {
this.game.interact.removeListener('attain', this.onAttain);
this.game.interact.request();
}
this.isOpen = false;
};
Modal.prototype.toggle = function() {
if (this.isOpen)
this.close();
else
this.open();
};
},{"ever":63}],63:[function(require,module,exports){
var EventEmitter = require('events').EventEmitter;
module.exports = function (elem) {
return new Ever(elem);
};
function Ever (elem) {
this.element = elem;
}
Ever.prototype = new EventEmitter;
Ever.prototype.on = function (name, cb, useCapture) {
if (!this._events) this._events = {};
if (!this._events[name]) this._events[name] = [];
this._events[name].push(cb);
this.element.addEventListener(name, cb, useCapture || false);
return this;
};
Ever.prototype.addListener = Ever.prototype.on;
Ever.prototype.removeListener = function (type, listener, useCapture) {
if (!this._events) this._events = {};
this.element.removeEventListener(type, listener, useCapture || false);
var xs = this.listeners(type);
var ix = xs.indexOf(listener);
if (ix >= 0) xs.splice(ix, 1);
return this;
};
Ever.prototype.removeAllListeners = function (type) {
var self = this;
function removeAll (t) {
var xs = self.listeners(t);
for (var i = 0; i < xs.length; i++) {
self.removeListener(t, xs[i]);
}
}
if (type) {
removeAll(type)
}
else if (self._events) {
for (var key in self._events) {
if (key) removeAll(key);
}
}
return EventEmitter.prototype.removeAllListeners.apply(self, arguments);
}
var initSignatures = require('./init.json');
Ever.prototype.emit = function (name, ev) {
if (typeof name === 'object') {
ev = name;
name = ev.type;
}
if (!isEvent(ev)) {
var type = Ever.typeOf(name);
var opts = ev || {};
if (opts.type === undefined) opts.type = name;
ev = document.createEvent(type + 's');
var init = typeof ev['init' + type] === 'function'
? 'init' + type : 'initEvent'
;
var sig = initSignatures[init];
var used = {};
var args = [];
for (var i = 0; i < sig.length; i++) {
var key = sig[i];
args.push(opts[key]);
used[key] = true;
}
ev[init].apply(ev, args);
// attach remaining unused options to the object
for (var key in opts) {
if (!used[key]) ev[key] = opts[key];
}
}
return this.element.dispatchEvent(ev);
};
function isEvent (ev) {
var s = Object.prototype.toString.call(ev);
return /\[object \S+Event\]/.test(s);
}
Ever.types = require('./types.json');
Ever.typeOf = (function () {
var types = {};
for (var key in Ever.types) {
var ts = Ever.types[key];
for (var i = 0; i < ts.length; i++) {
types[ts[i]] = key;
}
}
return function (name) {
return types[name] || 'Event';
};
})();;
},{"./init.json":64,"./types.json":65,"events":6}],64:[function(require,module,exports){
module.exports={
"initEvent" : [
"type",
"canBubble",
"cancelable"
],
"initUIEvent" : [
"type",
"canBubble",
"cancelable",
"view",
"detail"
],
"initMouseEvent" : [
"type",
"canBubble",
"cancelable",
"view",
"detail",
"screenX",
"screenY",
"clientX",
"clientY",
"ctrlKey",
"altKey",
"shiftKey",
"metaKey",
"button",
"relatedTarget"
],
"initMutationEvent" : [
"type",
"canBubble",
"cancelable",
"relatedNode",
"prevValue",
"newValue",
"attrName",
"attrChange"
]
}
},{}],65:[function(require,module,exports){
module.exports={
"MouseEvent" : [
"click",
"mousedown",
"mouseup",
"mouseover",
"mousemove",
"mouseout"
],
"KeyBoardEvent" : [
"keydown",
"keyup",
"keypress"
],
"MutationEvent" : [
"DOMSubtreeModified",
"DOMNodeInserted",
"DOMNodeRemoved",
"DOMNodeRemovedFromDocument",
"DOMNodeInsertedIntoDocument",
"DOMAttrModified",
"DOMCharacterDataModified"
],
"HTMLEvent" : [
"load",
"unload",
"abort",
"error",
"select",
"change",
"submit",
"reset",
"focus",
"blur",
"resize",
"scroll"
],
"UIEvent" : [
"DOMFocusIn",
"DOMFocusOut",
"DOMActivate"
]
}
},{}],66:[function(require,module,exports){
(function (process){
var voxel = require('voxel')
var voxelMesh = require('voxel-mesh')
var ray = require('voxel-raycast')
var texture = require('voxel-texture')
var control = require('voxel-control')
var voxelView = require('voxel-view')
var THREE = require('three')
var Stats = require('./lib/stats')
var Detector = require('./lib/detector')
var inherits = require('inherits')
var path = require('path')
var EventEmitter = require('events').EventEmitter
if (process.browser) var interact = require('interact')
var requestAnimationFrame = require('raf')
var collisions = require('collide-3d-tilemap')
var aabb = require('aabb-3d')
var glMatrix = require('gl-matrix')
var vector = glMatrix.vec3
var SpatialEventEmitter = require('spatial-events')
var regionChange = require('voxel-region-change')
var kb = require('kb-controls')
var physical = require('voxel-physical')
var pin = require('pin-it')
var tic = require('tic')()
module.exports = Game
function Game(opts) {
if (!(this instanceof Game)) return new Game(opts)
var self = this
if (!opts) opts = {}
if (process.browser && this.notCapable(opts)) return
// is this a client or a headless server
this.isClient = Boolean( (typeof opts.isClient !== 'undefined') ? opts.isClient : process.browser )
if (!('generateChunks' in opts)) opts.generateChunks = true
this.generateChunks = opts.generateChunks
this.setConfigurablePositions(opts)
this.configureChunkLoading(opts)
this.setDimensions(opts)
this.THREE = THREE
this.vector = vector
this.glMatrix = glMatrix
this.arrayType = opts.arrayType || Uint8Array
this.cubeSize = 1 // backwards compat
this.chunkSize = opts.chunkSize || 32
// chunkDistance and removeDistance should not be set to the same thing
// as it causes lag when you go back and forth on a chunk boundary
this.chunkDistance = opts.chunkDistance || 2
this.removeDistance = opts.removeDistance || this.chunkDistance + 1
this.skyColor = opts.skyColor || 0xBFD1E5
this.antialias = opts.antialias
this.playerHeight = opts.playerHeight || 1.62
this.meshType = opts.meshType || 'surfaceMesh'
this.mesher = opts.mesher || voxel.meshers.culled
this.materialType = opts.materialType || THREE.MeshLambertMaterial
this.materialParams = opts.materialParams || {}
this.items = []
this.voxels = voxel(this)
this.scene = new THREE.Scene()
this.view = opts.view || new voxelView(THREE, {
width: this.width,
height: this.height,
skyColor: this.skyColor,
antialias: this.antialias
})
this.view.bindToScene(this.scene)
this.camera = this.view.getCamera()
if (!opts.lightsDisabled) this.addLights(this.scene)
this.fogScale = opts.fogScale || 32
if (!opts.fogDisabled) this.scene.fog = new THREE.Fog( this.skyColor, 0.00025, this.worldWidth() * this.fogScale )
this.collideVoxels = collisions(
this.getBlock.bind(this),
1,
[Infinity, Infinity, Infinity],
[-Infinity, -Infinity, -Infinity]
)
this.timer = this.initializeTimer((opts.tickFPS || 16))
this.paused = false
this.spatial = new SpatialEventEmitter
this.region = regionChange(this.spatial, aabb([0, 0, 0], [1, 1, 1]), this.chunkSize)
this.voxelRegion = regionChange(this.spatial, 1)
this.chunkRegion = regionChange(this.spatial, this.chunkSize)
this.asyncChunkGeneration = false
// contains chunks that has had an update this tick. Will be generated right before redrawing the frame
this.chunksNeedsUpdate = {}
// contains new chunks yet to be generated. Handled by game.loadPendingChunks
this.pendingChunks = []
this.materials = texture({
game: this,
texturePath: opts.texturePath || './textures/',
materialType: opts.materialType || THREE.MeshLambertMaterial,
materialParams: opts.materialParams || {},
materialFlatColor: opts.materialFlatColor === true
})
this.materialNames = opts.materials || [['grass', 'dirt', 'grass_dirt'], 'brick', 'dirt']
self.chunkRegion.on('change', function(newChunk) {
self.removeFarChunks()
})
if (this.isClient) this.materials.load(this.materialNames)
if (this.generateChunks) this.handleChunkGeneration()
// client side only after this point
if (!this.isClient) return
this.paused = true
this.initializeRendering(opts)
this.showAllChunks()
setTimeout(function() {
self.asyncChunkGeneration = 'asyncChunkGeneration' in opts ? opts.asyncChunkGeneration : true
}, 2000)
this.initializeControls(opts)
}
inherits(Game, EventEmitter)
// # External API
Game.prototype.voxelPosition = function(gamePosition) {
var _ = Math.floor
var p = gamePosition
var v = []
v[0] = _(p[0])
v[1] = _(p[1])
v[2] = _(p[2])
return v
}
Game.prototype.cameraPosition = function() {
return this.view.cameraPosition()
}
Game.prototype.cameraVector = function() {
return this.view.cameraVector()
}
Game.prototype.makePhysical = function(target, envelope, blocksCreation) {
var vel = this.terminalVelocity
envelope = envelope || [2/3, 1.5, 2/3]
var obj = physical(target, this.potentialCollisionSet(), envelope, {x: vel[0], y: vel[1], z: vel[2]})
obj.blocksCreation = !!blocksCreation
return obj
}
Game.prototype.addItem = function(item) {
if (!item.tick) {
var newItem = physical(
item.mesh,
this.potentialCollisionSet(),
[item.size, item.size, item.size]
)
if (item.velocity) {
newItem.velocity.copy(item.velocity)
newItem.subjectTo(this.gravity)
}
newItem.repr = function() { return 'debris' }
newItem.mesh = item.mesh
newItem.blocksCreation = item.blocksCreation
item = newItem
}
this.items.push(item)
if (item.mesh) this.scene.add(item.mesh)
return this.items[this.items.length - 1]
}
Game.prototype.removeItem = function(item) {
var ix = this.items.indexOf(item)
if (ix < 0) return
this.items.splice(ix, 1)
if (item.mesh) this.scene.remove(item.mesh)
}
// only intersects voxels, not items (for now)
Game.prototype.raycast = // backwards compat
Game.prototype.raycastVoxels = function(start, direction, maxDistance, epilson) {
if (!start) return this.raycastVoxels(this.cameraPosition(), this.cameraVector(), 10)
var hitNormal = [0, 0, 0]
var hitPosition = [0, 0, 0]
var cp = start || this.cameraPosition()
var cv = direction || this.cameraVector()
var hitBlock = ray(this, cp, cv, maxDistance || 10.0, hitPosition, hitNormal, epilson || this.epilson)
if (hitBlock <= 0) return false
var adjacentPosition = [0, 0, 0]
var voxelPosition = this.voxelPosition(hitPosition)
vector.add(adjacentPosition, voxelPosition, hitNormal)
return {
position: hitPosition,
voxel: voxelPosition,
direction: direction,
value: hitBlock,
normal: hitNormal,
adjacent: adjacentPosition
}
}
Game.prototype.canCreateBlock = function(pos) {
pos = this.parseVectorArguments(arguments)
var floored = pos.map(function(i) { return Math.floor(i) })
var bbox = aabb(floored, [1, 1, 1])
for (var i = 0, len = this.items.length; i < len; ++i) {
var item = this.items[i]
var itemInTheWay = item.blocksCreation && item.aabb && bbox.intersects(item.aabb())
if (itemInTheWay) return false
}
return true
}
Game.prototype.createBlock = function(pos, val) {
if (typeof val === 'string') val = this.materials.find(val)
if (!this.canCreateBlock(pos)) return false
this.setBlock(pos, val)
return true
}
Game.prototype.setBlock = function(pos, val) {
if (typeof val === 'string') val = this.materials.find(val)
var old = this.voxels.voxelAtPosition(pos, val)
var c = this.voxels.chunkAtPosition(pos)
var chunk = this.voxels.chunks[c.join('|')]
if (!chunk) return// todo - does self.emit('missingChunk', c.join('|')) make sense here?
this.addChunkToNextUpdate(chunk)
this.spatial.emit('change-block', pos, old, val)
this.emit('setBlock', pos, val, old)
}
Game.prototype.getBlock = function(pos) {
pos = this.parseVectorArguments(arguments)
return this.voxels.voxelAtPosition(pos)
}
Game.prototype.blockPosition = function(pos) {
pos = this.parseVectorArguments(arguments)
var ox = Math.floor(pos[0])
var oy = Math.floor(pos[1])
var oz = Math.floor(pos[2])
return [ox, oy, oz]
}
Game.prototype.blocks = function(low, high, iterator) {
var l = low, h = high
var d = [ h[0]-l[0], h[1]-l[1], h[2]-l[2] ]
if (!iterator) var voxels = new this.arrayType(d[0]*d[1]*d[2])
var i = 0
for(var z=l[2]; z<h[2]; ++z)
for(var y=l[1]; y<h[1]; ++y)
for(var x=l[0]; x<h[0]; ++x, ++i) {
if (iterator) iterator(x, y, z, i)
else voxels[i] = this.voxels.voxelAtPosition([x, y, z])
}
if (!iterator) return {voxels: voxels, dims: d}
}
// backwards compat
Game.prototype.createAdjacent = function(hit, val) {
this.createBlock(hit.adjacent, val)
}
Game.prototype.appendTo = function (element) {
this.view.appendTo(element)
}
// # Defaults/options parsing
Game.prototype.gravity = [0, -0.0000036, 0]
Game.prototype.friction = 0.3
Game.prototype.epilson = 1e-8
Game.prototype.terminalVelocity = [0.9, 0.1, 0.9]
Game.prototype.defaultButtons = {
'W': 'forward'
, 'A': 'left'
, 'S': 'backward'
, 'D': 'right'
, '<up>': 'forward'
, '<left>': 'left'
, '<down>': 'backward'
, '<right>': 'right'
, '<mouse 1>': 'fire'
, '<mouse 3>': 'firealt'
, '<space>': 'jump'
, '<shift>': 'crouch'
, '<control>': 'alt'
}
// used in methods that have identity function(pos) {}
Game.prototype.parseVectorArguments = function(args) {
if (!args) return false
if (args[0] instanceof Array) return args[0]
return [args[0], args[1], args[2]]
}
Game.prototype.setConfigurablePositions = function(opts) {
var sp = opts.startingPosition
this.startingPosition = sp || [35, 1024, 35]
var wo = opts.worldOrigin
this.worldOrigin = wo || [0, 0, 0]
}
Game.prototype.setDimensions = function(opts) {
if (opts.container) this.container = opts.container
if (opts.container && opts.container.clientHeight) {
this.height = opts.container.clientHeight
} else {
this.height = typeof window === "undefined" ? 1 : window.innerHeight
}
if (opts.container && opts.container.clientWidth) {
this.width = opts.container.clientWidth
} else {
this.width = typeof window === "undefined" ? 1 : window.innerWidth
}
}
Game.prototype.notCapable = function(opts) {
var self = this
if( !Detector().webgl ) {
this.view = {
appendTo: function(el) {
el.appendChild(self.notCapableMessage())
}
}
return true
}
return false
}
Game.prototype.notCapableMessage = function() {
var wrapper = document.createElement('div')
wrapper.className = "errorMessage"
var a = document.createElement('a')
a.title = "You need WebGL and Pointer Lock (Chrome 23/Firefox 14) to play this game. Click here for more information."
a.innerHTML = a.title
a.href = "http://get.webgl.org"
wrapper.appendChild(a)
return wrapper
}
Game.prototype.onWindowResize = function() {
var width = window.innerWidth
var height = window.innerHeight
if (this.container) {
width = this.container.clientWidth
height = this.container.clientHeight
}
this.view.resizeWindow(width, height)
}
// # Physics/collision related methods
Game.prototype.control = function(target) {
this.controlling = target
return this.controls.target(target)
}
Game.prototype.potentialCollisionSet = function() {
return [{ collide: this.collideTerrain.bind(this) }]
}
/**
* Get the position of the player under control.
* If there is no player under control, return
* current position of the game's camera.
*
* @return {Array} an [x, y, z] tuple
*/
Game.prototype.playerPosition = function() {
var target = this.controls.target()
var position = target
? target.avatar.position
: this.camera.localToWorld(this.camera.position.clone())
return [position.x, position.y, position.z]
}
Game.prototype.playerAABB = function(position) {
var pos = position || this.playerPosition()
var lower = []
var upper = [1/2, this.playerHeight, 1/2]
var playerBottom = [1/4, this.playerHeight, 1/4]
vector.subtract(lower, pos, playerBottom)
var bbox = aabb(lower, upper)
return bbox
}
Game.prototype.collideTerrain = function(other, bbox, vec, resting) {
var self = this
var axes = ['x', 'y', 'z']
var vec3 = [vec.x, vec.y, vec.z]
this.collideVoxels(bbox, vec3, function hit(axis, tile, coords, dir, edge) {
if (!tile) return
if (Math.abs(vec3[axis]) < Math.abs(edge)) return
vec3[axis] = vec[axes[axis]] = edge
other.acceleration[axes[axis]] = 0
resting[axes[axis]] = dir
other.friction[axes[(axis + 1) % 3]] = other.friction[axes[(axis + 2) % 3]] = axis === 1 ? self.friction : 1
return true
})
}
// # Three.js specific methods
Game.prototype.addStats = function() {
stats = new Stats()
stats.domElement.style.position = 'absolute'
stats.domElement.style.bottom = '0px'
document.body.appendChild( stats.domElement )
}
Game.prototype.addLights = function(scene) {
var ambientLight, directionalLight
ambientLight = new THREE.AmbientLight(0xcccccc)
scene.add(ambientLight)
var light = new THREE.DirectionalLight( 0xffffff , 1)
light.position.set( 1, 1, 0.5 ).normalize()
scene.add( light )
}
// # Chunk related methods
Game.prototype.configureChunkLoading = function(opts) {
var self = this
if (!opts.generateChunks) return
if (!opts.generate) {
this.generate = function(x,y,z) {
return x*x+y*y+z*z <= 15*15 ? 1 : 0 // sphere world
}
} else {
this.generate = opts.generate
}
if (opts.generateVoxelChunk) {
this.generateVoxelChunk = opts.generateVoxelChunk
} else {
this.generateVoxelChunk = function(low, high) {
return voxel.generate(low, high, self.generate, self)
}
}
}
Game.prototype.worldWidth = function() {
return this.chunkSize * 2 * this.chunkDistance
}
Game.prototype.chunkToWorld = function(pos) {
return [
pos[0] * this.chunkSize,
pos[1] * this.chunkSize,
pos[2] * this.chunkSize
]
}
Game.prototype.removeFarChunks = function(playerPosition) {
var self = this
playerPosition = playerPosition || this.playerPosition()
var nearbyChunks = this.voxels.nearbyChunks(playerPosition, this.removeDistance).map(function(chunkPos) {
return chunkPos.join('|')
})
Object.keys(self.voxels.chunks).map(function(chunkIndex) {
if (nearbyChunks.indexOf(chunkIndex) > -1) return
var chunk = self.voxels.chunks[chunkIndex]
var mesh = self.voxels.meshes[chunkIndex]
var pendingIndex = self.pendingChunks.indexOf(chunkIndex)
if (pendingIndex !== -1) self.pendingChunks.splice(pendingIndex, 1)
if (!chunk) return
var chunkPosition = chunk.position
if (mesh) {
if (mesh.surfaceMesh) {
self.scene.remove(mesh.surfaceMesh)
mesh.surfaceMesh.geometry.dispose()
}
if (mesh.wireMesh) {
mesh.wireMesh.geometry.dispose()
self.scene.remove(mesh.wireMesh)
}
delete mesh.data
delete mesh.geometry
delete mesh.meshed
delete mesh.surfaceMesh
delete mesh.wireMesh
}
delete self.voxels.chunks[chunkIndex]
self.emit('removeChunk', chunkPosition)
})
self.voxels.requestMissingChunks(playerPosition)
}
Game.prototype.addChunkToNextUpdate = function(chunk) {
this.chunksNeedsUpdate[chunk.position.join('|')] = chunk
}
Game.prototype.updateDirtyChunks = function() {
var self = this
Object.keys(this.chunksNeedsUpdate).forEach(function showChunkAtIndex(chunkIndex) {
var chunk = self.chunksNeedsUpdate[chunkIndex]
self.emit('dirtyChunkUpdate', chunk)
self.showChunk(chunk)
})
this.chunksNeedsUpdate = {}
}
Game.prototype.loadPendingChunks = function(count) {
var pendingChunks = this.pendingChunks
if (!this.asyncChunkGeneration) {
count = pendingChunks.length
} else {
count = count || (pendingChunks.length * 0.1)
count = Math.max(1, Math.min(count, pendingChunks.length))
}
for (var i = 0; i < count; i += 1) {
var chunkPos = pendingChunks[i].split('|')
var chunk = this.voxels.generateChunk(chunkPos[0]|0, chunkPos[1]|0, chunkPos[2]|0)
if (this.isClient) this.showChunk(chunk)
}
if (count) pendingChunks.splice(0, count)
}
Game.prototype.getChunkAtPosition = function(pos) {
var chunkID = this.voxels.chunkAtPosition(pos).join('|')
var chunk = this.voxels.chunks[chunkID]
return chunk
}
Game.prototype.showAllChunks = function() {
for (var chunkIndex in this.voxels.chunks) {
this.showChunk(this.voxels.chunks[chunkIndex])
}
}
Game.prototype.showChunk = function(chunk) {
var chunkIndex = chunk.position.join('|')
var bounds = this.voxels.getBounds.apply(this.voxels, chunk.position)
var scale = new THREE.Vector3(1, 1, 1)
var mesh = voxelMesh(chunk, this.mesher, scale, this.THREE)
this.voxels.chunks[chunkIndex] = chunk
if (this.voxels.meshes[chunkIndex]) {
if (this.voxels.meshes[chunkIndex].surfaceMesh) this.scene.remove(this.voxels.meshes[chunkIndex].surfaceMesh)
if (this.voxels.meshes[chunkIndex].wireMesh) this.scene.remove(this.voxels.meshes[chunkIndex].wireMesh)
}
this.voxels.meshes[chunkIndex] = mesh
if (this.isClient) {
if (this.meshType === 'wireMesh') mesh.createWireMesh()
else mesh.createSurfaceMesh(this.materials.material)
this.materials.paint(mesh)
}
mesh.setPosition(bounds[0][0], bounds[0][1], bounds[0][2])
mesh.addToScene(this.scene)
this.emit('renderChunk', chunk)
return mesh
}
// # Debugging methods
Game.prototype.addMarker = function(position) {
var geometry = new THREE.SphereGeometry( 0.1, 10, 10 )
var material = new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } )
var mesh = new THREE.Mesh( geometry, material )
mesh.position.copy(position)
this.scene.add(mesh)
}
Game.prototype.addAABBMarker = function(aabb, color) {
var geometry = new THREE.CubeGeometry(aabb.width(), aabb.height(), aabb.depth())
var material = new THREE.MeshBasicMaterial({ color: color || 0xffffff, wireframe: true, transparent: true, opacity: 0.5, side: THREE.DoubleSide })
var mesh = new THREE.Mesh(geometry, material)
mesh.position.set(aabb.x0() + aabb.width() / 2, aabb.y0() + aabb.height() / 2, aabb.z0() + aabb.depth() / 2)
this.scene.add(mesh)
return mesh
}
Game.prototype.addVoxelMarker = function(x, y, z, color) {
var bbox = aabb([x, y, z], [1, 1, 1])
return this.addAABBMarker(bbox, color)
}
Game.prototype.pin = pin
// # Misc internal methods
Game.prototype.onControlChange = function(gained, stream) {
this.paused = false
if (!gained && !this.optout) {
this.buttons.disable()
return
}
this.buttons.enable()
stream.pipe(this.controls.createWriteRotationStream())
}
Game.prototype.onControlOptOut = function() {
this.optout = true
}
Game.prototype.onFire = function(state) {
this.emit('fire', this.controlling, state)
}
Game.prototype.setInterval = tic.interval.bind(tic)
Game.prototype.setTimeout = tic.timeout.bind(tic)
Game.prototype.tick = function(delta) {
for(var i = 0, len = this.items.length; i < len; ++i) {
this.items[i].tick(delta)
}
if (this.materials) this.materials.tick(delta)
if (this.pendingChunks.length) this.loadPendingChunks()
if (Object.keys(this.chunksNeedsUpdate).length > 0) this.updateDirtyChunks()
tic.tick(delta)
this.emit('tick', delta)
if (!this.controls) return
var playerPos = this.playerPosition()
this.spatial.emit('position', playerPos, playerPos)
}
Game.prototype.render = function(delta) {
this.view.render(this.scene)
}
Game.prototype.initializeTimer = function(rate) {
var self = this
var accum = 0
var now = 0
var last = null
var dt = 0
var wholeTick
self.frameUpdated = true
self.interval = setInterval(timer, 0)
return self.interval
function timer() {
if (self.paused) {
last = Date.now()
accum = 0
return
}
now = Date.now()
dt = now - (last || now)
last = now
accum += dt
if (accum < rate) return
wholeTick = ((accum / rate)|0)
if (wholeTick <= 0) return
wholeTick *= rate
self.tick(wholeTick)
accum -= wholeTick
self.frameUpdated = true
}
}
Game.prototype.initializeRendering = function(opts) {
var self = this
if (!opts.statsDisabled) self.addStats()
window.addEventListener('resize', self.onWindowResize.bind(self), false)
requestAnimationFrame(window).on('data', function(dt) {
self.emit('prerender', dt)
self.render(dt)
self.emit('postrender', dt)
})
if (typeof stats !== 'undefined') {
self.on('postrender', function() {
stats.update()
})
}
}
Game.prototype.initializeControls = function(opts) {
// player control
this.keybindings = opts.keybindings || this.defaultButtons
this.buttons = kb(document.body, this.keybindings)
this.buttons.disable()
this.optout = false
this.interact = interact(opts.interactElement || this.view.element, opts.interactMouseDrag)
this.interact
.on('attain', this.onControlChange.bind(this, true))
.on('release', this.onControlChange.bind(this, false))
.on('opt-out', this.onControlOptOut.bind(this))
this.hookupControls(this.buttons, opts)
}
Game.prototype.hookupControls = function(buttons, opts) {
opts = opts || {}
opts.controls = opts.controls || {}
opts.controls.onfire = this.onFire.bind(this)
this.controls = control(buttons, opts.controls)
this.items.push(this.controls)
this.controlling = null
}
Game.prototype.handleChunkGeneration = function() {
var self = this
this.voxels.on('missingChunk', function(chunkPos) {
self.pendingChunks.push(chunkPos.join('|'))
})
this.voxels.requestMissingChunks(this.worldOrigin)
this.loadPendingChunks(this.pendingChunks.length)
}
// teardown methods
Game.prototype.destroy = function() {
clearInterval(this.timer)
}
}).call(this,require('_process'))
},{"./lib/detector":67,"./lib/stats":68,"_process":10,"aabb-3d":69,"collide-3d-tilemap":70,"events":6,"gl-matrix":71,"inherits":72,"interact":73,"kb-controls":82,"path":9,"pin-it":87,"raf":88,"spatial-events":89,"three":91,"tic":92,"voxel":102,"voxel-control":93,"voxel-mesh":94,"voxel-physical":95,"voxel-raycast":96,"voxel-region-change":97,"voxel-texture":98,"voxel-view":100}],67:[function(require,module,exports){
/**
* @author alteredq / http://alteredqualia.com/
* @author mr.doob / http://mrdoob.com/
*/
module.exports = function() {
return {
canvas : !! window.CanvasRenderingContext2D,
webgl : ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
workers : !! window.Worker,
fileapi : window.File && window.FileReader && window.FileList && window.Blob,
getWebGLErrorMessage : function () {
var domElement = document.createElement( 'div' );
domElement.style.fontFamily = 'monospace';
domElement.style.fontSize = '13px';
domElement.style.textAlign = 'center';
domElement.style.background = '#eee';
domElement.style.color = '#000';
domElement.style.padding = '1em';
domElement.style.width = '475px';
domElement.style.margin = '5em auto 0';
if ( ! this.webgl ) {
domElement.innerHTML = window.WebGLRenderingContext ? [
'Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br />',
'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
].join( '\n' ) : [
'Your browser does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br/>',
'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
].join( '\n' );
}
return domElement;
},
addGetWebGLMessage : function ( parameters ) {
var parent, id, domElement;
parameters = parameters || {};
parent = parameters.parent !== undefined ? parameters.parent : document.body;
id = parameters.id !== undefined ? parameters.id : 'oldie';
domElement = Detector.getWebGLErrorMessage();
domElement.id = id;
parent.appendChild( domElement );
}
};
}
},{}],68:[function(require,module,exports){
/**
* @author mrdoob / http://mrdoob.com/
*/
var Stats = function () {
var startTime = Date.now(), prevTime = startTime;
var ms = 0, msMin = Infinity, msMax = 0;
var fps = 0, fpsMin = Infinity, fpsMax = 0;
var frames = 0, mode = 0;
var container = document.createElement( 'div' );
container.id = 'stats';
container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false );
container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
var fpsDiv = document.createElement( 'div' );
fpsDiv.id = 'fps';
fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002';
container.appendChild( fpsDiv );
var fpsText = document.createElement( 'div' );
fpsText.id = 'fpsText';
fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
fpsText.innerHTML = 'FPS';
fpsDiv.appendChild( fpsText );
var fpsGraph = document.createElement( 'div' );
fpsGraph.id = 'fpsGraph';
fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff';
fpsDiv.appendChild( fpsGraph );
while ( fpsGraph.children.length < 74 ) {
var bar = document.createElement( 'span' );
bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113';
fpsGraph.appendChild( bar );
}
var msDiv = document.createElement( 'div' );
msDiv.id = 'ms';
msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none';
container.appendChild( msDiv );
var msText = document.createElement( 'div' );
msText.id = 'msText';
msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
msText.innerHTML = 'MS';
msDiv.appendChild( msText );
var msGraph = document.createElement( 'div' );
msGraph.id = 'msGraph';
msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
msDiv.appendChild( msGraph );
while ( msGraph.children.length < 74 ) {
var bar = document.createElement( 'span' );
bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
msGraph.appendChild( bar );
}
var setMode = function ( value ) {
mode = value;
switch ( mode ) {
case 0:
fpsDiv.style.display = 'block';
msDiv.style.display = 'none';
break;
case 1:
fpsDiv.style.display = 'none';
msDiv.style.display = 'block';
break;
}
}
var updateGraph = function ( dom, value ) {
var child = dom.appendChild( dom.firstChild );
child.style.height = value + 'px';
}
return {
REVISION: 11,
domElement: container,
setMode: setMode,
begin: function () {
startTime = Date.now();
},
end: function () {
var time = Date.now();
ms = time - startTime;
msMin = Math.min( msMin, ms );
msMax = Math.max( msMax, ms );
msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')';
updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) );
frames ++;
if ( time > prevTime + 1000 ) {
fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) );
fpsMin = Math.min( fpsMin, fps );
fpsMax = Math.max( fpsMax, fps );
fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')';
updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) );
prevTime = time;
frames = 0;
}
return time;
},
update: function () {
startTime = this.end();
}
}
};
module.exports = Stats
},{}],69:[function(require,module,exports){
module.exports = AABB
var vec3 = require('gl-matrix').vec3
function AABB(pos, vec) {
if(!(this instanceof AABB)) {
return new AABB(pos, vec)
}
this.base = pos
this.vec = vec
this.mag = vec3.length(this.vec)
this.max = vec3.create()
vec3.add(this.max, this.base, this.vec)
}
var cons = AABB
, proto = cons.prototype
proto.width = function() {
return this.vec[0]
}
proto.height = function() {
return this.vec[1]
}
proto.depth = function() {
return this.vec[2]
}
proto.x0 = function() {
return this.base[0]
}
proto.y0 = function() {
return this.base[1]
}
proto.z0 = function() {
return this.base[2]
}
proto.x1 = function() {
return this.max[0]
}
proto.y1 = function() {
return this.max[1]
}
proto.z1 = function() {
return this.max[2]
}
proto.translate = function(by) {
vec3.add(this.max, this.max, by)
vec3.add(this.base, this.base, by)
return this
}
proto.expand = function(aabb) {
var max = vec3.create()
, min = vec3.create()
vec3.max(max, aabb.max, this.max)
vec3.min(min, aabb.base, this.base)
vec3.sub(max, max, min)
return new AABB(min, max)
}
proto.intersects = function(aabb) {
if(aabb.base[0] > this.max[0]) return false
if(aabb.base[1] > this.max[1]) return false
if(aabb.base[2] > this.max[2]) return false
if(aabb.max[0] < this.base[0]) return false
if(aabb.max[1] < this.base[1]) return false
if(aabb.max[2] < this.base[2]) return false
return true
}
proto.union = function(aabb) {
if(!this.intersects(aabb)) return null
var base_x = Math.max(aabb.base[0], this.base[0])
, base_y = Math.max(aabb.base[1], this.base[1])
, base_z = Math.max(aabb.base[2], this.base[2])
, max_x = Math.min(aabb.max[0], this.max[0])
, max_y = Math.min(aabb.max[1], this.max[1])
, max_z = Math.min(aabb.max[2], this.max[2])
return new AABB([base_x, base_y, base_z], [max_x - base_x, max_y - base_y, max_z - base_z])
}
},{"gl-matrix":71}],70:[function(require,module,exports){
module.exports = function(field, tilesize, dimensions, offset) {
dimensions = dimensions || [
Math.sqrt(field.length) >> 0
, Math.sqrt(field.length) >> 0
, Math.sqrt(field.length) >> 0
]
offset = offset || [
0
, 0
, 0
]
field = typeof field === 'function' ? field : function(x, y, z) {
return this[x + y * dimensions[1] + (z * dimensions[1] * dimensions[2])]
}.bind(field)
var coords
coords = [0, 0, 0]
return collide
function collide(box, vec, oncollision) {
if(vec[0] === 0 && vec[1] === 0 && vec[2] === 0) return
// collide x, then y
collideaxis(0)
collideaxis(1)
collideaxis(2)
function collideaxis(i_axis) {
var j_axis = (i_axis + 1) % 3
, k_axis = (i_axis + 2) % 3
, posi = vec[i_axis] > 0
, leading = box[posi ? 'max' : 'base'][i_axis]
, dir = posi ? 1 : -1
, i_start = Math.floor(leading / tilesize)
, i_end = (Math.floor((leading + vec[i_axis]) / tilesize)) + dir
, j_start = Math.floor(box.base[j_axis] / tilesize)
, j_end = Math.ceil(box.max[j_axis] / tilesize)
, k_start = Math.floor(box.base[k_axis] / tilesize)
, k_end = Math.ceil(box.max[k_axis] / tilesize)
, done = false
, edge_vector
, edge
, tile
// loop from the current tile coord to the dest tile coord
// -> loop on the opposite axis to get the other candidates
// -> if `oncollision` return `true` we've hit something and
// should break out of the loops entirely.
// NB: `oncollision` is where the client gets the chance
// to modify the `vec` in-flight.
// once we're done translate the box to the vec results
var step = 0
for(var i = i_start; !done && i !== i_end; ++step, i += dir) {
if(i < offset[i_axis] || i >= dimensions[i_axis]) continue
for(var j = j_start; !done && j !== j_end; ++j) {
if(j < offset[j_axis] || j >= dimensions[j_axis]) continue
for(var k = k_start; k !== k_end; ++k) {
if(k < offset[k_axis] || k >= dimensions[k_axis]) continue
coords[i_axis] = i
coords[j_axis] = j
coords[k_axis] = k
tile = field.apply(field, coords)
if(tile === undefined) continue
edge = dir > 0 ? i * tilesize : (i + 1) * tilesize
edge_vector = edge - leading
if(oncollision(i_axis, tile, coords, dir, edge_vector)) {
done = true
break
}
}
}
}
coords[0] = coords[1] = coords[2] = 0
coords[i_axis] = vec[i_axis]
box.translate(coords)
}
}
}
},{}],71:[function(require,module,exports){
/**
* @fileoverview gl-matrix - High performance matrix and vector operations
* @author Brandon Jones
* @author Colin MacKenzie IV
* @version 2.0.0
*/
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
(function() {
"use strict";
var shim = {};
if (typeof(exports) === 'undefined') {
if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
shim.exports = {};
define(function() {
return shim.exports;
});
} else {
// gl-matrix lives in a browser, define its namespaces in global
shim.exports = window;
}
}
else {
// gl-matrix lives in commonjs, define its namespaces in exports
shim.exports = exports;
}
(function(exports) {
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 2 Dimensional Vector
* @name vec2
*/
var vec2 = {};
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new, empty vec2
*
* @returns {vec2} a new 2D vector
*/
vec2.create = function() {
return new Float32Array(2);
};
/**
* Creates a new vec2 initialized with values from an existing vector
*
* @param {vec2} a vector to clone
* @returns {vec2} a new 2D vector
*/
vec2.clone = function(a) {
var out = new Float32Array(2);
out[0] = a[0];
out[1] = a[1];
return out;
};
/**
* Creates a new vec2 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @returns {vec2} a new 2D vector
*/
vec2.fromValues = function(x, y) {
var out = new Float32Array(2);
out[0] = x;
out[1] = y;
return out;
};
/**
* Copy the values from one vec2 to another
*
* @param {vec2} out the receiving vector
* @param {vec2} a the source vector
* @returns {vec2} out
*/
vec2.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
return out;
};
/**
* Set the components of a vec2 to the given values
*
* @param {vec2} out the receiving vector
* @param {Number} x X component
* @param {Number} y Y component
* @returns {vec2} out
*/
vec2.set = function(out, x, y) {
out[0] = x;
out[1] = y;
return out;
};
/**
* Adds two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.add = function(out, a, b) {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
return out;
};
/**
* Subtracts two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.sub = vec2.subtract = function(out, a, b) {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
return out;
};
/**
* Multiplies two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.mul = vec2.multiply = function(out, a, b) {
out[0] = a[0] * b[0];
out[1] = a[1] * b[1];
return out;
};
/**
* Divides two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.div = vec2.divide = function(out, a, b) {
out[0] = a[0] / b[0];
out[1] = a[1] / b[1];
return out;
};
/**
* Returns the minimum of two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.min = function(out, a, b) {
out[0] = Math.min(a[0], b[0]);
out[1] = Math.min(a[1], b[1]);
return out;
};
/**
* Returns the maximum of two vec2's
*
* @param {vec2} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec2} out
*/
vec2.max = function(out, a, b) {
out[0] = Math.max(a[0], b[0]);
out[1] = Math.max(a[1], b[1]);
return out;
};
/**
* Scales a vec2 by a scalar number
*
* @param {vec2} out the receiving vector
* @param {vec2} a the vector to scale
* @param {vec2} b amount to scale the vector by
* @returns {vec2} out
*/
vec2.scale = function(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
return out;
};
/**
* Calculates the euclidian distance between two vec2's
*
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {Number} distance between a and b
*/
vec2.dist = vec2.distance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1];
return Math.sqrt(x*x + y*y);
};
/**
* Calculates the squared euclidian distance between two vec2's
*
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {Number} squared distance between a and b
*/
vec2.sqrDist = vec2.squaredDistance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1];
return x*x + y*y;
};
/**
* Caclulates the length of a vec2
*
* @param {vec2} a vector to calculate length of
* @returns {Number} length of a
*/
vec2.len = vec2.length = function (a) {
var x = a[0],
y = a[1];
return Math.sqrt(x*x + y*y);
};
/**
* Caclulates the squared length of a vec2
*
* @param {vec2} a vector to calculate squared length of
* @returns {Number} squared length of a
*/
vec2.sqrLen = vec2.squaredLength = function (a) {
var x = a[0],
y = a[1];
return x*x + y*y;
};
/**
* Negates the components of a vec2
*
* @param {vec2} out the receiving vector
* @param {vec2} a vector to negate
* @returns {vec2} out
*/
vec2.negate = function(out, a) {
out[0] = -a[0];
out[1] = -a[1];
return out;
};
/**
* Normalize a vec2
*
* @param {vec2} out the receiving vector
* @param {vec2} a vector to normalize
* @returns {vec2} out
*/
vec2.normalize = function(out, a) {
var x = a[0],
y = a[1];
var len = x*x + y*y;
if (len > 0) {
//TODO: evaluate use of glm_invsqrt here?
len = 1 / Math.sqrt(len);
out[0] = a[0] * len;
out[1] = a[1] * len;
}
return out;
};
/**
* Caclulates the dot product of two vec2's
*
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {Number} dot product of a and b
*/
vec2.dot = function (a, b) {
return a[0] * b[0] + a[1] * b[1];
};
/**
* Computes the cross product of two vec2's
* Note that the cross product must by definition produce a 3D vector
*
* @param {vec3} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @returns {vec3} out
*/
vec2.cross = function(out, a, b) {
var z = a[0] * b[1] - a[1] * b[0];
out[0] = out[1] = 0;
out[2] = z;
return out;
};
/**
* Performs a linear interpolation between two vec2's
*
* @param {vec3} out the receiving vector
* @param {vec2} a the first operand
* @param {vec2} b the second operand
* @param {Number} t interpolation amount between the two inputs
* @returns {vec2} out
*/
vec2.lerp = function (out, a, b, t) {
var ax = a[0],
ay = a[1];
out[0] = ax + t * (b[0] - ax);
out[1] = ay + t * (b[1] - ay);
return out;
};
/**
* Transforms the vec2 with a mat2
*
* @param {vec2} out the receiving vector
* @param {vec2} a the vector to transform
* @param {mat2} m matrix to transform with
* @returns {vec2} out
*/
vec2.transformMat2 = function(out, a, m) {
var x = a[0],
y = a[1];
out[0] = x * m[0] + y * m[1];
out[1] = x * m[2] + y * m[3];
return out;
};
/**
* Perform some operation over an array of vec2s.
*
* @param {Array} a the array of vectors to iterate over
* @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
* @param {Number} offset Number of elements to skip at the beginning of the array
* @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
* @param {Function} fn Function to call for each vector in the array
* @param {Object} [arg] additional argument to pass to fn
* @returns {Array} a
*/
vec2.forEach = (function() {
var vec = new Float32Array(2);
return function(a, stride, offset, count, fn, arg) {
var i, l;
if(!stride) {
stride = 2;
}
if(!offset) {
offset = 0;
}
if(count) {
l = Math.min((count * stride) + offset, a.length);
} else {
l = a.length;
}
for(i = offset; i < l; i += stride) {
vec[0] = a[i]; vec[1] = a[i+1];
fn(vec, vec, arg);
a[i] = vec[0]; a[i+1] = vec[1];
}
return a;
};
})();
/**
* Returns a string representation of a vector
*
* @param {vec2} vec vector to represent as a string
* @returns {String} string representation of the vector
*/
vec2.str = function (a) {
return 'vec2(' + a[0] + ', ' + a[1] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.vec2 = vec2;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 3 Dimensional Vector
* @name vec3
*/
var vec3 = {};
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new, empty vec3
*
* @returns {vec3} a new 3D vector
*/
vec3.create = function() {
return new Float32Array(3);
};
/**
* Creates a new vec3 initialized with values from an existing vector
*
* @param {vec3} a vector to clone
* @returns {vec3} a new 3D vector
*/
vec3.clone = function(a) {
var out = new Float32Array(3);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
return out;
};
/**
* Creates a new vec3 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @returns {vec3} a new 3D vector
*/
vec3.fromValues = function(x, y, z) {
var out = new Float32Array(3);
out[0] = x;
out[1] = y;
out[2] = z;
return out;
};
/**
* Copy the values from one vec3 to another
*
* @param {vec3} out the receiving vector
* @param {vec3} a the source vector
* @returns {vec3} out
*/
vec3.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
return out;
};
/**
* Set the components of a vec3 to the given values
*
* @param {vec3} out the receiving vector
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @returns {vec3} out
*/
vec3.set = function(out, x, y, z) {
out[0] = x;
out[1] = y;
out[2] = z;
return out;
};
/**
* Adds two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.add = function(out, a, b) {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
out[2] = a[2] + b[2];
return out;
};
/**
* Subtracts two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.sub = vec3.subtract = function(out, a, b) {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
return out;
};
/**
* Multiplies two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.mul = vec3.multiply = function(out, a, b) {
out[0] = a[0] * b[0];
out[1] = a[1] * b[1];
out[2] = a[2] * b[2];
return out;
};
/**
* Divides two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.div = vec3.divide = function(out, a, b) {
out[0] = a[0] / b[0];
out[1] = a[1] / b[1];
out[2] = a[2] / b[2];
return out;
};
/**
* Returns the minimum of two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.min = function(out, a, b) {
out[0] = Math.min(a[0], b[0]);
out[1] = Math.min(a[1], b[1]);
out[2] = Math.min(a[2], b[2]);
return out;
};
/**
* Returns the maximum of two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.max = function(out, a, b) {
out[0] = Math.max(a[0], b[0]);
out[1] = Math.max(a[1], b[1]);
out[2] = Math.max(a[2], b[2]);
return out;
};
/**
* Scales a vec3 by a scalar number
*
* @param {vec3} out the receiving vector
* @param {vec3} a the vector to scale
* @param {vec3} b amount to scale the vector by
* @returns {vec3} out
*/
vec3.scale = function(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
out[2] = a[2] * b;
return out;
};
/**
* Calculates the euclidian distance between two vec3's
*
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {Number} distance between a and b
*/
vec3.dist = vec3.distance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1],
z = b[2] - a[2];
return Math.sqrt(x*x + y*y + z*z);
};
/**
* Calculates the squared euclidian distance between two vec3's
*
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {Number} squared distance between a and b
*/
vec3.sqrDist = vec3.squaredDistance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1],
z = b[2] - a[2];
return x*x + y*y + z*z;
};
/**
* Caclulates the length of a vec3
*
* @param {vec3} a vector to calculate length of
* @returns {Number} length of a
*/
vec3.len = vec3.length = function (a) {
var x = a[0],
y = a[1],
z = a[2];
return Math.sqrt(x*x + y*y + z*z);
};
/**
* Caclulates the squared length of a vec3
*
* @param {vec3} a vector to calculate squared length of
* @returns {Number} squared length of a
*/
vec3.sqrLen = vec3.squaredLength = function (a) {
var x = a[0],
y = a[1],
z = a[2];
return x*x + y*y + z*z;
};
/**
* Negates the components of a vec3
*
* @param {vec3} out the receiving vector
* @param {vec3} a vector to negate
* @returns {vec3} out
*/
vec3.negate = function(out, a) {
out[0] = -a[0];
out[1] = -a[1];
out[2] = -a[2];
return out;
};
/**
* Normalize a vec3
*
* @param {vec3} out the receiving vector
* @param {vec3} a vector to normalize
* @returns {vec3} out
*/
vec3.normalize = function(out, a) {
var x = a[0],
y = a[1],
z = a[2];
var len = x*x + y*y + z*z;
if (len > 0) {
//TODO: evaluate use of glm_invsqrt here?
len = 1 / Math.sqrt(len);
out[0] = a[0] * len;
out[1] = a[1] * len;
out[2] = a[2] * len;
}
return out;
};
/**
* Caclulates the dot product of two vec3's
*
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {Number} dot product of a and b
*/
vec3.dot = function (a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
};
/**
* Computes the cross product of two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @returns {vec3} out
*/
vec3.cross = function(out, a, b) {
var ax = a[0], ay = a[1], az = a[2],
bx = b[0], by = b[1], bz = b[2];
out[0] = ay * bz - az * by;
out[1] = az * bx - ax * bz;
out[2] = ax * by - ay * bx;
return out;
};
/**
* Performs a linear interpolation between two vec3's
*
* @param {vec3} out the receiving vector
* @param {vec3} a the first operand
* @param {vec3} b the second operand
* @param {Number} t interpolation amount between the two inputs
* @returns {vec3} out
*/
vec3.lerp = function (out, a, b, t) {
var ax = a[0],
ay = a[1],
az = a[2];
out[0] = ax + t * (b[0] - ax);
out[1] = ay + t * (b[1] - ay);
out[2] = az + t * (b[2] - az);
return out;
};
/**
* Transforms the vec3 with a mat4.
* 4th vector component is implicitly '1'
*
* @param {vec3} out the receiving vector
* @param {vec3} a the vector to transform
* @param {mat4} m matrix to transform with
* @returns {vec3} out
*/
vec3.transformMat4 = function(out, a, m) {
var x = a[0], y = a[1], z = a[2];
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12];
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13];
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14];
return out;
};
/**
* Transforms the vec3 with a quat
*
* @param {vec3} out the receiving vector
* @param {vec3} a the vector to transform
* @param {quat} q quaternion to transform with
* @returns {vec3} out
*/
vec3.transformQuat = function(out, a, q) {
var x = a[0], y = a[1], z = a[2],
qx = q[0], qy = q[1], qz = q[2], qw = q[3],
// calculate quat * vec
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;
// calculate result * inverse quat
out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return out;
};
/**
* Perform some operation over an array of vec3s.
*
* @param {Array} a the array of vectors to iterate over
* @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
* @param {Number} offset Number of elements to skip at the beginning of the array
* @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
* @param {Function} fn Function to call for each vector in the array
* @param {Object} [arg] additional argument to pass to fn
* @returns {Array} a
*/
vec3.forEach = (function() {
var vec = new Float32Array(3);
return function(a, stride, offset, count, fn, arg) {
var i, l;
if(!stride) {
stride = 3;
}
if(!offset) {
offset = 0;
}
if(count) {
l = Math.min((count * stride) + offset, a.length);
} else {
l = a.length;
}
for(i = offset; i < l; i += stride) {
vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2];
fn(vec, vec, arg);
a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2];
}
return a;
};
})();
/**
* Returns a string representation of a vector
*
* @param {vec3} vec vector to represent as a string
* @returns {String} string representation of the vector
*/
vec3.str = function (a) {
return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.vec3 = vec3;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 4 Dimensional Vector
* @name vec4
*/
var vec4 = {};
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new, empty vec4
*
* @returns {vec4} a new 4D vector
*/
vec4.create = function() {
return new Float32Array(4);
};
/**
* Creates a new vec4 initialized with values from an existing vector
*
* @param {vec4} a vector to clone
* @returns {vec4} a new 4D vector
*/
vec4.clone = function(a) {
var out = new Float32Array(4);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
return out;
};
/**
* Creates a new vec4 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {vec4} a new 4D vector
*/
vec4.fromValues = function(x, y, z, w) {
var out = new Float32Array(4);
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = w;
return out;
};
/**
* Copy the values from one vec4 to another
*
* @param {vec4} out the receiving vector
* @param {vec4} a the source vector
* @returns {vec4} out
*/
vec4.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
return out;
};
/**
* Set the components of a vec4 to the given values
*
* @param {vec4} out the receiving vector
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {vec4} out
*/
vec4.set = function(out, x, y, z, w) {
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = w;
return out;
};
/**
* Adds two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.add = function(out, a, b) {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
out[2] = a[2] + b[2];
out[3] = a[3] + b[3];
return out;
};
/**
* Subtracts two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.sub = vec4.subtract = function(out, a, b) {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
out[3] = a[3] - b[3];
return out;
};
/**
* Multiplies two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.mul = vec4.multiply = function(out, a, b) {
out[0] = a[0] * b[0];
out[1] = a[1] * b[1];
out[2] = a[2] * b[2];
out[3] = a[3] * b[3];
return out;
};
/**
* Divides two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.div = vec4.divide = function(out, a, b) {
out[0] = a[0] / b[0];
out[1] = a[1] / b[1];
out[2] = a[2] / b[2];
out[3] = a[3] / b[3];
return out;
};
/**
* Returns the minimum of two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.min = function(out, a, b) {
out[0] = Math.min(a[0], b[0]);
out[1] = Math.min(a[1], b[1]);
out[2] = Math.min(a[2], b[2]);
out[3] = Math.min(a[3], b[3]);
return out;
};
/**
* Returns the maximum of two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {vec4} out
*/
vec4.max = function(out, a, b) {
out[0] = Math.max(a[0], b[0]);
out[1] = Math.max(a[1], b[1]);
out[2] = Math.max(a[2], b[2]);
out[3] = Math.max(a[3], b[3]);
return out;
};
/**
* Scales a vec4 by a scalar number
*
* @param {vec4} out the receiving vector
* @param {vec4} a the vector to scale
* @param {vec4} b amount to scale the vector by
* @returns {vec4} out
*/
vec4.scale = function(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
out[2] = a[2] * b;
out[3] = a[3] * b;
return out;
};
/**
* Calculates the euclidian distance between two vec4's
*
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {Number} distance between a and b
*/
vec4.dist = vec4.distance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1],
z = b[2] - a[2],
w = b[3] - a[3];
return Math.sqrt(x*x + y*y + z*z + w*w);
};
/**
* Calculates the squared euclidian distance between two vec4's
*
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {Number} squared distance between a and b
*/
vec4.sqrDist = vec4.squaredDistance = function(a, b) {
var x = b[0] - a[0],
y = b[1] - a[1],
z = b[2] - a[2],
w = b[3] - a[3];
return x*x + y*y + z*z + w*w;
};
/**
* Caclulates the length of a vec4
*
* @param {vec4} a vector to calculate length of
* @returns {Number} length of a
*/
vec4.len = vec4.length = function (a) {
var x = a[0],
y = a[1],
z = a[2],
w = a[3];
return Math.sqrt(x*x + y*y + z*z + w*w);
};
/**
* Caclulates the squared length of a vec4
*
* @param {vec4} a vector to calculate squared length of
* @returns {Number} squared length of a
*/
vec4.sqrLen = vec4.squaredLength = function (a) {
var x = a[0],
y = a[1],
z = a[2],
w = a[3];
return x*x + y*y + z*z + w*w;
};
/**
* Negates the components of a vec4
*
* @param {vec4} out the receiving vector
* @param {vec4} a vector to negate
* @returns {vec4} out
*/
vec4.negate = function(out, a) {
out[0] = -a[0];
out[1] = -a[1];
out[2] = -a[2];
out[3] = -a[3];
return out;
};
/**
* Normalize a vec4
*
* @param {vec4} out the receiving vector
* @param {vec4} a vector to normalize
* @returns {vec4} out
*/
vec4.normalize = function(out, a) {
var x = a[0],
y = a[1],
z = a[2],
w = a[3];
var len = x*x + y*y + z*z + w*w;
if (len > 0) {
len = 1 / Math.sqrt(len);
out[0] = a[0] * len;
out[1] = a[1] * len;
out[2] = a[2] * len;
out[3] = a[3] * len;
}
return out;
};
/**
* Caclulates the dot product of two vec4's
*
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @returns {Number} dot product of a and b
*/
vec4.dot = function (a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
};
/**
* Performs a linear interpolation between two vec4's
*
* @param {vec4} out the receiving vector
* @param {vec4} a the first operand
* @param {vec4} b the second operand
* @param {Number} t interpolation amount between the two inputs
* @returns {vec4} out
*/
vec4.lerp = function (out, a, b, t) {
var ax = a[0],
ay = a[1],
az = a[2],
aw = a[3];
out[0] = ax + t * (b[0] - ax);
out[1] = ay + t * (b[1] - ay);
out[2] = az + t * (b[2] - az);
out[3] = aw + t * (b[3] - aw);
return out;
};
/**
* Transforms the vec4 with a mat4.
*
* @param {vec4} out the receiving vector
* @param {vec4} a the vector to transform
* @param {mat4} m matrix to transform with
* @returns {vec4} out
*/
vec4.transformMat4 = function(out, a, m) {
var x = a[0], y = a[1], z = a[2], w = a[3];
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
return out;
};
/**
* Transforms the vec4 with a quat
*
* @param {vec4} out the receiving vector
* @param {vec4} a the vector to transform
* @param {quat} q quaternion to transform with
* @returns {vec4} out
*/
vec4.transformQuat = function(out, a, q) {
var x = a[0], y = a[1], z = a[2],
qx = q[0], qy = q[1], qz = q[2], qw = q[3],
// calculate quat * vec
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;
// calculate result * inverse quat
out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return out;
};
/**
* Perform some operation over an array of vec4s.
*
* @param {Array} a the array of vectors to iterate over
* @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed
* @param {Number} offset Number of elements to skip at the beginning of the array
* @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
* @param {Function} fn Function to call for each vector in the array
* @param {Object} [arg] additional argument to pass to fn
* @returns {Array} a
*/
vec4.forEach = (function() {
var vec = new Float32Array(4);
return function(a, stride, offset, count, fn, arg) {
var i, l;
if(!stride) {
stride = 4;
}
if(!offset) {
offset = 0;
}
if(count) {
l = Math.min((count * stride) + offset, a.length);
} else {
l = a.length;
}
for(i = offset; i < l; i += stride) {
vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3];
fn(vec, vec, arg);
a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3];
}
return a;
};
})();
/**
* Returns a string representation of a vector
*
* @param {vec4} vec vector to represent as a string
* @returns {String} string representation of the vector
*/
vec4.str = function (a) {
return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.vec4 = vec4;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 2x2 Matrix
* @name mat2
*/
var mat2 = {};
var mat2Identity = new Float32Array([
1, 0,
0, 1
]);
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new identity mat2
*
* @returns {mat2} a new 2x2 matrix
*/
mat2.create = function() {
return new Float32Array(mat2Identity);
};
/**
* Creates a new mat2 initialized with values from an existing matrix
*
* @param {mat2} a matrix to clone
* @returns {mat2} a new 2x2 matrix
*/
mat2.clone = function(a) {
var out = new Float32Array(4);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
return out;
};
/**
* Copy the values from one mat2 to another
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the source matrix
* @returns {mat2} out
*/
mat2.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
return out;
};
/**
* Set a mat2 to the identity matrix
*
* @param {mat2} out the receiving matrix
* @returns {mat2} out
*/
mat2.identity = function(out) {
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 1;
return out;
};
/**
* Transpose the values of a mat2
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the source matrix
* @returns {mat2} out
*/
mat2.transpose = function(out, a) {
// If we are transposing ourselves we can skip a few steps but have to cache some values
if (out === a) {
var a1 = a[1];
out[1] = a[2];
out[2] = a1;
} else {
out[0] = a[0];
out[1] = a[2];
out[2] = a[1];
out[3] = a[3];
}
return out;
};
/**
* Inverts a mat2
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the source matrix
* @returns {mat2} out
*/
mat2.invert = function(out, a) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
// Calculate the determinant
det = a0 * a3 - a2 * a1;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = a3 * det;
out[1] = -a1 * det;
out[2] = -a2 * det;
out[3] = a0 * det;
return out;
};
/**
* Caclulates the adjugate of a mat2
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the source matrix
* @returns {mat2} out
*/
mat2.adjoint = function(out, a) {
// Caching this value is nessecary if out == a
var a0 = a[0];
out[0] = a[3];
out[1] = -a[1];
out[2] = -a[2];
out[3] = a0;
return out;
};
/**
* Calculates the determinant of a mat2
*
* @param {mat2} a the source matrix
* @returns {Number} determinant of a
*/
mat2.determinant = function (a) {
return a[0] * a[3] - a[2] * a[1];
};
/**
* Multiplies two mat2's
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the first operand
* @param {mat2} b the second operand
* @returns {mat2} out
*/
mat2.mul = mat2.multiply = function (out, a, b) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = a0 * b0 + a1 * b2;
out[1] = a0 * b1 + a1 * b3;
out[2] = a2 * b0 + a3 * b2;
out[3] = a2 * b1 + a3 * b3;
return out;
};
/**
* Rotates a mat2 by the given angle
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the matrix to rotate
* @param {mat2} rad the angle to rotate the matrix by
* @returns {mat2} out
*/
mat2.rotate = function (out, a, rad) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
s = Math.sin(rad),
c = Math.cos(rad);
out[0] = a0 * c + a1 * s;
out[1] = a0 * -s + a1 * c;
out[2] = a2 * c + a3 * s;
out[3] = a2 * -s + a3 * c;
return out;
};
/**
* Scales the mat2 by the dimensions in the given vec2
*
* @param {mat2} out the receiving matrix
* @param {mat2} a the matrix to rotate
* @param {mat2} v the vec2 to scale the matrix by
* @returns {mat2} out
**/
mat2.scale = function(out, a, v) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
v0 = v[0], v1 = v[1];
out[0] = a0 * v0;
out[1] = a1 * v1;
out[2] = a2 * v0;
out[3] = a3 * v1;
return out;
};
/**
* Returns a string representation of a mat2
*
* @param {mat2} mat matrix to represent as a string
* @returns {String} string representation of the matrix
*/
mat2.str = function (a) {
return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.mat2 = mat2;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 3x3 Matrix
* @name mat3
*/
var mat3 = {};
var mat3Identity = new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new identity mat3
*
* @returns {mat3} a new 3x3 matrix
*/
mat3.create = function() {
return new Float32Array(mat3Identity);
};
/**
* Creates a new mat3 initialized with values from an existing matrix
*
* @param {mat3} a matrix to clone
* @returns {mat3} a new 3x3 matrix
*/
mat3.clone = function(a) {
var out = new Float32Array(9);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
return out;
};
/**
* Copy the values from one mat3 to another
*
* @param {mat3} out the receiving matrix
* @param {mat3} a the source matrix
* @returns {mat3} out
*/
mat3.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
return out;
};
/**
* Set a mat3 to the identity matrix
*
* @param {mat3} out the receiving matrix
* @returns {mat3} out
*/
mat3.identity = function(out) {
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 1;
out[5] = 0;
out[6] = 0;
out[7] = 0;
out[8] = 1;
return out;
};
/**
* Transpose the values of a mat3
*
* @param {mat3} out the receiving matrix
* @param {mat3} a the source matrix
* @returns {mat3} out
*/
mat3.transpose = function(out, a) {
// If we are transposing ourselves we can skip a few steps but have to cache some values
if (out === a) {
var a01 = a[1], a02 = a[2], a12 = a[5];
out[1] = a[3];
out[2] = a[6];
out[3] = a01;
out[5] = a[7];
out[6] = a02;
out[7] = a12;
} else {
out[0] = a[0];
out[1] = a[3];
out[2] = a[6];
out[3] = a[1];
out[4] = a[4];
out[5] = a[7];
out[6] = a[2];
out[7] = a[5];
out[8] = a[8];
}
return out;
};
/**
* Inverts a mat3
*
* @param {mat3} out the receiving matrix
* @param {mat3} a the source matrix
* @returns {mat3} out
*/
mat3.invert = function(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8],
b01 = a22 * a11 - a12 * a21,
b11 = -a22 * a10 + a12 * a20,
b21 = a21 * a10 - a11 * a20,
// Calculate the determinant
det = a00 * b01 + a01 * b11 + a02 * b21;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = b01 * det;
out[1] = (-a22 * a01 + a02 * a21) * det;
out[2] = (a12 * a01 - a02 * a11) * det;
out[3] = b11 * det;
out[4] = (a22 * a00 - a02 * a20) * det;
out[5] = (-a12 * a00 + a02 * a10) * det;
out[6] = b21 * det;
out[7] = (-a21 * a00 + a01 * a20) * det;
out[8] = (a11 * a00 - a01 * a10) * det;
return out;
};
/**
* Caclulates the adjugate of a mat3
*
* @param {mat3} out the receiving matrix
* @param {mat3} a the source matrix
* @returns {mat3} out
*/
mat3.adjoint = function(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
out[0] = (a11 * a22 - a12 * a21);
out[1] = (a02 * a21 - a01 * a22);
out[2] = (a01 * a12 - a02 * a11);
out[3] = (a12 * a20 - a10 * a22);
out[4] = (a00 * a22 - a02 * a20);
out[5] = (a02 * a10 - a00 * a12);
out[6] = (a10 * a21 - a11 * a20);
out[7] = (a01 * a20 - a00 * a21);
out[8] = (a00 * a11 - a01 * a10);
return out;
};
/**
* Calculates the determinant of a mat3
*
* @param {mat3} a the source matrix
* @returns {Number} determinant of a
*/
mat3.determinant = function (a) {
var a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
};
/**
* Multiplies two mat3's
*
* @param {mat3} out the receiving matrix
* @param {mat3} a the first operand
* @param {mat3} b the second operand
* @returns {mat3} out
*/
mat3.mul = mat3.multiply = function (out, a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8],
b00 = b[0], b01 = b[1], b02 = b[2],
b10 = b[3], b11 = b[4], b12 = b[5],
b20 = b[6], b21 = b[7], b22 = b[8];
out[0] = b00 * a00 + b01 * a10 + b02 * a20;
out[1] = b00 * a01 + b01 * a11 + b02 * a21;
out[2] = b00 * a02 + b01 * a12 + b02 * a22;
out[3] = b10 * a00 + b11 * a10 + b12 * a20;
out[4] = b10 * a01 + b11 * a11 + b12 * a21;
out[5] = b10 * a02 + b11 * a12 + b12 * a22;
out[6] = b20 * a00 + b21 * a10 + b22 * a20;
out[7] = b20 * a01 + b21 * a11 + b22 * a21;
out[8] = b20 * a02 + b21 * a12 + b22 * a22;
return out;
};
/**
* Returns a string representation of a mat3
*
* @param {mat3} mat matrix to represent as a string
* @returns {String} string representation of the matrix
*/
mat3.str = function (a) {
return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +
a[6] + ', ' + a[7] + ', ' + a[8] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.mat3 = mat3;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class 4x4 Matrix
* @name mat4
*/
var mat4 = {};
var mat4Identity = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new identity mat4
*
* @returns {mat4} a new 4x4 matrix
*/
mat4.create = function() {
return new Float32Array(mat4Identity);
};
/**
* Creates a new mat4 initialized with values from an existing matrix
*
* @param {mat4} a matrix to clone
* @returns {mat4} a new 4x4 matrix
*/
mat4.clone = function(a) {
var out = new Float32Array(16);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
};
/**
* Copy the values from one mat4 to another
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the source matrix
* @returns {mat4} out
*/
mat4.copy = function(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
};
/**
* Set a mat4 to the identity matrix
*
* @param {mat4} out the receiving matrix
* @returns {mat4} out
*/
mat4.identity = function(out) {
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = 1;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 1;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return out;
};
/**
* Transpose the values of a mat4
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the source matrix
* @returns {mat4} out
*/
mat4.transpose = function(out, a) {
// If we are transposing ourselves we can skip a few steps but have to cache some values
if (out === a) {
var a01 = a[1], a02 = a[2], a03 = a[3],
a12 = a[6], a13 = a[7],
a23 = a[11];
out[1] = a[4];
out[2] = a[8];
out[3] = a[12];
out[4] = a01;
out[6] = a[9];
out[7] = a[13];
out[8] = a02;
out[9] = a12;
out[11] = a[14];
out[12] = a03;
out[13] = a13;
out[14] = a23;
} else {
out[0] = a[0];
out[1] = a[4];
out[2] = a[8];
out[3] = a[12];
out[4] = a[1];
out[5] = a[5];
out[6] = a[9];
out[7] = a[13];
out[8] = a[2];
out[9] = a[6];
out[10] = a[10];
out[11] = a[14];
out[12] = a[3];
out[13] = a[7];
out[14] = a[11];
out[15] = a[15];
}
return out;
};
/**
* Inverts a mat4
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the source matrix
* @returns {mat4} out
*/
mat4.invert = function(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32,
// Calculate the determinant
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return out;
};
/**
* Caclulates the adjugate of a mat4
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the source matrix
* @returns {mat4} out
*/
mat4.adjoint = function(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
return out;
};
/**
* Calculates the determinant of a mat4
*
* @param {mat4} a the source matrix
* @returns {Number} determinant of a
*/
mat4.determinant = function (a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32;
// Calculate the determinant
return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
};
/**
* Multiplies two mat4's
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the first operand
* @param {mat4} b the second operand
* @returns {mat4} out
*/
mat4.mul = mat4.multiply = function (out, a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
// Cache only the current line of the second matrix
var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
return out;
};
/**
* Translate a mat4 by the given vector
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to translate
* @param {vec3} v vector to translate by
* @returns {mat4} out
*/
mat4.translate = function (out, a, v) {
var x = v[0], y = v[1], z = v[2],
a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
};
/**
* Scales the mat4 by the dimensions in the given vec3
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to scale
* @param {vec3} v the vec3 to scale the matrix by
* @returns {mat4} out
**/
mat4.scale = function(out, a, v) {
var x = v[0], y = v[1], z = v[2];
out[0] = a[0] * x;
out[1] = a[1] * x;
out[2] = a[2] * x;
out[3] = a[3] * x;
out[4] = a[4] * y;
out[5] = a[5] * y;
out[6] = a[6] * y;
out[7] = a[7] * y;
out[8] = a[8] * z;
out[9] = a[9] * z;
out[10] = a[10] * z;
out[11] = a[11] * z;
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
};
/**
* Rotates a mat4 by the given angle
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @param {vec3} axis the axis to rotate around
* @returns {mat4} out
*/
mat4.rotate = function (out, a, rad, axis) {
var x = axis[0], y = axis[1], z = axis[2],
len = Math.sqrt(x * x + y * y + z * z),
s, c, t,
a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23,
b00, b01, b02,
b10, b11, b12,
b20, b21, b22;
if (Math.abs(len) < GLMAT_EPSILON) { return null; }
len = 1 / len;
x *= len;
y *= len;
z *= len;
s = Math.sin(rad);
c = Math.cos(rad);
t = 1 - c;
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
// Construct the elements of the rotation matrix
b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
// Perform rotation-specific matrix multiplication
out[0] = a00 * b00 + a10 * b01 + a20 * b02;
out[1] = a01 * b00 + a11 * b01 + a21 * b02;
out[2] = a02 * b00 + a12 * b01 + a22 * b02;
out[3] = a03 * b00 + a13 * b01 + a23 * b02;
out[4] = a00 * b10 + a10 * b11 + a20 * b12;
out[5] = a01 * b10 + a11 * b11 + a21 * b12;
out[6] = a02 * b10 + a12 * b11 + a22 * b12;
out[7] = a03 * b10 + a13 * b11 + a23 * b12;
out[8] = a00 * b20 + a10 * b21 + a20 * b22;
out[9] = a01 * b20 + a11 * b21 + a21 * b22;
out[10] = a02 * b20 + a12 * b21 + a22 * b22;
out[11] = a03 * b20 + a13 * b21 + a23 * b22;
if (a !== out) { // If the source and destination differ, copy the unchanged last row
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
return out;
};
/**
* Rotates a matrix by the given angle around the X axis
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
mat4.rotateX = function (out, a, rad) {
var s = Math.sin(rad),
c = Math.cos(rad),
a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7],
a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
if (a !== out) { // If the source and destination differ, copy the unchanged rows
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
// Perform axis-specific matrix multiplication
out[4] = a10 * c + a20 * s;
out[5] = a11 * c + a21 * s;
out[6] = a12 * c + a22 * s;
out[7] = a13 * c + a23 * s;
out[8] = a20 * c - a10 * s;
out[9] = a21 * c - a11 * s;
out[10] = a22 * c - a12 * s;
out[11] = a23 * c - a13 * s;
return out;
};
/**
* Rotates a matrix by the given angle around the Y axis
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
mat4.rotateY = function (out, a, rad) {
var s = Math.sin(rad),
c = Math.cos(rad),
a00 = a[0],
a01 = a[1],
a02 = a[2],
a03 = a[3],
a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
if (a !== out) { // If the source and destination differ, copy the unchanged rows
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
// Perform axis-specific matrix multiplication
out[0] = a00 * c - a20 * s;
out[1] = a01 * c - a21 * s;
out[2] = a02 * c - a22 * s;
out[3] = a03 * c - a23 * s;
out[8] = a00 * s + a20 * c;
out[9] = a01 * s + a21 * c;
out[10] = a02 * s + a22 * c;
out[11] = a03 * s + a23 * c;
return out;
};
/**
* Rotates a matrix by the given angle around the Z axis
*
* @param {mat4} out the receiving matrix
* @param {mat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
mat4.rotateZ = function (out, a, rad) {
var s = Math.sin(rad),
c = Math.cos(rad),
a00 = a[0],
a01 = a[1],
a02 = a[2],
a03 = a[3],
a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7];
if (a !== out) { // If the source and destination differ, copy the unchanged last row
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
// Perform axis-specific matrix multiplication
out[0] = a00 * c + a10 * s;
out[1] = a01 * c + a11 * s;
out[2] = a02 * c + a12 * s;
out[3] = a03 * c + a13 * s;
out[4] = a10 * c - a00 * s;
out[5] = a11 * c - a01 * s;
out[6] = a12 * c - a02 * s;
out[7] = a13 * c - a03 * s;
return out;
};
/**
* Creates a matrix from a quaternion rotation and vector translation
* This is equivalent to (but much faster than):
*
* mat4.identity(dest);
* mat4.translate(dest, vec);
* var quatMat = mat4.create();
* quat4.toMat4(quat, quatMat);
* mat4.multiply(dest, quatMat);
*
* @param {mat4} out mat4 receiving operation result
* @param {quat4} q Rotation quaternion
* @param {vec3} v Translation vector
* @returns {mat4} out
*/
mat4.fromRotationTranslation = function (out, q, v) {
// Quaternion math
var x = q[0], y = q[1], z = q[2], w = q[3],
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;
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = v[0];
out[13] = v[1];
out[14] = v[2];
out[15] = 1;
return out;
};
/**
* Generates a frustum matrix with the given bounds
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {Number} left Left bound of the frustum
* @param {Number} right Right bound of the frustum
* @param {Number} bottom Bottom bound of the frustum
* @param {Number} top Top bound of the frustum
* @param {Number} near Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @returns {mat4} out
*/
mat4.frustum = function (out, left, right, bottom, top, near, far) {
var rl = 1 / (right - left),
tb = 1 / (top - bottom),
nf = 1 / (near - far);
out[0] = (near * 2) * rl;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = (near * 2) * tb;
out[6] = 0;
out[7] = 0;
out[8] = (right + left) * rl;
out[9] = (top + bottom) * tb;
out[10] = (far + near) * nf;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[14] = (far * near * 2) * nf;
out[15] = 0;
return out;
};
/**
* Generates a perspective projection matrix with the given bounds
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {number} fovy Vertical field of view in radians
* @param {number} aspect Aspect ratio. typically viewport width/height
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum
* @returns {mat4} out
*/
mat4.perspective = function (out, fovy, aspect, near, far) {
var f = 1.0 / Math.tan(fovy / 2),
nf = 1 / (near - far);
out[0] = f / aspect;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = f;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = (far + near) * nf;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[14] = (2 * far * near) * nf;
out[15] = 0;
return out;
};
/**
* Generates a orthogonal projection matrix with the given bounds
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {number} left Left bound of the frustum
* @param {number} right Right bound of the frustum
* @param {number} bottom Bottom bound of the frustum
* @param {number} top Top bound of the frustum
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum
* @returns {mat4} out
*/
mat4.ortho = function (out, left, right, bottom, top, near, far) {
var lr = 1 / (left - right),
bt = 1 / (bottom - top),
nf = 1 / (near - far);
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
return out;
};
/**
* Generates a look-at matrix with the given eye position, focal point, and up axis
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {vec3} eye Position of the viewer
* @param {vec3} center Point the viewer is looking at
* @param {vec3} up vec3 pointing up
* @returns {mat4} out
*/
mat4.lookAt = function (out, eye, center, up) {
var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
eyex = eye[0],
eyey = eye[1],
eyez = eye[2],
upx = up[0],
upy = up[1],
upz = up[2],
centerx = center[0],
centery = center[1],
centerz = center[2];
if (Math.abs(eyex - centerx) < GLMAT_EPSILON &&
Math.abs(eyey - centery) < GLMAT_EPSILON &&
Math.abs(eyez - centerz) < GLMAT_EPSILON) {
return mat4.identity(out);
}
z0 = eyex - centerx;
z1 = eyey - centery;
z2 = eyez - centerz;
len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
z0 *= len;
z1 *= len;
z2 *= len;
x0 = upy * z2 - upz * z1;
x1 = upz * z0 - upx * z2;
x2 = upx * z1 - upy * z0;
len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
if (!len) {
x0 = 0;
x1 = 0;
x2 = 0;
} else {
len = 1 / len;
x0 *= len;
x1 *= len;
x2 *= len;
}
y0 = z1 * x2 - z2 * x1;
y1 = z2 * x0 - z0 * x2;
y2 = z0 * x1 - z1 * x0;
len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
if (!len) {
y0 = 0;
y1 = 0;
y2 = 0;
} else {
len = 1 / len;
y0 *= len;
y1 *= len;
y2 *= len;
}
out[0] = x0;
out[1] = y0;
out[2] = z0;
out[3] = 0;
out[4] = x1;
out[5] = y1;
out[6] = z1;
out[7] = 0;
out[8] = x2;
out[9] = y2;
out[10] = z2;
out[11] = 0;
out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
out[15] = 1;
return out;
};
/**
* Returns a string representation of a mat4
*
* @param {mat4} mat matrix to represent as a string
* @returns {String} string representation of the matrix
*/
mat4.str = function (a) {
return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.mat4 = mat4;
}
;
/* Copyright (c) 2012, Brandon Jones, Colin MacKenzie IV. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/**
* @class Quaternion
* @name quat
*/
var quat = {};
var quatIdentity = new Float32Array([0, 0, 0, 1]);
if(!GLMAT_EPSILON) {
var GLMAT_EPSILON = 0.000001;
}
/**
* Creates a new identity quat
*
* @returns {quat} a new quaternion
*/
quat.create = function() {
return new Float32Array(quatIdentity);
};
/**
* Creates a new quat initialized with values from an existing quaternion
*
* @param {quat} a quaternion to clone
* @returns {quat} a new quaternion
*/
quat.clone = vec4.clone;
/**
* Creates a new quat initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {quat} a new quaternion
*/
quat.fromValues = vec4.fromValues;
/**
* Copy the values from one quat to another
*
* @param {quat} out the receiving quaternion
* @param {quat} a the source quaternion
* @returns {quat} out
*/
quat.copy = vec4.copy;
/**
* Set the components of a quat to the given values
*
* @param {quat} out the receiving quaternion
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {quat} out
*/
quat.set = vec4.set;
/**
* Set a quat to the identity quaternion
*
* @param {quat} out the receiving quaternion
* @returns {quat} out
*/
quat.identity = function(out) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
out[3] = 1;
return out;
};
/**
* Sets a quat from the given angle and rotation axis,
* then returns it.
*
* @param {quat} out the receiving quaternion
* @param {vec3} axis the axis around which to rotate
* @param {Number} rad the angle in radians
* @returns {quat} out
**/
quat.setAxisAngle = function(out, axis, rad) {
rad = rad * 0.5;
var s = Math.sin(rad);
out[0] = s * axis[0];
out[1] = s * axis[1];
out[2] = s * axis[2];
out[3] = Math.cos(rad);
return out;
};
/**
* Adds two quat's
*
* @param {quat} out the receiving quaternion
* @param {quat} a the first operand
* @param {quat} b the second operand
* @returns {quat} out
*/
quat.add = vec4.add;
/**
* Multiplies two quat's
*
* @param {quat} out the receiving quaternion
* @param {quat} a the first operand
* @param {quat} b the second operand
* @returns {quat} out
*/
quat.mul = quat.multiply = function(out, a, b) {
var ax = a[0], ay = a[1], az = a[2], aw = a[3],
bx = b[0], by = b[1], bz = b[2], bw = b[3];
out[0] = ax * bw + aw * bx + ay * bz - az * by;
out[1] = ay * bw + aw * by + az * bx - ax * bz;
out[2] = az * bw + aw * bz + ax * by - ay * bx;
out[3] = aw * bw - ax * bx - ay * by - az * bz;
return out;
};
/**
* Scales a quat by a scalar number
*
* @param {quat} out the receiving vector
* @param {quat} a the vector to scale
* @param {quat} b amount to scale the vector by
* @returns {quat} out
*/
quat.scale = vec4.scale;
/**
* Rotates a quaternion by the given angle around the X axis
*
* @param {quat} out quat receiving operation result
* @param {quat} a quat to rotate
* @param {number} rad angle (in radians) to rotate
* @returns {quat} out
*/
quat.rotateX = function (out, a, rad) {
rad *= 0.5;
var ax = a[0], ay = a[1], az = a[2], aw = a[3],
bx = Math.sin(rad), bw = Math.cos(rad);
out[0] = ax * bw + aw * bx;
out[1] = ay * bw + az * bx;
out[2] = az * bw - ay * bx;
out[3] = aw * bw - ax * bx;
return out;
};
/**
* Rotates a quaternion by the given angle around the X axis
*
* @param {quat} out quat receiving operation result
* @param {quat} a quat to rotate
* @param {number} rad angle (in radians) to rotate
* @returns {quat} out
*/
quat.rotateY = function (out, a, rad) {
rad *= 0.5;
var ax = a[0], ay = a[1], az = a[2], aw = a[3],
by = Math.sin(rad), bw = Math.cos(rad);
out[0] = ax * bw - az * by;
out[1] = ay * bw + aw * by;
out[2] = az * bw + ax * by;
out[3] = aw * bw - ay * by;
return out;
};
/**
* Rotates a quaternion by the given angle around the X axis
*
* @param {quat} out quat receiving operation result
* @param {quat} a quat to rotate
* @param {number} rad angle (in radians) to rotate
* @returns {quat} out
*/
quat.rotateZ = function (out, a, rad) {
rad *= 0.5;
var ax = a[0], ay = a[1], az = a[2], aw = a[3],
bz = Math.sin(rad), bw = Math.cos(rad);
out[0] = ax * bw + ay * bz;
out[1] = ay * bw - ax * bz;
out[2] = az * bw + aw * bz;
out[3] = aw * bw - az * bz;
return out;
};
/**
* Calculates the W component of a quat from the X, Y, and Z components.
* Assumes that quaternion is 1 unit in length.
* Any existing W component will be ignored.
*
* @param {quat} out the receiving quaternion
* @param {quat} a quat to calculate W component of
* @returns {quat} out
*/
quat.calculateW = function (out, a) {
var x = a[0], y = a[1], z = a[2];
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
return out;
};
/**
* Caclulates the dot product of two quat's
*
* @param {quat} a the first operand
* @param {quat} b the second operand
* @returns {Number} dot product of a and b
*/
quat.dot = vec4.dot;
/**
* Performs a linear interpolation between two quat's
*
* @param {quat} out the receiving quaternion
* @param {quat} a the first operand
* @param {quat} b the second operand
* @param {Number} t interpolation amount between the two inputs
* @returns {quat} out
*/
quat.lerp = vec4.lerp;
/**
* Performs a spherical linear interpolation between two quat
*
* @param {quat} out the receiving quaternion
* @param {quat} a the first operand
* @param {quat} b the second operand
* @param {Number} t interpolation amount between the two inputs
* @returns {quat} out
*/
quat.slerp = function (out, a, b, t) {
var ax = a[0], ay = a[1], az = a[2], aw = a[3],
bx = b[0], by = b[1], bz = b[2], bw = a[3];
var cosHalfTheta = ax * bx + ay * by + az * bz + aw * bw,
halfTheta,
sinHalfTheta,
ratioA,
ratioB;
if (Math.abs(cosHalfTheta) >= 1.0) {
if (out !== a) {
out[0] = ax;
out[1] = ay;
out[2] = az;
out[3] = aw;
}
return out;
}
halfTheta = Math.acos(cosHalfTheta);
sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta);
if (Math.abs(sinHalfTheta) < 0.001) {
out[0] = (ax * 0.5 + bx * 0.5);
out[1] = (ay * 0.5 + by * 0.5);
out[2] = (az * 0.5 + bz * 0.5);
out[3] = (aw * 0.5 + bw * 0.5);
return out;
}
ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta;
ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
out[0] = (ax * ratioA + bx * ratioB);
out[1] = (ay * ratioA + by * ratioB);
out[2] = (az * ratioA + bz * ratioB);
out[3] = (aw * ratioA + bw * ratioB);
return out;
};
/**
* Calculates the inverse of a quat
*
* @param {quat} out the receiving quaternion
* @param {quat} a quat to calculate inverse of
* @returns {quat} out
*/
quat.invert = function(out, a) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
dot = a0*a0 + a1*a1 + a2*a2 + a3*a3,
invDot = dot ? 1.0/dot : 0;
// TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
out[0] = -a0*invDot;
out[1] = -a1*invDot;
out[2] = -a2*invDot;
out[3] = a3*invDot;
return out;
};
/**
* Calculates the conjugate of a quat
* If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.
*
* @param {quat} out the receiving quaternion
* @param {quat} a quat to calculate conjugate of
* @returns {quat} out
*/
quat.conjugate = function (out, a) {
out[0] = -a[0];
out[1] = -a[1];
out[2] = -a[2];
out[3] = a[3];
return out;
};
/**
* Caclulates the length of a quat
*
* @param {quat} a vector to calculate length of
* @returns {Number} length of a
*/
quat.len = quat.length = vec4.length;
/**
* Caclulates the squared length of a quat
*
* @param {quat} a vector to calculate squared length of
* @returns {Number} squared length of a
*/
quat.sqrLen = quat.squaredLength = vec4.squaredLength;
/**
* Normalize a quat
*
* @param {quat} out the receiving quaternion
* @param {quat} a quaternion to normalize
* @returns {quat} out
*/
quat.normalize = vec4.normalize;
/**
* Returns a string representation of a quatenion
*
* @param {quat} vec vector to represent as a string
* @returns {String} string representation of the vector
*/
quat.str = function (a) {
return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
};
if(typeof(exports) !== 'undefined') {
exports.quat = quat;
}
;
})(shim.exports);
})();
},{}],72:[function(require,module,exports){
module.exports = inherits
function inherits (c, p, proto) {
proto = proto || {}
var e = {}
;[c.prototype, proto].forEach(function (s) {
Object.getOwnPropertyNames(s).forEach(function (k) {
e[k] = Object.getOwnPropertyDescriptor(s, k)
})
})
c.prototype = Object.create(p.prototype, e)
c.super = p
}
//function Child () {
// Child.super.call(this)
// console.error([this
// ,this.constructor
// ,this.constructor === Child
// ,this.constructor.super === Parent
// ,Object.getPrototypeOf(this) === Child.prototype
// ,Object.getPrototypeOf(Object.getPrototypeOf(this))
// === Parent.prototype
// ,this instanceof Child
// ,this instanceof Parent])
//}
//function Parent () {}
//inherits(Child, Parent)
//new Child
},{}],73:[function(require,module,exports){
var lock = require('pointer-lock')
, drag = require('drag-stream')
, full = require('fullscreen')
var EE = require('events').EventEmitter
, Stream = require('stream').Stream
module.exports = interact
function interact(el, skiplock) {
var ee = new EE
, internal
if(!lock.available() || skiplock) {
internal = usedrag(el)
} else {
internal = uselock(el, politelydeclined)
}
ee.release = function() { internal.release && internal.release() }
ee.request = function() { internal.request && internal.request() }
ee.destroy = function() { internal.destroy && internal.destroy() }
ee.pointerAvailable = function() { return lock.available() }
ee.fullscreenAvailable = function() { return full.available() }
forward()
return ee
function politelydeclined() {
ee.emit('opt-out')
internal.destroy()
internal = usedrag(el)
forward()
}
function forward() {
internal.on('attain', function(stream) {
ee.emit('attain', stream)
})
internal.on('release', function() {
ee.emit('release')
})
}
}
function uselock(el, declined) {
var pointer = lock(el)
, fs = full(el)
pointer.on('needs-fullscreen', function() {
fs.once('attain', function() {
pointer.request()
})
fs.request()
})
pointer.on('error', declined)
return pointer
}
function usedrag(el) {
var ee = new EE
, d = drag(el)
, stream
d.paused = true
d.on('resume', function() {
stream = new Stream
stream.readable = true
stream.initial = null
})
d.on('data', function(datum) {
if(!stream) {
stream = new Stream
stream.readable = true
stream.initial = null
}
if(!stream.initial) {
stream.initial = {
x: datum.dx
, y: datum.dy
, t: datum.dt
}
return ee.emit('attain', stream)
}
if(stream.paused) {
ee.emit('release')
stream.emit('end')
stream.readable = false
stream.emit('close')
stream = null
}
stream.emit('data', datum)
})
return ee
}
},{"drag-stream":74,"events":6,"fullscreen":80,"pointer-lock":81,"stream":22}],74:[function(require,module,exports){
module.exports = dragstream
var Stream = require('stream')
, read = require('domnode-dom').createReadStream
, through = require('through')
function dragstream(el) {
var body = el.ownerDocument.body
, down = read(el, 'mousedown')
, up = read(body, 'mouseup', false)
, move = read(body, 'mousemove', false)
, anchor = {x: 0, y: 0, t: 0}
, drag = through(on_move)
// default to "paused"
drag.pause()
down.on('data', on_down)
up.on('data', on_up)
return move.pipe(drag)
// listeners:
function on_move(ev) {
if(drag.paused) return
drag.emit('data', datum(
ev.screenX - anchor.x
, ev.screenY - anchor.y
, +new Date
))
anchor.x = ev.screenX
anchor.y = ev.screenY
}
function on_down(ev) {
anchor.x = ev.screenX
anchor.y = ev.screenY
anchor.t = +new Date
drag.resume()
drag.emit('data', datum(
anchor.x
, anchor.y
, anchor.t
))
}
function on_up(ev) {
drag.pause()
drag.emit('data', datum(
ev.screenX - anchor.x
, ev.screenY - anchor.y
, +new Date
))
}
function datum(dx, dy, when) {
return {
dx: dx
, dy: dy
, dt: when - anchor.t
}
}
}
},{"domnode-dom":75,"stream":22,"through":79}],75:[function(require,module,exports){
module.exports = require('./lib/index')
},{"./lib/index":76}],76:[function(require,module,exports){
var WriteStream = require('./writable')
, ReadStream = require('./readable')
, DOMStream = {}
DOMStream.WriteStream = WriteStream
DOMStream.ReadStream = ReadStream
DOMStream.createAppendStream = function(el, mimetype) {
return new DOMStream.WriteStream(
el
, DOMStream.WriteStream.APPEND
, mimetype
)
}
DOMStream.createWriteStream = function(el, mimetype) {
return new DOMStream.WriteStream(
el
, DOMStream.WriteStream.WRITE
, mimetype
)
}
DOMStream.createReadStream =
DOMStream.createEventStream = function(el, type, preventDefault) {
preventDefault = preventDefault === undefined ? true : preventDefault
return new DOMStream.ReadStream(
el
, type
, preventDefault
)
}
module.exports = DOMStream
},{"./readable":77,"./writable":78}],77:[function(require,module,exports){
module.exports = DOMStream
var Stream = require('stream').Stream
var listener = function(el, type, onmsg) {
return el.addEventListener(type, onmsg, false)
}
if(typeof $ !== 'undefined')
listener = function(el, type, onmsg) {
return el = $(el)[type](onmsg)
}
if(typeof document !== 'undefined' && !document.createElement('div').addEventListener)
listener = function(el, type, onmsg) {
return el.attachEvent('on'+type, onmsg)
}
function DOMStream(el, eventType, shouldPreventDefault) {
this.el = el
this.eventType = eventType
this.shouldPreventDefault = shouldPreventDefault
var self = this
if(el && this.eventType)
listener(
this.el
, this.eventType
, function() { return self.listen.apply(self, arguments) }
)
Stream.call(this)
}
var cons = DOMStream
, proto = cons.prototype = Object.create(Stream.prototype)
proto.constructor = cons
proto.listen = function(ev) {
if(this.shouldPreventDefault)
ev.preventDefault ? ev.preventDefault() : (ev.returnValue = false)
var collectData =
this.eventType === 'submit' ||
this.eventType === 'change' ||
this.eventType === 'keydown' ||
this.eventType === 'keyup' ||
this.eventType === 'input'
if(collectData) {
if(this.el.tagName.toUpperCase() === 'FORM')
return this.handleFormSubmit(ev)
return this.emit('data', valueFromElement(this.el))
}
this.emit('data', ev)
}
proto.handleFormSubmit = function(ev) {
var elements = []
if(this.el.querySelectorAll) {
elements = this.el.querySelectorAll('input,textarea,select')
} else {
var inputs = {'INPUT':true, 'TEXTAREA':true, 'SELECT':true}
var recurse = function(el) {
for(var i = 0, len = el.childNodes.length; i < len; ++i) {
if(el.childNodes[i].tagName) {
if(inputs[el.childNodes[i].tagName.toUpperCase()]) {
elements.push(el)
} else {
recurse(el.childNodes[i])
}
}
}
}
recurse(this.el)
}
var output = {}
, attr
, val
for(var i = 0, len = elements.length; i < len; ++i) {
attr = elements[i].getAttribute('name')
val = valueFromElement(elements[i])
if(val !== null) {
output[attr] = val
}
}
return this.emit('data', output)
}
function valueFromElement(el) {
switch(el.getAttribute('type')) {
case 'radio':
return el.checked ? el.value : null
case 'checkbox':
return 'data', el.checked
}
return el.value
}
},{"stream":22}],78:[function(require,module,exports){
module.exports = DOMStream
var Stream = require('stream').Stream
function DOMStream(el, mode, mimetype) {
this.el = el
this.mode = mode
this.mimetype = mimetype || 'text/html'
Stream.call(this)
}
var cons = DOMStream
, proto = cons.prototype = Object.create(Stream.prototype)
proto.constructor = cons
cons.APPEND = 0
cons.WRITE = 1
proto.writable = true
proto.setMimetype = function(mime) {
this.mimetype = mime
}
proto.write = function(data) {
var result = (this.mode === cons.APPEND) ? this.append(data) : this.insert(data)
this.emit('data', this.el.childNodes)
return result
}
proto.end = function() {
}
proto.insert = function(data) {
this.el.innerHTML = ''
return this.append(data)
}
proto.append = function(data) {
var result = this[this.resolveMimetypeHandler()](data)
for(var i = 0, len = result.length; i < len; ++i) {
this.el.appendChild(result[i])
}
return true
}
proto.resolveMimetypeHandler = function() {
var type = this.mimetype.replace(/(\/\w)/, function(x) {
return x.slice(1).toUpperCase()
})
type = type.charAt(0).toUpperCase() + type.slice(1)
return 'construct'+type
}
proto.constructTextHtml = function(data) {
var isTableFragment = /(tr|td|th)/.test(data) && !/table/.test(data)
, div
if(isTableFragment) {
// wuh-oh.
div = document.createElement('table')
}
div = div || document.createElement('div')
div.innerHTML = data
return [].slice.call(div.childNodes)
}
proto.constructTextPlain = function(data) {
var textNode = document.createTextNode(data)
return [textNode]
}
},{"stream":22}],79:[function(require,module,exports){
(function (process){
var Stream = require('stream')
// through
//
// a stream that does nothing but re-emit the input.
// useful for aggregating a series of changing but not ending streams into one stream)
exports = module.exports = through
through.through = through
//create a readable writable stream.
function through (write, end) {
write = write || function (data) { this.emit('data', data) }
end = end || function () { this.emit('end') }
var ended = false, destroyed = false
var stream = new Stream(), buffer = []
stream.buffer = buffer
stream.readable = stream.writable = true
stream.paused = false
stream.write = function (data) {
write.call(this, data)
return !stream.paused
}
function drain() {
while(buffer.length && !stream.paused) {
var data = buffer.shift()
if(null === data)
return stream.emit('end')
else
stream.emit('data', data)
}
}
stream.queue = function (data) {
buffer.push(data)
drain()
}
//this will be registered as the first 'end' listener
//must call destroy next tick, to make sure we're after any
//stream piped from here.
//this is only a problem if end is not emitted synchronously.
//a nicer way to do this is to make sure this is the last listener for 'end'
stream.on('end', function () {
stream.readable = false
if(!stream.writable)
process.nextTick(function () {
stream.destroy()
})
})
function _end () {
stream.writable = false
end.call(stream)
if(!stream.readable)
stream.destroy()
}
stream.end = function (data) {
if(ended) return
ended = true
if(arguments.length) stream.write(data)
_end() // will emit or queue
}
stream.destroy = function () {
if(destroyed) return
destroyed = true
ended = true
buffer.length = 0
stream.writable = stream.readable = false
stream.emit('close')
}
stream.pause = function () {
if(stream.paused) return
stream.paused = true
stream.emit('pause')
}
stream.resume = function () {
if(stream.paused) {
stream.paused = false
}
drain()
//may have become paused again,
//as drain emits 'data'.
if(!stream.paused)
stream.emit('drain')
}
return stream
}
}).call(this,require('_process'))
},{"_process":10,"stream":22}],80:[function(require,module,exports){
module.exports = fullscreen
fullscreen.available = available
var EE = require('events').EventEmitter
function available() {
return !!shim(document.body)
}
function fullscreen(el) {
var ael = el.addEventListener || el.attachEvent
, doc = el.ownerDocument
, body = doc.body
, rfs = shim(el)
, ee = new EE
var vendors = ['', 'webkit', 'moz', 'ms', 'o']
for(var i = 0, len = vendors.length; i < len; ++i) {
ael.call(doc, vendors[i]+'fullscreenchange', onfullscreenchange)
ael.call(doc, vendors[i]+'fullscreenerror', onfullscreenerror)
}
ee.release = release
ee.request = request
ee.target = fullscreenelement
if(!shim) {
setTimeout(function() {
ee.emit('error', new Error('fullscreen is not supported'))
}, 0)
}
return ee
function onfullscreenchange() {
if(!fullscreenelement()) {
return ee.emit('release')
}
ee.emit('attain')
}
function onfullscreenerror() {
ee.emit('error')
}
function request() {
return rfs.call(el)
}
function release() {
(el.exitFullscreen ||
el.exitFullscreen ||
el.webkitExitFullScreen ||
el.webkitExitFullscreen ||
el.mozExitFullScreen ||
el.mozExitFullscreen ||
el.msExitFullScreen ||
el.msExitFullscreen ||
el.oExitFullScreen ||
el.oExitFullscreen).call(el)
}
function fullscreenelement() {
return 0 ||
doc.fullScreenElement ||
doc.fullscreenElement ||
doc.webkitFullScreenElement ||
doc.webkitFullscreenElement ||
doc.mozFullScreenElement ||
doc.mozFullscreenElement ||
doc.msFullScreenElement ||
doc.msFullscreenElement ||
doc.oFullScreenElement ||
doc.oFullscreenElement ||
null
}
}
function shim(el) {
return (el.requestFullscreen ||
el.webkitRequestFullscreen ||
el.webkitRequestFullScreen ||
el.mozRequestFullscreen ||
el.mozRequestFullScreen ||
el.msRequestFullscreen ||
el.msRequestFullScreen ||
el.oRequestFullscreen ||
el.oRequestFullScreen)
}
},{"events":6}],81:[function(require,module,exports){
module.exports = pointer
pointer.available = available
var EE = require('events').EventEmitter
, Stream = require('stream').Stream
function available() {
return !!shim(document.body)
}
function pointer(el) {
var ael = el.addEventListener || el.attachEvent
, rel = el.removeEventListener || el.detachEvent
, doc = el.ownerDocument
, body = doc.body
, rpl = shim(el)
, out = {dx: 0, dy: 0, dt: 0}
, ee = new EE
, stream = null
, lastPageX, lastPageY
, needsFullscreen = false
, mouseDownMS
ael.call(el, 'mousedown', onmousedown, false)
ael.call(el, 'mouseup', onmouseup, false)
ael.call(body, 'mousemove', onmove, false)
var vendors = ['', 'webkit', 'moz', 'ms', 'o']
for(var i = 0, len = vendors.length; i < len; ++i) {
ael.call(doc, vendors[i]+'pointerlockchange', onpointerlockchange)
ael.call(doc, vendors[i]+'pointerlockerror', onpointerlockerror)
}
ee.release = release
ee.target = pointerlockelement
ee.request = onmousedown
ee.destroy = function() {
rel.call(el, 'mouseup', onmouseup, false)
rel.call(el, 'mousedown', onmousedown, false)
rel.call(el, 'mousemove', onmove, false)
}
if(!shim) {
setTimeout(function() {
ee.emit('error', new Error('pointer lock is not supported'))
}, 0)
}
return ee
function onmousedown(ev) {
if(pointerlockelement()) {
return
}
mouseDownMS = +new Date
rpl.call(el)
}
function onmouseup(ev) {
if(!needsFullscreen) {
return
}
ee.emit('needs-fullscreen')
needsFullscreen = false
}
function onpointerlockchange(ev) {
if(!pointerlockelement()) {
if(stream) release()
return
}
stream = new Stream
stream.readable = true
stream.initial = {x: lastPageX, y: lastPageY, t: Date.now()}
ee.emit('attain', stream)
}
function onpointerlockerror(ev) {
var dt = +(new Date) - mouseDownMS
if(dt < 100) {
// we errored immediately, we need to do fullscreen first.
needsFullscreen = true
return
}
if(stream) {
stream.emit('error', ev)
stream = null
}
}
function release() {
ee.emit('release')
if(stream) {
stream.emit('end')
stream.readable = false
stream.emit('close')
stream = null
}
var pel = pointerlockelement()
if(!pel) {
return
}
(doc.exitPointerLock ||
doc.mozExitPointerLock ||
doc.webkitExitPointerLock ||
doc.msExitPointerLock ||
doc.oExitPointerLock).call(doc)
}
function onmove(ev) {
lastPageX = ev.pageX
lastPageY = ev.pageY
if(!stream) return
// we're reusing a single object
// because I'd like to avoid piling up
// a ton of objects for the garbage
// collector.
out.dx =
ev.movementX || ev.webkitMovementX ||
ev.mozMovementX || ev.msMovementX ||
ev.oMovementX || 0
out.dy =
ev.movementY || ev.webkitMovementY ||
ev.mozMovementY || ev.msMovementY ||
ev.oMovementY || 0
out.dt = Date.now() - stream.initial.t
ee.emit('data', out)
stream.emit('data', out)
}
function pointerlockelement() {
return 0 ||
doc.pointerLockElement ||
doc.mozPointerLockElement ||
doc.webkitPointerLockElement ||
doc.msPointerLockElement ||
doc.oPointerLockElement ||
null
}
}
function shim(el) {
return el.requestPointerLock ||
el.webkitRequestPointerLock ||
el.mozRequestPointerLock ||
el.msRequestPointerLock ||
el.oRequestPointerLock ||
null
}
},{"events":6,"stream":22}],82:[function(require,module,exports){
var ever = require('ever')
, vkey = require('vkey')
, max = Math.max
module.exports = function(el, bindings, state) {
if(bindings === undefined || !el.ownerDocument) {
state = bindings
bindings = el
el = this.document.body
}
var ee = ever(el)
, measured = {}
, enabled = true
state = state || {}
// always initialize the state.
for(var key in bindings) {
if(bindings[key] === 'enabled' ||
bindings[key] === 'enable' ||
bindings[key] === 'disable' ||
bindings[key] === 'destroy') {
throw new Error(bindings[key]+' is reserved')
}
state[bindings[key]] = 0
measured[key] = 1
}
ee.on('keyup', wrapped(onoff(kb, false)))
ee.on('keydown', wrapped(onoff(kb, true)))
ee.on('mouseup', wrapped(onoff(mouse, false)))
ee.on('mousedown', wrapped(onoff(mouse, true)))
state.enabled = function() {
return enabled
}
state.enable = enable_disable(true)
state.disable = enable_disable(false)
state.destroy = function() {
ee.removeAllListeners()
}
return state
function clear() {
// always initialize the state.
for(var key in bindings) {
state[bindings[key]] = 0
measured[key] = 1
}
}
function enable_disable(on_or_off) {
return function() {
clear()
enabled = on_or_off
return this
}
}
function wrapped(fn) {
return function(ev) {
if(enabled) {
ev.preventDefault()
fn(ev)
} else {
return
}
}
}
function onoff(find, on_or_off) {
return function(ev) {
var key = find(ev)
, binding = bindings[key]
if(binding) {
state[binding] += on_or_off ? max(measured[key]--, 0) : -(measured[key] = 1)
if(!on_or_off && state[binding] < 0) {
state[binding] = 0
}
}
}
}
function mouse(ev) {
return '<mouse '+ev.which+'>'
}
function kb(ev) {
return vkey[ev.keyCode] || ev.char
}
}
},{"ever":83,"vkey":86}],83:[function(require,module,exports){
arguments[4][63][0].apply(exports,arguments)
},{"./init.json":84,"./types.json":85,"dup":63,"events":6}],84:[function(require,module,exports){
arguments[4][64][0].apply(exports,arguments)
},{"dup":64}],85:[function(require,module,exports){
arguments[4][65][0].apply(exports,arguments)
},{"dup":65}],86:[function(require,module,exports){
arguments[4][61][0].apply(exports,arguments)
},{"dup":61}],87:[function(require,module,exports){
module.exports = pin
var pins = {}
, stack_holder = {}
, pin_holder
function make_pin_for(name, obj) {
var container = document.createElement('div')
, header = document.createElement('h4')
, body = document.createElement('pre')
container.style.background = 'white'
container.style.marginBottom = '4px'
container.appendChild(header)
container.appendChild(body)
header.textContents = header.innerText = obj && obj.repr ? obj.repr() : name
body.style.padding = '8px'
if(!pin_holder) {
pin_holder = document.createElement('div')
pin_holder.style.position = 'absolute'
pin_holder.style.top =
pin_holder.style.right = '4px'
document.body.appendChild(pin_holder)
}
pin_holder.appendChild(container)
return (pins[name] = pins[name] || []).push({body: body, last: -Infinity, for_object: obj}), pins[name]
}
function update_pin(item, into, retain, depth) {
if(!retain) into.innerHTML = ''
if(depth > 1) return
depth = depth || 0
switch(typeof item) {
case 'number': into.innerText += item.toFixed(3); break
case 'string': into.innerText += '"'+item+'"'; break
case 'undefined':
case 'object':
if(item) {
for(var key in item) if(item.hasOwnProperty(key)) {
into.innerText += key +':'
update_pin(item[key], into, true, depth+1)
into.innerText += '\n'
}
break
}
case 'boolean': into.innerText += ''+item; break
}
}
function pin(item, every, obj, name) {
if(!name) Error.captureStackTrace(stack_holder)
var location = name || stack_holder.stack.split('\n').slice(2)[0].replace(/^\s+at /g, '')
, target = pins[location] || make_pin_for(location, obj)
, now = Date.now()
, every = every || 0
if(arguments.length < 3) target = target[0]
else {
for(var i = 0, len = target.length; i < len; ++i) {
if(target[i].for_object === obj) {
target = target[i]
break
}
}
if(i === len) {
pins[location].push(target = make_pin_for(location, obj))
}
}
if(now - target.last > every) {
update_pin(item, target.body)
target.last = now
}
}
},{}],88:[function(require,module,exports){
module.exports = raf
var EE = require('events').EventEmitter
, global = typeof window === 'undefined' ? this : window
var _raf =
global.requestAnimationFrame ||
global.webkitRequestAnimationFrame ||
global.mozRequestAnimationFrame ||
global.msRequestAnimationFrame ||
global.oRequestAnimationFrame ||
(global.setImmediate ? function(fn, el) {
setImmediate(fn)
} :
function(fn, el) {
setTimeout(fn, 0)
})
function raf(el) {
var now = raf.now()
, ee = new EE
ee.pause = function() { ee.paused = true }
ee.resume = function() { ee.paused = false }
_raf(iter, el)
return ee
function iter(timestamp) {
var _now = raf.now()
, dt = _now - now
now = _now
ee.emit('data', dt)
if(!ee.paused) {
_raf(iter, el)
}
}
}
raf.polyfill = _raf
raf.now = function() { return Date.now() }
},{"events":6}],89:[function(require,module,exports){
module.exports = SpatialEventEmitter
var slice = [].slice
, Tree = require('./tree')
, aabb = require('aabb-3d')
function SpatialEventEmitter() {
this.root = null
this.infinites = {}
}
var cons = SpatialEventEmitter
, proto = cons.prototype
proto.size = 16
proto.addListener =
proto.addEventListener =
proto.on = function(event, bbox, listener) {
if(!finite(bbox)) {
(this.infinites[event] = this.infinites[event] || []).push({
bbox: bbox
, func: listener
})
return this
}
(this.root = this.root || this.create_root(bbox))
.add(event, bbox, listener)
return this
}
proto.once = function(event, bbox, listener) {
var self = this
self.on(event, bbox, function once() {
listener.apply(null, arguments)
self.remove(event, once)
})
return self
}
proto.removeListener =
proto.removeEventListener =
proto.remove = function(event, listener) {
if(this.root) {
this.root.remove(event, listener)
}
if(!this.infinites[event]) {
return this
}
for(var i = 0, len = this.infinites[event].length; i < len; ++i) {
if(this.infinites[event][i].func === listener) {
break
}
}
if(i !== len) {
this.infinites[event].splice(i, 1)
}
return this
}
proto.emit = function(event, bbox/*, ...args */) {
var args = slice.call(arguments, 2)
// support point emitting
if('0' in bbox) {
bbox = aabb(bbox, [0, 0, 0])
}
if(this.root) {
this.root.send(event, bbox, args)
}
if(!this.infinites[event]) {
return this
}
var list = this.infinites[event].slice()
for(var i = 0, len = list.length; i < len; ++i) {
if(list[i].bbox.intersects(bbox)) {
list[i].func.apply(null, args)
}
}
return this
}
proto.rootSize = function(size) {
proto.size = size
}
proto.create_root = function(bbox) {
var self = this
, size = self.size
, base = [
Math.floor(bbox.x0() / size) * size
, Math.floor(bbox.y0() / size) * size
, Math.floor(bbox.z0() / size) * size
]
, tree_bbox = new bbox.constructor(base, [size, size, size])
function OurTree(size, bbox) {
Tree.call(this, size, bbox, null)
}
OurTree.prototype = Object.create(Tree.prototype)
OurTree.prototype.constructor = OurTree
OurTree.prototype.grow = function(new_root) {
self.root = new_root
}
OurTree.prototype.min_size = size
return new OurTree(size, tree_bbox)
}
function finite(bbox) {
return isFinite(bbox.x0()) &&
isFinite(bbox.x1()) &&
isFinite(bbox.y0()) &&
isFinite(bbox.y1()) &&
isFinite(bbox.z0()) &&
isFinite(bbox.z1())
}
},{"./tree":90,"aabb-3d":69}],90:[function(require,module,exports){
module.exports = Tree
var aabb = require('aabb-3d')
function Tree(size, bbox, parent) {
this.listeners = {}
this.size = size
this.bbox = bbox
this.parent = parent
this.children = []
}
var cons = Tree
, proto = cons.prototype
proto.add = function(event, bbox, listener) {
if(!this.parent && !this.contains(bbox)) {
return this.expand(bbox).add(event, bbox, listener)
}
for(var i = 0, len = this.children.length; i < len; ++i) {
if(this.children[i].contains(bbox)) {
return this.children[i].add(event, bbox, listener)
}
}
var size = this.size / 2
if(size > this.min_size && bbox.vec[0] < size && bbox.vec[1] < size && bbox.vec[2] < size) {
// if it fits into a child node, make that childnode
if(Math.floor(bbox.x0() / size) === Math.floor(bbox.x1() / size) &&
Math.floor(bbox.y0() / size) === Math.floor(bbox.y1() / size) &&
Math.floor(bbox.z0() / size) === Math.floor(bbox.z1() / size)) {
var inst = new this.constructor(
size
, aabb([
Math.floor(bbox.x0() / size) * size
, Math.floor(bbox.y0() / size) * size
, Math.floor(bbox.z0() / size) * size
]
, [size, size, size]
)
, this
)
this.children.push(inst)
return inst.add(event, bbox, listener)
}
}
(this.listeners[event] = this.listeners[event] || [])
.push({bbox: bbox, func: listener})
}
proto.contains = function(bbox) {
return bbox.x0() >= this.bbox.x0() &&
bbox.y0() >= this.bbox.y0() &&
bbox.z0() >= this.bbox.z0() &&
bbox.x1() <= this.bbox.x1() &&
bbox.y1() <= this.bbox.y1() &&
bbox.z1() <= this.bbox.z1()
}
proto.expand = function(bbox) {
var size = this.size
, new_size = size * 2
, expanded = this.bbox.expand(bbox)
, new_i = Math.floor(bbox.x0() / size)
, new_j = Math.floor(bbox.y0() / size)
, new_k = Math.floor(bbox.z0() / size)
, cur_i = Math.floor(this.bbox.x0() / size)
, cur_j = Math.floor(this.bbox.y0() / size)
, cur_k = Math.floor(this.bbox.z0() / size)
, new_base = [
new_i - cur_i >= 0 ? cur_i : cur_i - 1
, new_j - cur_j >= 0 ? cur_j : cur_j - 1
, new_k - cur_k >= 0 ? cur_k : cur_k - 1
].map(function(ii) { return ii * size })
, new_bbox = aabb(new_base, [new_size, new_size, new_size])
, new_root = new this.constructor(new_size, new_bbox)
, self = this
this.parent = new_root
this.grow(this.parent)
new_root.children.push(self)
return new_root
}
proto.remove = function(event, listener) {
var list = this.listeners[event]
if(list) {
for(var i = 0, len = list.length; i < len; ++i) {
if(list[i].func === listener)
break
}
if(i !== len) {
list.splice(i, 1)
}
}
for(var i = 0, len = this.children.length; i < len; ++i) {
this.children[i].remove(event, listener)
}
}
proto.send = function(event, bbox, args) {
for(var i = 0, len = this.children.length; i < len; ++i) {
if(bbox.intersects(this.children[i].bbox)) {
this.children[i].send(event, bbox, args)
}
}
var list = this.listeners[event]
if(!list) {
return
}
for(var i = 0, len = list.length; i < len; ++i) {
if(list[i].bbox.intersects(bbox)) {
list[i].func.apply(null, args)
}
}
}
},{"aabb-3d":69}],91:[function(require,module,exports){
(function (process){
var window = window || {};
var self = self || {};
// High-resulution counter: emulate window.performance.now() for THREE.CLOCK
if( window.performance === undefined ) {
window.performance = { };
}
if( window.performance.now === undefined ) {
window.performance.now = function () {
var time = process.hrtime();
return ( time[0] + time[1] / 1e9 ) * 1000;
};
}
/**
* @author mrdoob / http://mrdoob.com/
* @author Larry Battle / http://bateru.com/news
*/
var THREE = THREE || { REVISION: '56' };
self.console = self.console || {
info: function () {},
log: function () {},
debug: function () {},
warn: function () {},
error: function () {}
};
self.Int32Array = self.Int32Array || Array;
self.Float32Array = self.Float32Array || Array;
String.prototype.trim = String.prototype.trim || function () {
return this.replace( /^\s+|\s+$/g, '' );
};
// based on https://github.com/documentcloud/underscore/blob/bf657be243a075b5e72acc8a83e6f12a564d8f55/underscore.js#L767
THREE.extend = function ( obj, source ) {
// ECMAScript5 compatibility based on: http://www.nczonline.net/blog/2012/12/11/are-your-mixins-ecmascript-5-compatible/
if ( Object.keys ) {
var keys = Object.keys( source );
for (var i = 0, il = keys.length; i < il; i++) {
var prop = keys[i];
Object.defineProperty( obj, prop, Object.getOwnPropertyDescriptor( source, prop ) );
}
} else {
var safeHasOwnProperty = {}.hasOwnProperty;
for ( var prop in source ) {
if ( safeHasOwnProperty.call( source, prop ) ) {
obj[prop] = source[prop];
}
}
}
return obj;
};
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
( function () {
var lastTime = 0;
var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {
window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
}
if ( window.requestAnimationFrame === undefined ) {
window.requestAnimationFrame = function ( callback ) {
var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
lastTime = currTime + timeToCall;
return id;
};
}
window.cancelAnimationFrame = window.cancelAnimationFrame || function ( id ) { window.clearTimeout( id ) };
}() );
// GL STATE CONSTANTS
THREE.CullFaceNone = 0;
THREE.CullFaceBack = 1;
THREE.CullFaceFront = 2;
THREE.CullFaceFrontBack = 3;
THREE.FrontFaceDirectionCW = 0;
THREE.FrontFaceDirectionCCW = 1;
// SHADOWING TYPES
THREE.BasicShadowMap = 0;
THREE.PCFShadowMap = 1;
THREE.PCFSoftShadowMap = 2;
// MATERIAL CONSTANTS
// side
THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;
// shading
THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;
// colors
THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;
// blending modes
THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;
// custom blending equations
// (numbers start from 100 not to clash with other
// mappings to OpenGL constants defined in Texture.js)
THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;
// custom blending destination factors
THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;
// custom blending source factors
//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;
// TEXTURE CONSTANTS
THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;
THREE.AddOperation = 2;
// Mapping modes
THREE.UVMapping = function () {};
THREE.CubeReflectionMapping = function () {};
THREE.CubeRefractionMapping = function () {};
THREE.SphericalReflectionMapping = function () {};
THREE.SphericalRefractionMapping = function () {};
// Wrapping modes
THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;
// Filters
THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;
// Data types
THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;
// Pixel types
//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;
// Pixel formats
THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;
// Compressed texture formats
THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;
/*
// Potential future PVRTC compressed texture formats
THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
*/
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.Color = function ( value ) {
if ( value !== undefined ) this.set( value );
return this;
};
THREE.extend( THREE.Color.prototype, {
r: 1, g: 1, b: 1,
set: function ( value ) {
switch ( typeof value ) {
case "number":
this.setHex( value );
break;
case "string":
this.setStyle( value );
break;
}
},
setHex: function ( hex ) {
hex = Math.floor( hex );
this.r = ( hex >> 16 & 255 ) / 255;
this.g = ( hex >> 8 & 255 ) / 255;
this.b = ( hex & 255 ) / 255;
return this;
},
setRGB: function ( r, g, b ) {
this.r = r;
this.g = g;
this.b = b;
return this;
},
setHSV: function ( h, s, v ) {
console.log( 'DEPRECATED: Color\'s .setHSV() will be removed. Use .setHSL( h, s, l ) instead.' );
return this.setHSL(h,s*v/((h=(2-s)*v)<1?h:2-h),h/2); // https://gist.github.com/xpansive/1337890
},
setHSL: function ( h, s, l ) {
// h,s,l ranges are in 0.0 - 1.0
if ( s === 0 ) {
this.r = this.g = this.b = l;
} else {
var hue2rgb = function ( p, q, t ) {
if ( t < 0 ) t += 1;
if ( t > 1 ) t -= 1;
if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
if ( t < 1 / 2 ) return q;
if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
return p;
};
var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
var q = ( 2 * l ) - p;
this.r = hue2rgb( q, p, h + 1 / 3 );
this.g = hue2rgb( q, p, h );
this.b = hue2rgb( q, p, h - 1 / 3 );
}
return this;
},
setStyle: function ( style ) {
// rgb(255,0,0)
if ( /^rgb\((\d+),(\d+),(\d+)\)$/i.test( style ) ) {
var color = /^rgb\((\d+),(\d+),(\d+)\)$/i.exec( style );
this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
return this;
}
// rgb(100%,0%,0%)
if ( /^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.test( style ) ) {
var color = /^rgb\((\d+)\%,(\d+)\%,(\d+)\%\)$/i.exec( style );
this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
return this;
}
// #ff0000
if ( /^\#([0-9a-f]{6})$/i.test( style ) ) {
var color = /^\#([0-9a-f]{6})$/i.exec( style );
this.setHex( parseInt( color[ 1 ], 16 ) );
return this;
}
// #f00
if ( /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test( style ) ) {
var color = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec( style );
this.setHex( parseInt( color[ 1 ] + color[ 1 ] + color[ 2 ] + color[ 2 ] + color[ 3 ] + color[ 3 ], 16 ) );
return this;
}
// red
if ( /^(\w+)$/i.test( style ) ) {
this.setHex( THREE.ColorKeywords[ style ] );
return this;
}
},
copy: function ( color ) {
this.r = color.r;
this.g = color.g;
this.b = color.b;
return this;
},
copyGammaToLinear: function ( color ) {
this.r = color.r * color.r;
this.g = color.g * color.g;
this.b = color.b * color.b;
return this;
},
copyLinearToGamma: function ( color ) {
this.r = Math.sqrt( color.r );
this.g = Math.sqrt( color.g );
this.b = Math.sqrt( color.b );
return this;
},
convertGammaToLinear: function () {
var r = this.r, g = this.g, b = this.b;
this.r = r * r;
this.g = g * g;
this.b = b * b;
return this;
},
convertLinearToGamma: function () {
this.r = Math.sqrt( this.r );
this.g = Math.sqrt( this.g );
this.b = Math.sqrt( this.b );
return this;
},
getHex: function () {
return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
},
getHexString: function () {
return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
},
getHSL: function () {
var hsl = { h: 0, s: 0, l: 0 };
return function () {
// h,s,l ranges are in 0.0 - 1.0
var r = this.r, g = this.g, b = this.b;
var max = Math.max( r, g, b );
var min = Math.min( r, g, b );
var hue, saturation;
var lightness = ( min + max ) / 2.0;
if ( min === max ) {
hue = 0;
saturation = 0;
} else {
var delta = max - min;
saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
switch ( max ) {
case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
case g: hue = ( b - r ) / delta + 2; break;
case b: hue = ( r - g ) / delta + 4; break;
}
hue /= 6;
}
hsl.h = hue;
hsl.s = saturation;
hsl.l = lightness;
return hsl;
};
}(),
getStyle: function () {
return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
},
offsetHSL: function ( h, s, l ) {
var hsl = this.getHSL();
hsl.h += h; hsl.s += s; hsl.l += l;
this.setHSL( hsl.h, hsl.s, hsl.l );
return this;
},
add: function ( color ) {
this.r += color.r;
this.g += color.g;
this.b += color.b;
return this;
},
addColors: function ( color1, color2 ) {
this.r = color1.r + color2.r;
this.g = color1.g + color2.g;
this.b = color1.b + color2.b;
return this;
},
addScalar: function ( s ) {
this.r += s;
this.g += s;
this.b += s;
return this;
},
multiply: function ( color ) {
this.r *= color.r;
this.g *= color.g;
this.b *= color.b;
return this;
},
multiplyScalar: function ( s ) {
this.r *= s;
this.g *= s;
this.b *= s;
return this;
},
lerp: function ( color, alpha ) {
this.r += ( color.r - this.r ) * alpha;
this.g += ( color.g - this.g ) * alpha;
this.b += ( color.b - this.b ) * alpha;
return this;
},
clone: function () {
return new THREE.Color().setRGB( this.r, this.g, this.b );
}
} );
THREE.ColorKeywords = { "aliceblue": 0xF0F8FF, "antiquewhite": 0xFAEBD7, "aqua": 0x00FFFF, "aquamarine": 0x7FFFD4, "azure": 0xF0FFFF,
"beige": 0xF5F5DC, "bisque": 0xFFE4C4, "black": 0x000000, "blanchedalmond": 0xFFEBCD, "blue": 0x0000FF, "blueviolet": 0x8A2BE2,
"brown": 0xA52A2A, "burlywood": 0xDEB887, "cadetblue": 0x5F9EA0, "chartreuse": 0x7FFF00, "chocolate": 0xD2691E, "coral": 0xFF7F50,
"cornflowerblue": 0x6495ED, "cornsilk": 0xFFF8DC, "crimson": 0xDC143C, "cyan": 0x00FFFF, "darkblue": 0x00008B, "darkcyan": 0x008B8B,
"darkgoldenrod": 0xB8860B, "darkgray": 0xA9A9A9, "darkgreen": 0x006400, "darkgrey": 0xA9A9A9, "darkkhaki": 0xBDB76B, "darkmagenta": 0x8B008B,
"darkolivegreen": 0x556B2F, "darkorange": 0xFF8C00, "darkorchid": 0x9932CC, "darkred": 0x8B0000, "darksalmon": 0xE9967A, "darkseagreen": 0x8FBC8F,
"darkslateblue": 0x483D8B, "darkslategray": 0x2F4F4F, "darkslategrey": 0x2F4F4F, "darkturquoise": 0x00CED1, "darkviolet": 0x9400D3,
"deeppink": 0xFF1493, "deepskyblue": 0x00BFFF, "dimgray": 0x696969, "dimgrey": 0x696969, "dodgerblue": 0x1E90FF, "firebrick": 0xB22222,
"floralwhite": 0xFFFAF0, "forestgreen": 0x228B22, "fuchsia": 0xFF00FF, "gainsboro": 0xDCDCDC, "ghostwhite": 0xF8F8FF, "gold": 0xFFD700,
"goldenrod": 0xDAA520, "gray": 0x808080, "green": 0x008000, "greenyellow": 0xADFF2F, "grey": 0x808080, "honeydew": 0xF0FFF0, "hotpink": 0xFF69B4,
"indianred": 0xCD5C5C, "indigo": 0x4B0082, "ivory": 0xFFFFF0, "khaki": 0xF0E68C, "lavender": 0xE6E6FA, "lavenderblush": 0xFFF0F5, "lawngreen": 0x7CFC00,
"lemonchiffon": 0xFFFACD, "lightblue": 0xADD8E6, "lightcoral": 0xF08080, "lightcyan": 0xE0FFFF, "lightgoldenrodyellow": 0xFAFAD2, "lightgray": 0xD3D3D3,
"lightgreen": 0x90EE90, "lightgrey": 0xD3D3D3, "lightpink": 0xFFB6C1, "lightsalmon": 0xFFA07A, "lightseagreen": 0x20B2AA, "lightskyblue": 0x87CEFA,
"lightslategray": 0x778899, "lightslategrey": 0x778899, "lightsteelblue": 0xB0C4DE, "lightyellow": 0xFFFFE0, "lime": 0x00FF00, "limegreen": 0x32CD32,
"linen": 0xFAF0E6, "magenta": 0xFF00FF, "maroon": 0x800000, "mediumaquamarine": 0x66CDAA, "mediumblue": 0x0000CD, "mediumorchid": 0xBA55D3,
"mediumpurple": 0x9370DB, "mediumseagreen": 0x3CB371, "mediumslateblue": 0x7B68EE, "mediumspringgreen": 0x00FA9A, "mediumturquoise": 0x48D1CC,
"mediumvioletred": 0xC71585, "midnightblue": 0x191970, "mintcream": 0xF5FFFA, "mistyrose": 0xFFE4E1, "moccasin": 0xFFE4B5, "navajowhite": 0xFFDEAD,
"navy": 0x000080, "oldlace": 0xFDF5E6, "olive": 0x808000, "olivedrab": 0x6B8E23, "orange": 0xFFA500, "orangered": 0xFF4500, "orchid": 0xDA70D6,
"palegoldenrod": 0xEEE8AA, "palegreen": 0x98FB98, "paleturquoise": 0xAFEEEE, "palevioletred": 0xDB7093, "papayawhip": 0xFFEFD5, "peachpuff": 0xFFDAB9,
"peru": 0xCD853F, "pink": 0xFFC0CB, "plum": 0xDDA0DD, "powderblue": 0xB0E0E6, "purple": 0x800080, "red": 0xFF0000, "rosybrown": 0xBC8F8F,
"royalblue": 0x4169E1, "saddlebrown": 0x8B4513, "salmon": 0xFA8072, "sandybrown": 0xF4A460, "seagreen": 0x2E8B57, "seashell": 0xFFF5EE,
"sienna": 0xA0522D, "silver": 0xC0C0C0, "skyblue": 0x87CEEB, "slateblue": 0x6A5ACD, "slategray": 0x708090, "slategrey": 0x708090, "snow": 0xFFFAFA,
"springgreen": 0x00FF7F, "steelblue": 0x4682B4, "tan": 0xD2B48C, "teal": 0x008080, "thistle": 0xD8BFD8, "tomato": 0xFF6347, "turquoise": 0x40E0D0,
"violet": 0xEE82EE, "wheat": 0xF5DEB3, "white": 0xFFFFFF, "whitesmoke": 0xF5F5F5, "yellow": 0xFFFF00, "yellowgreen": 0x9ACD32 };
/**
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author bhouston / http://exocortex.com
*/
THREE.Quaternion = function( x, y, z, w ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
this.w = ( w !== undefined ) ? w : 1;
};
THREE.extend( THREE.Quaternion.prototype, {
set: function ( x, y, z, w ) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
},
copy: function ( q ) {
this.x = q.x;
this.y = q.y;
this.z = q.z;
this.w = q.w;
return this;
},
setFromEuler: function ( v, order ) {
// http://www.mathworks.com/matlabcentral/fileexchange/
// 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
// content/SpinCalc.m
var c1 = Math.cos( v.x / 2 );
var c2 = Math.cos( v.y / 2 );
var c3 = Math.cos( v.z / 2 );
var s1 = Math.sin( v.x / 2 );
var s2 = Math.sin( v.y / 2 );
var s3 = Math.sin( v.z / 2 );
if ( order === undefined || 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;
},
setFromAxisAngle: function ( axis, angle ) {
// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
// axis have to be normalized
var halfAngle = angle / 2,
s = Math.sin( halfAngle );
this.x = axis.x * s;
this.y = axis.y * s;
this.z = axis.z * s;
this.w = Math.cos( halfAngle );
return this;
},
setFromRotationMatrix: function ( m ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
var te = m.elements,
m11 = te[0], m12 = te[4], m13 = te[8],
m21 = te[1], m22 = te[5], m23 = te[9],
m31 = te[2], m32 = te[6], m33 = te[10],
trace = m11 + m22 + m33,
s;
if ( trace > 0 ) {
s = 0.5 / Math.sqrt( trace + 1.0 );
this.w = 0.25 / s;
this.x = ( m32 - m23 ) * s;
this.y = ( m13 - m31 ) * s;
this.z = ( m21 - m12 ) * s;
} else if ( m11 > m22 && m11 > m33 ) {
s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
this.w = (m32 - m23 ) / s;
this.x = 0.25 * s;
this.y = (m12 + m21 ) / s;
this.z = (m13 + m31 ) / s;
} else if ( m22 > m33 ) {
s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
this.w = (m13 - m31 ) / s;
this.x = (m12 + m21 ) / s;
this.y = 0.25 * s;
this.z = (m23 + m32 ) / s;
} else {
s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
this.w = ( m21 - m12 ) / s;
this.x = ( m13 + m31 ) / s;
this.y = ( m23 + m32 ) / s;
this.z = 0.25 * s;
}
return this;
},
inverse: function () {
this.conjugate().normalize();
return this;
},
conjugate: function () {
this.x *= -1;
this.y *= -1;
this.z *= -1;
return this;
},
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 l = this.length();
if ( l === 0 ) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
} else {
l = 1 / l;
this.x = this.x * l;
this.y = this.y * l;
this.z = this.z * l;
this.w = this.w * l;
}
return this;
},
multiply: function ( q, p ) {
if ( p !== undefined ) {
console.warn( 'DEPRECATED: Quaternion\'s .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
return this.multiplyQuaternions( q, p );
}
return this.multiplyQuaternions( this, q );
},
multiplyQuaternions: function ( a, b ) {
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w;
var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
return this;
},
multiplyVector3: function ( vector ) {
console.warn( 'DEPRECATED: Quaternion\'s .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
return vector.applyQuaternion( this );
},
slerp: function ( qb, t ) {
var x = this.x, y = this.y, z = this.z, w = this.w;
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
if ( cosHalfTheta < 0 ) {
this.w = -qb.w;
this.x = -qb.x;
this.y = -qb.y;
this.z = -qb.z;
cosHalfTheta = -cosHalfTheta;
} else {
this.copy( qb );
}
if ( cosHalfTheta >= 1.0 ) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
return this;
}
var halfTheta = Math.acos( cosHalfTheta );
var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
if ( Math.abs( sinHalfTheta ) < 0.001 ) {
this.w = 0.5 * ( w + this.w );
this.x = 0.5 * ( x + this.x );
this.y = 0.5 * ( y + this.y );
this.z = 0.5 * ( z + this.z );
return this;
}
var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
this.w = ( w * ratioA + this.w * ratioB );
this.x = ( x * ratioA + this.x * ratioB );
this.y = ( y * ratioA + this.y * ratioB );
this.z = ( z * ratioA + this.z * ratioB );
return this;
},
equals: function ( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
},
clone: function () {
return new THREE.Quaternion( this.x, this.y, this.z, this.w );
}
} );
THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
return qm.copy( qa ).slerp( qb, t );
}
/**
* @author mrdoob / http://mrdoob.com/
* @author philogb / http://blog.thejit.org/
* @author egraether / http://egraether.com/
* @author zz85 / http://www.lab4games.net/zz85/blog
*/
THREE.Vector2 = function ( x, y ) {
this.x = x || 0;
this.y = y || 0;
};
THREE.extend( THREE.Vector2.prototype, {
set: function ( x, y ) {
this.x = x;
this.y = y;
return this;
},
setX: function ( x ) {
this.x = x;
return this;
},
setY: function ( y ) {
this.y = y;
return this;
},
setComponent: function ( index, value ) {
switch ( index ) {
case 0: this.x = value; break;
case 1: this.y = value; break;
default: throw new Error( "index is out of range: " + index );
}
},
getComponent: function ( index ) {
switch ( index ) {
case 0: return this.x;
case 1: return this.y;
default: throw new Error( "index is out of range: " + index );
}
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
return this;
},
add: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector2\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
return this.addVectors( v, w );
}
this.x += v.x;
this.y += v.y;
return this;
},
addVectors: function ( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
return this;
},
addScalar: function ( s ) {
this.x += s;
this.y += s;
return this;
},
sub: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector2\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
return this.subVectors( v, w );
}
this.x -= v.x;
this.y -= v.y;
return this;
},
subVectors: function ( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
return this;
},
multiplyScalar: function ( s ) {
this.x *= s;
this.y *= s;
return this;
},
divideScalar: function ( s ) {
if ( s !== 0 ) {
this.x /= s;
this.y /= s;
} else {
this.set( 0, 0 );
}
return this;
},
min: function ( v ) {
if ( this.x > v.x ) {
this.x = v.x;
}
if ( this.y > v.y ) {
this.y = v.y;
}
return this;
},
max: function ( v ) {
if ( this.x < v.x ) {
this.x = v.x;
}
if ( this.y < v.y ) {
this.y = v.y;
}
return this;
},
clamp: function ( min, max ) {
// This function assumes min < max, if this assumption isn't true it will not operate correctly
if ( this.x < min.x ) {
this.x = min.x;
} else if ( this.x > max.x ) {
this.x = max.x;
}
if ( this.y < min.y ) {
this.y = min.y;
} else if ( this.y > max.y ) {
this.y = max.y;
}
return this;
},
negate: function() {
return this.multiplyScalar( - 1 );
},
dot: function ( v ) {
return this.x * v.x + this.y * v.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 );
},
normalize: function () {
return this.divideScalar( this.length() );
},
distanceTo: function ( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
},
distanceToSquared: function ( v ) {
var dx = this.x - v.x, dy = this.y - v.y;
return dx * dx + dy * dy;
},
setLength: function ( l ) {
var oldLength = this.length();
if ( oldLength !== 0 && l !== oldLength ) {
this.multiplyScalar( l / oldLength );
}
return this;
},
lerp: function ( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
return this;
},
equals: function( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) );
},
toArray: function () {
return [ this.x, this.y ];
},
clone: function () {
return new THREE.Vector2( this.x, this.y );
}
} );
/**
* @author mrdoob / http://mrdoob.com/
* @author *kile / http://kile.stravaganza.org/
* @author philogb / http://blog.thejit.org/
* @author mikael emtinger / http://gomo.se/
* @author egraether / http://egraether.com/
* @author WestLangley / http://github.com/WestLangley
*/
THREE.Vector3 = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
THREE.extend( THREE.Vector3.prototype, {
set: function ( x, y, z ) {
this.x = x;
this.y = y;
this.z = z;
return this;
},
setX: function ( x ) {
this.x = x;
return this;
},
setY: function ( y ) {
this.y = y;
return this;
},
setZ: function ( z ) {
this.z = z;
return this;
},
setComponent: function ( index, value ) {
switch ( index ) {
case 0: this.x = value; break;
case 1: this.y = value; break;
case 2: this.z = value; break;
default: throw new Error( "index is out of range: " + index );
}
},
getComponent: function ( index ) {
switch ( index ) {
case 0: return this.x;
case 1: return this.y;
case 2: return this.z;
default: throw new Error( "index is out of range: " + index );
}
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
},
add: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector3\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
return this.addVectors( v, w );
}
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
},
addScalar: function ( s ) {
this.x += s;
this.y += s;
this.z += s;
return this;
},
addVectors: function ( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
},
sub: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector3\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
return this.subVectors( v, w );
}
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
},
subVectors: function ( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
},
multiply: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector3\'s .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
return this.multiplyVectors( v, w );
}
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
},
multiplyScalar: function ( s ) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
},
multiplyVectors: function ( a, b ) {
this.x = a.x * b.x;
this.y = a.y * b.y;
this.z = a.z * b.z;
return this;
},
applyMatrix3: function ( m ) {
var x = this.x;
var y = this.y;
var z = this.z;
var e = m.elements;
this.x = e[0] * x + e[3] * y + e[6] * z;
this.y = e[1] * x + e[4] * y + e[7] * z;
this.z = e[2] * x + e[5] * y + e[8] * z;
return this;
},
applyMatrix4: function ( m ) {
// input: THREE.Matrix4 affine matrix
var x = this.x, y = this.y, z = this.z;
var e = m.elements;
this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
return this;
},
applyProjection: function ( m ) {
// input: THREE.Matrix4 projection matrix
var x = this.x, y = this.y, z = this.z;
var e = m.elements;
var d = 1 / ( e[3] * x + e[7] * y + e[11] * z + e[15] ); // perspective divide
this.x = ( e[0] * x + e[4] * y + e[8] * z + e[12] ) * d;
this.y = ( e[1] * x + e[5] * y + e[9] * z + e[13] ) * d;
this.z = ( e[2] * x + e[6] * y + e[10] * z + e[14] ) * d;
return this;
},
applyQuaternion: function ( q ) {
var x = this.x;
var y = this.y;
var z = this.z;
var qx = q.x;
var qy = q.y;
var qz = q.z;
var qw = q.w;
// calculate quat * vector
var ix = qw * x + qy * z - qz * y;
var iy = qw * y + qz * x - qx * z;
var iz = qw * z + qx * y - qy * x;
var iw = -qx * x - qy * y - qz * z;
// calculate result * inverse quat
this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return this;
},
applyEuler: function () {
var q1 = new THREE.Quaternion();
return function ( v, eulerOrder ) {
var quaternion = q1.setFromEuler( v, eulerOrder );
this.applyQuaternion( quaternion );
return this;
};
}(),
applyAxisAngle: function () {
var q1 = new THREE.Quaternion();
return function ( axis, angle ) {
var quaternion = q1.setFromAxisAngle( axis, angle );
this.applyQuaternion( quaternion );
return this;
};
}(),
transformDirection: function ( m ) {
// input: THREE.Matrix4 affine matrix
// vector interpreted as a direction
var x = this.x, y = this.y, z = this.z;
var e = m.elements;
this.x = e[0] * x + e[4] * y + e[8] * z;
this.y = e[1] * x + e[5] * y + e[9] * z;
this.z = e[2] * x + e[6] * y + e[10] * z;
this.normalize();
return this;
},
divide: function ( v ) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
},
divideScalar: function ( s ) {
if ( s !== 0 ) {
this.x /= s;
this.y /= s;
this.z /= s;
} else {
this.x = 0;
this.y = 0;
this.z = 0;
}
return this;
},
min: function ( v ) {
if ( this.x > v.x ) {
this.x = v.x;
}
if ( this.y > v.y ) {
this.y = v.y;
}
if ( this.z > v.z ) {
this.z = v.z;
}
return this;
},
max: function ( v ) {
if ( this.x < v.x ) {
this.x = v.x;
}
if ( this.y < v.y ) {
this.y = v.y;
}
if ( this.z < v.z ) {
this.z = v.z;
}
return this;
},
clamp: function ( min, max ) {
// This function assumes min < max, if this assumption isn't true it will not operate correctly
if ( this.x < min.x ) {
this.x = min.x;
} else if ( this.x > max.x ) {
this.x = max.x;
}
if ( this.y < min.y ) {
this.y = min.y;
} else if ( this.y > max.y ) {
this.y = max.y;
}
if ( this.z < min.z ) {
this.z = min.z;
} else if ( this.z > max.z ) {
this.z = max.z;
}
return this;
},
negate: function () {
return this.multiplyScalar( - 1 );
},
dot: function ( v ) {
return this.x * v.x + this.y * v.y + this.z * v.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 ( l ) {
var oldLength = this.length();
if ( oldLength !== 0 && l !== oldLength ) {
this.multiplyScalar( l / oldLength );
}
return this;
},
lerp: function ( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
this.z += ( v.z - this.z ) * alpha;
return this;
},
cross: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector3\'s .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
return this.crossVectors( v, w );
}
var x = this.x, y = this.y, z = this.z;
this.x = y * v.z - z * v.y;
this.y = z * v.x - x * v.z;
this.z = x * v.y - y * v.x;
return this;
},
crossVectors: function ( a, b ) {
this.x = a.y * b.z - a.z * b.y;
this.y = a.z * b.x - a.x * b.z;
this.z = a.x * b.y - a.y * b.x;
return this;
},
projectOnVector: function () {
var v1 = new THREE.Vector3();
return function( vector ) {
v1.copy( vector ).normalize();
var d = this.dot( v1 );
return this.copy( v1 ).multiplyScalar( d );
};
}(),
projectOnPlane: function () {
var v1 = new THREE.Vector3();
return function( planeNormal ) {
v1.copy( this ).projectOnVector( planeNormal );
return this.sub( v1 );
}
}(),
reflect: function () {
var v1 = new THREE.Vector3();
return function ( vector ) {
v1.copy( this ).projectOnVector( vector ).multiplyScalar( 2 );
return this.subVectors( v1, this );
}
}(),
angleTo: function ( v ) {
var theta = this.dot( v ) / ( this.length() * v.length() );
// clamp, to handle numerical problems
return Math.acos( THREE.Math.clamp( theta, -1, 1 ) );
},
distanceTo: function ( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
},
distanceToSquared: function ( v ) {
var dx = this.x - v.x;
var dy = this.y - v.y;
var dz = this.z - v.z;
return dx * dx + dy * dy + dz * dz;
},
getPositionFromMatrix: function ( m ) {
this.x = m.elements[12];
this.y = m.elements[13];
this.z = m.elements[14];
return this;
},
setEulerFromRotationMatrix: function ( m, order ) {
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
// clamp, to handle numerical problems
function clamp( x ) {
return Math.min( Math.max( x, -1 ), 1 );
}
var te = m.elements;
var m11 = te[0], m12 = te[4], m13 = te[8];
var m21 = te[1], m22 = te[5], m23 = te[9];
var m31 = te[2], m32 = te[6], m33 = te[10];
if ( order === undefined || order === 'XYZ' ) {
this.y = Math.asin( clamp( m13 ) );
if ( Math.abs( m13 ) < 0.99999 ) {
this.x = Math.atan2( - m23, m33 );
this.z = Math.atan2( - m12, m11 );
} else {
this.x = Math.atan2( m32, m22 );
this.z = 0;
}
} else if ( order === 'YXZ' ) {
this.x = Math.asin( - clamp( m23 ) );
if ( Math.abs( m23 ) < 0.99999 ) {
this.y = Math.atan2( m13, m33 );
this.z = Math.atan2( m21, m22 );
} else {
this.y = Math.atan2( - m31, m11 );
this.z = 0;
}
} else if ( order === 'ZXY' ) {
this.x = Math.asin( clamp( m32 ) );
if ( Math.abs( m32 ) < 0.99999 ) {
this.y = Math.atan2( - m31, m33 );
this.z = Math.atan2( - m12, m22 );
} else {
this.y = 0;
this.z = Math.atan2( m21, m11 );
}
} else if ( order === 'ZYX' ) {
this.y = Math.asin( - clamp( m31 ) );
if ( Math.abs( m31 ) < 0.99999 ) {
this.x = Math.atan2( m32, m33 );
this.z = Math.atan2( m21, m11 );
} else {
this.x = 0;
this.z = Math.atan2( - m12, m22 );
}
} else if ( order === 'YZX' ) {
this.z = Math.asin( clamp( m21 ) );
if ( Math.abs( m21 ) < 0.99999 ) {
this.x = Math.atan2( - m23, m22 );
this.y = Math.atan2( - m31, m11 );
} else {
this.x = 0;
this.y = Math.atan2( m13, m33 );
}
} else if ( order === 'XZY' ) {
this.z = Math.asin( - clamp( m12 ) );
if ( Math.abs( m12 ) < 0.99999 ) {
this.x = Math.atan2( m32, m22 );
this.y = Math.atan2( m13, m11 );
} else {
this.x = Math.atan2( - m23, m33 );
this.y = 0;
}
}
return this;
},
setEulerFromQuaternion: function ( q, order ) {
// q is assumed to be normalized
// clamp, to handle numerical problems
function clamp( x ) {
return Math.min( Math.max( x, -1 ), 1 );
}
// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
var sqx = q.x * q.x;
var sqy = q.y * q.y;
var sqz = q.z * q.z;
var sqw = q.w * q.w;
if ( order === undefined || order === 'XYZ' ) {
this.x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
this.y = Math.asin( clamp( 2 * ( q.x * q.z + q.y * q.w ) ) );
this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
} else if ( order === 'YXZ' ) {
this.x = Math.asin( clamp( 2 * ( q.x * q.w - q.y * q.z ) ) );
this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
} else if ( order === 'ZXY' ) {
this.x = Math.asin( clamp( 2 * ( q.x * q.w + q.y * q.z ) ) );
this.y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
} else if ( order === 'ZYX' ) {
this.x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
this.y = Math.asin( clamp( 2 * ( q.y * q.w - q.x * q.z ) ) );
this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
} else if ( order === 'YZX' ) {
this.x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
this.y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
this.z = Math.asin( clamp( 2 * ( q.x * q.y + q.z * q.w ) ) );
} else if ( order === 'XZY' ) {
this.x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
this.z = Math.asin( clamp( 2 * ( q.z * q.w - q.x * q.y ) ) );
}
return this;
},
getScaleFromMatrix: function ( m ) {
var sx = this.set( m.elements[0], m.elements[1], m.elements[2] ).length();
var sy = this.set( m.elements[4], m.elements[5], m.elements[6] ).length();
var sz = this.set( m.elements[8], m.elements[9], m.elements[10] ).length();
this.x = sx;
this.y = sy;
this.z = sz;
return this;
},
equals: function ( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
},
toArray: function () {
return [ this.x, this.y, this.z ];
},
clone: function () {
return new THREE.Vector3( this.x, this.y, this.z );
}
} );
/**
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author philogb / http://blog.thejit.org/
* @author mikael emtinger / http://gomo.se/
* @author egraether / http://egraether.com/
* @author WestLangley / http://github.com/WestLangley
*/
THREE.Vector4 = function ( x, y, z, w ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
this.w = ( w !== undefined ) ? w : 1;
};
THREE.extend( THREE.Vector4.prototype, {
set: function ( x, y, z, w ) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
},
setX: function ( x ) {
this.x = x;
return this;
},
setY: function ( y ) {
this.y = y;
return this;
},
setZ: function ( z ) {
this.z = z;
return this;
},
setW: function ( w ) {
this.w = w;
return this;
},
setComponent: function ( index, value ) {
switch ( index ) {
case 0: this.x = value; break;
case 1: this.y = value; break;
case 2: this.z = value; break;
case 3: this.w = value; break;
default: throw new Error( "index is out of range: " + index );
}
},
getComponent: function ( index ) {
switch ( index ) {
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: " + index );
}
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
this.w = ( v.w !== undefined ) ? v.w : 1;
return this;
},
add: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector4\'s .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
return this.addVectors( v, w );
}
this.x += v.x;
this.y += v.y;
this.z += v.z;
this.w += v.w;
return this;
},
addScalar: function ( s ) {
this.x += s;
this.y += s;
this.z += s;
this.w += s;
return this;
},
addVectors: function ( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
this.w = a.w + b.w;
return this;
},
sub: function ( v, w ) {
if ( w !== undefined ) {
console.warn( 'DEPRECATED: Vector4\'s .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
return this.subVectors( v, w );
}
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
this.w -= v.w;
return this;
},
subVectors: function ( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
this.w = a.w - b.w;
return this;
},
multiplyScalar: function ( s ) {
this.x *= s;
this.y *= s;
this.z *= s;
this.w *= s;
return this;
},
applyMatrix4: function ( m ) {
var x = this.x;
var y = this.y;
var z = this.z;
var w = this.w;
var e = m.elements;
this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w;
this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w;
this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w;
this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w;
return this;
},
divideScalar: function ( s ) {
if ( s !== 0 ) {
this.x /= s;
this.y /= s;
this.z /= s;
this.w /= s;
} else {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
}
return this;
},
setAxisAngleFromQuaternion: function ( q ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
// q is assumed to be normalized
this.w = 2 * Math.acos( q.w );
var s = Math.sqrt( 1 - q.w * q.w );
if ( s < 0.0001 ) {
this.x = 1;
this.y = 0;
this.z = 0;
} else {
this.x = q.x / s;
this.y = q.y / s;
this.z = q.z / s;
}
return this;
},
setAxisAngleFromRotationMatrix: function ( m ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
var angle, x, y, z, // variables for result
epsilon = 0.01, // margin to allow for rounding errors
epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
te = m.elements,
m11 = te[0], m12 = te[4], m13 = te[8],
m21 = te[1], m22 = te[5], m23 = te[9],
m31 = te[2], m32 = te[6], m33 = te[10];
if ( ( Math.abs( m12 - m21 ) < epsilon )
&& ( Math.abs( m13 - m31 ) < epsilon )
&& ( Math.abs( m23 - m32 ) < epsilon ) ) {
// singularity found
// first check for identity matrix which must have +1 for all terms
// in leading diagonal and zero in other terms
if ( ( Math.abs( m12 + m21 ) < epsilon2 )
&& ( Math.abs( m13 + m31 ) < epsilon2 )
&& ( Math.abs( m23 + m32 ) < epsilon2 )
&& ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
// this singularity is identity matrix so angle = 0
this.set( 1, 0, 0, 0 );
return this; // zero angle, arbitrary axis
}
// otherwise this singularity is angle = 180
angle = Math.PI;
var xx = ( m11 + 1 ) / 2;
var yy = ( m22 + 1 ) / 2;
var zz = ( m33 + 1 ) / 2;
var xy = ( m12 + m21 ) / 4;
var xz = ( m13 + m31 ) / 4;
var yz = ( m23 + m32 ) / 4;
if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term
if ( xx < epsilon ) {
x = 0;
y = 0.707106781;
z = 0.707106781;
} else {
x = Math.sqrt( xx );
y = xy / x;
z = xz / x;
}
} else if ( yy > zz ) { // m22 is the largest diagonal term
if ( yy < epsilon ) {
x = 0.707106781;
y = 0;
z = 0.707106781;
} else {
y = Math.sqrt( yy );
x = xy / y;
z = yz / y;
}
} else { // m33 is the largest diagonal term so base result on this
if ( zz < epsilon ) {
x = 0.707106781;
y = 0.707106781;
z = 0;
} else {
z = Math.sqrt( zz );
x = xz / z;
y = yz / z;
}
}
this.set( x, y, z, angle );
return this; // return 180 deg rotation
}
// as we have reached here there are no singularities so we can handle normally
var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
+ ( m13 - m31 ) * ( m13 - m31 )
+ ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
if ( Math.abs( s ) < 0.001 ) s = 1;
// prevent divide by zero, should not happen if matrix is orthogonal and should be
// caught by singularity test above, but I've left it in just in case
this.x = ( m32 - m23 ) / s;
this.y = ( m13 - m31 ) / s;
this.z = ( m21 - m12 ) / s;
this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
return this;
},
min: function ( v ) {
if ( this.x > v.x ) {
this.x = v.x;
}
if ( this.y > v.y ) {
this.y = v.y;
}
if ( this.z > v.z ) {
this.z = v.z;
}
if ( this.w > v.w ) {
this.w = v.w;
}
return this;
},
max: function ( v ) {
if ( this.x < v.x ) {
this.x = v.x;
}
if ( this.y < v.y ) {
this.y = v.y;
}
if ( this.z < v.z ) {
this.z = v.z;
}
if ( this.w < v.w ) {
this.w = v.w;
}
return this;
},
clamp: function ( min, max ) {
// This function assumes min < max, if this assumption isn't true it will not operate correctly
if ( this.x < min.x ) {
this.x = min.x;
} else if ( this.x > max.x ) {
this.x = max.x;
}
if ( this.y < min.y ) {
this.y = min.y;
} else if ( this.y > max.y ) {
this.y = max.y;
}
if ( this.z < min.z ) {
this.z = min.z;
} else if ( this.z > max.z ) {
this.z = max.z;
}
if ( this.w < min.w ) {
this.w = min.w;
} else if ( this.w > max.w ) {
this.w = max.w;
}
return this;
},
negate: function() {
return this.multiplyScalar( -1 );
},
dot: function ( v ) {
return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.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 ( l ) {
var oldLength = this.length();
if ( oldLength !== 0 && l !== oldLength ) {
this.multiplyScalar( l / oldLength );
}
return this;
},
lerp: function ( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
this.z += ( v.z - this.z ) * alpha;
this.w += ( v.w - this.w ) * alpha;
return this;
},
equals: function ( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
},
toArray: function () {
return [ this.x, this.y, this.z, this.w ];
},
clone: function () {
return new THREE.Vector4( this.x, this.y, this.z, this.w );
}
} );
/**
* @author bhouston / http://exocortex.com
*/
THREE.Box2 = function ( min, max ) {
this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity );
this.max = ( max !== undefined ) ? max : new THREE.Vector2( -Infinity, -Infinity );
};
THREE.extend( THREE.Box2.prototype, {
set: function ( min, max ) {
this.min.copy( min );
this.max.copy( max );
return this;
},
setFromPoints: function ( points ) {
if ( points.length > 0 ) {
var point = points[ 0 ];
this.min.copy( point );
this.max.copy( point );
for ( var i = 1, il = points.length; i < il; i ++ ) {
point = points[ i ];
if ( point.x < this.min.x ) {
this.min.x = point.x;
} else if ( point.x > this.max.x ) {
this.max.x = point.x;
}
if ( point.y < this.min.y ) {
this.min.y = point.y;
} else if ( point.y > this.max.y ) {
this.max.y = point.y;
}
}
} else {
this.makeEmpty();
}
return this;
},
setFromCenterAndSize: function() {
var v1 = new THREE.Vector2();
return function ( center, size ) {
var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
this.min.copy( center ).sub( halfSize );
this.max.copy( center ).add( halfSize );
return this;
};
}(),
copy: function ( box ) {
this.min.copy( box.min );
this.max.copy( box.max );
return this;
},
makeEmpty: function () {
this.min.x = this.min.y = Infinity;
this.max.x = this.max.y = -Infinity;
return this;
},
empty: function () {
// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
},
center: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector2();
return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
},
size: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector2();
return result.subVectors( this.max, this.min );
},
expandByPoint: function ( point ) {
this.min.min( point );
this.max.max( point );
return this;
},
expandByVector: function ( vector ) {
this.min.sub( vector );
this.max.add( vector );
return this;
},
expandByScalar: function ( scalar ) {
this.min.addScalar( -scalar );
this.max.addScalar( scalar );
return this;
},
containsPoint: function ( point ) {
if ( point.x < this.min.x || point.x > this.max.x ||
point.y < this.min.y || point.y > this.max.y ) {
return false;
}
return true;
},
containsBox: function ( box ) {
if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {
return true;
}
return false;
},
getParameter: function ( point ) {
// This can potentially have a divide by zero if the box
// has a size dimension of 0.
return new THREE.Vector2(
( point.x - this.min.x ) / ( this.max.x - this.min.x ),
( point.y - this.min.y ) / ( this.max.y - this.min.y )
);
},
isIntersectionBox: function ( box ) {
// using 6 splitting planes to rule out intersections.
if ( box.max.x < this.min.x || box.min.x > this.max.x ||
box.max.y < this.min.y || box.min.y > this.max.y ) {
return false;
}
return true;
},
clampPoint: function ( point, optionalTarget ) {
var result = optionalTarget || new THREE.Vector2();
return result.copy( point ).clamp( this.min, this.max );
},
distanceToPoint: function() {
var v1 = new THREE.Vector2();
return function ( point ) {
var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
return clampedPoint.sub( point ).length();
};
}(),
intersect: function ( box ) {
this.min.max( box.min );
this.max.min( box.max );
return this;
},
union: function ( box ) {
this.min.min( box.min );
this.max.max( box.max );
return this;
},
translate: function ( offset ) {
this.min.add( offset );
this.max.add( offset );
return this;
},
equals: function ( box ) {
return box.min.equals( this.min ) && box.max.equals( this.max );
},
clone: function () {
return new THREE.Box2().copy( this );
}
} );
/**
* @author bhouston / http://exocortex.com
*/
THREE.Box3 = function ( min, max ) {
this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity );
this.max = ( max !== undefined ) ? max : new THREE.Vector3( -Infinity, -Infinity, -Infinity );
};
THREE.extend( THREE.Box3.prototype, {
set: function ( min, max ) {
this.min.copy( min );
this.max.copy( max );
return this;
},
setFromPoints: function ( points ) {
if ( points.length > 0 ) {
var point = points[ 0 ];
this.min.copy( point );
this.max.copy( point );
for ( var i = 1, il = points.length; i < il; i ++ ) {
point = points[ i ];
if ( point.x < this.min.x ) {
this.min.x = point.x;
} else if ( point.x > this.max.x ) {
this.max.x = point.x;
}
if ( point.y < this.min.y ) {
this.min.y = point.y;
} else if ( point.y > this.max.y ) {
this.max.y = point.y;
}
if ( point.z < this.min.z ) {
this.min.z = point.z;
} else if ( point.z > this.max.z ) {
this.max.z = point.z;
}
}
} else {
this.makeEmpty();
}
return this;
},
setFromCenterAndSize: function() {
var v1 = new THREE.Vector3();
return function ( center, size ) {
var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
this.min.copy( center ).sub( halfSize );
this.max.copy( center ).add( halfSize );
return this;
};
}(),
copy: function ( box ) {
this.min.copy( box.min );
this.max.copy( box.max );
return this;
},
makeEmpty: function () {
this.min.x = this.min.y = this.min.z = Infinity;
this.max.x = this.max.y = this.max.z = -Infinity;
return this;
},
empty: function () {
// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
},
center: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
},
size: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.subVectors( this.max, this.min );
},
expandByPoint: function ( point ) {
this.min.min( point );
this.max.max( point );
return this;
},
expandByVector: function ( vector ) {
this.min.sub( vector );
this.max.add( vector );
return this;
},
expandByScalar: function ( scalar ) {
this.min.addScalar( -scalar );
this.max.addScalar( scalar );
return this;
},
containsPoint: function ( point ) {
if ( point.x < this.min.x || point.x > this.max.x ||
point.y < this.min.y || point.y > this.max.y ||
point.z < this.min.z || point.z > this.max.z ) {
return false;
}
return true;
},
containsBox: function ( box ) {
if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {
return true;
}
return false;
},
getParameter: function ( point ) {
// This can potentially have a divide by zero if the box
// has a size dimension of 0.
return new THREE.Vector3(
( point.x - this.min.x ) / ( this.max.x - this.min.x ),
( point.y - this.min.y ) / ( this.max.y - this.min.y ),
( point.z - this.min.z ) / ( this.max.z - this.min.z )
);
},
isIntersectionBox: function ( box ) {
// using 6 splitting planes to rule out intersections.
if ( box.max.x < this.min.x || box.min.x > this.max.x ||
box.max.y < this.min.y || box.min.y > this.max.y ||
box.max.z < this.min.z || box.min.z > this.max.z ) {
return false;
}
return true;
},
clampPoint: function ( point, optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.copy( point ).clamp( this.min, this.max );
},
distanceToPoint: function() {
var v1 = new THREE.Vector3();
return function ( point ) {
var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
return clampedPoint.sub( point ).length();
};
}(),
getBoundingSphere: function() {
var v1 = new THREE.Vector3();
return function ( optionalTarget ) {
var result = optionalTarget || new THREE.Sphere();
result.center = this.center();
result.radius = this.size( v1 ).length() * 0.5;
return result;
};
}(),
intersect: function ( box ) {
this.min.max( box.min );
this.max.min( box.max );
return this;
},
union: function ( box ) {
this.min.min( box.min );
this.max.max( box.max );
return this;
},
applyMatrix4: function() {
var points = [
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3()
];
return function ( matrix ) {
// NOTE: I am using a binary pattern to specify all 2^3 combinations below
points[0].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
points[1].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
points[2].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
points[3].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
points[4].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
points[5].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
points[6].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
points[7].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
this.makeEmpty();
this.setFromPoints( points );
return this;
};
}(),
translate: function ( offset ) {
this.min.add( offset );
this.max.add( offset );
return this;
},
equals: function ( box ) {
return box.min.equals( this.min ) && box.max.equals( this.max );
},
clone: function () {
return new THREE.Box3().copy( this );
}
} );
/**
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author bhouston / http://exocortex.com
*/
THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
this.elements = new Float32Array(9);
this.set(
( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0,
n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0,
n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1
);
};
THREE.extend( THREE.Matrix3.prototype, {
set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
var te = this.elements;
te[0] = n11; te[3] = n12; te[6] = n13;
te[1] = n21; te[4] = n22; te[7] = n23;
te[2] = n31; te[5] = n32; te[8] = n33;
return this;
},
identity: function () {
this.set(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
return this;
},
copy: function ( m ) {
var me = m.elements;
this.set(
me[0], me[3], me[6],
me[1], me[4], me[7],
me[2], me[5], me[8]
);
return this;
},
multiplyVector3: function ( vector ) {
console.warn( 'DEPRECATED: Matrix3\'s .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
return vector.applyMatrix3( this );
},
multiplyVector3Array: function() {
var v1 = new THREE.Vector3();
return function ( a ) {
for ( var i = 0, il = a.length; i < il; i += 3 ) {
v1.x = a[ i ];
v1.y = a[ i + 1 ];
v1.z = a[ i + 2 ];
v1.applyMatrix3(this);
a[ i ] = v1.x;
a[ i + 1 ] = v1.y;
a[ i + 2 ] = v1.z;
}
return a;
};
}(),
multiplyScalar: function ( s ) {
var te = this.elements;
te[0] *= s; te[3] *= s; te[6] *= s;
te[1] *= s; te[4] *= s; te[7] *= s;
te[2] *= s; te[5] *= s; te[8] *= s;
return this;
},
determinant: function () {
var te = this.elements;
var a = te[0], b = te[1], c = te[2],
d = te[3], e = te[4], f = te[5],
g = te[6], h = te[7], i = te[8];
return a*e*i - a*f*h - b*d*i + b*f*g + c*d*h - c*e*g;
},
getInverse: function ( matrix, throwOnInvertible ) {
// input: THREE.Matrix4
// ( based on http://code.google.com/p/webgl-mjs/ )
var me = matrix.elements;
var te = this.elements;
te[ 0 ] = me[10] * me[5] - me[6] * me[9];
te[ 1 ] = - me[10] * me[1] + me[2] * me[9];
te[ 2 ] = me[6] * me[1] - me[2] * me[5];
te[ 3 ] = - me[10] * me[4] + me[6] * me[8];
te[ 4 ] = me[10] * me[0] - me[2] * me[8];
te[ 5 ] = - me[6] * me[0] + me[2] * me[4];
te[ 6 ] = me[9] * me[4] - me[5] * me[8];
te[ 7 ] = - me[9] * me[0] + me[1] * me[8];
te[ 8 ] = me[5] * me[0] - me[1] * me[4];
var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
// no inverse
if ( det === 0 ) {
var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
if ( throwOnInvertible || false ) {
throw new Error( msg );
} else {
console.warn( msg );
}
this.identity();
return this;
}
this.multiplyScalar( 1.0 / det );
return this;
},
transpose: function () {
var tmp, m = this.elements;
tmp = m[1]; m[1] = m[3]; m[3] = tmp;
tmp = m[2]; m[2] = m[6]; m[6] = tmp;
tmp = m[5]; m[5] = m[7]; m[7] = tmp;
return this;
},
getNormalMatrix: function ( m ) {
// input: THREE.Matrix4
this.getInverse( m ).transpose();
return this;
},
transposeIntoArray: function ( r ) {
var m = this.elements;
r[ 0 ] = m[ 0 ];
r[ 1 ] = m[ 3 ];
r[ 2 ] = m[ 6 ];
r[ 3 ] = m[ 1 ];
r[ 4 ] = m[ 4 ];
r[ 5 ] = m[ 7 ];
r[ 6 ] = m[ 2 ];
r[ 7 ] = m[ 5 ];
r[ 8 ] = m[ 8 ];
return this;
},
clone: function () {
var te = this.elements;
return new THREE.Matrix3(
te[0], te[3], te[6],
te[1], te[4], te[7],
te[2], te[5], te[8]
);
}
} );
/**
* @author mrdoob / http://mrdoob.com/
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author philogb / http://blog.thejit.org/
* @author jordi_ros / http://plattsoft.com
* @author D1plo1d / http://github.com/D1plo1d
* @author alteredq / http://alteredqualia.com/
* @author mikael emtinger / http://gomo.se/
* @author timknip / http://www.floorplanner.com/
* @author bhouston / http://exocortex.com
*/
THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
var te = this.elements = new Float32Array( 16 );
// TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix
// we should not support semi specification of Matrix4, it is just weird.
te[0] = ( n11 !== undefined ) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0;
te[1] = n21 || 0; te[5] = ( n22 !== undefined ) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0;
te[2] = n31 || 0; te[6] = n32 || 0; te[10] = ( n33 !== undefined ) ? n33 : 1; te[14] = n34 || 0;
te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = ( n44 !== undefined ) ? n44 : 1;
};
THREE.extend( THREE.Matrix4.prototype, {
set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
var te = this.elements;
te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14;
te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24;
te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34;
te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44;
return this;
},
identity: function () {
this.set(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
return this;
},
copy: function ( m ) {
var me = m.elements;
this.set(
me[0], me[4], me[8], me[12],
me[1], me[5], me[9], me[13],
me[2], me[6], me[10], me[14],
me[3], me[7], me[11], me[15]
);
return this;
},
setRotationFromEuler: function ( v, order ) {
var te = this.elements;
var x = v.x, y = v.y, z = v.z;
var a = Math.cos( x ), b = Math.sin( x );
var c = Math.cos( y ), d = Math.sin( y );
var e = Math.cos( z ), f = Math.sin( z );
if ( order === undefined || order === 'XYZ' ) {
var ae = a * e, af = a * f, be = b * e, bf = b * f;
te[0] = c * e;
te[4] = - c * f;
te[8] = d;
te[1] = af + be * d;
te[5] = ae - bf * d;
te[9] = - b * c;
te[2] = bf - ae * d;
te[6] = be + af * d;
te[10] = a * c;
} else if ( order === 'YXZ' ) {
var ce = c * e, cf = c * f, de = d * e, df = d * f;
te[0] = ce + df * b;
te[4] = de * b - cf;
te[8] = a * d;
te[1] = a * f;
te[5] = a * e;
te[9] = - b;
te[2] = cf * b - de;
te[6] = df + ce * b;
te[10] = a * c;
} else if ( order === 'ZXY' ) {
var ce = c * e, cf = c * f, de = d * e, df = d * f;
te[0] = ce - df * b;
te[4] = - a * f;
te[8] = de + cf * b;
te[1] = cf + de * b;
te[5] = a * e;
te[9] = df - ce * b;
te[2] = - a * d;
te[6] = b;
te[10] = a * c;
} else if ( order === 'ZYX' ) {
var ae = a * e, af = a * f, be = b * e, bf = b * f;
te[0] = c * e;
te[4] = be * d - af;
te[8] = ae * d + bf;
te[1] = c * f;
te[5] = bf * d + ae;
te[9] = af * d - be;
te[2] = - d;
te[6] = b * c;
te[10] = a * c;
} else if ( order === 'YZX' ) {
var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
te[0] = c * e;
te[4] = bd - ac * f;
te[8] = bc * f + ad;
te[1] = f;
te[5] = a * e;
te[9] = - b * e;
te[2] = - d * e;
te[6] = ad * f + bc;
te[10] = ac - bd * f;
} else if ( order === 'XZY' ) {
var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
te[0] = c * e;
te[4] = - f;
te[8] = d * e;
te[1] = ac * f + bd;
te[5] = a * e;
te[9] = ad * f - bc;
te[2] = bc * f - ad;
te[6] = b * e;
te[10] = bd * f + ac;
}
return this;
},
setRotationFromQuaternion: function ( q ) {
var te = this.elements;
var x = q.x, y = q.y, z = q.z, w = q.w;
var x2 = x + x, y2 = y + y, z2 = z + z;
var xx = x * x2, xy = x * y2, xz = x * z2;
var yy = y * y2, yz = y * z2, zz = z * z2;
var wx = w * x2, wy = w * y2, wz = w * z2;
te[0] = 1 - ( yy + zz );
te[4] = xy - wz;
te[8] = xz + wy;
te[1] = xy + wz;
te[5] = 1 - ( xx + zz );
te[9] = yz - wx;
te[2] = xz - wy;
te[6] = yz + wx;
te[10] = 1 - ( xx + yy );
return this;
},
lookAt: function() {
var x = new THREE.Vector3();
var y = new THREE.Vector3();
var z = new THREE.Vector3();
return function ( eye, target, up ) {
var te = this.elements;
z.subVectors( eye, target ).normalize();
if ( z.length() === 0 ) {
z.z = 1;
}
x.crossVectors( up, z ).normalize();
if ( x.length() === 0 ) {
z.x += 0.0001;
x.crossVectors( up, z ).normalize();
}
y.crossVectors( z, x );
te[0] = x.x; te[4] = y.x; te[8] = z.x;
te[1] = x.y; te[5] = y.y; te[9] = z.y;
te[2] = x.z; te[6] = y.z; te[10] = z.z;
return this;
};
}(),
multiply: function ( m, n ) {
if ( n !== undefined ) {
console.warn( 'DEPRECATED: Matrix4\'s .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
return this.multiplyMatrices( m, n );
}
return this.multiplyMatrices( this, m );
},
multiplyMatrices: function ( a, b ) {
var ae = a.elements;
var be = b.elements;
var te = this.elements;
var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12];
var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13];
var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14];
var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15];
var b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12];
var b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13];
var b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14];
var b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15];
te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
return this;
},
multiplyToArray: function ( a, b, r ) {
var te = this.elements;
this.multiplyMatrices( a, b );
r[ 0 ] = te[0]; r[ 1 ] = te[1]; r[ 2 ] = te[2]; r[ 3 ] = te[3];
r[ 4 ] = te[4]; r[ 5 ] = te[5]; r[ 6 ] = te[6]; r[ 7 ] = te[7];
r[ 8 ] = te[8]; r[ 9 ] = te[9]; r[ 10 ] = te[10]; r[ 11 ] = te[11];
r[ 12 ] = te[12]; r[ 13 ] = te[13]; r[ 14 ] = te[14]; r[ 15 ] = te[15];
return this;
},
multiplyScalar: function ( s ) {
var te = this.elements;
te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s;
te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s;
te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s;
te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s;
return this;
},
multiplyVector3: function ( vector ) {
console.warn( 'DEPRECATED: Matrix4\'s .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
return vector.applyProjection( this );
},
multiplyVector4: function ( vector ) {
console.warn( 'DEPRECATED: Matrix4\'s .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
return vector.applyMatrix4( this );
},
multiplyVector3Array: function() {
var v1 = new THREE.Vector3();
return function ( a ) {
for ( var i = 0, il = a.length; i < il; i += 3 ) {
v1.x = a[ i ];
v1.y = a[ i + 1 ];
v1.z = a[ i + 2 ];
v1.applyProjection( this );
a[ i ] = v1.x;
a[ i + 1 ] = v1.y;
a[ i + 2 ] = v1.z;
}
return a;
};
}(),
rotateAxis: function ( v ) {
var te = this.elements;
var vx = v.x, vy = v.y, vz = v.z;
v.x = vx * te[0] + vy * te[4] + vz * te[8];
v.y = vx * te[1] + vy * te[5] + vz * te[9];
v.z = vx * te[2] + vy * te[6] + vz * te[10];
v.normalize();
return v;
},
crossVector: function ( a ) {
var te = this.elements;
var v = new THREE.Vector4();
v.x = te[0] * a.x + te[4] * a.y + te[8] * a.z + te[12] * a.w;
v.y = te[1] * a.x + te[5] * a.y + te[9] * a.z + te[13] * a.w;
v.z = te[2] * a.x + te[6] * a.y + te[10] * a.z + te[14] * a.w;
v.w = ( a.w ) ? te[3] * a.x + te[7] * a.y + te[11] * a.z + te[15] * a.w : 1;
return v;
},
determinant: function () {
var te = this.elements;
var n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12];
var n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13];
var n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14];
var n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15];
//TODO: make this more efficient
//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
return (
n41 * (
+n14 * n23 * n32
-n13 * n24 * n32
-n14 * n22 * n33
+n12 * n24 * n33
+n13 * n22 * n34
-n12 * n23 * n34
) +
n42 * (
+n11 * n23 * n34
-n11 * n24 * n33
+n14 * n21 * n33
-n13 * n21 * n34
+n13 * n24 * n31
-n14 * n23 * n31
) +
n43 * (
+n11 * n24 * n32
-n11 * n22 * n34
-n14 * n21 * n32
+n12 * n21 * n34
+n14 * n22 * n31
-n12 * n24 * n31
) +
n44 * (
-n13 * n22 * n31
-n11 * n23 * n32
+n11 * n22 * n33
+n13 * n21 * n32
-n12 * n21 * n33
+n12 * n23 * n31
)
);
},
transpose: function () {
var te = this.elements;
var tmp;
tmp = te[1]; te[1] = te[4]; te[4] = tmp;
tmp = te[2]; te[2] = te[8]; te[8] = tmp;
tmp = te[6]; te[6] = te[9]; te[9] = tmp;
tmp = te[3]; te[3] = te[12]; te[12] = tmp;
tmp = te[7]; te[7] = te[13]; te[13] = tmp;
tmp = te[11]; te[11] = te[14]; te[14] = tmp;
return this;
},
flattenToArray: function ( flat ) {
var te = this.elements;
flat[ 0 ] = te[0]; flat[ 1 ] = te[1]; flat[ 2 ] = te[2]; flat[ 3 ] = te[3];
flat[ 4 ] = te[4]; flat[ 5 ] = te[5]; flat[ 6 ] = te[6]; flat[ 7 ] = te[7];
flat[ 8 ] = te[8]; flat[ 9 ] = te[9]; flat[ 10 ] = te[10]; flat[ 11 ] = te[11];
flat[ 12 ] = te[12]; flat[ 13 ] = te[13]; flat[ 14 ] = te[14]; flat[ 15 ] = te[15];
return flat;
},
flattenToArrayOffset: function( flat, offset ) {
var te = this.elements;
flat[ offset ] = te[0];
flat[ offset + 1 ] = te[1];
flat[ offset + 2 ] = te[2];
flat[ offset + 3 ] = te[3];
flat[ offset + 4 ] = te[4];
flat[ offset + 5 ] = te[5];
flat[ offset + 6 ] = te[6];
flat[ offset + 7 ] = te[7];
flat[ offset + 8 ] = te[8];
flat[ offset + 9 ] = te[9];
flat[ offset + 10 ] = te[10];
flat[ offset + 11 ] = te[11];
flat[ offset + 12 ] = te[12];
flat[ offset + 13 ] = te[13];
flat[ offset + 14 ] = te[14];
flat[ offset + 15 ] = te[15];
return flat;
},
getPosition: function() {
var v1 = new THREE.Vector3();
return function () {
console.warn( 'DEPRECATED: Matrix4\'s .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.' );
var te = this.elements;
return v1.set( te[12], te[13], te[14] );
};
}(),
setPosition: function ( v ) {
var te = this.elements;
te[12] = v.x;
te[13] = v.y;
te[14] = v.z;
return this;
},
getInverse: function ( m, throwOnInvertible ) {
// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
var te = this.elements;
var me = m.elements;
var n11 = me[0], n12 = me[4], n13 = me[8], n14 = me[12];
var n21 = me[1], n22 = me[5], n23 = me[9], n24 = me[13];
var n31 = me[2], n32 = me[6], n33 = me[10], n34 = me[14];
var n41 = me[3], n42 = me[7], n43 = me[11], n44 = me[15];
te[0] = n23*n34*n42 - n24*n33*n42 + n24*n32*n43 - n22*n34*n43 - n23*n32*n44 + n22*n33*n44;
te[4] = n14*n33*n42 - n13*n34*n42 - n14*n32*n43 + n12*n34*n43 + n13*n32*n44 - n12*n33*n44;
te[8] = n13*n24*n42 - n14*n23*n42 + n14*n22*n43 - n12*n24*n43 - n13*n22*n44 + n12*n23*n44;
te[12] = n14*n23*n32 - n13*n24*n32 - n14*n22*n33 + n12*n24*n33 + n13*n22*n34 - n12*n23*n34;
te[1] = n24*n33*n41 - n23*n34*n41 - n24*n31*n43 + n21*n34*n43 + n23*n31*n44 - n21*n33*n44;
te[5] = n13*n34*n41 - n14*n33*n41 + n14*n31*n43 - n11*n34*n43 - n13*n31*n44 + n11*n33*n44;
te[9] = n14*n23*n41 - n13*n24*n41 - n14*n21*n43 + n11*n24*n43 + n13*n21*n44 - n11*n23*n44;
te[13] = n13*n24*n31 - n14*n23*n31 + n14*n21*n33 - n11*n24*n33 - n13*n21*n34 + n11*n23*n34;
te[2] = n22*n34*n41 - n24*n32*n41 + n24*n31*n42 - n21*n34*n42 - n22*n31*n44 + n21*n32*n44;
te[6] = n14*n32*n41 - n12*n34*n41 - n14*n31*n42 + n11*n34*n42 + n12*n31*n44 - n11*n32*n44;
te[10] = n12*n24*n41 - n14*n22*n41 + n14*n21*n42 - n11*n24*n42 - n12*n21*n44 + n11*n22*n44;
te[14] = n14*n22*n31 - n12*n24*n31 - n14*n21*n32 + n11*n24*n32 + n12*n21*n34 - n11*n22*n34;
te[3] = n23*n32*n41 - n22*n33*n41 - n23*n31*n42 + n21*n33*n42 + n22*n31*n43 - n21*n32*n43;
te[7] = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43;
te[11] = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43;
te[15] = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33;
var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 4 ] + me[ 2 ] * te[ 8 ] + me[ 3 ] * te[ 12 ];
if ( det == 0 ) {
var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0";
if ( throwOnInvertible || false ) {
throw new Error( msg );
} else {
console.warn( msg );
}
this.identity();
return this;
}
this.multiplyScalar( 1 / det );
return this;
},
compose: function() {
var mRotation = new THREE.Matrix4(),
mScale = new THREE.Matrix4();
return function ( translation, rotation, scale ) {
var te = this.elements;
mRotation.identity();
mRotation.setRotationFromQuaternion( rotation );
mScale.makeScale( scale.x, scale.y, scale.z );
this.multiplyMatrices( mRotation, mScale );
te[12] = translation.x;
te[13] = translation.y;
te[14] = translation.z;
return this;
};
}(),
decompose: function() {
var x = new THREE.Vector3(),
y = new THREE.Vector3(),
z = new THREE.Vector3(),
matrix = new THREE.Matrix4();
return function ( translation, rotation, scale ) {
var te = this.elements;
// grab the axis vectors
x.set( te[0], te[1], te[2] );
y.set( te[4], te[5], te[6] );
z.set( te[8], te[9], te[10] );
translation = ( translation instanceof THREE.Vector3 ) ? translation : new THREE.Vector3();
rotation = ( rotation instanceof THREE.Quaternion ) ? rotation : new THREE.Quaternion();
scale = ( scale instanceof THREE.Vector3 ) ? scale : new THREE.Vector3();
scale.x = x.length();
scale.y = y.length();
scale.z = z.length();
translation.x = te[12];
translation.y = te[13];
translation.z = te[14];
// scale the rotation part
matrix.copy( this );
matrix.elements[0] /= scale.x;
matrix.elements[1] /= scale.x;
matrix.elements[2] /= scale.x;
matrix.elements[4] /= scale.y;
matrix.elements[5] /= scale.y;
matrix.elements[6] /= scale.y;
matrix.elements[8] /= scale.z;
matrix.elements[9] /= scale.z;
matrix.elements[10] /= scale.z;
rotation.setFromRotationMatrix( matrix );
return [ translation, rotation, scale ];
};
}(),
extractPosition: function ( m ) {
var te = this.elements;
var me = m.elements;
te[12] = me[12];
te[13] = me[13];
te[14] = me[14];
return this;
},
extractRotation: function() {
var v1 = new THREE.Vector3();
return function ( m ) {
var te = this.elements;
var me = m.elements;
var scaleX = 1 / v1.set( me[0], me[1], me[2] ).length();
var scaleY = 1 / v1.set( me[4], me[5], me[6] ).length();
var scaleZ = 1 / v1.set( me[8], me[9], me[10] ).length();
te[0] = me[0] * scaleX;
te[1] = me[1] * scaleX;
te[2] = me[2] * scaleX;
te[4] = me[4] * scaleY;
te[5] = me[5] * scaleY;
te[6] = me[6] * scaleY;
te[8] = me[8] * scaleZ;
te[9] = me[9] * scaleZ;
te[10] = me[10] * scaleZ;
return this;
};
}(),
translate: function ( v ) {
var te = this.elements;
var x = v.x, y = v.y, z = v.z;
te[12] = te[0] * x + te[4] * y + te[8] * z + te[12];
te[13] = te[1] * x + te[5] * y + te[9] * z + te[13];
te[14] = te[2] * x + te[6] * y + te[10] * z + te[14];
te[15] = te[3] * x + te[7] * y + te[11] * z + te[15];
return this;
},
rotateX: function ( angle ) {
var te = this.elements;
var m12 = te[4];
var m22 = te[5];
var m32 = te[6];
var m42 = te[7];
var m13 = te[8];
var m23 = te[9];
var m33 = te[10];
var m43 = te[11];
var c = Math.cos( angle );
var s = Math.sin( angle );
te[4] = c * m12 + s * m13;
te[5] = c * m22 + s * m23;
te[6] = c * m32 + s * m33;
te[7] = c * m42 + s * m43;
te[8] = c * m13 - s * m12;
te[9] = c * m23 - s * m22;
te[10] = c * m33 - s * m32;
te[11] = c * m43 - s * m42;
return this;
},
rotateY: function ( angle ) {
var te = this.elements;
var m11 = te[0];
var m21 = te[1];
var m31 = te[2];
var m41 = te[3];
var m13 = te[8];
var m23 = te[9];
var m33 = te[10];
var m43 = te[11];
var c = Math.cos( angle );
var s = Math.sin( angle );
te[0] = c * m11 - s * m13;
te[1] = c * m21 - s * m23;
te[2] = c * m31 - s * m33;
te[3] = c * m41 - s * m43;
te[8] = c * m13 + s * m11;
te[9] = c * m23 + s * m21;
te[10] = c * m33 + s * m31;
te[11] = c * m43 + s * m41;
return this;
},
rotateZ: function ( angle ) {
var te = this.elements;
var m11 = te[0];
var m21 = te[1];
var m31 = te[2];
var m41 = te[3];
var m12 = te[4];
var m22 = te[5];
var m32 = te[6];
var m42 = te[7];
var c = Math.cos( angle );
var s = Math.sin( angle );
te[0] = c * m11 + s * m12;
te[1] = c * m21 + s * m22;
te[2] = c * m31 + s * m32;
te[3] = c * m41 + s * m42;
te[4] = c * m12 - s * m11;
te[5] = c * m22 - s * m21;
te[6] = c * m32 - s * m31;
te[7] = c * m42 - s * m41;
return this;
},
rotateByAxis: function ( axis, angle ) {
var te = this.elements;
// optimize by checking axis
if ( axis.x === 1 && axis.y === 0 && axis.z === 0 ) {
return this.rotateX( angle );
} else if ( axis.x === 0 && axis.y === 1 && axis.z === 0 ) {
return this.rotateY( angle );
} else if ( axis.x === 0 && axis.y === 0 && axis.z === 1 ) {
return this.rotateZ( angle );
}
var x = axis.x, y = axis.y, z = axis.z;
var n = Math.sqrt(x * x + y * y + z * z);
x /= n;
y /= n;
z /= n;
var xx = x * x, yy = y * y, zz = z * z;
var c = Math.cos( angle );
var s = Math.sin( angle );
var oneMinusCosine = 1 - c;
var xy = x * y * oneMinusCosine;
var xz = x * z * oneMinusCosine;
var yz = y * z * oneMinusCosine;
var xs = x * s;
var ys = y * s;
var zs = z * s;
var r11 = xx + (1 - xx) * c;
var r21 = xy + zs;
var r31 = xz - ys;
var r12 = xy - zs;
var r22 = yy + (1 - yy) * c;
var r32 = yz + xs;
var r13 = xz + ys;
var r23 = yz - xs;
var r33 = zz + (1 - zz) * c;
var m11 = te[0], m21 = te[1], m31 = te[2], m41 = te[3];
var m12 = te[4], m22 = te[5], m32 = te[6], m42 = te[7];
var m13 = te[8], m23 = te[9], m33 = te[10], m43 = te[11];
te[0] = r11 * m11 + r21 * m12 + r31 * m13;
te[1] = r11 * m21 + r21 * m22 + r31 * m23;
te[2] = r11 * m31 + r21 * m32 + r31 * m33;
te[3] = r11 * m41 + r21 * m42 + r31 * m43;
te[4] = r12 * m11 + r22 * m12 + r32 * m13;
te[5] = r12 * m21 + r22 * m22 + r32 * m23;
te[6] = r12 * m31 + r22 * m32 + r32 * m33;
te[7] = r12 * m41 + r22 * m42 + r32 * m43;
te[8] = r13 * m11 + r23 * m12 + r33 * m13;
te[9] = r13 * m21 + r23 * m22 + r33 * m23;
te[10] = r13 * m31 + r23 * m32 + r33 * m33;
te[11] = r13 * m41 + r23 * m42 + r33 * m43;
return this;
},
scale: function ( v ) {
var te = this.elements;
var x = v.x, y = v.y, z = v.z;
te[0] *= x; te[4] *= y; te[8] *= z;
te[1] *= x; te[5] *= y; te[9] *= z;
te[2] *= x; te[6] *= y; te[10] *= z;
te[3] *= x; te[7] *= y; te[11] *= z;
return this;
},
getMaxScaleOnAxis: function () {
var te = this.elements;
var scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2];
var scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6];
var scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10];
return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) );
},
makeTranslation: function ( x, y, z ) {
this.set(
1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1
);
return this;
},
makeRotationX: function ( theta ) {
var c = Math.cos( theta ), s = Math.sin( theta );
this.set(
1, 0, 0, 0,
0, c, -s, 0,
0, s, c, 0,
0, 0, 0, 1
);
return this;
},
makeRotationY: function ( theta ) {
var c = Math.cos( theta ), s = Math.sin( theta );
this.set(
c, 0, s, 0,
0, 1, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1
);
return this;
},
makeRotationZ: function ( theta ) {
var c = Math.cos( theta ), s = Math.sin( theta );
this.set(
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
return this;
},
makeRotationAxis: function ( axis, angle ) {
// Based on http://www.gamedev.net/reference/articles/article1199.asp
var c = Math.cos( angle );
var s = Math.sin( angle );
var t = 1 - c;
var x = axis.x, y = axis.y, z = axis.z;
var tx = t * x, ty = t * y;
this.set(
tx * x + c, tx * y - s * z, tx * z + s * y, 0,
tx * y + s * z, ty * y + c, ty * z - s * x, 0,
tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
0, 0, 0, 1
);
return this;
},
makeScale: function ( x, y, z ) {
this.set(
x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1
);
return this;
},
makeFrustum: function ( left, right, bottom, top, near, far ) {
var te = this.elements;
var x = 2 * near / ( right - left );
var y = 2 * near / ( top - bottom );
var a = ( right + left ) / ( right - left );
var b = ( top + bottom ) / ( top - bottom );
var c = - ( far + near ) / ( far - near );
var d = - 2 * far * near / ( far - near );
te[0] = x; te[4] = 0; te[8] = a; te[12] = 0;
te[1] = 0; te[5] = y; te[9] = b; te[13] = 0;
te[2] = 0; te[6] = 0; te[10] = c; te[14] = d;
te[3] = 0; te[7] = 0; te[11] = - 1; te[15] = 0;
return this;
},
makePerspective: function ( fov, aspect, near, far ) {
var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) );
var ymin = - ymax;
var xmin = ymin * aspect;
var xmax = ymax * aspect;
return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );
},
makeOrthographic: function ( left, right, top, bottom, near, far ) {
var te = this.elements;
var w = right - left;
var h = top - bottom;
var p = far - near;
var x = ( right + left ) / w;
var y = ( top + bottom ) / h;
var z = ( far + near ) / p;
te[0] = 2 / w; te[4] = 0; te[8] = 0; te[12] = -x;
te[1] = 0; te[5] = 2 / h; te[9] = 0; te[13] = -y;
te[2] = 0; te[6] = 0; te[10] = -2/p; te[14] = -z;
te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1;
return this;
},
clone: function () {
var te = this.elements;
return new THREE.Matrix4(
te[0], te[4], te[8], te[12],
te[1], te[5], te[9], te[13],
te[2], te[6], te[10], te[14],
te[3], te[7], te[11], te[15]
);
}
} );
/**
* @author bhouston / http://exocortex.com
*/
THREE.Ray = function ( origin, direction ) {
this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
};
THREE.extend( THREE.Ray.prototype, {
set: function ( origin, direction ) {
this.origin.copy( origin );
this.direction.copy( direction );
return this;
},
copy: function ( ray ) {
this.origin.copy( ray.origin );
this.direction.copy( ray.direction );
return this;
},
at: function( t, optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
},
recast: function() {
var v1 = new THREE.Vector3();
return function ( t ) {
this.origin.copy( this.at( t, v1 ) );
return this;
};
}(),
closestPointToPoint: function ( point, optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
result.subVectors( point, this.origin );
var directionDistance = result.dot( this.direction );
return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
},
distanceToPoint: function() {
var v1 = new THREE.Vector3();
return function ( point ) {
var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
return v1.distanceTo( point );
};
}(),
isIntersectionSphere: function( sphere ) {
return ( this.distanceToPoint( sphere.center ) <= sphere.radius );
},
isIntersectionPlane: function ( plane ) {
// check if the line and plane are non-perpendicular, if they
// eventually they will intersect.
var denominator = plane.normal.dot( this.direction );
if ( denominator != 0 ) {
return true;
}
// line is coplanar, return origin
if( plane.distanceToPoint( this.origin ) == 0 ) {
return true;
}
return false;
},
distanceToPlane: function ( plane ) {
var denominator = plane.normal.dot( this.direction );
if ( denominator == 0 ) {
// line is coplanar, return origin
if( plane.distanceToPoint( this.origin ) == 0 ) {
return 0;
}
// Unsure if this is the correct method to handle this case.
return undefined;
}
var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
return t;
},
intersectPlane: function ( plane, optionalTarget ) {
var t = this.distanceToPlane( plane );
if ( t === undefined ) {
return undefined;
}
return this.at( t, optionalTarget );
},
applyMatrix4: function ( matrix4 ) {
this.direction.add( this.origin ).applyMatrix4( matrix4 );
this.origin.applyMatrix4( matrix4 );
this.direction.sub( this.origin );
return this;
},
equals: function ( ray ) {
return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
},
clone: function () {
return new THREE.Ray().copy( this );
}
} );
/**
* @author bhouston / http://exocortex.com
* @author mrdoob / http://mrdoob.com/
*/
THREE.Sphere = function ( center, radius ) {
this.center = ( center !== undefined ) ? center : new THREE.Vector3();
this.radius = ( radius !== undefined ) ? radius : 0;
};
THREE.extend( THREE.Sphere.prototype, {
set: function ( center, radius ) {
this.center.copy( center );
this.radius = radius;
return this;
},
setFromCenterAndPoints: function ( center, points ) {
var maxRadiusSq = 0;
for ( var i = 0, il = points.length; i < il; i ++ ) {
var radiusSq = center.distanceToSquared( points[ i ] );
maxRadiusSq = Math.max( maxRadiusSq, radiusSq );
}
this.center = center;
this.radius = Math.sqrt( maxRadiusSq );
return this;
},
copy: function ( sphere ) {
this.center.copy( sphere.center );
this.radius = sphere.radius;
return this;
},
empty: function () {
return ( this.radius <= 0 );
},
containsPoint: function ( point ) {
return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
},
distanceToPoint: function ( point ) {
return ( point.distanceTo( this.center ) - this.radius );
},
intersectsSphere: function ( sphere ) {
var radiusSum = this.radius + sphere.radius;
return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
},
clampPoint: function ( point, optionalTarget ) {
var deltaLengthSq = this.center.distanceToSquared( point );
var result = optionalTarget || new THREE.Vector3();
result.copy( point );
if ( deltaLengthSq > ( this.radius * this.radius ) ) {
result.sub( this.center ).normalize();
result.multiplyScalar( this.radius ).add( this.center );
}
return result;
},
getBoundingBox: function ( optionalTarget ) {
var box = optionalTarget || new THREE.Box3();
box.set( this.center, this.center );
box.expandByScalar( this.radius );
return box;
},
applyMatrix4: function ( matrix ) {
this.center.applyMatrix4( matrix );
this.radius = this.radius * matrix.getMaxScaleOnAxis();
return this;
},
translate: function ( offset ) {
this.center.add( offset );
return this;
},
equals: function ( sphere ) {
return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
},
clone: function () {
return new THREE.Sphere().copy( this );
}
} );
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author bhouston / http://exocortex.com
*/
THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) {
this.planes = [
( p0 !== undefined ) ? p0 : new THREE.Plane(),
( p1 !== undefined ) ? p1 : new THREE.Plane(),
( p2 !== undefined ) ? p2 : new THREE.Plane(),
( p3 !== undefined ) ? p3 : new THREE.Plane(),
( p4 !== undefined ) ? p4 : new THREE.Plane(),
( p5 !== undefined ) ? p5 : new THREE.Plane()
];
};
THREE.extend( THREE.Frustum.prototype, {
set: function ( p0, p1, p2, p3, p4, p5 ) {
var planes = this.planes;
planes[0].copy( p0 );
planes[1].copy( p1 );
planes[2].copy( p2 );
planes[3].copy( p3 );
planes[4].copy( p4 );
planes[5].copy( p5 );
return this;
},
copy: function ( frustum ) {
var planes = this.planes;
for( var i = 0; i < 6; i ++ ) {
planes[i].copy( frustum.planes[i] );
}
return this;
},
setFromMatrix: function ( m ) {
var planes = this.planes;
var me = m.elements;
var me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3];
var me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7];
var me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11];
var me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15];
planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
return this;
},
intersectsObject: function () {
var center = new THREE.Vector3();
return function ( object ) {
// this method is expanded inlined for performance reasons.
var matrix = object.matrixWorld;
var planes = this.planes;
var negRadius = - object.geometry.boundingSphere.radius * matrix.getMaxScaleOnAxis();
center.getPositionFromMatrix( matrix );
for ( var i = 0; i < 6; i ++ ) {
var distance = planes[ i ].distanceToPoint( center );
if ( distance < negRadius ) {
return false;
}
}
return true;
};
}(),
intersectsSphere: function ( sphere ) {
var planes = this.planes;
var center = sphere.center;
var negRadius = -sphere.radius;
for ( var i = 0; i < 6; i ++ ) {
var distance = planes[ i ].distanceToPoint( center );
if ( distance < negRadius ) {
return false;
}
}
return true;
},
containsPoint: function ( point ) {
var planes = this.planes;
for ( var i = 0; i < 6; i ++ ) {
if ( planes[ i ].distanceToPoint( point ) < 0 ) {
return false;
}
}
return true;
},
clone: function () {
return new THREE.Frustum().copy( this );
}
} );
/**
* @author bhouston / http://exocortex.com
*/
THREE.Plane = function ( normal, constant ) {
this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 );
this.constant = ( constant !== undefined ) ? constant : 0;
};
THREE.extend( THREE.Plane.prototype, {
set: function ( normal, constant ) {
this.normal.copy( normal );
this.constant = constant;
return this;
},
setComponents: function ( x, y, z, w ) {
this.normal.set( x, y, z );
this.constant = w;
return this;
},
setFromNormalAndCoplanarPoint: function ( normal, point ) {
this.normal.copy( normal );
this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized
return this;
},
setFromCoplanarPoints: function() {
var v1 = new THREE.Vector3();
var v2 = new THREE.Vector3();
return function ( a, b, c ) {
var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
this.setFromNormalAndCoplanarPoint( normal, a );
return this;
};
}(),
copy: function ( plane ) {
this.normal.copy( plane.normal );
this.constant = plane.constant;
return this;
},
normalize: function () {
// Note: will lead to a divide by zero if the plane is invalid.
var inverseNormalLength = 1.0 / this.normal.length();
this.normal.multiplyScalar( inverseNormalLength );
this.constant *= inverseNormalLength;
return this;
},
negate: function () {
this.constant *= -1;
this.normal.negate();
return this;
},
distanceToPoint: function ( point ) {
return this.normal.dot( point ) + this.constant;
},
distanceToSphere: function ( sphere ) {
return this.distanceToPoint( sphere.center ) - sphere.radius;
},
projectPoint: function ( point, optionalTarget ) {
return this.orthoPoint( point, optionalTarget ).sub( point ).negate();
},
orthoPoint: function ( point, optionalTarget ) {
var perpendicularMagnitude = this.distanceToPoint( point );
var result = optionalTarget || new THREE.Vector3();
return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );
},
isIntersectionLine: function ( line ) {
// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
var startSign = this.distanceToPoint( line.start );
var endSign = this.distanceToPoint( line.end );
return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
},
intersectLine: function() {
var v1 = new THREE.Vector3();
return function ( line, optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
var direction = line.delta( v1 );
var denominator = this.normal.dot( direction );
if ( denominator == 0 ) {
// line is coplanar, return origin
if( this.distanceToPoint( line.start ) == 0 ) {
return result.copy( line.start );
}
// Unsure if this is the correct method to handle this case.
return undefined;
}
var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
if( t < 0 || t > 1 ) {
return undefined;
}
return result.copy( direction ).multiplyScalar( t ).add( line.start );
};
}(),
coplanarPoint: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.copy( this.normal ).multiplyScalar( - this.constant );
},
applyMatrix4: function() {
var v1 = new THREE.Vector3();
var v2 = new THREE.Vector3();
return function ( matrix, optionalNormalMatrix ) {
// compute new normal based on theory here:
// http://www.songho.ca/opengl/gl_normaltransform.html
optionalNormalMatrix = optionalNormalMatrix || new THREE.Matrix3().getInverse( matrix ).transpose();
var newNormal = v1.copy( this.normal ).applyMatrix3( optionalNormalMatrix );
var newCoplanarPoint = this.coplanarPoint( v2 );
newCoplanarPoint.applyMatrix4( matrix );
this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );
return this;
};
}(),
translate: function ( offset ) {
this.constant = this.constant - offset.dot( this.normal );
return this;
},
equals: function ( plane ) {
return plane.normal.equals( this.normal ) && ( plane.constant == this.constant );
},
clone: function () {
return new THREE.Plane().copy( this );
}
} );
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.Math = {
// Clamp value to range <a, b>
clamp: function ( x, a, b ) {
return ( x < a ) ? a : ( ( x > b ) ? b : x );
},
// Clamp value to range <a, inf)
clampBottom: function ( x, a ) {
return x < a ? a : x;
},
// Linear mapping from range <a1, a2> to range <b1, b2>
mapLinear: function ( x, a1, a2, b1, b2 ) {
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
},
// http://en.wikipedia.org/wiki/Smoothstep
smoothstep: function ( x, min, max ) {
if ( x <= min ) return 0;
if ( x >= max ) return 1;
x = ( x - min )/( max - min );
return x*x*(3 - 2*x);
},
smootherstep: function ( x, min, max ) {
if ( x <= min ) return 0;
if ( x >= max ) return 1;
x = ( x - min )/( max - min );
return x*x*x*(x*(x*6 - 15) + 10);
},
// Random float from <0, 1> with 16 bits of randomness
// (standard Math.random() creates repetitive patterns when applied over larger space)
random16: function () {
return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
},
// Random integer from <low, high> interval
randInt: function ( low, high ) {
return low + Math.floor( Math.random() * ( high - low + 1 ) );
},
// Random float from <low, high> interval
randFloat: function ( low, high ) {
return low + Math.random() * ( high - low );
},
// Random float from <-range/2, range/2> interval
randFloatSpread: function ( range ) {
return range * ( 0.5 - Math.random() );
},
sign: function ( x ) {
return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 );
},
degToRad: function() {
var degreeToRadiansFactor = Math.PI / 180;
return function ( degrees ) {
return degrees * degreeToRadiansFactor;
};
}(),
radToDeg: function() {
var radianToDegreesFactor = 180 / Math.PI;
return function ( radians ) {
return radians * radianToDegreesFactor;
};
}()
};
/**
* Spline from Tween.js, slightly optimized (and trashed)
* http://sole.github.com/tween.js/examples/05_spline.html
*
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Spline = function ( points ) {
this.points = points;
var c = [], v3 = { x: 0, y: 0, z: 0 },
point, intPoint, weight, w2, w3,
pa, pb, pc, pd;
this.initFromArray = function( a ) {
this.points = [];
for ( var i = 0; i < a.length; i++ ) {
this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };
}
};
this.getPoint = function ( k ) {
point = ( this.points.length - 1 ) * k;
intPoint = Math.floor( point );
weight = point - intPoint;
c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
c[ 1 ] = intPoint;
c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;
pa = this.points[ c[ 0 ] ];
pb = this.points[ c[ 1 ] ];
pc = this.points[ c[ 2 ] ];
pd = this.points[ c[ 3 ] ];
w2 = weight * weight;
w3 = weight * w2;
v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );
return v3;
};
this.getControlPointsArray = function () {
var i, p, l = this.points.length,
coords = [];
for ( i = 0; i < l; i ++ ) {
p = this.points[ i ];
coords[ i ] = [ p.x, p.y, p.z ];
}
return coords;
};
// approximate length by summing linear segments
this.getLength = function ( nSubDivisions ) {
var i, index, nSamples, position,
point = 0, intPoint = 0, oldIntPoint = 0,
oldPosition = new THREE.Vector3(),
tmpVec = new THREE.Vector3(),
chunkLengths = [],
totalLength = 0;
// first point has 0 length
chunkLengths[ 0 ] = 0;
if ( !nSubDivisions ) nSubDivisions = 100;
nSamples = this.points.length * nSubDivisions;
oldPosition.copy( this.points[ 0 ] );
for ( i = 1; i < nSamples; i ++ ) {
index = i / nSamples;
position = this.getPoint( index );
tmpVec.copy( position );
totalLength += tmpVec.distanceTo( oldPosition );
oldPosition.copy( position );
point = ( this.points.length - 1 ) * index;
intPoint = Math.floor( point );
if ( intPoint != oldIntPoint ) {
chunkLengths[ intPoint ] = totalLength;
oldIntPoint = intPoint;
}
}
// last point ends with total length
chunkLengths[ chunkLengths.length ] = totalLength;
return { chunks: chunkLengths, total: totalLength };
};
this.reparametrizeByArcLength = function ( samplingCoef ) {
var i, j,
index, indexCurrent, indexNext,
linearDistance, realDistance,
sampling, position,
newpoints = [],
tmpVec = new THREE.Vector3(),
sl = this.getLength();
newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );
for ( i = 1; i < this.points.length; i++ ) {
//tmpVec.copy( this.points[ i - 1 ] );
//linearDistance = tmpVec.distanceTo( this.points[ i ] );
realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];
sampling = Math.ceil( samplingCoef * realDistance / sl.total );
indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
indexNext = i / ( this.points.length - 1 );
for ( j = 1; j < sampling - 1; j++ ) {
index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );
position = this.getPoint( index );
newpoints.push( tmpVec.copy( position ).clone() );
}
newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );
}
this.points = newpoints;
};
// Catmull-Rom
function interpolate( p0, p1, p2, p3, t, t2, t3 ) {
var v0 = ( p2 - p0 ) * 0.5,
v1 = ( p3 - p1 ) * 0.5;
return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
};
};
/**
* @author bhouston / http://exocortex.com
* @author mrdoob / http://mrdoob.com/
*/
THREE.Triangle = function ( a, b, c ) {
this.a = ( a !== undefined ) ? a : new THREE.Vector3();
this.b = ( b !== undefined ) ? b : new THREE.Vector3();
this.c = ( c !== undefined ) ? c : new THREE.Vector3();
};
THREE.Triangle.normal = function() {
var v0 = new THREE.Vector3();
return function( a, b, c, optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
result.subVectors( c, b );
v0.subVectors( a, b );
result.cross( v0 );
var resultLengthSq = result.lengthSq();
if( resultLengthSq > 0 ) {
return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );
}
return result.set( 0, 0, 0 );
};
}();
// static/instance method to calculate barycoordinates
// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
THREE.Triangle.barycoordFromPoint = function() {
var v0 = new THREE.Vector3(),
v1 = new THREE.Vector3(),
v2 = new THREE.Vector3();
return function ( point, a, b, c, optionalTarget ) {
v0.subVectors( c, a );
v1.subVectors( b, a );
v2.subVectors( point, a );
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 denom = ( dot00 * dot11 - dot01 * dot01 );
var result = optionalTarget || new THREE.Vector3();
// colinear or singular triangle
if( denom == 0 ) {
// arbitrary location outside of triangle?
// not sure if this is the best idea, maybe should be returning undefined
return result.set( -2, -1, -1 );
}
var invDenom = 1 / denom;
var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
// barycoordinates must always sum to 1
return result.set( 1 - u - v, v, u );
};
}();
THREE.Triangle.containsPoint = function() {
var v1 = new THREE.Vector3();
return function ( point, a, b, c ) {
var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 );
return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );
};
}();
THREE.extend( THREE.Triangle.prototype, {
constructor: THREE.Triangle,
set: function ( a, b, c ) {
this.a.copy( a );
this.b.copy( b );
this.c.copy( c );
return this;
},
setFromPointsAndIndices: function ( points, i0, i1, i2 ) {
this.a.copy( points[i0] );
this.b.copy( points[i1] );
this.c.copy( points[i2] );
return this;
},
copy: function ( triangle ) {
this.a.copy( triangle.a );
this.b.copy( triangle.b );
this.c.copy( triangle.c );
return this;
},
area: function() {
var v0 = new THREE.Vector3();
var v1 = new THREE.Vector3();
return function () {
v0.subVectors( this.c, this.b );
v1.subVectors( this.a, this.b );
return v0.cross( v1 ).length() * 0.5;
};
}(),
midpoint: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Vector3();
return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
},
normal: function ( optionalTarget ) {
return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget );
},
plane: function ( optionalTarget ) {
var result = optionalTarget || new THREE.Plane();
return result.setFromCoplanarPoints( this.a, this.b, this.c );
},
barycoordFromPoint: function ( point, optionalTarget ) {
return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );
},
containsPoint: function ( point ) {
return THREE.Triangle.containsPoint( point, this.a, this.b, this.c );
},
equals: function ( triangle ) {
return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
},
clone: function () {
return new THREE.Triangle().copy( this );
}
} );
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.Vertex = function ( v ) {
console.warn( 'THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.')
return v;
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.UV = function ( u, v ) {
console.warn( 'THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.')
return new THREE.Vector2( u, v );
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.Clock = function ( autoStart ) {
this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
this.startTime = 0;
this.oldTime = 0;
this.elapsedTime = 0;
this.running = false;
};
THREE.extend( THREE.Clock.prototype, {
start: function () {
this.startTime = window.performance !== undefined && window.performance.now !== undefined
? window.performance.now()
: Date.now();
this.oldTime = this.startTime;
this.running = true;
},
stop: function () {
this.getElapsedTime();
this.running = false;
},
getElapsedTime: function () {
this.getDelta();
return this.elapsedTime;
},
getDelta: function () {
var diff = 0;
if ( this.autoStart && ! this.running ) {
this.start();
}
if ( this.running ) {
var newTime = window.performance !== undefined && window.performance.now !== undefined
? window.performance.now()
: Date.now();
diff = 0.001 * ( newTime - this.oldTime );
this.oldTime = newTime;
this.elapsedTime += diff;
}
return diff;
}
} );
/**
* https://github.com/mrdoob/eventdispatcher.js/
*/
THREE.EventDispatcher = function () {
var listeners = {};
this.addEventListener = function ( type, listener ) {
if ( listeners[ type ] === undefined ) {
listeners[ type ] = [];
}
if ( listeners[ type ].indexOf( listener ) === - 1 ) {
listeners[ type ].push( listener );
}
};
this.removeEventListener = function ( type, listener ) {
var index = listeners[ type ].indexOf( listener );
if ( index !== - 1 ) {
listeners[ type ].splice( index, 1 );
}
};
this.dispatchEvent = function ( event ) {
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 );
}
}
};
};
/**
* @author mrdoob / http://mrdoob.com/
* @author bhouston / http://exocortex.com/
*/
( function ( THREE ) {
THREE.Raycaster = function ( origin, direction, near, far ) {
this.ray = new THREE.Ray( origin, direction );
// normalized ray.direction required for accurate distance calculations
if( this.ray.direction.lengthSq() > 0 ) {
this.ray.direction.normalize();
}
this.near = near || 0;
this.far = far || Infinity;
};
var sphere = new THREE.Sphere();
var localRay = new THREE.Ray();
var facePlane = new THREE.Plane();
var intersectPoint = new THREE.Vector3();
var matrixPosition = new THREE.Vector3();
var inverseMatrix = new THREE.Matrix4();
var descSort = function ( a, b ) {
return a.distance - b.distance;
};
var intersectObject = function ( object, raycaster, intersects ) {
if ( object instanceof THREE.Particle ) {
matrixPosition.getPositionFromMatrix( object.matrixWorld );
var distance = raycaster.ray.distanceToPoint( matrixPosition );
if ( distance > object.scale.x ) {
return intersects;
}
intersects.push( {
distance: distance,
point: object.position,
face: null,
object: object
} );
} else if ( object instanceof THREE.Mesh ) {
// Checking boundingSphere distance to ray
matrixPosition.getPositionFromMatrix( object.matrixWorld );
sphere.set(
matrixPosition,
object.geometry.boundingSphere.radius * object.matrixWorld.getMaxScaleOnAxis() );
if ( ! raycaster.ray.isIntersectionSphere( sphere ) ) {
return intersects;
}
// Checking faces
var geometry = object.geometry;
var vertices = geometry.vertices;
var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
var objectMaterials = isFaceMaterial === true ? object.material.materials : null;
var side = object.material.side;
var a, b, c, d;
var precision = raycaster.precision;
object.matrixRotationWorld.extractRotation( object.matrixWorld );
inverseMatrix.getInverse( object.matrixWorld );
localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
var face = geometry.faces[ f ];
var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : object.material;
if ( material === undefined ) continue;
facePlane.setFromNormalAndCoplanarPoint( face.normal, vertices[face.a] );
var planeDistance = localRay.distanceToPlane( facePlane );
// bail if raycaster and plane are parallel
if ( Math.abs( planeDistance ) < precision ) continue;
// if negative distance, then plane is behind raycaster
if ( planeDistance < 0 ) continue;
// check if we hit the wrong side of a single sided face
side = material.side;
if( side !== THREE.DoubleSide ) {
var planeSign = localRay.direction.dot( facePlane.normal );
if( ! ( side === THREE.FrontSide ? planeSign < 0 : planeSign > 0 ) ) continue;
}
// this can be done using the planeDistance from localRay because localRay wasn't normalized, but ray was
if ( planeDistance < raycaster.near || planeDistance > raycaster.far ) continue;
intersectPoint = localRay.at( planeDistance, intersectPoint ); // passing in intersectPoint avoids a copy
if ( face instanceof THREE.Face3 ) {
a = vertices[ face.a ];
b = vertices[ face.b ];
c = vertices[ face.c ];
if ( ! THREE.Triangle.containsPoint( intersectPoint, a, b, c ) ) continue;
} else if ( face instanceof THREE.Face4 ) {
a = vertices[ face.a ];
b = vertices[ face.b ];
c = vertices[ face.c ];
d = vertices[ face.d ];
if ( ( ! THREE.Triangle.containsPoint( intersectPoint, a, b, d ) ) &&
( ! THREE.Triangle.containsPoint( intersectPoint, b, c, d ) ) ) continue;
} else {
// This is added because if we call out of this if/else group when none of the cases
// match it will add a point to the intersection list erroneously.
throw Error( "face type not supported" );
}
intersects.push( {
distance: planeDistance, // this works because the original ray was normalized, and the transformed localRay wasn't
point: raycaster.ray.at( planeDistance ),
face: face,
faceIndex: f,
object: object
} );
}
}
};
var intersectDescendants = function ( object, raycaster, intersects ) {
var descendants = object.getDescendants();
for ( var i = 0, l = descendants.length; i < l; i ++ ) {
intersectObject( descendants[ i ], raycaster, intersects );
}
};
//
THREE.Raycaster.prototype.precision = 0.0001;
THREE.Raycaster.prototype.set = function ( origin, direction ) {
this.ray.set( origin, direction );
// normalized ray.direction required for accurate distance calculations
if( this.ray.direction.length() > 0 ) {
this.ray.direction.normalize();
}
};
THREE.Raycaster.prototype.intersectObject = function ( object, recursive ) {
var intersects = [];
if ( recursive === true ) {
intersectDescendants( object, this, intersects );
}
intersectObject( object, this, intersects );
intersects.sort( descSort );
return intersects;
};
THREE.Raycaster.prototype.intersectObjects = function ( objects, recursive ) {
var intersects = [];
for ( var i = 0, l = objects.length; i < l; i ++ ) {
intersectObject( objects[ i ], this, intersects );
if ( recursive === true ) {
intersectDescendants( objects[ i ], this, intersects );
}
}
intersects.sort( descSort );
return intersects;
};
}( THREE ) );
/**
* @author mrdoob / http://mrdoob.com/
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Object3D = function () {
this.id = THREE.Object3DIdCount ++;
this.name = '';
this.properties = {};
this.parent = undefined;
this.children = [];
this.up = new THREE.Vector3( 0, 1, 0 );
this.position = new THREE.Vector3();
this.rotation = new THREE.Vector3();
this.eulerOrder = THREE.Object3D.defaultEulerOrder;
this.scale = new THREE.Vector3( 1, 1, 1 );
this.renderDepth = null;
this.rotationAutoUpdate = true;
this.matrix = new THREE.Matrix4();
this.matrixWorld = new THREE.Matrix4();
this.matrixRotationWorld = new THREE.Matrix4();
this.matrixAutoUpdate = true;
this.matrixWorldNeedsUpdate = true;
this.quaternion = new THREE.Quaternion();
this.useQuaternion = false;
this.visible = true;
this.castShadow = false;
this.receiveShadow = false;
this.frustumCulled = true;
this._vector = new THREE.Vector3();
};
THREE.Object3D.prototype = {
constructor: THREE.Object3D,
applyMatrix: function ( matrix ) {
this.matrix.multiplyMatrices( matrix, this.matrix );
this.scale.getScaleFromMatrix( this.matrix );
var mat = new THREE.Matrix4().extractRotation( this.matrix );
this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder );
this.position.getPositionFromMatrix( this.matrix );
},
translate: function ( distance, axis ) {
this.matrix.rotateAxis( axis );
this.position.add( axis.multiplyScalar( distance ) );
},
translateX: function ( distance ) {
this.translate( distance, this._vector.set( 1, 0, 0 ) );
},
translateY: function ( distance ) {
this.translate( distance, this._vector.set( 0, 1, 0 ) );
},
translateZ: function ( distance ) {
this.translate( distance, this._vector.set( 0, 0, 1 ) );
},
localToWorld: function ( vector ) {
return vector.applyMatrix4( this.matrixWorld );
},
worldToLocal: function ( vector ) {
return vector.applyMatrix4( THREE.Object3D.__m1.getInverse( this.matrixWorld ) );
},
lookAt: function ( vector ) {
// TODO: Add hierarchy support.
this.matrix.lookAt( vector, this.position, this.up );
if ( this.rotationAutoUpdate ) {
if ( this.useQuaternion === false ) {
this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
} else {
this.quaternion.copy( this.matrix.decompose()[ 1 ] );
}
}
},
add: function ( object ) {
if ( object === this ) {
console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
return;
}
if ( object instanceof THREE.Object3D ) {
if ( object.parent !== undefined ) {
object.parent.remove( object );
}
object.parent = this;
this.children.push( object );
// add to scene
var scene = this;
while ( scene.parent !== undefined ) {
scene = scene.parent;
}
if ( scene !== undefined && scene instanceof THREE.Scene ) {
scene.__addObject( object );
}
}
},
remove: function ( object ) {
var index = this.children.indexOf( object );
if ( index !== - 1 ) {
object.parent = undefined;
this.children.splice( index, 1 );
// remove from scene
var scene = this;
while ( scene.parent !== undefined ) {
scene = scene.parent;
}
if ( scene !== undefined && scene instanceof THREE.Scene ) {
scene.__removeObject( object );
}
}
},
traverse: function ( callback ) {
callback( this );
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
this.children[ i ].traverse( callback );
}
},
getChildByName: function ( name, recursive ) {
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
var child = this.children[ i ];
if ( child.name === name ) {
return child;
}
if ( recursive === true ) {
child = child.getChildByName( name, recursive );
if ( child !== undefined ) {
return child;
}
}
}
return undefined;
},
getDescendants: function ( array ) {
if ( array === undefined ) array = [];
Array.prototype.push.apply( array, this.children );
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
this.children[ i ].getDescendants( array );
}
return array;
},
updateMatrix: function () {
this.matrix.setPosition( this.position );
if ( this.useQuaternion === false ) {
this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder );
} else {
this.matrix.setRotationFromQuaternion( this.quaternion );
}
if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) {
this.matrix.scale( this.scale );
}
this.matrixWorldNeedsUpdate = true;
},
updateMatrixWorld: function ( force ) {
if ( this.matrixAutoUpdate === true ) this.updateMatrix();
if ( this.matrixWorldNeedsUpdate === true || force === true ) {
if ( this.parent === undefined ) {
this.matrixWorld.copy( this.matrix );
} else {
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
}
this.matrixWorldNeedsUpdate = false;
force = true;
}
// update children
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
this.children[ i ].updateMatrixWorld( force );
}
},
clone: function ( object ) {
if ( object === undefined ) object = new THREE.Object3D();
object.name = this.name;
object.up.copy( this.up );
object.position.copy( this.position );
if ( object.rotation instanceof THREE.Vector3 ) object.rotation.copy( this.rotation ); // because of Sprite madness
object.eulerOrder = this.eulerOrder;
object.scale.copy( this.scale );
object.renderDepth = this.renderDepth;
object.rotationAutoUpdate = this.rotationAutoUpdate;
object.matrix.copy( this.matrix );
object.matrixWorld.copy( this.matrixWorld );
object.matrixRotationWorld.copy( this.matrixRotationWorld );
object.matrixAutoUpdate = this.matrixAutoUpdate;
object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
object.quaternion.copy( this.quaternion );
object.useQuaternion = this.useQuaternion;
object.visible = this.visible;
object.castShadow = this.castShadow;
object.receiveShadow = this.receiveShadow;
object.frustumCulled = this.frustumCulled;
for ( var i = 0; i < this.children.length; i ++ ) {
var child = this.children[ i ];
object.add( child.clone() );
}
return object;
}
};
THREE.Object3D.__m1 = new THREE.Matrix4();
THREE.Object3D.defaultEulerOrder = 'XYZ',
THREE.Object3DIdCount = 0;
/**
* @author mrdoob / http://mrdoob.com/
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author julianwa / https://github.com/julianwa
*/
THREE.Projector = function () {
var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
_face, _face3Count, _face3Pool = [], _face3PoolLength = 0,
_face4Count, _face4Pool = [], _face4PoolLength = 0,
_line, _lineCount, _linePool = [], _linePoolLength = 0,
_particle, _particleCount, _particlePool = [], _particlePoolLength = 0,
_renderData = { objects: [], sprites: [], lights: [], elements: [] },
_vector3 = new THREE.Vector3(),
_vector4 = new THREE.Vector4(),
_clipBox = new THREE.Box3( new THREE.Vector3( -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) ),
_boundingBox = new THREE.Box3(),
_points3 = new Array( 3 ),
_points4 = new Array( 4 ),
_viewMatrix = new THREE.Matrix4(),
_viewProjectionMatrix = new THREE.Matrix4(),
_modelMatrix,
_modelViewProjectionMatrix = new THREE.Matrix4(),
_normalMatrix = new THREE.Matrix3(),
_normalViewMatrix = new THREE.Matrix3(),
_centroid = new THREE.Vector3(),
_frustum = new THREE.Frustum(),
_clippedVertex1PositionScreen = new THREE.Vector4(),
_clippedVertex2PositionScreen = new THREE.Vector4();
this.projectVector = function ( vector, camera ) {
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
return vector.applyProjection( _viewProjectionMatrix );
};
this.unprojectVector = function ( vector, camera ) {
camera.projectionMatrixInverse.getInverse( camera.projectionMatrix );
_viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, camera.projectionMatrixInverse );
return vector.applyProjection( _viewProjectionMatrix );
};
this.pickingRay = function ( vector, camera ) {
// set two vectors with opposing z values
vector.z = -1.0;
var end = new THREE.Vector3( vector.x, vector.y, 1.0 );
this.unprojectVector( vector, camera );
this.unprojectVector( end, camera );
// find direction from vector to end
end.sub( vector ).normalize();
return new THREE.Raycaster( vector, end );
};
var projectGraph = function ( root, sortObjects ) {
_objectCount = 0;
_renderData.objects.length = 0;
_renderData.sprites.length = 0;
_renderData.lights.length = 0;
var projectObject = function ( parent ) {
for ( var c = 0, cl = parent.children.length; c < cl; c ++ ) {
var object = parent.children[ c ];
if ( object.visible === false ) continue;
if ( object instanceof THREE.Light ) {
_renderData.lights.push( object );
} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line ) {
if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
_object = getNextObjectInPool();
_object.object = object;
if ( object.renderDepth !== null ) {
_object.z = object.renderDepth;
} else {
_vector3.getPositionFromMatrix( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
}
_renderData.objects.push( _object );
}
} else if ( object instanceof THREE.Sprite || object instanceof THREE.Particle ) {
_object = getNextObjectInPool();
_object.object = object;
// TODO: Find an elegant and performant solution and remove this dupe code.
if ( object.renderDepth !== null ) {
_object.z = object.renderDepth;
} else {
_vector3.getPositionFromMatrix( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
}
_renderData.sprites.push( _object );
} else {
_object = getNextObjectInPool();
_object.object = object;
if ( object.renderDepth !== null ) {
_object.z = object.renderDepth;
} else {
_vector3.getPositionFromMatrix( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
}
_renderData.objects.push( _object );
}
projectObject( object );
}
};
projectObject( root );
if ( sortObjects === true ) _renderData.objects.sort( painterSort );
return _renderData;
};
this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
var visible = false,
o, ol, v, vl, f, fl, n, nl, c, cl, u, ul, object,
geometry, vertices, faces, face, faceVertexNormals, faceVertexUvs, uvs,
v1, v2, v3, v4, isFaceMaterial, objectMaterials;
_face3Count = 0;
_face4Count = 0;
_lineCount = 0;
_particleCount = 0;
_renderData.elements.length = 0;
scene.updateMatrixWorld();
if ( camera.parent === undefined ) camera.updateMatrixWorld();
_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
_normalViewMatrix.getInverse( _viewMatrix );
_normalViewMatrix.transpose();
_frustum.setFromMatrix( _viewProjectionMatrix );
_renderData = projectGraph( scene, sortObjects );
for ( o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
object = _renderData.objects[ o ].object;
_modelMatrix = object.matrixWorld;
_vertexCount = 0;
if ( object instanceof THREE.Mesh ) {
geometry = object.geometry;
vertices = geometry.vertices;
faces = geometry.faces;
faceVertexUvs = geometry.faceVertexUvs;
_normalMatrix.getInverse( _modelMatrix );
_normalMatrix.transpose();
isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
objectMaterials = isFaceMaterial === true ? object.material : null;
for ( v = 0, vl = vertices.length; v < vl; v ++ ) {
_vertex = getNextVertexInPool();
_vertex.positionWorld.copy( vertices[ v ] ).applyMatrix4( _modelMatrix );
_vertex.positionScreen.copy( _vertex.positionWorld ).applyMatrix4( _viewProjectionMatrix );
_vertex.positionScreen.x /= _vertex.positionScreen.w;
_vertex.positionScreen.y /= _vertex.positionScreen.w;
_vertex.positionScreen.z /= _vertex.positionScreen.w;
_vertex.visible = ! ( _vertex.positionScreen.x < -1 || _vertex.positionScreen.x > 1 ||
_vertex.positionScreen.y < -1 || _vertex.positionScreen.y > 1 ||
_vertex.positionScreen.z < -1 || _vertex.positionScreen.z > 1 );
}
for ( f = 0, fl = faces.length; f < fl; f ++ ) {
face = faces[ f ];
var material = isFaceMaterial === true
? objectMaterials.materials[ face.materialIndex ]
: object.material;
if ( material === undefined ) continue;
var side = material.side;
if ( face instanceof THREE.Face3 ) {
v1 = _vertexPool[ face.a ];
v2 = _vertexPool[ face.b ];
v3 = _vertexPool[ face.c ];
_points3[ 0 ] = v1.positionScreen;
_points3[ 1 ] = v2.positionScreen;
_points3[ 2 ] = v3.positionScreen;
if ( v1.visible === true || v2.visible === true || v3.visible === true ||
_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ) ) {
visible = ( ( v3.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) -
( v3.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) {
_face = getNextFace3InPool();
_face.v1.copy( v1 );
_face.v2.copy( v2 );
_face.v3.copy( v3 );
} else {
continue;
}
} else {
continue;
}
} else if ( face instanceof THREE.Face4 ) {
v1 = _vertexPool[ face.a ];
v2 = _vertexPool[ face.b ];
v3 = _vertexPool[ face.c ];
v4 = _vertexPool[ face.d ];
_points4[ 0 ] = v1.positionScreen;
_points4[ 1 ] = v2.positionScreen;
_points4[ 2 ] = v3.positionScreen;
_points4[ 3 ] = v4.positionScreen;
if ( v1.visible === true || v2.visible === true || v3.visible === true || v4.visible === true ||
_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points4 ) ) ) {
visible = ( v4.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) -
( v4.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) < 0 ||
( v2.positionScreen.x - v3.positionScreen.x ) * ( v4.positionScreen.y - v3.positionScreen.y ) -
( v2.positionScreen.y - v3.positionScreen.y ) * ( v4.positionScreen.x - v3.positionScreen.x ) < 0;
if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) {
_face = getNextFace4InPool();
_face.v1.copy( v1 );
_face.v2.copy( v2 );
_face.v3.copy( v3 );
_face.v4.copy( v4 );
} else {
continue;
}
} else {
continue;
}
}
_face.normalModel.copy( face.normal );
if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
_face.normalModel.negate();
}
_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
_face.normalModelView.copy( _face.normalModel ).applyMatrix3( _normalViewMatrix );
_face.centroidModel.copy( face.centroid ).applyMatrix4( _modelMatrix );
faceVertexNormals = face.vertexNormals;
for ( n = 0, nl = faceVertexNormals.length; n < nl; n ++ ) {
var normalModel = _face.vertexNormalsModel[ n ];
normalModel.copy( faceVertexNormals[ n ] );
if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
normalModel.negate();
}
normalModel.applyMatrix3( _normalMatrix ).normalize();
var normalModelView = _face.vertexNormalsModelView[ n ];
normalModelView.copy( normalModel ).applyMatrix3( _normalViewMatrix );
}
_face.vertexNormalsLength = faceVertexNormals.length;
for ( c = 0, cl = faceVertexUvs.length; c < cl; c ++ ) {
uvs = faceVertexUvs[ c ][ f ];
if ( uvs === undefined ) continue;
for ( u = 0, ul = uvs.length; u < ul; u ++ ) {
_face.uvs[ c ][ u ] = uvs[ u ];
}
}
_face.color = face.color;
_face.material = material;
_centroid.copy( _face.centroidModel ).applyProjection( _viewProjectionMatrix );
_face.z = _centroid.z;
_renderData.elements.push( _face );
}
} else if ( object instanceof THREE.Line ) {
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
vertices = object.geometry.vertices;
v1 = getNextVertexInPool();
v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
// Handle LineStrip and LinePieces
var step = object.type === THREE.LinePieces ? 2 : 1;
for ( v = 1, vl = vertices.length; v < vl; v ++ ) {
v1 = getNextVertexInPool();
v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix );
if ( ( v + 1 ) % step > 0 ) continue;
v2 = _vertexPool[ _vertexCount - 2 ];
_clippedVertex1PositionScreen.copy( v1.positionScreen );
_clippedVertex2PositionScreen.copy( v2.positionScreen );
if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) {
// Perform the perspective divide
_clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w );
_clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w );
_line = getNextLineInPool();
_line.v1.positionScreen.copy( _clippedVertex1PositionScreen );
_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
_line.material = object.material;
_renderData.elements.push( _line );
}
}
}
}
for ( o = 0, ol = _renderData.sprites.length; o < ol; o++ ) {
object = _renderData.sprites[ o ].object;
_modelMatrix = object.matrixWorld;
if ( object instanceof THREE.Particle ) {
_vector4.set( _modelMatrix.elements[12], _modelMatrix.elements[13], _modelMatrix.elements[14], 1 );
_vector4.applyMatrix4( _viewProjectionMatrix );
_vector4.z /= _vector4.w;
if ( _vector4.z > 0 && _vector4.z < 1 ) {
_particle = getNextParticleInPool();
_particle.object = object;
_particle.x = _vector4.x / _vector4.w;
_particle.y = _vector4.y / _vector4.w;
_particle.z = _vector4.z;
_particle.rotation = object.rotation.z;
_particle.scale.x = object.scale.x * Math.abs( _particle.x - ( _vector4.x + camera.projectionMatrix.elements[0] ) / ( _vector4.w + camera.projectionMatrix.elements[12] ) );
_particle.scale.y = object.scale.y * Math.abs( _particle.y - ( _vector4.y + camera.projectionMatrix.elements[5] ) / ( _vector4.w + camera.projectionMatrix.elements[13] ) );
_particle.material = object.material;
_renderData.elements.push( _particle );
}
}
}
if ( sortElements === true ) _renderData.elements.sort( painterSort );
return _renderData;
};
// Pools
function getNextObjectInPool() {
if ( _objectCount === _objectPoolLength ) {
var object = new THREE.RenderableObject();
_objectPool.push( object );
_objectPoolLength ++;
_objectCount ++;
return object;
}
return _objectPool[ _objectCount ++ ];
}
function getNextVertexInPool() {
if ( _vertexCount === _vertexPoolLength ) {
var vertex = new THREE.RenderableVertex();
_vertexPool.push( vertex );
_vertexPoolLength ++;
_vertexCount ++;
return vertex;
}
return _vertexPool[ _vertexCount ++ ];
}
function getNextFace3InPool() {
if ( _face3Count === _face3PoolLength ) {
var face = new THREE.RenderableFace3();
_face3Pool.push( face );
_face3PoolLength ++;
_face3Count ++;
return face;
}
return _face3Pool[ _face3Count ++ ];
}
function getNextFace4InPool() {
if ( _face4Count === _face4PoolLength ) {
var face = new THREE.RenderableFace4();
_face4Pool.push( face );
_face4PoolLength ++;
_face4Count ++;
return face;
}
return _face4Pool[ _face4Count ++ ];
}
function getNextLineInPool() {
if ( _lineCount === _linePoolLength ) {
var line = new THREE.RenderableLine();
_linePool.push( line );
_linePoolLength ++;
_lineCount ++
return line;
}
return _linePool[ _lineCount ++ ];
}
function getNextParticleInPool() {
if ( _particleCount === _particlePoolLength ) {
var particle = new THREE.RenderableParticle();
_particlePool.push( particle );
_particlePoolLength ++;
_particleCount ++
return particle;
}
return _particlePool[ _particleCount ++ ];
}
//
function painterSort( a, b ) {
return b.z - a.z;
}
function clipLine( s1, s2 ) {
var alpha1 = 0, alpha2 = 1,
// Calculate the boundary coordinate of each vertex for the near and far clip planes,
// Z = -1 and Z = +1, respectively.
bc1near = s1.z + s1.w,
bc2near = s2.z + s2.w,
bc1far = - s1.z + s1.w,
bc2far = - s2.z + s2.w;
if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
// Both vertices lie entirely within all clip planes.
return true;
} else if ( ( bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0 ) ) {
// Both vertices lie entirely outside one of the clip planes.
return false;
} else {
// The line segment spans at least one clip plane.
if ( bc1near < 0 ) {
// v1 lies outside the near plane, v2 inside
alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
} else if ( bc2near < 0 ) {
// v2 lies outside the near plane, v1 inside
alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
}
if ( bc1far < 0 ) {
// v1 lies outside the far plane, v2 inside
alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
} else if ( bc2far < 0 ) {
// v2 lies outside the far plane, v2 inside
alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
}
if ( alpha2 < alpha1 ) {
// The line segment spans two boundaries, but is outside both of them.
// (This can't happen when we're only clipping against just near/far but good
// to leave the check here for future usage if other clip planes are added.)
return false;
} else {
// Update the s1 and s2 vertices to match the clipped line segment.
s1.lerp( s2, alpha1 );
s2.lerp( s1, 1 - alpha2 );
return true;
}
}
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {
this.a = a;
this.b = b;
this.c = c;
this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
this.vertexNormals = normal instanceof Array ? normal : [ ];
this.color = color instanceof THREE.Color ? color : new THREE.Color();
this.vertexColors = color instanceof Array ? color : [];
this.vertexTangents = [];
this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
this.centroid = new THREE.Vector3();
};
THREE.Face3.prototype = {
constructor: THREE.Face3,
clone: function () {
var face = new THREE.Face3( this.a, this.b, this.c );
face.normal.copy( this.normal );
face.color.copy( this.color );
face.centroid.copy( this.centroid );
face.materialIndex = this.materialIndex;
var i, il;
for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone();
for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone();
for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone();
return face;
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
this.vertexNormals = normal instanceof Array ? normal : [ ];
this.color = color instanceof THREE.Color ? color : new THREE.Color();
this.vertexColors = color instanceof Array ? color : [];
this.vertexTangents = [];
this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
this.centroid = new THREE.Vector3();
};
THREE.Face4.prototype = {
constructor: THREE.Face4,
clone: function () {
var face = new THREE.Face4( this.a, this.b, this.c, this.d );
face.normal.copy( this.normal );
face.color.copy( this.color );
face.centroid.copy( this.centroid );
face.materialIndex = this.materialIndex;
var i, il;
for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone();
for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone();
for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone();
return face;
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author kile / http://kile.stravaganza.org/
* @author alteredq / http://alteredqualia.com/
* @author mikael emtinger / http://gomo.se/
* @author zz85 / http://www.lab4games.net/zz85/blog
* @author bhouston / http://exocortex.com
*/
THREE.Geometry = function () {
THREE.EventDispatcher.call( this );
this.id = THREE.GeometryIdCount ++;
this.name = '';
this.vertices = [];
this.colors = []; // one-to-one vertex colors, used in ParticleSystem, Line and Ribbon
this.normals = []; // one-to-one vertex normals, used in Ribbon
this.faces = [];
this.faceUvs = [[]];
this.faceVertexUvs = [[]];
this.morphTargets = [];
this.morphColors = [];
this.morphNormals = [];
this.skinWeights = [];
this.skinIndices = [];
this.lineDistances = [];
this.boundingBox = null;
this.boundingSphere = null;
this.hasTangents = false;
this.dynamic = true; // the intermediate typed arrays will be deleted when set to false
// update flags
this.verticesNeedUpdate = false;
this.elementsNeedUpdate = false;
this.uvsNeedUpdate = false;
this.normalsNeedUpdate = false;
this.tangentsNeedUpdate = false;
this.colorsNeedUpdate = false;
this.lineDistancesNeedUpdate = false;
this.buffersNeedUpdate = false;
};
THREE.Geometry.prototype = {
constructor: THREE.Geometry,
applyMatrix: function ( matrix ) {
var normalMatrix = new THREE.Matrix3().getInverse( matrix ).transpose();
for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
var vertex = this.vertices[ i ];
vertex.applyMatrix4( matrix );
}
for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
var face = this.faces[ i ];
face.normal.applyMatrix3( normalMatrix ).normalize();
for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
}
face.centroid.applyMatrix4( matrix );
}
},
computeCentroids: function () {
var f, fl, face;
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
face.centroid.set( 0, 0, 0 );
if ( face instanceof THREE.Face3 ) {
face.centroid.add( this.vertices[ face.a ] );
face.centroid.add( this.vertices[ face.b ] );
face.centroid.add( this.vertices[ face.c ] );
face.centroid.divideScalar( 3 );
} else if ( face instanceof THREE.Face4 ) {
face.centroid.add( this.vertices[ face.a ] );
face.centroid.add( this.vertices[ face.b ] );
face.centroid.add( this.vertices[ face.c ] );
face.centroid.add( this.vertices[ face.d ] );
face.centroid.divideScalar( 4 );
}
}
},
computeFaceNormals: function () {
var cb = new THREE.Vector3(), ab = new THREE.Vector3();
for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
var face = this.faces[ f ];
var vA = this.vertices[ face.a ];
var vB = this.vertices[ face.b ];
var vC = this.vertices[ face.c ];
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
cb.normalize();
face.normal.copy( cb );
}
},
computeVertexNormals: function ( areaWeighted ) {
var v, vl, f, fl, face, vertices;
// create internal buffers for reuse when calling this method repeatedly
// (otherwise memory allocation / deallocation every frame is big resource hog)
if ( this.__tmpVertices === undefined ) {
this.__tmpVertices = new Array( this.vertices.length );
vertices = this.__tmpVertices;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ] = new THREE.Vector3();
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
} else if ( face instanceof THREE.Face4 ) {
face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
}
}
} else {
vertices = this.__tmpVertices;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ].set( 0, 0, 0 );
}
}
if ( areaWeighted ) {
// vertex normals weighted by triangle areas
// http://www.iquilezles.org/www/articles/normals/normals.htm
var vA, vB, vC, vD;
var cb = new THREE.Vector3(), ab = new THREE.Vector3(),
db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3();
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
vA = this.vertices[ face.a ];
vB = this.vertices[ face.b ];
vC = this.vertices[ face.c ];
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
vertices[ face.a ].add( cb );
vertices[ face.b ].add( cb );
vertices[ face.c ].add( cb );
} else if ( face instanceof THREE.Face4 ) {
vA = this.vertices[ face.a ];
vB = this.vertices[ face.b ];
vC = this.vertices[ face.c ];
vD = this.vertices[ face.d ];
// abd
db.subVectors( vD, vB );
ab.subVectors( vA, vB );
db.cross( ab );
vertices[ face.a ].add( db );
vertices[ face.b ].add( db );
vertices[ face.d ].add( db );
// bcd
dc.subVectors( vD, vC );
bc.subVectors( vB, vC );
dc.cross( bc );
vertices[ face.b ].add( dc );
vertices[ face.c ].add( dc );
vertices[ face.d ].add( dc );
}
}
} else {
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
vertices[ face.a ].add( face.normal );
vertices[ face.b ].add( face.normal );
vertices[ face.c ].add( face.normal );
} else if ( face instanceof THREE.Face4 ) {
vertices[ face.a ].add( face.normal );
vertices[ face.b ].add( face.normal );
vertices[ face.c ].add( face.normal );
vertices[ face.d ].add( face.normal );
}
}
}
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ].normalize();
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
} else if ( face instanceof THREE.Face4 ) {
face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
face.vertexNormals[ 3 ].copy( vertices[ face.d ] );
}
}
},
computeMorphNormals: function () {
var i, il, f, fl, face;
// save original normals
// - create temp variables on first access
// otherwise just copy (for faster repeated calls)
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( ! face.__originalFaceNormal ) {
face.__originalFaceNormal = face.normal.clone();
} else {
face.__originalFaceNormal.copy( face.normal );
}
if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
if ( ! face.__originalVertexNormals[ i ] ) {
face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
} else {
face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
}
}
}
// use temp geometry to compute face and vertex normals for each morph
var tmpGeo = new THREE.Geometry();
tmpGeo.faces = this.faces;
for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
// create on first access
if ( ! this.morphNormals[ i ] ) {
this.morphNormals[ i ] = {};
this.morphNormals[ i ].faceNormals = [];
this.morphNormals[ i ].vertexNormals = [];
var dstNormalsFace = this.morphNormals[ i ].faceNormals;
var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
var faceNormal, vertexNormals;
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
faceNormal = new THREE.Vector3();
if ( face instanceof THREE.Face3 ) {
vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
} else {
vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3(), d: new THREE.Vector3() };
}
dstNormalsFace.push( faceNormal );
dstNormalsVertex.push( vertexNormals );
}
}
var morphNormals = this.morphNormals[ i ];
// set vertices to morph target
tmpGeo.vertices = this.morphTargets[ i ].vertices;
// compute morph normals
tmpGeo.computeFaceNormals();
tmpGeo.computeVertexNormals();
// store morph normals
var faceNormal, vertexNormals;
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
faceNormal = morphNormals.faceNormals[ f ];
vertexNormals = morphNormals.vertexNormals[ f ];
faceNormal.copy( face.normal );
if ( face instanceof THREE.Face3 ) {
vertexNormals.a.copy( face.vertexNormals[ 0 ] );
vertexNormals.b.copy( face.vertexNormals[ 1 ] );
vertexNormals.c.copy( face.vertexNormals[ 2 ] );
} else {
vertexNormals.a.copy( face.vertexNormals[ 0 ] );
vertexNormals.b.copy( face.vertexNormals[ 1 ] );
vertexNormals.c.copy( face.vertexNormals[ 2 ] );
vertexNormals.d.copy( face.vertexNormals[ 3 ] );
}
}
}
// restore original normals
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
face.normal = face.__originalFaceNormal;
face.vertexNormals = face.__originalVertexNormals;
}
},
computeTangents: function () {
// based on http://www.terathon.com/code/tangent.html
// tangents go to vertices
var f, fl, v, vl, i, il, vertexIndex,
face, uv, vA, vB, vC, uvA, uvB, uvC,
x1, x2, y1, y2, z1, z2,
s1, s2, t1, t2, r, t, test,
tan1 = [], tan2 = [],
sdir = new THREE.Vector3(), tdir = new THREE.Vector3(),
tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(),
n = new THREE.Vector3(), w;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
tan1[ v ] = new THREE.Vector3();
tan2[ v ] = new THREE.Vector3();
}
function handleTriangle( context, a, b, c, ua, ub, uc ) {
vA = context.vertices[ a ];
vB = context.vertices[ b ];
vC = context.vertices[ c ];
uvA = uv[ ua ];
uvB = uv[ ub ];
uvC = uv[ uc ];
x1 = vB.x - vA.x;
x2 = vC.x - vA.x;
y1 = vB.y - vA.y;
y2 = vC.y - vA.y;
z1 = vB.z - vA.z;
z2 = vC.z - vA.z;
s1 = uvB.x - uvA.x;
s2 = uvC.x - uvA.x;
t1 = uvB.y - uvA.y;
t2 = uvC.y - uvA.y;
r = 1.0 / ( s1 * t2 - s2 * t1 );
sdir.set( ( t2 * x1 - t1 * x2 ) * r,
( t2 * y1 - t1 * y2 ) * r,
( t2 * z1 - t1 * z2 ) * r );
tdir.set( ( s1 * x2 - s2 * x1 ) * r,
( s1 * y2 - s2 * y1 ) * r,
( s1 * z2 - s2 * z1 ) * r );
tan1[ a ].add( sdir );
tan1[ b ].add( sdir );
tan1[ c ].add( sdir );
tan2[ a ].add( tdir );
tan2[ b ].add( tdir );
tan2[ c ].add( tdir );
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents
if ( face instanceof THREE.Face3 ) {
handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
} else if ( face instanceof THREE.Face4 ) {
handleTriangle( this, face.a, face.b, face.d, 0, 1, 3 );
handleTriangle( this, face.b, face.c, face.d, 1, 2, 3 );
}
}
var faceIndex = [ 'a', 'b', 'c', 'd' ];
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
for ( i = 0; i < face.vertexNormals.length; i++ ) {
n.copy( face.vertexNormals[ i ] );
vertexIndex = face[ faceIndex[ i ] ];
t = tan1[ vertexIndex ];
// Gram-Schmidt orthogonalize
tmp.copy( t );
tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
// Calculate handedness
tmp2.crossVectors( face.vertexNormals[ i ], t );
test = tmp2.dot( tan2[ vertexIndex ] );
w = (test < 0.0) ? -1.0 : 1.0;
face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w );
}
}
this.hasTangents = true;
},
computeLineDistances: function ( ) {
var d = 0;
var vertices = this.vertices;
for ( var i = 0, il = vertices.length; i < il; i ++ ) {
if ( i > 0 ) {
d += vertices[ i ].distanceTo( vertices[ i - 1 ] );
}
this.lineDistances[ i ] = d;
}
},
computeBoundingBox: function () {
if ( this.boundingBox === null ) {
this.boundingBox = new THREE.Box3();
}
this.boundingBox.setFromPoints( this.vertices );
},
computeBoundingSphere: function () {
if ( this.boundingSphere === null ) {
this.boundingSphere = new THREE.Sphere();
}
this.boundingSphere.setFromCenterAndPoints( this.boundingSphere.center, this.vertices );
},
/*
* Checks for duplicate vertices with hashmap.
* Duplicated vertices are removed
* and faces' vertices are updated.
*/
mergeVertices: function () {
var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique)
var unique = [], changes = [];
var v, key;
var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001
var precision = Math.pow( 10, precisionPoints );
var i,il, face;
var indices, k, j, jl, u;
// reset cache of vertices as it now will be changing.
this.__tmpVertices = undefined;
for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
v = this.vertices[ i ];
key = [ Math.round( v.x * precision ), Math.round( v.y * precision ), Math.round( v.z * precision ) ].join( '_' );
if ( verticesMap[ key ] === undefined ) {
verticesMap[ key ] = i;
unique.push( this.vertices[ i ] );
changes[ i ] = unique.length - 1;
} else {
//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
changes[ i ] = changes[ verticesMap[ key ] ];
}
};
// if faces are completely degenerate after merging vertices, we
// have to remove them from the geometry.
var faceIndicesToRemove = [];
for( i = 0, il = this.faces.length; i < il; i ++ ) {
face = this.faces[ i ];
if ( face instanceof THREE.Face3 ) {
face.a = changes[ face.a ];
face.b = changes[ face.b ];
face.c = changes[ face.c ];
indices = [ face.a, face.b, face.c ];
var dupIndex = -1;
// if any duplicate vertices are found in a Face3
// we have to remove the face as nothing can be saved
for ( var n = 0; n < 3; n ++ ) {
if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) {
dupIndex = n;
faceIndicesToRemove.push( i );
break;
}
}
} else if ( face instanceof THREE.Face4 ) {
face.a = changes[ face.a ];
face.b = changes[ face.b ];
face.c = changes[ face.c ];
face.d = changes[ face.d ];
// check dups in (a, b, c, d) and convert to -> face3
indices = [ face.a, face.b, face.c, face.d ];
var dupIndex = -1;
for ( var n = 0; n < 4; n ++ ) {
if ( indices[ n ] == indices[ ( n + 1 ) % 4 ] ) {
// if more than one duplicated vertex is found
// we can't generate any valid Face3's, thus
// we need to remove this face complete.
if ( dupIndex >= 0 ) {
faceIndicesToRemove.push( i );
}
dupIndex = n;
}
}
if ( dupIndex >= 0 ) {
indices.splice( dupIndex, 1 );
var newFace = new THREE.Face3( indices[0], indices[1], indices[2], face.normal, face.color, face.materialIndex );
for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
u = this.faceVertexUvs[ j ][ i ];
if ( u ) {
u.splice( dupIndex, 1 );
}
}
if( face.vertexNormals && face.vertexNormals.length > 0) {
newFace.vertexNormals = face.vertexNormals;
newFace.vertexNormals.splice( dupIndex, 1 );
}
if( face.vertexColors && face.vertexColors.length > 0 ) {
newFace.vertexColors = face.vertexColors;
newFace.vertexColors.splice( dupIndex, 1 );
}
this.faces[ i ] = newFace;
}
}
}
for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
this.faces.splice( i, 1 );
for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
this.faceVertexUvs[ j ].splice( i, 1 );
}
}
// Use unique set of vertices
var diff = this.vertices.length - unique.length;
this.vertices = unique;
return diff;
},
clone: function () {
var geometry = new THREE.Geometry();
var vertices = this.vertices;
for ( var i = 0, il = vertices.length; i < il; i ++ ) {
geometry.vertices.push( vertices[ i ].clone() );
}
var faces = this.faces;
for ( var i = 0, il = faces.length; i < il; i ++ ) {
geometry.faces.push( faces[ i ].clone() );
}
var uvs = this.faceVertexUvs[ 0 ];
for ( var i = 0, il = uvs.length; i < il; i ++ ) {
var uv = uvs[ i ], uvCopy = [];
for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
}
geometry.faceVertexUvs[ 0 ].push( uvCopy );
}
return geometry;
},
dispose: function () {
this.dispatchEvent( { type: 'dispose' } );
}
};
THREE.GeometryIdCount = 0;
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.BufferGeometry = function () {
THREE.EventDispatcher.call( this );
this.id = THREE.GeometryIdCount ++;
// attributes
this.attributes = {};
// attributes typed arrays are kept only if dynamic flag is set
this.dynamic = false;
// offsets for chunks when using indexed elements
this.offsets = [];
// boundings
this.boundingBox = null;
this.boundingSphere = null;
this.hasTangents = false;
// for compatibility
this.morphTargets = [];
};
THREE.BufferGeometry.prototype = {
constructor : THREE.BufferGeometry,
applyMatrix: function ( matrix ) {
var positionArray;
var normalArray;
if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array;
if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array;
if ( positionArray !== undefined ) {
matrix.multiplyVector3Array( positionArray );
this.verticesNeedUpdate = true;
}
if ( normalArray !== undefined ) {
var normalMatrix = new THREE.Matrix3();
normalMatrix.getInverse( matrix ).transpose();
normalMatrix.multiplyVector3Array( normalArray );
this.normalizeNormals();
this.normalsNeedUpdate = true;
}
},
computeBoundingBox: function () {
if ( this.boundingBox === null ) {
this.boundingBox = new THREE.Box3();
}
var positions = this.attributes[ "position" ].array;
if ( positions ) {
var bb = this.boundingBox;
var x, y, z;
if( positions.length >= 3 ) {
bb.min.x = bb.max.x = positions[ 0 ];
bb.min.y = bb.max.y = positions[ 1 ];
bb.min.z = bb.max.z = positions[ 2 ];
}
for ( var i = 3, il = positions.length; i < il; i += 3 ) {
x = positions[ i ];
y = positions[ i + 1 ];
z = positions[ i + 2 ];
// bounding box
if ( x < bb.min.x ) {
bb.min.x = x;
} else if ( x > bb.max.x ) {
bb.max.x = x;
}
if ( y < bb.min.y ) {
bb.min.y = y;
} else if ( y > bb.max.y ) {
bb.max.y = y;
}
if ( z < bb.min.z ) {
bb.min.z = z;
} else if ( z > bb.max.z ) {
bb.max.z = z;
}
}
}
if ( positions === undefined || positions.length === 0 ) {
this.boundingBox.min.set( 0, 0, 0 );
this.boundingBox.max.set( 0, 0, 0 );
}
},
computeBoundingSphere: function () {
if ( this.boundingSphere === null ) {
this.boundingSphere = new THREE.Sphere();
}
var positions = this.attributes[ "position" ].array;
if ( positions ) {
var radiusSq, maxRadiusSq = 0;
var x, y, z;
for ( var i = 0, il = positions.length; i < il; i += 3 ) {
x = positions[ i ];
y = positions[ i + 1 ];
z = positions[ i + 2 ];
radiusSq = x * x + y * y + z * z;
if ( radiusSq > maxRadiusSq ) maxRadiusSq = radiusSq;
}
this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
}
},
computeVertexNormals: function () {
if ( this.attributes[ "position" ] ) {
var i, il;
var j, jl;
var nVertexElements = this.attributes[ "position" ].array.length;
if ( this.attributes[ "normal" ] === undefined ) {
this.attributes[ "normal" ] = {
itemSize: 3,
array: new Float32Array( nVertexElements ),
numItems: nVertexElements
};
} else {
// reset existing normals to zero
for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
this.attributes[ "normal" ].array[ i ] = 0;
}
}
var positions = this.attributes[ "position" ].array;
var normals = this.attributes[ "normal" ].array;
var vA, vB, vC, x, y, z,
pA = new THREE.Vector3(),
pB = new THREE.Vector3(),
pC = new THREE.Vector3(),
cb = new THREE.Vector3(),
ab = new THREE.Vector3();
// indexed elements
if ( this.attributes[ "index" ] ) {
var indices = this.attributes[ "index" ].array;
var offsets = this.offsets;
for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
var start = offsets[ j ].start;
var count = offsets[ j ].count;
var index = offsets[ j ].index;
for ( i = start, il = start + count; i < il; i += 3 ) {
vA = index + indices[ i ];
vB = index + indices[ i + 1 ];
vC = index + indices[ i + 2 ];
x = positions[ vA * 3 ];
y = positions[ vA * 3 + 1 ];
z = positions[ vA * 3 + 2 ];
pA.set( x, y, z );
x = positions[ vB * 3 ];
y = positions[ vB * 3 + 1 ];
z = positions[ vB * 3 + 2 ];
pB.set( x, y, z );
x = positions[ vC * 3 ];
y = positions[ vC * 3 + 1 ];
z = positions[ vC * 3 + 2 ];
pC.set( x, y, z );
cb.subVectors( pC, pB );
ab.subVectors( pA, pB );
cb.cross( ab );
normals[ vA * 3 ] += cb.x;
normals[ vA * 3 + 1 ] += cb.y;
normals[ vA * 3 + 2 ] += cb.z;
normals[ vB * 3 ] += cb.x;
normals[ vB * 3 + 1 ] += cb.y;
normals[ vB * 3 + 2 ] += cb.z;
normals[ vC * 3 ] += cb.x;
normals[ vC * 3 + 1 ] += cb.y;
normals[ vC * 3 + 2 ] += cb.z;
}
}
// non-indexed elements (unconnected triangle soup)
} else {
for ( i = 0, il = positions.length; i < il; i += 9 ) {
x = positions[ i ];
y = positions[ i + 1 ];
z = positions[ i + 2 ];
pA.set( x, y, z );
x = positions[ i + 3 ];
y = positions[ i + 4 ];
z = positions[ i + 5 ];
pB.set( x, y, z );
x = positions[ i + 6 ];
y = positions[ i + 7 ];
z = positions[ i + 8 ];
pC.set( x, y, z );
cb.subVectors( pC, pB );
ab.subVectors( pA, pB );
cb.cross( ab );
normals[ i ] = cb.x;
normals[ i + 1 ] = cb.y;
normals[ i + 2 ] = cb.z;
normals[ i + 3 ] = cb.x;
normals[ i + 4 ] = cb.y;
normals[ i + 5 ] = cb.z;
normals[ i + 6 ] = cb.x;
normals[ i + 7 ] = cb.y;
normals[ i + 8 ] = cb.z;
}
}
this.normalizeNormals();
this.normalsNeedUpdate = true;
}
},
normalizeNormals: function () {
var normals = this.attributes[ "normal" ].array;
var x, y, z, n;
for ( var i = 0, il = normals.length; i < il; i += 3 ) {
x = normals[ i ];
y = normals[ i + 1 ];
z = normals[ i + 2 ];
n = 1.0 / Math.sqrt( x * x + y * y + z * z );
normals[ i ] *= n;
normals[ i + 1 ] *= n;
normals[ i + 2 ] *= n;
}
},
computeTangents: function () {
// based on http://www.terathon.com/code/tangent.html
// (per vertex tangents)
if ( this.attributes[ "index" ] === undefined ||
this.attributes[ "position" ] === undefined ||
this.attributes[ "normal" ] === undefined ||
this.attributes[ "uv" ] === undefined ) {
console.warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" );
return;
}
var indices = this.attributes[ "index" ].array;
var positions = this.attributes[ "position" ].array;
var normals = this.attributes[ "normal" ].array;
var uvs = this.attributes[ "uv" ].array;
var nVertices = positions.length / 3;
if ( this.attributes[ "tangent" ] === undefined ) {
var nTangentElements = 4 * nVertices;
this.attributes[ "tangent" ] = {
itemSize: 4,
array: new Float32Array( nTangentElements ),
numItems: nTangentElements
};
}
var tangents = this.attributes[ "tangent" ].array;
var tan1 = [], tan2 = [];
for ( var k = 0; k < nVertices; k ++ ) {
tan1[ k ] = new THREE.Vector3();
tan2[ k ] = new THREE.Vector3();
}
var xA, yA, zA,
xB, yB, zB,
xC, yC, zC,
uA, vA,
uB, vB,
uC, vC,
x1, x2, y1, y2, z1, z2,
s1, s2, t1, t2, r;
var sdir = new THREE.Vector3(), tdir = new THREE.Vector3();
function handleTriangle( a, b, c ) {
xA = positions[ a * 3 ];
yA = positions[ a * 3 + 1 ];
zA = positions[ a * 3 + 2 ];
xB = positions[ b * 3 ];
yB = positions[ b * 3 + 1 ];
zB = positions[ b * 3 + 2 ];
xC = positions[ c * 3 ];
yC = positions[ c * 3 + 1 ];
zC = positions[ c * 3 + 2 ];
uA = uvs[ a * 2 ];
vA = uvs[ a * 2 + 1 ];
uB = uvs[ b * 2 ];
vB = uvs[ b * 2 + 1 ];
uC = uvs[ c * 2 ];
vC = uvs[ c * 2 + 1 ];
x1 = xB - xA;
x2 = xC - xA;
y1 = yB - yA;
y2 = yC - yA;
z1 = zB - zA;
z2 = zC - zA;
s1 = uB - uA;
s2 = uC - uA;
t1 = vB - vA;
t2 = vC - vA;
r = 1.0 / ( s1 * t2 - s2 * t1 );
sdir.set(
( t2 * x1 - t1 * x2 ) * r,
( t2 * y1 - t1 * y2 ) * r,
( t2 * z1 - t1 * z2 ) * r
);
tdir.set(
( s1 * x2 - s2 * x1 ) * r,
( s1 * y2 - s2 * y1 ) * r,
( s1 * z2 - s2 * z1 ) * r
);
tan1[ a ].add( sdir );
tan1[ b ].add( sdir );
tan1[ c ].add( sdir );
tan2[ a ].add( tdir );
tan2[ b ].add( tdir );
tan2[ c ].add( tdir );
}
var i, il;
var j, jl;
var iA, iB, iC;
var offsets = this.offsets;
for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
var start = offsets[ j ].start;
var count = offsets[ j ].count;
var index = offsets[ j ].index;
for ( i = start, il = start + count; i < il; i += 3 ) {
iA = index + indices[ i ];
iB = index + indices[ i + 1 ];
iC = index + indices[ i + 2 ];
handleTriangle( iA, iB, iC );
}
}
var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
var n = new THREE.Vector3(), n2 = new THREE.Vector3();
var w, t, test;
function handleVertex( v ) {
n.x = normals[ v * 3 ];
n.y = normals[ v * 3 + 1 ];
n.z = normals[ v * 3 + 2 ];
n2.copy( n );
t = tan1[ v ];
// Gram-Schmidt orthogonalize
tmp.copy( t );
tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
// Calculate handedness
tmp2.crossVectors( n2, t );
test = tmp2.dot( tan2[ v ] );
w = ( test < 0.0 ) ? -1.0 : 1.0;
tangents[ v * 4 ] = tmp.x;
tangents[ v * 4 + 1 ] = tmp.y;
tangents[ v * 4 + 2 ] = tmp.z;
tangents[ v * 4 + 3 ] = w;
}
for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
var start = offsets[ j ].start;
var count = offsets[ j ].count;
var index = offsets[ j ].index;
for ( i = start, il = start + count; i < il; i += 3 ) {
iA = index + indices[ i ];
iB = index + indices[ i + 1 ];
iC = index + indices[ i + 2 ];
handleVertex( iA );
handleVertex( iB );
handleVertex( iC );
}
}
this.hasTangents = true;
this.tangentsNeedUpdate = true;
},
dispose: function () {
this.dispatchEvent( { type: 'dispose' } );
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author mikael emtinger / http://gomo.se/
*/
THREE.Camera = function () {
THREE.Object3D.call( this );
this.matrixWorldInverse = new THREE.Matrix4();
this.projectionMatrix = new THREE.Matrix4();
this.projectionMatrixInverse = new THREE.Matrix4();
};
THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
THREE.Camera.prototype.lookAt = function ( vector ) {
// TODO: Add hierarchy support.
this.matrix.lookAt( this.position, vector, this.up );
if ( this.rotationAutoUpdate === true ) {
if ( this.useQuaternion === false ) {
this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
} else {
this.quaternion.copy( this.matrix.decompose()[ 1 ] );
}
}
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
THREE.Camera.call( this );
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
this.near = ( near !== undefined ) ? near : 0.1;
this.far = ( far !== undefined ) ? far : 2000;
this.updateProjectionMatrix();
};
THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {
this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far );
};
/**
* @author mrdoob / http://mrdoob.com/
* @author greggman / http://games.greggman.com/
* @author zz85 / http://www.lab4games.net/zz85/blog
*/
THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {
THREE.Camera.call( this );
this.fov = fov !== undefined ? fov : 50;
this.aspect = aspect !== undefined ? aspect : 1;
this.near = near !== undefined ? near : 0.1;
this.far = far !== undefined ? far : 2000;
this.updateProjectionMatrix();
};
THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
/**
* Uses Focal Length (in mm) to estimate and set FOV
* 35mm (fullframe) camera is used if frame size is not specified;
* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
*/
THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {
if ( frameHeight === undefined ) frameHeight = 24;
this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
this.updateProjectionMatrix();
}
/**
* Sets an offset in a larger frustum. This is useful for multi-window or
* multi-monitor/multi-machine setups.
*
* For example, if you have 3x2 monitors and each monitor is 1920x1080 and
* the monitors are in grid like this
*
* +---+---+---+
* | A | B | C |
* +---+---+---+
* | D | E | F |
* +---+---+---+
*
* then for each monitor you would call it like this
*
* var w = 1920;
* var h = 1080;
* var fullWidth = w * 3;
* var fullHeight = h * 2;
*
* --A--
* camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
* --B--
* camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
* --C--
* camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
* --D--
* camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
* --E--
* camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
* --F--
* camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
*
* Note there is no reason monitors have to be the same size or in a grid.
*/
THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {
this.fullWidth = fullWidth;
this.fullHeight = fullHeight;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.updateProjectionMatrix();
};
THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {
if ( this.fullWidth ) {
var aspect = this.fullWidth / this.fullHeight;
var top = Math.tan( THREE.Math.degToRad( this.fov * 0.5 ) ) * this.near;
var bottom = -top;
var left = aspect * bottom;
var right = aspect * top;
var width = Math.abs( right - left );
var height = Math.abs( top - bottom );
this.projectionMatrix.makeFrustum(
left + this.x * width / this.fullWidth,
left + ( this.x + this.width ) * width / this.fullWidth,
top - ( this.y + this.height ) * height / this.fullHeight,
top - this.y * height / this.fullHeight,
this.near,
this.far
);
} else {
this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far );
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Light = function ( hex ) {
THREE.Object3D.call( this );
this.color = new THREE.Color( hex );
};
THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.AmbientLight = function ( hex ) {
THREE.Light.call( this, hex );
};
THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author MPanknin / http://www.redplant.de/
* @author alteredq / http://alteredqualia.com/
*/
THREE.AreaLight = function ( hex, intensity ) {
THREE.Light.call( this, hex );
this.normal = new THREE.Vector3( 0, -1, 0 );
this.right = new THREE.Vector3( 1, 0, 0 );
this.intensity = ( intensity !== undefined ) ? intensity : 1;
this.width = 1.0;
this.height = 1.0;
this.constantAttenuation = 1.5;
this.linearAttenuation = 0.5;
this.quadraticAttenuation = 0.1;
};
THREE.AreaLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.DirectionalLight = function ( hex, intensity ) {
THREE.Light.call( this, hex );
this.position = new THREE.Vector3( 0, 1, 0 );
this.target = new THREE.Object3D();
this.intensity = ( intensity !== undefined ) ? intensity : 1;
this.castShadow = false;
this.onlyShadow = false;
//
this.shadowCameraNear = 50;
this.shadowCameraFar = 5000;
this.shadowCameraLeft = -500;
this.shadowCameraRight = 500;
this.shadowCameraTop = 500;
this.shadowCameraBottom = -500;
this.shadowCameraVisible = false;
this.shadowBias = 0;
this.shadowDarkness = 0.5;
this.shadowMapWidth = 512;
this.shadowMapHeight = 512;
//
this.shadowCascade = false;
this.shadowCascadeOffset = new THREE.Vector3( 0, 0, -1000 );
this.shadowCascadeCount = 2;
this.shadowCascadeBias = [ 0, 0, 0 ];
this.shadowCascadeWidth = [ 512, 512, 512 ];
this.shadowCascadeHeight = [ 512, 512, 512 ];
this.shadowCascadeNearZ = [ -1.000, 0.990, 0.998 ];
this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ];
this.shadowCascadeArray = [];
//
this.shadowMap = null;
this.shadowMapSize = null;
this.shadowCamera = null;
this.shadowMatrix = null;
};
THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) {
THREE.Light.call( this, skyColorHex );
this.groundColor = new THREE.Color( groundColorHex );
this.position = new THREE.Vector3( 0, 100, 0 );
this.intensity = ( intensity !== undefined ) ? intensity : 1;
};
THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.PointLight = function ( hex, intensity, distance ) {
THREE.Light.call( this, hex );
this.position = new THREE.Vector3( 0, 0, 0 );
this.intensity = ( intensity !== undefined ) ? intensity : 1;
this.distance = ( distance !== undefined ) ? distance : 0;
};
THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) {
THREE.Light.call( this, hex );
this.position = new THREE.Vector3( 0, 1, 0 );
this.target = new THREE.Object3D();
this.intensity = ( intensity !== undefined ) ? intensity : 1;
this.distance = ( distance !== undefined ) ? distance : 0;
this.angle = ( angle !== undefined ) ? angle : Math.PI / 2;
this.exponent = ( exponent !== undefined ) ? exponent : 10;
this.castShadow = false;
this.onlyShadow = false;
//
this.shadowCameraNear = 50;
this.shadowCameraFar = 5000;
this.shadowCameraFov = 50;
this.shadowCameraVisible = false;
this.shadowBias = 0;
this.shadowDarkness = 0.5;
this.shadowMapWidth = 512;
this.shadowMapHeight = 512;
//
this.shadowMap = null;
this.shadowMapSize = null;
this.shadowCamera = null;
this.shadowMatrix = null;
};
THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.Loader = function ( showStatus ) {
this.showStatus = showStatus;
this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null;
this.onLoadStart = function () {};
this.onLoadProgress = function () {};
this.onLoadComplete = function () {};
};
THREE.Loader.prototype = {
constructor: THREE.Loader,
crossOrigin: 'anonymous',
addStatusElement: function () {
var e = document.createElement( "div" );
e.style.position = "absolute";
e.style.right = "0px";
e.style.top = "0px";
e.style.fontSize = "0.8em";
e.style.textAlign = "left";
e.style.background = "rgba(0,0,0,0.25)";
e.style.color = "#fff";
e.style.width = "120px";
e.style.padding = "0.5em 0.5em 0.5em 0.5em";
e.style.zIndex = 1000;
e.innerHTML = "Loading ...";
return e;
},
updateProgress: function ( progress ) {
var message = "Loaded ";
if ( progress.total ) {
message += ( 100 * progress.loaded / progress.total ).toFixed(0) + "%";
} else {
message += ( progress.loaded / 1000 ).toFixed(2) + " KB";
}
this.statusDomElement.innerHTML = message;
},
extractUrlBase: function ( url ) {
var parts = url.split( '/' );
parts.pop();
return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
},
initMaterials: function ( materials, texturePath ) {
var array = [];
for ( var i = 0; i < materials.length; ++ i ) {
array[ i ] = THREE.Loader.prototype.createMaterial( materials[ i ], texturePath );
}
return array;
},
needsTangents: function ( materials ) {
for( var i = 0, il = materials.length; i < il; i ++ ) {
var m = materials[ i ];
if ( m instanceof THREE.ShaderMaterial ) return true;
}
return false;
},
createMaterial: function ( m, texturePath ) {
var _this = this;
function is_pow2( n ) {
var l = Math.log( n ) / Math.LN2;
return Math.floor( l ) == l;
}
function nearest_pow2( n ) {
var l = Math.log( n ) / Math.LN2;
return Math.pow( 2, Math.round( l ) );
}
function load_image( where, url ) {
var image = new Image();
image.onload = function () {
if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) {
var width = nearest_pow2( this.width );
var height = nearest_pow2( this.height );
where.image.width = width;
where.image.height = height;
where.image.getContext( '2d' ).drawImage( this, 0, 0, width, height );
} else {
where.image = this;
}
where.needsUpdate = true;
};
image.crossOrigin = _this.crossOrigin;
image.src = url;
}
function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
var isCompressed = /\.dds$/i.test( sourceFile );
var fullPath = texturePath + "/" + sourceFile;
if ( isCompressed ) {
var texture = THREE.ImageUtils.loadCompressedTexture( fullPath );
where[ name ] = texture;
} else {
var texture = document.createElement( 'canvas' );
where[ name ] = new THREE.Texture( texture );
}
where[ name ].sourceFile = sourceFile;
if( repeat ) {
where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] );
if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping;
if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping;
}
if ( offset ) {
where[ name ].offset.set( offset[ 0 ], offset[ 1 ] );
}
if ( wrap ) {
var wrapMap = {
"repeat": THREE.RepeatWrapping,
"mirror": THREE.MirroredRepeatWrapping
}
if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ];
if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ];
}
if ( anisotropy ) {
where[ name ].anisotropy = anisotropy;
}
if ( ! isCompressed ) {
load_image( where[ name ], fullPath );
}
}
function rgb2hex( rgb ) {
return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255;
}
// defaults
var mtype = "MeshLambertMaterial";
var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false };
// parameters from model file
if ( m.shading ) {
var shading = m.shading.toLowerCase();
if ( shading === "phong" ) mtype = "MeshPhongMaterial";
else if ( shading === "basic" ) mtype = "MeshBasicMaterial";
}
if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) {
mpars.blending = THREE[ m.blending ];
}
if ( m.transparent !== undefined || m.opacity < 1.0 ) {
mpars.transparent = m.transparent;
}
if ( m.depthTest !== undefined ) {
mpars.depthTest = m.depthTest;
}
if ( m.depthWrite !== undefined ) {
mpars.depthWrite = m.depthWrite;
}
if ( m.visible !== undefined ) {
mpars.visible = m.visible;
}
if ( m.flipSided !== undefined ) {
mpars.side = THREE.BackSide;
}
if ( m.doubleSided !== undefined ) {
mpars.side = THREE.DoubleSide;
}
if ( m.wireframe !== undefined ) {
mpars.wireframe = m.wireframe;
}
if ( m.vertexColors !== undefined ) {
if ( m.vertexColors === "face" ) {
mpars.vertexColors = THREE.FaceColors;
} else if ( m.vertexColors ) {
mpars.vertexColors = THREE.VertexColors;
}
}
// colors
if ( m.colorDiffuse ) {
mpars.color = rgb2hex( m.colorDiffuse );
} else if ( m.DbgColor ) {
mpars.color = m.DbgColor;
}
if ( m.colorSpecular ) {
mpars.specular = rgb2hex( m.colorSpecular );
}
if ( m.colorAmbient ) {
mpars.ambient = rgb2hex( m.colorAmbient );
}
// modifiers
if ( m.transparency ) {
mpars.opacity = m.transparency;
}
if ( m.specularCoef ) {
mpars.shininess = m.specularCoef;
}
// textures
if ( m.mapDiffuse && texturePath ) {
create_texture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
}
if ( m.mapLight && texturePath ) {
create_texture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
}
if ( m.mapBump && texturePath ) {
create_texture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
}
if ( m.mapNormal && texturePath ) {
create_texture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
}
if ( m.mapSpecular && texturePath ) {
create_texture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
}
//
if ( m.mapBumpScale ) {
mpars.bumpScale = m.mapBumpScale;
}
// special case for normal mapped material
if ( m.mapNormal ) {
var shader = THREE.ShaderLib[ "normalmap" ];
var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
uniforms[ "tNormal" ].value = mpars.normalMap;
if ( m.mapNormalFactor ) {
uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor );
}
if ( mpars.map ) {
uniforms[ "tDiffuse" ].value = mpars.map;
uniforms[ "enableDiffuse" ].value = true;
}
if ( mpars.specularMap ) {
uniforms[ "tSpecular" ].value = mpars.specularMap;
uniforms[ "enableSpecular" ].value = true;
}
if ( mpars.lightMap ) {
uniforms[ "tAO" ].value = mpars.lightMap;
uniforms[ "enableAO" ].value = true;
}
// for the moment don't handle displacement texture
uniforms[ "uDiffuseColor" ].value.setHex( mpars.color );
uniforms[ "uSpecularColor" ].value.setHex( mpars.specular );
uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient );
uniforms[ "uShininess" ].value = mpars.shininess;
if ( mpars.opacity !== undefined ) {
uniforms[ "uOpacity" ].value = mpars.opacity;
}
var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
var material = new THREE.ShaderMaterial( parameters );
if ( mpars.transparent ) {
material.transparent = true;
}
} else {
var material = new THREE[ mtype ]( mpars );
}
if ( m.DbgName !== undefined ) material.name = m.DbgName;
return material;
}
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.ImageLoader = function () {
THREE.EventDispatcher.call( this );
this.crossOrigin = null;
};
THREE.ImageLoader.prototype = {
constructor: THREE.ImageLoader,
load: function ( url, image ) {
var scope = this;
if ( image === undefined ) image = new Image();
image.addEventListener( 'load', function () {
scope.dispatchEvent( { type: 'load', content: image } );
}, false );
image.addEventListener( 'error', function () {
scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
}, false );
if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin;
image.src = url;
}
}
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.JSONLoader = function ( showStatus ) {
THREE.Loader.call( this, showStatus );
this.withCredentials = false;
};
THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype );
THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) {
var scope = this;
// todo: unify load API to for easier SceneLoader use
texturePath = texturePath && ( typeof texturePath === "string" ) ? texturePath : this.extractUrlBase( url );
this.onLoadStart();
this.loadAjaxJSON( this, url, callback, texturePath );
};
THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) {
var xhr = new XMLHttpRequest();
var length = 0;
xhr.onreadystatechange = function () {
if ( xhr.readyState === xhr.DONE ) {
if ( xhr.status === 200 || xhr.status === 0 ) {
if ( xhr.responseText ) {
var json = JSON.parse( xhr.responseText );
context.createModel( json, callback, texturePath );
} else {
console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there is empty" );
}
// in context of more complex asset initialization
// do not block on single failed file
// maybe should go even one more level up
context.onLoadComplete();
} else {
console.error( "THREE.JSONLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
}
} else if ( xhr.readyState === xhr.LOADING ) {
if ( callbackProgress ) {
if ( length === 0 ) {
length = xhr.getResponseHeader( "Content-Length" );
}
callbackProgress( { total: length, loaded: xhr.responseText.length } );
}
} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
length = xhr.getResponseHeader( "Content-Length" );
}
};
xhr.open( "GET", url, true );
xhr.withCredentials = this.withCredentials;
xhr.send( null );
};
THREE.JSONLoader.prototype.createModel = function ( json, callback, texturePath ) {
var scope = this,
geometry = new THREE.Geometry(),
scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
parseModel( scale );
parseSkin();
parseMorphing( scale );
geometry.computeCentroids();
geometry.computeFaceNormals();
function parseModel( scale ) {
function isBitSet( value, position ) {
return value & ( 1 << position );
}
var i, j, fi,
offset, zLength, nVertices,
colorIndex, normalIndex, uvIndex, materialIndex,
type,
isQuad,
hasMaterial,
hasFaceUv, hasFaceVertexUv,
hasFaceNormal, hasFaceVertexNormal,
hasFaceColor, hasFaceVertexColor,
vertex, face, color, normal,
uvLayer, uvs, u, v,
faces = json.faces,
vertices = json.vertices,
normals = json.normals,
colors = json.colors,
nUvLayers = 0;
// disregard empty arrays
for ( i = 0; i < json.uvs.length; i++ ) {
if ( json.uvs[ i ].length ) nUvLayers ++;
}
for ( i = 0; i < nUvLayers; i++ ) {
geometry.faceUvs[ i ] = [];
geometry.faceVertexUvs[ i ] = [];
}
offset = 0;
zLength = vertices.length;
while ( offset < zLength ) {
vertex = new THREE.Vector3();
vertex.x = vertices[ offset ++ ] * scale;
vertex.y = vertices[ offset ++ ] * scale;
vertex.z = vertices[ offset ++ ] * scale;
geometry.vertices.push( vertex );
}
offset = 0;
zLength = faces.length;
while ( offset < zLength ) {
type = faces[ offset ++ ];
isQuad = isBitSet( type, 0 );
hasMaterial = isBitSet( type, 1 );
hasFaceUv = isBitSet( type, 2 );
hasFaceVertexUv = isBitSet( type, 3 );
hasFaceNormal = isBitSet( type, 4 );
hasFaceVertexNormal = isBitSet( type, 5 );
hasFaceColor = isBitSet( type, 6 );
hasFaceVertexColor = isBitSet( type, 7 );
//console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
if ( isQuad ) {
face = new THREE.Face4();
face.a = faces[ offset ++ ];
face.b = faces[ offset ++ ];
face.c = faces[ offset ++ ];
face.d = faces[ offset ++ ];
nVertices = 4;
} else {
face = new THREE.Face3();
face.a = faces[ offset ++ ];
face.b = faces[ offset ++ ];
face.c = faces[ offset ++ ];
nVertices = 3;
}
if ( hasMaterial ) {
materialIndex = faces[ offset ++ ];
face.materialIndex = materialIndex;
}
// to get face <=> uv index correspondence
fi = geometry.faces.length;
if ( hasFaceUv ) {
for ( i = 0; i < nUvLayers; i++ ) {
uvLayer = json.uvs[ i ];
uvIndex = faces[ offset ++ ];
u = uvLayer[ uvIndex * 2 ];
v = uvLayer[ uvIndex * 2 + 1 ];
geometry.faceUvs[ i ][ fi ] = new THREE.Vector2( u, v );
}
}
if ( hasFaceVertexUv ) {
for ( i = 0; i < nUvLayers; i++ ) {
uvLayer = json.uvs[ i ];
uvs = [];
for ( j = 0; j < nVertices; j ++ ) {
uvIndex = faces[ offset ++ ];
u = uvLayer[ uvIndex * 2 ];
v = uvLayer[ uvIndex * 2 + 1 ];
uvs[ j ] = new THREE.Vector2( u, v );
}
geometry.faceVertexUvs[ i ][ fi ] = uvs;
}
}
if ( hasFaceNormal ) {
normalIndex = faces[ offset ++ ] * 3;
normal = new THREE.Vector3();
normal.x = normals[ normalIndex ++ ];
normal.y = normals[ normalIndex ++ ];
normal.z = normals[ normalIndex ];
face.normal = normal;
}
if ( hasFaceVertexNormal ) {
for ( i = 0; i < nVertices; i++ ) {
normalIndex = faces[ offset ++ ] * 3;
normal = new THREE.Vector3();
normal.x = normals[ normalIndex ++ ];
normal.y = normals[ normalIndex ++ ];
normal.z = normals[ normalIndex ];
face.vertexNormals.push( normal );
}
}
if ( hasFaceColor ) {
colorIndex = faces[ offset ++ ];
color = new THREE.Color( colors[ colorIndex ] );
face.color = color;
}
if ( hasFaceVertexColor ) {
for ( i = 0; i < nVertices; i++ ) {
colorIndex = faces[ offset ++ ];
color = new THREE.Color( colors[ colorIndex ] );
face.vertexColors.push( color );
}
}
geometry.faces.push( face );
}
};
function parseSkin() {
var i, l, x, y, z, w, a, b, c, d;
if ( json.skinWeights ) {
for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) {
x = json.skinWeights[ i ];
y = json.skinWeights[ i + 1 ];
z = 0;
w = 0;
geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
}
}
if ( json.skinIndices ) {
for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) {
a = json.skinIndices[ i ];
b = json.skinIndices[ i + 1 ];
c = 0;
d = 0;
geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
}
}
geometry.bones = json.bones;
geometry.animation = json.animation;
};
function parseMorphing( scale ) {
if ( json.morphTargets !== undefined ) {
var i, l, v, vl, dstVertices, srcVertices;
for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
geometry.morphTargets[ i ] = {};
geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
geometry.morphTargets[ i ].vertices = [];
dstVertices = geometry.morphTargets[ i ].vertices;
srcVertices = json.morphTargets [ i ].vertices;
for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
var vertex = new THREE.Vector3();
vertex.x = srcVertices[ v ] * scale;
vertex.y = srcVertices[ v + 1 ] * scale;
vertex.z = srcVertices[ v + 2 ] * scale;
dstVertices.push( vertex );
}
}
}
if ( json.morphColors !== undefined ) {
var i, l, c, cl, dstColors, srcColors, color;
for ( i = 0, l = json.morphColors.length; i < l; i++ ) {
geometry.morphColors[ i ] = {};
geometry.morphColors[ i ].name = json.morphColors[ i ].name;
geometry.morphColors[ i ].colors = [];
dstColors = geometry.morphColors[ i ].colors;
srcColors = json.morphColors [ i ].colors;
for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) {
color = new THREE.Color( 0xffaa00 );
color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );
dstColors.push( color );
}
}
}
};
var materials = this.initMaterials( json.materials, texturePath );
if ( this.needsTangents( materials ) ) geometry.computeTangents();
callback( geometry, materials );
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.LoadingMonitor = function () {
THREE.EventDispatcher.call( this );
var scope = this;
var loaded = 0;
var total = 0;
var onLoad = function ( event ) {
loaded ++;
scope.dispatchEvent( { type: 'progress', loaded: loaded, total: total } );
if ( loaded === total ) {
scope.dispatchEvent( { type: 'load' } );
}
};
this.add = function ( loader ) {
total ++;
loader.addEventListener( 'load', onLoad, false );
};
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.SceneLoader = function () {
this.onLoadStart = function () {};
this.onLoadProgress = function() {};
this.onLoadComplete = function () {};
this.callbackSync = function () {};
this.callbackProgress = function () {};
this.geometryHandlerMap = {};
this.hierarchyHandlerMap = {};
this.addGeometryHandler( "ascii", THREE.JSONLoader );
};
THREE.SceneLoader.prototype.constructor = THREE.SceneLoader;
THREE.SceneLoader.prototype.load = function ( url, callbackFinished ) {
var scope = this;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if ( xhr.readyState === 4 ) {
if ( xhr.status === 200 || xhr.status === 0 ) {
var json = JSON.parse( xhr.responseText );
scope.parse( json, callbackFinished, url );
} else {
console.error( "THREE.SceneLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
}
}
};
xhr.open( "GET", url, true );
xhr.send( null );
};
THREE.SceneLoader.prototype.addGeometryHandler = function ( typeID, loaderClass ) {
this.geometryHandlerMap[ typeID ] = { "loaderClass": loaderClass };
};
THREE.SceneLoader.prototype.addHierarchyHandler = function ( typeID, loaderClass ) {
this.hierarchyHandlerMap[ typeID ] = { "loaderClass": loaderClass };
};
THREE.SceneLoader.prototype.parse = function ( json, callbackFinished, url ) {
var scope = this;
var urlBase = THREE.Loader.prototype.extractUrlBase( url );
var geometry, material, camera, fog,
texture, images, color,
light, hex, intensity,
counter_models, counter_textures,
total_models, total_textures,
result;
var target_array = [];
var data = json;
// async geometry loaders
for ( var typeID in this.geometryHandlerMap ) {
var loaderClass = this.geometryHandlerMap[ typeID ][ "loaderClass" ];
this.geometryHandlerMap[ typeID ][ "loaderObject" ] = new loaderClass();
}
// async hierachy loaders
for ( var typeID in this.hierarchyHandlerMap ) {
var loaderClass = this.hierarchyHandlerMap[ typeID ][ "loaderClass" ];
this.hierarchyHandlerMap[ typeID ][ "loaderObject" ] = new loaderClass();
}
counter_models = 0;
counter_textures = 0;
result = {
scene: new THREE.Scene(),
geometries: {},
face_materials: {},
materials: {},
textures: {},
objects: {},
cameras: {},
lights: {},
fogs: {},
empties: {},
groups: {}
};
if ( data.transform ) {
var position = data.transform.position,
rotation = data.transform.rotation,
scale = data.transform.scale;
if ( position )
result.scene.position.set( position[ 0 ], position[ 1 ], position [ 2 ] );
if ( rotation )
result.scene.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation [ 2 ] );
if ( scale )
result.scene.scale.set( scale[ 0 ], scale[ 1 ], scale [ 2 ] );
if ( position || rotation || scale ) {
result.scene.updateMatrix();
result.scene.updateMatrixWorld();
}
}
function get_url( source_url, url_type ) {
if ( url_type == "relativeToHTML" ) {
return source_url;
} else {
return urlBase + "/" + source_url;
}
};
// toplevel loader function, delegates to handle_children
function handle_objects() {
handle_children( result.scene, data.objects );
}
// handle all the children from the loaded json and attach them to given parent
function handle_children( parent, children ) {
var mat, dst, pos, rot, scl, quat;
for ( var objID in children ) {
// check by id if child has already been handled,
// if not, create new object
if ( result.objects[ objID ] === undefined ) {
var objJSON = children[ objID ];
var object = null;
// meshes
if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlerMap ) ) {
if ( objJSON.loading === undefined ) {
var reservedTypes = { "type": 1, "url": 1, "material": 1,
"position": 1, "rotation": 1, "scale" : 1,
"visible": 1, "children": 1, "properties": 1,
"skin": 1, "morph": 1, "mirroredLoop": 1, "duration": 1 };
var loaderParameters = {};
for ( var parType in objJSON ) {
if ( ! ( parType in reservedTypes ) ) {
loaderParameters[ parType ] = objJSON[ parType ];
}
}
material = result.materials[ objJSON.material ];
objJSON.loading = true;
var loader = scope.hierarchyHandlerMap[ objJSON.type ][ "loaderObject" ];
// ColladaLoader
if ( loader.options ) {
loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
// UTF8Loader
// OBJLoader
} else {
loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ), loaderParameters );
}
}
} else if ( objJSON.geometry !== undefined ) {
geometry = result.geometries[ objJSON.geometry ];
// geometry already loaded
if ( geometry ) {
var needsTangents = false;
material = result.materials[ objJSON.material ];
needsTangents = material instanceof THREE.ShaderMaterial;
pos = objJSON.position;
rot = objJSON.rotation;
scl = objJSON.scale;
mat = objJSON.matrix;
quat = objJSON.quaternion;
// use materials from the model file
// if there is no material specified in the object
if ( ! objJSON.material ) {
material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
}
// use materials from the model file
// if there is just empty face material
// (must create new material as each model has its own face material)
if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) {
material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
}
if ( material instanceof THREE.MeshFaceMaterial ) {
for ( var i = 0; i < material.materials.length; i ++ ) {
needsTangents = needsTangents || ( material.materials[ i ] instanceof THREE.ShaderMaterial );
}
}
if ( needsTangents ) {
geometry.computeTangents();
}
if ( objJSON.skin ) {
object = new THREE.SkinnedMesh( geometry, material );
} else if ( objJSON.morph ) {
object = new THREE.MorphAnimMesh( geometry, material );
if ( objJSON.duration !== undefined ) {
object.duration = objJSON.duration;
}
if ( objJSON.time !== undefined ) {
object.time = objJSON.time;
}
if ( objJSON.mirroredLoop !== undefined ) {
object.mirroredLoop = objJSON.mirroredLoop;
}
if ( material.morphNormals ) {
geometry.computeMorphNormals();
}
} else {
object = new THREE.Mesh( geometry, material );
}
object.name = objID;
if ( mat ) {
object.matrixAutoUpdate = false;
object.matrix.set(
mat[0], mat[1], mat[2], mat[3],
mat[4], mat[5], mat[6], mat[7],
mat[8], mat[9], mat[10], mat[11],
mat[12], mat[13], mat[14], mat[15]
);
} else {
object.position.set( pos[0], pos[1], pos[2] );
if ( quat ) {
object.quaternion.set( quat[0], quat[1], quat[2], quat[3] );
object.useQuaternion = true;
} else {
object.rotation.set( rot[0], rot[1], rot[2] );
}
object.scale.set( scl[0], scl[1], scl[2] );
}
object.visible = objJSON.visible;
object.castShadow = objJSON.castShadow;
object.receiveShadow = objJSON.receiveShadow;
parent.add( object );
result.objects[ objID ] = object;
}
// lights
} else if ( objJSON.type === "DirectionalLight" || objJSON.type === "PointLight" || objJSON.type === "AmbientLight" ) {
hex = ( objJSON.color !== undefined ) ? objJSON.color : 0xffffff;
intensity = ( objJSON.intensity !== undefined ) ? objJSON.intensity : 1;
if ( objJSON.type === "DirectionalLight" ) {
pos = objJSON.direction;
light = new THREE.DirectionalLight( hex, intensity );
light.position.set( pos[0], pos[1], pos[2] );
if ( objJSON.target ) {
target_array.push( { "object": light, "targetName" : objJSON.target } );
// kill existing default target
// otherwise it gets added to scene when parent gets added
light.target = null;
}
} else if ( objJSON.type === "PointLight" ) {
pos = objJSON.position;
dst = objJSON.distance;
light = new THREE.PointLight( hex, intensity, dst );
light.position.set( pos[0], pos[1], pos[2] );
} else if ( objJSON.type === "AmbientLight" ) {
light = new THREE.AmbientLight( hex );
}
parent.add( light );
light.name = objID;
result.lights[ objID ] = light;
result.objects[ objID ] = light;
// cameras
} else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) {
if ( objJSON.type === "PerspectiveCamera" ) {
camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far );
} else if ( objJSON.type === "OrthographicCamera" ) {
camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far );
}
pos = objJSON.position;
camera.position.set( pos[0], pos[1], pos[2] );
parent.add( camera );
camera.name = objID;
result.cameras[ objID ] = camera;
result.objects[ objID ] = camera;
// pure Object3D
} else {
pos = objJSON.position;
rot = objJSON.rotation;
scl = objJSON.scale;
quat = objJSON.quaternion;
object = new THREE.Object3D();
object.name = objID;
object.position.set( pos[0], pos[1], pos[2] );
if ( quat ) {
object.quaternion.set( quat[0], quat[1], quat[2], quat[3] );
object.useQuaternion = true;
} else {
object.rotation.set( rot[0], rot[1], rot[2] );
}
object.scale.set( scl[0], scl[1], scl[2] );
object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
parent.add( object );
result.objects[ objID ] = object;
result.empties[ objID ] = object;
}
if ( object ) {
if ( objJSON.properties !== undefined ) {
for ( var key in objJSON.properties ) {
var value = objJSON.properties[ key ];
object.properties[ key ] = value;
}
}
if ( objJSON.groups !== undefined ) {
for ( var i = 0; i < objJSON.groups.length; i ++ ) {
var groupID = objJSON.groups[ i ];
if ( result.groups[ groupID ] === undefined ) {
result.groups[ groupID ] = [];
}
result.groups[ groupID ].push( objID );
}
}
if ( objJSON.children !== undefined ) {
handle_children( object, objJSON.children );
}
}
}
}
};
function handle_mesh( geo, mat, id ) {
result.geometries[ id ] = geo;
result.face_materials[ id ] = mat;
handle_objects();
};
function handle_hierarchy( node, id, parent, material, obj ) {
var p = obj.position;
var r = obj.rotation;
var q = obj.quaternion;
var s = obj.scale;
node.position.set( p[0], p[1], p[2] );
if ( q ) {
node.quaternion.set( q[0], q[1], q[2], q[3] );
node.useQuaternion = true;
} else {
node.rotation.set( r[0], r[1], r[2] );
}
node.scale.set( s[0], s[1], s[2] );
// override children materials
// if object material was specified in JSON explicitly
if ( material ) {
node.traverse( function ( child ) {
child.material = material;
} );
}
// override children visibility
// with root node visibility as specified in JSON
var visible = ( obj.visible !== undefined ) ? obj.visible : true;
node.traverse( function ( child ) {
child.visible = visible;
} );
parent.add( node );
node.name = id;
result.objects[ id ] = node;
handle_objects();
};
function create_callback_geometry( id ) {
return function( geo, mat ) {
handle_mesh( geo, mat, id );
counter_models -= 1;
scope.onLoadComplete();
async_callback_gate();
}
};
function create_callback_hierachy( id, parent, material, obj ) {
return function( event ) {
var result;
// loaders which use EventDispatcher
if ( event.content ) {
result = event.content;
// ColladaLoader
} else if ( event.dae ) {
result = event.scene;
// UTF8Loader
} else {
result = event;
}
handle_hierarchy( result, id, parent, material, obj );
counter_models -= 1;
scope.onLoadComplete();
async_callback_gate();
}
};
function create_callback_embed( id ) {
return function( geo, mat ) {
result.geometries[ id ] = geo;
result.face_materials[ id ] = mat;
}
};
function async_callback_gate() {
var progress = {
totalModels : total_models,
totalTextures : total_textures,
loadedModels : total_models - counter_models,
loadedTextures : total_textures - counter_textures
};
scope.callbackProgress( progress, result );
scope.onLoadProgress();
if ( counter_models === 0 && counter_textures === 0 ) {
finalize();
callbackFinished( result );
}
};
function finalize() {
// take care of targets which could be asynchronously loaded objects
for ( var i = 0; i < target_array.length; i ++ ) {
var ta = target_array[ i ];
var target = result.objects[ ta.targetName ];
if ( target ) {
ta.object.target = target;
} else {
// if there was error and target of specified name doesn't exist in the scene file
// create instead dummy target
// (target must be added to scene explicitly as parent is already added)
ta.object.target = new THREE.Object3D();
result.scene.add( ta.object.target );
}
ta.object.target.properties.targetInverse = ta.object;
}
};
var callbackTexture = function ( count ) {
counter_textures -= count;
async_callback_gate();
scope.onLoadComplete();
};
// must use this instead of just directly calling callbackTexture
// because of closure in the calling context loop
var generateTextureCallback = function ( count ) {
return function() {
callbackTexture( count );
};
};
// first go synchronous elements
// fogs
var fogID, fogJSON;
for ( fogID in data.fogs ) {
fogJSON = data.fogs[ fogID ];
if ( fogJSON.type === "linear" ) {
fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far );
} else if ( fogJSON.type === "exp2" ) {
fog = new THREE.FogExp2( 0x000000, fogJSON.density );
}
color = fogJSON.color;
fog.color.setRGB( color[0], color[1], color[2] );
result.fogs[ fogID ] = fog;
}
// now come potentially asynchronous elements
// geometries
// count how many geometries will be loaded asynchronously
var geoID, geoJSON;
for ( geoID in data.geometries ) {
geoJSON = data.geometries[ geoID ];
if ( geoJSON.type in this.geometryHandlerMap ) {
counter_models += 1;
scope.onLoadStart();
}
}
// count how many hierarchies will be loaded asynchronously
var objID, objJSON;
for ( objID in data.objects ) {
objJSON = data.objects[ objID ];
if ( objJSON.type && ( objJSON.type in this.hierarchyHandlerMap ) ) {
counter_models += 1;
scope.onLoadStart();
}
}
total_models = counter_models;
for ( geoID in data.geometries ) {
geoJSON = data.geometries[ geoID ];
if ( geoJSON.type === "cube" ) {
geometry = new THREE.CubeGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type === "plane" ) {
geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type === "sphere" ) {
geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type === "cylinder" ) {
geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type === "torus" ) {
geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type === "icosahedron" ) {
geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
result.geometries[ geoID ] = geometry;
} else if ( geoJSON.type in this.geometryHandlerMap ) {
var loaderParameters = {};
for ( var parType in geoJSON ) {
if ( parType !== "type" && parType !== "url" ) {
loaderParameters[ parType ] = geoJSON[ parType ];
}
}
var loader = this.geometryHandlerMap[ geoJSON.type ][ "loaderObject" ];
loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ), loaderParameters );
} else if ( geoJSON.type === "embedded" ) {
var modelJson = data.embeds[ geoJSON.id ],
texture_path = "";
// pass metadata along to jsonLoader so it knows the format version
modelJson.metadata = data.metadata;
if ( modelJson ) {
var jsonLoader = this.geometryHandlerMap[ "ascii" ][ "loaderObject" ];
jsonLoader.createModel( modelJson, create_callback_embed( geoID ), texture_path );
}
}
}
// textures
// count how many textures will be loaded asynchronously
var textureID, textureJSON;
for ( textureID in data.textures ) {
textureJSON = data.textures[ textureID ];
if ( textureJSON.url instanceof Array ) {
counter_textures += textureJSON.url.length;
for( var n = 0; n < textureJSON.url.length; n ++ ) {
scope.onLoadStart();
}
} else {
counter_textures += 1;
scope.onLoadStart();
}
}
total_textures = counter_textures;
for ( textureID in data.textures ) {
textureJSON = data.textures[ textureID ];
if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) {
textureJSON.mapping = new THREE[ textureJSON.mapping ]();
}
if ( textureJSON.url instanceof Array ) {
var count = textureJSON.url.length;
var url_array = [];
for( var i = 0; i < count; i ++ ) {
url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType );
}
var isCompressed = /\.dds$/i.test( url_array[ 0 ] );
if ( isCompressed ) {
texture = THREE.ImageUtils.loadCompressedTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
} else {
texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
}
} else {
var isCompressed = /\.dds$/i.test( textureJSON.url );
var fullUrl = get_url( textureJSON.url, data.urlBaseType );
var textureCallback = generateTextureCallback( 1 );
if ( isCompressed ) {
texture = THREE.ImageUtils.loadCompressedTexture( fullUrl, textureJSON.mapping, textureCallback );
} else {
texture = THREE.ImageUtils.loadTexture( fullUrl, textureJSON.mapping, textureCallback );
}
if ( THREE[ textureJSON.minFilter ] !== undefined )
texture.minFilter = THREE[ textureJSON.minFilter ];
if ( THREE[ textureJSON.magFilter ] !== undefined )
texture.magFilter = THREE[ textureJSON.magFilter ];
if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy;
if ( textureJSON.repeat ) {
texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] );
if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
}
if ( textureJSON.offset ) {
texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] );
}
// handle wrap after repeat so that default repeat can be overriden
if ( textureJSON.wrap ) {
var wrapMap = {
"repeat" : THREE.RepeatWrapping,
"mirror" : THREE.MirroredRepeatWrapping
}
if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ];
if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ];
}
}
result.textures[ textureID ] = texture;
}
// materials
var matID, matJSON;
var parID;
for ( matID in data.materials ) {
matJSON = data.materials[ matID ];
for ( parID in matJSON.parameters ) {
if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" ) {
matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ];
} else if ( parID === "shading" ) {
matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading;
} else if ( parID === "side" ) {
if ( matJSON.parameters[ parID ] == "double" ) {
matJSON.parameters[ parID ] = THREE.DoubleSide;
} else if ( matJSON.parameters[ parID ] == "back" ) {
matJSON.parameters[ parID ] = THREE.BackSide;
} else {
matJSON.parameters[ parID ] = THREE.FrontSide;
}
} else if ( parID === "blending" ) {
matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending;
} else if ( parID === "combine" ) {
matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation;
} else if ( parID === "vertexColors" ) {
if ( matJSON.parameters[ parID ] == "face" ) {
matJSON.parameters[ parID ] = THREE.FaceColors;
// default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false
} else if ( matJSON.parameters[ parID ] ) {
matJSON.parameters[ parID ] = THREE.VertexColors;
}
} else if ( parID === "wrapRGB" ) {
var v3 = matJSON.parameters[ parID ];
matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] );
}
}
if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) {
matJSON.parameters.transparent = true;
}
if ( matJSON.parameters.normalMap ) {
var shader = THREE.ShaderLib[ "normalmap" ];
var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
var diffuse = matJSON.parameters.color;
var specular = matJSON.parameters.specular;
var ambient = matJSON.parameters.ambient;
var shininess = matJSON.parameters.shininess;
uniforms[ "tNormal" ].value = result.textures[ matJSON.parameters.normalMap ];
if ( matJSON.parameters.normalScale ) {
uniforms[ "uNormalScale" ].value.set( matJSON.parameters.normalScale[ 0 ], matJSON.parameters.normalScale[ 1 ] );
}
if ( matJSON.parameters.map ) {
uniforms[ "tDiffuse" ].value = matJSON.parameters.map;
uniforms[ "enableDiffuse" ].value = true;
}
if ( matJSON.parameters.envMap ) {
uniforms[ "tCube" ].value = matJSON.parameters.envMap;
uniforms[ "enableReflection" ].value = true;
uniforms[ "uReflectivity" ].value = matJSON.parameters.reflectivity;
}
if ( matJSON.parameters.lightMap ) {
uniforms[ "tAO" ].value = matJSON.parameters.lightMap;
uniforms[ "enableAO" ].value = true;
}
if ( matJSON.parameters.specularMap ) {
uniforms[ "tSpecular" ].value = result.textures[ matJSON.parameters.specularMap ];
uniforms[ "enableSpecular" ].value = true;
}
if ( matJSON.parameters.displacementMap ) {
uniforms[ "tDisplacement" ].value = result.textures[ matJSON.parameters.displacementMap ];
uniforms[ "enableDisplacement" ].value = true;
uniforms[ "uDisplacementBias" ].value = matJSON.parameters.displacementBias;
uniforms[ "uDisplacementScale" ].value = matJSON.parameters.displacementScale;
}
uniforms[ "uDiffuseColor" ].value.setHex( diffuse );
uniforms[ "uSpecularColor" ].value.setHex( specular );
uniforms[ "uAmbientColor" ].value.setHex( ambient );
uniforms[ "uShininess" ].value = shininess;
if ( matJSON.parameters.opacity ) {
uniforms[ "uOpacity" ].value = matJSON.parameters.opacity;
}
var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
material = new THREE.ShaderMaterial( parameters );
} else {
material = new THREE[ matJSON.type ]( matJSON.parameters );
}
result.materials[ matID ] = material;
}
// second pass through all materials to initialize MeshFaceMaterials
// that could be referring to other materials out of order
for ( matID in data.materials ) {
matJSON = data.materials[ matID ];
if ( matJSON.parameters.materials ) {
var materialArray = [];
for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) {
var label = matJSON.parameters.materials[ i ];
materialArray.push( result.materials[ label ] );
}
result.materials[ matID ].materials = materialArray;
}
}
// objects ( synchronous init of procedural primitives )
handle_objects();
// defaults
if ( result.cameras && data.defaults.camera ) {
result.currentCamera = result.cameras[ data.defaults.camera ];
}
if ( result.fogs && data.defaults.fog ) {
result.scene.fog = result.fogs[ data.defaults.fog ];
}
// synchronous callback
scope.callbackSync( result );
// just in case there are no async elements
async_callback_gate();
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.TextureLoader = function () {
THREE.EventDispatcher.call( this );
this.crossOrigin = null;
};
THREE.TextureLoader.prototype = {
constructor: THREE.TextureLoader,
load: function ( url ) {
var scope = this;
var image = new Image();
image.addEventListener( 'load', function () {
var texture = new THREE.Texture( image );
texture.needsUpdate = true;
scope.dispatchEvent( { type: 'load', content: texture } );
}, false );
image.addEventListener( 'error', function () {
scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
}, false );
if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin;
image.src = url;
}
}
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Material = function () {
THREE.EventDispatcher.call( this );
this.id = THREE.MaterialIdCount ++;
this.name = '';
this.side = THREE.FrontSide;
this.opacity = 1;
this.transparent = false;
this.blending = THREE.NormalBlending;
this.blendSrc = THREE.SrcAlphaFactor;
this.blendDst = THREE.OneMinusSrcAlphaFactor;
this.blendEquation = THREE.AddEquation;
this.depthTest = true;
this.depthWrite = true;
this.polygonOffset = false;
this.polygonOffsetFactor = 0;
this.polygonOffsetUnits = 0;
this.alphaTest = 0;
this.overdraw = false; // Boolean for fixing antialiasing gaps in CanvasRenderer
this.visible = true;
this.needsUpdate = true;
};
THREE.Material.prototype.setValues = function ( values ) {
if ( values === undefined ) return;
for ( var key in values ) {
var newValue = values[ key ];
if ( newValue === undefined ) {
console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' );
continue;
}
if ( key in this ) {
var currentValue = this[ key ];
if ( currentValue instanceof THREE.Color && newValue instanceof THREE.Color ) {
currentValue.copy( newValue );
} else if ( currentValue instanceof THREE.Color ) {
currentValue.set( newValue );
} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {
currentValue.copy( newValue );
} else {
this[ key ] = newValue;
}
}
}
};
THREE.Material.prototype.clone = function ( material ) {
if ( material === undefined ) material = new THREE.Material();
material.name = this.name;
material.side = this.side;
material.opacity = this.opacity;
material.transparent = this.transparent;
material.blending = this.blending;
material.blendSrc = this.blendSrc;
material.blendDst = this.blendDst;
material.blendEquation = this.blendEquation;
material.depthTest = this.depthTest;
material.depthWrite = this.depthWrite;
material.polygonOffset = this.polygonOffset;
material.polygonOffsetFactor = this.polygonOffsetFactor;
material.polygonOffsetUnits = this.polygonOffsetUnits;
material.alphaTest = this.alphaTest;
material.overdraw = this.overdraw;
material.visible = this.visible;
return material;
};
THREE.Material.prototype.dispose = function () {
this.dispatchEvent( { type: 'dispose' } );
};
THREE.MaterialIdCount = 0;
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* opacity: <float>,
*
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* linewidth: <float>,
* linecap: "round",
* linejoin: "round",
*
* vertexColors: <bool>
*
* fog: <bool>
* }
*/
THREE.LineBasicMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff );
this.linewidth = 1;
this.linecap = 'round';
this.linejoin = 'round';
this.vertexColors = false;
this.fog = true;
this.setValues( parameters );
};
THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineBasicMaterial.prototype.clone = function () {
var material = new THREE.LineBasicMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.linewidth = this.linewidth;
material.linecap = this.linecap;
material.linejoin = this.linejoin;
material.vertexColors = this.vertexColors;
material.fog = this.fog;
return material;
};
/**
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* opacity: <float>,
*
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* linewidth: <float>,
*
* scale: <float>,
* dashSize: <float>,
* gapSize: <float>,
*
* vertexColors: <bool>
*
* fog: <bool>
* }
*/
THREE.LineDashedMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff );
this.linewidth = 1;
this.scale = 1;
this.dashSize = 3;
this.gapSize = 1;
this.vertexColors = false;
this.fog = true;
this.setValues( parameters );
};
THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineDashedMaterial.prototype.clone = function () {
var material = new THREE.LineDashedMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.linewidth = this.linewidth;
material.scale = this.scale;
material.dashSize = this.dashSize;
material.gapSize = this.gapSize;
material.vertexColors = this.vertexColors;
material.fog = this.fog;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* opacity: <float>,
* map: new THREE.Texture( <Image> ),
*
* lightMap: new THREE.Texture( <Image> ),
*
* specularMap: new THREE.Texture( <Image> ),
*
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
* combine: THREE.Multiply,
* reflectivity: <float>,
* refractionRatio: <float>,
*
* shading: THREE.SmoothShading,
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>,
*
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
*
* skinning: <bool>,
* morphTargets: <bool>,
*
* fog: <bool>
* }
*/
THREE.MeshBasicMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff ); // emissive
this.map = null;
this.lightMap = null;
this.specularMap = null;
this.envMap = null;
this.combine = THREE.MultiplyOperation;
this.reflectivity = 1;
this.refractionRatio = 0.98;
this.fog = true;
this.shading = THREE.SmoothShading;
this.wireframe = false;
this.wireframeLinewidth = 1;
this.wireframeLinecap = 'round';
this.wireframeLinejoin = 'round';
this.vertexColors = THREE.NoColors;
this.skinning = false;
this.morphTargets = false;
this.setValues( parameters );
};
THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshBasicMaterial.prototype.clone = function () {
var material = new THREE.MeshBasicMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.map = this.map;
material.lightMap = this.lightMap;
material.specularMap = this.specularMap;
material.envMap = this.envMap;
material.combine = this.combine;
material.reflectivity = this.reflectivity;
material.refractionRatio = this.refractionRatio;
material.fog = this.fog;
material.shading = this.shading;
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
material.wireframeLinecap = this.wireframeLinecap;
material.wireframeLinejoin = this.wireframeLinejoin;
material.vertexColors = this.vertexColors;
material.skinning = this.skinning;
material.morphTargets = this.morphTargets;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* ambient: <hex>,
* emissive: <hex>,
* opacity: <float>,
*
* map: new THREE.Texture( <Image> ),
*
* lightMap: new THREE.Texture( <Image> ),
*
* specularMap: new THREE.Texture( <Image> ),
*
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
* combine: THREE.Multiply,
* reflectivity: <float>,
* refractionRatio: <float>,
*
* shading: THREE.SmoothShading,
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>,
*
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
*
* skinning: <bool>,
* morphTargets: <bool>,
* morphNormals: <bool>,
*
* fog: <bool>
* }
*/
THREE.MeshLambertMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff ); // diffuse
this.ambient = new THREE.Color( 0xffffff );
this.emissive = new THREE.Color( 0x000000 );
this.wrapAround = false;
this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
this.map = null;
this.lightMap = null;
this.specularMap = null;
this.envMap = null;
this.combine = THREE.MultiplyOperation;
this.reflectivity = 1;
this.refractionRatio = 0.98;
this.fog = true;
this.shading = THREE.SmoothShading;
this.wireframe = false;
this.wireframeLinewidth = 1;
this.wireframeLinecap = 'round';
this.wireframeLinejoin = 'round';
this.vertexColors = THREE.NoColors;
this.skinning = false;
this.morphTargets = false;
this.morphNormals = false;
this.setValues( parameters );
};
THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshLambertMaterial.prototype.clone = function () {
var material = new THREE.MeshLambertMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.ambient.copy( this.ambient );
material.emissive.copy( this.emissive );
material.wrapAround = this.wrapAround;
material.wrapRGB.copy( this.wrapRGB );
material.map = this.map;
material.lightMap = this.lightMap;
material.specularMap = this.specularMap;
material.envMap = this.envMap;
material.combine = this.combine;
material.reflectivity = this.reflectivity;
material.refractionRatio = this.refractionRatio;
material.fog = this.fog;
material.shading = this.shading;
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
material.wireframeLinecap = this.wireframeLinecap;
material.wireframeLinejoin = this.wireframeLinejoin;
material.vertexColors = this.vertexColors;
material.skinning = this.skinning;
material.morphTargets = this.morphTargets;
material.morphNormals = this.morphNormals;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* ambient: <hex>,
* emissive: <hex>,
* specular: <hex>,
* shininess: <float>,
* opacity: <float>,
*
* map: new THREE.Texture( <Image> ),
*
* lightMap: new THREE.Texture( <Image> ),
*
* bumpMap: new THREE.Texture( <Image> ),
* bumpScale: <float>,
*
* normalMap: new THREE.Texture( <Image> ),
* normalScale: <Vector2>,
*
* specularMap: new THREE.Texture( <Image> ),
*
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
* combine: THREE.Multiply,
* reflectivity: <float>,
* refractionRatio: <float>,
*
* shading: THREE.SmoothShading,
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>,
*
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
*
* skinning: <bool>,
* morphTargets: <bool>,
* morphNormals: <bool>,
*
* fog: <bool>
* }
*/
THREE.MeshPhongMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff ); // diffuse
this.ambient = new THREE.Color( 0xffffff );
this.emissive = new THREE.Color( 0x000000 );
this.specular = new THREE.Color( 0x111111 );
this.shininess = 30;
this.metal = false;
this.perPixel = true;
this.wrapAround = false;
this.wrapRGB = new THREE.Vector3( 1, 1, 1 );
this.map = null;
this.lightMap = null;
this.bumpMap = null;
this.bumpScale = 1;
this.normalMap = null;
this.normalScale = new THREE.Vector2( 1, 1 );
this.specularMap = null;
this.envMap = null;
this.combine = THREE.MultiplyOperation;
this.reflectivity = 1;
this.refractionRatio = 0.98;
this.fog = true;
this.shading = THREE.SmoothShading;
this.wireframe = false;
this.wireframeLinewidth = 1;
this.wireframeLinecap = 'round';
this.wireframeLinejoin = 'round';
this.vertexColors = THREE.NoColors;
this.skinning = false;
this.morphTargets = false;
this.morphNormals = false;
this.setValues( parameters );
};
THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshPhongMaterial.prototype.clone = function () {
var material = new THREE.MeshPhongMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.ambient.copy( this.ambient );
material.emissive.copy( this.emissive );
material.specular.copy( this.specular );
material.shininess = this.shininess;
material.metal = this.metal;
material.perPixel = this.perPixel;
material.wrapAround = this.wrapAround;
material.wrapRGB.copy( this.wrapRGB );
material.map = this.map;
material.lightMap = this.lightMap;
material.bumpMap = this.bumpMap;
material.bumpScale = this.bumpScale;
material.normalMap = this.normalMap;
material.normalScale.copy( this.normalScale );
material.specularMap = this.specularMap;
material.envMap = this.envMap;
material.combine = this.combine;
material.reflectivity = this.reflectivity;
material.refractionRatio = this.refractionRatio;
material.fog = this.fog;
material.shading = this.shading;
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
material.wireframeLinecap = this.wireframeLinecap;
material.wireframeLinejoin = this.wireframeLinejoin;
material.vertexColors = this.vertexColors;
material.skinning = this.skinning;
material.morphTargets = this.morphTargets;
material.morphNormals = this.morphNormals;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* opacity: <float>,
*
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>
* }
*/
THREE.MeshDepthMaterial = function ( parameters ) {
THREE.Material.call( this );
this.wireframe = false;
this.wireframeLinewidth = 1;
this.setValues( parameters );
};
THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshDepthMaterial.prototype.clone = function () {
var material = new THREE.LineBasicMaterial();
THREE.Material.prototype.clone.call( this, material );
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
*
* parameters = {
* opacity: <float>,
*
* shading: THREE.FlatShading,
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>
* }
*/
THREE.MeshNormalMaterial = function ( parameters ) {
THREE.Material.call( this, parameters );
this.shading = THREE.FlatShading;
this.wireframe = false;
this.wireframeLinewidth = 1;
this.setValues( parameters );
};
THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshNormalMaterial.prototype.clone = function () {
var material = new THREE.MeshNormalMaterial();
THREE.Material.prototype.clone.call( this, material );
material.shading = this.shading;
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.MeshFaceMaterial = function ( materials ) {
this.materials = materials instanceof Array ? materials : [];
};
THREE.MeshFaceMaterial.prototype.clone = function () {
return new THREE.MeshFaceMaterial( this.materials.slice( 0 ) );
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* opacity: <float>,
* map: new THREE.Texture( <Image> ),
*
* size: <float>,
*
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* vertexColors: <bool>,
*
* fog: <bool>
* }
*/
THREE.ParticleBasicMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff );
this.map = null;
this.size = 1;
this.sizeAttenuation = true;
this.vertexColors = false;
this.fog = true;
this.setValues( parameters );
};
THREE.ParticleBasicMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.ParticleBasicMaterial.prototype.clone = function () {
var material = new THREE.ParticleBasicMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.map = this.map;
material.size = this.size;
material.sizeAttenuation = this.sizeAttenuation;
material.vertexColors = this.vertexColors;
material.fog = this.fog;
return material;
};
/**
* @author mrdoob / http://mrdoob.com/
*
* parameters = {
* color: <hex>,
* program: <function>,
* opacity: <float>,
* blending: THREE.NormalBlending
* }
*/
THREE.ParticleCanvasMaterial = function ( parameters ) {
THREE.Material.call( this );
this.color = new THREE.Color( 0xffffff );
this.program = function ( context, color ) {};
this.setValues( parameters );
};
THREE.ParticleCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.ParticleCanvasMaterial.prototype.clone = function () {
var material = new THREE.ParticleCanvasMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.program = this.program;
return material;
};
/**
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* fragmentShader: <string>,
* vertexShader: <string>,
*
* uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
*
* defines: { "label" : "value" },
*
* shading: THREE.SmoothShading,
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* wireframe: <boolean>,
* wireframeLinewidth: <float>,
*
* lights: <bool>,
*
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
*
* skinning: <bool>,
* morphTargets: <bool>,
* morphNormals: <bool>,
*
* fog: <bool>
* }
*/
THREE.ShaderMaterial = function ( parameters ) {
THREE.Material.call( this );
this.fragmentShader = "void main() {}";
this.vertexShader = "void main() {}";
this.uniforms = {};
this.defines = {};
this.attributes = null;
this.shading = THREE.SmoothShading;
this.wireframe = false;
this.wireframeLinewidth = 1;
this.fog = false; // set to use scene fog
this.lights = false; // set to use scene lights
this.vertexColors = THREE.NoColors; // set to use "color" attribute stream
this.skinning = false; // set to use skinning attribute streams
this.morphTargets = false; // set to use morph targets
this.morphNormals = false; // set to use morph normals
this.setValues( parameters );
};
THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.ShaderMaterial.prototype.clone = function () {
var material = new THREE.ShaderMaterial();
THREE.Material.prototype.clone.call( this, material );
material.fragmentShader = this.fragmentShader;
material.vertexShader = this.vertexShader;
material.uniforms = THREE.UniformsUtils.clone( this.uniforms );
material.attributes = this.attributes;
material.defines = this.defines;
material.shading = this.shading;
material.wireframe = this.wireframe;
material.wireframeLinewidth = this.wireframeLinewidth;
material.fog = this.fog;
material.lights = this.lights;
material.vertexColors = this.vertexColors;
material.skinning = this.skinning;
material.morphTargets = this.morphTargets;
material.morphNormals = this.morphNormals;
return material;
};
/**
* @author alteredq / http://alteredqualia.com/
*
* parameters = {
* color: <hex>,
* opacity: <float>,
* map: new THREE.Texture( <Image> ),
*
* blending: THREE.NormalBlending,
* depthTest: <bool>,
* depthWrite: <bool>,
*
* useScreenCoordinates: <bool>,
* sizeAttenuation: <bool>,
* scaleByViewport: <bool>,
* alignment: THREE.SpriteAlignment.center,
*
* uvOffset: new THREE.Vector2(),
* uvScale: new THREE.Vector2(),
*
* fog: <bool>
* }
*/
THREE.SpriteMaterial = function ( parameters ) {
THREE.Material.call( this );
// defaults
this.color = new THREE.Color( 0xffffff );
this.map = new THREE.Texture();
this.useScreenCoordinates = true;
this.depthTest = !this.useScreenCoordinates;
this.sizeAttenuation = !this.useScreenCoordinates;
this.scaleByViewport = !this.sizeAttenuation;
this.alignment = THREE.SpriteAlignment.center.clone();
this.fog = false;
this.uvOffset = new THREE.Vector2( 0, 0 );
this.uvScale = new THREE.Vector2( 1, 1 );
// set parameters
this.setValues( parameters );
// override coupled defaults if not specified explicitly by parameters
parameters = parameters || {};
if ( parameters.depthTest === undefined ) this.depthTest = !this.useScreenCoordinates;
if ( parameters.sizeAttenuation === undefined ) this.sizeAttenuation = !this.useScreenCoordinates;
if ( parameters.scaleByViewport === undefined ) this.scaleByViewport = !this.sizeAttenuation;
};
THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.SpriteMaterial.prototype.clone = function () {
var material = new THREE.SpriteMaterial();
THREE.Material.prototype.clone.call( this, material );
material.color.copy( this.color );
material.map = this.map;
material.useScreenCoordinates = this.useScreenCoordinates;
material.sizeAttenuation = this.sizeAttenuation;
material.scaleByViewport = this.scaleByViewport;
material.alignment.copy( this.alignment );
material.uvOffset.copy( this.uvOffset );
material.uvScale.copy( this.uvScale );
material.fog = this.fog;
return material;
};
// Alignment enums
THREE.SpriteAlignment = {};
THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 );
THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 );
THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 );
THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 );
THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 );
THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 );
THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 );
THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 );
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author szimek / https://github.com/szimek/
*/
THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
THREE.EventDispatcher.call( this );
this.id = THREE.TextureIdCount ++;
this.name = '';
this.image = image;
this.mipmaps = [];
this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping();
this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;
this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;
this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
this.format = format !== undefined ? format : THREE.RGBAFormat;
this.type = type !== undefined ? type : THREE.UnsignedByteType;
this.offset = new THREE.Vector2( 0, 0 );
this.repeat = new THREE.Vector2( 1, 1 );
this.generateMipmaps = true;
this.premultiplyAlpha = false;
this.flipY = true;
this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
this.needsUpdate = false;
this.onUpdate = null;
};
THREE.Texture.prototype = {
constructor: THREE.Texture,
clone: function ( texture ) {
if ( texture === undefined ) texture = new THREE.Texture();
texture.image = this.image;
texture.mipmaps = this.mipmaps.slice(0);
texture.mapping = this.mapping;
texture.wrapS = this.wrapS;
texture.wrapT = this.wrapT;
texture.magFilter = this.magFilter;
texture.minFilter = this.minFilter;
texture.anisotropy = this.anisotropy;
texture.format = this.format;
texture.type = this.type;
texture.offset.copy( this.offset );
texture.repeat.copy( this.repeat );
texture.generateMipmaps = this.generateMipmaps;
texture.premultiplyAlpha = this.premultiplyAlpha;
texture.flipY = this.flipY;
texture.unpackAlignment = this.unpackAlignment;
return texture;
},
dispose: function () {
this.dispatchEvent( { type: 'dispose' } );
}
};
THREE.TextureIdCount = 0;
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
this.image = { width: width, height: height };
this.mipmaps = mipmaps;
this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file
};
THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.CompressedTexture.prototype.clone = function () {
var texture = new THREE.CompressedTexture();
THREE.Texture.prototype.clone.call( this, texture );
return texture;
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {
THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
this.image = { data: data, width: width, height: height };
};
THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.DataTexture.prototype.clone = function () {
var texture = new THREE.DataTexture();
THREE.Texture.prototype.clone.call( this, texture );
return texture;
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.Particle = function ( material ) {
THREE.Object3D.call( this );
this.material = material;
};
THREE.Particle.prototype = Object.create( THREE.Object3D.prototype );
THREE.Particle.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Particle( this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.ParticleSystem = function ( geometry, material ) {
THREE.Object3D.call( this );
this.geometry = geometry;
this.material = ( material !== undefined ) ? material : new THREE.ParticleBasicMaterial( { color: Math.random() * 0xffffff } );
this.sortParticles = false;
if ( this.geometry ) {
// calc bound radius
if( this.geometry.boundingSphere === null ) {
this.geometry.computeBoundingSphere();
}
}
this.frustumCulled = false;
};
THREE.ParticleSystem.prototype = Object.create( THREE.Object3D.prototype );
THREE.ParticleSystem.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.ParticleSystem( this.geometry, this.material );
object.sortParticles = this.sortParticles;
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.Line = function ( geometry, material, type ) {
THREE.Object3D.call( this );
this.geometry = geometry;
this.material = ( material !== undefined ) ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
this.type = ( type !== undefined ) ? type : THREE.LineStrip;
if ( this.geometry ) {
if ( ! this.geometry.boundingSphere ) {
this.geometry.computeBoundingSphere();
}
}
};
THREE.LineStrip = 0;
THREE.LinePieces = 1;
THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
THREE.Line.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author mikael emtinger / http://gomo.se/
* @author jonobr1 / http://jonobr1.com/
*/
THREE.Mesh = function ( geometry, material ) {
THREE.Object3D.call( this );
this.geometry = geometry;
this.material = ( material !== undefined ) ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, wireframe: true } );
if ( this.geometry !== undefined ) {
if ( this.geometry.boundingSphere === null ) {
this.geometry.computeBoundingSphere();
}
this.updateMorphTargets();
}
};
THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
THREE.Mesh.prototype.updateMorphTargets = function () {
if ( this.geometry.morphTargets.length > 0 ) {
this.morphTargetBase = -1;
this.morphTargetForcedOrder = [];
this.morphTargetInfluences = [];
this.morphTargetDictionary = {};
for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {
this.morphTargetInfluences.push( 0 );
this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
}
}
};
THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {
if ( this.morphTargetDictionary[ name ] !== undefined ) {
return this.morphTargetDictionary[ name ];
}
console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." );
return 0;
};
THREE.Mesh.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Bone = function( belongsToSkin ) {
THREE.Object3D.call( this );
this.skin = belongsToSkin;
this.skinMatrix = new THREE.Matrix4();
};
THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
// update local
if ( this.matrixAutoUpdate ) {
forceUpdate |= this.updateMatrix();
}
// update skin matrix
if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
if( parentSkinMatrix ) {
this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
} else {
this.skinMatrix.copy( this.matrix );
}
this.matrixWorldNeedsUpdate = false;
forceUpdate = true;
}
// update children
var child, i, l = this.children.length;
for ( i = 0; i < l; i ++ ) {
this.children[ i ].update( this.skinMatrix, forceUpdate );
}
};
/**
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
*/
THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
THREE.Mesh.call( this, geometry, material );
//
this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
// init bones
this.identityMatrix = new THREE.Matrix4();
this.bones = [];
this.boneMatrices = [];
var b, bone, gbone, p, q, s;
if ( this.geometry && this.geometry.bones !== undefined ) {
for ( b = 0; b < this.geometry.bones.length; b ++ ) {
gbone = this.geometry.bones[ b ];
p = gbone.pos;
q = gbone.rotq;
s = gbone.scl;
bone = this.addBone();
bone.name = gbone.name;
bone.position.set( p[0], p[1], p[2] );
bone.quaternion.set( q[0], q[1], q[2], q[3] );
bone.useQuaternion = true;
if ( s !== undefined ) {
bone.scale.set( s[0], s[1], s[2] );
} else {
bone.scale.set( 1, 1, 1 );
}
}
for ( b = 0; b < this.bones.length; b ++ ) {
gbone = this.geometry.bones[ b ];
bone = this.bones[ b ];
if ( gbone.parent === -1 ) {
this.add( bone );
} else {
this.bones[ gbone.parent ].add( bone );
}
}
//
var nBones = this.bones.length;
if ( this.useVertexTexture ) {
// layout (1 matrix = 4 pixels)
// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
// with 8x8 pixel texture max 16 bones (8 * 8 / 4)
// 16x16 pixel texture max 64 bones (16 * 16 / 4)
// 32x32 pixel texture max 256 bones (32 * 32 / 4)
// 64x64 pixel texture max 1024 bones (64 * 64 / 4)
var size;
if ( nBones > 256 )
size = 64;
else if ( nBones > 64 )
size = 32;
else if ( nBones > 16 )
size = 16;
else
size = 8;
this.boneTextureWidth = size;
this.boneTextureHeight = size;
this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );
this.boneTexture.minFilter = THREE.NearestFilter;
this.boneTexture.magFilter = THREE.NearestFilter;
this.boneTexture.generateMipmaps = false;
this.boneTexture.flipY = false;
} else {
this.boneMatrices = new Float32Array( 16 * nBones );
}
this.pose();
}
};
THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
THREE.SkinnedMesh.prototype.addBone = function( bone ) {
if ( bone === undefined ) {
bone = new THREE.Bone( this );
}
this.bones.push( bone );
return bone;
};
THREE.SkinnedMesh.prototype.updateMatrixWorld = function ( force ) {
this.matrixAutoUpdate && this.updateMatrix();
// update matrixWorld
if ( this.matrixWorldNeedsUpdate || force ) {
if ( this.parent ) {
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
} else {
this.matrixWorld.copy( this.matrix );
}
this.matrixWorldNeedsUpdate = false;
force = true;
}
// update children
for ( var i = 0, l = this.children.length; i < l; i ++ ) {
var child = this.children[ i ];
if ( child instanceof THREE.Bone ) {
child.update( this.identityMatrix, false );
} else {
child.updateMatrixWorld( true );
}
}
// make a snapshot of the bones' rest position
if ( this.boneInverses == undefined ) {
this.boneInverses = [];
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
var inverse = new THREE.Matrix4();
inverse.getInverse( this.bones[ b ].skinMatrix );
this.boneInverses.push( inverse );
}
}
// flatten bone matrices to array
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
// compute the offset between the current and the original transform;
//TODO: we could get rid of this multiplication step if the skinMatrix
// was already representing the offset; however, this requires some
// major changes to the animation system
THREE.SkinnedMesh.offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );
THREE.SkinnedMesh.offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
}
if ( this.useVertexTexture ) {
this.boneTexture.needsUpdate = true;
}
};
THREE.SkinnedMesh.prototype.pose = function () {
this.updateMatrixWorld( true );
for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {
// normalize weights
var sw = this.geometry.skinWeights[ i ];
var scale = 1.0 / sw.lengthManhattan();
if ( scale !== Infinity ) {
sw.multiplyScalar( scale );
} else {
sw.set( 1 ); // this will be normalized by the shader anyway
}
}
};
THREE.SkinnedMesh.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture );
THREE.Mesh.prototype.clone.call( this, object );
return object;
};
THREE.SkinnedMesh.offsetMatrix = new THREE.Matrix4();
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.MorphAnimMesh = function ( geometry, material ) {
THREE.Mesh.call( this, geometry, material );
// API
this.duration = 1000; // milliseconds
this.mirroredLoop = false;
this.time = 0;
// internals
this.lastKeyframe = 0;
this.currentKeyframe = 0;
this.direction = 1;
this.directionBackwards = false;
this.setFrameRange( 0, this.geometry.morphTargets.length - 1 );
};
THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype );
THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) {
this.startKeyframe = start;
this.endKeyframe = end;
this.length = this.endKeyframe - this.startKeyframe + 1;
};
THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
this.direction = 1;
this.directionBackwards = false;
};
THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
this.direction = -1;
this.directionBackwards = true;
};
THREE.MorphAnimMesh.prototype.parseAnimations = function () {
var geometry = this.geometry;
if ( ! geometry.animations ) geometry.animations = {};
var firstAnimation, animations = geometry.animations;
var pattern = /([a-z]+)(\d+)/;
for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
var morph = geometry.morphTargets[ i ];
var parts = morph.name.match( pattern );
if ( parts && parts.length > 1 ) {
var label = parts[ 1 ];
var num = parts[ 2 ];
if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: -Infinity };
var animation = animations[ label ];
if ( i < animation.start ) animation.start = i;
if ( i > animation.end ) animation.end = i;
if ( ! firstAnimation ) firstAnimation = label;
}
}
geometry.firstAnimation = firstAnimation;
};
THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) {
if ( ! this.geometry.animations ) this.geometry.animations = {};
this.geometry.animations[ label ] = { start: start, end: end };
};
THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) {
var animation = this.geometry.animations[ label ];
if ( animation ) {
this.setFrameRange( animation.start, animation.end );
this.duration = 1000 * ( ( animation.end - animation.start ) / fps );
this.time = 0;
} else {
console.warn( "animation[" + label + "] undefined" );
}
};
THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) {
var frameTime = this.duration / this.length;
this.time += this.direction * delta;
if ( this.mirroredLoop ) {
if ( this.time > this.duration || this.time < 0 ) {
this.direction *= -1;
if ( this.time > this.duration ) {
this.time = this.duration;
this.directionBackwards = true;
}
if ( this.time < 0 ) {
this.time = 0;
this.directionBackwards = false;
}
}
} else {
this.time = this.time % this.duration;
if ( this.time < 0 ) this.time += this.duration;
}
var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 );
if ( keyframe !== this.currentKeyframe ) {
this.morphTargetInfluences[ this.lastKeyframe ] = 0;
this.morphTargetInfluences[ this.currentKeyframe ] = 1;
this.morphTargetInfluences[ keyframe ] = 0;
this.lastKeyframe = this.currentKeyframe;
this.currentKeyframe = keyframe;
}
var mix = ( this.time % frameTime ) / frameTime;
if ( this.directionBackwards ) {
mix = 1 - mix;
}
this.morphTargetInfluences[ this.currentKeyframe ] = mix;
this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix;
};
THREE.MorphAnimMesh.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material );
object.duration = this.duration;
object.mirroredLoop = this.mirroredLoop;
object.time = this.time;
object.lastKeyframe = this.lastKeyframe;
object.currentKeyframe = this.currentKeyframe;
object.direction = this.direction;
object.directionBackwards = this.directionBackwards;
THREE.Mesh.prototype.clone.call( this, object );
return object;
};
/**
* @author alteredq / http://alteredqualia.com/
*/
THREE.Ribbon = function ( geometry, material ) {
THREE.Object3D.call( this );
this.geometry = geometry;
this.material = material;
};
THREE.Ribbon.prototype = Object.create( THREE.Object3D.prototype );
THREE.Ribbon.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Ribbon( this.geometry, this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
* @author mrdoob / http://mrdoob.com/
*/
THREE.LOD = function () {
THREE.Object3D.call( this );
this.LODs = [];
};
THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
THREE.LOD.prototype.addLevel = function ( object3D, visibleAtDistance ) {
if ( visibleAtDistance === undefined ) {
visibleAtDistance = 0;
}
visibleAtDistance = Math.abs( visibleAtDistance );
for ( var l = 0; l < this.LODs.length; l ++ ) {
if ( visibleAtDistance < this.LODs[ l ].visibleAtDistance ) {
break;
}
}
this.LODs.splice( l, 0, { visibleAtDistance: visibleAtDistance, object3D: object3D } );
this.add( object3D );
};
THREE.LOD.prototype.update = function ( camera ) {
if ( this.LODs.length > 1 ) {
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
var inverse = camera.matrixWorldInverse;
var distance = -( inverse.elements[2] * this.matrixWorld.elements[12] + inverse.elements[6] * this.matrixWorld.elements[13] + inverse.elements[10] * this.matrixWorld.elements[14] + inverse.elements[14] );
this.LODs[ 0 ].object3D.visible = true;
for ( var l = 1; l < this.LODs.length; l ++ ) {
if( distance >= this.LODs[ l ].visibleAtDistance ) {
this.LODs[ l - 1 ].object3D.visible = false;
this.LODs[ l ].object3D.visible = true;
} else {
break;
}
}
for( ; l < this.LODs.length; l ++ ) {
this.LODs[ l ].object3D.visible = false;
}
}
};
THREE.LOD.prototype.clone = function () {
// TODO
};
/**
* @author mikael emtinger / http://gomo.se/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Sprite = function ( material ) {
THREE.Object3D.call( this );
this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
this.rotation3d = this.rotation;
this.rotation = 0;
};
THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
/*
* Custom update matrix
*/
THREE.Sprite.prototype.updateMatrix = function () {
this.matrix.setPosition( this.position );
this.rotation3d.set( 0, 0, this.rotation );
this.matrix.setRotationFromEuler( this.rotation3d );
if ( this.scale.x !== 1 || this.scale.y !== 1 ) {
this.matrix.scale( this.scale );
}
this.matrixWorldNeedsUpdate = true;
};
THREE.Sprite.prototype.clone = function ( object ) {
if ( object === undefined ) object = new THREE.Sprite( this.material );
THREE.Object3D.prototype.clone.call( this, object );
return object;
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.Scene = function () {
THREE.Object3D.call( this );
this.fog = null;
this.overrideMaterial = null;
this.matrixAutoUpdate = false;
this.__objects = [];
this.__lights = [];
this.__objectsAdded = [];
this.__objectsRemoved = [];
};
THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
THREE.Scene.prototype.__addObject = function ( object ) {
if ( object instanceof THREE.Light ) {
if ( this.__lights.indexOf( object ) === - 1 ) {
this.__lights.push( object );
}
if ( object.target && object.target.parent === undefined ) {
this.add( object.target );
}
} else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) {
if ( this.__objects.indexOf( object ) === - 1 ) {
this.__objects.push( object );
this.__objectsAdded.push( object );
// check if previously removed
var i = this.__objectsRemoved.indexOf( object );
if ( i !== -1 ) {
this.__objectsRemoved.splice( i, 1 );
}
}
}
for ( var c = 0; c < object.children.length; c ++ ) {
this.__addObject( object.children[ c ] );
}
};
THREE.Scene.prototype.__removeObject = function ( object ) {
if ( object instanceof THREE.Light ) {
var i = this.__lights.indexOf( object );
if ( i !== -1 ) {
this.__lights.splice( i, 1 );
}
} else if ( !( object instanceof THREE.Camera ) ) {
var i = this.__objects.indexOf( object );
if( i !== -1 ) {
this.__objects.splice( i, 1 );
this.__objectsRemoved.push( object );
// check if previously added
var ai = this.__objectsAdded.indexOf( object );
if ( ai !== -1 ) {
this.__objectsAdded.splice( ai, 1 );
}
}
}
for ( var c = 0; c < object.children.length; c ++ ) {
this.__removeObject( object.children[ c ] );
}
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.Fog = function ( hex, near, far ) {
this.name = '';
this.color = new THREE.Color( hex );
this.near = ( near !== undefined ) ? near : 1;
this.far = ( far !== undefined ) ? far : 1000;
};
THREE.Fog.prototype.clone = function () {
return new THREE.Fog( this.color.getHex(), this.near, this.far );
};
/**
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
*/
THREE.FogExp2 = function ( hex, density ) {
this.name = '';
this.color = new THREE.Color( hex );
this.density = ( density !== undefined ) ? density : 0.00025;
};
THREE.FogExp2.prototype.clone = function () {
return new THREE.FogExp2( this.color.getHex(), this.density );
};
/**
* @author mrdoob / http://mrdoob.com/
*/
THREE.CanvasRenderer = function ( parameters ) {
console.log( 'THREE.CanvasRenderer', THREE.REVISION );
var smoothstep = THREE.Math.smoothstep;
parameters = parameters || {};
var _this = this,
_renderData, _elements, _lights,
_projector = new THREE.Projector(),
_canvas = parameters.canvas !== undefined
? parameters.canvas
: document.createElement( 'canvas' ),
_canvasWidth, _canvasHeight, _canvasWidthHalf, _canvasHeightHalf,
_context = _canvas.getContext( '2d' ),
_clearColor = new THREE.Color( 0x000000 ),
_clearOpacity = 0,
_contextGlobalAlpha = 1,
_contextGlobalCompositeOperation = 0,
_contextStrokeStyle = null,
_contextFillStyle = null,
_contextLineWidth = null,
_contextLineCap = null,
_contextLineJoin = null,
_contextDashSize = null,
_contextGapSize = 0,
_v1, _v2, _v3, _v4,
_v5 = new THREE.RenderableVertex(),
_v6 = new THREE.RenderableVertex(),
_v1x, _v1y, _v2x, _v2y, _v3x, _v3y,
_v4x, _v4y, _v5x, _v5y, _v6x, _v6y,
_color = new THREE.Color(),
_color1 = new THREE.Color(),
_color2 = new THREE.Color(),
_color3 = new THREE.Color(),
_color4 = new THREE.Color(),
_diffuseColor = new THREE.Color(),
_emissiveColor = new THREE.Color(),
_lightColor = new THREE.Color(),
_patterns = {}, _imagedatas = {},
_near, _far,
_image, _uvs,
_uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y,
_clipBox = new THREE.Box2(),
_clearBox = new THREE.Box2(),
_elemBox = new THREE.Box2(),
_enableLighting = false,
_ambientLight = new THREE.Color(),
_directionalLights = new THREE.Color(),
_pointLights = new THREE.Color(),
_vector3 = new THREE.Vector3(), // Needed for PointLight
_pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData,
_gradientMap, _gradientMapContext, _gradientMapQuality = 16;
_pixelMap = document.createElement( 'canvas' );
_pixelMap.width = _pixelMap.height = 2;
_pixelMapContext = _pixelMap.getContext( '2d' );
_pixelMapContext.fillStyle = 'rgba(0,0,0,1)';
_pixelMapContext.fillRect( 0, 0, 2, 2 );
_pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 );
_pixelMapData = _pixelMapImage.data;
_gradientMap = document.createElement( 'canvas' );
_gradientMap.width = _gradientMap.height = _gradientMapQuality;
_gradientMapContext = _gradientMap.getContext( '2d' );
_gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 );
_gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality );
_gradientMapQuality --; // Fix UVs
// dash+gap fallbacks for Firefox and everything else
if ( _context.setLineDash === undefined ) {
if ( _context.mozDash !== undefined ) {
_context.setLineDash = function ( values ) {
_context.mozDash = values[ 0 ] !== null ? values : null;
}
} else {
_context.setLineDash = function () {}
}
}
this.domElement = _canvas;
this.devicePixelRatio = parameters.devicePixelRatio !== undefined
? parameters.devicePixelRatio
: window.devicePixelRatio !== undefined
? window.devicePixelRatio
: 1;
this.autoClear = true;
this.sortObjects = true;
this.sortElements = true;
this.info = {
render: {
vertices: 0,
faces: 0
}
}
// WebGLRenderer compatibility
this.supportsVertexTextures = function () {};
this.setFaceCulling = function () {};
this.setSize = function ( width, height ) {
_canvasWidth = width * this.devicePixelRatio;
_canvasHeight = height * this.devicePixelRatio;
_canvasWidthHalf = Math.floor( _canvasWidth / 2 );
_canvasHeightHalf = Math.floor( _canvasHeight / 2 );
_canvas.width = _canvasWidth;
_canvas.height = _canvasHeight;
_canvas.style.width = width + 'px';
_canvas.style.height = height + 'px';
_clipBox.set(
new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
);
_clearBox.set(
new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
);
_contextGlobalAlpha = 1;
_contextGlobalCompositeOperation = 0;
_contextStrokeStyle = null;
_contextFillStyle = null;
_contextLineWidth = null;
_contextLineCap = null;
_contextLineJoin = null;
};
this.setClearColor = function ( color, opacity ) {
_clearColor.copy( color );
_clearOpacity = opacity !== undefined ? opacity : 1;
_clearBox.set(
new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
);
};
this.setClearColorHex = function ( hex, opacity ) {
_clearColor.setHex( hex );
_clearOpacity = opacity !== undefined ? opacity : 1;
_clearBox.set(
new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
);
};
this.getMaxAnisotropy = function () {
return 0;
};
this.clear = function () {
_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
if ( _clearBox.empty() === false ) {
_clearBox.intersect( _clipBox );
_clearBox.expandByScalar( 2 );
if ( _clearOpacity < 1 ) {
_context.clearRect(
_clearBox.min.x | 0,
_clearBox.min.y | 0,
( _clearBox.max.x - _clearBox.min.x ) | 0,
( _clearBox.max.y - _clearBox.min.y ) | 0
);
}
if ( _clearOpacity > 0 ) {
setBlending( THREE.NormalBlending );
setOpacity( 1 );
setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearOpacity + ')' );
_context.fillRect(
_clearBox.min.x | 0,
_clearBox.min.y | 0,
( _clearBox.max.x - _clearBox.min.x ) | 0,
( _clearBox.max.y - _clearBox.min.y ) | 0
);
}
_clearBox.makeEmpty();
}
};
this.render = function ( scene, camera ) {
if ( camera instanceof THREE.Camera === false ) {
console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' );
return;
}
if ( this.autoClear === true ) {
this.clear();
}
_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
_this.info.render.vertices = 0;
_this.info.render.faces = 0;
_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
_elements = _renderData.elements;
_lights = _renderData.lights;
/* DEBUG
setFillStyle( 'rgba( 0, 255, 255, 0.5 )' );
_context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y );
*/
_enableLighting = _lights.length > 0;
if ( _enableLighting === true ) {
calculateLights();
}
for ( var e = 0, el = _elements.length; e < el; e++ ) {
var element = _elements[ e ];
var material = element.material;
if ( material === undefined || material.visible === false ) continue;
_elemBox.makeEmpty();
if ( element instanceof THREE.RenderableParticle ) {
_v1 = element;
_v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf;
renderParticle( _v1, element, material );
} else if ( element instanceof THREE.RenderableLine ) {
_v1 = element.v1; _v2 = element.v2;
_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
renderLine( _v1, _v2, element, material );
}
} else if ( element instanceof THREE.RenderableFace3 ) {
_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;
_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
_v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf;
if ( material.overdraw === true ) {
expand( _v1.positionScreen, _v2.positionScreen );
expand( _v2.positionScreen, _v3.positionScreen );
expand( _v3.positionScreen, _v1.positionScreen );
}
_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen ] );
renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material );
} else if ( element instanceof THREE.RenderableFace4 ) {
_v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4;
if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;
if ( _v4.positionScreen.z < -1 || _v4.positionScreen.z > 1 ) continue;
_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
_v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf;
_v4.positionScreen.x *= _canvasWidthHalf; _v4.positionScreen.y *= _canvasHeightHalf;
_v5.positionScreen.copy( _v2.positionScreen );
_v6.positionScreen.copy( _v4.positionScreen );
if ( material.overdraw === true ) {
expand( _v1.positionScreen, _v2.positionScreen );
expand( _v2.positionScreen, _v4.positionScreen );
expand( _v4.positionScreen, _v1.positionScreen );
expand( _v3.positionScreen, _v5.positionScreen );
expand( _v3.positionScreen, _v6.positionScreen );
}
_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen, _v4.positionScreen ] );
renderFace4( _v1, _v2, _v3, _v4, _v5, _v6, element, material, scene );
}
/* DEBUG
setLineWidth( 1 );
setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' );
_context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y );
*/
_clearBox.union( _elemBox );
}
/* DEBUG
setLineWidth( 1 );
setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' );
_context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y );
*/
_context.setTransform( 1, 0, 0, 1, 0, 0 );
//
function calculateLights() {
_ambientLight.setRGB( 0, 0, 0 );
_directionalLights.setRGB( 0, 0, 0 );
_pointLights.setRGB( 0, 0, 0 );
for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
var light = _lights[ l ];
var lightColor = light.color;
if ( light instanceof THREE.AmbientLight ) {
_ambientLight.add( lightColor );
} else if ( light instanceof THREE.DirectionalLight ) {
// for particles
_directionalLights.add( lightColor );
} else if ( light instanceof THREE.PointLight ) {
// for particles
_pointLights.add( lightColor );
}
}
}
function calculateLight( position, normal, color ) {
for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
var light = _lights[ l ];
_lightColor.copy( light.color );
if ( light instanceof THREE.DirectionalLight ) {
var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize();
var amount = normal.dot( lightPosition );
if ( amount <= 0 ) continue;
amount *= light.intensity;
color.add( _lightColor.multiplyScalar( amount ) );
} else if ( light instanceof THREE.PointLight ) {
var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld );
var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
if ( amount <= 0 ) continue;
amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
if ( amount == 0 ) continue;
amount *= light.intensity;
color.add( _lightColor.multiplyScalar( amount ) );
}
}
}
function renderParticle( v1, element, material ) {
setOpacity( material.opacity );
setBlending( material.blending );
var width, height, scaleX, scaleY,
bitmap, bitmapWidth, bitmapHeight;
if ( material instanceof THREE.ParticleBasicMaterial ) {
if ( material.map === null ) {
scaleX = element.object.scale.x;
scaleY = element.object.scale.y;
// TODO: Be able to disable this
scaleX *= element.scale.x * _canvasWidthHalf;
scaleY *= element.scale.y * _canvasHeightHalf;
_elemBox.min.set( v1.x - scaleX, v1.y - scaleY );
_elemBox.max.set( v1.x + scaleX, v1.y + scaleY );
if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
return;
}
setFillStyle( material.color.getStyle() );
_context.save();
_context.translate( v1.x, v1.y );
_context.rotate( - element.rotation );
_context.scale( scaleX, scaleY );
_context.fillRect( -1, -1, 2, 2 );
_context.restore();
} else {
bitmap = material.map.image;
bitmapWidth = bitmap.width >> 1;
bitmapHeight = bitmap.height >> 1;
scaleX = element.scale.x * _canvasWidthHalf;
scaleY = element.scale.y * _canvasHeightHalf;
width = scaleX * bitmapWidth;
height = scaleY * bitmapHeight;
// TODO: Rotations break this...
_elemBox.min.set( v1.x - width, v1.y - height );
_elemBox.max.set( v1.x + width, v1.y + height );
if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
return;
}
_context.save();
_context.translate( v1.x, v1.y );
_context.rotate( - element.rotation );
_context.scale( scaleX, - scaleY );
_context.translate( - bitmapWidth, - bitmapHeight );
_context.drawImage( bitmap, 0, 0 );
_context.restore();
}
/* DEBUG
setStrokeStyle( 'rgb(255,255,0)' );
_context.beginPath();
_context.moveTo( v1.x - 10, v1.y );
_context.lineTo( v1.x + 10, v1.y );
_context.moveTo( v1.x, v1.y - 10 );
_context.lineTo( v1.x, v1.y + 10 );
_context.stroke();
*/
} else if ( material instanceof THREE.ParticleCanvasMaterial ) {
width = element.scale.x * _canvasWidthHalf;
height = element.scale.y * _canvasHeightHalf;
_elemBox.min.set( v1.x - width, v1.y - height );
_elemBox.max.set( v1.x + width, v1.y + height );
if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
return;
}
setStrokeStyle( material.color.getStyle() );
setFillStyle( material.color.getStyle() );
_context.save();
_context.translate( v1.x, v1.y );
_context.rotate( - element.rotation );
_context.scale( width, height );
material.program( _context );
_context.restore();
}
}
function renderLine( v1, v2, element, material ) {
setOpacity( material.opacity );
setBlending( material.blending );
_context.beginPath();
_context.moveTo( v1.positionScreen.x, v1.positionScreen.y );
_context.lineTo( v2.positionScreen.x, v2.positionScreen.y );
if ( material instanceof THREE.LineBasicMaterial ) {
setLineWidth( material.linewidth );
setLineCap( material.linecap );
setLineJoin( material.linejoin );
setStrokeStyle( material.color.getStyle() );
setDashAndGap( null, null );
_context.stroke();
_elemBox.expandByScalar( material.linewidth * 2 );
} else if ( material instanceof THREE.LineDashedMaterial ) {
setLineWidth( material.linewidth );
setLineCap( material.linecap );
setLineJoin( material.linejoin );
setStrokeStyle( material.color.getStyle() );
setDashAndGap( material.dashSize, material.gapSize );
_context.stroke();
_elemBox.expandByScalar( material.linewidth * 2 );
}
}
function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) {
_this.info.render.vertices += 3;
_this.info.render.faces ++;
setOpacity( material.opacity );
setBlending( material.blending );
_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y );
if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) {
_diffuseColor.copy( material.color );
_emissiveColor.copy( material.emissive );
if ( material.vertexColors === THREE.FaceColors ) {
_diffuseColor.multiply( element.color );
}
if ( _enableLighting === true ) {
if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) {
_color1.copy( _ambientLight );
_color2.copy( _ambientLight );
_color3.copy( _ambientLight );
calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color3 );
_color1.multiply( _diffuseColor ).add( _emissiveColor );
_color2.multiply( _diffuseColor ).add( _emissiveColor );
_color3.multiply( _diffuseColor ).add( _emissiveColor );
_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
} else {
_color.copy( _ambientLight );
calculateLight( element.centroidModel, element.normalModel, _color );
_color.multiply( _diffuseColor ).add( _emissiveColor );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
}
} else {
material.wireframe === true
? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( material.color );
}
} else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
if ( material.map !== null ) {
if ( material.map.mapping instanceof THREE.UVMapping ) {
_uvs = element.uvs[ 0 ];
patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map );
}
} else if ( material.envMap !== null ) {
if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) {
_vector3.copy( element.vertexNormalsModelView[ uv1 ] );
_uv1x = 0.5 * _vector3.x + 0.5;
_uv1y = 0.5 * _vector3.y + 0.5;
_vector3.copy( element.vertexNormalsModelView[ uv2 ] );
_uv2x = 0.5 * _vector3.x + 0.5;
_uv2y = 0.5 * _vector3.y + 0.5;
_vector3.copy( element.vertexNormalsModelView[ uv3 ] );
_uv3x = 0.5 * _vector3.x + 0.5;
_uv3y = 0.5 * _vector3.y + 0.5;
patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
}/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) {
}*/
} else {
_color.copy( material.color );
if ( material.vertexColors === THREE.FaceColors ) {
_color.multiply( element.color );
}
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
}
} else if ( material instanceof THREE.MeshDepthMaterial ) {
_near = camera.near;
_far = camera.far;
_color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _near, _far );
_color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z * v2.positionScreen.w, _near, _far );
_color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far );
_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
} else if ( material instanceof THREE.MeshNormalMaterial ) {
var normal;
if ( material.shading == THREE.FlatShading ) {
normal = element.normalModelView;
_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
} else if ( material.shading == THREE.SmoothShading ) {
normal = element.vertexNormalsModelView[ uv1 ];
_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
normal = element.vertexNormalsModelView[ uv2 ];
_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
normal = element.vertexNormalsModelView[ uv3 ];
_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
}
}
}
function renderFace4( v1, v2, v3, v4, v5, v6, element, material ) {
_this.info.render.vertices += 4;
_this.info.render.faces ++;
setOpacity( material.opacity );
setBlending( material.blending );
if ( ( material.map !== undefined && material.map !== null ) || ( material.envMap !== undefined && material.envMap !== null ) ) {
// Let renderFace3() handle this
renderFace3( v1, v2, v4, 0, 1, 3, element, material );
renderFace3( v5, v3, v6, 1, 2, 3, element, material );
return;
}
_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
_v4x = v4.positionScreen.x; _v4y = v4.positionScreen.y;
_v5x = v5.positionScreen.x; _v5y = v5.positionScreen.y;
_v6x = v6.positionScreen.x; _v6y = v6.positionScreen.y;
if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
_diffuseColor.copy( material.color );
_emissiveColor.copy( material.emissive );
if ( material.vertexColors === THREE.FaceColors ) {
_diffuseColor.multiply( element.color );
}
if ( _enableLighting === true ) {
if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 4 ) {
_color1.copy( _ambientLight );
_color2.copy( _ambientLight );
_color3.copy( _ambientLight );
_color4.copy( _ambientLight );
calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
calculateLight( element.v4.positionWorld, element.vertexNormalsModel[ 3 ], _color3 );
calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color4 );
_color1.multiply( _diffuseColor ).add( _emissiveColor );
_color2.multiply( _diffuseColor ).add( _emissiveColor );
_color3.multiply( _diffuseColor ).add( _emissiveColor );
_color4.multiply( _diffuseColor ).add( _emissiveColor );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
// TODO: UVs are incorrect, v4->v3?
drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
} else {
_color.copy( _ambientLight );
calculateLight( element.centroidModel, element.normalModel, _color );
_color.multiply( _diffuseColor ).add( _emissiveColor );
drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
}
} else {
_color.addColors( _diffuseColor, _emissiveColor );
drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
}
} else if ( material instanceof THREE.MeshBasicMaterial ) {
_color.copy( material.color );
if ( material.vertexColors === THREE.FaceColors ) {
_color.multiply( element.color );
}
drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
} else if ( material instanceof THREE.MeshNormalMaterial ) {
var normal;
if ( material.shading == THREE.FlatShading ) {
normal = element.normalModelView;
_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y );
material.wireframe === true
? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
: fillPath( _color );
} else if ( material.shading == THREE.SmoothShading ) {
normal = element.vertexNormalsModelView[ 0 ];
_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
normal = element.vertexNormalsModelView[ 1 ];
_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
normal = element.vertexNormalsModelView[ 3 ];
_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
normal = element.vertexNormalsModelView[ 2 ];
_color4.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
}
} else if ( material instanceof THREE.MeshDepthMaterial ) {
_near = camera.near;
_far = camera.far;
_color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _near, _far );
_color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z * v2.positionScreen.w, _near, _far );
_color3.r = _color3.g = _color3.b = 1 - smoothstep( v4.positionScreen.z * v4.positionScreen.w, _near, _far );
_color4.r = _color4.g = _color4.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far );
_image = getGradientTexture( _color1, _color2, _color3, _color4 );
// TODO: UVs are incorrect, v4->v3?
drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y );
clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image );
drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y );
clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image );
}
}
//
function drawTriangle( x0, y0, x1, y1, x2, y2 ) {
_context.beginPath();
_context.moveTo( x0, y0 );
_context.lineTo( x1, y1 );
_context.lineTo( x2, y2 );
_context.closePath();
}
function drawQuad( x0, y0, x1, y1, x2, y2, x3, y3 ) {
_context.beginPath();
_context.moveTo( x0, y0 );
_context.lineTo( x1, y1 );
_context.lineTo( x2, y2 );
_context.lineTo( x3, y3 );
_context.closePath();
}
function strokePath( color, linewidth, linecap, linejoin ) {
setLineWidth( linewidth );
setLineCap( linecap );
setLineJoin( linejoin );
setStrokeStyle( color.getStyle() );
_context.stroke();
_elemBox.expandByScalar( linewidth * 2 );
}
function fillPath( color ) {
setFillStyle( color.getStyle() );
_context.fill();
}
function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) {
if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return;
if ( texture.needsUpdate === true ) {
var repeatX = texture.wrapS == THREE.RepeatWrapping;
var repeatY = texture.wrapT == THREE.RepeatWrapping;
_patterns[ texture.id ] = _context.createPattern(
texture.image, repeatX === true && repeatY === true
? 'repeat'
: repeatX === true && repeatY === false
? 'repeat-x'
: repeatX === false && repeatY === true
? 'repeat-y'
: 'no-repeat'
);
texture.needsUpdate = false;
}
_patterns[ texture.id ] === undefined
? setFillStyle( 'rgba(0,0,0,1)' )
: setFillStyle( _patterns[ texture.id ] );
// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
var a, b, c, d, e, f, det, idet,
offsetX = texture.offset.x / texture.repeat.x,
offsetY = texture.offset.y / texture.repeat.y,
width = texture.image.width * texture.repeat.x,
height = texture.image.height * texture.repeat.y;
u0 = ( u0 + offsetX ) * width;
v0 = ( 1.0 - v0 + offsetY ) * height;
u1 = ( u1 + offsetX ) * width;
v1 = ( 1.0 - v1 + offsetY ) * height;
u2 = ( u2 + offsetX ) * width;
v2 = ( 1.0 - v2 + offsetY ) * height;
x1 -= x0; y1 -= y0;
x2 -= x0; y2 -= y0;
u1 -= u0; v1 -= v0;
u2 -= u0; v2 -= v0;
det = u1 * v2 - u2 * v1;
if ( det === 0 ) {
if ( _imagedatas[ texture.id ] === undefined ) {
var canvas = document.createElement( 'canvas' )
canvas.width = texture.image.width;
canvas.height = texture.image.height;
var context = canvas.getContext( '2d' );
context.drawImage( texture.image, 0, 0 );
_imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data;
}
var data = _imagedatas[ texture.id ];
var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4;
_color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 );
fillPath( _color );
return;
}
idet = 1 / det;
a = ( v2 * x1 - v1 * x2 ) * idet;
b = ( v2 * y1 - v1 * y2 ) * idet;
c = ( u1 * x2 - u2 * x1 ) * idet;
d = ( u1 * y2 - u2 * y1 ) * idet;
e = x0 - a * u0 - c * v0;
f = y0 - b * u0 - d * v0;
_context.save();
_context.transform( a, b, c, d, e, f );
_context.fill();
_context.restore();
}
function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) {
// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
var a, b, c, d, e, f, det, idet,
width = image.width - 1,
height = image.height - 1;
u0 *= width; v0 *= height;
u1 *= width; v1 *= height;
u2 *= width; v2 *= height;
x1 -= x0; y1 -= y0;
x2 -= x0; y2 -= y0;
u1 -= u0; v1 -= v0;
u2 -= u0; v2 -= v0;
det = u1 * v2 - u2 * v1;
idet = 1 / det;
a = ( v2 * x1 - v1 * x2 ) * idet;
b = ( v2 * y1 - v1 * y2 ) * idet;
c = ( u1 * x2 - u2 * x1 ) * idet;
d = ( u1 * y2 - u2 * y1 ) * idet;
e = x0 - a * u0 - c * v0;
f = y0 - b * u0 - d * v0;
_context.save();
_context.transform( a, b, c, d, e, f );
_context.clip();
_context.drawImage( image, 0, 0 );
_context.restore();
}
function getGradientTexture( color1, color2, color3, color4 ) {
// http://mrdoob.com/blog/post/710
_pixelMapData[ 0 ] = ( color1.r * 255 ) | 0;
_pixelMapData[ 1 ] = ( color1.g * 255 ) | 0;
_pixelMapData[ 2 ] = ( color1.b * 255 ) | 0;
_pixelMapData[ 4 ] = ( color2.r * 255 ) | 0;
_pixelMapData[ 5 ] = ( color2.g * 255 ) | 0;
_pixelMapData[ 6 ] = ( color2.b * 255 ) | 0;
_pixelMapData[ 8 ] = ( color3.r * 255 ) | 0;
_pixelMapData[ 9 ] = ( color3.g * 255 ) | 0;
_pixelMapData[ 10 ] = ( color3.b * 255 ) | 0;
_pixelMapData[ 12 ] = ( color4.r * 255 ) | 0;
_pixelMapData[ 13 ] = ( color4.g * 255 ) | 0;
_pixelMapData[ 14 ] = ( color4.b * 255 ) | 0;
_pixelMapContext.putImageData( _pixelMapImage, 0, 0 );
_gradientMapContext.drawImage( _pixelMap, 0, 0 );
return _gradientMap;
}
// Hide anti-alias gaps
function expand( v1, v2 ) {
var x = v2.x - v1.x, y = v2.y - v1.y,
det = x * x + y * y, idet;
if ( det === 0 ) return;
idet = 1 / Math.sqrt( det );
x *= idet; y *= idet;
v2.x += x; v2.y += y;
v1.x -= x; v1.y -= y;
}
};
// Context cached methods.
function setOpacity( value ) {
if ( _contextGlobalAlpha !== value ) {
_context.globalAlpha = value;
_contextGlobalAlpha = value;
}
}
function setBlending( value ) {
if ( _contextGlobalCompositeOperation !== value ) {
if ( value === THREE.NormalBlending ) {
_context.globalCompositeOperation = 'source-over';
} else if ( value === THREE.AdditiveBlending ) {
_context.globalCompositeOperation = 'lighter';
} else if ( value === THREE.SubtractiveBlending ) {
_context.globalCompositeOperation = 'darker';
}
_contextGlobalCompositeOperation = value;
}
}
function setLineWidth( value ) {
if ( _contextLineWidth !== value ) {
_context.lineWidth = value;
_contextLineWidth = value;
}
}
function setLineCap( value ) {
// "butt", "round", "square"
if ( _contextLineCap !== value ) {
_context.lineCap = value;
_contextLineCap = value;
}
}
function setLineJoin( value ) {
// "round", "bevel", "miter"
if ( _contextLineJoin !== value ) {
_context.lineJoin = value;
_contextLineJoin = value;
}
}
function setStrokeStyle( value ) {
if ( _contextStrokeStyle !== value ) {
_context.strokeStyle = value;
_contextStrokeStyle = value;
}
}
function setFillStyle( value ) {
if ( _contextFillStyle !== value ) {
_context.fillStyle = value;
_contextFillStyle = value;
}
}
function setDashAndGap( dashSizeValue, gapSizeValue ) {
if ( _contextDashSize !== dashSizeValue || _contextGapSize !== gapSizeValue ) {
_context.setLineDash( [ dashSizeValue, gapSizeValue ] );
_contextDashSize = dashSizeValue;
_contextGapSize = gapSizeValue;
}
}
};
/**
* @author alteredq / http://alteredqualia.com/
* @author mrdoob / http://mrdoob.com/
* @author mikael emtinger / http://gomo.se/
*/
THREE.ShaderChunk = {
// FOG
fog_pars_fragment: [
"#ifdef USE_FOG",
"uniform vec3 fogColor;",
"#ifdef FOG_EXP2",
"uniform float fogDensity;",
"#else",
"uniform float fogNear;",
"uniform float fogFar;",
"#endif",
"#endif"
].join("\n"),
fog_fragment: [
"#ifdef USE_FOG",
"float depth = gl_FragCoord.z / gl_FragCoord.w;",
"#ifdef FOG_EXP2",
"const float LOG2 = 1.442695;",
"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
"#else",
"float fogFactor = smoothstep( fogNear, fogFar, depth );",
"#endif",
"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
"#endif"
].join("\n"),
// ENVIRONMENT MAP
envmap_pars_fragment: [
"#ifdef USE_ENVMAP",
"uniform float reflectivity;",
"uniform samplerCube envMap;",
"uniform float flipEnvMap;",
"uniform int combine;",
"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
"uniform bool useRefract;",
"uniform float refractionRatio;",
"#else",
"varying vec3 vReflect;",
"#endif",
"#endif"
].join("\n"),
envmap_fragment: [
"#ifdef USE_ENVMAP",
"vec3 reflectVec;",
"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
"if ( useRefract ) {",
"reflectVec = refract( cameraToVertex, normal, refractionRatio );",
"} else { ",
"reflectVec = reflect( cameraToVertex, normal );",
"}",
"#else",
"reflectVec = vReflect;",
"#endif",
"#ifdef DOUBLE_SIDED",
"float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
"vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
"#else",
"vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
"#endif",
"#ifdef GAMMA_INPUT",
"cubeColor.xyz *= cubeColor.xyz;",
"#endif",
"if ( combine == 1 ) {",
"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );",
"} else if ( combine == 2 ) {",
"gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;",
"} else {",
"gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );",
"}",
"#endif"
].join("\n"),
envmap_pars_vertex: [
"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
"varying vec3 vReflect;",
"uniform float refractionRatio;",
"uniform bool useRefract;",
"#endif"
].join("\n"),
worldpos_vertex : [
"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )",
"#ifdef USE_SKINNING",
"vec4 worldPosition = modelMatrix * skinned;",
"#endif",
"#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
"vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );",
"#endif",
"#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
"#endif",
"#endif"
].join("\n"),
envmap_vertex : [
"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
"vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;",
"worldNormal = normalize( worldNormal );",
"vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );",
"if ( useRefract ) {",
"vReflect = refract( cameraToVertex, worldNormal, refractionRatio );",
"} else {",
"vReflect = reflect( cameraToVertex, worldNormal );",
"}",
"#endif"
].join("\n"),
// COLOR MAP (particles)
map_particle_pars_fragment: [
"#ifdef USE_MAP",
"uniform sampler2D map;",
"#endif"
].join("\n"),
map_particle_fragment: [
"#ifdef USE_MAP",
"gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );",
"#endif"
].join("\n"),
// COLOR MAP (triangles)
map_pars_vertex: [
"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
"varying vec2 vUv;",
"uniform vec4 offsetRepeat;",
"#endif"
].join("\n"),
map_pars_fragment: [
"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
"varying vec2 vUv;",
"#endif",
"#ifdef USE_MAP",
"uniform sampler2D map;",
"#endif"
].join("\n"),
map_vertex: [
"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
"#endif"
].join("\n"),
map_fragment: [
"#ifdef USE_MAP",
"vec4 texelColor = texture2D( map, vUv );",
"#ifdef GAMMA_INPUT",
"texelColor.xyz *= texelColor.xyz;",
"#endif",
"gl_FragColor = gl_FragColor * texelColor;",
"#endif"
].join("\n"),
// LIGHT MAP
lightmap_pars_fragment: [
"#ifdef USE_LIGHTMAP",
"varying vec2 vUv2;",
"uniform sampler2D lightMap;",
"#endif"
].join("\n"),
lightmap_pars_vertex: [
"#ifdef USE_LIGHTMAP",
"varying vec2 vUv2;",
"#endif"
].join("\n"),
lightmap_fragment: [
"#ifdef USE_LIGHTMAP",
"gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );",
"#endif"
].join("\n"),
lightmap_vertex: [
"#ifdef USE_LIGHTMAP",
"vUv2 = uv2;",
"#endif"
].join("\n"),
// BUMP MAP
bumpmap_pars_fragment: [
"#ifdef USE_BUMPMAP",
"uniform sampler2D bumpMap;",
"uniform float bumpScale;",
// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen
// http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html
// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
"vec2 dHdxy_fwd() {",
"vec2 dSTdx = dFdx( vUv );",
"vec2 dSTdy = dFdy( vUv );",
"float Hll = bumpScale * texture2D( bumpMap, vUv ).x;",
"float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;",
"float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;",
"return vec2( dBx, dBy );",
"}",
"vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {",
"vec3 vSigmaX = dFdx( surf_pos );",
"vec3 vSigmaY = dFdy( surf_pos );",
"vec3 vN = surf_norm;", // normalized
"vec3 R1 = cross( vSigmaY, vN );",
"vec3 R2 = cross( vN, vSigmaX );",
"float fDet = dot( vSigmaX, R1 );",
"vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );",
"return normalize( abs( fDet ) * surf_norm - vGrad );",
"}",
"#endif"
].join("\n"),
// NORMAL MAP
normalmap_pars_fragment: [
"#ifdef USE_NORMALMAP",
"uniform sampler2D normalMap;",
"uniform vec2 normalScale;",
// Per-Pixel Tangent Space Normal Mapping
// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {",
"vec3 q0 = dFdx( eye_pos.xyz );",
"vec3 q1 = dFdy( eye_pos.xyz );",
"vec2 st0 = dFdx( vUv.st );",
"vec2 st1 = dFdy( vUv.st );",
"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
"vec3 N = normalize( surf_norm );",
"vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;",
"mapN.xy = normalScale * mapN.xy;",
"mat3 tsn = mat3( S, T, N );",
"return normalize( tsn * mapN );",
"}",
"#endif"
].join("\n"),
// SPECULAR MAP
specularmap_pars_fragment: [
"#ifdef USE_SPECULARMAP",
"uniform sampler2D specularMap;",
"#endif"
].join("\n"),
specularmap_fragment: [
"float specularStrength;",
"#ifdef USE_SPECULARMAP",
"vec4 texelSpecular = texture2D( specularMap, vUv );",
"specularStrength = texelSpecular.r;",
"#else",
"specularStrength = 1.0;",
"#endif"
].join("\n"),
// LIGHTS LAMBERT
lights_lambert_pars_vertex: [
"uniform vec3 ambient;",
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform vec3 ambientLightColor;",
"#if MAX_DIR_LIGHTS > 0",
"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
"#endif",
"#ifdef WRAP_AROUND",
"uniform vec3 wrapRGB;",
"#endif"
].join("\n"),
lights_lambert_vertex: [
"vLightFront = vec3( 0.0 );",
"#ifdef DOUBLE_SIDED",
"vLightBack = vec3( 0.0 );",
"#endif",
"transformedNormal = normalize( transformedNormal );",
"#if MAX_DIR_LIGHTS > 0",
"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
"vec3 dirVector = normalize( lDirection.xyz );",
"float dotProduct = dot( transformedNormal, dirVector );",
"vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
"#ifdef DOUBLE_SIDED",
"vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
"#ifdef WRAP_AROUND",
"vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
"#endif",
"#endif",
"#ifdef WRAP_AROUND",
"vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
"directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );",
"#ifdef DOUBLE_SIDED",
"directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );",
"#endif",
"#endif",
"vLightFront += directionalLightColor[ i ] * directionalLightWeighting;",
"#ifdef DOUBLE_SIDED",
"vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;",
"#endif",
"}",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
"float lDistance = 1.0;",
"if ( pointLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
"lVector = normalize( lVector );",
"float dotProduct = dot( transformedNormal, lVector );",
"vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );",
"#ifdef DOUBLE_SIDED",
"vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
"#ifdef WRAP_AROUND",
"vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
"#endif",
"#endif",
"#ifdef WRAP_AROUND",
"vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
"pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );",
"#ifdef DOUBLE_SIDED",
"pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );",
"#endif",
"#endif",
"vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;",
"#ifdef DOUBLE_SIDED",
"vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;",
"#endif",
"}",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );",
"if ( spotEffect > spotLightAngleCos[ i ] ) {",
"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
"float lDistance = 1.0;",
"if ( spotLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
"lVector = normalize( lVector );",
"float dotProduct = dot( transformedNormal, lVector );",
"vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );",
"#ifdef DOUBLE_SIDED",
"vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
"#ifdef WRAP_AROUND",
"vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
"#endif",
"#endif",
"#ifdef WRAP_AROUND",
"vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
"spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );",
"#ifdef DOUBLE_SIDED",
"spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );",
"#endif",
"#endif",
"vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;",
"#ifdef DOUBLE_SIDED",
"vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;",
"#endif",
"}",
"}",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
"vec3 lVector = normalize( lDirection.xyz );",
"float dotProduct = dot( transformedNormal, lVector );",
"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
"float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;",
"vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
"#ifdef DOUBLE_SIDED",
"vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );",
"#endif",
"}",
"#endif",
"vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
"#ifdef DOUBLE_SIDED",
"vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;",
"#endif"
].join("\n"),
// LIGHTS PHONG
lights_phong_pars_vertex: [
"#ifndef PHONG_PER_PIXEL",
"#if MAX_POINT_LIGHTS > 0",
"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
"#endif",
"#endif",
"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
"varying vec3 vWorldPosition;",
"#endif"
].join("\n"),
lights_phong_vertex: [
"#ifndef PHONG_PER_PIXEL",
"#if MAX_POINT_LIGHTS > 0",
"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
"float lDistance = 1.0;",
"if ( pointLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
"vPointLight[ i ] = vec4( lVector, lDistance );",
"}",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
"float lDistance = 1.0;",
"if ( spotLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
"vSpotLight[ i ] = vec4( lVector, lDistance );",
"}",
"#endif",
"#endif",
"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
"vWorldPosition = worldPosition.xyz;",
"#endif"
].join("\n"),
lights_phong_pars_fragment: [
"uniform vec3 ambientLightColor;",
"#if MAX_DIR_LIGHTS > 0",
"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
"#ifdef PHONG_PER_PIXEL",
"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
"#else",
"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
"#endif",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
"#ifdef PHONG_PER_PIXEL",
"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
"#else",
"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
"#endif",
"#endif",
"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
"varying vec3 vWorldPosition;",
"#endif",
"#ifdef WRAP_AROUND",
"uniform vec3 wrapRGB;",
"#endif",
"varying vec3 vViewPosition;",
"varying vec3 vNormal;"
].join("\n"),
lights_phong_fragment: [
"vec3 normal = normalize( vNormal );",
"vec3 viewPosition = normalize( vViewPosition );",
"#ifdef DOUBLE_SIDED",
"normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
"#endif",
"#ifdef USE_NORMALMAP",
"normal = perturbNormal2Arb( -viewPosition, normal );",
"#elif defined( USE_BUMPMAP )",
"normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"vec3 pointDiffuse = vec3( 0.0 );",
"vec3 pointSpecular = vec3( 0.0 );",
"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
"#ifdef PHONG_PER_PIXEL",
"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
"float lDistance = 1.0;",
"if ( pointLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
"lVector = normalize( lVector );",
"#else",
"vec3 lVector = normalize( vPointLight[ i ].xyz );",
"float lDistance = vPointLight[ i ].w;",
"#endif",
// diffuse
"float dotProduct = dot( normal, lVector );",
"#ifdef WRAP_AROUND",
"float pointDiffuseWeightFull = max( dotProduct, 0.0 );",
"float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
"#else",
"float pointDiffuseWeight = max( dotProduct, 0.0 );",
"#endif",
"pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;",
// specular
"vec3 pointHalfVector = normalize( lVector + viewPosition );",
"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
"float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );",
"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;",
"#else",
"pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;",
"#endif",
"}",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"vec3 spotDiffuse = vec3( 0.0 );",
"vec3 spotSpecular = vec3( 0.0 );",
"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
"#ifdef PHONG_PER_PIXEL",
"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
"float lDistance = 1.0;",
"if ( spotLightDistance[ i ] > 0.0 )",
"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
"lVector = normalize( lVector );",
"#else",
"vec3 lVector = normalize( vSpotLight[ i ].xyz );",
"float lDistance = vSpotLight[ i ].w;",
"#endif",
"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
"if ( spotEffect > spotLightAngleCos[ i ] ) {",
"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
// diffuse
"float dotProduct = dot( normal, lVector );",
"#ifdef WRAP_AROUND",
"float spotDiffuseWeightFull = max( dotProduct, 0.0 );",
"float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
"#else",
"float spotDiffuseWeight = max( dotProduct, 0.0 );",
"#endif",
"spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;",
// specular
"vec3 spotHalfVector = normalize( lVector + viewPosition );",
"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
"float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );",
"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;",
"#else",
"spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;",
"#endif",
"}",
"}",
"#endif",
"#if MAX_DIR_LIGHTS > 0",
"vec3 dirDiffuse = vec3( 0.0 );",
"vec3 dirSpecular = vec3( 0.0 );" ,
"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
"vec3 dirVector = normalize( lDirection.xyz );",
// diffuse
"float dotProduct = dot( normal, dirVector );",
"#ifdef WRAP_AROUND",
"float dirDiffuseWeightFull = max( dotProduct, 0.0 );",
"float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
"vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );",
"#else",
"float dirDiffuseWeight = max( dotProduct, 0.0 );",
"#endif",
"dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;",
// specular
"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
"float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
/*
// fresnel term from skin shader
"const float F0 = 0.128;",
"float base = 1.0 - dot( viewPosition, dirHalfVector );",
"float exponential = pow( base, 5.0 );",
"float fresnel = exponential + F0 * ( 1.0 - exponential );",
*/
/*
// fresnel term from fresnel shader
"const float mFresnelBias = 0.08;",
"const float mFresnelScale = 0.3;",
"const float mFresnelPower = 5.0;",
"float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );",
*/
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
//"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;",
"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
"#else",
"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;",
"#endif",
"}",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"vec3 hemiDiffuse = vec3( 0.0 );",
"vec3 hemiSpecular = vec3( 0.0 );" ,
"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
"vec3 lVector = normalize( lDirection.xyz );",
// diffuse
"float dotProduct = dot( normal, lVector );",
"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
"hemiDiffuse += diffuse * hemiColor;",
// specular (sky light)
"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
"float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
// specular (ground light)
"vec3 lVectorGround = -lVector;",
"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
"float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
"float dotProductGround = dot( normal, lVectorGround );",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
"vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
"vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
"#else",
"hemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
"#endif",
"}",
"#endif",
"vec3 totalDiffuse = vec3( 0.0 );",
"vec3 totalSpecular = vec3( 0.0 );",
"#if MAX_DIR_LIGHTS > 0",
"totalDiffuse += dirDiffuse;",
"totalSpecular += dirSpecular;",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"totalDiffuse += hemiDiffuse;",
"totalSpecular += hemiSpecular;",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"totalDiffuse += pointDiffuse;",
"totalSpecular += pointSpecular;",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"totalDiffuse += spotDiffuse;",
"totalSpecular += spotSpecular;",
"#endif",
"#ifdef METAL",
"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );",
"#else",
"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;",
"#endif"
].join("\n"),
// VERTEX COLORS
color_pars_fragment: [
"#ifdef USE_COLOR",
"varying vec3 vColor;",
"#endif"
].join("\n"),
color_fragment: [
"#ifdef USE_COLOR",
"gl_FragColor = gl_FragColor * vec4( vColor, opacity );",
"#endif"
].join("\n"),
color_pars_vertex: [
"#ifdef USE_COLOR",
"varying vec3 vColor;",
"#endif"
].join("\n"),
color_vertex: [
"#ifdef USE_COLOR",
"#ifdef GAMMA_INPUT",
"vColor = color * color;",
"#else",
"vColor = color;",
"#endif",
"#endif"
].join("\n"),
// SKINNING
skinning_pars_vertex: [
"#ifdef USE_SKINNING",
"#ifdef BONE_TEXTURE",
"uniform sampler2D boneTexture;",
"mat4 getBoneMatrix( const in float i ) {",
"float j = i * 4.0;",
"float x = mod( j, N_BONE_PIXEL_X );",
"float y = floor( j / N_BONE_PIXEL_X );",
"const float dx = 1.0 / N_BONE_PIXEL_X;",
"const float dy = 1.0 / N_BONE_PIXEL_Y;",
"y = dy * ( y + 0.5 );",
"vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );",
"vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );",
"vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );",
"vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );",
"mat4 bone = mat4( v1, v2, v3, v4 );",
"return bone;",
"}",
"#else",
"uniform mat4 boneGlobalMatrices[ MAX_BONES ];",
"mat4 getBoneMatrix( const in float i ) {",
"mat4 bone = boneGlobalMatrices[ int(i) ];",
"return bone;",
"}",
"#endif",
"#endif"
].join("\n"),
skinbase_vertex: [
"#ifdef USE_SKINNING",
"mat4 boneMatX = getBoneMatrix( skinIndex.x );",
"mat4 boneMatY = getBoneMatrix( skinIndex.y );",
"#endif"
].join("\n"),
skinning_vertex: [
"#ifdef USE_SKINNING",
"#ifdef USE_MORPHTARGETS",
"vec4 skinVertex = vec4( morphed, 1.0 );",
"#else",
"vec4 skinVertex = vec4( position, 1.0 );",
"#endif",
"vec4 skinned = boneMatX * skinVertex * skinWeight.x;",
"skinned += boneMatY * skinVertex * skinWeight.y;",
"#endif"
].join("\n"),
// MORPHING
morphtarget_pars_vertex: [
"#ifdef USE_MORPHTARGETS",
"#ifndef USE_MORPHNORMALS",
"uniform float morphTargetInfluences[ 8 ];",
"#else",
"uniform float morphTargetInfluences[ 4 ];",
"#endif",
"#endif"
].join("\n"),
morphtarget_vertex: [
"#ifdef USE_MORPHTARGETS",
"vec3 morphed = vec3( 0.0 );",
"morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];",
"morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];",
"morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];",
"morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];",
"#ifndef USE_MORPHNORMALS",
"morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];",
"morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];",
"morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];",
"morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];",
"#endif",
"morphed += position;",
"#endif"
].join("\n"),
default_vertex : [
"vec4 mvPosition;",
"#ifdef USE_SKINNING",
"mvPosition = modelViewMatrix * skinned;",
"#endif",
"#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )",
"mvPosition = modelViewMatrix * vec4( morphed, 1.0 );",
"#endif",
"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )",
"mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"#endif",
"gl_Position = projectionMatrix * mvPosition;"
].join("\n"),
morphnormal_vertex: [
"#ifdef USE_MORPHNORMALS",
"vec3 morphedNormal = vec3( 0.0 );",
"morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];",
"morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];",
"morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];",
"morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];",
"morphedNormal += normal;",
"#endif"
].join("\n"),
skinnormal_vertex: [
"#ifdef USE_SKINNING",
"mat4 skinMatrix = skinWeight.x * boneMatX;",
"skinMatrix += skinWeight.y * boneMatY;",
"#ifdef USE_MORPHNORMALS",
"vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );",
"#else",
"vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );",
"#endif",
"#endif"
].join("\n"),
defaultnormal_vertex: [
"vec3 objectNormal;",
"#ifdef USE_SKINNING",
"objectNormal = skinnedNormal.xyz;",
"#endif",
"#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )",
"objectNormal = morphedNormal;",
"#endif",
"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )",
"objectNormal = normal;",
"#endif",
"#ifdef FLIP_SIDED",
"objectNormal = -objectNormal;",
"#endif",
"vec3 transformedNormal = normalMatrix * objectNormal;"
].join("\n"),
// SHADOW MAP
// based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples
// http://spidergl.org/example.php?id=6
// http://fabiensanglard.net/shadowmapping
shadowmap_pars_fragment: [
"#ifdef USE_SHADOWMAP",
"uniform sampler2D shadowMap[ MAX_SHADOWS ];",
"uniform vec2 shadowMapSize[ MAX_SHADOWS ];",
"uniform float shadowDarkness[ MAX_SHADOWS ];",
"uniform float shadowBias[ MAX_SHADOWS ];",
"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
"float unpackDepth( const in vec4 rgba_depth ) {",
"const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
"float depth = dot( rgba_depth, bit_shift );",
"return depth;",
"}",
"#endif"
].join("\n"),
shadowmap_fragment: [
"#ifdef USE_SHADOWMAP",
"#ifdef SHADOWMAP_DEBUG",
"vec3 frustumColors[3];",
"frustumColors[0] = vec3( 1.0, 0.5, 0.0 );",
"frustumColors[1] = vec3( 0.0, 1.0, 0.8 );",
"frustumColors[2] = vec3( 0.0, 0.5, 1.0 );",
"#endif",
"#ifdef SHADOWMAP_CASCADE",
"int inFrustumCount = 0;",
"#endif",
"float fDepth;",
"vec3 shadowColor = vec3( 1.0 );",
"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
"vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;",
// "if ( something && something )" breaks ATI OpenGL shader compiler
// "if ( all( something, something ) )" using this instead
"bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );",
"bool inFrustum = all( inFrustumVec );",
// don't shadow pixels outside of light frustum
// use just first frustum (for cascades)
// don't shadow pixels behind far plane of light frustum
"#ifdef SHADOWMAP_CASCADE",
"inFrustumCount += int( inFrustum );",
"bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );",
"#else",
"bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );",
"#endif",
"bool frustumTest = all( frustumTestVec );",
"if ( frustumTest ) {",
"shadowCoord.z += shadowBias[ i ];",
"#if defined( SHADOWMAP_TYPE_PCF )",
// Percentage-close filtering
// (9 pixel kernel)
// http://fabiensanglard.net/shadowmappingPCF/
"float shadow = 0.0;",
/*
// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
// must enroll loop manually
"for ( float y = -1.25; y <= 1.25; y += 1.25 )",
"for ( float x = -1.25; x <= 1.25; x += 1.25 ) {",
"vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );",
// doesn't seem to produce any noticeable visual difference compared to simple "texture2D" lookup
//"vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );",
"float fDepth = unpackDepth( rgbaDepth );",
"if ( fDepth < shadowCoord.z )",
"shadow += 1.0;",
"}",
"shadow /= 9.0;",
*/
"const float shadowDelta = 1.0 / 9.0;",
"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
"float dx0 = -1.25 * xPixelOffset;",
"float dy0 = -1.25 * yPixelOffset;",
"float dx1 = 1.25 * xPixelOffset;",
"float dy1 = 1.25 * yPixelOffset;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
"#elif defined( SHADOWMAP_TYPE_PCF_SOFT )",
// Percentage-close filtering
// (9 pixel kernel)
// http://fabiensanglard.net/shadowmappingPCF/
"float shadow = 0.0;",
"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
"float dx0 = -1.0 * xPixelOffset;",
"float dy0 = -1.0 * yPixelOffset;",
"float dx1 = 1.0 * xPixelOffset;",
"float dy1 = 1.0 * yPixelOffset;",
"mat3 shadowKernel;",
"mat3 depthKernel;",
"depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
"if ( depthKernel[0][0] < shadowCoord.z ) shadowKernel[0][0] = 0.25;",
"else shadowKernel[0][0] = 0.0;",
"depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
"if ( depthKernel[0][1] < shadowCoord.z ) shadowKernel[0][1] = 0.25;",
"else shadowKernel[0][1] = 0.0;",
"depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
"if ( depthKernel[0][2] < shadowCoord.z ) shadowKernel[0][2] = 0.25;",
"else shadowKernel[0][2] = 0.0;",
"depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
"if ( depthKernel[1][0] < shadowCoord.z ) shadowKernel[1][0] = 0.25;",
"else shadowKernel[1][0] = 0.0;",
"depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
"if ( depthKernel[1][1] < shadowCoord.z ) shadowKernel[1][1] = 0.25;",
"else shadowKernel[1][1] = 0.0;",
"depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
"if ( depthKernel[1][2] < shadowCoord.z ) shadowKernel[1][2] = 0.25;",
"else shadowKernel[1][2] = 0.0;",
"depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
"if ( depthKernel[2][0] < shadowCoord.z ) shadowKernel[2][0] = 0.25;",
"else shadowKernel[2][0] = 0.0;",
"depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
"if ( depthKernel[2][1] < shadowCoord.z ) shadowKernel[2][1] = 0.25;",
"else shadowKernel[2][1] = 0.0;",
"depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
"if ( depthKernel[2][2] < shadowCoord.z ) shadowKernel[2][2] = 0.25;",
"else shadowKernel[2][2] = 0.0;",
"vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );",
"shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );",
"shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );",
"vec4 shadowValues;",
"shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );",
"shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );",
"shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );",
"shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );",
"shadow = dot( shadowValues, vec4( 1.0 ) );",
"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
"#else",
"vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );",
"float fDepth = unpackDepth( rgbaDepth );",
"if ( fDepth < shadowCoord.z )",
// spot with multiple shadows is darker
"shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );",
// spot with multiple shadows has the same color as single shadow spot
//"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );",
"#endif",
"}",
"#ifdef SHADOWMAP_DEBUG",
"#ifdef SHADOWMAP_CASCADE",
"if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];",
"#else",
"if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];",
"#endif",
"#endif",
"}",
"#ifdef GAMMA_OUTPUT",
"shadowColor *= shadowColor;",
"#endif",
"gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;",
"#endif"
].join("\n"),
shadowmap_pars_vertex: [
"#ifdef USE_SHADOWMAP",
"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
"uniform mat4 shadowMatrix[ MAX_SHADOWS ];",
"#endif"
].join("\n"),
shadowmap_vertex: [
"#ifdef USE_SHADOWMAP",
"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
"}",
"#endif"
].join("\n"),
// ALPHATEST
alphatest_fragment: [
"#ifdef ALPHATEST",
"if ( gl_FragColor.a < ALPHATEST ) discard;",
"#endif"
].join("\n"),
// LINEAR SPACE
linear_to_gamma_fragment: [
"#ifdef GAMMA_OUTPUT",
"gl_FragColor.xyz = sqrt( gl_FragColor.xyz );",
"#endif"
].join("\n")
};
THREE.UniformsUtils = {
merge: function ( uniforms ) {
var u, p, tmp, merged = {};
for ( u = 0; u < uniforms.length; u ++ ) {
tmp = this.clone( uniforms[ u ] );
for ( p in tmp ) {
merged[ p ] = tmp[ p ];
}
}
return merged;
},
clone: function ( uniforms_src ) {
var u, p, parameter, parameter_src, uniforms_dst = {};
for ( u in uniforms_src ) {
uniforms_dst[ u ] = {};
for ( p in uniforms_src[ u ] ) {
parameter_src = uniforms_src[ u ][ p ];
if ( parameter_src instanceof THREE.Color ||
parameter_src instanceof THREE.Vector2 ||
parameter_src instanceof THREE.Vector3 ||
parameter_src instanceof THREE.Vector4 ||
parameter_src instanceof THREE.Matrix4 ||
parameter_src instanceof THREE.Texture ) {
uniforms_dst[ u ][ p ] = parameter_src.clone();
} else if ( parameter_src instanceof Array ) {
uniforms_dst[ u ][ p ] = parameter_src.slice();
} else {
uniforms_dst[ u ][ p ] = parameter_src;
}
}
}
return uniforms_dst;
}
};
THREE.UniformsLib = {
common: {
"diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
"opacity" : { type: "f", value: 1.0 },
"map" : { type: "t", value: null },
"offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
"lightMap" : { type: "t", value: null },
"specularMap" : { type: "t", value: null },
"envMap" : { type: "t", value: null },
"flipEnvMap" : { type: "f", value: -1 },
"useRefract" : { type: "i", value: 0 },
"reflectivity" : { type: "f", value: 1.0 },
"refractionRatio" : { type: "f", value: 0.98 },
"combine" : { type: "i", value: 0 },
"morphTargetInfluences" : { type: "f", value: 0 }
},
bump: {
"bumpMap" : { type: "t", value: null },
"bumpScale" : { type: "f", value: 1 }
},
normalmap: {
"normalMap" : { type: "t", value: null },
"normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }
},
fog : {
"fogDensity" : { type: "f", value: 0.00025 },
"fogNear" : { type: "f", value: 1 },
"fogFar" : { type: "f", value: 2000 },
"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
},
lights: {
"ambientLightColor" : { type: "fv", value: [] },
"directionalLightDirection" : { type: "fv", value: [] },
"directionalLightColor" : { type: "fv", value: [] },
"hemisphereLightDirection" : { type: "fv", value: [] },
"hemisphereLightSkyColor" : { type: "fv", value: [] },
"hemisphereLightGroundColor" : { type: "fv", value: [] },
"pointLightColor" : { type: "fv", value: [] },
"pointLightPosition" : { type: "fv", value: [] },
"pointLightDistance" : { type: "fv1", value: [] },
"spotLightColor" : { type: "fv", value: [] },
"spotLightPosition" : { type: "fv", value: [] },
"spotLightDirection" : { type: "fv", value: [] },
"spotLightDistance" : { type: "fv1", value: [] },
"spotLightAngleCos" : { type: "fv1", value: [] },
"spotLightExponent" : { type: "fv1", value: [] }
},
particle: {
"psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
"opacity" : { type: "f", value: 1.0 },
"size" : { type: "f", value: 1.0 },
"scale" : { type: "f", value: 1.0 },
"map" : { type: "t", value: null },
"fogDensity" : { type: "f", value: 0.00025 },
"fogNear" : { type: "f", value: 1 },
"fogFar" : { type: "f", value: 2000 },
"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
},
shadowmap: {
"shadowMap": { type: "tv", value: [] },
"shadowMapSize": { type: "v2v", value: [] },
"shadowBias" : { type: "fv1", value: [] },
"shadowDarkness": { type: "fv1", value: [] },
"shadowMatrix" : { type: "m4v", value: [] }
}
};
THREE.ShaderLib = {
'basic': {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "common" ],
THREE.UniformsLib[ "fog" ],
THREE.UniformsLib[ "shadowmap" ]
] ),
vertexShader: [
THREE.ShaderChunk[ "map_pars_vertex" ],
THREE.ShaderChunk[ "lightmap_pars_vertex" ],
THREE.ShaderChunk[ "envmap_pars_vertex" ],
THREE.ShaderChunk[ "color_pars_vertex" ],
THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
THREE.ShaderChunk[ "skinning_pars_vertex" ],
THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "map_vertex" ],
THREE.ShaderChunk[ "lightmap_vertex" ],
THREE.ShaderChunk[ "color_vertex" ],
THREE.ShaderChunk[ "skinbase_vertex" ],
"#ifdef USE_ENVMAP",
THREE.ShaderChunk[ "morphnormal_vertex" ],
THREE.ShaderChunk[ "skinnormal_vertex" ],
THREE.ShaderChunk[ "defaultnormal_vertex" ],
"#endif",
THREE.ShaderChunk[ "morphtarget_vertex" ],
THREE.ShaderChunk[ "skinning_vertex" ],
THREE.ShaderChunk[ "default_vertex" ],
THREE.ShaderChunk[ "worldpos_vertex" ],
THREE.ShaderChunk[ "envmap_vertex" ],
THREE.ShaderChunk[ "shadowmap_vertex" ],
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 diffuse;",
"uniform float opacity;",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
THREE.ShaderChunk[ "lightmap_pars_fragment" ],
THREE.ShaderChunk[ "envmap_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
THREE.ShaderChunk[ "specularmap_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( diffuse, opacity );",
THREE.ShaderChunk[ "map_fragment" ],
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
THREE.ShaderChunk[ "lightmap_fragment" ],
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "envmap_fragment" ],
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
},
'lambert': {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "common" ],
THREE.UniformsLib[ "fog" ],
THREE.UniformsLib[ "lights" ],
THREE.UniformsLib[ "shadowmap" ],
{
"ambient" : { type: "c", value: new THREE.Color( 0xffffff ) },
"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
"wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
}
] ),
vertexShader: [
"#define LAMBERT",
"varying vec3 vLightFront;",
"#ifdef DOUBLE_SIDED",
"varying vec3 vLightBack;",
"#endif",
THREE.ShaderChunk[ "map_pars_vertex" ],
THREE.ShaderChunk[ "lightmap_pars_vertex" ],
THREE.ShaderChunk[ "envmap_pars_vertex" ],
THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
THREE.ShaderChunk[ "color_pars_vertex" ],
THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
THREE.ShaderChunk[ "skinning_pars_vertex" ],
THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "map_vertex" ],
THREE.ShaderChunk[ "lightmap_vertex" ],
THREE.ShaderChunk[ "color_vertex" ],
THREE.ShaderChunk[ "morphnormal_vertex" ],
THREE.ShaderChunk[ "skinbase_vertex" ],
THREE.ShaderChunk[ "skinnormal_vertex" ],
THREE.ShaderChunk[ "defaultnormal_vertex" ],
THREE.ShaderChunk[ "morphtarget_vertex" ],
THREE.ShaderChunk[ "skinning_vertex" ],
THREE.ShaderChunk[ "default_vertex" ],
THREE.ShaderChunk[ "worldpos_vertex" ],
THREE.ShaderChunk[ "envmap_vertex" ],
THREE.ShaderChunk[ "lights_lambert_vertex" ],
THREE.ShaderChunk[ "shadowmap_vertex" ],
"}"
].join("\n"),
fragmentShader: [
"uniform float opacity;",
"varying vec3 vLightFront;",
"#ifdef DOUBLE_SIDED",
"varying vec3 vLightBack;",
"#endif",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
THREE.ShaderChunk[ "lightmap_pars_fragment" ],
THREE.ShaderChunk[ "envmap_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
THREE.ShaderChunk[ "specularmap_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
THREE.ShaderChunk[ "map_fragment" ],
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
"#ifdef DOUBLE_SIDED",
//"float isFront = float( gl_FrontFacing );",
//"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;",
"if ( gl_FrontFacing )",
"gl_FragColor.xyz *= vLightFront;",
"else",
"gl_FragColor.xyz *= vLightBack;",
"#else",
"gl_FragColor.xyz *= vLightFront;",
"#endif",
THREE.ShaderChunk[ "lightmap_fragment" ],
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "envmap_fragment" ],
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
},
'phong': {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "common" ],
THREE.UniformsLib[ "bump" ],
THREE.UniformsLib[ "normalmap" ],
THREE.UniformsLib[ "fog" ],
THREE.UniformsLib[ "lights" ],
THREE.UniformsLib[ "shadowmap" ],
{
"ambient" : { type: "c", value: new THREE.Color( 0xffffff ) },
"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
"specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
"shininess": { type: "f", value: 30 },
"wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
}
] ),
vertexShader: [
"#define PHONG",
"varying vec3 vViewPosition;",
"varying vec3 vNormal;",
THREE.ShaderChunk[ "map_pars_vertex" ],
THREE.ShaderChunk[ "lightmap_pars_vertex" ],
THREE.ShaderChunk[ "envmap_pars_vertex" ],
THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
THREE.ShaderChunk[ "color_pars_vertex" ],
THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
THREE.ShaderChunk[ "skinning_pars_vertex" ],
THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "map_vertex" ],
THREE.ShaderChunk[ "lightmap_vertex" ],
THREE.ShaderChunk[ "color_vertex" ],
THREE.ShaderChunk[ "morphnormal_vertex" ],
THREE.ShaderChunk[ "skinbase_vertex" ],
THREE.ShaderChunk[ "skinnormal_vertex" ],
THREE.ShaderChunk[ "defaultnormal_vertex" ],
"vNormal = normalize( transformedNormal );",
THREE.ShaderChunk[ "morphtarget_vertex" ],
THREE.ShaderChunk[ "skinning_vertex" ],
THREE.ShaderChunk[ "default_vertex" ],
"vViewPosition = -mvPosition.xyz;",
THREE.ShaderChunk[ "worldpos_vertex" ],
THREE.ShaderChunk[ "envmap_vertex" ],
THREE.ShaderChunk[ "lights_phong_vertex" ],
THREE.ShaderChunk[ "shadowmap_vertex" ],
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 diffuse;",
"uniform float opacity;",
"uniform vec3 ambient;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
THREE.ShaderChunk[ "lightmap_pars_fragment" ],
THREE.ShaderChunk[ "envmap_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
THREE.ShaderChunk[ "normalmap_pars_fragment" ],
THREE.ShaderChunk[ "specularmap_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
THREE.ShaderChunk[ "map_fragment" ],
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
THREE.ShaderChunk[ "lights_phong_fragment" ],
THREE.ShaderChunk[ "lightmap_fragment" ],
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "envmap_fragment" ],
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
},
'particle_basic': {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "particle" ],
THREE.UniformsLib[ "shadowmap" ]
] ),
vertexShader: [
"uniform float size;",
"uniform float scale;",
THREE.ShaderChunk[ "color_pars_vertex" ],
THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "color_vertex" ],
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"#ifdef USE_SIZEATTENUATION",
"gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
"#else",
"gl_PointSize = size;",
"#endif",
"gl_Position = projectionMatrix * mvPosition;",
THREE.ShaderChunk[ "worldpos_vertex" ],
THREE.ShaderChunk[ "shadowmap_vertex" ],
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 psColor;",
"uniform float opacity;",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "map_particle_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( psColor, opacity );",
THREE.ShaderChunk[ "map_particle_fragment" ],
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
},
'dashed': {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "common" ],
THREE.UniformsLib[ "fog" ],
{
"scale": { type: "f", value: 1 },
"dashSize": { type: "f", value: 1 },
"totalSize": { type: "f", value: 2 }
}
] ),
vertexShader: [
"uniform float scale;",
"attribute float lineDistance;",
"varying float vLineDistance;",
THREE.ShaderChunk[ "color_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "color_vertex" ],
"vLineDistance = scale * lineDistance;",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 diffuse;",
"uniform float opacity;",
"uniform float dashSize;",
"uniform float totalSize;",
"varying float vLineDistance;",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
"void main() {",
"if ( mod( vLineDistance, totalSize ) > dashSize ) {",
"discard;",
"}",
"gl_FragColor = vec4( diffuse, opacity );",
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
},
'depth': {
uniforms: {
"mNear": { type: "f", value: 1.0 },
"mFar" : { type: "f", value: 2000.0 },
"opacity" : { type: "f", value: 1.0 }
},
vertexShader: [
"void main() {",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform float mNear;",
"uniform float mFar;",
"uniform float opacity;",
"void main() {",
"float depth = gl_FragCoord.z / gl_FragCoord.w;",
"float color = 1.0 - smoothstep( mNear, mFar, depth );",
"gl_FragColor = vec4( vec3( color ), opacity );",
"}"
].join("\n")
},
'normal': {
uniforms: {
"opacity" : { type: "f", value: 1.0 }
},
vertexShader: [
"varying vec3 vNormal;",
"void main() {",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"vNormal = normalize( normalMatrix * normal );",
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader: [
"uniform float opacity;",
"varying vec3 vNormal;",
"void main() {",
"gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
"}"
].join("\n")
},
/* -------------------------------------------------------------------------
// Normal map shader
// - Blinn-Phong
// - normal + diffuse + specular + AO + displacement + reflection + shadow maps
// - point and directional lights (use with "lights: true" material option)
------------------------------------------------------------------------- */
'normalmap' : {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "fog" ],
THREE.UniformsLib[ "lights" ],
THREE.UniformsLib[ "shadowmap" ],
{
"enableAO" : { type: "i", value: 0 },
"enableDiffuse" : { type: "i", value: 0 },
"enableSpecular" : { type: "i", value: 0 },
"enableReflection": { type: "i", value: 0 },
"enableDisplacement": { type: "i", value: 0 },
"tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture
"tDiffuse" : { type: "t", value: null },
"tCube" : { type: "t", value: null },
"tNormal" : { type: "t", value: null },
"tSpecular" : { type: "t", value: null },
"tAO" : { type: "t", value: null },
"uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) },
"uDisplacementBias": { type: "f", value: 0.0 },
"uDisplacementScale": { type: "f", value: 1.0 },
"uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) },
"uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) },
"uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) },
"uShininess": { type: "f", value: 30 },
"uOpacity": { type: "f", value: 1 },
"useRefract": { type: "i", value: 0 },
"uRefractionRatio": { type: "f", value: 0.98 },
"uReflectivity": { type: "f", value: 0.5 },
"uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) },
"uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) },
"wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
}
] ),
fragmentShader: [
"uniform vec3 uAmbientColor;",
"uniform vec3 uDiffuseColor;",
"uniform vec3 uSpecularColor;",
"uniform float uShininess;",
"uniform float uOpacity;",
"uniform bool enableDiffuse;",
"uniform bool enableSpecular;",
"uniform bool enableAO;",
"uniform bool enableReflection;",
"uniform sampler2D tDiffuse;",
"uniform sampler2D tNormal;",
"uniform sampler2D tSpecular;",
"uniform sampler2D tAO;",
"uniform samplerCube tCube;",
"uniform vec2 uNormalScale;",
"uniform bool useRefract;",
"uniform float uRefractionRatio;",
"uniform float uReflectivity;",
"varying vec3 vTangent;",
"varying vec3 vBinormal;",
"varying vec3 vNormal;",
"varying vec2 vUv;",
"uniform vec3 ambientLightColor;",
"#if MAX_DIR_LIGHTS > 0",
"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
"#endif",
"#ifdef WRAP_AROUND",
"uniform vec3 wrapRGB;",
"#endif",
"varying vec3 vWorldPosition;",
"varying vec3 vViewPosition;",
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( vec3( 1.0 ), uOpacity );",
"vec3 specularTex = vec3( 1.0 );",
"vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
"normalTex.xy *= uNormalScale;",
"normalTex = normalize( normalTex );",
"if( enableDiffuse ) {",
"#ifdef GAMMA_INPUT",
"vec4 texelColor = texture2D( tDiffuse, vUv );",
"texelColor.xyz *= texelColor.xyz;",
"gl_FragColor = gl_FragColor * texelColor;",
"#else",
"gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );",
"#endif",
"}",
"if( enableAO ) {",
"#ifdef GAMMA_INPUT",
"vec4 aoColor = texture2D( tAO, vUv );",
"aoColor.xyz *= aoColor.xyz;",
"gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;",
"#else",
"gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;",
"#endif",
"}",
"if( enableSpecular )",
"specularTex = texture2D( tSpecular, vUv ).xyz;",
"mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );",
"vec3 finalNormal = tsb * normalTex;",
"#ifdef FLIP_SIDED",
"finalNormal = -finalNormal;",
"#endif",
"vec3 normal = normalize( finalNormal );",
"vec3 viewPosition = normalize( vViewPosition );",
// point lights
"#if MAX_POINT_LIGHTS > 0",
"vec3 pointDiffuse = vec3( 0.0 );",
"vec3 pointSpecular = vec3( 0.0 );",
"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
"vec3 pointVector = lPosition.xyz + vViewPosition.xyz;",
"float pointDistance = 1.0;",
"if ( pointLightDistance[ i ] > 0.0 )",
"pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );",
"pointVector = normalize( pointVector );",
// diffuse
"#ifdef WRAP_AROUND",
"float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );",
"float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );",
"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
"#else",
"float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
"#endif",
"pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;",
// specular
"vec3 pointHalfVector = normalize( pointVector + viewPosition );",
"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );",
"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;",
"#else",
"pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;",
"#endif",
"}",
"#endif",
// spot lights
"#if MAX_SPOT_LIGHTS > 0",
"vec3 spotDiffuse = vec3( 0.0 );",
"vec3 spotSpecular = vec3( 0.0 );",
"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
"vec3 spotVector = lPosition.xyz + vViewPosition.xyz;",
"float spotDistance = 1.0;",
"if ( spotLightDistance[ i ] > 0.0 )",
"spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );",
"spotVector = normalize( spotVector );",
"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
"if ( spotEffect > spotLightAngleCos[ i ] ) {",
"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
// diffuse
"#ifdef WRAP_AROUND",
"float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );",
"float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );",
"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
"#else",
"float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );",
"#endif",
"spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;",
// specular
"vec3 spotHalfVector = normalize( spotVector + viewPosition );",
"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
"float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );",
"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;",
"#else",
"spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;",
"#endif",
"}",
"}",
"#endif",
// directional lights
"#if MAX_DIR_LIGHTS > 0",
"vec3 dirDiffuse = vec3( 0.0 );",
"vec3 dirSpecular = vec3( 0.0 );",
"for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {",
"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
"vec3 dirVector = normalize( lDirection.xyz );",
// diffuse
"#ifdef WRAP_AROUND",
"float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );",
"float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
"vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );",
"#else",
"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
"#endif",
"dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;",
// specular
"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
"#else",
"dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;",
"#endif",
"}",
"#endif",
// hemisphere lights
"#if MAX_HEMI_LIGHTS > 0",
"vec3 hemiDiffuse = vec3( 0.0 );",
"vec3 hemiSpecular = vec3( 0.0 );" ,
"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
"vec3 lVector = normalize( lDirection.xyz );",
// diffuse
"float dotProduct = dot( normal, lVector );",
"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
"hemiDiffuse += uDiffuseColor * hemiColor;",
// specular (sky light)
"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
"float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );",
// specular (ground light)
"vec3 lVectorGround = -lVector;",
"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
"float hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );",
"#ifdef PHYSICALLY_BASED_SHADING",
"float dotProductGround = dot( normal, lVectorGround );",
// 2.0 => 2.0001 is hack to work around ANGLE bug
"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
"vec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
"vec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
"#else",
"hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
"#endif",
"}",
"#endif",
// all lights contribution summation
"vec3 totalDiffuse = vec3( 0.0 );",
"vec3 totalSpecular = vec3( 0.0 );",
"#if MAX_DIR_LIGHTS > 0",
"totalDiffuse += dirDiffuse;",
"totalSpecular += dirSpecular;",
"#endif",
"#if MAX_HEMI_LIGHTS > 0",
"totalDiffuse += hemiDiffuse;",
"totalSpecular += hemiSpecular;",
"#endif",
"#if MAX_POINT_LIGHTS > 0",
"totalDiffuse += pointDiffuse;",
"totalSpecular += pointSpecular;",
"#endif",
"#if MAX_SPOT_LIGHTS > 0",
"totalDiffuse += spotDiffuse;",
"totalSpecular += spotSpecular;",
"#endif",
"#ifdef METAL",
"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );",
"#else",
"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;",
"#endif",
"if ( enableReflection ) {",
"vec3 vReflect;",
"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
"if ( useRefract ) {",
"vReflect = refract( cameraToVertex, normal, uRefractionRatio );",
"} else {",
"vReflect = reflect( cameraToVertex, normal );",
"}",
"vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
"#ifdef GAMMA_INPUT",
"cubeColor.xyz *= cubeColor.xyz;",
"#endif",
"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );",
"}",
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n"),
vertexShader: [
"attribute vec4 tangent;",
"uniform vec2 uOffset;",
"uniform vec2 uRepeat;",
"uniform bool enableDisplacement;",
"#ifdef VERTEX_TEXTURES",
"uniform sampler2D tDisplacement;",
"uniform float uDisplacementScale;",
"uniform float uDisplacementBias;",
"#endif",
"varying vec3 vTangent;",
"varying vec3 vBinormal;",
"varying vec3 vNormal;",
"varying vec2 vUv;",
"varying vec3 vWorldPosition;",
"varying vec3 vViewPosition;",
THREE.ShaderChunk[ "skinning_pars_vertex" ],
THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "skinbase_vertex" ],
THREE.ShaderChunk[ "skinnormal_vertex" ],
// normal, tangent and binormal vectors
"#ifdef USE_SKINNING",
"vNormal = normalize( normalMatrix * skinnedNormal.xyz );",
"vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );",
"vTangent = normalize( normalMatrix * skinnedTangent.xyz );",
"#else",
"vNormal = normalize( normalMatrix * normal );",
"vTangent = normalize( normalMatrix * tangent.xyz );",
"#endif",
"vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );",
"vUv = uv * uRepeat + uOffset;",
// displacement mapping
"vec3 displacedPosition;",
"#ifdef VERTEX_TEXTURES",
"if ( enableDisplacement ) {",
"vec3 dv = texture2D( tDisplacement, uv ).xyz;",
"float df = uDisplacementScale * dv.x + uDisplacementBias;",
"displacedPosition = position + normalize( normal ) * df;",
"} else {",
"#ifdef USE_SKINNING",
"vec4 skinVertex = vec4( position, 1.0 );",
"vec4 skinned = boneMatX * skinVertex * skinWeight.x;",
"skinned += boneMatY * skinVertex * skinWeight.y;",
"displacedPosition = skinned.xyz;",
"#else",
"displacedPosition = position;",
"#endif",
"}",
"#else",
"#ifdef USE_SKINNING",
"vec4 skinVertex = vec4( position, 1.0 );",
"vec4 skinned = boneMatX * skinVertex * skinWeight.x;",
"skinned += boneMatY * skinVertex * skinWeight.y;",
"displacedPosition = skinned.xyz;",
"#else",
"displacedPosition = position;",
"#endif",
"#endif",
//
"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
"gl_Position = projectionMatrix * mvPosition;",
//
"vWorldPosition = worldPosition.xyz;",
"vViewPosition = -mvPosition.xyz;",
// shadows
"#ifdef USE_SHADOWMAP",
"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
"}",
"#endif",
"}"
].join("\n")
},
/* -------------------------------------------------------------------------
// Cube map shader
------------------------------------------------------------------------- */
'cube': {
uniforms: { "tCube": { type: "t", value: null },
"tFlip": { type: "f", value: -1 } },
vertexShader: [
"varying vec3 vWorldPosition;",
"void main() {",
"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
"vWorldPosition = worldPosition.xyz;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform samplerCube tCube;",
"uniform float tFlip;",
"varying vec3 vWorldPosition;",
"void main() {",
"gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
"}"
].join("\n")
},
// Depth encoding into RGBA texture
// based on SpiderGL shadow map example
// http://spidergl.org/example.php?id=6
// originally from
// http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
// see also here:
// http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
'depthRGBA': {
uniforms: {},
vertexShader: [
THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
THREE.ShaderChunk[ "skinning_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "skinbase_vertex" ],
THREE.ShaderChunk[ "morphtarget_vertex" ],
THREE.ShaderChunk[ "skinning_vertex" ],
THREE.ShaderChunk[ "default_vertex" ],
"}"
].join("\n"),
fragmentShader: [
"vec4 pack_depth( const in float depth ) {",
"const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
"const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
"vec4 res = fract( depth * bit_shift );",
"res -= res.xxyz * bit_mask;",
"return res;",
"}",
"void main() {",
"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
//"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );",
//"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );",
//"gl_FragData[ 0 ] = pack_depth( z );",
//"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );",
"}"
].join("\n")
}
};
/**
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author szimek / https://github.com/szimek/
*/
THREE.WebGLRenderer = function ( parameters ) {
console.log( 'THREE.WebGLRenderer', THREE.REVISION );
parameters = parameters || {};
var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
_alpha = parameters.alpha !== undefined ? parameters.alpha : true,
_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
_clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ),
_clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0;
// public properties
this.domElement = _canvas;
this.context = null;
this.devicePixelRatio = parameters.devicePixelRatio !== undefined
? parameters.devicePixelRatio
: window.devicePixelRatio !== undefined
? window.devicePixelRatio
: 1;
// clearing
this.autoClear = true;
this.autoClearColor = true;
this.autoClearDepth = true;
this.autoClearStencil = true;
// scene graph
this.sortObjects = true;
this.autoUpdateObjects = true;
this.autoUpdateScene = true;
// physically based shading
this.gammaInput = false;
this.gammaOutput = false;
this.physicallyBasedShading = false;
// shadow map
this.shadowMapEnabled = false;
this.shadowMapAutoUpdate = true;
this.shadowMapType = THREE.PCFShadowMap;
this.shadowMapCullFace = THREE.CullFaceFront;
this.shadowMapDebug = false;
this.shadowMapCascade = false;
// morphs
this.maxMorphTargets = 8;
this.maxMorphNormals = 4;
// flags
this.autoScaleCubemaps = true;
// custom render plugins
this.renderPluginsPre = [];
this.renderPluginsPost = [];
// info
this.info = {
memory: {
programs: 0,
geometries: 0,
textures: 0
},
render: {
calls: 0,
vertices: 0,
faces: 0,
points: 0
}
};
// internal properties
var _this = this,
_programs = [],
_programs_counter = 0,
// internal state cache
_currentProgram = null,
_currentFramebuffer = null,
_currentMaterialId = -1,
_currentGeometryGroupHash = null,
_currentCamera = null,
_geometryGroupCounter = 0,
_usedTextureUnits = 0,
// GL state cache
_oldDoubleSided = -1,
_oldFlipSided = -1,
_oldBlending = -1,
_oldBlendEquation = -1,
_oldBlendSrc = -1,
_oldBlendDst = -1,
_oldDepthTest = -1,
_oldDepthWrite = -1,
_oldPolygonOffset = null,
_oldPolygonOffsetFactor = null,
_oldPolygonOffsetUnits = null,
_oldLineWidth = null,
_viewportX = 0,
_viewportY = 0,
_viewportWidth = 0,
_viewportHeight = 0,
_currentWidth = 0,
_currentHeight = 0,
_enabledAttributes = {},
// frustum
_frustum = new THREE.Frustum(),
// camera matrices cache
_projScreenMatrix = new THREE.Matrix4(),
_projScreenMatrixPS = new THREE.Matrix4(),
_vector3 = new THREE.Vector3(),
// light arrays cache
_direction = new THREE.Vector3(),
_lightsNeedUpdate = true,
_lights = {
ambient: [ 0, 0, 0 ],
directional: { length: 0, colors: new Array(), positions: new Array() },
point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() },
spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), anglesCos: new Array(), exponents: new Array() },
hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() }
};
// initialize
var _gl;
var _glExtensionTextureFloat;
var _glExtensionStandardDerivatives;
var _glExtensionTextureFilterAnisotropic;
var _glExtensionCompressedTextureS3TC;
initGL();
setDefaultGLState();
this.context = _gl;
// GPU capabilities
var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS );
var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE );
var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE );
var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
var _supportsVertexTextures = ( _maxVertexTextures > 0 );
var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat;
var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : [];
//
var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT );
var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT );
var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT );
var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT );
var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT );
var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT );
var _vertexShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_INT );
var _vertexShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_INT );
var _vertexShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_INT );
var _fragmentShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_INT );
var _fragmentShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_INT );
var _fragmentShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_INT );
// clamp precision to maximum available
var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0;
var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0;
if ( _precision === "highp" && ! highpAvailable ) {
if ( mediumpAvailable ) {
_precision = "mediump";
console.warn( "WebGLRenderer: highp not supported, using mediump" );
} else {
_precision = "lowp";
console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" );
}
}
if ( _precision === "mediump" && ! mediumpAvailable ) {
_precision = "lowp";
console.warn( "WebGLRenderer: mediump not supported, using lowp" );
}
// API
this.getContext = function () {
return _gl;
};
this.supportsVertexTextures = function () {
return _supportsVertexTextures;
};
this.supportsFloatTextures = function () {
return _glExtensionTextureFloat;
};
this.supportsStandardDerivatives = function () {
return _glExtensionStandardDerivatives;
};
this.supportsCompressedTextureS3TC = function () {
return _glExtensionCompressedTextureS3TC;
};
this.getMaxAnisotropy = function () {
return _maxAnisotropy;
};
this.getPrecision = function () {
return _precision;
};
this.setSize = function ( width, height ) {
_canvas.width = width * this.devicePixelRatio;
_canvas.height = height * this.devicePixelRatio;
_canvas.style.width = width + 'px';
_canvas.style.height = height + 'px';
this.setViewport( 0, 0, _canvas.width, _canvas.height );
};
this.setViewport = function ( x, y, width, height ) {
_viewportX = x !== undefined ? x : 0;
_viewportY = y !== undefined ? y : 0;
_viewportWidth = width !== undefined ? width : _canvas.width;
_viewportHeight = height !== undefined ? height : _canvas.height;
_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
};
this.setScissor = function ( x, y, width, height ) {
_gl.scissor( x, y, width, height );
};
this.enableScissorTest = function ( enable ) {
enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
};
// Clearing
this.setClearColorHex = function ( hex, alpha ) {
_clearColor.setHex( hex );
_clearAlpha = alpha;
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
this.setClearColor = function ( color, alpha ) {
_clearColor.copy( color );
_clearAlpha = alpha;
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
this.getClearColor = function () {
return _clearColor;
};
this.getClearAlpha = function () {
return _clearAlpha;
};
this.clear = function ( color, depth, stencil ) {
var bits = 0;
if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
_gl.clear( bits );
};
this.clearTarget = function ( renderTarget, color, depth, stencil ) {
this.setRenderTarget( renderTarget );
this.clear( color, depth, stencil );
};
// Plugins
this.addPostPlugin = function ( plugin ) {
plugin.init( this );
this.renderPluginsPost.push( plugin );
};
this.addPrePlugin = function ( plugin ) {
plugin.init( this );
this.renderPluginsPre.push( plugin );
};
// Rendering
this.updateShadowMap = function ( scene, camera ) {
_currentProgram = null;
_oldBlending = -1;
_oldDepthTest = -1;
_oldDepthWrite = -1;
_currentGeometryGroupHash = -1;
_currentMaterialId = -1;
_lightsNeedUpdate = true;
_oldDoubleSided = -1;
_oldFlipSided = -1;
this.shadowMapPlugin.update( scene, camera );
};
// Internal functions
// Buffer allocation
function createParticleBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createLineBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
geometry.__webglLineDistanceBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createRibbonBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
geometry.__webglNormalBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createMeshBuffers ( geometryGroup ) {
geometryGroup.__webglVertexBuffer = _gl.createBuffer();
geometryGroup.__webglNormalBuffer = _gl.createBuffer();
geometryGroup.__webglTangentBuffer = _gl.createBuffer();
geometryGroup.__webglColorBuffer = _gl.createBuffer();
geometryGroup.__webglUVBuffer = _gl.createBuffer();
geometryGroup.__webglUV2Buffer = _gl.createBuffer();
geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer();
geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer();
geometryGroup.__webglFaceBuffer = _gl.createBuffer();
geometryGroup.__webglLineBuffer = _gl.createBuffer();
var m, ml;
if ( geometryGroup.numMorphTargets ) {
geometryGroup.__webglMorphTargetsBuffers = [];
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
}
}
if ( geometryGroup.numMorphNormals ) {
geometryGroup.__webglMorphNormalsBuffers = [];
for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
}
}
_this.info.memory.geometries ++;
};
// Events
var onGeometryDispose = function ( event ) {
var geometry = event.target;
geometry.removeEventListener( 'dispose', onGeometryDispose );
deallocateGeometry( geometry );
_this.info.memory.geometries --;
};
var onTextureDispose = function ( event ) {
var texture = event.target;
texture.removeEventListener( 'dispose', onTextureDispose );
deallocateTexture( texture );
_this.info.memory.textures --;
};
var onRenderTargetDispose = function ( event ) {
var renderTarget = event.target;
renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
deallocateRenderTarget( renderTarget );
_this.info.memory.textures --;
};
var onMaterialDispose = function ( event ) {
var material = event.target;
material.removeEventListener( 'dispose', onMaterialDispose );
deallocateMaterial( material );
};
// Buffer deallocation
var deallocateGeometry = function ( geometry ) {
geometry.__webglInit = undefined;
if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer );
if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer );
if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer );
if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer );
if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer );
if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer );
if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer );
if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer );
if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer );
if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer );
if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
// geometry groups
if ( geometry.geometryGroups !== undefined ) {
for ( var g in geometry.geometryGroups ) {
var geometryGroup = geometry.geometryGroups[ g ];
if ( geometryGroup.numMorphTargets !== undefined ) {
for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
}
}
if ( geometryGroup.numMorphNormals !== undefined ) {
for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
}
}
deleteCustomAttributesBuffers( geometryGroup );
}
}
deleteCustomAttributesBuffers( geometry );
};
var deallocateTexture = function ( texture ) {
if ( texture.image && texture.image.__webglTextureCube ) {
// cube texture
_gl.deleteTexture( texture.image.__webglTextureCube );
} else {
// 2D texture
if ( ! texture.__webglInit ) return;
texture.__webglInit = false;
_gl.deleteTexture( texture.__webglTexture );
}
};
var deallocateRenderTarget = function ( renderTarget ) {
if ( !renderTarget || ! renderTarget.__webglTexture ) return;
_gl.deleteTexture( renderTarget.__webglTexture );
if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
for ( var i = 0; i < 6; i ++ ) {
_gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] );
_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] );
}
} else {
_gl.deleteFramebuffer( renderTarget.__webglFramebuffer );
_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer );
}
};
var deallocateMaterial = function ( material ) {
var program = material.program;
if ( program === undefined ) return;
material.program = undefined;
// only deallocate GL program if this was the last use of shared program
// assumed there is only single copy of any program in the _programs list
// (that's how it's constructed)
var i, il, programInfo;
var deleteProgram = false;
for ( i = 0, il = _programs.length; i < il; i ++ ) {
programInfo = _programs[ i ];
if ( programInfo.program === program ) {
programInfo.usedTimes --;
if ( programInfo.usedTimes === 0 ) {
deleteProgram = true;
}
break;
}
}
if ( deleteProgram === true ) {
// avoid using array.splice, this is costlier than creating new array from scratch
var newPrograms = [];
for ( i = 0, il = _programs.length; i < il; i ++ ) {
programInfo = _programs[ i ];
if ( programInfo.program !== program ) {
newPrograms.push( programInfo );
}
}
_programs = newPrograms;
_gl.deleteProgram( program );
_this.info.memory.programs --;
}
};
//
/*
function deleteParticleBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
deleteCustomAttributesBuffers( geometry );
_this.info.memory.geometries --;
};
function deleteLineBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
_gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
deleteCustomAttributesBuffers( geometry );
_this.info.memory.geometries --;
};
function deleteRibbonBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
_gl.deleteBuffer( geometry.__webglNormalBuffer );
deleteCustomAttributesBuffers( geometry );
_this.info.memory.geometries --;
};
function deleteMeshBuffers ( geometryGroup ) {
_gl.deleteBuffer( geometryGroup.__webglVertexBuffer );
_gl.deleteBuffer( geometryGroup.__webglNormalBuffer );
_gl.deleteBuffer( geometryGroup.__webglTangentBuffer );
_gl.deleteBuffer( geometryGroup.__webglColorBuffer );
_gl.deleteBuffer( geometryGroup.__webglUVBuffer );
_gl.deleteBuffer( geometryGroup.__webglUV2Buffer );
_gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer );
_gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer );
_gl.deleteBuffer( geometryGroup.__webglFaceBuffer );
_gl.deleteBuffer( geometryGroup.__webglLineBuffer );
var m, ml;
if ( geometryGroup.numMorphTargets ) {
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
}
}
if ( geometryGroup.numMorphNormals ) {
for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
}
}
deleteCustomAttributesBuffers( geometryGroup );
_this.info.memory.geometries --;
};
*/
function deleteCustomAttributesBuffers( geometry ) {
if ( geometry.__webglCustomAttributesList ) {
for ( var id in geometry.__webglCustomAttributesList ) {
_gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
}
}
};
// Buffer initialization
function initCustomAttributes ( geometry, object ) {
var nvertices = geometry.vertices.length;
var material = object.material;
if ( material.attributes ) {
if ( geometry.__webglCustomAttributesList === undefined ) {
geometry.__webglCustomAttributesList = [];
}
for ( var a in material.attributes ) {
var attribute = material.attributes[ a ];
if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
attribute.__webglInitialized = true;
var size = 1; // "f" and "i"
if ( attribute.type === "v2" ) size = 2;
else if ( attribute.type === "v3" ) size = 3;
else if ( attribute.type === "v4" ) size = 4;
else if ( attribute.type === "c" ) size = 3;
attribute.size = size;
attribute.array = new Float32Array( nvertices * size );
attribute.buffer = _gl.createBuffer();
attribute.buffer.belongsToAttribute = a;
attribute.needsUpdate = true;
}
geometry.__webglCustomAttributesList.push( attribute );
}
}
};
function initParticleBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__sortArray = [];
geometry.__webglParticleCount = nvertices;
initCustomAttributes ( geometry, object );
};
function initLineBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__lineDistanceArray = new Float32Array( nvertices * 1 );
geometry.__webglLineCount = nvertices;
initCustomAttributes ( geometry, object );
};
function initRibbonBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__normalArray = new Float32Array( nvertices * 3 );
geometry.__webglVertexCount = nvertices;
initCustomAttributes ( geometry, object );
};
function initMeshBuffers ( geometryGroup, object ) {
var geometry = object.geometry,
faces3 = geometryGroup.faces3,
faces4 = geometryGroup.faces4,
nvertices = faces3.length * 3 + faces4.length * 4,
ntris = faces3.length * 1 + faces4.length * 2,
nlines = faces3.length * 3 + faces4.length * 4,
material = getBufferMaterial( object, geometryGroup ),
uvType = bufferGuessUVType( material ),
normalType = bufferGuessNormalType( material ),
vertexColorType = bufferGuessVertexColorType( material );
//console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
if ( normalType ) {
geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
}
if ( geometry.hasTangents ) {
geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
}
if ( vertexColorType ) {
geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
}
if ( uvType ) {
if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) {
geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
}
if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) {
geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
}
}
if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
}
geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
var m, ml;
if ( geometryGroup.numMorphTargets ) {
geometryGroup.__morphTargetsArrays = [];
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
}
}
if ( geometryGroup.numMorphNormals ) {
geometryGroup.__morphNormalsArrays = [];
for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) );
}
}
geometryGroup.__webglFaceCount = ntris * 3;
geometryGroup.__webglLineCount = nlines * 2;
// custom attributes
if ( material.attributes ) {
if ( geometryGroup.__webglCustomAttributesList === undefined ) {
geometryGroup.__webglCustomAttributesList = [];
}
for ( var a in material.attributes ) {
// Do a shallow copy of the attribute object so different geometryGroup chunks use different
// attribute buffers which are correctly indexed in the setMeshBuffers function
var originalAttribute = material.attributes[ a ];
var attribute = {};
for ( var property in originalAttribute ) {
attribute[ property ] = originalAttribute[ property ];
}
if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
attribute.__webglInitialized = true;
var size = 1; // "f" and "i"
if( attribute.type === "v2" ) size = 2;
else if( attribute.type === "v3" ) size = 3;
else if( attribute.type === "v4" ) size = 4;
else if( attribute.type === "c" ) size = 3;
attribute.size = size;
attribute.array = new Float32Array( nvertices * size );
attribute.buffer = _gl.createBuffer();
attribute.buffer.belongsToAttribute = a;
originalAttribute.needsUpdate = true;
attribute.__original = originalAttribute;
}
geometryGroup.__webglCustomAttributesList.push( attribute );
}
}
geometryGroup.__inittedArrays = true;
};
function getBufferMaterial( object, geometryGroup ) {
return object.material instanceof THREE.MeshFaceMaterial
? object.material.materials[ geometryGroup.materialIndex ]
: object.material;
};
function materialNeedsSmoothNormals ( material ) {
return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
};
function bufferGuessNormalType ( material ) {
// only MeshBasicMaterial and MeshDepthMaterial don't need normals
if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
return false;
}
if ( materialNeedsSmoothNormals( material ) ) {
return THREE.SmoothShading;
} else {
return THREE.FlatShading;
}
};
function bufferGuessVertexColorType ( material ) {
if ( material.vertexColors ) {
return material.vertexColors;
}
return false;
};
function bufferGuessUVType ( material ) {
// material must use some texture to require uvs
if ( material.map || material.lightMap || material.bumpMap || material.normalMap || material.specularMap || material instanceof THREE.ShaderMaterial ) {
return true;
}
return false;
};
//
function initDirectBuffers( geometry ) {
var a, attribute, type;
for ( a in geometry.attributes ) {
if ( a === "index" ) {
type = _gl.ELEMENT_ARRAY_BUFFER;
} else {
type = _gl.ARRAY_BUFFER;
}
attribute = geometry.attributes[ a ];
attribute.buffer = _gl.createBuffer();
_gl.bindBuffer( type, attribute.buffer );
_gl.bufferData( type, attribute.array, _gl.STATIC_DRAW );
}
};
// Buffer setting
function setParticleBuffers ( geometry, hint, object ) {
var v, c, vertex, offset, index, color,
vertices = geometry.vertices,
vl = vertices.length,
colors = geometry.colors,
cl = colors.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
sortArray = geometry.__sortArray,
dirtyVertices = geometry.verticesNeedUpdate,
dirtyElements = geometry.elementsNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( object.sortParticles ) {
_projScreenMatrixPS.copy( _projScreenMatrix );
_projScreenMatrixPS.multiply( object.matrixWorld );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
_vector3.copy( vertex );
_vector3.applyProjection( _projScreenMatrixPS );
sortArray[ v ] = [ _vector3.z, v ];
}
sortArray.sort( numericalSort );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ sortArray[v][1] ];
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
for ( c = 0; c < cl; c ++ ) {
offset = c * 3;
color = colors[ sortArray[c][1] ];
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
offset = 0;
cal = customAttribute.value.length;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
customAttribute.array[ ca ] = customAttribute.value[ index ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
}
}
} else {
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate &&
( customAttribute.boundTo === undefined ||
customAttribute.boundTo === "vertices") ) {
cal = customAttribute.value.length;
offset = 0;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
customAttribute.array[ ca ] = customAttribute.value[ ca ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
}
}
}
}
if ( dirtyVertices || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
}
};
function setLineBuffers ( geometry, hint ) {
var v, c, d, vertex, offset, color,
vertices = geometry.vertices,
colors = geometry.colors,
lineDistances = geometry.lineDistances,
vl = vertices.length,
cl = colors.length,
dl = lineDistances.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
lineDistanceArray = geometry.__lineDistanceArray,
dirtyVertices = geometry.verticesNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
dirtyLineDistances = geometry.lineDistancesNeedUpdate,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
if ( dirtyLineDistances ) {
for ( d = 0; d < dl; d ++ ) {
lineDistanceArray[ d ] = lineDistances[ d ];
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate &&
( customAttribute.boundTo === undefined ||
customAttribute.boundTo === "vertices" ) ) {
offset = 0;
cal = customAttribute.value.length;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
customAttribute.array[ ca ] = customAttribute.value[ ca ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
}
};
function setRibbonBuffers ( geometry, hint ) {
var v, c, n, vertex, offset, color, normal,
i, il, ca, cal, customAttribute, value,
vertices = geometry.vertices,
colors = geometry.colors,
normals = geometry.normals,
vl = vertices.length,
cl = colors.length,
nl = normals.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
normalArray = geometry.__normalArray,
dirtyVertices = geometry.verticesNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
dirtyNormals = geometry.normalsNeedUpdate,
customAttributes = geometry.__webglCustomAttributesList;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
if ( dirtyNormals ) {
for ( n = 0; n < nl; n ++ ) {
normal = normals[ n ];
offset = n * 3;
normalArray[ offset ] = normal.x;
normalArray[ offset + 1 ] = normal.y;
normalArray[ offset + 2 ] = normal.z;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglNormalBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate &&
( customAttribute.boundTo === undefined ||
customAttribute.boundTo === "vertices" ) ) {
offset = 0;
cal = customAttribute.value.length;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
customAttribute.array[ ca ] = customAttribute.value[ ca ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
}
};
function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
if ( ! geometryGroup.__inittedArrays ) {
return;
}
var normalType = bufferGuessNormalType( material ),
vertexColorType = bufferGuessVertexColorType( material ),
uvType = bufferGuessUVType( material ),
needsSmoothNormals = ( normalType === THREE.SmoothShading );
var f, fl, fi, face,
vertexNormals, faceNormal, normal,
vertexColors, faceColor,
vertexTangents,
uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4,
c1, c2, c3, c4,
sw1, sw2, sw3, sw4,
si1, si2, si3, si4,
sa1, sa2, sa3, sa4,
sb1, sb2, sb3, sb4,
m, ml, i, il,
vn, uvi, uv2i,
vk, vkl, vka,
nka, chf, faceVertexNormals,
a,
vertexIndex = 0,
offset = 0,
offset_uv = 0,
offset_uv2 = 0,
offset_face = 0,
offset_normal = 0,
offset_tangent = 0,
offset_line = 0,
offset_color = 0,
offset_skin = 0,
offset_morphTarget = 0,
offset_custom = 0,
offset_customSrc = 0,
value,
vertexArray = geometryGroup.__vertexArray,
uvArray = geometryGroup.__uvArray,
uv2Array = geometryGroup.__uv2Array,
normalArray = geometryGroup.__normalArray,
tangentArray = geometryGroup.__tangentArray,
colorArray = geometryGroup.__colorArray,
skinIndexArray = geometryGroup.__skinIndexArray,
skinWeightArray = geometryGroup.__skinWeightArray,
morphTargetsArrays = geometryGroup.__morphTargetsArrays,
morphNormalsArrays = geometryGroup.__morphNormalsArrays,
customAttributes = geometryGroup.__webglCustomAttributesList,
customAttribute,
faceArray = geometryGroup.__faceArray,
lineArray = geometryGroup.__lineArray,
geometry = object.geometry, // this is shared for all chunks
dirtyVertices = geometry.verticesNeedUpdate,
dirtyElements = geometry.elementsNeedUpdate,
dirtyUvs = geometry.uvsNeedUpdate,
dirtyNormals = geometry.normalsNeedUpdate,
dirtyTangents = geometry.tangentsNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
dirtyMorphTargets = geometry.morphTargetsNeedUpdate,
vertices = geometry.vertices,
chunk_faces3 = geometryGroup.faces3,
chunk_faces4 = geometryGroup.faces4,
obj_faces = geometry.faces,
obj_uvs = geometry.faceVertexUvs[ 0 ],
obj_uvs2 = geometry.faceVertexUvs[ 1 ],
obj_colors = geometry.colors,
obj_skinIndices = geometry.skinIndices,
obj_skinWeights = geometry.skinWeights,
morphTargets = geometry.morphTargets,
morphNormals = geometry.morphNormals;
if ( dirtyVertices ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = vertices[ face.a ];
v2 = vertices[ face.b ];
v3 = vertices[ face.c ];
vertexArray[ offset ] = v1.x;
vertexArray[ offset + 1 ] = v1.y;
vertexArray[ offset + 2 ] = v1.z;
vertexArray[ offset + 3 ] = v2.x;
vertexArray[ offset + 4 ] = v2.y;
vertexArray[ offset + 5 ] = v2.z;
vertexArray[ offset + 6 ] = v3.x;
vertexArray[ offset + 7 ] = v3.y;
vertexArray[ offset + 8 ] = v3.z;
offset += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = vertices[ face.a ];
v2 = vertices[ face.b ];
v3 = vertices[ face.c ];
v4 = vertices[ face.d ];
vertexArray[ offset ] = v1.x;
vertexArray[ offset + 1 ] = v1.y;
vertexArray[ offset + 2 ] = v1.z;
vertexArray[ offset + 3 ] = v2.x;
vertexArray[ offset + 4 ] = v2.y;
vertexArray[ offset + 5 ] = v2.z;
vertexArray[ offset + 6 ] = v3.x;
vertexArray[ offset + 7 ] = v3.y;
vertexArray[ offset + 8 ] = v3.z;
vertexArray[ offset + 9 ] = v4.x;
vertexArray[ offset + 10 ] = v4.y;
vertexArray[ offset + 11 ] = v4.z;
offset += 12;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyMorphTargets ) {
for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) {
offset_morphTarget = 0;
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
chf = chunk_faces3[ f ];
face = obj_faces[ chf ];
// morph positions
v1 = morphTargets[ vk ].vertices[ face.a ];
v2 = morphTargets[ vk ].vertices[ face.b ];
v3 = morphTargets[ vk ].vertices[ face.c ];
vka = morphTargetsArrays[ vk ];
vka[ offset_morphTarget ] = v1.x;
vka[ offset_morphTarget + 1 ] = v1.y;
vka[ offset_morphTarget + 2 ] = v1.z;
vka[ offset_morphTarget + 3 ] = v2.x;
vka[ offset_morphTarget + 4 ] = v2.y;
vka[ offset_morphTarget + 5 ] = v2.z;
vka[ offset_morphTarget + 6 ] = v3.x;
vka[ offset_morphTarget + 7 ] = v3.y;
vka[ offset_morphTarget + 8 ] = v3.z;
// morph normals
if ( material.morphNormals ) {
if ( needsSmoothNormals ) {
faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
n1 = faceVertexNormals.a;
n2 = faceVertexNormals.b;
n3 = faceVertexNormals.c;
} else {
n1 = morphNormals[ vk ].faceNormals[ chf ];
n2 = n1;
n3 = n1;
}
nka = morphNormalsArrays[ vk ];
nka[ offset_morphTarget ] = n1.x;
nka[ offset_morphTarget + 1 ] = n1.y;
nka[ offset_morphTarget + 2 ] = n1.z;
nka[ offset_morphTarget + 3 ] = n2.x;
nka[ offset_morphTarget + 4 ] = n2.y;
nka[ offset_morphTarget + 5 ] = n2.z;
nka[ offset_morphTarget + 6 ] = n3.x;
nka[ offset_morphTarget + 7 ] = n3.y;
nka[ offset_morphTarget + 8 ] = n3.z;
}
//
offset_morphTarget += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
chf = chunk_faces4[ f ];
face = obj_faces[ chf ];
// morph positions
v1 = morphTargets[ vk ].vertices[ face.a ];
v2 = morphTargets[ vk ].vertices[ face.b ];
v3 = morphTargets[ vk ].vertices[ face.c ];
v4 = morphTargets[ vk ].vertices[ face.d ];
vka = morphTargetsArrays[ vk ];
vka[ offset_morphTarget ] = v1.x;
vka[ offset_morphTarget + 1 ] = v1.y;
vka[ offset_morphTarget + 2 ] = v1.z;
vka[ offset_morphTarget + 3 ] = v2.x;
vka[ offset_morphTarget + 4 ] = v2.y;
vka[ offset_morphTarget + 5 ] = v2.z;
vka[ offset_morphTarget + 6 ] = v3.x;
vka[ offset_morphTarget + 7 ] = v3.y;
vka[ offset_morphTarget + 8 ] = v3.z;
vka[ offset_morphTarget + 9 ] = v4.x;
vka[ offset_morphTarget + 10 ] = v4.y;
vka[ offset_morphTarget + 11 ] = v4.z;
// morph normals
if ( material.morphNormals ) {
if ( needsSmoothNormals ) {
faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
n1 = faceVertexNormals.a;
n2 = faceVertexNormals.b;
n3 = faceVertexNormals.c;
n4 = faceVertexNormals.d;
} else {
n1 = morphNormals[ vk ].faceNormals[ chf ];
n2 = n1;
n3 = n1;
n4 = n1;
}
nka = morphNormalsArrays[ vk ];
nka[ offset_morphTarget ] = n1.x;
nka[ offset_morphTarget + 1 ] = n1.y;
nka[ offset_morphTarget + 2 ] = n1.z;
nka[ offset_morphTarget + 3 ] = n2.x;
nka[ offset_morphTarget + 4 ] = n2.y;
nka[ offset_morphTarget + 5 ] = n2.z;
nka[ offset_morphTarget + 6 ] = n3.x;
nka[ offset_morphTarget + 7 ] = n3.y;
nka[ offset_morphTarget + 8 ] = n3.z;
nka[ offset_morphTarget + 9 ] = n4.x;
nka[ offset_morphTarget + 10 ] = n4.y;
nka[ offset_morphTarget + 11 ] = n4.z;
}
//
offset_morphTarget += 12;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] );
_gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint );
if ( material.morphNormals ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] );
_gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint );
}
}
}
if ( obj_skinWeights.length ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
// weights
sw1 = obj_skinWeights[ face.a ];
sw2 = obj_skinWeights[ face.b ];
sw3 = obj_skinWeights[ face.c ];
skinWeightArray[ offset_skin ] = sw1.x;
skinWeightArray[ offset_skin + 1 ] = sw1.y;
skinWeightArray[ offset_skin + 2 ] = sw1.z;
skinWeightArray[ offset_skin + 3 ] = sw1.w;
skinWeightArray[ offset_skin + 4 ] = sw2.x;
skinWeightArray[ offset_skin + 5 ] = sw2.y;
skinWeightArray[ offset_skin + 6 ] = sw2.z;
skinWeightArray[ offset_skin + 7 ] = sw2.w;
skinWeightArray[ offset_skin + 8 ] = sw3.x;
skinWeightArray[ offset_skin + 9 ] = sw3.y;
skinWeightArray[ offset_skin + 10 ] = sw3.z;
skinWeightArray[ offset_skin + 11 ] = sw3.w;
// indices
si1 = obj_skinIndices[ face.a ];
si2 = obj_skinIndices[ face.b ];
si3 = obj_skinIndices[ face.c ];
skinIndexArray[ offset_skin ] = si1.x;
skinIndexArray[ offset_skin + 1 ] = si1.y;
skinIndexArray[ offset_skin + 2 ] = si1.z;
skinIndexArray[ offset_skin + 3 ] = si1.w;
skinIndexArray[ offset_skin + 4 ] = si2.x;
skinIndexArray[ offset_skin + 5 ] = si2.y;
skinIndexArray[ offset_skin + 6 ] = si2.z;
skinIndexArray[ offset_skin + 7 ] = si2.w;
skinIndexArray[ offset_skin + 8 ] = si3.x;
skinIndexArray[ offset_skin + 9 ] = si3.y;
skinIndexArray[ offset_skin + 10 ] = si3.z;
skinIndexArray[ offset_skin + 11 ] = si3.w;
offset_skin += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
// weights
sw1 = obj_skinWeights[ face.a ];
sw2 = obj_skinWeights[ face.b ];
sw3 = obj_skinWeights[ face.c ];
sw4 = obj_skinWeights[ face.d ];
skinWeightArray[ offset_skin ] = sw1.x;
skinWeightArray[ offset_skin + 1 ] = sw1.y;
skinWeightArray[ offset_skin + 2 ] = sw1.z;
skinWeightArray[ offset_skin + 3 ] = sw1.w;
skinWeightArray[ offset_skin + 4 ] = sw2.x;
skinWeightArray[ offset_skin + 5 ] = sw2.y;
skinWeightArray[ offset_skin + 6 ] = sw2.z;
skinWeightArray[ offset_skin + 7 ] = sw2.w;
skinWeightArray[ offset_skin + 8 ] = sw3.x;
skinWeightArray[ offset_skin + 9 ] = sw3.y;
skinWeightArray[ offset_skin + 10 ] = sw3.z;
skinWeightArray[ offset_skin + 11 ] = sw3.w;
skinWeightArray[ offset_skin + 12 ] = sw4.x;
skinWeightArray[ offset_skin + 13 ] = sw4.y;
skinWeightArray[ offset_skin + 14 ] = sw4.z;
skinWeightArray[ offset_skin + 15 ] = sw4.w;
// indices
si1 = obj_skinIndices[ face.a ];
si2 = obj_skinIndices[ face.b ];
si3 = obj_skinIndices[ face.c ];
si4 = obj_skinIndices[ face.d ];
skinIndexArray[ offset_skin ] = si1.x;
skinIndexArray[ offset_skin + 1 ] = si1.y;
skinIndexArray[ offset_skin + 2 ] = si1.z;
skinIndexArray[ offset_skin + 3 ] = si1.w;
skinIndexArray[ offset_skin + 4 ] = si2.x;
skinIndexArray[ offset_skin + 5 ] = si2.y;
skinIndexArray[ offset_skin + 6 ] = si2.z;
skinIndexArray[ offset_skin + 7 ] = si2.w;
skinIndexArray[ offset_skin + 8 ] = si3.x;
skinIndexArray[ offset_skin + 9 ] = si3.y;
skinIndexArray[ offset_skin + 10 ] = si3.z;
skinIndexArray[ offset_skin + 11 ] = si3.w;
skinIndexArray[ offset_skin + 12 ] = si4.x;
skinIndexArray[ offset_skin + 13 ] = si4.y;
skinIndexArray[ offset_skin + 14 ] = si4.z;
skinIndexArray[ offset_skin + 15 ] = si4.w;
offset_skin += 16;
}
if ( offset_skin > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint );
}
}
if ( dirtyColors && vertexColorType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexColors = face.vertexColors;
faceColor = face.color;
if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
c1 = vertexColors[ 0 ];
c2 = vertexColors[ 1 ];
c3 = vertexColors[ 2 ];
} else {
c1 = faceColor;
c2 = faceColor;
c3 = faceColor;
}
colorArray[ offset_color ] = c1.r;
colorArray[ offset_color + 1 ] = c1.g;
colorArray[ offset_color + 2 ] = c1.b;
colorArray[ offset_color + 3 ] = c2.r;
colorArray[ offset_color + 4 ] = c2.g;
colorArray[ offset_color + 5 ] = c2.b;
colorArray[ offset_color + 6 ] = c3.r;
colorArray[ offset_color + 7 ] = c3.g;
colorArray[ offset_color + 8 ] = c3.b;
offset_color += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexColors = face.vertexColors;
faceColor = face.color;
if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) {
c1 = vertexColors[ 0 ];
c2 = vertexColors[ 1 ];
c3 = vertexColors[ 2 ];
c4 = vertexColors[ 3 ];
} else {
c1 = faceColor;
c2 = faceColor;
c3 = faceColor;
c4 = faceColor;
}
colorArray[ offset_color ] = c1.r;
colorArray[ offset_color + 1 ] = c1.g;
colorArray[ offset_color + 2 ] = c1.b;
colorArray[ offset_color + 3 ] = c2.r;
colorArray[ offset_color + 4 ] = c2.g;
colorArray[ offset_color + 5 ] = c2.b;
colorArray[ offset_color + 6 ] = c3.r;
colorArray[ offset_color + 7 ] = c3.g;
colorArray[ offset_color + 8 ] = c3.b;
colorArray[ offset_color + 9 ] = c4.r;
colorArray[ offset_color + 10 ] = c4.g;
colorArray[ offset_color + 11 ] = c4.b;
offset_color += 12;
}
if ( offset_color > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
}
if ( dirtyTangents && geometry.hasTangents ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexTangents = face.vertexTangents;
t1 = vertexTangents[ 0 ];
t2 = vertexTangents[ 1 ];
t3 = vertexTangents[ 2 ];
tangentArray[ offset_tangent ] = t1.x;
tangentArray[ offset_tangent + 1 ] = t1.y;
tangentArray[ offset_tangent + 2 ] = t1.z;
tangentArray[ offset_tangent + 3 ] = t1.w;
tangentArray[ offset_tangent + 4 ] = t2.x;
tangentArray[ offset_tangent + 5 ] = t2.y;
tangentArray[ offset_tangent + 6 ] = t2.z;
tangentArray[ offset_tangent + 7 ] = t2.w;
tangentArray[ offset_tangent + 8 ] = t3.x;
tangentArray[ offset_tangent + 9 ] = t3.y;
tangentArray[ offset_tangent + 10 ] = t3.z;
tangentArray[ offset_tangent + 11 ] = t3.w;
offset_tangent += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexTangents = face.vertexTangents;
t1 = vertexTangents[ 0 ];
t2 = vertexTangents[ 1 ];
t3 = vertexTangents[ 2 ];
t4 = vertexTangents[ 3 ];
tangentArray[ offset_tangent ] = t1.x;
tangentArray[ offset_tangent + 1 ] = t1.y;
tangentArray[ offset_tangent + 2 ] = t1.z;
tangentArray[ offset_tangent + 3 ] = t1.w;
tangentArray[ offset_tangent + 4 ] = t2.x;
tangentArray[ offset_tangent + 5 ] = t2.y;
tangentArray[ offset_tangent + 6 ] = t2.z;
tangentArray[ offset_tangent + 7 ] = t2.w;
tangentArray[ offset_tangent + 8 ] = t3.x;
tangentArray[ offset_tangent + 9 ] = t3.y;
tangentArray[ offset_tangent + 10 ] = t3.z;
tangentArray[ offset_tangent + 11 ] = t3.w;
tangentArray[ offset_tangent + 12 ] = t4.x;
tangentArray[ offset_tangent + 13 ] = t4.y;
tangentArray[ offset_tangent + 14 ] = t4.z;
tangentArray[ offset_tangent + 15 ] = t4.w;
offset_tangent += 16;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint );
}
if ( dirtyNormals && normalType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexNormals = face.vertexNormals;
faceNormal = face.normal;
if ( vertexNormals.length === 3 && needsSmoothNormals ) {
for ( i = 0; i < 3; i ++ ) {
vn = vertexNormals[ i ];
normalArray[ offset_normal ] = vn.x;
normalArray[ offset_normal + 1 ] = vn.y;
normalArray[ offset_normal + 2 ] = vn.z;
offset_normal += 3;
}
} else {
for ( i = 0; i < 3; i ++ ) {
normalArray[ offset_normal ] = faceNormal.x;
normalArray[ offset_normal + 1 ] = faceNormal.y;
normalArray[ offset_normal + 2 ] = faceNormal.z;
offset_normal += 3;
}
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexNormals = face.vertexNormals;
faceNormal = face.normal;
if ( vertexNormals.length === 4 && needsSmoothNormals ) {
for ( i = 0; i < 4; i ++ ) {
vn = vertexNormals[ i ];
normalArray[ offset_normal ] = vn.x;
normalArray[ offset_normal + 1 ] = vn.y;
normalArray[ offset_normal + 2 ] = vn.z;
offset_normal += 3;
}
} else {
for ( i = 0; i < 4; i ++ ) {
normalArray[ offset_normal ] = faceNormal.x;
normalArray[ offset_normal + 1 ] = faceNormal.y;
normalArray[ offset_normal + 2 ] = faceNormal.z;
offset_normal += 3;
}
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
}
if ( dirtyUvs && obj_uvs && uvType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
uv = obj_uvs[ fi ];
if ( uv === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uvi = uv[ i ];
uvArray[ offset_uv ] = uvi.x;
uvArray[ offset_uv + 1 ] = uvi.y;
offset_uv += 2;
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
fi = chunk_faces4[ f ];
uv = obj_uvs[ fi ];
if ( uv === undefined ) continue;
for ( i = 0; i < 4; i ++ ) {
uvi = uv[ i ];
uvArray[ offset_uv ] = uvi.x;
uvArray[ offset_uv + 1 ] = uvi.y;
offset_uv += 2;
}
}
if ( offset_uv > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint );
}
}
if ( dirtyUvs && obj_uvs2 && uvType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
uv2 = obj_uvs2[ fi ];
if ( uv2 === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uv2i = uv2[ i ];
uv2Array[ offset_uv2 ] = uv2i.x;
uv2Array[ offset_uv2 + 1 ] = uv2i.y;
offset_uv2 += 2;
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
fi = chunk_faces4[ f ];
uv2 = obj_uvs2[ fi ];
if ( uv2 === undefined ) continue;
for ( i = 0; i < 4; i ++ ) {
uv2i = uv2[ i ];
uv2Array[ offset_uv2 ] = uv2i.x;
uv2Array[ offset_uv2 + 1 ] = uv2i.y;
offset_uv2 += 2;
}
}
if ( offset_uv2 > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint );
}
}
if ( dirtyElements ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
faceArray[ offset_face ] = vertexIndex;
faceArray[ offset_face + 1 ] = vertexIndex + 1;
faceArray[ offset_face + 2 ] = vertexIndex + 2;
offset_face += 3;
lineArray[ offset_line ] = vertexIndex;
lineArray[ offset_line + 1 ] = vertexIndex + 1;
lineArray[ offset_line + 2 ] = vertexIndex;
lineArray[ offset_line + 3 ] = vertexIndex + 2;
lineArray[ offset_line + 4 ] = vertexIndex + 1;
lineArray[ offset_line + 5 ] = vertexIndex + 2;
offset_line += 6;
vertexIndex += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
faceArray[ offset_face ] = vertexIndex;
faceArray[ offset_face + 1 ] = vertexIndex + 1;
faceArray[ offset_face + 2 ] = vertexIndex + 3;
faceArray[ offset_face + 3 ] = vertexIndex + 1;
faceArray[ offset_face + 4 ] = vertexIndex + 2;
faceArray[ offset_face + 5 ] = vertexIndex + 3;
offset_face += 6;
lineArray[ offset_line ] = vertexIndex;
lineArray[ offset_line + 1 ] = vertexIndex + 1;
lineArray[ offset_line + 2 ] = vertexIndex;
lineArray[ offset_line + 3 ] = vertexIndex + 3;
lineArray[ offset_line + 4 ] = vertexIndex + 1;
lineArray[ offset_line + 5 ] = vertexIndex + 2;
lineArray[ offset_line + 6 ] = vertexIndex + 2;
lineArray[ offset_line + 7 ] = vertexIndex + 3;
offset_line += 8;
vertexIndex += 4;
}
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( ! customAttribute.__original.needsUpdate ) continue;
offset_custom = 0;
offset_customSrc = 0;
if ( customAttribute.size === 1 ) {
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ];
customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
offset_custom += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ];
customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ];
offset_custom += 4;
}
} else if ( customAttribute.boundTo === "faces" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
customAttribute.array[ offset_custom ] = value;
customAttribute.array[ offset_custom + 1 ] = value;
customAttribute.array[ offset_custom + 2 ] = value;
offset_custom += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces4[ f ] ];
customAttribute.array[ offset_custom ] = value;
customAttribute.array[ offset_custom + 1 ] = value;
customAttribute.array[ offset_custom + 2 ] = value;
customAttribute.array[ offset_custom + 3 ] = value;
offset_custom += 4;
}
}
} else if ( customAttribute.size === 2 ) {
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v2.x;
customAttribute.array[ offset_custom + 3 ] = v2.y;
customAttribute.array[ offset_custom + 4 ] = v3.x;
customAttribute.array[ offset_cu
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