diff --git a/examples/WebsocketGateway/GatewayConfiguration.properties b/examples/WebsocketGateway/GatewayConfiguration.properties new file mode 100644 index 0000000000000000000000000000000000000000..d9e6cae4676dc496bcfc8a6d19c0d39ceaf7b888 --- /dev/null +++ b/examples/WebsocketGateway/GatewayConfiguration.properties @@ -0,0 +1,11 @@ +# Properties for the local native DIS network. DIS packets are read from +# this socket and forwarded to clients in the web browser. + +# Multicast Address. If using bcast comment this out. +#multicastAddress = 239.1.2.3 +#multicastAddress=225.4.5.6 + +# DIS UDP port. 3000 for VBS2 bcast. +disPort = 3000 + +webserverPort = 8282 diff --git a/examples/WebsocketGateway/LICENSE.txt b/examples/WebsocketGateway/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..dd2d9c8ceb1c137d3e9c415f7f73d0ee3b34f735 --- /dev/null +++ b/examples/WebsocketGateway/LICENSE.txt @@ -0,0 +1,24 @@ +* Copyright (c) 2006-2014, Naval Postgraduate School, MOVES Institute +* 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. +* * Neither the name of the Naval Postgraduate School, MOVES Institute, nor +* the names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY NPS 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 REGENTS AND 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. diff --git a/examples/WebsocketGateway/README.txt b/examples/WebsocketGateway/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..56bf784d6622e8b6d9b9635495d68ae7cca69b77 --- /dev/null +++ b/examples/WebsocketGateway/README.txt @@ -0,0 +1,57 @@ +This is a gateway (or more properly bridge) that reads from +the native TCP/IP network and forwards DIS binary traffic to +web pages in binary format. A javascript implementation of +DIS on the web page decodes the binary DIS and updates a google +maps display. + +The websocket gateway is a web server that listens on, by default, +port 8282. Run the project in netbeans (or via ant), open a +web browser, and go to http://localhost:8282. If you are connected +to the internet you should see a Google Maps display. + +The web page begins sending binary DIS updates to the gateway +over a websocket. Likewise, the gateway will begin forwarding the +local TCP/IP internet DIS traffic to the web page. The web page +will load a clickable icon showing the position of the entity. + +The local web page uses browser geolocation to create an entity +at the approximate location of the user. + +The index.html web page in the content directory holds the +default web page. Supporting javascript files are in +content/javascript, and the DIS implementation is in +content/javascript/dis.js. + +The web server is a Jetty websockets host as well. + +This uses my personal Google Maps API key. That's fine for demo +puproses, but please get your own API key for any real world +use. + +License is BSD. + +* Copyright (c) 2006-2020, Naval Postgraduate School, MOVES Institute +* 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. +* * Neither the name of the Naval Postgraduate School, MOVES Institute, nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY NPS 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 REGENTS AND 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. +DMcG diff --git a/examples/WebsocketGateway/build.xml b/examples/WebsocketGateway/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..296a10f490c7ed1a0d2dcae3c0f3be79cc742158 --- /dev/null +++ b/examples/WebsocketGateway/build.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- You may freely edit this file. See commented blocks below for --> +<!-- some examples of how to customize the build. --> +<!-- (If you delete it and reopen the project it will be recreated.) --> +<!-- By default, only the Clean and Build commands use this build script. --> +<!-- Commands such as Run, Debug, and Test only use this build script if --> +<!-- the Compile on Save feature is turned off for the project. --> +<!-- You can turn off the Compile on Save (or Deploy on Save) setting --> +<!-- in the project's Project Properties dialog box.--> +<project name="WebsocketGateway" default="default" basedir="."> + <description>Builds, tests, and runs the project WebsocketGateway.</description> + <import file="nbproject/build-impl.xml"/> + <!-- + + There exist several targets which are by default empty and which can be + used for execution of your tasks. These targets are usually executed + before and after some main targets. They are: + + -pre-init: called before initialization of project properties + -post-init: called after initialization of project properties + -pre-compile: called before javac compilation + -post-compile: called after javac compilation + -pre-compile-single: called before javac compilation of single file + -post-compile-single: called after javac compilation of single file + -pre-compile-test: called before javac compilation of JUnit tests + -post-compile-test: called after javac compilation of JUnit tests + -pre-compile-test-single: called before javac compilation of single JUnit test + -post-compile-test-single: called after javac compilation of single JUunit test + -pre-jar: called before JAR building + -post-jar: called after JAR building + -post-clean: called after cleaning build products + + (Targets beginning with '-' are not intended to be called on their own.) + + Example of inserting an obfuscator after compilation could look like this: + + <target name="-post-compile"> + <obfuscate> + <fileset dir="${build.classes.dir}"/> + </obfuscate> + </target> + + For list of available properties check the imported + nbproject/build-impl.xml file. + + + Another way to customize the build is by overriding existing main targets. + The targets of interest are: + + -init-macrodef-javac: defines macro for javac compilation + -init-macrodef-junit: defines macro for junit execution + -init-macrodef-debug: defines macro for class debugging + -init-macrodef-java: defines macro for class execution + -do-jar: JAR building + run: execution of project + -javadoc-build: Javadoc generation + test-report: JUnit report generation + + An example of overriding the target for project execution could look like this: + + <target name="run" depends="WebsocketGateway-impl.jar"> + <exec dir="bin" executable="launcher.exe"> + <arg file="${dist.jar}"/> + </exec> + </target> + + Notice that the overridden target depends on the jar target and not only on + the compile target as the regular run target does. Again, for a list of available + properties which you can use, check the target you are overriding in the + nbproject/build-impl.xml file. + + --> + + <!-- Open relevant pages w/ default browser after run has been initiated. + Set default browser to Chrome via NB Preferences --> + <target name="open.web.pages"> + <!--<nbbrowse file="content/index.html"/>--> + <nbbrowse url="http://localhost:8282/3d.html"/> + <nbbrowse url="http://localhost:8282"/> + </target> +</project> diff --git a/examples/WebsocketGateway/content/3d.html b/examples/WebsocketGateway/content/3d.html new file mode 100644 index 0000000000000000000000000000000000000000..f6d6799b3d66ee78fee589b902fb2dc23a0ba705 --- /dev/null +++ b/examples/WebsocketGateway/content/3d.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> + <head> + <title>3D Cube Entities</title> + + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + + <!-- DIS library --> + <script src="scripts/dis.js"></script> + + <!-- three.js 3d javascript library --> + <script src="scripts/three.js"></script> + + <script src="scripts/app/3dsEntity.js"></script> + </head> + <body> + </body> +</html> diff --git a/examples/WebsocketGateway/content/README.txt b/examples/WebsocketGateway/content/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..dd78c93c831e8ae0d3975fcab99e0eb3313778a7 --- /dev/null +++ b/examples/WebsocketGateway/content/README.txt @@ -0,0 +1,29 @@ +The server by default will listen on the local network for broadcast +traffic on UDP port 3000, the same port as is used by VBS2. The server +will read from the local network and forward any ESPDUs to web clients +that connect, in binary DIS format. The web clients will send any +ESPDUs they generate to the server over a websocket (in binary DIS +format) and the server will repeat the message to all web clients +and send on port 3000 on all interfaces on broadcast. + +You can configure the port and whether to use mcast or bcast in the +GatewayConfiguration.properties file. + +This directory holds content served up by the web server, including HTML +files and Javascript files. + +3d.html is a simple three.js WebGL 3D scene that creates an entity in the +world, then sends out binary format IEEE DIS updates to reflect that position. +It also reads from a websocket binary IEEE DIS, does a coordinate system +transformation, and creates a cube to reflect any entities it hears of from +the network. + +index.html implements a simple Google Maps page that displays the location +of entities on a map. + +The javascript directory contains supporting javascript files for DIS +and three.js. DIS files include RangeCoordinates (for coordinate conversions) +and the javascript classes necessary to encode and decode DIS. + +The models directory holds Collada 3D models that can be loaded by web +pages. diff --git a/examples/WebsocketGateway/content/favicon.ico b/examples/WebsocketGateway/content/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7924ad5fad250efae98dcce07c5941e99664dcef Binary files /dev/null and b/examples/WebsocketGateway/content/favicon.ico differ diff --git a/examples/WebsocketGateway/content/images/armor.gif b/examples/WebsocketGateway/content/images/armor.gif new file mode 100644 index 0000000000000000000000000000000000000000..044a1c213ba7b8982fa3f2d9cba94a515a6d6c63 Binary files /dev/null and b/examples/WebsocketGateway/content/images/armor.gif differ diff --git a/examples/WebsocketGateway/content/images/infantry.gif b/examples/WebsocketGateway/content/images/infantry.gif new file mode 100644 index 0000000000000000000000000000000000000000..bb6327b96c9c2d9013f23e6c8b53b4ed9056bb35 Binary files /dev/null and b/examples/WebsocketGateway/content/images/infantry.gif differ diff --git a/examples/WebsocketGateway/content/images/machineGun.gif b/examples/WebsocketGateway/content/images/machineGun.gif new file mode 100644 index 0000000000000000000000000000000000000000..46aac95e20bd8258903da6a091930c9809b60303 Binary files /dev/null and b/examples/WebsocketGateway/content/images/machineGun.gif differ diff --git a/examples/WebsocketGateway/content/index.html b/examples/WebsocketGateway/content/index.html new file mode 100644 index 0000000000000000000000000000000000000000..ada6e4ac1bbcf5d10375a9bf28a9a7c067428013 --- /dev/null +++ b/examples/WebsocketGateway/content/index.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<!-- +Note that if you want to use this web page from clients other than the +one running on the same host as the web browser, you MUST change the +url the websocket connects to. + +A demo that uses google map's Javascript api and web sockets. We receive +updates in IEEE 1278 DIS binary format, and place icons on the map for each +entity's location. The icons are clickable for more info. Typically the server +will be forwarding information, including from the server-local network, for such +information as may be being put out by VBS2 or the like. + +Does the conversion from DIS global (geocentric/earth-centered, earth-fixed) +to lat/lon/alt. +--> +<html> + <head> + <title>DIS Demo Featuring Google Maps</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> + + <style type="text/css"> + html {height:100%} + body { height:100%; margin:0; padding:0 } + #map-canvas{height:100% } + </style> + + <!-- Note: this is using a Google Maps API key linked to me, DMcG. For serious use, go to --> + <!-- https://code.google.com/apis/console and get your own api key. Using this key is fine for demo purposes. --> + <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAZihiWY3ho8hHfkhu9_6mcMSkxSvpjbTI&sensor=true"></script> + + <!-- Binary DIS implementation, also coordinate system conversion utilities --> + <script type="text/javascript" src="scripts/dis.js"></script> + <script type="text/javascript" src="scripts/DisAppearance.js"></script> + + <script type="text/javascript" src="scripts/app/index.js"></script> + </head> + <body> + <div id="map-canvas"></div> + </body> +</html> diff --git a/examples/WebsocketGateway/content/scripts/DisAppearance.js b/examples/WebsocketGateway/content/scripts/DisAppearance.js new file mode 100644 index 0000000000000000000000000000000000000000..ba96c04ce7955653056966bf1cd82db591aa79a2 --- /dev/null +++ b/examples/WebsocketGateway/content/scripts/DisAppearance.js @@ -0,0 +1,114 @@ +/** + * Some code to extract the entity apperance bit fields.<p> + * + * The entityAppearance field in the espdu is a 32 bit integer. To save + * space, several different fields are contained within it. + * Specifically: + * + * Name bit position Purpose + * ---- ------------ -------- + * Paint 0 0 = uniform color, 1=camo + * Mobility 1 0 = no mobility kill, 1 = mobility kill + * Fire Power 2 0 = no firepower kill, 1 = firepower kill + * Damage 3-4 0=no damange, 1=slight, 2=moderate, 3=destroyed + * Smoke 5-6 0=not smoking, 1=smoke plume, 2=emitting engine smoke, 3=engine smoke + smoke plume + * Trailing effects 7-8 dust cloud, 0=none, 1=small, 2=medium, 3=large + * hatch 9-11 0=NA, 1=hatch closed, 2=popped, 3=popped + person visible, 4=open, 5=open and visible + * head lights 12 0=off, 1=on + * tail light 13 0=off, 1=on + * brake lights 14 0=off, 1=on + * flaming 15 0=none, 1=flames present + * launcher 16 0=not raised, 1=raised + * camo type 17-18 0=desert, 1=winter, 2=forest + * concealed 19 0=not concealed, 1=prepared concealed position (netting, etc) + * frozen status 20 0=not frozen, 1=frozen (in simulation terms) + * power plant 22 0=power plant off 1=on + * state 23 0=active, 1=deactivated + * tent 24 0=not extended 1=extended + * ramp 25 0=not extended, 1=extended + * blackout lights 26 0=off, 1=on + * blackout brake 27 0=off, 1=on + * spot lights 28 0=off, 1=on + * interior lights 29 0=off, 1=on + * unused 30-31 + * + * Typical use: + * + * var entityAppearance = new DisAppearance(espdu.entityAppearance); + * var damage = entityAppearance.getBitfield(3, 4); + * + * This returns the "damage" bitfield in bits 3-4. + * + * var mobility = entityAppearance.getBitfield(1, 1); + * + * Returns the mobility field, 0 = no mobo kill, 1 = mobility kill + * + * @author DMcG + **/ + +/** Constructor. Takes the integer value extracted from the DIS Entity State Field appearance + * + * @param {type} integerValue the entity appearance from the espdu + * @returns {undefined} + */ +DisAppearance = function(integerValue) +{ + this.entityAppearance = integerValue; +}; + +/** + * Test code for creating the correct bitmask + * @returns {undefined} + */ +DisAppearance.prototype.getTestMask = function() +{ + mask = 0; + for(var idx = 0; idx < 7; idx++) + { + mask = mask + this.bit_set(mask, idx); + } + + return mask; +}; + +/** + * + * @param {integer} startPosition + * @param {integer} finishPosition + * @returns {integer} + */ +DisAppearance.prototype.getBitField = function(startPosition, finishPosition) +{ + // do some sanity checks + if(startPosition < 0 || startPosition > 31 || finishPosition < 0 || finishPosition > 31 || startPosition > finishPosition) + { + console.log("invalid start or finish for bitfield values: ", startPosition, " ", finishPosition); + return 0; + } + + // Develop the mask. Addition is equivalent to setting multiple bits. + var mask = 0; + for(var idx = startPosition; idx <= finishPosition; idx++) + { + mask = mask + this.bit_set(0, idx); + } + + // do the bitmask + var maskedValue = this.entityAppearance & mask; + // Shift bits to get the normalized value + var fieldValue = maskedValue >>> startPosition; + + return fieldValue; +}; + +/** Set the "bit" position in a number to 1 + * + * @param {integer} num the number whose bit we are setting. Typically zero. + * @param {integer} bit which bit to set + * @return {integer} the number passed in, with the "bit"th bit flipped on. + **/ +DisAppearance.prototype.bit_set = function(num, bit) +{ + return num | 1<<bit; +}; + diff --git a/examples/WebsocketGateway/content/scripts/app/3dsEntity.js b/examples/WebsocketGateway/content/scripts/app/3dsEntity.js new file mode 100644 index 0000000000000000000000000000000000000000..370f5c959a2433467ae33bf669f136dbde7653b9 --- /dev/null +++ b/examples/WebsocketGateway/content/scripts/app/3dsEntity.js @@ -0,0 +1,196 @@ +/* global dis, THREE */ + +/** Unique(ish) ID for this web page entity.*/ +var uniqueId = Math.floor(Math.random() * 10000); + +/** List of all entities in the world */ +var allEntities = {}; + +/** The entity this web page controls */ +var ourEntity = {}; + +/** The websocket back to the server */ +var websocket; + +/** three.js scene */ +var renderer, scene; + +/** Local East-North-Up (ENU) tangent plane coordinates for Monterey, CA */ +var rangeCoordinates = new dis.RangeCoordinates(36.595, -121.877, 0.0); + +/** Fired when the window is finished loading. Create the 3D scene and initialize + * communications with the server. + */ +window.onload = function () +{ + renderer = new THREE.WebGLRenderer(); + renderer.setSize(1024, 680); + document.body.appendChild(renderer.domElement); + + scene = new THREE.Scene(); + + // Compatiability checks--not all web browsers support Websockets. + // note that to reach this web server from off-host you MUST change + // the localhost to an IP or DNS name. + if (window.WebSocket) + websocket = new WebSocket("ws://localhost:8282");//, "nve"); + else if (window.MozWebSocket) + websocket = new MozWebSocket("ws://localhost:8282");//, "nve"); + else + alert("This web browser does not support web sockets"); + + // Note that though we have created the web socket, we can at this + // point still not send messages to the server. That can happen only + // after the onopen method is called at some later time. + + // Set the format we want to use to receive binary messages + websocket.binaryType = 'arraybuffer'; + + // Attach functions to the the web socket for various events + websocket.onopen = function (evt) { + console.log("websocket onopen"); + }; + websocket.onclose = function (evt) { + console.log("websocket close"); + }; + websocket.onerror = function (evt) { + console.log("websocket error"); + }; + + // Set the format we want to use to receive binary messages + websocket.binaryType = 'arraybuffer'; + + // Handle the messages sent from the server to us here + websocket.onmessage = function (evt) + { + // Convert the JSON into a javascript object + // Use this to convert JSON message format + //var disMessage = eval('(' + evt.data + ')' ); + + // convert from binary to javascript object + var inputStream = new dis.InputStream(evt.data); + var disMessage = new dis.EntityStatePdu(); + disMessage.initFromBinaryDIS(inputStream); + //console.log(disMessage); + + // The entityID in the DIS message should be a unqiue triplet. We can + // use that as a key to create an attribute on an object--in effect this + // is a sneaky hash table. + eid = disMessage.entityID; + eidString = JSON.stringify(eid); + + // Look up the entity in our database. If not found, this is a + // new entity and we should add it. + thisEntity = allEntities[eidString]; + + // First time we've heard from this entity--create a new one to represent it + if (thisEntity === undefined) + { + console.log("heard from new entity ", eidString); + var newEntity = {}; + newEntity.lastEspdu = disMessage; + var mesh = newCube(0xFF0000); + newEntity.mesh = mesh; + thisEntity = newEntity; + scene.add(mesh); + allEntities[eidString] = newEntity; + } + + // Convert from global coordinates (center of the earth/geocentric/ECEF) + // to the local coordinate system, a plane tangent to the earth at the + // given origin for the rangeCoordinates object. + var localCoordinates = rangeCoordinates.ECEFObjectToENU(disMessage.entityLocation); + thisEntity.mesh.position.x = localCoordinates.x; + thisEntity.mesh.position.y = localCoordinates.y; + thisEntity.mesh.position.z = localCoordinates.z; + thisEntity.lastEspdu = disMessage; + + renderer.render(scene, camera); + }; + + // Scene setup + var camera = new THREE.PerspectiveCamera(35, // Field of view + 1024 / 680, // Aspect ratio + .1, // Near + 10000); // Far + + camera.position.set(-15, 10, 15); + camera.lookAt(scene.position); + + // Put our cube, the cube controlled by our web page, at some semi-random position and color, so they don't + // sit on top of each other. Draw our cube as green, and all other cubes as red. + var mesh = newCube(0x00FF00); + ourEntity.mesh = mesh; + + // Create an espdu to represent our state + ourEntity.lastEspdu = new dis.EntityStatePdu(); + + // Init the unique ID for this entity, and a position. We need to convert + // from the local coordinate system to the global DIS coordinate system. + var localPosition = {x: 0, y: 0, z: 0}; + ourEntity.lastEspdu.entityID.entity = Math.round(Math.random() * 15000); + localPosition.x = Math.round(Math.random() * 7); + localPosition.y = Math.round(Math.random() * 7); + localPosition.z = Math.round(Math.random() * 7); + + var globalCoordinates = rangeCoordinates.ENUObjectToECEF(localPosition); + ourEntity.lastEspdu.entityLocation.x = globalCoordinates.x; + ourEntity.lastEspdu.entityLocation.y = globalCoordinates.y; + ourEntity.lastEspdu.entityLocation.z = globalCoordinates.z; + + // Set the position of the mesh. + ourEntity.mesh.position.x = localPosition.x; + ourEntity.mesh.position.y = localPosition.y; + ourEntity.mesh.position.z = localPosition.z; + + scene.add(ourEntity.mesh); + + var light = new THREE.PointLight(0xFFFF00); + light.position.set(10, 0, 10); + scene.add(light); + renderer.render(scene, camera); + + // Periodically send heartbeat messages + setInterval(heartbeat, 1000); + + console.log("started web page"); +}; + +/** + * Periodically send messages to the server + */ +function heartbeat() +{ + //console.log("heartbeat"); + // use this to send in JSON format + //websocket.send(JSON.stringify(ourEntity.lastEspdu)); + + // Use this to send in standard IEEE DIS binary format + var dataBuffer = new ArrayBuffer(1500); + var outputStream = new dis.OutputStream(dataBuffer); + + // Wiggle the marker icon + ourEntity.lastEspdu.entityLocation.x += Math.round(Math.random() * 7); + ourEntity.lastEspdu.entityLocation.y += Math.round(Math.random() * 7); + ourEntity.lastEspdu.entityLocation.z += Math.round(Math.random() * 7); + + ourEntity.lastEspdu.encodeToBinaryDIS(outputStream); + var trimmedData = dataBuffer.slice(0, outputStream.currentPosition); + + var readyState = websocket.readyState; + if (readyState === 1) // 1 == OPEN + websocket.send(trimmedData); +} + +/** + * Returns a THREE.Mesh cube + * @param myColor RGB value, two bytes per value eg 0xFF0000 is red. + */ +function newCube(myColor) +{ + var geometry = new THREE.CubeGeometry(2, 2, 2); + var material = new THREE.MeshLambertMaterial({color: myColor}); + var mesh = new THREE.Mesh(geometry, material); + + return mesh; +} diff --git a/examples/WebsocketGateway/content/scripts/app/index.js b/examples/WebsocketGateway/content/scripts/app/index.js new file mode 100644 index 0000000000000000000000000000000000000000..726c9ff76fd98e551592b8793f281db4c4a90dc6 --- /dev/null +++ b/examples/WebsocketGateway/content/scripts/app/index.js @@ -0,0 +1,337 @@ +/* global google, dis */ + +var map; +var websocket; + +/** A likely map location */ +var montereyLocation = new google.maps.LatLng(36.595, -121.877); + +/** Marker for the entity represented by this web browser's location */ +var ourMarker; + +/** current location of the device running this browser, as defined by geolocation api */ +var browserPosition; + +/** Hash table Used to keep track of all entities in the world */ +var allEntities = {}; + +/** EntityStatePdu for the entity represented by this page. This is + * periodically sent as a heartbeat, so others can find us. + */ +var espdu = new dis.EntityStatePdu(); +console.log(JSON.stringify(espdu)); + +/** Used to convert DIS earth-centered coordinates to lat/lon/alt */ +var conversion = new dis.CoordinateConversion(); + +/** benchmarking */ +var messageCount = 0; +var startTime = new Date(); + +// Set the initialize function to run on page load +google.maps.event.addDomListener(window, 'load', initialize); + +/** + * Initialize the map and websocket + * + * @returns {undefined} + */ +function initialize() +{ + // Initialize the PDU that represents this web browser page. The + // geolocation API is used to place this entity whereever the web + // browser happens to be located. For example, if the computer running + // this web browser page is in Orlando, the icon will drop in Orlando. + + // Entity ID + espdu.entityID.site = 42; + espdu.entityID.application = 17; + espdu.entityID.entity = Math.round(Math.random() * 16000); // Unique (ish) ID + + // What type of entity this is--in this case a dismounted infantry guy + espdu.entityType.entityKind = 3; // life form + espdu.entityType.domain = 1; // land + espdu.entityType.country = 225; // US + espdu.entityType.cat = 1; // dismounted infantry + espdu.entityType.subcategory = 17; // Mini-14! A-Team roolz! + espdu.entityType.specific = 1; // number of people + + espdu.timestamp = 1; // Timestamp is bogus--should fix this. Just needs to be incremented at a miminim + + // Use the geolocation API to find the current location + // Firefox keeps asking every time this is called, and you can't + // get in fast enough to choose the "always allow" function. + window.setInterval(getLocation, 5000); + // Send our state periodically + window.setInterval(heartbeat, 1000); + // periodically prune entities that we haven't heard from lately + window.setInterval(prune, 60000); + + // The region of the map that's visible, and where it's centered. + var mapOptions = + { + center: montereyLocation, + zoom: 14, + mapTypeId: google.maps.MapTypeId.ROADMAP + }; + map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions); + + // Compatiability checks--not all web browsers support Websockets. + // note that the host portion of the URL (localhost here) MUST + // be changed to a DNS name or IP to reach it from off-host. The + // existing localhost is just there for development purposes. + if (window.WebSocket) + websocket = new WebSocket("ws://localhost:8282");//, "nve"); + else if (window.MozWebSocket) + websocket = new MozWebSocket("ws://localhost:8282");//, "nve"); + else + alert("This web browser does not support web sockets"); + + // Set the format we want to use to receive binary messages + websocket.binaryType = 'arraybuffer'; + + // Attach functions to the the web socket for various events + websocket.onopen = function (evt) { + console.log("Opened websocket"); + };//console.log("websocket onopen");}; + websocket.onclose = function (evt) { + console.log("websocket close", evt.data); + }; + websocket.onerror = function (evt) { + console.log("websocket error", evt.data); + }; + + /** Handle the messages sent from the server to us here. We receive binary + * DIS from the server over the web socket. + * @param {event} evt The receive event object. Contains the binary data to decode + */ + websocket.onmessage = function (evt) + { + // Factory object for creating new PDUs from binary data + var pduFactory = new dis.PduFactory(); + + // InputStream is modeled on a Java input stream. Pass it binary data, + // and we will read through it, while input stream maintains a current + // place marker + //console.log(evt.data); + //var inputStream = new dis.InputStream(evt.data); + + var disMessage = pduFactory.createPdu(evt.data); + + // You can trap the various types of PDU here, and + // probably call a specific function to handle each + // type of message + switch (disMessage.pduType) + { + case 1: + console.log("ESPDU"); + break; + + // We don't actually do any logic with these yet + case 2: + console.log("FirePdu"); + return; + default: + return; + } + + // The ESPDU message sent by the server. Note that we do not attempt + // to determine the type of PDU sent by the server; they're all ESPDUs, + // or should be. + //var disMessage = new dis.EntityStatePdu(); + //console.log(evt.data); + //var disMessage = eval(evt.data); + //console.log(disMessage); + + // Non-ESPDUs will fail here; that's slow, but OK. You can + // filter out all but ESPDUs on the server side if you like, + // or do a test on this side (before decoding) to make sure + // the incoming message is an ESDPU, the only type we handle. + //disMessage.initFromBinaryDIS(inputStream); + + // Some instrumentation for performance. This is useful if hooked + // up to an AIS feed, for example. + + messageCount++; + if (messageCount % 10000 === 0) + { + var timeNow = new Date(); + var difference = timeNow.getTime() - startTime.getTime(); + console.log("Packets per second for 10K packets: ", 10000 / difference * 1000); + startTime = new Date(); + console.log(JSON.stringify(disMessage)); + } + + // Retrieve the entity ID from the DIS message, convert it to a JSON + // string, and then use that as a sleazy key into a "hash table" consisting + // of object attributes. + + var entityID = disMessage.entityID; + var eidString = JSON.stringify(entityID); + + // Convert from DIS global coordinates to latitude/longtitude + var latLonAlt = conversion.convertDisToLatLongInDegrees(disMessage.entityLocation); + + // We have an attribute on this object named the JSON text string, with a + // value of the marker data. Look that up. + var theMarker = allEntities[eidString]; + + // If it's not found, that means it's the first time we've heard of it. Create + // a new one. + if (theMarker === undefined) + { + theMarker = {}; + + var contentString = "<b>Entity ID:</b>" + JSON.stringify(disMessage.entityID) + "<br>"; + contentString = contentString + "<b>Entity Type:</b>" + JSON.stringify(disMessage.entityType) + "<br>"; + var infowindow = new google.maps.InfoWindow({content: contentString}); + + var newEntityLocation = new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude); + // The marker (with the default icon) is automatically added + // to the map when created + var marker = new google.maps.Marker({ + map: map, + draggable: true, + animation: google.maps.Animation.DROP, + position: newEntityLocation, + title: "DIS Entity from Network" + //icon:image + }); + + // add a function to be called when the user clicks on the icon + google.maps.event.addListener(marker, 'click', function () + { + infowindow.open(map, marker); + }); + + theMarker.marker = marker; + + //console.log("new theMarker:", theMarker); + allEntities[eidString] = theMarker; + } + + // The marker for the entity has either just been added, or recognized as already + // existing. In either event, update it's position on the map. + // Update the marker position + theMarker.marker.setPosition(new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude)); + theMarker.lastEspdu = disMessage; + theMarker.lastHeardFrom = new Date(); + + }; // end of onmessage + +} + +/** + * Removes icons that we haven't heard from in some amount of time + */ +function prune() +{ + var TIMEOUT = 120000; // 2 min + var now = new Date(); + + for (var anEntity in allEntities) + { + var entityInfo = allEntities[anEntity]; + if (now.getTime() - entityInfo.lastHeardFrom.getTime() > TIMEOUT) + { + var markerInfo = allEntities[anEntity]; + markerInfo.marker.setMap(null); + delete allEntities[anEntity]; + } + } +} + +/** + * Called periodicaly to send out an update of our position. Uses the + * browser geolocation routines to set the PDUs location, which is useful + * for mobile devices. If we have no local position fix, do not send out + * an update. + */ +function heartbeat() +{ + // We don't have a current position; sending an update is sort of pointless + if (browserPosition === undefined) + return; + + //console.log("heartbeat type pdu:", espdu.pduType); + var conversion = new dis.CoordinateConversion(); + latLonAlt = {}; + latLonAlt.lat = browserPosition.coords.latitude; + latLonAlt.lon = browserPosition.coords.longitude; + latLonAlt.alt = 0.0; + + // bump timestamp + espdu.timestamp++; + + // Set location and wiggle the marker icon a bit + var disCoordinates = conversion.getXYZfromLatLonAltDegrees(latLonAlt); + espdu.entityLocation.x = disCoordinates.x + Math.round(Math.random() * 7); + espdu.entityLocation.y = disCoordinates.y + Math.round(Math.random() * 7); + espdu.entityLocation.z = disCoordinates.z + Math.round(Math.random() * 7); + + // Marshal out the PDU that represents the local browser's position + // to the IEEE DIS binary format. We allocate a big buffer to write, + // and if the actual data occupies less than that, trim to fit. + var dataBuffer = new ArrayBuffer(1000); // typically 144 bytes, make it bigger for safety + var outputStream = new dis.OutputStream(dataBuffer); + espdu.encodeToBinaryDIS(outputStream); + + // Trim to fit + var trimmedData = dataBuffer.slice(0, outputStream.currentPosition); + + var readyState = websocket.readyState; + if (readyState === 1) // 1 == OPEN + websocket.send(trimmedData); + + // Create or update our marker position + var ourLocation = new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude); + if (ourMarker === undefined) + { + ourMarker = new google.maps.Marker({ + map: map, + draggable: true, + animation: google.maps.Animation.DROP, + position: ourLocation, + title: "DIS Local Entity", + icon: "http://maps.google.com/mapfiles/ms/icons/green-dot.png" + }); + + // Set up an infowindow that will be opened when the user clicks the green (self) icon + var contentString = "<b>Entity ID:</b>" + JSON.stringify(espdu.entityID) + "<br>"; + contentString = contentString + "<b>Entity Type:</b>" + JSON.stringify(espdu.entityType) + "<br>"; + var infowindow = new google.maps.InfoWindow({content: contentString}); + google.maps.event.addListener(ourMarker, 'click', function () + { + infowindow.open(map, ourMarker); + }); + } // end of creating new marker + + // Update the marker position + ourMarker.setPosition(new google.maps.LatLng(browserPosition.coords.latitude, browserPosition.coords.longitude)); + +} + +/** Standard browser geolocation code to get the location of the device + * that this web page is running on + */ +function getLocation() +{ + //console.log("geolocation called"); + if (navigator.geolocation) + { + // Works via callback to function passed in + navigator.geolocation.getCurrentPosition(showPosition); + //console.log("callback registered"); + } else + { + console.log("Geolocation not supported by this browser"); + } +} + +/** Callback function. Gets the location as the browser knows it. */ +function showPosition(position, error) +{ + browserPosition = position; + //console.log("Latitude:", position.coords.latitude , "Longitude: " + position.coords.longitude); +} + diff --git a/examples/WebsocketGateway/content/scripts/dis.js b/examples/WebsocketGateway/content/scripts/dis.js new file mode 100644 index 0000000000000000000000000000000000000000..6c75df1b2e6e6bfd5fa5d87e43863d9f01141ad3 --- /dev/null +++ b/examples/WebsocketGateway/content/scripts/dis.js @@ -0,0 +1,11434 @@ +if (typeof dis === "undefined") + dis = {}; + +dis.CoordinateConversion = function() + { + + this.RADIANS_TO_DEGREES = 180.0/Math.PI; + this.DEGREES_TO_RADIANS = Math.PI/180.0; + + this.a = 6378137.0; //semi major axis (WGS 84) + this.b = 6356752.3142; //semi minor axis (WGS 84) + + /** + * Converts DIS xyz world coordinates to latitude and longitude (IN DEGREES). This algorithm may not be 100% accurate + * near the poles. Uses WGS84 , though you can change the ellipsoid constants a and b if you want to use something + * else. These formulas were obtained from Military Handbook 600008. The code itself has been + * translated from C to Java to Javascript over the years, so hold onto your hats. + * + * @param position {x:, y:, z:} + * @return {latitude:, longitude: altitude:} + */ + this.convertDisToLatLongInDegrees = function(position) + { + var x = position.x; + var y = position.y; + var z = position.z; + var answer = []; + answer[0] = 0.0; + answer[1] = 0.0; + answer[2] = 0.0; + var a = 6378137.0; //semi major axis (WGS 84) + var b = 6356752.3142; //semi minor axis (WGX 84) + + var eSquared; //first eccentricity squared + var rSubN; //radius of the curvature of the prime vertical + var ePrimeSquared;//second eccentricity squared + var W = Math.sqrt((x*x + y*y)); + + eSquared = (a*a - b*b) / (a*a); + ePrimeSquared = (a*a - b*b) / (b*b); + + /** + * Get the longitude. + */ + if(x >= 0 ) + { + answer[1] = Math.atan(y/x); + } + else if(x < 0 && y >= 0) + { + answer[1] = Math.atan(y/x) + Math.PI; + } + else + { + answer[1] = Math.atan(y/x) - Math.PI; + } + /** + * Longitude calculation done. Now calculate latitude. + * NOTE: The handbook mentions using the calculated phi (latitude) value to recalculate B + * using tan B = (1-f) tan phi and then performing the entire calculation again to get more accurate values. + * However, for terrestrial applications, one iteration is accurate to .1 millimeter on the surface of the + * earth (Rapp, 1984, p.124), so one iteration is enough for our purposes + */ + + var tanBZero = (a*z) / (b * W); + var BZero = Math.atan((tanBZero)); + var tanPhi = (z + (ePrimeSquared * b * (Math.pow(Math.sin(BZero), 3))) ) /(W - (a * eSquared * (Math.pow(Math.cos(BZero), 3)))); + var phi = Math.atan(tanPhi); + answer[0] = phi; + /** + * Latitude done, now get the elevation. Note: The handbook states that near the poles, it is preferable to use + * h = (Z / sin phi ) - rSubN + (eSquared * rSubN). Our applications are never near the poles, so this formula + * was left unimplemented. + */ + rSubN = (a*a) / Math.sqrt(((a*a) * (Math.cos(phi)*Math.cos(phi)) + ((b*b) * (Math.sin(phi)*Math.sin(phi))))); + + answer[2] = (W / Math.cos(phi)) - rSubN; + + var result = {latitude:answer[0] * this.RADIANS_TO_DEGREES, longitude:answer[1] * this.RADIANS_TO_DEGREES, altitude:answer[2] * this.RADIANS_TO_DEGREES}; + return result; + + }; + + /** + * Converts lat long and geodetic height (elevation) into DIS XYZ + * This algorithm also uses the WGS84 ellipsoid, though you can change the values + * of a and b for a different ellipsoid. Adapted from Military Handbook 600008 + * @param latLonAlt {lat: lon: alt:} in degrees and meters + * @return {x: y: z:} in meters + */ + this.getXYZfromLatLonAltDegrees = function(latLonAlt) + { + var latitudeRadians = latLonAlt.lat * this.DEGREES_TO_RADIANS; + var longtitudeRadians = latLonAlt.lon * this.DEGREES_TO_RADIANS; + + //var a = 6378137.0; //semi major axis + //var b = 6356752.3142; //semi minor axis + var cosLat = Math.cos(latitudeRadians); + var sinLat = Math.sin(latitudeRadians); + + + var rSubN = (this.a*this.a) / Math.sqrt(((this.a*this.a) * (cosLat*cosLat) + ((this.b*this.b) * (sinLat*sinLat)))); + + var X = (rSubN + latLonAlt.alt) * cosLat * Math.cos(longtitudeRadians); + var Y = (rSubN + latLonAlt.alt) * cosLat * Math.sin(longtitudeRadians); + var Z = ((((this.b*this.b) / (this.a*this.a)) * rSubN) + latLonAlt.alt) * sinLat; + + return {x:X, y:Y, z:Z}; + }; + }; +//var BigInteger = require('BigInteger'); + +if (typeof dis === "undefined") + dis = {}; + +dis.InputStream = function(binaryData) +{ + this.dataView = new DataView(binaryData, 0); // data, byte offset + this.currentPosition = 0; // ptr to "current" position in array + + dis.InputStream.prototype.readUByte = function() + { + var data = this.dataView.getUint8(this.currentPosition); + this.currentPosition = this.currentPosition + 1; + return data; + }; + + dis.InputStream.prototype.readByte = function() + { + var data = this.dataView.getInt8(this.currentPosition); + this.currentPosition = this.currentPosition + 1; + return data; + }; + + dis.InputStream.prototype.readUShort = function() + { + var data = this.dataView.getUint16(this.currentPosition); + this.currentPosition = this.currentPosition + 2; + return data; + }; + + dis.InputStream.prototype.readShort = function() + { + var data = this.dataView.getInt16(this.currentPosition); + this.currentPosition = this.currentPosition + 2; + return data; + }; + + dis.InputStream.prototype.readUInt = function() + { + var data = this.dataView.getUint32(this.currentPosition); + this.currentPosition = this.currentPosition + 4; + return data; + }; + + dis.InputStream.prototype.readInt = function() + { + var data = this.dataView.getInt32(this.currentPosition); + this.currentPosition = this.currentPosition + 4; + return data; + }; + + /** Read a long integer. Assumes big endian format. Uses the BigInteger package. */ + dis.InputStream.prototype.readLongInt = function() + { + var data1 = this.dataView.getInt32(this.currentPosition); + var data2 = this.dataView.getInt32(this.currentPosition + 4); + + this.currentPosition = this.currentPosition + 8; + + }; + + dis.InputStream.prototype.readFloat32 = function() + { + var data = this.dataView.getFloat32(this.currentPosition); + this.currentPosition = this.currentPosition + 4; + return data; + }; + + dis.InputStream.prototype.readFloat64 = function() + { + var data = this.dataView.getFloat64(this.currentPosition); + this.currentPosition = this.currentPosition + 8; + return data; + }; + + dis.InputStream.prototype.readLong = function() + { + console.log("Problem in dis.InputStream. Javascript cannot natively handle 64 bit ints"); + console.log("Returning 0 from read, which is almost certainly wrong"); + this.currentPosition = this.currentPosition + 8; + return 0; + }; +}; + + +if (typeof dis === "undefined") + dis = {}; + +/** + * @param binaryDataBuffer ArrayBuffer +*/ +dis.OutputStream = function(binaryDataBuffer) +{ + this.binaryData = binaryDataBuffer; + this.dataView = new DataView(this.binaryData); // data, byte offset + this.currentPosition = 0; // ptr to current position in array + + dis.OutputStream.prototype.writeUByte = function(userData) + { + this.dataView.setUint8(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 1; + }; + + dis.OutputStream.prototype.writeByte = function(userData) + { + this.dataView.setInt8(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 1; + }; + + dis.OutputStream.prototype.writeUShort = function(userData) + { + this.dataView.setUint16(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 2; + }; + + dis.OutputStream.prototype.writeShort = function(userData) + { + this.dataView.setInt16(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 2; + }; + + dis.OutputStream.prototype.writeUInt = function(userData) + { + this.dataView.setUint32(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 4; + }; + + dis.OutputStream.prototype.writeInt = function(userData) + { + this.dataView.setInt32(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 4; + }; + + dis.OutputStream.prototype.writeFloat32 = function(userData) + { + this.dataView.setFloat32(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 4; + }; + + dis.OutputStream.prototype.writeFloat64 = function(userData) + { + this.dataView.setFloat64(this.currentPosition, userData); + this.currentPosition = this.currentPosition + 8; + }; + + dis.OutputStream.prototype.writeLong = function(userData) + { + console.log("Problem in dis.outputStream. Javascript cannot natively handle 64 bit ints"); + console.log("writing 0, which is almost certainly wrong"); + this.dataView.setInt32(this.currentPosition, 0); + this.dataView.setInt32(this.currentPosition + 4, 0); + this.currentPosition = this.currentPosition + 8; + }; +}; + + + + + +if (typeof dis === "undefined") + dis = {}; + + /** + * The PDU factory is responsible for decoding binary data and turning + * it into the appropriate type of PDU. + * + * The websocket will typically send the web page a IEEE 1278.1 binary + * array of data. It could be any one of dozens of PDUs. The start of + * all PDUs is the same--they have the same header. One of the fields in + * the header is the PduType, an 8 bit integer with a unqiue value for + * each type of PDU. We have to peak at that value, decide what type + * of PDU to create of the binary we have received, and then decode it. + * + * * @DMcG + */ + + dis.PduFactory = function() + { + + }; + + /** + * decode incoming binary data and + * return the correct type of PDU. + * + * @param {type} data the IEEE 1278.1 binary data + * @returns {Pdu} Returns an instance of some PDU, be it espdu, fire, detonation, etc. Exception if PduType not known. + */ + dis.PduFactory.prototype.createPdu = function(data) + { + var asUint8Array = new Uint8Array(data); + var pduType = asUint8Array[2]; + var inputStream = new dis.InputStream(data); + var newPdu; + + switch(pduType) + { + case 1: // entity state PDU + newPdu = new dis.EntityStatePdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 2: // Fire + newPdu = new dis.FirePdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 3: // detonation + newPdu = new dis.DetonationPdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 4: // Collision + newPdu = new dis.CollisionPdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 11: // Create entity + newPdu = new dis.CreateEntityPdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 12: // Remove entity + newPdu = new dis.RemoveEntityPdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + case 20: // data + newPdu = new dis.DataPdu(); + newPdu.initFromBinaryDIS(inputStream); + break; + + default: + throw "PduType: " + pduType + " Unrecognized PDUType. Add PDU in dis.PduFactory."; + } + + return newPdu; + }; + + +/** + * Sets up a local tangent place (ENU) coordinate system at a given location + * and altitude, and handles conversions between geodetic, ECEF, and local + * tangent plane coordinate systems. + * + * For reference see "Conversion of Geodetic coordinates to the Local + * Tangent Plane", version 2.01, + * http://www.psas.pdx.edu/CoordinateSystem/Latitude_to_LocalTangent.pdf + * + * and "Geodetic Systems", + * http://wiki.gis.com/wiki/index.php/Geodetic_system#From_geodetic_coordinates_to_local_ENU_coordinates + * + * There's also a bunch of ancient code from older versions that someone, somewhere, + * lifted from a military handbook, originally written in C, translated to Java, + * and now translated to Javascript. + * + * Terminology: + * + * ECEF: earth centered, earth fixed coordinate system, same as DIS. Cartesian, + * origin at center of the earth, z through north pole, x out the equator and + * prime meridian, y out equator and 90 deg east. This coordinate system rotates + * with the earth, ie the x axis always points out the prime meridian and equator + * even as the earth rotates. + * + * Geodetic: latitude, longitude, altitude. + * + * WGS84: Shape of the earth, an ellipsoid roughly, with a and b the semimajor and semiminor axes + * + * ENU: East, North, Up: local coordinate system with a given geodetic origin. Tangent + * plane to the earth. + * + * All Errors mine + * + * @DMcG + * + * @param {float} lat latitude in degrees of the origin of the local tangent plane coordinate system + * @param {float} lon longitude, in degrees, of origin + * @param {float} alt altitude, in meters, of the origin of the local tangent plane coordinate system + */ + +if (typeof dis === "undefined") + dis = {}; + +/** Constructor, creates an object that can do coordinate systems conversions. + * Takes a geodetic point that is the origin of a tangent plane to the surface + * of the earth. This is useful for doing local simulation work. The local + * coordinate system has postive x east, positive y north, and positive Z up, + * aka an ENU coordinate system. Methods for converting from that coordinate system + * to the DIS (ECEF) coordinate system or geotetic coordinate systems are provided. + * + * @param {type} lat latitude, in degrees, of where the local tangent plane is located + * @param {type} lon longitude, in degrees, of the origin of the local tangent plane + * @param {type} alt altitude, in meters, of the origin of the local tangent plane + * @returns {RangeCoordinates} An object that can do coordinate system conversions + */ +dis.RangeCoordinates = function(lat, lon, alt) +{ + this.RADIANS_PER_DEGREE = 2 * Math.PI / 360.0; + this.DEGREES_PER_RADIAN = 360.0 / (2* Math.PI); + + /** WGS84 semimajor axis (constant) */ + this.a = 6378137.0; + + /** WGS84 semiminor axis (constant) */ + this.b = 6356752.3142; + + /** Ellipsoidal Flatness (constant) */ + this.f = (this.a - this.b) / this.a; // Should be 3.3528107 X 10^-3 + + /** Eccentricity (constant) */ + this.e = Math.sqrt(this.f * (2 - this.f)); // Should be 8.1819191 X 10^-2 + + // The origin of the local, East-North-Up (ENU) coordinate system, in lat/lon degrees and meters. + this.ENUOrigin = {}; + this.ENUOrigin.latitude = lat; + this.ENUOrigin.longitude = lon; + this.ENUOrigin.altitude = alt; + + // Find the origin of the ENU in earth-centered, earth-fixed ECEF aka DIS coordinates + this.ENUOriginInECEF = {}; + this.ENUOriginInECEF = this.latLonAltDegreesToECEF(lat, lon, alt); +}; + + /** Determines N, the distance from a normal plane at the given + * latitude to the Z-axis running through the center of the earth. + * This is NOT the same as the distance to the center of the earth. + * + * @param {float} lambda the latitude, in radians. + * @returns {float} distance in meters from the latitude to the axis of the earth + */ + dis.RangeCoordinates.prototype.N = function(lambda) + { + //N(lambda) = a / sqrt( 1 - e^2 * sin^2(lambda) ) + var val = this.a / Math.sqrt(1- ( Math.pow(this.e, 2) * Math.pow( Math.sin(lambda), 2) ) ); + return val; + }; + + /** + * Converts a latitude, longitude, and altitude object to DIS rectilinear + * coordinates, aka earth-centered, earth-fixed, rectilinear. + * + * @param {latitude:longitude:altitude:} latLonAlt The lat/lon/alt, in degrees and meters + * @returns {x, y, z} rectilienar coordinates in ECEF, aka DIS coordinates + */ + dis.RangeCoordinates.prototype.latLonAltDegreesObjectToECEF = function(latLonAlt) + { + return this.latLonAltDegreesToECEF(latLonAlt.latitude, latLonAlt.longitude, latLonAlt.altitude); + }; + + /** + * Converts a latitude, longitude, and altitude to DIS rectilinear + * coordinates, aka earth-centered, earth-fixed, rectilinear. + * + * @param {float} latitude (in radians) + * @param {float} longitude (in radians) + * @param {float} altitude (in meters) + * @returns {x, y, z} rectilienar coordinates in ECEF-r, aka DIS coordinates + */ + dis.RangeCoordinates.prototype.latLonAltRadiansToECEF = function(latitude, longitude, altitude) + { + /* + // altitude corresponds to h in the paper, lambda to latitude, phi to longitude + var x = (altitude + this.N(latitude)) * Math.cos(latitude) * Math.cos(longitude); + var y = (altitude + this.N(latitude)) * Math.cos(latitude) * Math.sin(longitude); + var z = (altitude + (1 - Math.pow(this.e, 2) ) * this.N(latitude)) * Math.sin(longitude); + + var coordinates = {}; + coordinates.x = x; + coordinates.y = y; + coordinates.z = z; + */ + + var cosLat = Math.cos(latitude); + var sinLat = Math.sin(latitude); + + var rSubN = (this.a*this.a) / Math.sqrt(((this.a*this.a) * (cosLat*cosLat) + ((this.b*this.b) * (sinLat*sinLat)))); + + var X = (rSubN + altitude) * cosLat * Math.cos(longitude); + var Y = (rSubN + altitude) * cosLat * Math.sin(longitude); + var Z = ((((this.b*this.b) / (this.a*this.a)) * rSubN) + altitude) * sinLat; + + return {x:X, y:Y, z:Z}; + }; + + /* + * + * @param {type} latitude in degrees + * @param {type} longitude in degrees + * @param {type} altitude in meters + * @returns {x,y,z} coordinates in ECEF, in meters aka DIS global coordinates + */ + dis.RangeCoordinates.prototype.latLonAltDegreesToECEF = function(latitude, longitude, altitude) + { + return this.latLonAltRadiansToECEF(latitude * this.RADIANS_PER_DEGREE, longitude * this.RADIANS_PER_DEGREE, altitude); + }; + + /** + * Converts DIS xyz world coordinates to latitude and longitude (IN DEGREES). This algorithm may not be 100% accurate + * near the poles. Uses WGS84 , though you can change the ellipsoid constants a and b if you want to use something + * else. These formulas were obtained from Military Handbook 600008. The code itself has been + * translated from C to Java to Javascript over the years, so hold onto your hats. (This is + * copied from other sources than those listed above. Seems to work, though.) + * + * @param position {x:, y:, z:} + * @return {latitude:, longitude: altitude:} + */ + dis.RangeCoordinates.prototype.ECEFObjectToLatLongAltInDegrees = function(position) + { + var x = position.x; + var y = position.y; + var z = position.z; + + var answer = []; + answer[0] = 0.0; + answer[1] = 0.0; + answer[2] = 0.0; + var a = this.a; // semi major axis (WGS 84) + var b = this.b; //semi minor axis (WGS 84) + + var eSquared; //first eccentricity squared + var rSubN; //radius of the curvature of the prime vertical + var ePrimeSquared;//second eccentricity squared + var W = Math.sqrt((x*x + y*y)); + + eSquared = (a*a - b*b) / (a*a); + ePrimeSquared = (a*a - b*b) / (b*b); + + /** + * Get the longitude. + */ + if(x >= 0 ) + { + answer[1] = Math.atan(y/x); + } + else if(x < 0 && y >= 0) + { + answer[1] = Math.atan(y/x) + Math.PI; + } + else + { + answer[1] = Math.atan(y/x) - Math.PI; + } + /** + * Longitude calculation done. Now calculate latitude. + * NOTE: The handbook mentions using the calculated phi (latitude) value to recalculate B + * using tan B = (1-f) tan phi and then performing the entire calculation again to get more accurate values. + * However, for terrestrial applications, one iteration is accurate to .1 millimeter on the surface of the + * earth (Rapp, 1984, p.124), so one iteration is enough for our purposes + */ + var tanBZero = (a*z) / (b * W); + var BZero = Math.atan((tanBZero)); + var tanPhi = (z + (ePrimeSquared * b * (Math.pow(Math.sin(BZero), 3))) ) /(W - (a * eSquared * (Math.pow(Math.cos(BZero), 3)))); + var phi = Math.atan(tanPhi); + answer[0] = phi; + /** + * Latitude done, now get the elevation. Note: The handbook states that near the poles, it is preferable to use + * h = (Z / sin phi ) - rSubN + (eSquared * rSubN). Our applications are never near the poles, so this formula + * was left unimplemented. + */ + rSubN = (a*a) / Math.sqrt(((a*a) * (Math.cos(phi)*Math.cos(phi)) + ((b*b) * (Math.sin(phi)*Math.sin(phi))))); + + answer[2] = (W / Math.cos(phi)) - rSubN; + + var ld = answer[0] * this.DEGREES_PER_RADIAN; + var lnd = answer[1] * this.DEGREES_PER_RADIAN; + var result = {latitude:ld, longitude:lnd, altitude:answer[2]}; + return result; + + }; + + /** + * Converts an ECEF position to the local ENU coordinate system. Units are meters, + * and the origin of the ENU coordinate system is set in the constructor. + * + * @param {x:y:z:} ecefPosition ecef position (in meters) + * @returns {x:y:z:} object with x, y, and z local coordinates, ENU + */ + dis.RangeCoordinates.prototype.ECEFObjectToENU = function(ecefPosition) + { + return this.ECEFtoENU(ecefPosition.x, ecefPosition.y, ecefPosition.z); + }; + + /** + * Converts an ECEF position to the local ENU coordinate system. Units are meters, + * and the origin of the ENU coordinate system is set in the constructor. + * + * @param {float} X the X coordinate of the ECEF position + * @param {float} Y the Y coordinate + * @param {float} Z the Z coordinate + * @returns {x:y:z:} object with x, y, and z local coordinates, ENU + */ + dis.RangeCoordinates.prototype.ECEFtoENU = function(X, Y, Z) + { + // Origin of ENU tangent plane coordinate system in ECEF coordinate system + var Xr = this.ENUOriginInECEF.x; + var Yr = this.ENUOriginInECEF.y; + var Zr = this.ENUOriginInECEF.z; + + var originLonRadians = this.ENUOrigin.longitude * this.RADIANS_PER_DEGREE; + var originLatRadians = this.ENUOrigin.latitude * this.RADIANS_PER_DEGREE; + + e = -(Math.sin(originLonRadians)) * (X-Xr) + Math.cos(originLonRadians) * (Y-Yr); + n = -(Math.sin(originLatRadians)) * Math.cos(originLonRadians) * (X-Xr) - Math.sin(originLatRadians) * Math.sin(originLonRadians) * (Y-Yr) + Math.cos(originLatRadians) * (Z-Zr); + u = Math.cos(originLatRadians) * Math.cos(originLonRadians) * (X-Xr) + Math.cos(originLatRadians) * Math.sin(originLonRadians) * (Y-Yr) + Math.sin(originLatRadians) * (Z-Zr); + + // Local coordinate system x, y, z + return {x:e, y:n, z:u}; + }; + + /** + * Converts a local coordinate system / ENU/ Local Tangent Plane object to ECEF, aka DIS coordinates. + * + * @param enuPosition {x:y:z:} local coordinate object + * @returns {x:y:z:} point in ECEF / DIS coordinate system + */ + dis.RangeCoordinates.prototype.ENUObjectToECEF = function(enuPosition) + { + return this.ENUtoECEF(enuPosition.x, enuPosition.y, enuPosition.z); + }; + + /** + * Converts a local coordinate system / ENU/ Local Tangent Plane point to ECEF, aka DIS coordinates. + * + * @param localX {float} local coordinate system X + * @param localY {float} local coordinate system Y + * @param localZ {float} local coordinate system Z + * @returns {x:y:z:} point in ECEF / DIS coordinate system + */ + dis.RangeCoordinates.prototype.ENUtoECEF = function(localX, localY, localZ) + { + // ENU local coordinate system origin, in ECEF + var Xr = this.ENUOriginInECEF.x; + var Yr = this.ENUOriginInECEF.y; + var Zr = this.ENUOriginInECEF.z; + + var refLong = this.ENUOrigin.longitude; + var refLat = this.ENUOrigin.latitude; + + /** original code this was copied from + + function [X, Y, Z] = enu2xyz(refLat, refLong, refH, e, n, u) + % Convert east, north, up coordinates (labeled e, n, u) to ECEF + % coordinates. The reference point (phi, lambda, h) must be given. All distances are in metres + + [Xr,Yr,Zr] = llh2xyz(refLat,refLong, refH); % location of reference point + + X = -sin(refLong)*e - cos(refLong)*sin(refLat)*n + cos(refLong)*cos(refLat)*u + Xr; + Y = cos(refLong)*e - sin(refLong)*sin(refLat)*n + cos(refLat)*sin(refLong)*u + Yr; + Z = cos(refLat)*n + sin(refLat)*u + Zr; + */ + + X = -(Math.sin(refLong)) * localX - Math.cos(refLong) * Math.sin(refLat) * localY + Math.cos(refLong) * Math.cos(refLat) * localZ + Xr; + Y = Math.cos(refLong) * localX - Math.sin(refLong) * Math.sin(refLat) * localY + Math.cos(refLat) * Math.sin(refLong) * localZ + Yr; + Z = Math.cos(refLat) * localY + Math.sin(refLat) * localZ + Zr; + + return {x:X, y:Y, z:Z}; + }; + +// This is a temporary placeholder until full require.js code +// support is present. +if (typeof exports === "undefined") + exports = {}; + +exports.RangeCoordinates = dis.RangeCoordinates; +exports.InputStream = dis.InputStream; +exports.OutputStream = dis.OutputStream; + +/** + * Section 5.3.6.5. Acknowledge the receiptof a start/resume, stop/freeze, or RemoveEntityPDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcknowledgePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 15; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** type of message being acknowledged */ + this.acknowledgeFlag = 0; + + /** Whether or not the receiving entity was able to comply with the request */ + this.responseFlag = 0; + + /** Request ID that is unique */ + this.requestID = 0; + + dis.AcknowledgePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.acknowledgeFlag = inputStream.readUShort(); + this.responseFlag = inputStream.readUShort(); + this.requestID = inputStream.readInt(); + }; + + dis.AcknowledgePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.acknowledgeFlag); + outputStream.writeUShort(this.responseFlag); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.12.5: Ack receipt of a start-resume, stop-freeze, create-entity or remove enitty (reliable) pdus. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcknowledgeReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 55; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** ack flags */ + this.acknowledgeFlag = 0; + + /** response flags */ + this.responseFlag = 0; + + /** Request ID */ + this.requestID = 0; + + dis.AcknowledgeReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.acknowledgeFlag = inputStream.readUShort(); + this.responseFlag = inputStream.readUShort(); + this.requestID = inputStream.readInt(); + }; + + dis.AcknowledgeReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.acknowledgeFlag); + outputStream.writeUShort(this.responseFlag); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Used in UA PDU + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcousticBeamData = function() +{ + /** beam data length */ + this.beamDataLength = 0; + + /** beamIDNumber */ + this.beamIDNumber = 0; + + /** padding */ + this.pad2 = 0; + + /** fundamental data parameters */ + this.fundamentalDataParameters = new dis.AcousticBeamFundamentalParameter(); + + dis.AcousticBeamData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.beamDataLength = inputStream.readUShort(); + this.beamIDNumber = inputStream.readUByte(); + this.pad2 = inputStream.readUShort(); + this.fundamentalDataParameters.initFromBinaryDIS(inputStream); + }; + + dis.AcousticBeamData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.beamDataLength); + outputStream.writeUByte(this.beamIDNumber); + outputStream.writeUShort(this.pad2); + this.fundamentalDataParameters.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Used in UaPdu + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcousticBeamFundamentalParameter = function() +{ + /** parameter index */ + this.activeEmissionParameterIndex = 0; + + /** scan pattern */ + this.scanPattern = 0; + + /** beam center azimuth */ + this.beamCenterAzimuth = 0; + + /** azimuthal beamwidth */ + this.azimuthalBeamwidth = 0; + + /** beam center */ + this.beamCenterDE = 0; + + /** DE beamwidth (vertical beamwidth) */ + this.deBeamwidth = 0; + + dis.AcousticBeamFundamentalParameter.prototype.initFromBinaryDIS = function(inputStream) + { + + this.activeEmissionParameterIndex = inputStream.readUShort(); + this.scanPattern = inputStream.readUShort(); + this.beamCenterAzimuth = inputStream.readFloat32(); + this.azimuthalBeamwidth = inputStream.readFloat32(); + this.beamCenterDE = inputStream.readFloat32(); + this.deBeamwidth = inputStream.readFloat32(); + }; + + dis.AcousticBeamFundamentalParameter.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.activeEmissionParameterIndex); + outputStream.writeUShort(this.scanPattern); + outputStream.writeFloat32(this.beamCenterAzimuth); + outputStream.writeFloat32(this.azimuthalBeamwidth); + outputStream.writeFloat32(this.beamCenterDE); + outputStream.writeFloat32(this.deBeamwidth); + }; +}; // end of class +/** + * Section 5.2.35. information about a specific UA emmtter + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcousticEmitter = function() +{ + /** the system for a particular UA emitter, and an enumeration */ + this.acousticName = 0; + + /** The function of the acoustic system */ + this.function = 0; + + /** The UA emitter identification number relative to a specific system */ + this.acousticIdNumber = 0; + + dis.AcousticEmitter.prototype.initFromBinaryDIS = function(inputStream) + { + + this.acousticName = inputStream.readUShort(); + this.function = inputStream.readUByte(); + this.acousticIdNumber = inputStream.readUByte(); + }; + + dis.AcousticEmitter.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.acousticName); + outputStream.writeUByte(this.function); + outputStream.writeUByte(this.acousticIdNumber); + }; +}; // end of class +/** + * 5.3.35: Information about a particular UA emitter shall be represented using an Acoustic Emitter System record. This record shall consist of three fields: Acoustic Name, Function, and Acoustic ID Number + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcousticEmitterSystem = function() +{ + /** This field shall specify the system for a particular UA emitter. */ + this.acousticName = 0; + + /** This field shall describe the function of the acoustic system. */ + this.acousticFunction = 0; + + /** This field shall specify the UA emitter identification number relative to a specific system. This field shall be represented by an 8-bit unsigned integer. This field allows the differentiation of multiple systems on an entity, even if in some instances two or more of the systems may be identical UA emitter types. Numbering of systems shall begin with the value 1. */ + this.acousticID = 0; + + dis.AcousticEmitterSystem.prototype.initFromBinaryDIS = function(inputStream) + { + + this.acousticName = inputStream.readUShort(); + this.acousticFunction = inputStream.readUByte(); + this.acousticID = inputStream.readUByte(); + }; + + dis.AcousticEmitterSystem.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.acousticName); + outputStream.writeUByte(this.acousticFunction); + outputStream.writeUByte(this.acousticID); + }; +}; // end of class +/** + * Used in the UA pdu; ties together an emmitter and a location. This requires manual cleanup; the beam data should not be attached to each emitter system. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AcousticEmitterSystemData = function() +{ + /** Length of emitter system data */ + this.emitterSystemDataLength = 0; + + /** Number of beams */ + this.numberOfBeams = 0; + + /** padding */ + this.pad2 = 0; + + /** This field shall specify the system for a particular UA emitter. */ + this.acousticEmitterSystem = new dis.AcousticEmitterSystem(); + + /** Represents the location wrt the entity */ + this.emitterLocation = new dis.Vector3Float(); + + /** For each beam in numberOfBeams, an emitter system. This is not right--the beam records need to be at the end of the PDU, rather than attached to each system. */ + this.beamRecords = new Array(); + + dis.AcousticEmitterSystemData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.emitterSystemDataLength = inputStream.readUByte(); + this.numberOfBeams = inputStream.readUByte(); + this.pad2 = inputStream.readUShort(); + this.acousticEmitterSystem.initFromBinaryDIS(inputStream); + this.emitterLocation.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfBeams; idx++) + { + var anX = new dis.AcousticBeamData(); + anX.initFromBinaryDIS(inputStream); + this.beamRecords.push(anX); + } + + }; + + dis.AcousticEmitterSystemData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.emitterSystemDataLength); + outputStream.writeUByte(this.numberOfBeams); + outputStream.writeUShort(this.pad2); + this.acousticEmitterSystem.encodeToBinaryDIS(outputStream); + this.emitterLocation.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.beamRecords.length; idx++) + { + beamRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.6.6. Request from simulation manager to an entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ActionRequestPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 16; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Request ID that is unique */ + this.requestID = 0; + + /** identifies the action being requested */ + this.actionID = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.ActionRequestPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.actionID = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.ActionRequestPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.actionID); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.6: request from a simulation manager to a managed entity to perform a specified action. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ActionRequestReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 56; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** request ID */ + this.requestID = 0; + + /** request ID */ + this.actionID = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.ActionRequestReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + this.actionID = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.ActionRequestReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.actionID); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.6.7. response to an action request PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ActionResponsePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 17; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Request ID that is unique */ + this.requestID = 0; + + /** Status of response */ + this.requestStatus = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.ActionResponsePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.requestStatus = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.ActionResponsePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.requestStatus); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.7: Response from an entity to an action request PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ActionResponseReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 57; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** request ID */ + this.requestID = 0; + + /** status of response */ + this.responseStatus = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.ActionResponseReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.responseStatus = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.ActionResponseReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.responseStatus); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.36. Each agregate in a given simulation app is given an aggregate identifier number unique for all other aggregates in that app and in that exercise. The id is valid for the duration of the the exercise. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AggregateID = function() +{ + /** The site ID */ + this.site = 0; + + /** The application ID */ + this.application = 0; + + /** the aggregate ID */ + this.aggregateID = 0; + + dis.AggregateID.prototype.initFromBinaryDIS = function(inputStream) + { + + this.site = inputStream.readUShort(); + this.application = inputStream.readUShort(); + this.aggregateID = inputStream.readUShort(); + }; + + dis.AggregateID.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.site); + outputStream.writeUShort(this.application); + outputStream.writeUShort(this.aggregateID); + }; +}; // end of class +/** + * Section 5.2.37. Specifies the character set used inthe first byte, followed by up to 31 characters of text data. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AggregateMarking = function() +{ + /** The character set */ + this.characterSet = 0; + + /** The characters */ + this.characters = new Array(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); + + dis.AggregateMarking.prototype.initFromBinaryDIS = function(inputStream) + { + + this.characterSet = inputStream.readUByte(); + for(var idx = 0; idx < 31; idx++) + { + this.characters[ idx ] = inputStream.readByte(); + } + }; + + dis.AggregateMarking.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.characterSet); + for(var idx = 0; idx < 31; idx++) + { + outputStream.writeByte(this.characters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.3.9.1 informationa bout aggregating entities anc communicating information about the aggregated entities. requires manual intervention to fix the padding between entityID lists and silent aggregate sysem lists--this padding is dependent on how many entityIDs there are, and needs to be on a 32 bit word boundary. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AggregateStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 33; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 7; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of aggregated entities */ + this.aggregateID = new dis.EntityID(); + + /** force ID */ + this.forceID = 0; + + /** state of aggregate */ + this.aggregateState = 0; + + /** entity type of the aggregated entities */ + this.aggregateType = new dis.EntityType(); + + /** formation of aggregated entities */ + this.formation = 0; + + /** marking for aggregate; first char is charset type, rest is char data */ + this.aggregateMarking = new dis.AggregateMarking(); + + /** dimensions of bounding box for the aggregated entities, origin at the center of mass */ + this.dimensions = new dis.Vector3Float(); + + /** orientation of the bounding box */ + this.orientation = new dis.Orientation(); + + /** center of mass of the aggregation */ + this.centerOfMass = new dis.Vector3Double(); + + /** velocity of aggregation */ + this.velocity = new dis.Vector3Float(); + + /** number of aggregates */ + this.numberOfDisAggregates = 0; + + /** number of entities */ + this.numberOfDisEntities = 0; + + /** number of silent aggregate types */ + this.numberOfSilentAggregateTypes = 0; + + /** number of silent entity types */ + this.numberOfSilentEntityTypes = 0; + + /** aggregates list */ + this.aggregateIDList = new Array(); + + /** entity ID list */ + this.entityIDList = new Array(); + + /** ^^^padding to put the start of the next list on a 32 bit boundary. This needs to be fixed */ + this.pad2 = 0; + + /** silent entity types */ + this.silentAggregateSystemList = new Array(); + + /** silent entity types */ + this.silentEntitySystemList = new Array(); + + /** number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variableDatums */ + this.variableDatumList = new Array(); + + dis.AggregateStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.aggregateID.initFromBinaryDIS(inputStream); + this.forceID = inputStream.readUByte(); + this.aggregateState = inputStream.readUByte(); + this.aggregateType.initFromBinaryDIS(inputStream); + this.formation = inputStream.readInt(); + this.aggregateMarking.initFromBinaryDIS(inputStream); + this.dimensions.initFromBinaryDIS(inputStream); + this.orientation.initFromBinaryDIS(inputStream); + this.centerOfMass.initFromBinaryDIS(inputStream); + this.velocity.initFromBinaryDIS(inputStream); + this.numberOfDisAggregates = inputStream.readUShort(); + this.numberOfDisEntities = inputStream.readUShort(); + this.numberOfSilentAggregateTypes = inputStream.readUShort(); + this.numberOfSilentEntityTypes = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfDisAggregates; idx++) + { + var anX = new dis.AggregateID(); + anX.initFromBinaryDIS(inputStream); + this.aggregateIDList.push(anX); + } + + for(var idx = 0; idx < this.numberOfDisEntities; idx++) + { + var anX = new dis.EntityID(); + anX.initFromBinaryDIS(inputStream); + this.entityIDList.push(anX); + } + + this.pad2 = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfSilentAggregateTypes; idx++) + { + var anX = new dis.EntityType(); + anX.initFromBinaryDIS(inputStream); + this.silentAggregateSystemList.push(anX); + } + + for(var idx = 0; idx < this.numberOfSilentEntityTypes; idx++) + { + var anX = new dis.EntityType(); + anX.initFromBinaryDIS(inputStream); + this.silentEntitySystemList.push(anX); + } + + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumList.push(anX); + } + + }; + + dis.AggregateStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.aggregateID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.forceID); + outputStream.writeUByte(this.aggregateState); + this.aggregateType.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.formation); + this.aggregateMarking.encodeToBinaryDIS(outputStream); + this.dimensions.encodeToBinaryDIS(outputStream); + this.orientation.encodeToBinaryDIS(outputStream); + this.centerOfMass.encodeToBinaryDIS(outputStream); + this.velocity.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.numberOfDisAggregates); + outputStream.writeUShort(this.numberOfDisEntities); + outputStream.writeUShort(this.numberOfSilentAggregateTypes); + outputStream.writeUShort(this.numberOfSilentEntityTypes); + for(var idx = 0; idx < this.aggregateIDList.length; idx++) + { + aggregateIDList[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.entityIDList.length; idx++) + { + entityIDList[idx].encodeToBinaryDIS(outputStream); + } + + outputStream.writeUByte(this.pad2); + for(var idx = 0; idx < this.silentAggregateSystemList.length; idx++) + { + silentAggregateSystemList[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.silentEntitySystemList.length; idx++) + { + silentEntitySystemList[idx].encodeToBinaryDIS(outputStream); + } + + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.variableDatumList.length; idx++) + { + variableDatumList[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.38. Identifies the type of aggregate including kind of entity, domain (surface, subsurface, air, etc) country, category, etc. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AggregateType = function() +{ + /** Kind of entity */ + this.aggregateKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.domain = 0; + + /** country to which the design of the entity is attributed */ + this.country = 0; + + /** category of entity */ + this.category = 0; + + /** subcategory of entity */ + this.subcategory = 0; + + /** specific info based on subcategory field, sql has a reserved word for specific */ + this.specificInfo = 0; + + this.extra = 0; + + dis.AggregateType.prototype.initFromBinaryDIS = function(inputStream) + { + + this.aggregateKind = inputStream.readUByte(); + this.domain = inputStream.readUByte(); + this.country = inputStream.readUShort(); + this.category = inputStream.readUByte(); + this.subcategory = inputStream.readUByte(); + this.specificInfo = inputStream.readUByte(); + this.extra = inputStream.readUByte(); + }; + + dis.AggregateType.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.aggregateKind); + outputStream.writeUByte(this.domain); + outputStream.writeUShort(this.country); + outputStream.writeUByte(this.category); + outputStream.writeUByte(this.subcategory); + outputStream.writeUByte(this.specificInfo); + outputStream.writeUByte(this.extra); + }; +}; // end of class +/** + * 5.2.2: angular velocity measured in radians per second out each of the entity's own coordinate axes. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AngularVelocityVector = function() +{ + /** velocity about the x axis */ + this.x = 0; + + /** velocity about the y axis */ + this.y = 0; + + /** velocity about the zaxis */ + this.z = 0; + + dis.AngularVelocityVector.prototype.initFromBinaryDIS = function(inputStream) + { + + this.x = inputStream.readFloat32(); + this.y = inputStream.readFloat32(); + this.z = inputStream.readFloat32(); + }; + + dis.AngularVelocityVector.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.x); + outputStream.writeFloat32(this.y); + outputStream.writeFloat32(this.z); + }; +}; // end of class +/** + * 5.2.3: location of the radiating portion of the antenna, specified in world coordinates and entity coordinates. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.AntennaLocation = function() +{ + /** Location of the radiating portion of the antenna in world coordinates */ + this.antennaLocation = new dis.Vector3Double(); + + /** Location of the radiating portion of the antenna in entity coordinates */ + this.relativeAntennaLocation = new dis.Vector3Float(); + + dis.AntennaLocation.prototype.initFromBinaryDIS = function(inputStream) + { + + this.antennaLocation.initFromBinaryDIS(inputStream); + this.relativeAntennaLocation.initFromBinaryDIS(inputStream); + }; + + dis.AntennaLocation.prototype.encodeToBinaryDIS = function(outputStream) + { + + this.antennaLocation.encodeToBinaryDIS(outputStream); + this.relativeAntennaLocation.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Used in UA PDU + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ApaData = function() +{ + /** Index of APA parameter */ + this.parameterIndex = 0; + + /** Index of APA parameter */ + this.parameterValue = 0; + + dis.ApaData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.parameterIndex = inputStream.readUShort(); + this.parameterValue = inputStream.readShort(); + }; + + dis.ApaData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.parameterIndex); + outputStream.writeShort(this.parameterValue); + }; +}; // end of class +/** + * Section 5.3.11.5: Information about the addition/modification of an oobject that is geometrically achored to the terrain with a set of three or more points that come to a closure. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ArealObjectStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 45; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object in synthetic environment */ + this.objectID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.referencedObjectID = new dis.EntityID(); + + /** unique update number of each state transition of an object */ + this.updateNumber = 0; + + /** force ID */ + this.forceID = 0; + + /** modifications enumeration */ + this.modifications = 0; + + /** Object type */ + this.objectType = new dis.EntityType(); + + /** Object appearance */ + this.objectAppearance = new dis.SixByteChunk(); + + /** Number of points */ + this.numberOfPoints = 0; + + /** requesterID */ + this.requesterID = new dis.SimulationAddress(); + + /** receiver ID */ + this.receivingID = new dis.SimulationAddress(); + + /** location of object */ + this.objectLocation = new Array(); + + dis.ArealObjectStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.objectID.initFromBinaryDIS(inputStream); + this.referencedObjectID.initFromBinaryDIS(inputStream); + this.updateNumber = inputStream.readUShort(); + this.forceID = inputStream.readUByte(); + this.modifications = inputStream.readUByte(); + this.objectType.initFromBinaryDIS(inputStream); + this.objectAppearance.initFromBinaryDIS(inputStream); + this.numberOfPoints = inputStream.readUShort(); + this.requesterID.initFromBinaryDIS(inputStream); + this.receivingID.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfPoints; idx++) + { + var anX = new dis.Vector3Double(); + anX.initFromBinaryDIS(inputStream); + this.objectLocation.push(anX); + } + + }; + + dis.ArealObjectStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.objectID.encodeToBinaryDIS(outputStream); + this.referencedObjectID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.updateNumber); + outputStream.writeUByte(this.forceID); + outputStream.writeUByte(this.modifications); + this.objectType.encodeToBinaryDIS(outputStream); + this.objectAppearance.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.numberOfPoints); + this.requesterID.encodeToBinaryDIS(outputStream); + this.receivingID.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.objectLocation.length; idx++) + { + objectLocation[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.5. Articulation parameters for movable parts and attached parts of an entity. Specifes wether or not a change has occured, the part identifcation of the articulated part to which it is attached, and the type and value of each parameter. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ArticulationParameter = function() +{ + this.parameterTypeDesignator = 0; + + this.changeIndicator = 0; + + this.partAttachedTo = 0; + + this.parameterType = 0; + + this.parameterValue = 0; + + dis.ArticulationParameter.prototype.initFromBinaryDIS = function(inputStream) + { + + this.parameterTypeDesignator = inputStream.readUByte(); + this.changeIndicator = inputStream.readUByte(); + this.partAttachedTo = inputStream.readUShort(); + this.parameterType = inputStream.readInt(); + this.parameterValue = inputStream.readFloat64(); + }; + + dis.ArticulationParameter.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.parameterTypeDesignator); + outputStream.writeUByte(this.changeIndicator); + outputStream.writeUShort(this.partAttachedTo); + outputStream.writeInt(this.parameterType); + outputStream.writeFloat64(this.parameterValue); + }; +}; // end of class +/** + * Section 5.2.4.2. Used when the antenna pattern type field has a value of 1. Specifies the direction, patter, and polarization of radiation from an antenna. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.BeamAntennaPattern = function() +{ + /** The rotation that transformst he reference coordinate sytem into the beam coordinate system. Either world coordinates or entity coordinates may be used as the reference coordinate system, as specified by teh reference system field of the antenna pattern record. */ + this.beamDirection = new dis.Orientation(); + + this.azimuthBeamwidth = 0; + + this.elevationBeamwidth = 0; + + this.referenceSystem = 0; + + this.padding1 = 0; + + this.padding2 = 0; + + /** Magnigute of the z-component in beam coordinates at some arbitrary single point in the mainbeam and in the far field of the antenna. */ + this.ez = 0; + + /** Magnigute of the x-component in beam coordinates at some arbitrary single point in the mainbeam and in the far field of the antenna. */ + this.ex = 0; + + /** THe phase angle between Ez and Ex in radians. */ + this.phase = 0; + + dis.BeamAntennaPattern.prototype.initFromBinaryDIS = function(inputStream) + { + + this.beamDirection.initFromBinaryDIS(inputStream); + this.azimuthBeamwidth = inputStream.readFloat32(); + this.elevationBeamwidth = inputStream.readFloat32(); + this.referenceSystem = inputStream.readFloat32(); + this.padding1 = inputStream.readShort(); + this.padding2 = inputStream.readByte(); + this.ez = inputStream.readFloat32(); + this.ex = inputStream.readFloat32(); + this.phase = inputStream.readFloat32(); + }; + + dis.BeamAntennaPattern.prototype.encodeToBinaryDIS = function(outputStream) + { + + this.beamDirection.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.azimuthBeamwidth); + outputStream.writeFloat32(this.elevationBeamwidth); + outputStream.writeFloat32(this.referenceSystem); + outputStream.writeShort(this.padding1); + outputStream.writeByte(this.padding2); + outputStream.writeFloat32(this.ez); + outputStream.writeFloat32(this.ex); + outputStream.writeFloat32(this.phase); + }; +}; // end of class +/** + * Section 5.2.39. Specification of the data necessary to describe the scan volume of an emitter. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.BeamData = function() +{ + /** Specifies the beam azimuth an elevation centers and corresponding half-angles to describe the scan volume */ + this.beamAzimuthCenter = 0; + + /** Specifies the beam azimuth sweep to determine scan volume */ + this.beamAzimuthSweep = 0; + + /** Specifies the beam elevation center to determine scan volume */ + this.beamElevationCenter = 0; + + /** Specifies the beam elevation sweep to determine scan volume */ + this.beamElevationSweep = 0; + + /** allows receiver to synchronize its regenerated scan pattern to that of the emmitter. Specifies the percentage of time a scan is through its pattern from its origion. */ + this.beamSweepSync = 0; + + dis.BeamData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.beamAzimuthCenter = inputStream.readFloat32(); + this.beamAzimuthSweep = inputStream.readFloat32(); + this.beamElevationCenter = inputStream.readFloat32(); + this.beamElevationSweep = inputStream.readFloat32(); + this.beamSweepSync = inputStream.readFloat32(); + }; + + dis.BeamData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.beamAzimuthCenter); + outputStream.writeFloat32(this.beamAzimuthSweep); + outputStream.writeFloat32(this.beamElevationCenter); + outputStream.writeFloat32(this.beamElevationSweep); + outputStream.writeFloat32(this.beamSweepSync); + }; +}; // end of class +/** + * Section 5.2.7. Specifies the type of muntion fired, the type of warhead, the type of fuse, the number of rounds fired, and the rate at which the roudns are fired in rounds per minute. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.BurstDescriptor = function() +{ + /** What munition was used in the burst */ + this.munition = new dis.EntityType(); + + /** type of warhead */ + this.warhead = 0; + + /** type of fuse used */ + this.fuse = 0; + + /** how many of the munition were fired */ + this.quantity = 0; + + /** rate at which the munition was fired */ + this.rate = 0; + + dis.BurstDescriptor.prototype.initFromBinaryDIS = function(inputStream) + { + + this.munition.initFromBinaryDIS(inputStream); + this.warhead = inputStream.readUShort(); + this.fuse = inputStream.readUShort(); + this.quantity = inputStream.readUShort(); + this.rate = inputStream.readUShort(); + }; + + dis.BurstDescriptor.prototype.encodeToBinaryDIS = function(outputStream) + { + + this.munition.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.warhead); + outputStream.writeUShort(this.fuse); + outputStream.writeUShort(this.quantity); + outputStream.writeUShort(this.rate); + }; +}; // end of class +/** + * Section 5.2.8. Time measurements that exceed one hour. Hours is the number of hours since January 1, 1970, UTC + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ClockTime = function() +{ + /** Hours in UTC */ + this.hour = 0; + + /** Time past the hour */ + this.timePastHour = 0; + + dis.ClockTime.prototype.initFromBinaryDIS = function(inputStream) + { + + this.hour = inputStream.readInt(); + this.timePastHour = inputStream.readInt(); + }; + + dis.ClockTime.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeInt(this.hour); + outputStream.writeUInt(this.timePastHour); + }; +}; // end of class +/** + * 5.3.3.3. Information about elastic collisions in a DIS exercise shall be communicated using a Collision-Elastic PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CollisionElasticPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 66; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that issued the collision PDU */ + this.issuingEntityID = new dis.EntityID(); + + /** ID of entity that has collided with the issuing entity ID */ + this.collidingEntityID = new dis.EntityID(); + + /** ID of event */ + this.collisionEventID = new dis.EventID(); + + /** some padding */ + this.pad = 0; + + /** velocity at collision */ + this.contactVelocity = new dis.Vector3Float(); + + /** mass of issuing entity */ + this.mass = 0; + + /** Location with respect to entity the issuing entity collided with */ + this.location = new dis.Vector3Float(); + + /** tensor values */ + this.collisionResultXX = 0; + + /** tensor values */ + this.collisionResultXY = 0; + + /** tensor values */ + this.collisionResultXZ = 0; + + /** tensor values */ + this.collisionResultYY = 0; + + /** tensor values */ + this.collisionResultYZ = 0; + + /** tensor values */ + this.collisionResultZZ = 0; + + /** This record shall represent the normal vector to the surface at the point of collision detection. The surface normal shall be represented in world coordinates. */ + this.unitSurfaceNormal = new dis.Vector3Float(); + + /** This field shall represent the degree to which energy is conserved in a collision */ + this.coefficientOfRestitution = 0; + + dis.CollisionElasticPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.issuingEntityID.initFromBinaryDIS(inputStream); + this.collidingEntityID.initFromBinaryDIS(inputStream); + this.collisionEventID.initFromBinaryDIS(inputStream); + this.pad = inputStream.readShort(); + this.contactVelocity.initFromBinaryDIS(inputStream); + this.mass = inputStream.readFloat32(); + this.location.initFromBinaryDIS(inputStream); + this.collisionResultXX = inputStream.readFloat32(); + this.collisionResultXY = inputStream.readFloat32(); + this.collisionResultXZ = inputStream.readFloat32(); + this.collisionResultYY = inputStream.readFloat32(); + this.collisionResultYZ = inputStream.readFloat32(); + this.collisionResultZZ = inputStream.readFloat32(); + this.unitSurfaceNormal.initFromBinaryDIS(inputStream); + this.coefficientOfRestitution = inputStream.readFloat32(); + }; + + dis.CollisionElasticPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.issuingEntityID.encodeToBinaryDIS(outputStream); + this.collidingEntityID.encodeToBinaryDIS(outputStream); + this.collisionEventID.encodeToBinaryDIS(outputStream); + outputStream.writeShort(this.pad); + this.contactVelocity.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.mass); + this.location.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.collisionResultXX); + outputStream.writeFloat32(this.collisionResultXY); + outputStream.writeFloat32(this.collisionResultXZ); + outputStream.writeFloat32(this.collisionResultYY); + outputStream.writeFloat32(this.collisionResultYZ); + outputStream.writeFloat32(this.collisionResultZZ); + this.unitSurfaceNormal.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.coefficientOfRestitution); + }; +}; // end of class +/** + * Section 5.3.3.2. Information about a collision. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CollisionPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 4; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that issued the collision PDU */ + this.issuingEntityID = new dis.EntityID(); + + /** ID of entity that has collided with the issuing entity ID */ + this.collidingEntityID = new dis.EntityID(); + + /** ID of event */ + this.eventID = new dis.EventID(); + + /** ID of event */ + this.collisionType = 0; + + /** some padding */ + this.pad = 0; + + /** velocity at collision */ + this.velocity = new dis.Vector3Float(); + + /** mass of issuing entity */ + this.mass = 0; + + /** Location with respect to entity the issuing entity collided with */ + this.location = new dis.Vector3Float(); + + dis.CollisionPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.issuingEntityID.initFromBinaryDIS(inputStream); + this.collidingEntityID.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.collisionType = inputStream.readUByte(); + this.pad = inputStream.readByte(); + this.velocity.initFromBinaryDIS(inputStream); + this.mass = inputStream.readFloat32(); + this.location.initFromBinaryDIS(inputStream); + }; + + dis.CollisionPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.issuingEntityID.encodeToBinaryDIS(outputStream); + this.collidingEntityID.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.collisionType); + outputStream.writeByte(this.pad); + this.velocity.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.mass); + this.location.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.6.12. Arbitrary messages can be entered into the data stream via use of this PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CommentPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 22; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.CommentPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.CommentPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.12: Arbitrary messages. Only reliable this time. Neds manual intervention to fix padding in variable datums. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CommentReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 62; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.CommentReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.CommentReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.6.1. Create a new entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CreateEntityPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 11; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Identifier for the request */ + this.requestID = 0; + + dis.CreateEntityPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + }; + + dis.CreateEntityPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.12.1: creation of an entity , reliable. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.CreateEntityReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 51; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Request ID */ + this.requestID = 0; + + dis.CreateEntityReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + }; + + dis.CreateEntityReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.6.10. Information issued in response to a data query pdu or a set data pdu is communicated using a data pdu. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DataPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 20; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** ID of request */ + this.requestID = 0; + + /** padding */ + this.padding1 = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.DataPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.padding1 = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.DataPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.padding1); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.6.8. Request for data from an entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DataQueryPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 18; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** ID of request */ + this.requestID = 0; + + /** time issues between issues of Data PDUs. Zero means send once only. */ + this.timeInterval = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.DataQueryPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.timeInterval = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.DataQueryPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.timeInterval); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.8: request for data from an entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DataQueryReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 58; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** request ID */ + this.requestID = 0; + + /** time interval between issuing data query PDUs */ + this.timeInterval = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.DataQueryReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + this.timeInterval = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.DataQueryReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.timeInterval); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.10: issued in response to a data query R or set dataR pdu. Needs manual intervention to fix padding on variable datums. UNFINSIHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DataReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 60; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** Request ID */ + this.requestID = 0; + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.DataReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.DataReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * represents values used in dead reckoning algorithms + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DeadReckoningParameter = function() +{ + /** enumeration of what dead reckoning algorighm to use */ + this.deadReckoningAlgorithm = 0; + + /** other parameters to use in the dead reckoning algorithm */ + this.otherParameters = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + /** Linear acceleration of the entity */ + this.entityLinearAcceleration = new dis.Vector3Float(); + + /** angular velocity of the entity */ + this.entityAngularVelocity = new dis.Vector3Float(); + + dis.DeadReckoningParameter.prototype.initFromBinaryDIS = function(inputStream) + { + + this.deadReckoningAlgorithm = inputStream.readUByte(); + for(var idx = 0; idx < 15; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + this.entityLinearAcceleration.initFromBinaryDIS(inputStream); + this.entityAngularVelocity.initFromBinaryDIS(inputStream); + }; + + dis.DeadReckoningParameter.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.deadReckoningAlgorithm); + for(var idx = 0; idx < 15; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + this.entityLinearAcceleration.encodeToBinaryDIS(outputStream); + this.entityAngularVelocity.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.7.2. Handles designating operations. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DesignatorPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 24; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity designating */ + this.designatingEntityID = new dis.EntityID(); + + /** This field shall specify a unique emitter database number assigned to differentiate between otherwise similar or identical emitter beams within an emitter system. */ + this.codeName = 0; + + /** ID of the entity being designated */ + this.designatedEntityID = new dis.EntityID(); + + /** This field shall identify the designator code being used by the designating entity */ + this.designatorCode = 0; + + /** This field shall identify the designator output power in watts */ + this.designatorPower = 0; + + /** This field shall identify the designator wavelength in units of microns */ + this.designatorWavelength = 0; + + /** designtor spot wrt the designated entity */ + this.designatorSpotWrtDesignated = new dis.Vector3Float(); + + /** designtor spot wrt the designated entity */ + this.designatorSpotLocation = new dis.Vector3Double(); + + /** Dead reckoning algorithm */ + this.deadReckoningAlgorithm = 0; + + /** padding */ + this.padding1 = 0; + + /** padding */ + this.padding2 = 0; + + /** linear accelleration of entity */ + this.entityLinearAcceleration = new dis.Vector3Float(); + + dis.DesignatorPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.designatingEntityID.initFromBinaryDIS(inputStream); + this.codeName = inputStream.readUShort(); + this.designatedEntityID.initFromBinaryDIS(inputStream); + this.designatorCode = inputStream.readUShort(); + this.designatorPower = inputStream.readFloat32(); + this.designatorWavelength = inputStream.readFloat32(); + this.designatorSpotWrtDesignated.initFromBinaryDIS(inputStream); + this.designatorSpotLocation.initFromBinaryDIS(inputStream); + this.deadReckoningAlgorithm = inputStream.readByte(); + this.padding1 = inputStream.readUShort(); + this.padding2 = inputStream.readByte(); + this.entityLinearAcceleration.initFromBinaryDIS(inputStream); + }; + + dis.DesignatorPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.designatingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.codeName); + this.designatedEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.designatorCode); + outputStream.writeFloat32(this.designatorPower); + outputStream.writeFloat32(this.designatorWavelength); + this.designatorSpotWrtDesignated.encodeToBinaryDIS(outputStream); + this.designatorSpotLocation.encodeToBinaryDIS(outputStream); + outputStream.writeByte(this.deadReckoningAlgorithm); + outputStream.writeUShort(this.padding1); + outputStream.writeByte(this.padding2); + this.entityLinearAcceleration.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.4.2. Information about stuff exploding. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DetonationPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 3; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 2; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that shot */ + this.firingEntityID = new dis.EntityID(); + + /** ID of the entity that is being shot at */ + this.targetEntityID = new dis.EntityID(); + + /** ID of muntion that was fired */ + this.munitionID = new dis.EntityID(); + + /** ID firing event */ + this.eventID = new dis.EventID(); + + /** ID firing event */ + this.velocity = new dis.Vector3Float(); + + /** where the detonation is, in world coordinates */ + this.locationInWorldCoordinates = new dis.Vector3Double(); + + /** Describes munition used */ + this.burstDescriptor = new dis.BurstDescriptor(); + + /** location of the detonation or impact in the target entity's coordinate system. This information should be used for damage assessment. */ + this.locationInEntityCoordinates = new dis.Vector3Float(); + + /** result of the explosion */ + this.detonationResult = 0; + + /** How many articulation parameters we have */ + this.numberOfArticulationParameters = 0; + + /** padding */ + this.pad = 0; + + this.articulationParameters = new Array(); + + dis.DetonationPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.firingEntityID.initFromBinaryDIS(inputStream); + this.targetEntityID.initFromBinaryDIS(inputStream); + this.munitionID.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.velocity.initFromBinaryDIS(inputStream); + this.locationInWorldCoordinates.initFromBinaryDIS(inputStream); + this.burstDescriptor.initFromBinaryDIS(inputStream); + this.locationInEntityCoordinates.initFromBinaryDIS(inputStream); + this.detonationResult = inputStream.readUByte(); + this.numberOfArticulationParameters = inputStream.readUByte(); + this.pad = inputStream.readShort(); + for(var idx = 0; idx < this.numberOfArticulationParameters; idx++) + { + var anX = new dis.ArticulationParameter(); + anX.initFromBinaryDIS(inputStream); + this.articulationParameters.push(anX); + } + + }; + + dis.DetonationPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.firingEntityID.encodeToBinaryDIS(outputStream); + this.targetEntityID.encodeToBinaryDIS(outputStream); + this.munitionID.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + this.velocity.encodeToBinaryDIS(outputStream); + this.locationInWorldCoordinates.encodeToBinaryDIS(outputStream); + this.burstDescriptor.encodeToBinaryDIS(outputStream); + this.locationInEntityCoordinates.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.detonationResult); + outputStream.writeUByte(this.numberOfArticulationParameters); + outputStream.writeShort(this.pad); + for(var idx = 0; idx < this.articulationParameters.length; idx++) + { + articulationParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.7. Electronic Emissions. Abstract superclass for distirubted emissions PDU + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.DistributedEmissionsFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.DistributedEmissionsFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.DistributedEmissionsFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * 64 bit piece of data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EightByteChunk = function() +{ + /** Eight bytes of arbitrary data */ + this.otherParameters = new Array(0, 0, 0, 0, 0, 0, 0, 0); + + dis.EightByteChunk.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 8; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + }; + + dis.EightByteChunk.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 8; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + }; +}; // end of class +/** + * Description of one electronic emission beam + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ElectronicEmissionBeamData = function() +{ + /** This field shall specify the length of this beams data in 32 bit words */ + this.beamDataLength = 0; + + /** This field shall specify a unique emitter database number assigned to differentiate between otherwise similar or identical emitter beams within an emitter system. */ + this.beamIDNumber = 0; + + /** This field shall specify a Beam Parameter Index number that shall be used by receiving entities in conjunction with the Emitter Name field to provide a pointer to the stored database parameters required to regenerate the beam. */ + this.beamParameterIndex = 0; + + /** Fundamental parameter data such as frequency range, beam sweep, etc. */ + this.fundamentalParameterData = new dis.FundamentalParameterData(); + + /** beam function of a particular beam */ + this.beamFunction = 0; + + /** Number of track/jam targets */ + this.numberOfTrackJamTargets = 0; + + /** wheher or not the receiving simulation apps can assume all the targets in the scan pattern are being tracked/jammed */ + this.highDensityTrackJam = 0; + + /** padding */ + this.pad4 = 0; + + /** identify jamming techniques used */ + this.jammingModeSequence = 0; + + /** variable length list of track/jam targets */ + this.trackJamTargets = new Array(); + + dis.ElectronicEmissionBeamData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.beamDataLength = inputStream.readUByte(); + this.beamIDNumber = inputStream.readUByte(); + this.beamParameterIndex = inputStream.readUShort(); + this.fundamentalParameterData.initFromBinaryDIS(inputStream); + this.beamFunction = inputStream.readUByte(); + this.numberOfTrackJamTargets = inputStream.readUByte(); + this.highDensityTrackJam = inputStream.readUByte(); + this.pad4 = inputStream.readUByte(); + this.jammingModeSequence = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfTrackJamTargets; idx++) + { + var anX = new dis.TrackJamTarget(); + anX.initFromBinaryDIS(inputStream); + this.trackJamTargets.push(anX); + } + + }; + + dis.ElectronicEmissionBeamData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.beamDataLength); + outputStream.writeUByte(this.beamIDNumber); + outputStream.writeUShort(this.beamParameterIndex); + this.fundamentalParameterData.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.beamFunction); + outputStream.writeUByte(this.numberOfTrackJamTargets); + outputStream.writeUByte(this.highDensityTrackJam); + outputStream.writeUByte(this.pad4); + outputStream.writeUInt(this.jammingModeSequence); + for(var idx = 0; idx < this.trackJamTargets.length; idx++) + { + trackJamTargets[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Data about one electronic system + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ElectronicEmissionSystemData = function() +{ + /** This field shall specify the length of this emitter system�s data (including beam data and its track/jam information) in 32-bit words. The length shall include the System Data Length field. */ + this.systemDataLength = 0; + + /** This field shall specify the number of beams being described in the current PDU for the system being described. */ + this.numberOfBeams = 0; + + /** padding. */ + this.emissionsPadding2 = 0; + + /** This field shall specify information about a particular emitter system */ + this.emitterSystem = new dis.EmitterSystem(); + + /** Location with respect to the entity */ + this.location = new dis.Vector3Float(); + + /** variable length list of beam data records */ + this.beamDataRecords = new Array(); + + dis.ElectronicEmissionSystemData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.systemDataLength = inputStream.readUByte(); + this.numberOfBeams = inputStream.readUByte(); + this.emissionsPadding2 = inputStream.readUShort(); + this.emitterSystem.initFromBinaryDIS(inputStream); + this.location.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfBeams; idx++) + { + var anX = new dis.ElectronicEmissionBeamData(); + anX.initFromBinaryDIS(inputStream); + this.beamDataRecords.push(anX); + } + + }; + + dis.ElectronicEmissionSystemData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.systemDataLength); + outputStream.writeUByte(this.numberOfBeams); + outputStream.writeUShort(this.emissionsPadding2); + this.emitterSystem.encodeToBinaryDIS(outputStream); + this.location.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.beamDataRecords.length; idx++) + { + beamDataRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.7.1. Information about active electronic warfare (EW) emissions and active EW countermeasures shall be communicated using an Electromagnetic Emission PDU. COMPLETE (I think) + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ElectronicEmissionsPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 23; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity emitting */ + this.emittingEntityID = new dis.EntityID(); + + /** ID of event */ + this.eventID = new dis.EventID(); + + /** This field shall be used to indicate if the data in the PDU represents a state update or just data that has changed since issuance of the last Electromagnetic Emission PDU [relative to the identified entity and emission system(s)]. */ + this.stateUpdateIndicator = 0; + + /** This field shall specify the number of emission systems being described in the current PDU. */ + this.numberOfSystems = 0; + + /** padding */ + this.paddingForEmissionsPdu = 0; + + /** Electronic emmissions systems */ + this.systems = new Array(); + + dis.ElectronicEmissionsPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.emittingEntityID.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.stateUpdateIndicator = inputStream.readUByte(); + this.numberOfSystems = inputStream.readUByte(); + this.paddingForEmissionsPdu = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfSystems; idx++) + { + var anX = new dis.ElectronicEmissionSystemData(); + anX.initFromBinaryDIS(inputStream); + this.systems.push(anX); + } + + }; + + dis.ElectronicEmissionsPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.emittingEntityID.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.stateUpdateIndicator); + outputStream.writeUByte(this.numberOfSystems); + outputStream.writeUShort(this.paddingForEmissionsPdu); + for(var idx = 0; idx < this.systems.length; idx++) + { + systems[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.11. This field shall specify information about a particular emitter system + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EmitterSystem = function() +{ + /** Name of the emitter, 16 bit enumeration */ + this.emitterName = 0; + + /** function of the emitter, 8 bit enumeration */ + this.function = 0; + + /** emitter ID, 8 bit enumeration */ + this.emitterIdNumber = 0; + + dis.EmitterSystem.prototype.initFromBinaryDIS = function(inputStream) + { + + this.emitterName = inputStream.readUShort(); + this.function = inputStream.readUByte(); + this.emitterIdNumber = inputStream.readUByte(); + }; + + dis.EmitterSystem.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.emitterName); + outputStream.writeUByte(this.function); + outputStream.writeUByte(this.emitterIdNumber); + }; +}; // end of class +/** + * Each entity in a given DIS simulation application shall be given an entity identifier number unique to all other entities in that application. This identifier number is valid for the duration of the exercise; however, entity identifier numbers may be reused when all possible numbers have been exhausted. No entity shall have an entity identifier number of NO_ENTITY, ALL_ENTITIES, or RQST_ASSIGN_ID. The entity iden- tifier number need not be registered or retained for future exercises. The entity identifier number shall be specified by a 16-bit unsigned integer. An entity identifier number equal to zero with valid site and application identification shall address a simulation application. An entity identifier number equal to ALL_ENTITIES shall mean all entities within the specified site and application. An entity identifier number equal to RQST_ASSIGN_ID allows the receiver of the create entity to define the entity identifier number of the new entity. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityID = function() +{ + /** The site ID */ + this.site = 0; + + /** The application ID */ + this.application = 0; + + /** the entity ID */ + this.entity = 0; + + dis.EntityID.prototype.initFromBinaryDIS = function(inputStream) + { + + this.site = inputStream.readUShort(); + this.application = inputStream.readUShort(); + this.entity = inputStream.readUShort(); + }; + + dis.EntityID.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.site); + outputStream.writeUShort(this.application); + outputStream.writeUShort(this.entity); + }; +}; // end of class +/** + * Section 5.3.3. Common superclass for EntityState, Collision, collision-elastic, and entity state update PDUs. This should be abstract. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityInformationFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.EntityInformationFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.EntityInformationFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Section 5.3.9. Common superclass for EntityManagment PDUs, including aggregate state, isGroupOf, TransferControLRequest, and isPartOf + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityManagementFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 7; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.EntityManagementFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.EntityManagementFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Section 5.3.3.1. Represents the postion and state of one entity in the world. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 1; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Unique ID for an entity that is tied to this state information */ + this.entityID = new dis.EntityID(); + + /** What force this entity is affiliated with, eg red, blue, neutral, etc */ + this.forceId = 0; + + /** How many articulation parameters are in the variable length list */ + this.numberOfArticulationParameters = 0; + + /** Describes the type of entity in the world */ + this.entityType = new dis.EntityType(); + + this.alternativeEntityType = new dis.EntityType(); + + /** Describes the speed of the entity in the world */ + this.entityLinearVelocity = new dis.Vector3Float(); + + /** describes the location of the entity in the world */ + this.entityLocation = new dis.Vector3Double(); + + /** describes the orientation of the entity, in euler angles */ + this.entityOrientation = new dis.Orientation(); + + /** a series of bit flags that are used to help draw the entity, such as smoking, on fire, etc. */ + this.entityAppearance = 0; + + /** parameters used for dead reckoning */ + this.deadReckoningParameters = new dis.DeadReckoningParameter(); + + /** characters that can be used for debugging, or to draw unique strings on the side of entities in the world */ + this.marking = new dis.Marking(); + + /** a series of bit flags */ + this.capabilities = 0; + + /** variable length list of articulation parameters */ + this.articulationParameters = new Array(); + + dis.EntityStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityID.initFromBinaryDIS(inputStream); + this.forceId = inputStream.readUByte(); + this.numberOfArticulationParameters = inputStream.readByte(); + this.entityType.initFromBinaryDIS(inputStream); + this.alternativeEntityType.initFromBinaryDIS(inputStream); + this.entityLinearVelocity.initFromBinaryDIS(inputStream); + this.entityLocation.initFromBinaryDIS(inputStream); + this.entityOrientation.initFromBinaryDIS(inputStream); + this.entityAppearance = inputStream.readInt(); + this.deadReckoningParameters.initFromBinaryDIS(inputStream); + this.marking.initFromBinaryDIS(inputStream); + this.capabilities = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfArticulationParameters; idx++) + { + var anX = new dis.ArticulationParameter(); + anX.initFromBinaryDIS(inputStream); + this.articulationParameters.push(anX); + } + + }; + + dis.EntityStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.forceId); + outputStream.writeByte(this.numberOfArticulationParameters); + this.entityType.encodeToBinaryDIS(outputStream); + this.alternativeEntityType.encodeToBinaryDIS(outputStream); + this.entityLinearVelocity.encodeToBinaryDIS(outputStream); + this.entityLocation.encodeToBinaryDIS(outputStream); + this.entityOrientation.encodeToBinaryDIS(outputStream); + outputStream.writeInt(this.entityAppearance); + this.deadReckoningParameters.encodeToBinaryDIS(outputStream); + this.marking.encodeToBinaryDIS(outputStream); + outputStream.writeInt(this.capabilities); + for(var idx = 0; idx < this.articulationParameters.length; idx++) + { + articulationParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.3.3.4. Nonstatic information about a particular entity may be communicated by issuing an Entity State Update PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityStateUpdatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 67; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** This field shall identify the entity issuing the PDU */ + this.entityID = new dis.EntityID(); + + /** Padding */ + this.padding1 = 0; + + /** How many articulation parameters are in the variable length list */ + this.numberOfArticulationParameters = 0; + + /** Describes the speed of the entity in the world */ + this.entityLinearVelocity = new dis.Vector3Float(); + + /** describes the location of the entity in the world */ + this.entityLocation = new dis.Vector3Double(); + + /** describes the orientation of the entity, in euler angles */ + this.entityOrientation = new dis.Orientation(); + + /** a series of bit flags that are used to help draw the entity, such as smoking, on fire, etc. */ + this.entityAppearance = 0; + + this.articulationParameters = new Array(); + + dis.EntityStateUpdatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityID.initFromBinaryDIS(inputStream); + this.padding1 = inputStream.readByte(); + this.numberOfArticulationParameters = inputStream.readUByte(); + this.entityLinearVelocity.initFromBinaryDIS(inputStream); + this.entityLocation.initFromBinaryDIS(inputStream); + this.entityOrientation.initFromBinaryDIS(inputStream); + this.entityAppearance = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfArticulationParameters; idx++) + { + var anX = new dis.ArticulationParameter(); + anX.initFromBinaryDIS(inputStream); + this.articulationParameters.push(anX); + } + + }; + + dis.EntityStateUpdatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityID.encodeToBinaryDIS(outputStream); + outputStream.writeByte(this.padding1); + outputStream.writeUByte(this.numberOfArticulationParameters); + this.entityLinearVelocity.encodeToBinaryDIS(outputStream); + this.entityLocation.encodeToBinaryDIS(outputStream); + this.entityOrientation.encodeToBinaryDIS(outputStream); + outputStream.writeInt(this.entityAppearance); + for(var idx = 0; idx < this.articulationParameters.length; idx++) + { + articulationParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.16. Identifies the type of entity, including kind of entity, domain (surface, subsurface, air, etc) country, category, etc. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EntityType = function() +{ + /** Kind of entity */ + this.entityKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.domain = 0; + + /** country to which the design of the entity is attributed */ + this.country = 0; + + /** category of entity */ + this.category = 0; + + /** subcategory of entity */ + this.subcategory = 0; + + /** specific info based on subcategory field. Renamed from specific because that is a reserved word in SQL */ + this.spec = 0; + + this.extra = 0; + + dis.EntityType.prototype.initFromBinaryDIS = function(inputStream) + { + + this.entityKind = inputStream.readUByte(); + this.domain = inputStream.readUByte(); + this.country = inputStream.readUShort(); + this.category = inputStream.readUByte(); + this.subcategory = inputStream.readUByte(); + this.spec = inputStream.readUByte(); + this.extra = inputStream.readUByte(); + }; + + dis.EntityType.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.entityKind); + outputStream.writeUByte(this.domain); + outputStream.writeUShort(this.country); + outputStream.writeUByte(this.category); + outputStream.writeUByte(this.subcategory); + outputStream.writeUByte(this.spec); + outputStream.writeUByte(this.extra); + }; +}; // end of class +/** + * Section 5.2.40. Information about a geometry, a state associated with a geometry, a bounding volume, or an associated entity ID. NOTE: this class requires hand coding. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Environment = function() +{ + /** Record type */ + this.environmentType = 0; + + /** length, in bits */ + this.length = 0; + + /** Identify the sequentially numbered record index */ + this.recordIndex = 0; + + /** padding */ + this.padding1 = 0; + + /** Geometry or state record */ + this.geometry = 0; + + /** padding to bring the total size up to a 64 bit boundry */ + this.padding2 = 0; + + dis.Environment.prototype.initFromBinaryDIS = function(inputStream) + { + + this.environmentType = inputStream.readInt(); + this.length = inputStream.readUByte(); + this.recordIndex = inputStream.readUByte(); + this.padding1 = inputStream.readUByte(); + this.geometry = inputStream.readUByte(); + this.padding2 = inputStream.readUByte(); + }; + + dis.Environment.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUInt(this.environmentType); + outputStream.writeUByte(this.length); + outputStream.writeUByte(this.recordIndex); + outputStream.writeUByte(this.padding1); + outputStream.writeUByte(this.geometry); + outputStream.writeUByte(this.padding2); + }; +}; // end of class +/** + * Section 5.3.11.1: Information about environmental effects and processes. This requires manual cleanup. the environmental record is variable, as is the padding. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EnvironmentalProcessPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 41; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Environmental process ID */ + this.environementalProcessID = new dis.EntityID(); + + /** Environment type */ + this.environmentType = new dis.EntityType(); + + /** model type */ + this.modelType = 0; + + /** Environment status */ + this.environmentStatus = 0; + + /** number of environment records */ + this.numberOfEnvironmentRecords = 0; + + /** PDU sequence number for the environmentla process if pdu sequencing required */ + this.sequenceNumber = 0; + + /** environemt records */ + this.environmentRecords = new Array(); + + dis.EnvironmentalProcessPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.environementalProcessID.initFromBinaryDIS(inputStream); + this.environmentType.initFromBinaryDIS(inputStream); + this.modelType = inputStream.readUByte(); + this.environmentStatus = inputStream.readUByte(); + this.numberOfEnvironmentRecords = inputStream.readUByte(); + this.sequenceNumber = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfEnvironmentRecords; idx++) + { + var anX = new dis.Environment(); + anX.initFromBinaryDIS(inputStream); + this.environmentRecords.push(anX); + } + + }; + + dis.EnvironmentalProcessPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.environementalProcessID.encodeToBinaryDIS(outputStream); + this.environmentType.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.modelType); + outputStream.writeUByte(this.environmentStatus); + outputStream.writeUByte(this.numberOfEnvironmentRecords); + outputStream.writeUShort(this.sequenceNumber); + for(var idx = 0; idx < this.environmentRecords.length; idx++) + { + environmentRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.18. Identifies a unique event in a simulation via the combination of three values + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EventID = function() +{ + /** The site ID */ + this.site = 0; + + /** The application ID */ + this.application = 0; + + /** the number of the event */ + this.eventNumber = 0; + + dis.EventID.prototype.initFromBinaryDIS = function(inputStream) + { + + this.site = inputStream.readUShort(); + this.application = inputStream.readUShort(); + this.eventNumber = inputStream.readUShort(); + }; + + dis.EventID.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.site); + outputStream.writeUShort(this.application); + outputStream.writeUShort(this.eventNumber); + }; +}; // end of class +/** + * Section 5.3.6.11. Reports occurance of a significant event to the simulation manager. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EventReportPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 21; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Type of event */ + this.eventType = 0; + + /** padding */ + this.padding1 = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.EventReportPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.eventType = inputStream.readInt(); + this.padding1 = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.EventReportPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.eventType); + outputStream.writeUInt(this.padding1); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.11: reports the occurance of a significatnt event to the simulation manager. Needs manual intervention to fix padding in variable datums. UNFINISHED. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.EventReportReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 61; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** Event type */ + this.eventType = 0; + + /** padding */ + this.pad1 = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.EventReportReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.eventType = inputStream.readUShort(); + this.pad1 = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.EventReportReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.eventType); + outputStream.writeUInt(this.pad1); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.3.1. Represents the postion and state of one entity in the world. This is identical in function to entity state pdu, but generates less garbage to collect in the Java world. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FastEntityStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 1; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 1; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** The site ID */ + this.site = 0; + + /** The application ID */ + this.application = 0; + + /** the entity ID */ + this.entity = 0; + + /** what force this entity is affiliated with, eg red, blue, neutral, etc */ + this.forceId = 0; + + /** How many articulation parameters are in the variable length list */ + this.numberOfArticulationParameters = 0; + + /** Kind of entity */ + this.entityKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.domain = 0; + + /** country to which the design of the entity is attributed */ + this.country = 0; + + /** category of entity */ + this.category = 0; + + /** subcategory of entity */ + this.subcategory = 0; + + /** specific info based on subcategory field. Name changed from specific because that is a reserved word in SQL. */ + this.specif = 0; + + this.extra = 0; + + /** Kind of entity */ + this.altEntityKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.altDomain = 0; + + /** country to which the design of the entity is attributed */ + this.altCountry = 0; + + /** category of entity */ + this.altCategory = 0; + + /** subcategory of entity */ + this.altSubcategory = 0; + + /** specific info based on subcategory field */ + this.altSpecific = 0; + + this.altExtra = 0; + + /** X velo */ + this.xVelocity = 0; + + /** y Value */ + this.yVelocity = 0; + + /** Z value */ + this.zVelocity = 0; + + /** X value */ + this.xLocation = 0; + + /** y Value */ + this.yLocation = 0; + + /** Z value */ + this.zLocation = 0; + + this.psi = 0; + + this.theta = 0; + + this.phi = 0; + + /** a series of bit flags that are used to help draw the entity, such as smoking, on fire, etc. */ + this.entityAppearance = 0; + + /** enumeration of what dead reckoning algorighm to use */ + this.deadReckoningAlgorithm = 0; + + /** other parameters to use in the dead reckoning algorithm */ + this.otherParameters = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + /** X value */ + this.xAcceleration = 0; + + /** y Value */ + this.yAcceleration = 0; + + /** Z value */ + this.zAcceleration = 0; + + /** X value */ + this.xAngularVelocity = 0; + + /** y Value */ + this.yAngularVelocity = 0; + + /** Z value */ + this.zAngularVelocity = 0; + + /** characters that can be used for debugging, or to draw unique strings on the side of entities in the world */ + this.marking = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + /** a series of bit flags */ + this.capabilities = 0; + + /** variable length list of articulation parameters */ + this.articulationParameters = new Array(); + + dis.FastEntityStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.site = inputStream.readUShort(); + this.application = inputStream.readUShort(); + this.entity = inputStream.readUShort(); + this.forceId = inputStream.readUByte(); + this.numberOfArticulationParameters = inputStream.readByte(); + this.entityKind = inputStream.readUByte(); + this.domain = inputStream.readUByte(); + this.country = inputStream.readUShort(); + this.category = inputStream.readUByte(); + this.subcategory = inputStream.readUByte(); + this.specif = inputStream.readUByte(); + this.extra = inputStream.readUByte(); + this.altEntityKind = inputStream.readUByte(); + this.altDomain = inputStream.readUByte(); + this.altCountry = inputStream.readUShort(); + this.altCategory = inputStream.readUByte(); + this.altSubcategory = inputStream.readUByte(); + this.altSpecific = inputStream.readUByte(); + this.altExtra = inputStream.readUByte(); + this.xVelocity = inputStream.readFloat32(); + this.yVelocity = inputStream.readFloat32(); + this.zVelocity = inputStream.readFloat32(); + this.xLocation = inputStream.readFloat64(); + this.yLocation = inputStream.readFloat64(); + this.zLocation = inputStream.readFloat64(); + this.psi = inputStream.readFloat32(); + this.theta = inputStream.readFloat32(); + this.phi = inputStream.readFloat32(); + this.entityAppearance = inputStream.readInt(); + this.deadReckoningAlgorithm = inputStream.readUByte(); + for(var idx = 0; idx < 15; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + this.xAcceleration = inputStream.readFloat32(); + this.yAcceleration = inputStream.readFloat32(); + this.zAcceleration = inputStream.readFloat32(); + this.xAngularVelocity = inputStream.readFloat32(); + this.yAngularVelocity = inputStream.readFloat32(); + this.zAngularVelocity = inputStream.readFloat32(); + for(var idx = 0; idx < 12; idx++) + { + this.marking[ idx ] = inputStream.readByte(); + } + this.capabilities = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfArticulationParameters; idx++) + { + var anX = new dis.ArticulationParameter(); + anX.initFromBinaryDIS(inputStream); + this.articulationParameters.push(anX); + } + + }; + + dis.FastEntityStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + outputStream.writeUShort(this.site); + outputStream.writeUShort(this.application); + outputStream.writeUShort(this.entity); + outputStream.writeUByte(this.forceId); + outputStream.writeByte(this.numberOfArticulationParameters); + outputStream.writeUByte(this.entityKind); + outputStream.writeUByte(this.domain); + outputStream.writeUShort(this.country); + outputStream.writeUByte(this.category); + outputStream.writeUByte(this.subcategory); + outputStream.writeUByte(this.specif); + outputStream.writeUByte(this.extra); + outputStream.writeUByte(this.altEntityKind); + outputStream.writeUByte(this.altDomain); + outputStream.writeUShort(this.altCountry); + outputStream.writeUByte(this.altCategory); + outputStream.writeUByte(this.altSubcategory); + outputStream.writeUByte(this.altSpecific); + outputStream.writeUByte(this.altExtra); + outputStream.writeFloat32(this.xVelocity); + outputStream.writeFloat32(this.yVelocity); + outputStream.writeFloat32(this.zVelocity); + outputStream.writeFloat64(this.xLocation); + outputStream.writeFloat64(this.yLocation); + outputStream.writeFloat64(this.zLocation); + outputStream.writeFloat32(this.psi); + outputStream.writeFloat32(this.theta); + outputStream.writeFloat32(this.phi); + outputStream.writeInt(this.entityAppearance); + outputStream.writeUByte(this.deadReckoningAlgorithm); + for(var idx = 0; idx < 15; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + outputStream.writeFloat32(this.xAcceleration); + outputStream.writeFloat32(this.yAcceleration); + outputStream.writeFloat32(this.zAcceleration); + outputStream.writeFloat32(this.xAngularVelocity); + outputStream.writeFloat32(this.yAngularVelocity); + outputStream.writeFloat32(this.zAngularVelocity); + for(var idx = 0; idx < 12; idx++) + { + outputStream.writeByte(this.marking[ idx ] ); + } + outputStream.writeInt(this.capabilities); + for(var idx = 0; idx < this.articulationParameters.length; idx++) + { + articulationParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Sectioin 5.3.4.1. Information about someone firing something. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FirePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 2; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 2; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that shot */ + this.firingEntityID = new dis.EntityID(); + + /** ID of the entity that is being shot at */ + this.targetEntityID = new dis.EntityID(); + + /** ID of the munition that is being shot */ + this.munitionID = new dis.EntityID(); + + /** ID of event */ + this.eventID = new dis.EventID(); + + this.fireMissionIndex = 0; + + /** location of the firing event */ + this.locationInWorldCoordinates = new dis.Vector3Double(); + + /** Describes munitions used in the firing event */ + this.burstDescriptor = new dis.BurstDescriptor(); + + /** Velocity of the ammunition */ + this.velocity = new dis.Vector3Float(); + + /** range to the target. Note the word range is a SQL reserved word. */ + this.rangeToTarget = 0; + + dis.FirePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.firingEntityID.initFromBinaryDIS(inputStream); + this.targetEntityID.initFromBinaryDIS(inputStream); + this.munitionID.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.fireMissionIndex = inputStream.readInt(); + this.locationInWorldCoordinates.initFromBinaryDIS(inputStream); + this.burstDescriptor.initFromBinaryDIS(inputStream); + this.velocity.initFromBinaryDIS(inputStream); + this.rangeToTarget = inputStream.readFloat32(); + }; + + dis.FirePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.firingEntityID.encodeToBinaryDIS(outputStream); + this.targetEntityID.encodeToBinaryDIS(outputStream); + this.munitionID.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + outputStream.writeInt(this.fireMissionIndex); + this.locationInWorldCoordinates.encodeToBinaryDIS(outputStream); + this.burstDescriptor.encodeToBinaryDIS(outputStream); + this.velocity.encodeToBinaryDIS(outputStream); + outputStream.writeFloat32(this.rangeToTarget); + }; +}; // end of class +/** + * Section 5.2.18. Fixed Datum Record + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FixedDatum = function() +{ + /** ID of the fixed datum */ + this.fixedDatumID = 0; + + /** Value for the fixed datum */ + this.fixedDatumValue = 0; + + dis.FixedDatum.prototype.initFromBinaryDIS = function(inputStream) + { + + this.fixedDatumID = inputStream.readInt(); + this.fixedDatumValue = inputStream.readInt(); + }; + + dis.FixedDatum.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUInt(this.fixedDatumID); + outputStream.writeUInt(this.fixedDatumValue); + }; +}; // end of class +/** + * 32 bit piece of data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FourByteChunk = function() +{ + /** four bytes of arbitrary data */ + this.otherParameters = new Array(0, 0, 0, 0); + + dis.FourByteChunk.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 4; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + }; + + dis.FourByteChunk.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 4; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.2.22. Contains electromagnetic emmision regineratin parameters that are variable throughout a scenario dependent on the actions of the participants in the simulation. Also provides basic parametric data that may be used to support low-fidelity simulations. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FundamentalParameterData = function() +{ + /** center frequency of the emission in hertz. */ + this.frequency = 0; + + /** Bandwidth of the frequencies corresponding to the fequency field. */ + this.frequencyRange = 0; + + /** Effective radiated power for the emission in DdBm. For a radar noise jammer, indicates the peak of the transmitted power. */ + this.effectiveRadiatedPower = 0; + + /** Average repetition frequency of the emission in hertz. */ + this.pulseRepetitionFrequency = 0; + + /** Average pulse width of the emission in microseconds. */ + this.pulseWidth = 0; + + /** Specifies the beam azimuth an elevation centers and corresponding half-angles to describe the scan volume */ + this.beamAzimuthCenter = 0; + + /** Specifies the beam azimuth sweep to determine scan volume */ + this.beamAzimuthSweep = 0; + + /** Specifies the beam elevation center to determine scan volume */ + this.beamElevationCenter = 0; + + /** Specifies the beam elevation sweep to determine scan volume */ + this.beamElevationSweep = 0; + + /** allows receiver to synchronize its regenerated scan pattern to that of the emmitter. Specifies the percentage of time a scan is through its pattern from its origion. */ + this.beamSweepSync = 0; + + dis.FundamentalParameterData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.frequency = inputStream.readFloat32(); + this.frequencyRange = inputStream.readFloat32(); + this.effectiveRadiatedPower = inputStream.readFloat32(); + this.pulseRepetitionFrequency = inputStream.readFloat32(); + this.pulseWidth = inputStream.readFloat32(); + this.beamAzimuthCenter = inputStream.readFloat32(); + this.beamAzimuthSweep = inputStream.readFloat32(); + this.beamElevationCenter = inputStream.readFloat32(); + this.beamElevationSweep = inputStream.readFloat32(); + this.beamSweepSync = inputStream.readFloat32(); + }; + + dis.FundamentalParameterData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.frequency); + outputStream.writeFloat32(this.frequencyRange); + outputStream.writeFloat32(this.effectiveRadiatedPower); + outputStream.writeFloat32(this.pulseRepetitionFrequency); + outputStream.writeFloat32(this.pulseWidth); + outputStream.writeFloat32(this.beamAzimuthCenter); + outputStream.writeFloat32(this.beamAzimuthSweep); + outputStream.writeFloat32(this.beamElevationCenter); + outputStream.writeFloat32(this.beamElevationSweep); + outputStream.writeFloat32(this.beamSweepSync); + }; +}; // end of class +/** + * 5.2.45. Fundamental IFF atc data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.FundamentalParameterDataIff = function() +{ + /** ERP */ + this.erp = 0; + + /** frequency */ + this.frequency = 0; + + /** pgrf */ + this.pgrf = 0; + + /** Pulse width */ + this.pulseWidth = 0; + + /** Burst length */ + this.burstLength = 0; + + /** Applicable modes enumeration */ + this.applicableModes = 0; + + /** padding */ + this.pad2 = 0; + + /** padding */ + this.pad3 = 0; + + dis.FundamentalParameterDataIff.prototype.initFromBinaryDIS = function(inputStream) + { + + this.erp = inputStream.readFloat32(); + this.frequency = inputStream.readFloat32(); + this.pgrf = inputStream.readFloat32(); + this.pulseWidth = inputStream.readFloat32(); + this.burstLength = inputStream.readInt(); + this.applicableModes = inputStream.readUByte(); + this.pad2 = inputStream.readUShort(); + this.pad3 = inputStream.readUByte(); + }; + + dis.FundamentalParameterDataIff.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.erp); + outputStream.writeFloat32(this.frequency); + outputStream.writeFloat32(this.pgrf); + outputStream.writeFloat32(this.pulseWidth); + outputStream.writeUInt(this.burstLength); + outputStream.writeUByte(this.applicableModes); + outputStream.writeUShort(this.pad2); + outputStream.writeUByte(this.pad3); + }; +}; // end of class +/** + * 5.2.44: Grid data record, a common abstract superclass for several subtypes + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.GridAxisRecord = function() +{ + /** type of environmental sample */ + this.sampleType = 0; + + /** value that describes data representation */ + this.dataRepresentation = 0; + + dis.GridAxisRecord.prototype.initFromBinaryDIS = function(inputStream) + { + + this.sampleType = inputStream.readUShort(); + this.dataRepresentation = inputStream.readUShort(); + }; + + dis.GridAxisRecord.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.sampleType); + outputStream.writeUShort(this.dataRepresentation); + }; +}; // end of class +/** + * 5.2.44: Grid data record, representation 0 + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.GridAxisRecordRepresentation0 = function() +{ + /** type of environmental sample */ + this.sampleType = 0; + + /** value that describes data representation */ + this.dataRepresentation = 0; + + /** number of bytes of environmental state data */ + this.numberOfBytes = 0; + + /** variable length list of data parameters ^^^this is wrong--need padding as well */ + this.dataValues = new Array(); + + dis.GridAxisRecordRepresentation0.prototype.initFromBinaryDIS = function(inputStream) + { + + this.sampleType = inputStream.readUShort(); + this.dataRepresentation = inputStream.readUShort(); + this.numberOfBytes = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfBytes; idx++) + { + var anX = new dis.OneByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.dataValues.push(anX); + } + + }; + + dis.GridAxisRecordRepresentation0.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.sampleType); + outputStream.writeUShort(this.dataRepresentation); + outputStream.writeUShort(this.numberOfBytes); + for(var idx = 0; idx < this.dataValues.length; idx++) + { + dataValues[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.2.44: Grid data record, representation 1 + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.GridAxisRecordRepresentation1 = function() +{ + /** type of environmental sample */ + this.sampleType = 0; + + /** value that describes data representation */ + this.dataRepresentation = 0; + + /** constant scale factor */ + this.fieldScale = 0; + + /** constant offset used to scale grid data */ + this.fieldOffset = 0; + + /** Number of data values */ + this.numberOfValues = 0; + + /** variable length list of data parameters ^^^this is wrong--need padding as well */ + this.dataValues = new Array(); + + dis.GridAxisRecordRepresentation1.prototype.initFromBinaryDIS = function(inputStream) + { + + this.sampleType = inputStream.readUShort(); + this.dataRepresentation = inputStream.readUShort(); + this.fieldScale = inputStream.readFloat32(); + this.fieldOffset = inputStream.readFloat32(); + this.numberOfValues = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfValues; idx++) + { + var anX = new dis.TwoByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.dataValues.push(anX); + } + + }; + + dis.GridAxisRecordRepresentation1.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.sampleType); + outputStream.writeUShort(this.dataRepresentation); + outputStream.writeFloat32(this.fieldScale); + outputStream.writeFloat32(this.fieldOffset); + outputStream.writeUShort(this.numberOfValues); + for(var idx = 0; idx < this.dataValues.length; idx++) + { + dataValues[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.2.44: Grid data record, representation 1 + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.GridAxisRecordRepresentation2 = function() +{ + /** type of environmental sample */ + this.sampleType = 0; + + /** value that describes data representation */ + this.dataRepresentation = 0; + + /** number of values */ + this.numberOfValues = 0; + + /** variable length list of data parameters ^^^this is wrong--need padding as well */ + this.dataValues = new Array(); + + dis.GridAxisRecordRepresentation2.prototype.initFromBinaryDIS = function(inputStream) + { + + this.sampleType = inputStream.readUShort(); + this.dataRepresentation = inputStream.readUShort(); + this.numberOfValues = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfValues; idx++) + { + var anX = new dis.FourByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.dataValues.push(anX); + } + + }; + + dis.GridAxisRecordRepresentation2.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.sampleType); + outputStream.writeUShort(this.dataRepresentation); + outputStream.writeUShort(this.numberOfValues); + for(var idx = 0; idx < this.dataValues.length; idx++) + { + dataValues[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.11.2: Information about globat, spatially varying enviornmental effects. This requires manual cleanup; the grid axis records are variable sized. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.GriddedDataPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 42; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** environmental simulation application ID */ + this.environmentalSimulationApplicationID = new dis.EntityID(); + + /** unique identifier for each piece of enviornmental data */ + this.fieldNumber = 0; + + /** sequence number for the total set of PDUS used to transmit the data */ + this.pduNumber = 0; + + /** Total number of PDUS used to transmit the data */ + this.pduTotal = 0; + + /** coordinate system of the grid */ + this.coordinateSystem = 0; + + /** number of grid axes for the environmental data */ + this.numberOfGridAxes = 0; + + /** are domain grid axes identidal to those of the priveious domain update? */ + this.constantGrid = 0; + + /** type of environment */ + this.environmentType = new dis.EntityType(); + + /** orientation of the data grid */ + this.orientation = new dis.Orientation(); + + /** valid time of the enviormental data sample, 64 bit unsigned int */ + this.sampleTime = 0; + + /** total number of all data values for all pdus for an environmental sample */ + this.totalValues = 0; + + /** total number of data values at each grid point. */ + this.vectorDimension = 0; + + /** padding */ + this.padding1 = 0; + + /** padding */ + this.padding2 = 0; + + /** Grid data ^^^This is wrong */ + this.gridDataList = new Array(); + + dis.GriddedDataPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.environmentalSimulationApplicationID.initFromBinaryDIS(inputStream); + this.fieldNumber = inputStream.readUShort(); + this.pduNumber = inputStream.readUShort(); + this.pduTotal = inputStream.readUShort(); + this.coordinateSystem = inputStream.readUShort(); + this.numberOfGridAxes = inputStream.readUByte(); + this.constantGrid = inputStream.readUByte(); + this.environmentType.initFromBinaryDIS(inputStream); + this.orientation.initFromBinaryDIS(inputStream); + this.sampleTime = inputStream.readLong(); + this.totalValues = inputStream.readInt(); + this.vectorDimension = inputStream.readUByte(); + this.padding1 = inputStream.readUShort(); + this.padding2 = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfGridAxes; idx++) + { + var anX = new dis.GridAxisRecord(); + anX.initFromBinaryDIS(inputStream); + this.gridDataList.push(anX); + } + + }; + + dis.GriddedDataPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.environmentalSimulationApplicationID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.fieldNumber); + outputStream.writeUShort(this.pduNumber); + outputStream.writeUShort(this.pduTotal); + outputStream.writeUShort(this.coordinateSystem); + outputStream.writeUByte(this.numberOfGridAxes); + outputStream.writeUByte(this.constantGrid); + this.environmentType.encodeToBinaryDIS(outputStream); + this.orientation.encodeToBinaryDIS(outputStream); + outputStream.writeLong(this.sampleTime); + outputStream.writeUInt(this.totalValues); + outputStream.writeUByte(this.vectorDimension); + outputStream.writeUShort(this.padding1); + outputStream.writeUByte(this.padding2); + for(var idx = 0; idx < this.gridDataList.length; idx++) + { + gridDataList[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.3.7.4.1: Navigational and IFF PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IffAtcNavAidsLayer1Pdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 28; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the emissions */ + this.emittingEntityId = new dis.EntityID(); + + /** Number generated by the issuing simulation to associate realted events. */ + this.eventID = new dis.EventID(); + + /** Location wrt entity. There is some ambugiuity in the standard here, but this is the order it is listed in the table. */ + this.location = new dis.Vector3Float(); + + /** System ID information */ + this.systemID = new dis.SystemID(); + + /** padding */ + this.pad2 = 0; + + /** fundamental parameters */ + this.fundamentalParameters = new dis.IffFundamentalData(); + + dis.IffAtcNavAidsLayer1Pdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.emittingEntityId.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.location.initFromBinaryDIS(inputStream); + this.systemID.initFromBinaryDIS(inputStream); + this.pad2 = inputStream.readUShort(); + this.fundamentalParameters.initFromBinaryDIS(inputStream); + }; + + dis.IffAtcNavAidsLayer1Pdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.emittingEntityId.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + this.location.encodeToBinaryDIS(outputStream); + this.systemID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.pad2); + this.fundamentalParameters.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.7.4.2 When present, layer 2 should follow layer 1 and have the following fields. This requires manual cleanup. the beamData attribute semantics are used in multiple ways. UNFINSISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IffAtcNavAidsLayer2Pdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 28; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the emissions */ + this.emittingEntityId = new dis.EntityID(); + + /** Number generated by the issuing simulation to associate realted events. */ + this.eventID = new dis.EventID(); + + /** Location wrt entity. There is some ambugiuity in the standard here, but this is the order it is listed in the table. */ + this.location = new dis.Vector3Float(); + + /** System ID information */ + this.systemID = new dis.SystemID(); + + /** padding */ + this.pad2 = 0; + + /** fundamental parameters */ + this.fundamentalParameters = new dis.IffFundamentalData(); + + /** layer header */ + this.layerHeader = new dis.LayerHeader(); + + /** beam data */ + this.beamData = new dis.BeamData(); + + /** Secondary operational data, 5.2.57 */ + this.secondaryOperationalData = new dis.BeamData(); + + /** variable length list of fundamental parameters. ^^^This is wrong */ + this.fundamentalIffParameters = new Array(); + + dis.IffAtcNavAidsLayer2Pdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.emittingEntityId.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.location.initFromBinaryDIS(inputStream); + this.systemID.initFromBinaryDIS(inputStream); + this.pad2 = inputStream.readUShort(); + this.fundamentalParameters.initFromBinaryDIS(inputStream); + this.layerHeader.initFromBinaryDIS(inputStream); + this.beamData.initFromBinaryDIS(inputStream); + this.secondaryOperationalData.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.pad2; idx++) + { + var anX = new dis.FundamentalParameterDataIff(); + anX.initFromBinaryDIS(inputStream); + this.fundamentalIffParameters.push(anX); + } + + }; + + dis.IffAtcNavAidsLayer2Pdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.emittingEntityId.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + this.location.encodeToBinaryDIS(outputStream); + this.systemID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.pad2); + this.fundamentalParameters.encodeToBinaryDIS(outputStream); + this.layerHeader.encodeToBinaryDIS(outputStream); + this.beamData.encodeToBinaryDIS(outputStream); + this.secondaryOperationalData.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.fundamentalIffParameters.length; idx++) + { + fundamentalIffParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.2.42. Basic operational data ofr IFF ATC NAVAIDS + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IffFundamentalData = function() +{ + /** system status */ + this.systemStatus = 0; + + /** Alternate parameter 4 */ + this.alternateParameter4 = 0; + + /** eight boolean fields */ + this.informationLayers = 0; + + /** enumeration */ + this.modifier = 0; + + /** parameter, enumeration */ + this.parameter1 = 0; + + /** parameter, enumeration */ + this.parameter2 = 0; + + /** parameter, enumeration */ + this.parameter3 = 0; + + /** parameter, enumeration */ + this.parameter4 = 0; + + /** parameter, enumeration */ + this.parameter5 = 0; + + /** parameter, enumeration */ + this.parameter6 = 0; + + dis.IffFundamentalData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.systemStatus = inputStream.readUByte(); + this.alternateParameter4 = inputStream.readUByte(); + this.informationLayers = inputStream.readUByte(); + this.modifier = inputStream.readUByte(); + this.parameter1 = inputStream.readUShort(); + this.parameter2 = inputStream.readUShort(); + this.parameter3 = inputStream.readUShort(); + this.parameter4 = inputStream.readUShort(); + this.parameter5 = inputStream.readUShort(); + this.parameter6 = inputStream.readUShort(); + }; + + dis.IffFundamentalData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.systemStatus); + outputStream.writeUByte(this.alternateParameter4); + outputStream.writeUByte(this.informationLayers); + outputStream.writeUByte(this.modifier); + outputStream.writeUShort(this.parameter1); + outputStream.writeUShort(this.parameter2); + outputStream.writeUShort(this.parameter3); + outputStream.writeUShort(this.parameter4); + outputStream.writeUShort(this.parameter5); + outputStream.writeUShort(this.parameter6); + }; +}; // end of class +/** + * 5.2.46. Intercom communcations parameters + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IntercomCommunicationsParameters = function() +{ + /** Type of intercom parameters record */ + this.recordType = 0; + + /** length of record-specifid field, in octets */ + this.recordLength = 0; + + /** variable length list of data parameters */ + this.parameterValues = new Array(); + + dis.IntercomCommunicationsParameters.prototype.initFromBinaryDIS = function(inputStream) + { + + this.recordType = inputStream.readUShort(); + this.recordLength = inputStream.readUShort(); + for(var idx = 0; idx < this.recordLength; idx++) + { + var anX = new dis.OneByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.parameterValues.push(anX); + } + + }; + + dis.IntercomCommunicationsParameters.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.recordType); + outputStream.writeUShort(this.recordLength); + for(var idx = 0; idx < this.parameterValues.length; idx++) + { + parameterValues[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.8.5. Detailed inofrmation about the state of an intercom device and the actions it is requestion of another intercom device, or the response to a requested action. Required manual intervention to fix the intercom parameters, which can be of varialbe length. UNFINSISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IntercomControlPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 32; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** control type */ + this.controlType = 0; + + /** control type */ + this.communicationsChannelType = 0; + + /** Source entity ID */ + this.sourceEntityID = new dis.EntityID(); + + /** The specific intercom device being simulated within an entity. */ + this.sourceCommunicationsDeviceID = 0; + + /** Line number to which the intercom control refers */ + this.sourceLineID = 0; + + /** priority of this message relative to transmissons from other intercom devices */ + this.transmitPriority = 0; + + /** current transmit state of the line */ + this.transmitLineState = 0; + + /** detailed type requested. */ + this.command = 0; + + /** eid of the entity that has created this intercom channel. */ + this.masterEntityID = new dis.EntityID(); + + /** specific intercom device that has created this intercom channel */ + this.masterCommunicationsDeviceID = 0; + + /** number of intercom parameters */ + this.intercomParametersLength = 0; + + /** Must be */ + this.intercomParameters = new Array(); + + dis.IntercomControlPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.controlType = inputStream.readUByte(); + this.communicationsChannelType = inputStream.readUByte(); + this.sourceEntityID.initFromBinaryDIS(inputStream); + this.sourceCommunicationsDeviceID = inputStream.readUByte(); + this.sourceLineID = inputStream.readUByte(); + this.transmitPriority = inputStream.readUByte(); + this.transmitLineState = inputStream.readUByte(); + this.command = inputStream.readUByte(); + this.masterEntityID.initFromBinaryDIS(inputStream); + this.masterCommunicationsDeviceID = inputStream.readUShort(); + this.intercomParametersLength = inputStream.readInt(); + for(var idx = 0; idx < this.intercomParametersLength; idx++) + { + var anX = new dis.IntercomCommunicationsParameters(); + anX.initFromBinaryDIS(inputStream); + this.intercomParameters.push(anX); + } + + }; + + dis.IntercomControlPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + outputStream.writeUByte(this.controlType); + outputStream.writeUByte(this.communicationsChannelType); + this.sourceEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.sourceCommunicationsDeviceID); + outputStream.writeUByte(this.sourceLineID); + outputStream.writeUByte(this.transmitPriority); + outputStream.writeUByte(this.transmitLineState); + outputStream.writeUByte(this.command); + this.masterEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.masterCommunicationsDeviceID); + outputStream.writeUInt(this.intercomParametersLength); + for(var idx = 0; idx < this.intercomParameters.length; idx++) + { + intercomParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.8.4. Actual transmission of intercome voice data. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IntercomSignalPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 31; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entitythat is the source of the communication */ + this.entityId = new dis.EntityID(); + + /** particular radio within an entity */ + this.communicationsDeviceID = 0; + + /** encoding scheme */ + this.encodingScheme = 0; + + /** tactical data link type */ + this.tdlType = 0; + + /** sample rate */ + this.sampleRate = 0; + + /** data length, in bits */ + this.dataLength = 0; + + /** samples */ + this.samples = 0; + + /** data bytes */ + this.data = new Array(); + + dis.IntercomSignalPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityId.initFromBinaryDIS(inputStream); + this.communicationsDeviceID = inputStream.readUShort(); + this.encodingScheme = inputStream.readUShort(); + this.tdlType = inputStream.readUShort(); + this.sampleRate = inputStream.readInt(); + this.dataLength = inputStream.readUShort(); + this.samples = inputStream.readUShort(); + for(var idx = 0; idx < this.dataLength; idx++) + { + var anX = new dis.OneByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.data.push(anX); + } + + }; + + dis.IntercomSignalPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityId.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.communicationsDeviceID); + outputStream.writeUShort(this.encodingScheme); + outputStream.writeUShort(this.tdlType); + outputStream.writeUInt(this.sampleRate); + outputStream.writeUShort(this.dataLength); + outputStream.writeUShort(this.samples); + for(var idx = 0; idx < this.data.length; idx++) + { + data[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.9.2 Information about a particular group of entities grouped together for the purposes of netowrk bandwidth reduction or aggregation. Needs manual cleanup. The GED size requires a database lookup. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IsGroupOfPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 34; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 7; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of aggregated entities */ + this.groupEntityID = new dis.EntityID(); + + /** type of entities constituting the group */ + this.groupedEntityCategory = 0; + + /** Number of individual entities constituting the group */ + this.numberOfGroupedEntities = 0; + + /** padding */ + this.pad2 = 0; + + /** latitude */ + this.latitude = 0; + + /** longitude */ + this.longitude = 0; + + /** GED records about each individual entity in the group. ^^^this is wrong--need a database lookup to find the actual size of the list elements */ + this.groupedEntityDescriptions = new Array(); + + dis.IsGroupOfPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.groupEntityID.initFromBinaryDIS(inputStream); + this.groupedEntityCategory = inputStream.readUByte(); + this.numberOfGroupedEntities = inputStream.readUByte(); + this.pad2 = inputStream.readInt(); + this.latitude = inputStream.readFloat64(); + this.longitude = inputStream.readFloat64(); + for(var idx = 0; idx < this.numberOfGroupedEntities; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.groupedEntityDescriptions.push(anX); + } + + }; + + dis.IsGroupOfPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.groupEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.groupedEntityCategory); + outputStream.writeUByte(this.numberOfGroupedEntities); + outputStream.writeUInt(this.pad2); + outputStream.writeFloat64(this.latitude); + outputStream.writeFloat64(this.longitude); + for(var idx = 0; idx < this.groupedEntityDescriptions.length; idx++) + { + groupedEntityDescriptions[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.9.4 The joining of two or more simulation entities is communicated by this PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.IsPartOfPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 36; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 7; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of entity originating PDU */ + this.orginatingEntityID = new dis.EntityID(); + + /** ID of entity receiving PDU */ + this.receivingEntityID = new dis.EntityID(); + + /** relationship of joined parts */ + this.relationship = new dis.Relationship(); + + /** location of part; centroid of part in host's coordinate system. x=range, y=bearing, z=0 */ + this.partLocation = new dis.Vector3Float(); + + /** named location */ + this.namedLocationID = new dis.NamedLocation(); + + /** entity type */ + this.partEntityType = new dis.EntityType(); + + dis.IsPartOfPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.orginatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.relationship.initFromBinaryDIS(inputStream); + this.partLocation.initFromBinaryDIS(inputStream); + this.namedLocationID.initFromBinaryDIS(inputStream); + this.partEntityType.initFromBinaryDIS(inputStream); + }; + + dis.IsPartOfPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.orginatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.relationship.encodeToBinaryDIS(outputStream); + this.partLocation.encodeToBinaryDIS(outputStream); + this.namedLocationID.encodeToBinaryDIS(outputStream); + this.partEntityType.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * 5.2.47. Layer header. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.LayerHeader = function() +{ + /** Layer number */ + this.layerNumber = 0; + + /** Layer speccific information enumeration */ + this.layerSpecificInformaiton = 0; + + /** information length */ + this.length = 0; + + dis.LayerHeader.prototype.initFromBinaryDIS = function(inputStream) + { + + this.layerNumber = inputStream.readUByte(); + this.layerSpecificInformaiton = inputStream.readUByte(); + this.length = inputStream.readUShort(); + }; + + dis.LayerHeader.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.layerNumber); + outputStream.writeUByte(this.layerSpecificInformaiton); + outputStream.writeUShort(this.length); + }; +}; // end of class +/** + * Section 5.3.11.4: Information abut the addition or modification of a synthecic enviroment object that is anchored to the terrain with a single point and has size or orientation. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.LinearObjectStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 44; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object in synthetic environment */ + this.objectID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.referencedObjectID = new dis.EntityID(); + + /** unique update number of each state transition of an object */ + this.updateNumber = 0; + + /** force ID */ + this.forceID = 0; + + /** number of linear segment parameters */ + this.numberOfSegments = 0; + + /** requesterID */ + this.requesterID = new dis.SimulationAddress(); + + /** receiver ID */ + this.receivingID = new dis.SimulationAddress(); + + /** Object type */ + this.objectType = new dis.ObjectType(); + + /** Linear segment parameters */ + this.linearSegmentParameters = new Array(); + + dis.LinearObjectStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.objectID.initFromBinaryDIS(inputStream); + this.referencedObjectID.initFromBinaryDIS(inputStream); + this.updateNumber = inputStream.readUShort(); + this.forceID = inputStream.readUByte(); + this.numberOfSegments = inputStream.readUByte(); + this.requesterID.initFromBinaryDIS(inputStream); + this.receivingID.initFromBinaryDIS(inputStream); + this.objectType.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfSegments; idx++) + { + var anX = new dis.LinearSegmentParameter(); + anX.initFromBinaryDIS(inputStream); + this.linearSegmentParameters.push(anX); + } + + }; + + dis.LinearObjectStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.objectID.encodeToBinaryDIS(outputStream); + this.referencedObjectID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.updateNumber); + outputStream.writeUByte(this.forceID); + outputStream.writeUByte(this.numberOfSegments); + this.requesterID.encodeToBinaryDIS(outputStream); + this.receivingID.encodeToBinaryDIS(outputStream); + this.objectType.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.linearSegmentParameters.length; idx++) + { + linearSegmentParameters[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 5.2.48: Linear segment parameters + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.LinearSegmentParameter = function() +{ + /** number of segments */ + this.segmentNumber = 0; + + /** segment appearance */ + this.segmentAppearance = new dis.SixByteChunk(); + + /** location */ + this.location = new dis.Vector3Double(); + + /** orientation */ + this.orientation = new dis.Orientation(); + + /** segmentLength */ + this.segmentLength = 0; + + /** segmentWidth */ + this.segmentWidth = 0; + + /** segmentHeight */ + this.segmentHeight = 0; + + /** segment Depth */ + this.segmentDepth = 0; + + /** segment Depth */ + this.pad1 = 0; + + dis.LinearSegmentParameter.prototype.initFromBinaryDIS = function(inputStream) + { + + this.segmentNumber = inputStream.readUByte(); + this.segmentAppearance.initFromBinaryDIS(inputStream); + this.location.initFromBinaryDIS(inputStream); + this.orientation.initFromBinaryDIS(inputStream); + this.segmentLength = inputStream.readUShort(); + this.segmentWidth = inputStream.readUShort(); + this.segmentHeight = inputStream.readUShort(); + this.segmentDepth = inputStream.readUShort(); + this.pad1 = inputStream.readInt(); + }; + + dis.LinearSegmentParameter.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.segmentNumber); + this.segmentAppearance.encodeToBinaryDIS(outputStream); + this.location.encodeToBinaryDIS(outputStream); + this.orientation.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.segmentLength); + outputStream.writeUShort(this.segmentWidth); + outputStream.writeUShort(this.segmentHeight); + outputStream.writeUShort(this.segmentDepth); + outputStream.writeUInt(this.pad1); + }; +}; // end of class +/** + * Section 5.3.5. Abstract superclass for logistics PDUs. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.LogisticsFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.LogisticsFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.LogisticsFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Section 5.2.15. Specifies the character set used inthe first byte, followed by 11 characters of text data. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Marking = function() +{ + /** The character set */ + this.characterSet = 0; + + /** The characters */ + this.characters = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + dis.Marking.prototype.initFromBinaryDIS = function(inputStream) + { + + this.characterSet = inputStream.readUByte(); + for(var idx = 0; idx < 11; idx++) + { + this.characters[ idx ] = inputStream.readByte(); + } + }; + + dis.Marking.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.characterSet); + for(var idx = 0; idx < 11; idx++) + { + outputStream.writeByte(this.characters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.3.10.3 Information about individual mines within a minefield. This is very, very wrong. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.MinefieldDataPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 39; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 8; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Minefield ID */ + this.minefieldID = new dis.EntityID(); + + /** ID of entity making request */ + this.requestingEntityID = new dis.EntityID(); + + /** Minefield sequence number */ + this.minefieldSequenceNumbeer = 0; + + /** request ID */ + this.requestID = 0; + + /** pdu sequence number */ + this.pduSequenceNumber = 0; + + /** number of pdus in response */ + this.numberOfPdus = 0; + + /** how many mines are in this PDU */ + this.numberOfMinesInThisPdu = 0; + + /** how many sensor type are in this PDU */ + this.numberOfSensorTypes = 0; + + /** padding */ + this.pad2 = 0; + + /** 32 boolean fields */ + this.dataFilter = 0; + + /** Mine type */ + this.mineType = new dis.EntityType(); + + /** Sensor types, each 16 bits long */ + this.sensorTypes = new Array(); + + /** Padding to get things 32-bit aligned. ^^^this is wrong--dyanmically sized padding needed */ + this.pad3 = 0; + + /** Mine locations */ + this.mineLocation = new Array(); + + dis.MinefieldDataPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.minefieldID.initFromBinaryDIS(inputStream); + this.requestingEntityID.initFromBinaryDIS(inputStream); + this.minefieldSequenceNumbeer = inputStream.readUShort(); + this.requestID = inputStream.readUByte(); + this.pduSequenceNumber = inputStream.readUByte(); + this.numberOfPdus = inputStream.readUByte(); + this.numberOfMinesInThisPdu = inputStream.readUByte(); + this.numberOfSensorTypes = inputStream.readUByte(); + this.pad2 = inputStream.readUByte(); + this.dataFilter = inputStream.readInt(); + this.mineType.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfSensorTypes; idx++) + { + var anX = new dis.TwoByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.sensorTypes.push(anX); + } + + this.pad3 = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfMinesInThisPdu; idx++) + { + var anX = new dis.Vector3Float(); + anX.initFromBinaryDIS(inputStream); + this.mineLocation.push(anX); + } + + }; + + dis.MinefieldDataPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.minefieldID.encodeToBinaryDIS(outputStream); + this.requestingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.minefieldSequenceNumbeer); + outputStream.writeUByte(this.requestID); + outputStream.writeUByte(this.pduSequenceNumber); + outputStream.writeUByte(this.numberOfPdus); + outputStream.writeUByte(this.numberOfMinesInThisPdu); + outputStream.writeUByte(this.numberOfSensorTypes); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.dataFilter); + this.mineType.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.sensorTypes.length; idx++) + { + sensorTypes[idx].encodeToBinaryDIS(outputStream); + } + + outputStream.writeUByte(this.pad3); + for(var idx = 0; idx < this.mineLocation.length; idx++) + { + mineLocation[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.10.1 Abstract superclass for PDUs relating to minefields + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.MinefieldFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 8; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.MinefieldFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.MinefieldFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Section 5.3.10.2 Query a minefield for information about individual mines. Requires manual clean up to get the padding right. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.MinefieldQueryPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 38; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 8; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Minefield ID */ + this.minefieldID = new dis.EntityID(); + + /** EID of entity making the request */ + this.requestingEntityID = new dis.EntityID(); + + /** request ID */ + this.requestID = 0; + + /** Number of perimeter points for the minefield */ + this.numberOfPerimeterPoints = 0; + + /** Padding */ + this.pad2 = 0; + + /** Number of sensor types */ + this.numberOfSensorTypes = 0; + + /** data filter, 32 boolean fields */ + this.dataFilter = 0; + + /** Entity type of mine being requested */ + this.requestedMineType = new dis.EntityType(); + + /** perimeter points of request */ + this.requestedPerimeterPoints = new Array(); + + /** Sensor types, each 16 bits long */ + this.sensorTypes = new Array(); + + dis.MinefieldQueryPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.minefieldID.initFromBinaryDIS(inputStream); + this.requestingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readUByte(); + this.numberOfPerimeterPoints = inputStream.readUByte(); + this.pad2 = inputStream.readUByte(); + this.numberOfSensorTypes = inputStream.readUByte(); + this.dataFilter = inputStream.readInt(); + this.requestedMineType.initFromBinaryDIS(inputStream); + for(var idx = 0; idx < this.numberOfPerimeterPoints; idx++) + { + var anX = new dis.Point(); + anX.initFromBinaryDIS(inputStream); + this.requestedPerimeterPoints.push(anX); + } + + for(var idx = 0; idx < this.numberOfSensorTypes; idx++) + { + var anX = new dis.TwoByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.sensorTypes.push(anX); + } + + }; + + dis.MinefieldQueryPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.minefieldID.encodeToBinaryDIS(outputStream); + this.requestingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requestID); + outputStream.writeUByte(this.numberOfPerimeterPoints); + outputStream.writeUByte(this.pad2); + outputStream.writeUByte(this.numberOfSensorTypes); + outputStream.writeUInt(this.dataFilter); + this.requestedMineType.encodeToBinaryDIS(outputStream); + for(var idx = 0; idx < this.requestedPerimeterPoints.length; idx++) + { + requestedPerimeterPoints[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.sensorTypes.length; idx++) + { + sensorTypes[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.10.4 proivde the means to request a retransmit of a minefield data pdu. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.MinefieldResponseNackPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 40; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 8; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Minefield ID */ + this.minefieldID = new dis.EntityID(); + + /** entity ID making the request */ + this.requestingEntityID = new dis.EntityID(); + + /** request ID */ + this.requestID = 0; + + /** how many pdus were missing */ + this.numberOfMissingPdus = 0; + + /** PDU sequence numbers that were missing */ + this.missingPduSequenceNumbers = new Array(); + + dis.MinefieldResponseNackPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.minefieldID.initFromBinaryDIS(inputStream); + this.requestingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readUByte(); + this.numberOfMissingPdus = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfMissingPdus; idx++) + { + var anX = new dis.EightByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.missingPduSequenceNumbers.push(anX); + } + + }; + + dis.MinefieldResponseNackPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.minefieldID.encodeToBinaryDIS(outputStream); + this.requestingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requestID); + outputStream.writeUByte(this.numberOfMissingPdus); + for(var idx = 0; idx < this.missingPduSequenceNumbers.length; idx++) + { + missingPduSequenceNumbers[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.10.1 Abstract superclass for PDUs relating to minefields. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.MinefieldStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 37; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 8; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Minefield ID */ + this.minefieldID = new dis.EntityID(); + + /** Minefield sequence */ + this.minefieldSequence = 0; + + /** force ID */ + this.forceID = 0; + + /** Number of permieter points */ + this.numberOfPerimeterPoints = 0; + + /** type of minefield */ + this.minefieldType = new dis.EntityType(); + + /** how many mine types */ + this.numberOfMineTypes = 0; + + /** location of minefield in world coords */ + this.minefieldLocation = new dis.Vector3Double(); + + /** orientation of minefield */ + this.minefieldOrientation = new dis.Orientation(); + + /** appearance bitflags */ + this.appearance = 0; + + /** protocolMode */ + this.protocolMode = 0; + + /** perimeter points for the minefield */ + this.perimeterPoints = new Array(); + + /** Type of mines */ + this.mineType = new Array(); + + dis.MinefieldStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.minefieldID.initFromBinaryDIS(inputStream); + this.minefieldSequence = inputStream.readUShort(); + this.forceID = inputStream.readUByte(); + this.numberOfPerimeterPoints = inputStream.readUByte(); + this.minefieldType.initFromBinaryDIS(inputStream); + this.numberOfMineTypes = inputStream.readUShort(); + this.minefieldLocation.initFromBinaryDIS(inputStream); + this.minefieldOrientation.initFromBinaryDIS(inputStream); + this.appearance = inputStream.readUShort(); + this.protocolMode = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfPerimeterPoints; idx++) + { + var anX = new dis.Point(); + anX.initFromBinaryDIS(inputStream); + this.perimeterPoints.push(anX); + } + + for(var idx = 0; idx < this.numberOfMineTypes; idx++) + { + var anX = new dis.EntityType(); + anX.initFromBinaryDIS(inputStream); + this.mineType.push(anX); + } + + }; + + dis.MinefieldStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.minefieldID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.minefieldSequence); + outputStream.writeUByte(this.forceID); + outputStream.writeUByte(this.numberOfPerimeterPoints); + this.minefieldType.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.numberOfMineTypes); + this.minefieldLocation.encodeToBinaryDIS(outputStream); + this.minefieldOrientation.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.appearance); + outputStream.writeUShort(this.protocolMode); + for(var idx = 0; idx < this.perimeterPoints.length; idx++) + { + perimeterPoints[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.mineType.length; idx++) + { + mineType[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Radio modulation + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ModulationType = function() +{ + /** spread spectrum, 16 bit boolean array */ + this.spreadSpectrum = 0; + + /** major */ + this.major = 0; + + /** detail */ + this.detail = 0; + + /** system */ + this.system = 0; + + dis.ModulationType.prototype.initFromBinaryDIS = function(inputStream) + { + + this.spreadSpectrum = inputStream.readUShort(); + this.major = inputStream.readUShort(); + this.detail = inputStream.readUShort(); + this.system = inputStream.readUShort(); + }; + + dis.ModulationType.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.spreadSpectrum); + outputStream.writeUShort(this.major); + outputStream.writeUShort(this.detail); + outputStream.writeUShort(this.system); + }; +}; // end of class +/** + * discrete ostional relationsihip + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.NamedLocation = function() +{ + /** station name enumeration */ + this.stationName = 0; + + /** station number */ + this.stationNumber = 0; + + dis.NamedLocation.prototype.initFromBinaryDIS = function(inputStream) + { + + this.stationName = inputStream.readUShort(); + this.stationNumber = inputStream.readUShort(); + }; + + dis.NamedLocation.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.stationName); + outputStream.writeUShort(this.stationNumber); + }; +}; // end of class +/** + * Identifies type of object. This is a shorter version of EntityType that omits the specific and extra fields. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ObjectType = function() +{ + /** Kind of entity */ + this.entityKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.domain = 0; + + /** country to which the design of the entity is attributed */ + this.country = 0; + + /** category of entity */ + this.category = 0; + + /** subcategory of entity */ + this.subcategory = 0; + + dis.ObjectType.prototype.initFromBinaryDIS = function(inputStream) + { + + this.entityKind = inputStream.readUByte(); + this.domain = inputStream.readUByte(); + this.country = inputStream.readUShort(); + this.category = inputStream.readUByte(); + this.subcategory = inputStream.readUByte(); + }; + + dis.ObjectType.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.entityKind); + outputStream.writeUByte(this.domain); + outputStream.writeUShort(this.country); + outputStream.writeUByte(this.category); + outputStream.writeUByte(this.subcategory); + }; +}; // end of class +/** + * 8 bit piece of data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.OneByteChunk = function() +{ + /** one byte of arbitrary data */ + this.otherParameters = new Array(0); + + dis.OneByteChunk.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 1; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + }; + + dis.OneByteChunk.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 1; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.2.17. Three floating point values representing an orientation, psi, theta, and phi, aka the euler angles, in radians + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Orientation = function() +{ + this.psi = 0; + + this.theta = 0; + + this.phi = 0; + + dis.Orientation.prototype.initFromBinaryDIS = function(inputStream) + { + + this.psi = inputStream.readFloat32(); + this.theta = inputStream.readFloat32(); + this.phi = inputStream.readFloat32(); + }; + + dis.Orientation.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.psi); + outputStream.writeFloat32(this.theta); + outputStream.writeFloat32(this.phi); + }; +}; // end of class +/** + * The superclass for all PDUs. This incorporates the PduHeader record, section 5.2.29. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Pdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 0; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.Pdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.Pdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Used for XML compatability. A container that holds PDUs + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.PduContainer = function() +{ + /** Number of PDUs in the container list */ + this.numberOfPdus = 0; + + /** record sets */ + this.pdus = new Array(); + + dis.PduContainer.prototype.initFromBinaryDIS = function(inputStream) + { + + this.numberOfPdus = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfPdus; idx++) + { + var anX = new dis.Pdu(); + anX.initFromBinaryDIS(inputStream); + this.pdus.push(anX); + } + + }; + + dis.PduContainer.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeInt(this.numberOfPdus); + for(var idx = 0; idx < this.pdus.length; idx++) + { + pdus[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Non-DIS class, used on SQL databases + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.PduStream = function() +{ + /** Longish description of this PDU stream */ + this.description = new Array(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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + /** short description of this PDU stream */ + this.name = new Array(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, 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, 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, 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, 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, 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, 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, 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); + + /** Start time of recording, in Unix time */ + this.startTime = 0; + + /** stop time of recording, in Unix time */ + this.stopTime = 0; + + dis.PduStream.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 512; idx++) + { + this.description[ idx ] = inputStream.readByte(); + } + for(var idx = 0; idx < 256; idx++) + { + this.name[ idx ] = inputStream.readByte(); + } + this.startTime = inputStream.readLong(); + this.stopTime = inputStream.readLong(); + }; + + dis.PduStream.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 512; idx++) + { + outputStream.writeByte(this.description[ idx ] ); + } + for(var idx = 0; idx < 256; idx++) + { + outputStream.writeByte(this.name[ idx ] ); + } + outputStream.writeLong(this.startTime); + outputStream.writeLong(this.stopTime); + }; +}; // end of class +/** + * x,y point + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Point = function() +{ + /** x */ + this.x = 0; + + /** y */ + this.y = 0; + + dis.Point.prototype.initFromBinaryDIS = function(inputStream) + { + + this.x = inputStream.readFloat32(); + this.y = inputStream.readFloat32(); + }; + + dis.Point.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.x); + outputStream.writeFloat32(this.y); + }; +}; // end of class +/** + * Section 5.3.11.3: Inormation abut the addition or modification of a synthecic enviroment object that is anchored to the terrain with a single point. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.PointObjectStatePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 43; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object in synthetic environment */ + this.objectID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.referencedObjectID = new dis.EntityID(); + + /** unique update number of each state transition of an object */ + this.updateNumber = 0; + + /** force ID */ + this.forceID = 0; + + /** modifications */ + this.modifications = 0; + + /** Object type */ + this.objectType = new dis.ObjectType(); + + /** Object location */ + this.objectLocation = new dis.Vector3Double(); + + /** Object orientation */ + this.objectOrientation = new dis.Orientation(); + + /** Object apperance */ + this.objectAppearance = 0; + + /** requesterID */ + this.requesterID = new dis.SimulationAddress(); + + /** receiver ID */ + this.receivingID = new dis.SimulationAddress(); + + /** padding */ + this.pad2 = 0; + + dis.PointObjectStatePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.objectID.initFromBinaryDIS(inputStream); + this.referencedObjectID.initFromBinaryDIS(inputStream); + this.updateNumber = inputStream.readUShort(); + this.forceID = inputStream.readUByte(); + this.modifications = inputStream.readUByte(); + this.objectType.initFromBinaryDIS(inputStream); + this.objectLocation.initFromBinaryDIS(inputStream); + this.objectOrientation.initFromBinaryDIS(inputStream); + this.objectAppearance = inputStream.readFloat64(); + this.requesterID.initFromBinaryDIS(inputStream); + this.receivingID.initFromBinaryDIS(inputStream); + this.pad2 = inputStream.readInt(); + }; + + dis.PointObjectStatePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.objectID.encodeToBinaryDIS(outputStream); + this.referencedObjectID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.updateNumber); + outputStream.writeUByte(this.forceID); + outputStream.writeUByte(this.modifications); + this.objectType.encodeToBinaryDIS(outputStream); + this.objectLocation.encodeToBinaryDIS(outputStream); + this.objectOrientation.encodeToBinaryDIS(outputStream); + outputStream.writeFloat64(this.objectAppearance); + this.requesterID.encodeToBinaryDIS(outputStream); + this.receivingID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.pad2); + }; +}; // end of class +/** + * Data about a propulsion system + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.PropulsionSystemData = function() +{ + /** powerSetting */ + this.powerSetting = 0; + + /** engine RPMs */ + this.engineRpm = 0; + + dis.PropulsionSystemData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.powerSetting = inputStream.readFloat32(); + this.engineRpm = inputStream.readFloat32(); + }; + + dis.PropulsionSystemData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.powerSetting); + outputStream.writeFloat32(this.engineRpm); + }; +}; // end of class +/** + * Section 5.3.8. Abstract superclass for radio communications PDUs. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RadioCommunicationsFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.RadioCommunicationsFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.RadioCommunicationsFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * Section 5.2.25. Identifies the type of radio + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RadioEntityType = function() +{ + /** Kind of entity */ + this.entityKind = 0; + + /** Domain of entity (air, surface, subsurface, space, etc) */ + this.domain = 0; + + /** country to which the design of the entity is attributed */ + this.country = 0; + + /** category of entity */ + this.category = 0; + + /** specific info based on subcategory field */ + this.nomenclatureVersion = 0; + + this.nomenclature = 0; + + dis.RadioEntityType.prototype.initFromBinaryDIS = function(inputStream) + { + + this.entityKind = inputStream.readUByte(); + this.domain = inputStream.readUByte(); + this.country = inputStream.readUShort(); + this.category = inputStream.readUByte(); + this.nomenclatureVersion = inputStream.readUByte(); + this.nomenclature = inputStream.readUShort(); + }; + + dis.RadioEntityType.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.entityKind); + outputStream.writeUByte(this.domain); + outputStream.writeUShort(this.country); + outputStream.writeUByte(this.category); + outputStream.writeUByte(this.nomenclatureVersion); + outputStream.writeUShort(this.nomenclature); + }; +}; // end of class +/** + * Section 5.3.8.3. Communication of a receiver state. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ReceiverPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 27; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the communication, ie contains the radio */ + this.entityId = new dis.EntityID(); + + /** particular radio within an entity */ + this.radioId = 0; + + /** encoding scheme used, and enumeration */ + this.receiverState = 0; + + /** padding */ + this.padding1 = 0; + + /** received power */ + this.receivedPower = 0; + + /** ID of transmitter */ + this.transmitterEntityId = new dis.EntityID(); + + /** ID of transmitting radio */ + this.transmitterRadioId = 0; + + dis.ReceiverPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityId.initFromBinaryDIS(inputStream); + this.radioId = inputStream.readUShort(); + this.receiverState = inputStream.readUShort(); + this.padding1 = inputStream.readUShort(); + this.receivedPower = inputStream.readFloat32(); + this.transmitterEntityId.initFromBinaryDIS(inputStream); + this.transmitterRadioId = inputStream.readUShort(); + }; + + dis.ReceiverPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityId.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.radioId); + outputStream.writeUShort(this.receiverState); + outputStream.writeUShort(this.padding1); + outputStream.writeFloat32(this.receivedPower); + this.transmitterEntityId.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.transmitterRadioId); + }; +}; // end of class +/** + * Section 5.3.12.13: A request for one or more records of data from an entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RecordQueryReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 65; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** request ID */ + this.requestID = 0; + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding. The spec is unclear and contradictory here. */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** event type */ + this.eventType = 0; + + /** time */ + this.time = 0; + + /** numberOfRecords */ + this.numberOfRecords = 0; + + /** record IDs */ + this.recordIDs = new Array(); + + dis.RecordQueryReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.eventType = inputStream.readUShort(); + this.time = inputStream.readInt(); + this.numberOfRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfRecords; idx++) + { + var anX = new dis.FourByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.recordIDs.push(anX); + } + + }; + + dis.RecordQueryReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUShort(this.eventType); + outputStream.writeUInt(this.time); + outputStream.writeUInt(this.numberOfRecords); + for(var idx = 0; idx < this.recordIDs.length; idx++) + { + recordIDs[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Record sets, used in transfer control request PDU + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RecordSet = function() +{ + /** record ID */ + this.recordID = 0; + + /** record set serial number */ + this.recordSetSerialNumber = 0; + + /** record length */ + this.recordLength = 0; + + /** record count */ + this.recordCount = 0; + + /** ^^^This is wrong--variable sized data records */ + this.recordValues = 0; + + /** ^^^This is wrong--variable sized padding */ + this.pad4 = 0; + + dis.RecordSet.prototype.initFromBinaryDIS = function(inputStream) + { + + this.recordID = inputStream.readInt(); + this.recordSetSerialNumber = inputStream.readInt(); + this.recordLength = inputStream.readUShort(); + this.recordCount = inputStream.readUShort(); + this.recordValues = inputStream.readUShort(); + this.pad4 = inputStream.readUByte(); + }; + + dis.RecordSet.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUInt(this.recordID); + outputStream.writeUInt(this.recordSetSerialNumber); + outputStream.writeUShort(this.recordLength); + outputStream.writeUShort(this.recordCount); + outputStream.writeUShort(this.recordValues); + outputStream.writeUByte(this.pad4); + }; +}; // end of class +/** + * 5.2.56. Purpose for joinging two entities + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Relationship = function() +{ + /** Nature of join */ + this.nature = 0; + + /** position of join */ + this.position = 0; + + dis.Relationship.prototype.initFromBinaryDIS = function(inputStream) + { + + this.nature = inputStream.readUShort(); + this.position = inputStream.readUShort(); + }; + + dis.Relationship.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.nature); + outputStream.writeUShort(this.position); + }; +}; // end of class +/** + * Section 5.3.6.2. Remove an entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RemoveEntityPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 12; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** Identifier for the request */ + this.requestID = 0; + + dis.RemoveEntityPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + }; + + dis.RemoveEntityPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.12.2: Removal of an entity , reliable. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RemoveEntityReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 52; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Request ID */ + this.requestID = 0; + + dis.RemoveEntityReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + }; + + dis.RemoveEntityReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.2.5.5. Repair is complete. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RepairCompletePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 9; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is receiving service */ + this.receivingEntityID = new dis.EntityID(); + + /** Entity that is supplying */ + this.repairingEntityID = new dis.EntityID(); + + /** Enumeration for type of repair */ + this.repair = 0; + + /** padding, number prevents conflict with superclass ivar name */ + this.padding2 = 0; + + dis.RepairCompletePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.repairingEntityID.initFromBinaryDIS(inputStream); + this.repair = inputStream.readUShort(); + this.padding2 = inputStream.readShort(); + }; + + dis.RepairCompletePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.repairingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.repair); + outputStream.writeShort(this.padding2); + }; +}; // end of class +/** + * Section 5.2.5.6. Sent after repair complete PDU. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.RepairResponsePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 10; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is receiving service */ + this.receivingEntityID = new dis.EntityID(); + + /** Entity that is supplying */ + this.repairingEntityID = new dis.EntityID(); + + /** Result of repair operation */ + this.repairResult = 0; + + /** padding */ + this.padding1 = 0; + + /** padding */ + this.padding2 = 0; + + dis.RepairResponsePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.repairingEntityID.initFromBinaryDIS(inputStream); + this.repairResult = inputStream.readUByte(); + this.padding1 = inputStream.readShort(); + this.padding2 = inputStream.readByte(); + }; + + dis.RepairResponsePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.repairingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.repairResult); + outputStream.writeShort(this.padding1); + outputStream.writeByte(this.padding2); + }; +}; // end of class +/** + * Section 5.2.5.4. Cancel of resupply by either the receiving or supplying entity. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ResupplyCancelPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 8; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is receiving service */ + this.receivingEntityID = new dis.EntityID(); + + /** Entity that is supplying */ + this.supplyingEntityID = new dis.EntityID(); + + dis.ResupplyCancelPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.supplyingEntityID.initFromBinaryDIS(inputStream); + }; + + dis.ResupplyCancelPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.supplyingEntityID.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.5.2. Information about a request for supplies. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ResupplyOfferPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 6; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is receiving service */ + this.receivingEntityID = new dis.EntityID(); + + /** Entity that is supplying */ + this.supplyingEntityID = new dis.EntityID(); + + /** how many supplies are being offered */ + this.numberOfSupplyTypes = 0; + + /** padding */ + this.padding1 = 0; + + /** padding */ + this.padding2 = 0; + + this.supplies = new Array(); + + dis.ResupplyOfferPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.supplyingEntityID.initFromBinaryDIS(inputStream); + this.numberOfSupplyTypes = inputStream.readUByte(); + this.padding1 = inputStream.readShort(); + this.padding2 = inputStream.readByte(); + for(var idx = 0; idx < this.numberOfSupplyTypes; idx++) + { + var anX = new dis.SupplyQuantity(); + anX.initFromBinaryDIS(inputStream); + this.supplies.push(anX); + } + + }; + + dis.ResupplyOfferPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.supplyingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.numberOfSupplyTypes); + outputStream.writeShort(this.padding1); + outputStream.writeByte(this.padding2); + for(var idx = 0; idx < this.supplies.length; idx++) + { + supplies[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.5.3. Receipt of supplies is communiated. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ResupplyReceivedPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 7; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is receiving service */ + this.receivingEntityID = new dis.EntityID(); + + /** Entity that is supplying */ + this.supplyingEntityID = new dis.EntityID(); + + /** how many supplies are being offered */ + this.numberOfSupplyTypes = 0; + + /** padding */ + this.padding1 = 0; + + /** padding */ + this.padding2 = 0; + + this.supplies = new Array(); + + dis.ResupplyReceivedPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.supplyingEntityID.initFromBinaryDIS(inputStream); + this.numberOfSupplyTypes = inputStream.readUByte(); + this.padding1 = inputStream.readShort(); + this.padding2 = inputStream.readByte(); + for(var idx = 0; idx < this.numberOfSupplyTypes; idx++) + { + var anX = new dis.SupplyQuantity(); + anX.initFromBinaryDIS(inputStream); + this.supplies.push(anX); + } + + }; + + dis.ResupplyReceivedPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.supplyingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.numberOfSupplyTypes); + outputStream.writeShort(this.padding1); + outputStream.writeByte(this.padding2); + for(var idx = 0; idx < this.supplies.length; idx++) + { + supplies[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.7.5. SEES PDU, supplemental emissions entity state information. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SeesPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 30; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Originating entity ID */ + this.orginatingEntityID = new dis.EntityID(); + + /** IR Signature representation index */ + this.infraredSignatureRepresentationIndex = 0; + + /** acoustic Signature representation index */ + this.acousticSignatureRepresentationIndex = 0; + + /** radar cross section representation index */ + this.radarCrossSectionSignatureRepresentationIndex = 0; + + /** how many propulsion systems */ + this.numberOfPropulsionSystems = 0; + + /** how many vectoring nozzle systems */ + this.numberOfVectoringNozzleSystems = 0; + + /** variable length list of propulsion system data */ + this.propulsionSystemData = new Array(); + + /** variable length list of vectoring system data */ + this.vectoringSystemData = new Array(); + + dis.SeesPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.orginatingEntityID.initFromBinaryDIS(inputStream); + this.infraredSignatureRepresentationIndex = inputStream.readUShort(); + this.acousticSignatureRepresentationIndex = inputStream.readUShort(); + this.radarCrossSectionSignatureRepresentationIndex = inputStream.readUShort(); + this.numberOfPropulsionSystems = inputStream.readUShort(); + this.numberOfVectoringNozzleSystems = inputStream.readUShort(); + for(var idx = 0; idx < this.numberOfPropulsionSystems; idx++) + { + var anX = new dis.PropulsionSystemData(); + anX.initFromBinaryDIS(inputStream); + this.propulsionSystemData.push(anX); + } + + for(var idx = 0; idx < this.numberOfVectoringNozzleSystems; idx++) + { + var anX = new dis.VectoringNozzleSystemData(); + anX.initFromBinaryDIS(inputStream); + this.vectoringSystemData.push(anX); + } + + }; + + dis.SeesPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.orginatingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.infraredSignatureRepresentationIndex); + outputStream.writeUShort(this.acousticSignatureRepresentationIndex); + outputStream.writeUShort(this.radarCrossSectionSignatureRepresentationIndex); + outputStream.writeUShort(this.numberOfPropulsionSystems); + outputStream.writeUShort(this.numberOfVectoringNozzleSystems); + for(var idx = 0; idx < this.propulsionSystemData.length; idx++) + { + propulsionSystemData[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.vectoringSystemData.length; idx++) + { + vectoringSystemData[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.5.1. Information about a request for supplies. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ServiceRequestPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 5; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 3; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is requesting service */ + this.requestingEntityID = new dis.EntityID(); + + /** Entity that is providing the service */ + this.servicingEntityID = new dis.EntityID(); + + /** type of service requested */ + this.serviceTypeRequested = 0; + + /** How many requested */ + this.numberOfSupplyTypes = 0; + + /** padding */ + this.serviceRequestPadding = 0; + + this.supplies = new Array(); + + dis.ServiceRequestPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.requestingEntityID.initFromBinaryDIS(inputStream); + this.servicingEntityID.initFromBinaryDIS(inputStream); + this.serviceTypeRequested = inputStream.readUByte(); + this.numberOfSupplyTypes = inputStream.readUByte(); + this.serviceRequestPadding = inputStream.readShort(); + for(var idx = 0; idx < this.numberOfSupplyTypes; idx++) + { + var anX = new dis.SupplyQuantity(); + anX.initFromBinaryDIS(inputStream); + this.supplies.push(anX); + } + + }; + + dis.ServiceRequestPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.requestingEntityID.encodeToBinaryDIS(outputStream); + this.servicingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.serviceTypeRequested); + outputStream.writeUByte(this.numberOfSupplyTypes); + outputStream.writeShort(this.serviceRequestPadding); + for(var idx = 0; idx < this.supplies.length; idx++) + { + supplies[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.6.9. Change state information with the data contained in this. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SetDataPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 19; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** ID of request */ + this.requestID = 0; + + /** padding */ + this.padding1 = 0; + + /** Number of fixed datum records */ + this.numberOfFixedDatumRecords = 0; + + /** Number of variable datum records */ + this.numberOfVariableDatumRecords = 0; + + /** variable length list of fixed datums */ + this.fixedDatums = new Array(); + + /** variable length list of variable length datums */ + this.variableDatums = new Array(); + + dis.SetDataPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.padding1 = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatums.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatums.push(anX); + } + + }; + + dis.SetDataPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.padding1); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatums.length; idx++) + { + fixedDatums[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatums.length; idx++) + { + variableDatums[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.9: initializing or chaning internal state information, reliable. Needs manual intervention to fix padding on variable datums. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SetDataReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 59; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Request ID */ + this.requestID = 0; + + /** Fixed datum record count */ + this.numberOfFixedDatumRecords = 0; + + /** variable datum record count */ + this.numberOfVariableDatumRecords = 0; + + /** Fixed datum records */ + this.fixedDatumRecords = new Array(); + + /** Variable datum records */ + this.variableDatumRecords = new Array(); + + dis.SetDataReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + this.numberOfFixedDatumRecords = inputStream.readInt(); + this.numberOfVariableDatumRecords = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfFixedDatumRecords; idx++) + { + var anX = new dis.FixedDatum(); + anX.initFromBinaryDIS(inputStream); + this.fixedDatumRecords.push(anX); + } + + for(var idx = 0; idx < this.numberOfVariableDatumRecords; idx++) + { + var anX = new dis.VariableDatum(); + anX.initFromBinaryDIS(inputStream); + this.variableDatumRecords.push(anX); + } + + }; + + dis.SetDataReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + outputStream.writeUInt(this.numberOfFixedDatumRecords); + outputStream.writeUInt(this.numberOfVariableDatumRecords); + for(var idx = 0; idx < this.fixedDatumRecords.length; idx++) + { + fixedDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.variableDatumRecords.length; idx++) + { + variableDatumRecords[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.12.14: Initializing or changing internal parameter info. Needs manual intervention to fix padding in recrod set PDUs. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SetRecordReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 64; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** request ID */ + this.requestID = 0; + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding. The spec is unclear and contradictory here. */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Number of record sets in list */ + this.numberOfRecordSets = 0; + + /** record sets */ + this.recordSets = new Array(); + + dis.SetRecordReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.numberOfRecordSets = inputStream.readInt(); + for(var idx = 0; idx < this.numberOfRecordSets; idx++) + { + var anX = new dis.RecordSet(); + anX.initFromBinaryDIS(inputStream); + this.recordSets.push(anX); + } + + }; + + dis.SetRecordReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.numberOfRecordSets); + for(var idx = 0; idx < this.recordSets.length; idx++) + { + recordSets[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Shaft RPMs, used in underwater acoustic clacluations. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.ShaftRPMs = function() +{ + /** Current shaft RPMs */ + this.currentShaftRPMs = 0; + + /** ordered shaft rpms */ + this.orderedShaftRPMs = 0; + + /** rate of change of shaft RPMs */ + this.shaftRPMRateOfChange = 0; + + dis.ShaftRPMs.prototype.initFromBinaryDIS = function(inputStream) + { + + this.currentShaftRPMs = inputStream.readShort(); + this.orderedShaftRPMs = inputStream.readShort(); + this.shaftRPMRateOfChange = inputStream.readFloat32(); + }; + + dis.ShaftRPMs.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeShort(this.currentShaftRPMs); + outputStream.writeShort(this.orderedShaftRPMs); + outputStream.writeFloat32(this.shaftRPMRateOfChange); + }; +}; // end of class +/** + * Section 5.3.8.2. Detailed information about a radio transmitter. This PDU requires manually written code to complete. The encodingScheme field can be used in multiple ways, which requires hand-written code to finish. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SignalPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 26; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the communication, ie contains the radio */ + this.entityId = new dis.EntityID(); + + /** particular radio within an entity */ + this.radioId = 0; + + /** encoding scheme used, and enumeration */ + this.encodingScheme = 0; + + /** tdl type */ + this.tdlType = 0; + + /** sample rate */ + this.sampleRate = 0; + + /** length of data, in bits */ + this.dataLength = 0; + + /** number of samples. If the PDU contains encoded audio, this should be zero. */ + this.samples = 0; + + /** list of eight bit values. Must be padded to fall on a 32 bit boundary. */ + this.data = new Array(); + + dis.SignalPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityId.initFromBinaryDIS(inputStream); + this.radioId = inputStream.readUShort(); + this.encodingScheme = inputStream.readUShort(); + this.tdlType = inputStream.readUShort(); + this.sampleRate = inputStream.readInt(); + this.dataLength = inputStream.readUShort(); + this.samples = inputStream.readUShort(); + for(var idx = 0; idx < this.dataLength; idx++) + { + var anX = new dis.OneByteChunk(); + anX.initFromBinaryDIS(inputStream); + this.data.push(anX); + } + + }; + + dis.SignalPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityId.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.radioId); + outputStream.writeUShort(this.encodingScheme); + outputStream.writeUShort(this.tdlType); + outputStream.writeUInt(this.sampleRate); + outputStream.writeUShort(this.dataLength); + outputStream.writeUShort(this.samples); + for(var idx = 0; idx < this.data.length; idx++) + { + data[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.14.1. A Simulation Address record shall consist of the Site Identification number and the Application Identification number. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SimulationAddress = function() +{ + /** The site ID */ + this.site = 0; + + /** The application ID */ + this.application = 0; + + dis.SimulationAddress.prototype.initFromBinaryDIS = function(inputStream) + { + + this.site = inputStream.readUShort(); + this.application = inputStream.readUShort(); + }; + + dis.SimulationAddress.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.site); + outputStream.writeUShort(this.application); + }; +}; // end of class +/** + * Section 5.3.6. Abstract superclass for PDUs relating to the simulation itself. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SimulationManagementFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + dis.SimulationManagementFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + }; + + dis.SimulationManagementFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * Section 5.3.12: Abstract superclass for reliable simulation management PDUs + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SimulationManagementWithReliabilityFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + dis.SimulationManagementWithReliabilityFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + }; + + dis.SimulationManagementWithReliabilityFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + }; +}; // end of class +/** + * 48 bit piece of data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SixByteChunk = function() +{ + /** six bytes of arbitrary data */ + this.otherParameters = new Array(0, 0, 0, 0, 0, 0); + + dis.SixByteChunk.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 6; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + }; + + dis.SixByteChunk.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 6; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.2.4.3. Used when the antenna pattern type in the transmitter pdu is of value 2. Specified the direction and radiation pattern from a radio transmitter's antenna. NOTE: this class must be hand-coded to clean up some implementation details. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SphericalHarmonicAntennaPattern = function() +{ + this.harmonicOrder = 0; + + dis.SphericalHarmonicAntennaPattern.prototype.initFromBinaryDIS = function(inputStream) + { + + this.harmonicOrder = inputStream.readByte(); + }; + + dis.SphericalHarmonicAntennaPattern.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeByte(this.harmonicOrder); + }; +}; // end of class +/** + * Section 5.2.6.3. Start or resume an exercise. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.StartResumePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 13; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** UTC time at which the simulation shall start or resume */ + this.realWorldTime = new dis.ClockTime(); + + /** Simulation clock time at which the simulation shall start or resume */ + this.simulationTime = new dis.ClockTime(); + + /** Identifier for the request */ + this.requestID = 0; + + dis.StartResumePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.realWorldTime.initFromBinaryDIS(inputStream); + this.simulationTime.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + }; + + dis.StartResumePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.realWorldTime.encodeToBinaryDIS(outputStream); + this.simulationTime.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.12.3: Start resume simulation, relaible. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.StartResumeReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 53; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** time in real world for this operation to happen */ + this.realWorldTime = new dis.ClockTime(); + + /** time in simulation for the simulation to resume */ + this.simulationTime = new dis.ClockTime(); + + /** level of reliability service used for this transaction */ + this.requiredReliabilityService = 0; + + /** padding */ + this.pad1 = 0; + + /** padding */ + this.pad2 = 0; + + /** Request ID */ + this.requestID = 0; + + dis.StartResumeReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.realWorldTime.initFromBinaryDIS(inputStream); + this.simulationTime.initFromBinaryDIS(inputStream); + this.requiredReliabilityService = inputStream.readUByte(); + this.pad1 = inputStream.readUShort(); + this.pad2 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + }; + + dis.StartResumeReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.realWorldTime.encodeToBinaryDIS(outputStream); + this.simulationTime.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUShort(this.pad1); + outputStream.writeUByte(this.pad2); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.2.3.4. Stop or freeze an exercise. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.StopFreezePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 14; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 5; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Entity that is sending message */ + this.originatingEntityID = new dis.EntityID(); + + /** Entity that is intended to receive message */ + this.receivingEntityID = new dis.EntityID(); + + /** UTC time at which the simulation shall stop or freeze */ + this.realWorldTime = new dis.ClockTime(); + + /** Reason the simulation was stopped or frozen */ + this.reason = 0; + + /** Internal behavior of the simulation and its appearance while frozento the other participants */ + this.frozenBehavior = 0; + + /** padding */ + this.padding1 = 0; + + /** Request ID that is unique */ + this.requestID = 0; + + dis.StopFreezePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.realWorldTime.initFromBinaryDIS(inputStream); + this.reason = inputStream.readUByte(); + this.frozenBehavior = inputStream.readUByte(); + this.padding1 = inputStream.readShort(); + this.requestID = inputStream.readInt(); + }; + + dis.StopFreezePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.realWorldTime.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.reason); + outputStream.writeUByte(this.frozenBehavior); + outputStream.writeShort(this.padding1); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.3.12.4: Stop freeze simulation, relaible. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.StopFreezeReliablePdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 54; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 10; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** Object originatig the request */ + this.originatingEntityID = new dis.EntityID(); + + /** Object with which this point object is associated */ + this.receivingEntityID = new dis.EntityID(); + + /** time in real world for this operation to happen */ + this.realWorldTime = new dis.ClockTime(); + + /** Reason for stopping/freezing simulation */ + this.reason = 0; + + /** internal behvior of the simulation while frozen */ + this.frozenBehavior = 0; + + /** reliablity level */ + this.requiredReliablityService = 0; + + /** padding */ + this.pad1 = 0; + + /** Request ID */ + this.requestID = 0; + + dis.StopFreezeReliablePdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.originatingEntityID.initFromBinaryDIS(inputStream); + this.receivingEntityID.initFromBinaryDIS(inputStream); + this.realWorldTime.initFromBinaryDIS(inputStream); + this.reason = inputStream.readUByte(); + this.frozenBehavior = inputStream.readUByte(); + this.requiredReliablityService = inputStream.readUByte(); + this.pad1 = inputStream.readUByte(); + this.requestID = inputStream.readInt(); + }; + + dis.StopFreezeReliablePdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.originatingEntityID.encodeToBinaryDIS(outputStream); + this.receivingEntityID.encodeToBinaryDIS(outputStream); + this.realWorldTime.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.reason); + outputStream.writeUByte(this.frozenBehavior); + outputStream.writeUByte(this.requiredReliablityService); + outputStream.writeUByte(this.pad1); + outputStream.writeUInt(this.requestID); + }; +}; // end of class +/** + * Section 5.2.30. A supply, and the amount of that supply. Similar to an entity kind but with the addition of a quantity. + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SupplyQuantity = function() +{ + /** Type of supply */ + this.supplyType = new dis.EntityType(); + + /** quantity to be supplied */ + this.quantity = 0; + + dis.SupplyQuantity.prototype.initFromBinaryDIS = function(inputStream) + { + + this.supplyType.initFromBinaryDIS(inputStream); + this.quantity = inputStream.readUByte(); + }; + + dis.SupplyQuantity.prototype.encodeToBinaryDIS = function(outputStream) + { + + this.supplyType.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.quantity); + }; +}; // end of class +/** + * Section 5.3.11: Abstract superclass for synthetic environment PDUs + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SyntheticEnvironmentFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 9; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + dis.SyntheticEnvironmentFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + }; + + dis.SyntheticEnvironmentFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + }; +}; // end of class +/** + * 5.2.58. Used in IFF ATC PDU + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.SystemID = function() +{ + /** System Type */ + this.systemType = 0; + + /** System name, an enumeration */ + this.systemName = 0; + + /** System mode */ + this.systemMode = 0; + + /** Change Options */ + this.changeOptions = 0; + + dis.SystemID.prototype.initFromBinaryDIS = function(inputStream) + { + + this.systemType = inputStream.readUShort(); + this.systemName = inputStream.readUShort(); + this.systemMode = inputStream.readUByte(); + this.changeOptions = inputStream.readUByte(); + }; + + dis.SystemID.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUShort(this.systemType); + outputStream.writeUShort(this.systemName); + outputStream.writeUByte(this.systemMode); + outputStream.writeUByte(this.changeOptions); + }; +}; // end of class +/** + * One track/jam target + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.TrackJamTarget = function() +{ + /** track/jam target */ + this.trackJam = new dis.EntityID(); + + /** Emitter ID */ + this.emitterID = 0; + + /** beam ID */ + this.beamID = 0; + + dis.TrackJamTarget.prototype.initFromBinaryDIS = function(inputStream) + { + + this.trackJam.initFromBinaryDIS(inputStream); + this.emitterID = inputStream.readUByte(); + this.beamID = inputStream.readUByte(); + }; + + dis.TrackJamTarget.prototype.encodeToBinaryDIS = function(outputStream) + { + + this.trackJam.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.emitterID); + outputStream.writeUByte(this.beamID); + }; +}; // end of class +/** + * Section 5.3.9.3 Information initiating the dyanic allocation and control of simulation entities between two simulation applications. Requires manual cleanup. The padding between record sets is variable. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.TransferControlRequestPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 35; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 7; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of entity originating request */ + this.orginatingEntityID = new dis.EntityID(); + + /** ID of entity receiving request */ + this.recevingEntityID = new dis.EntityID(); + + /** ID ofrequest */ + this.requestID = 0; + + /** required level of reliabliity service. */ + this.requiredReliabilityService = 0; + + /** type of transfer desired */ + this.tranferType = 0; + + /** The entity for which control is being requested to transfer */ + this.transferEntityID = new dis.EntityID(); + + /** number of record sets to transfer */ + this.numberOfRecordSets = 0; + + /** ^^^This is wrong--the RecordSet class needs more work */ + this.recordSets = new Array(); + + dis.TransferControlRequestPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.orginatingEntityID.initFromBinaryDIS(inputStream); + this.recevingEntityID.initFromBinaryDIS(inputStream); + this.requestID = inputStream.readInt(); + this.requiredReliabilityService = inputStream.readUByte(); + this.tranferType = inputStream.readUByte(); + this.transferEntityID.initFromBinaryDIS(inputStream); + this.numberOfRecordSets = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfRecordSets; idx++) + { + var anX = new dis.RecordSet(); + anX.initFromBinaryDIS(inputStream); + this.recordSets.push(anX); + } + + }; + + dis.TransferControlRequestPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.orginatingEntityID.encodeToBinaryDIS(outputStream); + this.recevingEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUInt(this.requestID); + outputStream.writeUByte(this.requiredReliabilityService); + outputStream.writeUByte(this.tranferType); + this.transferEntityID.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.numberOfRecordSets); + for(var idx = 0; idx < this.recordSets.length; idx++) + { + recordSets[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.3.8.1. Detailed information about a radio transmitter. This PDU requires manually written code to complete, since the modulation parameters are of variable length. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.TransmitterPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 25; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 4; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the communication, ie contains the radio */ + this.entityId = new dis.EntityID(); + + /** particular radio within an entity */ + this.radioId = 0; + + /** linear accelleration of entity */ + this.radioEntityType = new dis.RadioEntityType(); + + /** transmit state */ + this.transmitState = 0; + + /** input source */ + this.inputSource = 0; + + /** padding */ + this.padding1 = 0; + + /** Location of antenna */ + this.antennaLocation = new dis.Vector3Double(); + + /** relative location of antenna, in entity coordinates */ + this.relativeAntennaLocation = new dis.Vector3Float(); + + /** antenna pattern type */ + this.antennaPatternType = 0; + + /** atenna pattern length */ + this.antennaPatternCount = 0; + + /** frequency */ + this.frequency = 0; + + /** transmit frequency Bandwidth */ + this.transmitFrequencyBandwidth = 0; + + /** transmission power */ + this.power = 0; + + /** modulation */ + this.modulationType = new dis.ModulationType(); + + /** crypto system enumeration */ + this.cryptoSystem = 0; + + /** crypto system key identifer */ + this.cryptoKeyId = 0; + + /** how many modulation parameters we have */ + this.modulationParameterCount = 0; + + /** padding2 */ + this.padding2 = 0; + + /** padding3 */ + this.padding3 = 0; + + /** variable length list of modulation parameters */ + this.modulationParametersList = new Array(); + + /** variable length list of antenna pattern records */ + this.antennaPatternList = new Array(); + + dis.TransmitterPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.entityId.initFromBinaryDIS(inputStream); + this.radioId = inputStream.readUShort(); + this.radioEntityType.initFromBinaryDIS(inputStream); + this.transmitState = inputStream.readUByte(); + this.inputSource = inputStream.readUByte(); + this.padding1 = inputStream.readUShort(); + this.antennaLocation.initFromBinaryDIS(inputStream); + this.relativeAntennaLocation.initFromBinaryDIS(inputStream); + this.antennaPatternType = inputStream.readUShort(); + this.antennaPatternCount = inputStream.readUShort(); + this.frequency = inputStream.readLong(); + this.transmitFrequencyBandwidth = inputStream.readFloat32(); + this.power = inputStream.readFloat32(); + this.modulationType.initFromBinaryDIS(inputStream); + this.cryptoSystem = inputStream.readUShort(); + this.cryptoKeyId = inputStream.readUShort(); + this.modulationParameterCount = inputStream.readUByte(); + this.padding2 = inputStream.readUShort(); + this.padding3 = inputStream.readUByte(); + for(var idx = 0; idx < this.modulationParameterCount; idx++) + { + var anX = new dis.ModulationType(); + anX.initFromBinaryDIS(inputStream); + this.modulationParametersList.push(anX); + } + + for(var idx = 0; idx < this.antennaPatternCount; idx++) + { + var anX = new dis.BeamAntennaPattern(); + anX.initFromBinaryDIS(inputStream); + this.antennaPatternList.push(anX); + } + + }; + + dis.TransmitterPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.entityId.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.radioId); + this.radioEntityType.encodeToBinaryDIS(outputStream); + outputStream.writeUByte(this.transmitState); + outputStream.writeUByte(this.inputSource); + outputStream.writeUShort(this.padding1); + this.antennaLocation.encodeToBinaryDIS(outputStream); + this.relativeAntennaLocation.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.antennaPatternType); + outputStream.writeUShort(this.antennaPatternCount); + outputStream.writeLong(this.frequency); + outputStream.writeFloat32(this.transmitFrequencyBandwidth); + outputStream.writeFloat32(this.power); + this.modulationType.encodeToBinaryDIS(outputStream); + outputStream.writeUShort(this.cryptoSystem); + outputStream.writeUShort(this.cryptoKeyId); + outputStream.writeUByte(this.modulationParameterCount); + outputStream.writeUShort(this.padding2); + outputStream.writeUByte(this.padding3); + for(var idx = 0; idx < this.modulationParametersList.length; idx++) + { + modulationParametersList[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.antennaPatternList.length; idx++) + { + antennaPatternList[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * 16 bit piece of data + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.TwoByteChunk = function() +{ + /** two bytes of arbitrary data */ + this.otherParameters = new Array(0, 0); + + dis.TwoByteChunk.prototype.initFromBinaryDIS = function(inputStream) + { + + for(var idx = 0; idx < 2; idx++) + { + this.otherParameters[ idx ] = inputStream.readByte(); + } + }; + + dis.TwoByteChunk.prototype.encodeToBinaryDIS = function(outputStream) + { + + for(var idx = 0; idx < 2; idx++) + { + outputStream.writeByte(this.otherParameters[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.3.7.3. Information about underwater acoustic emmissions. This requires manual cleanup. The beam data records should ALL be a the finish, rather than attached to each emitter system. UNFINISHED + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.UaPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 29; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 6; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that is the source of the emission */ + this.emittingEntityID = new dis.EntityID(); + + /** ID of event */ + this.eventID = new dis.EventID(); + + /** This field shall be used to indicate whether the data in the UA PDU represent a state update or data that have changed since issuance of the last UA PDU */ + this.stateChangeIndicator = 0; + + /** padding */ + this.pad = 0; + + /** This field indicates which database record (or file) shall be used in the definition of passive signature (unintentional) emissions of the entity. The indicated database record (or file) shall define all noise generated as a function of propulsion plant configurations and associated auxiliaries. */ + this.passiveParameterIndex = 0; + + /** This field shall specify the entity propulsion plant configuration. This field is used to determine the passive signature characteristics of an entity. */ + this.propulsionPlantConfiguration = 0; + + /** This field shall represent the number of shafts on a platform */ + this.numberOfShafts = 0; + + /** This field shall indicate the number of APAs described in the current UA PDU */ + this.numberOfAPAs = 0; + + /** This field shall specify the number of UA emitter systems being described in the current UA PDU */ + this.numberOfUAEmitterSystems = 0; + + /** shaft RPM values */ + this.shaftRPMs = new Array(); + + /** apaData */ + this.apaData = new Array(); + + this.emitterSystems = new Array(); + + dis.UaPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.emittingEntityID.initFromBinaryDIS(inputStream); + this.eventID.initFromBinaryDIS(inputStream); + this.stateChangeIndicator = inputStream.readByte(); + this.pad = inputStream.readByte(); + this.passiveParameterIndex = inputStream.readUShort(); + this.propulsionPlantConfiguration = inputStream.readUByte(); + this.numberOfShafts = inputStream.readUByte(); + this.numberOfAPAs = inputStream.readUByte(); + this.numberOfUAEmitterSystems = inputStream.readUByte(); + for(var idx = 0; idx < this.numberOfShafts; idx++) + { + var anX = new dis.ShaftRPMs(); + anX.initFromBinaryDIS(inputStream); + this.shaftRPMs.push(anX); + } + + for(var idx = 0; idx < this.numberOfAPAs; idx++) + { + var anX = new dis.ApaData(); + anX.initFromBinaryDIS(inputStream); + this.apaData.push(anX); + } + + for(var idx = 0; idx < this.numberOfUAEmitterSystems; idx++) + { + var anX = new dis.AcousticEmitterSystemData(); + anX.initFromBinaryDIS(inputStream); + this.emitterSystems.push(anX); + } + + }; + + dis.UaPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.emittingEntityID.encodeToBinaryDIS(outputStream); + this.eventID.encodeToBinaryDIS(outputStream); + outputStream.writeByte(this.stateChangeIndicator); + outputStream.writeByte(this.pad); + outputStream.writeUShort(this.passiveParameterIndex); + outputStream.writeUByte(this.propulsionPlantConfiguration); + outputStream.writeUByte(this.numberOfShafts); + outputStream.writeUByte(this.numberOfAPAs); + outputStream.writeUByte(this.numberOfUAEmitterSystems); + for(var idx = 0; idx < this.shaftRPMs.length; idx++) + { + shaftRPMs[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.apaData.length; idx++) + { + apaData[idx].encodeToBinaryDIS(outputStream); + } + + for(var idx = 0; idx < this.emitterSystems.length; idx++) + { + emitterSystems[idx].encodeToBinaryDIS(outputStream); + } + + }; +}; // end of class +/** + * Section 5.2.32. Variable Datum Record + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.VariableDatum = function() +{ + /** ID of the variable datum */ + this.variableDatumID = 0; + + /** length of the variable datums, in bits. Note that this is not programmatically tied to the size of the variableData. The variable data field may be 64 bits long but only 16 bits of it could actually be used. */ + this.variableDatumLength = 0; + + /** data can be any length, but must increase in 8 byte quanta. This requires some postprocessing patches. Note that setting the data allocates a new internal array to account for the possibly increased size. The default initial size is 64 bits. */ + this.variableData = new Array(0, 0, 0, 0, 0, 0, 0, 0); + + dis.VariableDatum.prototype.initFromBinaryDIS = function(inputStream) + { + + this.variableDatumID = inputStream.readInt(); + this.variableDatumLength = inputStream.readInt(); + for(var idx = 0; idx < 8; idx++) + { + this.variableData[ idx ] = inputStream.readByte(); + } + }; + + dis.VariableDatum.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUInt(this.variableDatumID); + outputStream.writeUInt(this.variableDatumLength); + for(var idx = 0; idx < 8; idx++) + { + outputStream.writeByte(this.variableData[ idx ] ); + } + }; +}; // end of class +/** + * Section 5.3.34. Three double precision floating point values, x, y, and z + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Vector3Double = function() +{ + /** X value */ + this.x = 0; + + /** Y value */ + this.y = 0; + + /** Z value */ + this.z = 0; + + dis.Vector3Double.prototype.initFromBinaryDIS = function(inputStream) + { + + this.x = inputStream.readFloat64(); + this.y = inputStream.readFloat64(); + this.z = inputStream.readFloat64(); + }; + + dis.Vector3Double.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat64(this.x); + outputStream.writeFloat64(this.y); + outputStream.writeFloat64(this.z); + }; +}; // end of class +/** + * Section 5.2.33. Three floating point values, x, y, and z + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.Vector3Float = function() +{ + /** X value */ + this.x = 0; + + /** y Value */ + this.y = 0; + + /** Z value */ + this.z = 0; + + dis.Vector3Float.prototype.initFromBinaryDIS = function(inputStream) + { + + this.x = inputStream.readFloat32(); + this.y = inputStream.readFloat32(); + this.z = inputStream.readFloat32(); + }; + + dis.Vector3Float.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.x); + outputStream.writeFloat32(this.y); + outputStream.writeFloat32(this.z); + }; +}; // end of class +/** + * Data about a vectoring nozzle system + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.VectoringNozzleSystemData = function() +{ + /** horizontal deflection angle */ + this.horizontalDeflectionAngle = 0; + + /** vertical deflection angle */ + this.verticalDeflectionAngle = 0; + + dis.VectoringNozzleSystemData.prototype.initFromBinaryDIS = function(inputStream) + { + + this.horizontalDeflectionAngle = inputStream.readFloat32(); + this.verticalDeflectionAngle = inputStream.readFloat32(); + }; + + dis.VectoringNozzleSystemData.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeFloat32(this.horizontalDeflectionAngle); + outputStream.writeFloat32(this.verticalDeflectionAngle); + }; +}; // end of class +/** + * Section 5.3.4. abstract superclass for fire and detonation pdus that have shared information. COMPLETE + * + * Copyright (c) 2008-2014, MOVES Institute, Naval Postgraduate School. All rights reserved. + * This work is licensed under the BSD open source license, available at https://www.movesinstitute.org/licenses/bsd.html + * + * @author DMcG + */ +if (typeof dis === "undefined") + dis = {}; + +dis.WarfareFamilyPdu = function() +{ + /** The version of the protocol. 5=DIS-1995, 6=DIS-1998. */ + this.protocolVersion = 6; + + /** Exercise ID */ + this.exerciseID = 0; + + /** Type of pdu, unique for each PDU class */ + this.pduType = 0; + + /** value that refers to the protocol family, eg SimulationManagement, et */ + this.protocolFamily = 2; + + /** Timestamp value */ + this.timestamp = 0; + + /** Length, in bytes, of the PDU. Changed name from length to avoid use of Hibernate QL reserved word */ + this.pduLength = 0; + + /** zero-filled array of padding */ + this.padding = 0; + + /** ID of the entity that shot */ + this.firingEntityID = new dis.EntityID(); + + /** ID of the entity that is being shot at */ + this.targetEntityID = new dis.EntityID(); + + dis.WarfareFamilyPdu.prototype.initFromBinaryDIS = function(inputStream) + { + + this.protocolVersion = inputStream.readUByte(); + this.exerciseID = inputStream.readUByte(); + this.pduType = inputStream.readUByte(); + this.protocolFamily = inputStream.readUByte(); + this.timestamp = inputStream.readInt(); + this.pduLength = inputStream.readUShort(); + this.padding = inputStream.readShort(); + this.firingEntityID.initFromBinaryDIS(inputStream); + this.targetEntityID.initFromBinaryDIS(inputStream); + }; + + dis.WarfareFamilyPdu.prototype.encodeToBinaryDIS = function(outputStream) + { + + outputStream.writeUByte(this.protocolVersion); + outputStream.writeUByte(this.exerciseID); + outputStream.writeUByte(this.pduType); + outputStream.writeUByte(this.protocolFamily); + outputStream.writeUInt(this.timestamp); + outputStream.writeUShort(this.pduLength); + outputStream.writeShort(this.padding); + this.firingEntityID.encodeToBinaryDIS(outputStream); + this.targetEntityID.encodeToBinaryDIS(outputStream); + }; +}; // end of class + +// Exports for the dis module, used in require.js +// You should delete this if not using with require.js + +exports.StartResumeReliablePdu = dis.StartResumeReliablePdu; +exports.IffAtcNavAidsLayer2Pdu = dis.IffAtcNavAidsLayer2Pdu; +exports.ArealObjectStatePdu = dis.ArealObjectStatePdu; +exports.SystemID = dis.SystemID; +exports.DataQueryReliablePdu = dis.DataQueryReliablePdu; +exports.PduStream = dis.PduStream; +exports.RadioEntityType = dis.RadioEntityType; +exports.AggregateStatePdu = dis.AggregateStatePdu; +exports.ElectronicEmissionBeamData = dis.ElectronicEmissionBeamData; +exports.LogisticsFamilyPdu = dis.LogisticsFamilyPdu; +exports.EntityStateUpdatePdu = dis.EntityStateUpdatePdu; +exports.MinefieldStatePdu = dis.MinefieldStatePdu; +exports.LayerHeader = dis.LayerHeader; +exports.AcousticEmitterSystem = dis.AcousticEmitterSystem; +exports.DataReliablePdu = dis.DataReliablePdu; +exports.ServiceRequestPdu = dis.ServiceRequestPdu; +exports.RepairCompletePdu = dis.RepairCompletePdu; +exports.FourByteChunk = dis.FourByteChunk; +exports.CommentPdu = dis.CommentPdu; +exports.Orientation = dis.Orientation; +exports.CommentReliablePdu = dis.CommentReliablePdu; +exports.OneByteChunk = dis.OneByteChunk; +exports.DeadReckoningParameter = dis.DeadReckoningParameter; +exports.EventID = dis.EventID; +exports.VectoringNozzleSystemData = dis.VectoringNozzleSystemData; +exports.DetonationPdu = dis.DetonationPdu; +exports.BeamAntennaPattern = dis.BeamAntennaPattern; +exports.SetDataPdu = dis.SetDataPdu; +exports.SyntheticEnvironmentFamilyPdu = dis.SyntheticEnvironmentFamilyPdu; +exports.RecordQueryReliablePdu = dis.RecordQueryReliablePdu; +exports.AcousticEmitterSystemData = dis.AcousticEmitterSystemData; +exports.CollisionPdu = dis.CollisionPdu; +exports.RepairResponsePdu = dis.RepairResponsePdu; +exports.ObjectType = dis.ObjectType; +exports.ActionResponsePdu = dis.ActionResponsePdu; +exports.FundamentalParameterDataIff = dis.FundamentalParameterDataIff; +exports.SimulationManagementFamilyPdu = dis.SimulationManagementFamilyPdu; +exports.EightByteChunk = dis.EightByteChunk; +exports.AntennaLocation = dis.AntennaLocation; +exports.FirePdu = dis.FirePdu; +exports.DataQueryPdu = dis.DataQueryPdu; +exports.FixedDatum = dis.FixedDatum; +exports.BurstDescriptor = dis.BurstDescriptor; +exports.GridAxisRecord = dis.GridAxisRecord; +exports.ReceiverPdu = dis.ReceiverPdu; +exports.LinearObjectStatePdu = dis.LinearObjectStatePdu; +exports.AggregateID = dis.AggregateID; +exports.TwoByteChunk = dis.TwoByteChunk; +exports.ClockTime = dis.ClockTime; +exports.UaPdu = dis.UaPdu; +exports.IntercomControlPdu = dis.IntercomControlPdu; +exports.SignalPdu = dis.SignalPdu; +exports.Relationship = dis.Relationship; +exports.RemoveEntityReliablePdu = dis.RemoveEntityReliablePdu; +exports.CreateEntityPdu = dis.CreateEntityPdu; +exports.RadioCommunicationsFamilyPdu = dis.RadioCommunicationsFamilyPdu; +exports.AcousticBeamData = dis.AcousticBeamData; +exports.Vector3Float = dis.Vector3Float; +exports.SeesPdu = dis.SeesPdu; +exports.IntercomSignalPdu = dis.IntercomSignalPdu; +exports.ModulationType = dis.ModulationType; +exports.GridAxisRecordRepresentation2 = dis.GridAxisRecordRepresentation2; +exports.LinearSegmentParameter = dis.LinearSegmentParameter; +exports.SimulationAddress = dis.SimulationAddress; +exports.GridAxisRecordRepresentation1 = dis.GridAxisRecordRepresentation1; +exports.IffFundamentalData = dis.IffFundamentalData; +exports.CreateEntityReliablePdu = dis.CreateEntityReliablePdu; +exports.AggregateType = dis.AggregateType; +exports.GridAxisRecordRepresentation0 = dis.GridAxisRecordRepresentation0; +exports.BeamData = dis.BeamData; +exports.RemoveEntityPdu = dis.RemoveEntityPdu; +exports.ResupplyReceivedPdu = dis.ResupplyReceivedPdu; +exports.WarfareFamilyPdu = dis.WarfareFamilyPdu; +exports.StopFreezeReliablePdu = dis.StopFreezeReliablePdu; +exports.EventReportReliablePdu = dis.EventReportReliablePdu; +exports.NamedLocation = dis.NamedLocation; +exports.ElectronicEmissionSystemData = dis.ElectronicEmissionSystemData; +exports.MinefieldResponseNackPdu = dis.MinefieldResponseNackPdu; +exports.CollisionElasticPdu = dis.CollisionElasticPdu; +exports.RecordSet = dis.RecordSet; +exports.ActionRequestPdu = dis.ActionRequestPdu; +exports.SphericalHarmonicAntennaPattern = dis.SphericalHarmonicAntennaPattern; +exports.SupplyQuantity = dis.SupplyQuantity; +exports.AcknowledgePdu = dis.AcknowledgePdu; +exports.DistributedEmissionsFamilyPdu = dis.DistributedEmissionsFamilyPdu; +exports.ActionResponseReliablePdu = dis.ActionResponseReliablePdu; +exports.ShaftRPMs = dis.ShaftRPMs; +exports.IffAtcNavAidsLayer1Pdu = dis.IffAtcNavAidsLayer1Pdu; +exports.SimulationManagementWithReliabilityFamilyPdu = dis.SimulationManagementWithReliabilityFamilyPdu; +exports.ActionRequestReliablePdu = dis.ActionRequestReliablePdu; +exports.DesignatorPdu = dis.DesignatorPdu; +exports.IsPartOfPdu = dis.IsPartOfPdu; +exports.MinefieldQueryPdu = dis.MinefieldQueryPdu; +exports.IntercomCommunicationsParameters = dis.IntercomCommunicationsParameters; +exports.GriddedDataPdu = dis.GriddedDataPdu; +exports.AcousticBeamFundamentalParameter = dis.AcousticBeamFundamentalParameter; +exports.EntityType = dis.EntityType; +exports.FundamentalParameterData = dis.FundamentalParameterData; +exports.SetRecordReliablePdu = dis.SetRecordReliablePdu; +exports.ApaData = dis.ApaData; +exports.Environment = dis.Environment; +exports.AcousticEmitter = dis.AcousticEmitter; +exports.AngularVelocityVector = dis.AngularVelocityVector; +exports.AggregateMarking = dis.AggregateMarking; +exports.StopFreezePdu = dis.StopFreezePdu; +exports.EntityStatePdu = dis.EntityStatePdu; +exports.ResupplyCancelPdu = dis.ResupplyCancelPdu; +exports.EntityManagementFamilyPdu = dis.EntityManagementFamilyPdu; +exports.StartResumePdu = dis.StartResumePdu; +exports.TransmitterPdu = dis.TransmitterPdu; +exports.TrackJamTarget = dis.TrackJamTarget; +exports.ElectronicEmissionsPdu = dis.ElectronicEmissionsPdu; +exports.EntityID = dis.EntityID; +exports.SixByteChunk = dis.SixByteChunk; +exports.ResupplyOfferPdu = dis.ResupplyOfferPdu; +exports.MinefieldFamilyPdu = dis.MinefieldFamilyPdu; +exports.SetDataReliablePdu = dis.SetDataReliablePdu; +exports.Vector3Double = dis.Vector3Double; +exports.Pdu = dis.Pdu; +exports.VariableDatum = dis.VariableDatum; +exports.EventReportPdu = dis.EventReportPdu; +exports.PointObjectStatePdu = dis.PointObjectStatePdu; +exports.EnvironmentalProcessPdu = dis.EnvironmentalProcessPdu; +exports.ArticulationParameter = dis.ArticulationParameter; +exports.DataPdu = dis.DataPdu; +exports.IsGroupOfPdu = dis.IsGroupOfPdu; +exports.Marking = dis.Marking; +exports.Point = dis.Point; +exports.MinefieldDataPdu = dis.MinefieldDataPdu; +exports.PropulsionSystemData = dis.PropulsionSystemData; +exports.FastEntityStatePdu = dis.FastEntityStatePdu; +exports.TransferControlRequestPdu = dis.TransferControlRequestPdu; +exports.EmitterSystem = dis.EmitterSystem; +exports.EntityInformationFamilyPdu = dis.EntityInformationFamilyPdu; +exports.PduContainer = dis.PduContainer; +exports.AcknowledgeReliablePdu = dis.AcknowledgeReliablePdu; diff --git a/examples/WebsocketGateway/content/scripts/three.js b/examples/WebsocketGateway/content/scripts/three.js new file mode 100644 index 0000000000000000000000000000000000000000..cc43365172f36da039954f4ac902d6ad4d0c515a --- /dev/null +++ b/examples/WebsocketGateway/content/scripts/three.js @@ -0,0 +1,36949 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author Larry Battle / http://bateru.com/news + * @author bhouston / http://exocortex.com + */ + +var THREE = THREE || { REVISION: '61' }; + +self.console = self.console || { + + info: function () {}, + log: function () {}, + debug: function () {}, + warn: function () {}, + error: function () {} + +}; + +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 +// using 'self' instead of 'window' for compatibility with both NodeJS and IE10. +( function () { + + var lastTime = 0; + var vendors = [ 'ms', 'moz', 'webkit', 'o' ]; + + for ( var x = 0; x < vendors.length && !self.requestAnimationFrame; ++ x ) { + + self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ]; + self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ]; + + } + + if ( self.requestAnimationFrame === undefined && self['setTimeout'] !== undefined ) { + + self.requestAnimationFrame = function ( callback ) { + + var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); + var id = self.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall ); + lastTime = currTime + timeToCall; + return id; + + }; + + } + + if( self.cancelAnimationFrame === undefined && self['clearTimeout'] !== undefined ) { + + self.cancelAnimationFrame = function ( id ) { self.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.Color.prototype = { + + constructor: THREE.Color, + + r: 1, g: 1, b: 1, + + set: function ( value ) { + + if ( value instanceof THREE.Color ) { + + this.copy( value ); + + } else if ( typeof value === 'number' ) { + + this.setHex( value ); + + } else if ( typeof value === 'string' ) { + + this.setStyle( value ); + + } + + return this; + + }, + + 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; + + }, + + 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; + + }, + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array ) { + + this.r = array[ 0 ]; + this.g = array[ 1 ]; + this.b = array[ 2 ]; + + return this; + + }, + + toArray: function () { + + return [ this.r, this.g, this.b ]; + + }, + + 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.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + _euler: undefined, + + _updateEuler: function ( callback ) { + + if ( this._euler !== undefined ) { + + this._euler.setFromQuaternion( this, undefined, false ); + + } + + }, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this._updateEuler(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this._updateEuler(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this._updateEuler(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this._updateEuler(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this._updateEuler(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion._x; + this._y = quaternion._y; + this._z = quaternion._z; + this._w = quaternion._w; + + this._updateEuler(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'ERROR: Quaternion\'s .setFromEuler() now expects a Euler rotation rather than a Vector3 and order. Please update your code.' ); + } + + // 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( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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; + + } + + if ( update !== false ) this._updateEuler(); + + 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 ); + + this._updateEuler(); + + 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; + + } + + this._updateEuler(); + + return this; + + }, + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= -1; + this._y *= -1; + this._z *= -1; + + this._updateEuler(); + + 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; + + this._updateEuler(); + + 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 ); + + this._updateEuler(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + this._w = array[ 3 ]; + + this._updateEuler(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, 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.Vector2.prototype = { + + constructor: THREE.Vector2, + + 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 ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 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 ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + + return this; + + }, + + 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.Vector3.prototype = { + + constructor: THREE.Vector3, + + 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 ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + 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; + + }, + + 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 ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } 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 ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return 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; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( "REMOVED: Vector3\'s setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code."); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( "REMOVED: Vector3\'s setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code."); + + }, + + getPositionFromMatrix: function ( m ) { + + this.x = m.elements[12]; + this.y = m.elements[13]; + this.z = m.elements[14]; + + 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; + }, + + getColumnFromMatrix: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + this.z = array[ 2 ]; + + return this; + + }, + + toArray: function () { + + return [ this.x, this.y, this.z ]; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; + +THREE.extend( THREE.Vector3.prototype, { + + applyEuler: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'ERROR: Vector3\'s .applyEuler() now expects a Euler rotation rather than a Vector3 and order. Please update your code.' ); + + } + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( axis, angle ) { + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + 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 ); + + } + + }() + +} ); + +/** + * @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.Vector4.prototype = { + + constructor: THREE.Vector4, + + 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 ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + + 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 ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + this.w *= invScalar; + + } 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 ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + this.z = array[ 2 ]; + this.w = array[ 3 ]; + + return this; + + }, + + 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 mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + _quaternion: undefined, + + _updateQuaternion: function () { + + if ( this._quaternion !== undefined ) { + + this._quaternion.setFromEuler( this, false ); + + } + + }, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this._updateQuaternion(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this._updateQuaternion(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this._updateQuaternion(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this._updateQuaternion(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this._updateQuaternion(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this._updateQuaternion(); + + return this; + + }, + + setFromRotationMatrix: 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]; + + order = order || this._order; + + if ( 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; + + } + + } else { + + console.warn( 'WARNING: Euler.setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + this._updateQuaternion(); + + return this; + + }, + + setFromQuaternion: function ( q, order, update ) { + + // 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; + + order = order || this._order; + + if ( 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 ) ) ); + + } else { + + console.warn( 'WARNING: Euler.setFromQuaternion() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this._updateQuaternion(); + + return this; + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + + }(), + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this._updateQuaternion(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Line3 = function ( start, end ) { + + this.start = ( start !== undefined ) ? start : new THREE.Vector3(); + this.end = ( end !== undefined ) ? end : new THREE.Vector3(); + +}; + +THREE.Line3.prototype = { + + constructor: THREE.Line3, + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function() { + + var startP = new THREE.Vector3(); + var startEnd = new THREE.Vector3(); + + return function ( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = THREE.Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + }, + + clone: function () { + + return new THREE.Line3().copy( this ); + + } + +}; + +/** + * @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.Box2.prototype = { + + constructor: THREE.Box2, + + 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 + * @author WestLangley / http://github.com/WestLangley + */ + +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.Box3.prototype = { + + constructor: THREE.Box3, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + addPoint: function ( point ) { + + 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; + + } + + }, + + 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 ++ ) { + + this.addPoint( points[ i ] ) + + } + + } 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; + + }; + + }(), + + setFromObject: function() { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and childrens', world transforms + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var scope = this; + + object.updateMatrixWorld( true ); + + this.makeEmpty(); + + object.traverse( function ( node ) { + + if ( node.geometry !== undefined && node.geometry.vertices !== undefined ) { + + var vertices = node.geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } + + } ); + + 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.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + 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 + * @author WestLangley / http://github.com/WestLangley + */ + + +THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + 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. + + var te = this.elements; + + 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.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + 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 ) { + + this.elements.set( m.elements ); + + return this; + + }, + + extractPosition: function ( m ) { + + console.warn( 'DEPRECATED: Matrix4\'s .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + + }, + + copyPosition: 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; + + }; + + }(), + + makeRotationFromEuler: function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'ERROR: Matrix\'s .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order. Please update your code.' ); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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 ( euler.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; + + } + + // last column + te[3] = 0; + te[7] = 0; + te[11] = 0; + + // bottom row + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + + return this; + + }, + + setRotationFromQuaternion: function ( q ) { + + console.warn( 'DEPRECATED: Matrix4\'s .setRotationFromQuaternion() has been deprecated in favor of makeRotationFromQuaternion. Please update your code.' ); + + return this.makeRotationFromQuaternion( q ); + + }, + + makeRotationFromQuaternion: 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 ); + + // last column + te[3] = 0; + te[7] = 0; + te[11] = 0; + + // bottom row + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + + 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 ) { + + console.warn( 'DEPRECATED: Matrix4\'s .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + + v.transformDirection( this ); + + }, + + crossVector: function ( vector ) { + + console.warn( 'DEPRECATED: Matrix4\'s .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + 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 = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * 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; + + }, + + translate: function ( v ) { + + console.warn( 'DEPRECATED: Matrix4\'s .translate() has been removed.'); + + }, + + rotateX: function ( angle ) { + + console.warn( 'DEPRECATED: Matrix4\'s .rotateX() has been removed.'); + + }, + + rotateY: function ( angle ) { + + console.warn( 'DEPRECATED: Matrix4\'s .rotateY() has been removed.'); + + }, + + rotateZ: function ( angle ) { + + console.warn( 'DEPRECATED: Matrix4\'s .rotateZ() has been removed.'); + + }, + + rotateByAxis: function ( axis, angle ) { + + console.warn( 'DEPRECATED: Matrix4\'s .rotateByAxis() has been removed.'); + + }, + + 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; + + }, + + compose: function ( position, quaternion, scale ) { + + this.makeRotationFromQuaternion( quaternion ); + this.scale( scale ); + this.setPosition( position ); + + return this; + + }, + + decompose: function () { + + var vector = new THREE.Vector3(); + var matrix = new THREE.Matrix4(); + + return function ( position, quaternion, scale ) { + + var te = this.elements; + + var sx = vector.set( te[0], te[1], te[2] ).length(); + var sy = vector.set( te[4], te[5], te[6] ).length(); + var sz = vector.set( te[8], te[9], te[10] ).length(); + + position.x = te[12]; + position.y = te[13]; + position.z = te[14]; + + // scale the rotation part + + matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[0] *= invSX; + matrix.elements[1] *= invSX; + matrix.elements[2] *= invSX; + + matrix.elements[4] *= invSY; + matrix.elements[5] *= invSY; + matrix.elements[6] *= invSY; + + matrix.elements[8] *= invSZ; + matrix.elements[9] *= invSZ; + matrix.elements[10] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + 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; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], + te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], + te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], + te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] + ]; + + }, + + 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.Ray.prototype = { + + constructor: THREE.Ray, + + 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 ); + + if ( directionDistance < 0 ) { + + return result.copy( this.origin ); + + } + + 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 ); + + // point behind the ray + + if ( directionDistance < 0 ) { + + return this.origin.distanceTo( point ); + + } + + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + return v1.distanceTo( point ); + + }; + + }(), + + distanceSqToSegment: function( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + + // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 ); + var segDir = v1.clone().sub( v0 ).normalize(); + var segExtent = v0.distanceTo( v1 ) * 0.5; + var diff = this.origin.clone().sub( segCenter ); + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; + + if ( det >= 0 ) { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if ( s0 >= 0 ) { + + if ( s1 >= - extDet ) { + + if ( s1 <= extDet ) { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + + } else { + + // region 1 + + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + // region 5 + + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + if ( s1 <= - extDet) { + + // region 4 + + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } else if ( s1 <= extDet ) { + + // region 3 + + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; + + } else { + + // region 2 + + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } + + } else { + + // Ray and segment are parallel. + + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + if ( optionalPointOnRay ) { + + optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) ); + + } + + if ( optionalPointOnSegment ) { + + optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) ); + + } + + return sqrDist; + + }, + + isIntersectionSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) <= sphere.radius; + + }, + + isIntersectionPlane: function ( plane ) { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint( this.origin ); + + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true + + } + + // ray origin is behind the plane (and is pointing behind it) + + 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; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function ( plane, optionalTarget ) { + + var t = this.distanceToPlane( plane ); + + if ( t === null ) { + + return null; + } + + return this.at( t, optionalTarget ); + + }, + + isIntersectionBox: function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + } + + }(), + + intersectBox: function ( box , optionalTarget ) { + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + + var tmin,tmax,tymin,tymax,tzmin,tzmax; + + var invdirx = 1/this.direction.x, + invdiry = 1/this.direction.y, + invdirz = 1/this.direction.z; + + var origin = this.origin; + + if (invdirx >= 0) { + + tmin = (box.min.x - origin.x) * invdirx; + tmax = (box.max.x - origin.x) * invdirx; + + } else { + + tmin = (box.max.x - origin.x) * invdirx; + tmax = (box.min.x - origin.x) * invdirx; + } + + if (invdiry >= 0) { + + tymin = (box.min.y - origin.y) * invdiry; + tymax = (box.max.y - origin.y) * invdiry; + + } else { + + tymin = (box.max.y - origin.y) * invdiry; + tymax = (box.min.y - origin.y) * invdiry; + } + + if ((tmin > tymax) || (tymin > tmax)) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if (tymin > tmin || tmin !== tmin ) tmin = tymin; + + if (tymax < tmax || tmax !== tmax ) tmax = tymax; + + if (invdirz >= 0) { + + tzmin = (box.min.z - origin.z) * invdirz; + tzmax = (box.max.z - origin.z) * invdirz; + + } else { + + tzmin = (box.max.z - origin.z) * invdirz; + tzmax = (box.min.z - origin.z) * invdirz; + } + + if ((tmin > tzmax) || (tzmin > tmax)) return null; + + if (tzmin > tmin || tmin !== tmin ) tmin = tzmin; + + if (tzmax < tmax || tmax !== tmax ) tmax = tzmax; + + //return point closest to the ray (positive side) + + if ( tmax < 0 ) return null; + + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + + }, + + intersectTriangle: function() { + + // Compute the offset origin, edges, and normal. + var diff = new THREE.Vector3(); + var edge1 = new THREE.Vector3(); + var edge2 = new THREE.Vector3(); + var normal = new THREE.Vector3(); + + return function ( a, b, c, backfaceCulling, optionalTarget ) { + + // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp + + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; + + if ( DdN > 0 ) { + + if ( backfaceCulling ) return null; + sign = 1; + + } else if ( DdN < 0 ) { + + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + } + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + 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.Sphere.prototype = { + + constructor: THREE.Sphere, + + set: function ( center, radius ) { + + this.center.copy( center ); + this.radius = radius; + + return this; + }, + + + setFromPoints: function () { + + var box = new THREE.Box3(); + + return function ( points, optionalCenter ) { + + var center = this.center; + + if ( optionalCenter !== undefined ) { + + center.copy( optionalCenter ); + + } else { + + box.setFromPoints( points ).center( center ); + + } + + var maxRadiusSq = 0; + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + + } + + 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.Frustum.prototype = { + + constructor: THREE.Frustum, + + 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 sphere = new THREE.Sphere(); + + return function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + return this.intersectsSphere( sphere ); + + }; + + }(), + + 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; + + }, + + intersectsBox : function() { + + var p1 = new THREE.Vector3(), + p2 = new THREE.Vector3(); + + return function( box ) { + + var planes = this.planes; + + for ( var i = 0; i < 6 ; i ++ ) { + + var plane = planes[i]; + + p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; + p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; + p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; + p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + var d1 = plane.distanceToPoint( p1 ); + var d2 = plane.distanceToPoint( p2 ); + + // if both outside plane, no intersection + + if ( d1 < 0 && d2 < 0 ) { + + 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.Plane.prototype = { + + constructor: THREE.Plane, + + 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().getNormalMatrix( matrix ); + 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/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + PI2: Math.PI * 2, + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + var uuid = new Array(36); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; + + } + } + + return uuid.join(''); + + }; + + }(), + + // 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(); + var v1 = new THREE.Vector3(); + var 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.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.Clock.prototype = { + + constructor: THREE.Clock, + + start: function () { + + this.startTime = self.performance !== undefined && self.performance.now !== undefined + ? self.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 = self.performance !== undefined && self.performance.now !== undefined + ? self.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 () {} + +THREE.EventDispatcher.prototype = { + + constructor: THREE.EventDispatcher, + + apply: function ( object ) { + + object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; + object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; + + }, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var index = listeners[ type ].indexOf( listener ); + + if ( index !== - 1 ) { + + listeners[ type ].splice( index, 1 ); + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + for ( var i = 0, l = listenerArray.length; i < l; i ++ ) { + + listenerArray[ i ].call( this, event ); + + } + + } + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://exocortex.com/ + * @author stephomi / http://stephaneginier.com/ + */ + +( function ( THREE ) { + + THREE.Raycaster = function ( origin, direction, near, far ) { + + this.ray = new THREE.Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + 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 vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); + + 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.LOD ) { + + matrixPosition.getPositionFromMatrix( object.matrixWorld ); + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + + intersectObject( object.getObjectForDistance( distance ), raycaster, intersects ); + + } else if ( object instanceof THREE.Mesh ) { + + var geometry = object.geometry; + + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return intersects; + + } + + // Check boundingBox before continuing + + inverseMatrix.getInverse( object.matrixWorld ); + localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( localRay.isIntersectionBox( geometry.boundingBox ) === false ) { + + return intersects; + + } + + } + + if ( geometry instanceof THREE.BufferGeometry ) { + + var material = object.material; + + if ( material === undefined ) return intersects; + if ( geometry.dynamic === false ) return intersects; + + var a, b, c; + var precision = raycaster.precision; + + if ( geometry.attributes.index !== undefined ) { + + var offsets = geometry.offsets; + var indices = geometry.attributes.index.array; + var positions = geometry.attributes.position.array; + var offLength = geometry.offsets.length; + + var fl = geometry.attributes.index.array.length / 3; + + for ( var oi = 0; oi < offLength; ++oi ) { + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + a = index + indices[ i ]; + b = index + indices[ i + 1 ]; + c = index + indices[ i + 2 ]; + + vA.set( + positions[ a * 3 ], + positions[ a * 3 + 1 ], + positions[ a * 3 + 2 ] + ); + vB.set( + positions[ b * 3 ], + positions[ b * 3 + 1 ], + positions[ b * 3 + 2 ] + ); + vC.set( + positions[ c * 3 ], + positions[ c * 3 + 1 ], + positions[ c * 3 + 2 ] + ); + + var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: null, + faceIndex: null, + object: object + + } ); + + } + + } + + } else { + + var offsets = geometry.offsets; + var positions = geometry.attributes.position.array; + var offLength = geometry.offsets.length; + + var fl = geometry.attributes.position.array.length; + + for ( var i = 0; i < fl; i += 3 ) { + + a = i; + b = i + 1; + c = i + 2; + + vA.set( + positions[ a * 3 ], + positions[ a * 3 + 1 ], + positions[ a * 3 + 2 ] + ); + vB.set( + positions[ b * 3 ], + positions[ b * 3 + 1 ], + positions[ b * 3 + 2 ] + ); + vC.set( + positions[ c * 3 ], + positions[ c * 3 + 1 ], + positions[ c * 3 + 2 ] + ); + + var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: null, + faceIndex: null, + object: object + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? object.material.materials : null; + + var a, b, c, d; + var precision = raycaster.precision; + + var vertices = geometry.vertices; + + 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; + + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; + + var intersectionPoint = localRay.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: face, + faceIndex: f, + object: object + + } ); + + } + + } + + } else if ( object instanceof THREE.Line ) { + + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return intersects; + + } + + inverseMatrix.getInverse( object.matrixWorld ); + localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + /* if ( geometry instanceof THREE.BufferGeometry ) { + + } else */ if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var nbVertices = vertices.length; + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = object.type === THREE.LineStrip ? 1 : 2; + + for ( var i = 0; i < nbVertices - 1; i = i + step ) { + + var distSq = localRay.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + var distance = localRay.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( object.matrixWorld ), + face: null, + faceIndex: null, + 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.linePrecision = 1; + + THREE.Raycaster.prototype.set = function ( origin, direction ) { + + this.ray.set( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + }; + + 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/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Object3D = function () { + + this.id = THREE.Object3DIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.parent = undefined; + this.children = []; + + this.up = new THREE.Vector3( 0, 1, 0 ); + + this.position = new THREE.Vector3(); + this.rotation = new THREE.Euler(); + this.quaternion = new THREE.Quaternion(); + this.scale = new THREE.Vector3( 1, 1, 1 ); + + // keep rotation and quaternion in sync + + this.rotation._quaternion = this.quaternion; + this.quaternion._euler = this.rotation; + + this.renderDepth = null; + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = true; + this.matrixWorldNeedsUpdate = true; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + + this.userData = {}; + +}; + + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + get eulerOrder () { + + console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' ); + + return this.rotation.order; + + }, + + set eulerOrder ( value ) { + + console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' ); + + this.rotation.order = value; + + }, + + get useQuaternion () { + + console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set useQuaternion ( value ) { + + console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + applyMatrix: function () { + + var m1 = new THREE.Matrix4(); + + return function ( matrix ) { + + this.matrix.multiplyMatrices( matrix, this.matrix ); + + this.position.getPositionFromMatrix( this.matrix ); + + this.scale.getScaleFromMatrix( this.matrix ); + + m1.extractRotation( this.matrix ); + + this.quaternion.setFromRotationMatrix( m1 ); + + } + + }(), + + setRotationFromAxisAngle: function ( axis, angle ) { + + // assumes axis is normalized + + this.quaternion.setFromAxisAngle( axis, angle ); + + }, + + setRotationFromEuler: function ( euler ) { + + this.quaternion.setFromEuler( euler, true ); + + }, + + setRotationFromMatrix: function ( m ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + this.quaternion.setFromRotationMatrix( m ); + + }, + + setRotationFromQuaternion: function ( q ) { + + // assumes q is normalized + + this.quaternion.copy( q ); + + }, + + rotateOnAxis: function() { + + // rotate object on axis in object space + // axis is assumed to be normalized + + var q1 = new THREE.Quaternion(); + + return function ( axis, angle ) { + + q1.setFromAxisAngle( axis, angle ); + + this.quaternion.multiply( q1 ); + + return this; + + } + + }(), + + rotateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + translateOnAxis: function () { + + // translate object by distance along axis in object space + // axis is assumed to be normalized + + var v1 = new THREE.Vector3(); + + return function ( axis, distance ) { + + v1.copy( axis ); + + v1.applyQuaternion( this.quaternion ); + + this.position.add( v1.multiplyScalar( distance ) ); + + return this; + + } + + }(), + + translate: function ( distance, axis ) { + + console.warn( 'DEPRECATED: Object3D\'s .translate() has been removed. Use .translateOnAxis( axis, distance ) instead. Note args have been changed.' ); + return this.translateOnAxis( axis, distance ); + + }, + + translateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + localToWorld: function ( vector ) { + + return vector.applyMatrix4( this.matrixWorld ); + + }, + + worldToLocal: function () { + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); + + }; + + }(), + + lookAt: function () { + + // This routine does not support objects with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( vector, this.position, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + + }(), + + 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; + object.dispatchEvent( { type: 'added' } ); + + 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; + object.dispatchEvent( { type: 'removed' } ); + + 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 ); + + } + + }, + + getObjectById: function ( id, recursive ) { + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + + if ( child.id === id ) { + + return child; + + } + + if ( recursive === true ) { + + child = child.getObjectById( id, recursive ); + + if ( child !== undefined ) { + + return child; + + } + + } + + } + + return undefined; + + }, + + getObjectByName: 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.getObjectByName( name, recursive ); + + if ( child !== undefined ) { + + return child; + + } + + } + + } + + return undefined; + + }, + + getChildByName: function ( name, recursive ) { + + console.warn( 'DEPRECATED: Object3D\'s .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name, recursive ); + + }, + + 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.compose( this.position, this.quaternion, 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, recursive ) { + + if ( object === undefined ) object = new THREE.Object3D(); + if ( recursive === undefined ) recursive = true; + + object.name = this.name; + + object.up.copy( this.up ); + + object.position.copy( this.position ); + object.quaternion.copy( this.quaternion ); + object.scale.copy( this.scale ); + + object.renderDepth = this.renderDepth; + + object.rotationAutoUpdate = this.rotationAutoUpdate; + + object.matrix.copy( this.matrix ); + object.matrixWorld.copy( this.matrixWorld ); + + object.matrixAutoUpdate = this.matrixAutoUpdate; + object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + + object.visible = this.visible; + + object.castShadow = this.castShadow; + object.receiveShadow = this.receiveShadow; + + object.frustumCulled = this.frustumCulled; + + object.userData = JSON.parse( JSON.stringify( this.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < this.children.length; i ++ ) { + + var child = this.children[ i ]; + object.add( child.clone() ); + + } + + } + + return object; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + +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, + _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 getObject = function ( object ) { + + _object = getNextObjectInPool(); + _object.id = object.id; + _object.object = object; + + if ( object.renderDepth !== null ) { + + _object.z = object.renderDepth; + + } else { + + _vector3.getPositionFromMatrix( object.matrixWorld ); + _vector3.applyProjection( _viewProjectionMatrix ); + _object.z = _vector3.z; + + } + + return _object; + + }; + + var projectObject = function ( object ) { + + if ( object.visible === false ) return; + + 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 ) { + + _renderData.objects.push( getObject( object ) ); + + } + + } else if ( object instanceof THREE.Sprite || object instanceof THREE.Particle ) { + + _renderData.sprites.push( getObject( object ) ); + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + projectObject( object.children[ i ] ); + + } + + }; + + var projectGraph = function ( root, sortObjects ) { + + _objectCount = 0; + + _renderData.objects.length = 0; + _renderData.sprites.length = 0; + _renderData.lights.length = 0; + + projectObject( root ); + + if ( sortObjects === true ) { + + _renderData.objects.sort( painterSort ); + + } + + }; + + 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; + _lineCount = 0; + _particleCount = 0; + + _renderData.elements.length = 0; + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); + _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); + + _normalViewMatrix.getNormalMatrix( _viewMatrix ); + + _frustum.setFromMatrix( _viewProjectionMatrix ); + + 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.getNormalMatrix( _modelMatrix ); + + 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 ); + + var invW = 1 / _vertex.positionScreen.w; + + _vertex.positionScreen.x *= invW; + _vertex.positionScreen.y *= invW; + _vertex.positionScreen.z *= invW; + + _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; + + 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.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + + } 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 = Math.min( faceVertexNormals.length, 3 ); 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 = Math.min( faceVertexUvs.length, 3 ); 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.id = object.id; + _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); + _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + + _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + + _line.material = object.material; + + if ( object.material.vertexColors === THREE.VertexColors ) { + + _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); + _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); + + } + + _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 ); + + var invW = 1 / _vector4.w; + + _vector4.z *= invW; + + if ( _vector4.z > 0 && _vector4.z < 1 ) { + + _particle = getNextParticleInPool(); + _particle.id = object.id; + _particle.x = _vector4.x * invW; + _particle.y = _vector4.y * invW; + _particle.z = _vector4.z; + _particle.object = object; + + _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 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 ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else if ( a.id !== b.id ) { + + return a.id - b.id; + + } else { + + return 0; + + } + + } + + 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/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.') + + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +/** + * @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 () { + + this.id = THREE.GeometryIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in ParticleSystem and Line + + this.faces = []; + + 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().getNormalMatrix( matrix ); + + 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 ); + + } + + if ( this.boundingBox instanceof THREE.Box3 ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere instanceof THREE.Sphere ) { + + this.computeBoundingSphere(); + + } + + }, + + 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 ); + + 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 ); + + } + + }, + + 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 ]; + face.vertexNormals = [ 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 ]; + + 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 { + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].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 ]; + + face.vertexNormals[ 0 ].copy( vertices[ face.a ] ); + face.vertexNormals[ 1 ].copy( vertices[ face.b ] ); + face.vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } + + }, + + 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(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: 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 ); + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } + + } + + // 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 + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + + } + + var faceIndex = [ 'a', 'b', 'c', 'd' ]; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); 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.setFromPoints( 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 ); + + 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 ]; + + 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; + + } + } + + } + + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + var idx = faceIndicesToRemove[ i ]; + + this.faces.splice( idx, 1 ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + this.faceVertexUvs[ j ].splice( idx, 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.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + +THREE.GeometryIdCount = 0; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BufferGeometry = function () { + + this.id = THREE.GeometryIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + // attributes + + this.attributes = {}; + + // attributes typed arrays are kept only if dynamic flag is set + + this.dynamic = true; + + // 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().getNormalMatrix( matrix ); + + 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 () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes[ "position" ].array; + + if ( positions ) { + + var center = this.boundingSphere.center; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + box.addPoint( vector ); + + } + + box.center( center ); + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + 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 ) + + }; + + } 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 ) + + }; + + } + + 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; + + }, + + clone: function () { + + var geometry = new THREE.BufferGeometry(); + + var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; + + for ( var attr in this.attributes ) { + + var sourceAttr = this.attributes[ attr ]; + var sourceArray = sourceAttr.array; + + var attribute = { + + itemSize: sourceAttr.itemSize, + numItems: sourceAttr.numItems, + array: null + + }; + + for ( var i = 0, il = types.length; i < il; i ++ ) { + + var type = types[ i ]; + + if ( sourceArray instanceof type ) { + + attribute.array = new type( sourceArray ); + break; + + } + + } + + geometry.attributes[ attr ] = attribute; + + } + + for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { + + var offset = this.offsets[ i ]; + + geometry.offsets.push( { + + start: offset.start, + index: offset.index, + count: offset.count + + } ); + + } + + return geometry; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ + +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 () { + + // This routine does not support cameras with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( this.position, vector, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + +}(); + +THREE.Camera.prototype.clone = function (camera) { + + if ( camera === undefined ) camera = new THREE.Camera(); + + THREE.Object3D.prototype.clone.call( this, camera ); + + camera.matrixWorldInverse.copy( this.matrixWorldInverse ); + camera.projectionMatrix.copy( this.projectionMatrix ); + camera.projectionMatrixInverse.copy( this.projectionMatrixInverse ); + + return camera; +}; + +/** + * @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 ); + +}; + +THREE.OrthographicCamera.prototype.clone = function () { + + var camera = new THREE.OrthographicCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.left = this.left; + camera.right = this.right; + camera.top = this.top; + camera.bottom = this.bottom; + + camera.near = this.near; + camera.far = this.far; + + return camera; +}; + +/** + * @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 ); + + } + +}; + +THREE.PerspectiveCamera.prototype.clone = function () { + + var camera = new THREE.PerspectiveCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.fov = this.fov; + camera.aspect = this.aspect; + camera.near = this.near; + camera.far = this.far; + + return camera; +}; + +/** + * @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 ); + +THREE.Light.prototype.clone = function ( light ) { + + if ( light === undefined ) light = new THREE.Light(); + + THREE.Object3D.prototype.clone.call( this, light ); + + light.color.copy( this.color ); + + return light; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( hex ) { + + THREE.Light.call( this, hex ); + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.AmbientLight.prototype.clone = function () { + + var light = new THREE.AmbientLight(); + + THREE.Light.prototype.clone.call( this, light ); + + return light; + +}; + +/** + * @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.set( 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 ); + +THREE.DirectionalLight.prototype.clone = function () { + + var light = new THREE.DirectionalLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + return light; + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) { + + THREE.Light.call( this, skyColorHex ); + + this.position.set( 0, 100, 0 ); + + this.groundColor = new THREE.Color( groundColorHex ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.HemisphereLight.prototype.clone = function () { + + var light = new THREE.HemisphereLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.groundColor.copy( this.groundColor ); + light.intensity = this.intensity; + + return light; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLight = function ( hex, intensity, distance ) { + + THREE.Light.call( this, hex ); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.PointLight.prototype.clone = function () { + + var light = new THREE.PointLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.intensity = this.intensity; + light.distance = this.distance; + + return light; + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) { + + THREE.Light.call( this, hex ); + + this.position.set( 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 / 3; + 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 ); + +THREE.SpotLight.prototype.clone = function () { + + var light = new THREE.SpotLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + light.distance = this.distance; + light.angle = this.angle; + light.exponent = this.exponent; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + return light; + +}; + +/** + * @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.XHRLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.XHRLoader.prototype = { + + constructor: THREE.XHRLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + var request = new XMLHttpRequest(); + + if ( onLoad !== undefined ) { + + request.addEventListener( 'load', function ( event ) { + + onLoad( event.target.responseText ); + scope.manager.itemEnd( url ); + + }, false ); + + } + + if ( onProgress !== undefined ) { + + request.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + request.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + + request.open( 'GET', url, true ); + request.send( null ); + + scope.manager.itemStart( url ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + var image = document.createElement( 'img' ); + + if ( onLoad !== undefined ) { + + image.addEventListener( 'load', function ( event ) { + + scope.manager.itemEnd( url ); + onLoad( this ); + + }, false ); + + } + + if ( onProgress !== undefined ) { + + image.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + image.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + + image.src = url; + + scope.manager.itemStart( url ); + + return image; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +} + +/** + * @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 ); + var result = context.parse( json, texturePath ); + callback( result.geometry, result.materials ); + + } 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 ) { + + if ( callbackProgress !== undefined ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + } + + }; + + xhr.open( "GET", url, true ); + xhr.withCredentials = this.withCredentials; + xhr.send( null ); + +}; + +THREE.JSONLoader.prototype.parse = function ( json, 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(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, color, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; 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 ); + 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, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = [] + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + 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 ); + + } + + } + + } + + }; + + if ( json.materials === undefined ) { + + return { geometry: geometry }; + + } else { + + var materials = this.initMaterials( json.materials, texturePath ); + + if ( this.needsTangents( materials ) ) { + + geometry.computeTangents(); + + } + + return { geometry: geometry, materials: materials }; + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { + + var scope = this; + + var loaded = 0, total = 0; + + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function ( url ) { + + total ++; + + }; + + this.itemEnd = function ( url ) { + + loaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, loaded, total ); + + } + + if ( loaded === total && scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + }; + +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BufferGeometryLoader.prototype = { + + constructor: THREE.BufferGeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + var attributes = json.attributes; + var offsets = json.offsets; + var boundingSphere = json.boundingSphere; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + geometry.attributes[ key ] = { + itemSize: attribute.itemSize, + array: new self[ attribute.type ]( attribute.array ) + } + + } + + if ( offsets !== undefined ) { + + geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); + + } + + if ( boundingSphere !== undefined ) { + + geometry.boundingSphere = new THREE.Sphere( + new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ), + boundingSphere.radius + ); + + } + + return geometry; + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.GeometryLoader.prototype = { + + constructor: THREE.GeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MaterialLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MaterialLoader.prototype = { + + constructor: THREE.MaterialLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var material = new THREE[ json.type ]; + + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + + if ( json.materials !== undefined ) { + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + material.materials.push( this.parse( json.materials[ i ] ) ); + + } + + } + + return material; + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ObjectLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ObjectLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometries = this.parseGeometries( json.geometries ); + var materials = this.parseMaterials( json.materials ); + var object = this.parseObject( json.object, geometries, materials ); + + return object; + + }, + + parseGeometries: function ( json ) { + + var geometries = {}; + + if ( json !== undefined ) { + + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var geometry; + var data = json[ i ]; + + switch ( data.type ) { + + case 'PlaneGeometry': + + geometry = new THREE.PlaneGeometry( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); + + break; + + case 'CircleGeometry': + + geometry = new THREE.CircleGeometry( + data.radius, + data.segments + ); + + break; + + case 'CubeGeometry': + + geometry = new THREE.CubeGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); + + break; + + case 'CylinderGeometry': + + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radiusSegments, + data.heightSegments, + data.openEnded + ); + + break; + + case 'SphereGeometry': + + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'IcosahedronGeometry': + + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TorusGeometry': + + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); + + break; + + case 'TorusKnotGeometry': + + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); + + break; + + case 'BufferGeometry': + + geometry = bufferGeometryLoader.parse( data.data ); + + break; + + case 'Geometry': + + geometry = geometryLoader.parse( data.data ).geometry; + + break; + + } + + geometry.uuid = data.uuid; + + if ( data.name !== undefined ) geometry.name = data.name; + + geometries[ data.uuid ] = geometry; + + } + + } + + return geometries; + + }, + + parseMaterials: function ( json ) { + + var materials = {}; + + if ( json !== undefined ) { + + var loader = new THREE.MaterialLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var data = json[ i ]; + var material = loader.parse( data ); + + material.uuid = data.uuid; + + if ( data.name !== undefined ) material.name = data.name; + + materials[ data.uuid ] = material; + + } + + } + + return materials; + + }, + + parseObject: function () { + + var matrix = new THREE.Matrix4(); + + return function ( data, geometries, materials ) { + + var object; + + switch ( data.type ) { + + case 'Scene': + + object = new THREE.Scene(); + + break; + + case 'PerspectiveCamera': + + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + + break; + + case 'OrthographicCamera': + + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + + break; + + case 'AmbientLight': + + object = new THREE.AmbientLight( data.color ); + + break; + + case 'DirectionalLight': + + object = new THREE.DirectionalLight( data.color, data.intensity ); + + break; + + case 'PointLight': + + object = new THREE.PointLight( data.color, data.intensity, data.distance ); + + break; + + case 'SpotLight': + + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent ); + + break; + + case 'HemisphereLight': + + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + + break; + + case 'Mesh': + + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; + + if ( geometry === undefined ) { + + console.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry ); + + } + + if ( material === undefined ) { + + console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + + } + + object = new THREE.Mesh( geometry, material ); + + break; + + default: + + object = new THREE.Object3D(); + + } + + object.uuid = data.uuid; + + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { + + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); + + } else { + + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + + } + + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; + + if ( data.children !== undefined ) { + + for ( var child in data.children ) { + + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + + } + + } + + return object; + + } + + }() + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneLoader = function () { + + this.onLoadStart = function () {}; + this.onLoadProgress = function() {}; + this.onLoadComplete = function () {}; + + this.callbackSync = function () {}; + this.callbackProgress = function () {}; + + this.geometryHandlers = {}; + this.hierarchyHandlers = {}; + + this.addGeometryHandler( "ascii", THREE.JSONLoader ); + +}; + +THREE.SceneLoader.prototype = { + + constructor: THREE.SceneLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + scope.parse( JSON.parse( text ), onLoad, url ); + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + addGeometryHandler: function ( typeID, loaderClass ) { + + this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass }; + + }, + + addHierarchyHandler: function ( typeID, loaderClass ) { + + this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass }; + + }, + + 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.geometryHandlers ) { + + var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ]; + this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass(); + + } + + // async hierachy loaders + + for ( var typeID in this.hierarchyHandlers ) { + + var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ]; + this.hierarchyHandlers[ 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.fromArray( position ); + + } + + if ( rotation ) { + + result.scene.rotation.fromArray( rotation ); + + } + + if ( scale ) { + + result.scene.scale.fromArray( scale ); + + } + + 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 + + var object = result.objects[ objID ]; + var objJSON = children[ objID ]; + + if ( object === undefined ) { + + // meshes + + if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { + + if ( objJSON.loading === undefined ) { + + var reservedTypes = { + "type": 1, "url": 1, "material": 1, + "position": 1, "rotation": 1, "scale" : 1, + "visible": 1, "children": 1, "userData": 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.hierarchyHandlers[ 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.fromArray( pos ); + + if ( quat ) { + + object.quaternion.fromArray( quat ); + + } else { + + object.rotation.fromArray( rot ); + + } + + object.scale.fromArray( scl ); + + } + + 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.fromArray( pos ); + + 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.fromArray( pos ); + + } 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" ) { + + pos = objJSON.position; + rot = objJSON.rotation; + quat = objJSON.quaternion; + + 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 ); + + } + + camera.name = objID; + camera.position.fromArray( pos ); + + if ( quat !== undefined ) { + + camera.quaternion.fromArray( quat ); + + } else if ( rot !== undefined ) { + + camera.rotation.fromArray( rot ); + + } + + parent.add( camera ); + + 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.fromArray( pos ); + + if ( quat ) { + + object.quaternion.fromArray( quat ); + + } else { + + object.rotation.fromArray( rot ); + + } + + object.scale.fromArray( scl ); + object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false; + + parent.add( object ); + + result.objects[ objID ] = object; + result.empties[ objID ] = object; + + } + + if ( object ) { + + if ( objJSON.userData !== undefined ) { + + for ( var key in objJSON.userData ) { + + var value = objJSON.userData[ key ]; + object.userData[ 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 ( object !== undefined && 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.fromArray( p ); + + if ( q ) { + + node.quaternion.fromArray( q ); + + } else { + + node.rotation.fromArray( r ); + + } + + node.scale.fromArray( s ); + + // 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 ) { + + geo.name = id; + + 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 ) { + + geo.name = id; + + 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.userData.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 ); + + }; + + }; + + function traverse_json_hierarchy( objJSON, callback ) { + + callback( objJSON ); + + if ( objJSON.children !== undefined ) { + + for ( var objChildID in objJSON.children ) { + + traverse_json_hierarchy( objJSON.children[ objChildID ], callback ); + + } + + } + + }; + + // 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.geometryHandlers ) { + + counter_models += 1; + + scope.onLoadStart(); + + } + + } + + // count how many hierarchies will be loaded asynchronously + + for ( var objID in data.objects ) { + + traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) { + + if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { + + 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 ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "plane" ) { + + geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "sphere" ) { + + geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "cylinder" ) { + + geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "torus" ) { + + geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type === "icosahedron" ) { + + geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions ); + geometry.name = geoID; + result.geometries[ geoID ] = geometry; + + } else if ( geoJSON.type in this.geometryHandlers ) { + + var loaderParameters = {}; + + for ( var parType in geoJSON ) { + + if ( parType !== "type" && parType !== "url" ) { + + loaderParameters[ parType ] = geoJSON[ parType ]; + + } + + } + + var loader = this.geometryHandlers[ 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.geometryHandlers[ "ascii" ][ "loaderObject" ]; + var model = jsonLoader.parse( modelJson, texture_path ); + create_callback_embed( geoID )( model.geometry, model.materials ); + + } + + } + + } + + // 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 ); + + } + + material.name = matID; + + 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 ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.ImageLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + } ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + this.id = THREE.MaterialIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + 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 = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this.needsUpdate = true; + +}; + +THREE.Material.prototype = { + + constructor: THREE.Material, + + 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 ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key == 'overdraw') { + + // ensure overdraw is backwards-compatable with legacy boolean type + this[ key ] = Number(newValue); + + } else { + + this[ key ] = newValue; + + } + + } + + } + + }, + + 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; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +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.MeshDepthMaterial(); + + 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.morphTargets = false; + + 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 () { + + var material = new THREE.MeshFaceMaterial(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + return material; + +}; + +/** + * @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.linewidth = 1; + + 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 + + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + "color" : [ 1, 1, 1], + "uv" : [ 0, 0 ], + "uv2" : [ 0, 0 ] + }; + + // By default, bind position to attribute index 0. In WebGL, attribute 0 + // should always be used to avoid potentially expensive emulation. + this.index0AttributeName = "position"; + + 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 ) { + + this.id = THREE.TextureIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + 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.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + +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 !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.ParticleBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.sortParticles = false; + 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 !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.type = ( type !== undefined ) ? type : THREE.LineStrip; + +}; + +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 !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + + 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] ); + + 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 () { + + var offsetMatrix = new THREE.Matrix4(); + + return 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 + + offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + + }; + +}(); + +THREE.SkinnedMesh.prototype.pose = function () { + + this.updateMatrixWorld( true ); + + this.normalizeSkinWeights(); + +}; + +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + + if ( this.geometry instanceof THREE.Geometry ) { + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + 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 + + } + + } + + } else { + + // skinning weights assumed to be normalized for THREE.BufferGeometry + + } + +}; + +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; + +}; + +/** + * @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 mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.objects = []; + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.LOD.prototype.addLevel = function ( object, distance ) { + + if ( distance === undefined ) distance = 0; + + distance = Math.abs( distance ); + + for ( var l = 0; l < this.objects.length; l ++ ) { + + if ( distance < this.objects[ l ].distance ) { + + break; + + } + + } + + this.objects.splice( l, 0, { distance: distance, object: object } ); + this.add( object ); + +}; + +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance < this.objects[ i ].distance ) { + + break; + + } + + } + + return this.objects[ i - 1 ].object; + +}; + +THREE.LOD.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( camera ) { + + if ( this.objects.length > 1 ) { + + v1.getPositionFromMatrix( camera.matrixWorld ); + v2.getPositionFromMatrix( this.matrixWorld ); + + var distance = v1.distanceTo( v2 ); + + this.objects[ 0 ].object.visible = true; + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance >= this.objects[ i ].distance ) { + + this.objects[ i - 1 ].object.visible = false; + this.objects[ i ].object.visible = true; + + } else { + + break; + + } + + } + + for( ; i < l; i ++ ) { + + this.objects[ i ].object.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.rotation3d.set( 0, 0, this.rotation, this.rotation3d.order ); + this.quaternion.setFromEuler( this.rotation3d ); + this.matrix.compose( this.position, this.quaternion, 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.autoUpdate = true; // checked by the renderer + this.matrixAutoUpdate = false; + + 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 ) ) { + + 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 ); + + } + + if ( object.shadowCascadeArray ) { + + for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) { + + this.__removeObject( object.shadowCascadeArray[ x ] ); + + } + + } + + } else if ( !( object instanceof THREE.Camera ) ) { + + this.__objectsRemoved.push( object ); + + // check if previously added + + var i = this.__objectsAdded.indexOf( object ); + + if ( i !== -1 ) { + + this.__objectsAdded.splice( i, 1 ); + + } + + } + + for ( var c = 0; c < object.children.length; c ++ ) { + + this.__removeObject( object.children[ c ] ); + + } + +}; + +THREE.Scene.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Scene(); + + THREE.Object3D.prototype.clone.call(this, object); + + if ( this.fog !== null ) object.fog = this.fog.clone(); + if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + + object.autoUpdate = this.autoUpdate; + object.matrixAutoUpdate = this.matrixAutoUpdate; + + return object; + +}; + +/** + * @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 ), + _clearAlpha = 0, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + _contextDashSize = null, + _contextGapSize = 0, + + _camera, + + _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(), + + _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 + : self.devicePixelRatio !== undefined + ? self.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, updateStyle ) { + + _canvasWidth = width * this.devicePixelRatio; + _canvasHeight = height * this.devicePixelRatio; + + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + if ( this.devicePixelRatio !== 1 && updateStyle !== false ) { + + _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, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _clearBox.set( + new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ), + new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf ) + ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + 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 ( _clearAlpha < 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 ( _clearAlpha > 0 ) { + + setBlending( THREE.NormalBlending ); + setOpacity( 1 ); + + setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + + _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; + _camera = camera; + + /* 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 ); + */ + + 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 > 0 ) { + + expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); + expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); + expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + + } + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + + } + + } + + /* 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 ) { + + _elemBox.makeEmpty(); + 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 ) { + + _elemBox.makeEmpty(); + 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 ) { + + _elemBox.makeEmpty(); + 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 ); + + if ( material.vertexColors !== THREE.VertexColors ) { + + setStrokeStyle( material.color.getStyle() ); + + } else { + + var colorStyle1 = element.vertexColors[0].getStyle(); + var colorStyle2 = element.vertexColors[1].getStyle(); + + if ( colorStyle1 === colorStyle2 ) { + + setStrokeStyle( colorStyle1 ); + + } else { + + try { + + var grad = _context.createLinearGradient( + v1.positionScreen.x, + v1.positionScreen.y, + v2.positionScreen.x, + v2.positionScreen.y + ); + grad.addColorStop( 0, colorStyle1 ); + grad.addColorStop( 1, colorStyle2 ); + + } catch ( exception ) { + + grad = colorStyle1; + + } + + setStrokeStyle( grad ); + + } + + } + + _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 ); + + setDashAndGap( null, null ); + + } + + } + + 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 ( 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 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 drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _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, pixels ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + det = x * x + y * y, idet; + + if ( det === 0 ) return; + + idet = pixels / 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( -vViewPosition, 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;", + "uniform int boneTextureWidth;", + "uniform int boneTextureHeight;", + + "mat4 getBoneMatrix( const in float i ) {", + + "float j = i * 4.0;", + "float x = mod( j, float( boneTextureWidth ) );", + "float y = floor( j / float( boneTextureWidth ) );", + + "float dx = 1.0 / float( boneTextureWidth );", + "float dy = 1.0 / float( boneTextureHeight );", + + "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 ) ) );", + "depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );", + "depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );", + "depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );", + "depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );", + "depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );", + "depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );", + "depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );", + "depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );", + + "vec3 shadowZ = vec3( shadowCoord.z );", + "shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));", + "shadowKernel[0] *= vec3(0.25);", + + "shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));", + "shadowKernel[1] *= vec3(0.25);", + + "shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));", + "shadowKernel[2] *= vec3(0.25);", + + "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;", + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + + "void main() {", + + "vNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "}" + + ].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 = new THREE.Color( 0x000000 ), + _clearAlpha = 0; + + // public properties + + this.domElement = _canvas; + this.context = null; + this.devicePixelRatio = parameters.devicePixelRatio !== undefined + ? parameters.devicePixelRatio + : self.devicePixelRatio !== undefined + ? self.devicePixelRatio + : 1; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + this.autoUpdateObjects = 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 _glExtensionTextureFloatLinear; + 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, updateStyle ) { + + _canvas.width = width * this.devicePixelRatio; + _canvas.height = height * this.devicePixelRatio; + + if ( this.devicePixelRatio !== 1 && updateStyle !== false ) { + + _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.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + 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 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 ); + + }; + + 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 deleteBuffers = function ( geometry ) { + + 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 ); + // custom attributes + + if ( geometry.__webglCustomAttributesList !== undefined ) { + + for ( var id in geometry.__webglCustomAttributesList ) { + + _gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); + + } + + } + + _this.info.memory.geometries --; + + }; + + var deallocateGeometry = function ( geometry ) { + + geometry.__webglInit = undefined; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + + for ( var key in attributes ) { + + if ( attributes[ key ].buffer !== undefined ) { + + _gl.deleteBuffer( attributes[ key ].buffer ); + + } + + } + + _this.info.memory.geometries --; + + } else { + + 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 ] ); + + } + + } + + deleteBuffers( geometryGroup ); + + } + + } else { + + deleteBuffers( 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 --; + + } + + }; + + // 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 initMeshBuffers ( geometryGroup, object ) { + + var geometry = object.geometry, + faces3 = geometryGroup.faces3, + + nvertices = faces3.length * 3, + ntris = faces3.length * 1, + nlines = faces3.length * 3, + + 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.faceVertexUvs.length > 0 ) { + + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + + } + + if ( 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 ]; + + if ( attribute.numItems === undefined ) { + + attribute.numItems = attribute.array.length; + + } + + 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 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, + 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; + + } + + _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; + + } + + _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; + + } + + 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; + + } + + 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; + + } + + _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; + + } + + } + + } + + _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; + + } + + } + + 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; + + } + + } + + 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; + + } + + _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; + + } + + } 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; + + } + + } + + } 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_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + 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_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } + + } else if ( customAttribute.size === 3 ) { + + var pp; + + if ( customAttribute.type === "c" ) { + + pp = [ "r", "g", "b" ]; + + } else { + + pp = [ "x", "y", "z" ]; + + } + + 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[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + 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 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + if ( dispose ) { + + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; + + } + + }; + + function setDirectBuffers ( geometry, hint, dispose ) { + + var attributes = geometry.attributes; + + var attributeName, attributeItem; + + for ( attributeName in attributes ) { + + attributeItem = attributes[ attributeName ]; + + if ( attributeItem.needsUpdate ) { + + if ( attributeName === 'index' ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint ); + + } else { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint ); + + } + + attributeItem.needsUpdate = false; + + } + + if ( dispose && ! attributeItem.dynamic ) { + + attributeItem.array = null; + + } + + } + + }; + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); + if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + + if ( material.shading === THREE.FlatShading ) { + + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; + + for( i = 0; i < il; i += 9 ) { + + normalArray = object.normalArray; + + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; + + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; + + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; + + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; + + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; + + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; + + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.uv ); + _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.color ); + _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + + if ( material.visible === false ) return; + + var linewidth, a, attribute; + var attributeItem, attributeName, attributePointer, attributeSize; + + var program = setProgram( camera, lights, fog, material, object ); + + var programAttributes = program.attributes; + var geometryAttributes = geometry.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryHash; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + disableAttributes(); + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var index = geometryAttributes[ "index" ]; + + // indexed triangles + + if ( index ) { + + var offsets = geometry.offsets; + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + if ( offsets.length > 1 ) updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + for ( attributeName in programAttributes ) { + + attributePointer = programAttributes[ attributeName ]; + attributeItem = geometryAttributes[ attributeName ]; + + if ( attributePointer >= 0 ) { + + if ( attributeItem ) { + + attributeSize = attributeItem.itemSize; + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + enableAttribute( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32 + + } else if ( material.defaultAttributeValues ) { + + if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + + _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + + _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } + + } + + } + + } + + // indices + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed triangles + + _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16 + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + _this.info.render.faces += offsets[ i ].count / 3; + + } + + // non-indexed triangles + + } else { + + if ( updateBuffers ) { + + for ( attributeName in programAttributes ) { + + if ( attributeName === 'index') continue; + + attributePointer = programAttributes[ attributeName ]; + attributeItem = geometryAttributes[ attributeName ]; + + if ( attributePointer >= 0 ) { + + if ( attributeItem ) { + + attributeSize = attributeItem.itemSize; + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + enableAttribute( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { + + if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + + _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + + _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } + + } + + } + + } + + } + + var position = geometry.attributes[ "position" ]; + + // render non-indexed triangles + + _gl.drawArrays( _gl.TRIANGLES, 0, position.numItems / 3 ); + + _this.info.render.calls ++; + _this.info.render.vertices += position.numItems / 3; + _this.info.render.faces += position.numItems / 3 / 3; + + } + + // render particles + + } else if ( object instanceof THREE.ParticleSystem ) { + + if ( updateBuffers ) { + + for ( attributeName in programAttributes ) { + + attributePointer = programAttributes[ attributeName ]; + attributeItem = geometryAttributes[ attributeName ]; + + if ( attributePointer >= 0 ) { + + if ( attributeItem ) { + + attributeSize = attributeItem.itemSize; + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + enableAttribute( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { + + if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + + _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + + _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } + + } + + } + + } + + var position = geometryAttributes[ "position" ]; + + // render particles + + _gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.numItems / 3; + + } + + } else if ( object instanceof THREE.Line ) { + + if ( updateBuffers ) { + + for ( attributeName in programAttributes ) { + + attributePointer = programAttributes[ attributeName ]; + attributeItem = geometryAttributes[ attributeName ]; + + if ( attributePointer >= 0 ) { + + if ( attributeItem ) { + + attributeSize = attributeItem.itemSize; + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + enableAttribute( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { + + if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + + _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + + _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } + + } + + } + + } + + // render lines + + var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + var position = geometryAttributes[ "position" ]; + + _gl.drawArrays( primitives, 0, position.numItems / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.numItems; + + } + + } + + }; + + this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + + if ( material.visible === false ) return; + + var linewidth, a, attribute, i, il; + + var program = setProgram( camera, lights, fog, material, object ); + + var attributes = program.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryGroupHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryGroupHash; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + disableAttributes(); + + } + + // vertices + + if ( !material.morphTargets && attributes.position >= 0 ) { + + if ( updateBuffers ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + } else { + + if ( object.morphTargetBase ) { + + setupMorphTargets( material, geometryGroup, object ); + + } + + } + + + if ( updateBuffers ) { + + // custom attributes + + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + + if ( geometryGroup.__webglCustomAttributesList ) { + + for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + + attribute = geometryGroup.__webglCustomAttributesList[ i ]; + + if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); + _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + + // colors + + if ( attributes.color >= 0 ) { + + if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + enableAttribute( attributes.color ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); + + } + + } + + // normals + + if ( attributes.normal >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + enableAttribute( attributes.normal ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + enableAttribute( attributes.tangent ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // uvs + + if ( attributes.uv >= 0 ) { + + if ( object.geometry.faceVertexUvs[0] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + enableAttribute( attributes.uv ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + + } + + } + + if ( attributes.uv2 >= 0 ) { + + if ( object.geometry.faceVertexUvs[1] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + enableAttribute( attributes.uv2 ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); + + } + + } + + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + enableAttribute( attributes.skinIndex ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + enableAttribute( attributes.skinWeight ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // line distances + + if ( attributes.lineDistance >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); + enableAttribute( attributes.lineDistance ); + _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); + + } + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + // wireframe + + if ( material.wireframe ) { + + setLineWidth( material.wireframeLinewidth ); + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); + + // triangles + + } else { + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + + } + + _this.info.render.calls ++; + _this.info.render.vertices += geometryGroup.__webglFaceCount; + _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + + // render lines + + } else if ( object instanceof THREE.Line ) { + + var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); + + _this.info.render.calls ++; + + // render particles + + } else if ( object instanceof THREE.ParticleSystem ) { + + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + + _this.info.render.calls ++; + _this.info.render.points += geometryGroup.__webglParticleCount; + + } + + }; + + function enableAttribute( attribute ) { + + if ( ! _enabledAttributes[ attribute ] ) { + + _gl.enableVertexAttribArray( attribute ); + _enabledAttributes[ attribute ] = true; + + } + + }; + + function disableAttributes() { + + for ( var attribute in _enabledAttributes ) { + + if ( _enabledAttributes[ attribute ] ) { + + _gl.disableVertexAttribArray( attribute ); + _enabledAttributes[ attribute ] = false; + + } + + } + + }; + + function setupMorphTargets ( material, geometryGroup, object ) { + + // set base + + var attributes = material.program.attributes; + + if ( object.morphTargetBase !== -1 && attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.morphTargetForcedOrder.length ) { + + // set forced order + + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; + + while ( m < material.numSupportedMorphTargets && m < order.length ) { + + if ( attributes[ "morphTarget" + m ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ "morphTarget" + m ] ); + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ "morphNormal" + m ] ); + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + + m ++; + } + + } else { + + // find the most influencing + + var influence, activeInfluenceIndices = []; + var influences = object.morphTargetInfluences; + var i, il = influences.length; + + for ( i = 0; i < il; i ++ ) { + + influence = influences[ i ]; + + if ( influence > 0 ) { + + activeInfluenceIndices.push( [ influence, i ] ); + + } + + } + + if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { + + activeInfluenceIndices.sort( numericalSort ); + activeInfluenceIndices.length = material.numSupportedMorphTargets; + + } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { + + activeInfluenceIndices.sort( numericalSort ); + + } else if ( activeInfluenceIndices.length === 0 ) { + + activeInfluenceIndices.push( [ 0, 0 ] ); + + }; + + var influenceIndex, m = 0; + + while ( m < material.numSupportedMorphTargets ) { + + if ( activeInfluenceIndices[ m ] ) { + + influenceIndex = activeInfluenceIndices[ m ][ 1 ]; + + if ( attributes[ "morphTarget" + m ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ "morphTarget" + m ] ); + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ "morphNormal" + m ] ); + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + + } else { + + /* + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + */ + + object.__webglMorphTargetInfluences[ m ] = 0; + + } + + m ++; + + } + + } + + // load updated influences uniform + + if ( material.program.uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + + } + + }; + + // Sorting + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return a.id - b.id; + + } + + }; + + function numericalSort ( a, b ) { + + return b[ 0 ] - a[ 0 ]; + + }; + + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var i, il, + + webglObject, object, + renderList, + + lights = scene.__lights, + fog = scene.fog; + + // reset caching for this frame + + _currentMaterialId = -1; + _lightsNeedUpdate = true; + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // update WebGL objects + + if ( this.autoUpdateObjects ) this.initWebGLObjects( scene ); + + // custom render plugins (pre pass) + + renderPlugins( this.renderPluginsPre, scene, camera ); + + // + + _this.info.render.calls = 0; + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + _this.info.render.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // set matrices for regular objects (frustum culled) + + renderList = scene.__webglObjects; + + for ( i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + webglObject.id = i; + webglObject.render = false; + + if ( object.visible ) { + + if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { + + setupMatrices( object, camera ); + + unrollBufferMaterial( webglObject ); + + webglObject.render = true; + + if ( this.sortObjects === true ) { + + if ( object.renderDepth !== null ) { + + webglObject.z = object.renderDepth; + + } else { + + _vector3.getPositionFromMatrix( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + webglObject.z = _vector3.z; + + } + + } + + } + + } + + } + + if ( this.sortObjects ) { + + renderList.sort( painterSortStable ); + + } + + // set matrices for immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + setupMatrices( object, camera ); + + unrollImmediateBufferMaterial( webglObject ); + + } + + } + + if ( scene.overrideMaterial ) { + + var material = scene.overrideMaterial; + + this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + this.setDepthTest( material.depthTest ); + this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material ); + + } else { + + var material = null; + + // opaque pass (front-to-back order) + + this.setBlending( THREE.NoBlending ); + + renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material ); + + // transparent pass (back-to-front order) + + renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material ); + + } + + // custom render plugins (post pass) + + renderPlugins( this.renderPluginsPost, scene, camera ); + + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + this.setDepthTest( true ); + this.setDepthWrite( true ); + + // _gl.finish(); + + }; + + function renderPlugins( plugins, scene, camera ) { + + if ( ! plugins.length ) return; + + for ( var i = 0, il = plugins.length; i < il; i ++ ) { + + // reset state for plugin (to start from clean slate) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = -1; + _oldDepthTest = -1; + _oldDepthWrite = -1; + _oldDoubleSided = -1; + _oldFlipSided = -1; + _currentGeometryGroupHash = -1; + _currentMaterialId = -1; + + _lightsNeedUpdate = true; + + plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); + + // reset state after plugin (anything could have changed) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = -1; + _oldDepthTest = -1; + _oldDepthWrite = -1; + _oldDoubleSided = -1; + _oldFlipSided = -1; + _currentGeometryGroupHash = -1; + _currentMaterialId = -1; + + _lightsNeedUpdate = true; + + } + + }; + + function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, buffer, material, start, end, delta; + + if ( reverse ) { + + start = renderList.length - 1; + end = -1; + delta = -1; + + } else { + + start = 0; + end = renderList.length; + delta = 1; + } + + for ( var i = start; i !== end; i += delta ) { + + webglObject = renderList[ i ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.setMaterialFaces( material ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + + } else { + + _this.renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + } + + }; + + function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, material, program; + + for ( var i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.renderImmediateObject( camera, lights, fog, material, object ); + + } + + } + + }; + + this.renderImmediateObject = function ( camera, lights, fog, material, object ) { + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryGroupHash = -1; + + _this.setMaterialFaces( material ); + + if ( object.immediateRenderCallback ) { + + object.immediateRenderCallback( program, _gl, _frustum ); + + } else { + + object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } ); + + } + + }; + + function unrollImmediateBufferMaterial ( globject ) { + + var object = globject.object, + material = object.material; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + }; + + function unrollBufferMaterial ( globject ) { + + var object = globject.object, + buffer = globject.buffer, + material, materialIndex, meshMaterial; + + meshMaterial = object.material; + + if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { + + materialIndex = buffer.materialIndex; + + material = meshMaterial.materials[ materialIndex ]; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + } else { + + material = meshMaterial; + + if ( material ) { + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + } + + } + + }; + + // Geometry splitting + + function sortFacesByMaterial ( geometry, material ) { + + var f, fl, face, materialIndex, vertices, + groupHash, hash_map = {}; + + var numMorphTargets = geometry.morphTargets.length; + var numMorphNormals = geometry.morphNormals.length; + + var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial; + + geometry.geometryGroups = {}; + + for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + face = geometry.faces[ f ]; + materialIndex = usesFaceMaterial ? face.materialIndex : 0; + + if ( hash_map[ materialIndex ] === undefined ) { + + hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; + + } + + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( geometry.geometryGroups[ groupHash ] === undefined ) { + + geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + + } + + vertices = 3; + + if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) { + + hash_map[ materialIndex ].counter += 1; + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( geometry.geometryGroups[ groupHash ] === undefined ) { + + geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + + } + + } + + geometry.geometryGroups[ groupHash ].faces3.push( f ); + geometry.geometryGroups[ groupHash ].vertices += vertices; + + } + + geometry.geometryGroupsList = []; + + for ( var g in geometry.geometryGroups ) { + + geometry.geometryGroups[ g ].id = _geometryGroupCounter ++; + + geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] ); + + } + + }; + + // Objects refresh + + this.initWebGLObjects = function ( scene ) { + + if ( !scene.__webglObjects ) { + + scene.__webglObjects = []; + scene.__webglObjectsImmediate = []; + scene.__webglSprites = []; + scene.__webglFlares = []; + + } + + while ( scene.__objectsAdded.length ) { + + addObject( scene.__objectsAdded[ 0 ], scene ); + scene.__objectsAdded.splice( 0, 1 ); + + } + + while ( scene.__objectsRemoved.length ) { + + removeObject( scene.__objectsRemoved[ 0 ], scene ); + scene.__objectsRemoved.splice( 0, 1 ); + + } + + // update must be called after objects adding / removal + + for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) { + + var object = scene.__webglObjects[ o ].object; + + // TODO: Remove this hack (WebGLRenderer refactoring) + + if ( object.__webglInit === undefined ) { + + if ( object.__webglActive !== undefined ) { + + removeObject( object, scene ); + + } + + addObject( object, scene ); + + } + + updateObject( object ); + + } + + }; + + // Objects adding + + function addObject( object, scene ) { + + var g, geometry, material, geometryGroup; + + if ( object.__webglInit === undefined ) { + + object.__webglInit = true; + + object._modelViewMatrix = new THREE.Matrix4(); + object._normalMatrix = new THREE.Matrix3(); + + if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) { + + object.geometry.__webglInit = true; + object.geometry.addEventListener( 'dispose', onGeometryDispose ); + + } + + geometry = object.geometry; + + if ( geometry === undefined ) { + + // fail silently for now + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + initDirectBuffers( geometry ); + + } else if ( object instanceof THREE.Mesh ) { + + material = object.material; + + if ( geometry.geometryGroups === undefined ) { + + sortFacesByMaterial( geometry, material ); + + } + + // create separate VBOs per geometry chunk + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + // initialise VBO on the first access + + if ( ! geometryGroup.__webglVertexBuffer ) { + + createMeshBuffers( geometryGroup ); + initMeshBuffers( geometryGroup, object ); + + geometry.verticesNeedUpdate = true; + geometry.morphTargetsNeedUpdate = true; + geometry.elementsNeedUpdate = true; + geometry.uvsNeedUpdate = true; + geometry.normalsNeedUpdate = true; + geometry.tangentsNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } + + } else if ( object instanceof THREE.Line ) { + + if ( ! geometry.__webglVertexBuffer ) { + + createLineBuffers( geometry ); + initLineBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + geometry.lineDistancesNeedUpdate = true; + + } + + } else if ( object instanceof THREE.ParticleSystem ) { + + if ( ! geometry.__webglVertexBuffer ) { + + createParticleBuffers( geometry ); + initParticleBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } + + } + + if ( object.__webglActive === undefined ) { + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( geometry instanceof THREE.Geometry ) { + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + addBuffer( scene.__webglObjects, geometryGroup, object ); + + } + + } + + } else if ( object instanceof THREE.Line || + object instanceof THREE.ParticleSystem ) { + + geometry = object.geometry; + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + addBufferImmediate( scene.__webglObjectsImmediate, object ); + + } else if ( object instanceof THREE.Sprite ) { + + scene.__webglSprites.push( object ); + + } else if ( object instanceof THREE.LensFlare ) { + + scene.__webglFlares.push( object ); + + } + + object.__webglActive = true; + + } + + }; + + function addBuffer( objlist, buffer, object ) { + + objlist.push( + { + id: null, + buffer: buffer, + object: object, + opaque: null, + transparent: null, + z: 0 + } + ); + + }; + + function addBufferImmediate( objlist, object ) { + + objlist.push( + { + id: null, + object: object, + opaque: null, + transparent: null, + z: 0 + } + ); + + }; + + // Objects updates + + function updateObject( object ) { + + var geometry = object.geometry, + geometryGroup, customAttributesDirty, material; + + if ( geometry instanceof THREE.BufferGeometry ) { + + setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic ); + + } else if ( object instanceof THREE.Mesh ) { + + // check all geometry groups + + for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { + + geometryGroup = geometry.geometryGroupsList[ i ]; + + material = getBufferMaterial( object, geometryGroup ); + + if ( geometry.buffersNeedUpdate ) { + + initMeshBuffers( geometryGroup, object ); + + } + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || + geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || + geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { + + setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material ); + + } + + } + + geometry.verticesNeedUpdate = false; + geometry.morphTargetsNeedUpdate = false; + geometry.elementsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.tangentsNeedUpdate = false; + + geometry.buffersNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } else if ( object instanceof THREE.Line ) { + + material = getBufferMaterial( object, geometry ); + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { + + setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.lineDistancesNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + + } else if ( object instanceof THREE.ParticleSystem ) { + + material = getBufferMaterial( object, geometry ); + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) { + + setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } + + }; + + // Objects updates - custom attributes check + + function areCustomAttributesDirty( material ) { + + for ( var a in material.attributes ) { + + if ( material.attributes[ a ].needsUpdate ) return true; + + } + + return false; + + }; + + function clearCustomAttributes( material ) { + + for ( var a in material.attributes ) { + + material.attributes[ a ].needsUpdate = false; + + } + + }; + + // Objects removal + + function removeObject( object, scene ) { + + if ( object instanceof THREE.Mesh || + object instanceof THREE.ParticleSystem || + object instanceof THREE.Line ) { + + removeInstances( scene.__webglObjects, object ); + + } else if ( object instanceof THREE.Sprite ) { + + removeInstancesDirect( scene.__webglSprites, object ); + + } else if ( object instanceof THREE.LensFlare ) { + + removeInstancesDirect( scene.__webglFlares, object ); + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + removeInstances( scene.__webglObjectsImmediate, object ); + + } + + delete object.__webglActive; + + }; + + function removeInstances( objlist, object ) { + + for ( var o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ].object === object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + function removeInstancesDirect( objlist, object ) { + + for ( var o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ] === object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + // Materials + + this.initMaterial = function ( material, lights, fog, object ) { + + material.addEventListener( 'dispose', onMaterialDispose ); + + var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + + if ( material instanceof THREE.MeshDepthMaterial ) { + + shaderID = 'depth'; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + shaderID = 'normal'; + + } else if ( material instanceof THREE.MeshBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + shaderID = 'lambert'; + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + shaderID = 'phong'; + + } else if ( material instanceof THREE.LineBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + shaderID = 'dashed'; + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + shaderID = 'particle_basic'; + + } + + if ( shaderID ) { + + setMaterialShaders( material, THREE.ShaderLib[ shaderID ] ); + + } + + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + maxLightCount = allocateLights( lights ); + + maxShadows = allocateShadows( lights ); + + maxBones = allocateBones( object ); + + parameters = { + + map: !!material.map, + envMap: !!material.envMap, + lightMap: !!material.lightMap, + bumpMap: !!material.bumpMap, + normalMap: !!material.normalMap, + specularMap: !!material.specularMap, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + sizeAttenuation: material.sizeAttenuation, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: this.maxMorphTargets, + maxMorphNormals: this.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: maxShadows, + shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow, + shadowMapType: this.shadowMapType, + shadowMapDebug: this.shadowMapDebug, + shadowMapCascade: this.shadowMapCascade, + + alphaTest: material.alphaTest, + metal: material.metal, + perPixel: material.perPixel, + wrapAround: material.wrapAround, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters, material.index0AttributeName ); + + var attributes = material.program.attributes; + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + var id, base = "morphTarget"; + + for ( i = 0; i < this.maxMorphTargets; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + var id, base = "morphNormal"; + + for ( i = 0; i < this.maxMorphNormals; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphNormals ++; + + } + + } + + } + + material.uniformsList = []; + + for ( u in material.uniforms ) { + + material.uniformsList.push( [ material.uniforms[ u ], u ] ); + + } + + }; + + function setMaterialShaders( material, shaders ) { + + material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms ); + material.vertexShader = shaders.vertexShader; + material.fragmentShader = shaders.fragmentShader; + + }; + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + if ( material.needsUpdate ) { + + if ( material.program ) deallocateMaterial( material ); + + _this.initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + if ( material.morphTargets ) { + + if ( ! object.__webglMorphTargetInfluences ) { + + object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + + } + + } + + var refreshMaterial = false; + + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.uniforms; + + if ( program !== _currentProgram ) { + + _gl.useProgram( program ); + _currentProgram = program; + + refreshMaterial = true; + + } + + if ( material.id !== _currentMaterialId ) { + + _currentMaterialId = material.id; + refreshMaterial = true; + + } + + if ( refreshMaterial || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + if ( camera !== _currentCamera ) _currentCamera = camera; + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( _supportsBoneTextures && object.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== null ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.boneTexture, textureUnit ); + + } + + if ( p_uniforms.boneTextureWidth !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.boneTextureWidth ); + + } + + if ( p_uniforms.boneTextureHeight !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight ); + + } + + } else { + + if ( p_uniforms.boneGlobalMatrices !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + setupLights( program, lights ); + _lightsNeedUpdate = false; + + } + + refreshUniformsLights( m_uniforms, _lights ); + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + refreshUniformsLambert( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights ); + + } + + // load common uniforms + + loadUniformsGeneric( program, material.uniformsList ); + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== null ) { + + _vector3.getPositionFromMatrix( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } + + } + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + }; + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + if ( _this.gammaInput ) { + + uniforms.diffuse.value.copyGammaToLinear( material.color ); + + } else { + + uniforms.diffuse.value = material.color; + + } + + uniforms.map.value = material.map; + uniforms.lightMap.value = material.lightMap; + uniforms.specularMap.value = material.specularMap; + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } + + if ( uvScaleMap !== undefined ) { + + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1; + + if ( _this.gammaInput ) { + + //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; + uniforms.reflectivity.value = material.reflectivity; + + } else { + + uniforms.reflectivity.value = material.reflectivity; + + } + + uniforms.refractionRatio.value = material.refractionRatio; + uniforms.combine.value = material.combine; + uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + + }; + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + }; + + function refreshUniformsDash ( uniforms, material ) { + + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + + }; + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + }; + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + }; + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.shininess.value = material.shininess; + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + uniforms.specular.value.copyGammaToLinear( material.specular ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + uniforms.specular.value = material.specular; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLambert ( uniforms, material ) { + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngleCos.value = lights.spot.anglesCos; + uniforms.spotLightExponent.value = lights.spot.exponents; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; + + }; + + function refreshUniformsShadow ( uniforms, lights ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + + uniforms.shadowMap.value[ j ] = light.shadowMap; + uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; + uniforms.shadowBias.value[ j ] = light.shadowBias; + + j ++; + + } + + } + + } + + }; + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + + } + + }; + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= _maxTextures ) { + + console.warn( "WebGLRenderer: trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + }; + + function loadUniformsGeneric ( program, uniforms ) { + + var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset; + + for ( j = 0, jl = uniforms.length; j < jl; j ++ ) { + + location = program.uniforms[ uniforms[ j ][ 1 ] ]; + if ( !location ) continue; + + uniform = uniforms[ j ][ 0 ]; + + type = uniform.type; + value = uniform.value; + + if ( type === "i" ) { // single integer + + _gl.uniform1i( location, value ); + + } else if ( type === "f" ) { // single float + + _gl.uniform1f( location, value ); + + } else if ( type === "v2" ) { // single THREE.Vector2 + + _gl.uniform2f( location, value.x, value.y ); + + } else if ( type === "v3" ) { // single THREE.Vector3 + + _gl.uniform3f( location, value.x, value.y, value.z ); + + } else if ( type === "v4" ) { // single THREE.Vector4 + + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + } else if ( type === "c" ) { // single THREE.Color + + _gl.uniform3f( location, value.r, value.g, value.b ); + + } else if ( type === "iv1" ) { // flat array of integers (JS or typed array) + + _gl.uniform1iv( location, value ); + + } else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array) + + _gl.uniform3iv( location, value ); + + } else if ( type === "fv1" ) { // flat array of floats (JS or typed array) + + _gl.uniform1fv( location, value ); + + } else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array) + + _gl.uniform3fv( location, value ); + + } else if ( type === "v2v" ) { // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 2; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + } else if ( type === "v3v" ) { // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 3; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + } else if ( type === "v4v" ) { // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 4; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ offset + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + } else if ( type === "m4") { // single THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 ); + + } + + value.flattenToArray( uniform._array ); + _gl.uniformMatrix4fv( location, false, uniform._array ); + + } else if ( type === "m4v" ) { // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + } else if ( type === "t" ) { // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( !texture ) continue; + + if ( texture.image instanceof Array && texture.image.length === 6 ) { + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + } else if ( type === "tv" ) { // array of THREE.Texture (2d) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for( i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for( i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( !texture ) continue; + + _this.setTexture( texture, textureUnit ); + + } + + } else { + + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + + } + + } + + }; + + function setupMatrices ( object, camera ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); + + }; + + // + + function setColorGamma( array, offset, color, intensitySq ) { + + array[ offset ] = color.r * color.r * intensitySq; + array[ offset + 1 ] = color.g * color.g * intensitySq; + array[ offset + 2 ] = color.b * color.b * intensitySq; + + }; + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + }; + + function setupLights ( program, lights ) { + + var l, ll, light, n, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, intensitySq, + position, + distance, + + zlights = _lights, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + if ( light.onlyShadow ) continue; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( ! light.visible ) continue; + + if ( _this.gammaInput ) { + + r += color.r * color.r; + g += color.g * color.g; + b += color.b * color.b; + + } else { + + r += color.r; + g += color.g; + b += color.b; + + } + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirCount += 1; + + if ( ! light.visible ) continue; + + _direction.getPositionFromMatrix( light.matrixWorld ); + _vector3.getPositionFromMatrix( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + // skip lights with undefined direction + // these create troubles in OpenGL (making pixel black) + + if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue; + + dirOffset = dirLength * 3; + + dirPositions[ dirOffset ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + if ( _this.gammaInput ) { + + setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + + } else { + + setColorLinear( dirColors, dirOffset, color, intensity ); + + } + + dirLength += 1; + + } else if ( light instanceof THREE.PointLight ) { + + pointCount += 1; + + if ( ! light.visible ) continue; + + pointOffset = pointLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + + } else { + + setColorLinear( pointColors, pointOffset, color, intensity ); + + } + + _vector3.getPositionFromMatrix( light.matrixWorld ); + + pointPositions[ pointOffset ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; + + pointDistances[ pointLength ] = distance; + + pointLength += 1; + + } else if ( light instanceof THREE.SpotLight ) { + + spotCount += 1; + + if ( ! light.visible ) continue; + + spotOffset = spotLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + + } else { + + setColorLinear( spotColors, spotOffset, color, intensity ); + + } + + _vector3.getPositionFromMatrix( light.matrixWorld ); + + spotPositions[ spotOffset ] = _vector3.x; + spotPositions[ spotOffset + 1 ] = _vector3.y; + spotPositions[ spotOffset + 2 ] = _vector3.z; + + spotDistances[ spotLength ] = distance; + + _direction.copy( _vector3 ); + _vector3.getPositionFromMatrix( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + spotDirections[ spotOffset ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + hemiCount += 1; + + if ( ! light.visible ) continue; + + _direction.getPositionFromMatrix( light.matrixWorld ); + _direction.normalize(); + + // skip lights with undefined direction + // these create troubles in OpenGL (making pixel black) + + if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue; + + hemiOffset = hemiLength * 3; + + hemiPositions[ hemiOffset ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; + + skyColor = light.color; + groundColor = light.groundColor; + + if ( _this.gammaInput ) { + + intensitySq = intensity * intensity; + + setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); + setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + + } else { + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + } + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + }; + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + + if ( cullFace === THREE.CullFaceNone ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + if ( cullFace === THREE.CullFaceBack ) { + + _gl.cullFace( _gl.BACK ); + + } else if ( cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + _gl.enable( _gl.CULL_FACE ); + + } + + }; + + this.setMaterialFaces = function ( material ) { + + var doubleSided = material.side === THREE.DoubleSide; + var flipSided = material.side === THREE.BackSide; + + if ( _oldDoubleSided !== doubleSided ) { + + if ( doubleSided ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + _gl.enable( _gl.CULL_FACE ); + + } + + _oldDoubleSided = doubleSided; + + } + + if ( _oldFlipSided !== flipSided ) { + + if ( flipSided ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + _oldFlipSided = flipSided; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( _oldDepthTest !== depthTest ) { + + if ( depthTest ) { + + _gl.enable( _gl.DEPTH_TEST ); + + } else { + + _gl.disable( _gl.DEPTH_TEST ); + + } + + _oldDepthTest = depthTest; + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( _oldDepthWrite !== depthWrite ) { + + _gl.depthMask( depthWrite ); + _oldDepthWrite = depthWrite; + + } + + }; + + function setLineWidth ( width ) { + + if ( width !== _oldLineWidth ) { + + _gl.lineWidth( width ); + + _oldLineWidth = width; + + } + + }; + + function setPolygonOffset ( polygonoffset, factor, units ) { + + if ( _oldPolygonOffset !== polygonoffset ) { + + if ( polygonoffset ) { + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + + } else { + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + + } + + _oldPolygonOffset = polygonoffset; + + } + + if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + + _gl.polygonOffset( factor, units ); + + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; + + } + + }; + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { + + if ( blending !== _oldBlending ) { + + if ( blending === THREE.NoBlending ) { + + _gl.disable( _gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + _gl.enable( _gl.BLEND ); + + } else { + + _gl.enable( _gl.BLEND ); + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + + } + + _oldBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + if ( blendEquation !== _oldBlendEquation ) { + + _gl.blendEquation( paramThreeToGL( blendEquation ) ); + + _oldBlendEquation = blendEquation; + + } + + if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + + _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + + _oldBlendSrc = blendSrc; + _oldBlendDst = blendDst; + + } + + } else { + + _oldBlendEquation = null; + _oldBlendSrc = null; + _oldBlendDst = null; + + } + + }; + + // Defines + + function generateDefines ( defines ) { + + var value, chunk, chunks = []; + + for ( var d in defines ) { + + value = defines[ d ]; + if ( value === false ) continue; + + chunk = "#define " + d + " " + value; + chunks.push( chunk ); + + } + + return chunks.join( "\n" ); + + }; + + // Shaders + + function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters, index0AttributeName ) { + + var p, pl, d, program, code; + var chunks = []; + + // Generate code + + if ( shaderID ) { + + chunks.push( shaderID ); + + } else { + + chunks.push( fragmentShader ); + chunks.push( vertexShader ); + + } + + for ( d in defines ) { + + chunks.push( d ); + chunks.push( defines[ d ] ); + + } + + for ( p in parameters ) { + + chunks.push( p ); + chunks.push( parameters[ p ] ); + + } + + code = chunks.join(); + + // Check if code has been already compiled + + for ( p = 0, pl = _programs.length; p < pl; p ++ ) { + + var programInfo = _programs[ p ]; + + if ( programInfo.code === code ) { + + // console.log( "Code already compiled." /*: \n\n" + code*/ ); + + programInfo.usedTimes ++; + + return programInfo.program; + + } + + } + + var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; + + } + + // console.log( "building new program " ); + + // + + var customDefines = generateDefines( defines ); + + // + + program = _gl.createProgram(); + + var prefix_vertex = [ + + "precision " + _precision + " float;", + "precision " + _precision + " int;", + + customDefines, + + _supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + "#define MAX_BONES " + parameters.maxBones, + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.skinning ? "#define USE_SKINNING" : "", + parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", + + parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", + parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", + parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + + "uniform mat4 modelMatrix;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", + "uniform vec3 cameraPosition;", + + "attribute vec3 position;", + "attribute vec3 normal;", + "attribute vec2 uv;", + "attribute vec2 uv2;", + + "#ifdef USE_COLOR", + + "attribute vec3 color;", + + "#endif", + + "#ifdef USE_MORPHTARGETS", + + "attribute vec3 morphTarget0;", + "attribute vec3 morphTarget1;", + "attribute vec3 morphTarget2;", + "attribute vec3 morphTarget3;", + + "#ifdef USE_MORPHNORMALS", + + "attribute vec3 morphNormal0;", + "attribute vec3 morphNormal1;", + "attribute vec3 morphNormal2;", + "attribute vec3 morphNormal3;", + + "#else", + + "attribute vec3 morphTarget4;", + "attribute vec3 morphTarget5;", + "attribute vec3 morphTarget6;", + "attribute vec3 morphTarget7;", + + "#endif", + + "#endif", + + "#ifdef USE_SKINNING", + + "attribute vec4 skinIndex;", + "attribute vec4 skinWeight;", + + "#endif", + + "" + + ].join("\n"); + + var prefix_fragment = [ + + "precision " + _precision + " float;", + "precision " + _precision + " int;", + + ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", + + customDefines, + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + + ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", + ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.metal ? "#define METAL" : "", + parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + "uniform mat4 viewMatrix;", + "uniform vec3 cameraPosition;", + "" + + ].join("\n"); + + var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader ); + var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader ); + + _gl.attachShader( program, glVertexShader ); + _gl.attachShader( program, glFragmentShader ); + + //Force a particular attribute to index 0. + // because potentially expensive emulation is done by browser if attribute 0 is disabled. + //And, color, for example is often automatically bound to index 0 so disabling it + if ( index0AttributeName ) { + _gl.bindAttribLocation( program, 0, index0AttributeName ); + } + + _gl.linkProgram( program ); + + if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { + + console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); + console.error( "Program Info Log: " + _gl.getProgramInfoLog( program ) ); + } + + // clean up + + _gl.deleteShader( glFragmentShader ); + _gl.deleteShader( glVertexShader ); + + // console.log( prefix_fragment + fragmentShader ); + // console.log( prefix_vertex + vertexShader ); + + program.uniforms = {}; + program.attributes = {}; + + var identifiers, u, a, i; + + // cache uniform locations + + identifiers = [ + + 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', + 'morphTargetInfluences' + + ]; + + if ( parameters.useVertexTexture ) { + + identifiers.push( 'boneTexture' ); + identifiers.push( 'boneTextureWidth' ); + identifiers.push( 'boneTextureHeight' ); + + } else { + + identifiers.push( 'boneGlobalMatrices' ); + + } + + for ( u in uniforms ) { + + identifiers.push( u ); + + } + + cacheUniformLocations( program, identifiers ); + + // cache attributes locations + + identifiers = [ + + "position", "normal", "uv", "uv2", "tangent", "color", + "skinIndex", "skinWeight", "lineDistance" + + ]; + + for ( i = 0; i < parameters.maxMorphTargets; i ++ ) { + + identifiers.push( "morphTarget" + i ); + + } + + for ( i = 0; i < parameters.maxMorphNormals; i ++ ) { + + identifiers.push( "morphNormal" + i ); + + } + + for ( a in attributes ) { + + identifiers.push( a ); + + } + + cacheAttributeLocations( program, identifiers ); + + program.id = _programs_counter ++; + + _programs.push( { program: program, code: code, usedTimes: 1 } ); + + _this.info.memory.programs = _programs.length; + + return program; + + }; + + // Shader parameters cache + + function cacheUniformLocations ( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i ++ ) { + + id = identifiers[ i ]; + program.uniforms[ id ] = _gl.getUniformLocation( program, id ); + + } + + }; + + function cacheAttributeLocations ( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i ++ ) { + + id = identifiers[ i ]; + program.attributes[ id ] = _gl.getAttribLocation( program, id ); + + } + + }; + + function addLineNumbers ( string ) { + + var chunks = string.split( "\n" ); + + for ( var i = 0, il = chunks.length; i < il; i ++ ) { + + // Chrome reports shader errors on lines + // starting counting from 1 + + chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ]; + + } + + return chunks.join( "\n" ); + + }; + + function getShader ( type, string ) { + + var shader; + + if ( type === "fragment" ) { + + shader = _gl.createShader( _gl.FRAGMENT_SHADER ); + + } else if ( type === "vertex" ) { + + shader = _gl.createShader( _gl.VERTEX_SHADER ); + + } + + _gl.shaderSource( shader, string ); + _gl.compileShader( shader ); + + if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) { + + console.error( _gl.getShaderInfoLog( shader ) ); + console.error( addLineNumbers( string ) ); + return null; + + } + + return shader; + + }; + + // Textures + + + function isPowerOfTwo ( value ) { + + return ( value & ( value - 1 ) ) === 0; + + }; + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + } + + if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + + if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + + _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); + texture.__oldAnisotropy = texture.anisotropy; + + } + + } + + }; + + this.setTexture = function ( texture, slot ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.__webglInit ) { + + texture.__webglInit = true; + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + + var image = texture.image, + isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + var mipmap, mipmaps = texture.mipmaps; + + if ( texture instanceof THREE.DataTexture ) { + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } + + } else if ( texture instanceof THREE.CompressedTexture ) { + + for( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + if ( texture.format!==THREE.RGBAFormat ) { + _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } else { + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } + + } + + } else { // regular Texture (image, video, canvas) + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + } + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width <= maxSize && image.height <= maxSize ) { + + return image; + + } + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var maxDimension = Math.max( image.width, image.height ); + var newWidth = Math.floor( image.width * maxSize / maxDimension ); + var newHeight = Math.floor( image.height * maxSize / maxDimension ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = newWidth; + canvas.height = newHeight; + + var ctx = canvas.getContext( "2d" ); + ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + + return canvas; + + } + + function setCubeTexture ( texture, slot ) { + + if ( texture.image.length === 6 ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.image.__webglTextureCube ) { + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.image.__webglTextureCube = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + + } else { + + cubeImage[ i ] = texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if( !isCompressed ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } else { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + if ( texture.format!==THREE.RGBAFormat ) { + + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } + + } + } + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + } + + } + + }; + + function setCubeTextureDynamic ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + + }; + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + + }; + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + }; + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + renderTarget.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); + + if ( isCube ) { + + renderTarget.__webglFramebuffer = []; + renderTarget.__webglRenderbuffer = []; + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + + if ( renderTarget.shareDepthFrom ) { + + renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + + } else { + + renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + + } + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + + if ( renderTarget.shareDepthFrom ) { + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } + + } else { + + setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + if ( isCube ) { + + framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTarget.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + function updateRenderTargetMipmap ( renderTarget ) { + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + }; + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + }; + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + if ( _glExtensionCompressedTextureS3TC !== undefined ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + return 0; + + }; + + // Allocations + + function allocateBones ( object ) { + + if ( _supportsBoneTextures && object && object.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.bones.length, maxBones ); + + if ( maxBones < object.bones.length ) { + + console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" ); + + } + + } + + return maxBones; + + } + + }; + + function allocateLights( lights ) { + + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( light.onlyShadow ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights }; + + }; + + function allocateShadows( lights ) { + + var maxShadows = 0; + + for ( var l = 0, ll = lights.length; l < ll; l++ ) { + + var light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + + } + + return maxShadows; + + }; + + // Initialization + + function initGL() { + + try { + + var attributes = { + alpha: _alpha, + premultipliedAlpha: _premultipliedAlpha, + antialias: _antialias, + stencil: _stencil, + preserveDrawingBuffer: _preserveDrawingBuffer + }; + + _gl = _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + + if ( _gl === null ) { + + throw 'Error creating WebGL context.'; + + } + + } catch ( error ) { + + console.error( error ); + + } + + _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); + _glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' ); + _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); + + _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + + _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + + if ( ! _glExtensionTextureFloat ) { + + console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); + + } + + if ( ! _glExtensionStandardDerivatives ) { + + console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); + + } + + if ( ! _glExtensionTextureFilterAnisotropic ) { + + console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); + + } + + if ( ! _glExtensionCompressedTextureS3TC ) { + + console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); + + } + + if ( _gl.getShaderPrecisionFormat === undefined ) { + + _gl.getShaderPrecisionFormat = function() { + + return { + "rangeMin" : 1, + "rangeMax" : 1, + "precision" : 1 + }; + + } + } + + }; + + function setDefaultGLState () { + + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); + + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); + + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + // default plugins (order is important) + + this.shadowMapPlugin = new THREE.ShadowMapPlugin(); + this.addPrePlugin( this.shadowMapPlugin ); + + this.addPostPlugin( new THREE.SpritePlugin() ); + this.addPostPlugin( new THREE.LensFlarePlugin() ); + +}; + +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.width = width; + this.height = height; + + options = options || {}; + + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.generateMipmaps = true; + + this.shareDepthFrom = null; + +}; + +THREE.WebGLRenderTarget.prototype = { + + constructor: THREE.WebGLRenderTarget, + + clone: function () { + + var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + + tmp.wrapS = this.wrapS; + tmp.wrapT = this.wrapT; + + tmp.magFilter = this.magFilter; + tmp.minFilter = this.minFilter; + + tmp.anisotropy = this.anisotropy; + + tmp.offset.copy( this.offset ); + tmp.repeat.copy( this.repeat ); + + tmp.format = this.format; + tmp.type = this.type; + + tmp.depthBuffer = this.depthBuffer; + tmp.stencilBuffer = this.stencilBuffer; + + tmp.generateMipmaps = this.generateMipmaps; + + tmp.shareDepthFrom = this.shareDepthFrom; + + return tmp; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableVertex = function () { + + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function ( vertex ) { + + this.positionWorld.copy( vertex.positionWorld ); + this.positionScreen.copy( vertex.positionScreen ); + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableFace3 = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.centroidModel = new THREE.Vector3(); + + this.normalModel = new THREE.Vector3(); + this.normalModelView = new THREE.Vector3(); + + this.vertexNormalsLength = 0; + this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + this.vertexNormalsModelView = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + this.color = null; + this.material = null; + this.uvs = [[]]; + + this.z = 0; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableObject = function () { + + this.id = 0; + + this.object = null; + this.z = 0; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableParticle = function () { + + this.id = 0; + + this.object = null; + + this.x = 0; + this.y = 0; + this.z = 0; + + this.rotation = null; + this.scale = new THREE.Vector2(); + + this.material = null; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableLine = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; + this.material = null; + + this.z = 0; + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.GeometryUtils = { + + // Merge two geometries or geometry and geometry from object (using object's transform) + + merge: function ( geometry1, object2 /* mesh | geometry */, materialIndexOffset ) { + + var matrix, normalMatrix, + vertexOffset = geometry1.vertices.length, + uvPosition = geometry1.faceVertexUvs[ 0 ].length, + geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2, + vertices1 = geometry1.vertices, + vertices2 = geometry2.vertices, + faces1 = geometry1.faces, + faces2 = geometry2.faces, + uvs1 = geometry1.faceVertexUvs[ 0 ], + uvs2 = geometry2.faceVertexUvs[ 0 ]; + + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + + if ( object2 instanceof THREE.Mesh ) { + + object2.matrixAutoUpdate && object2.updateMatrix(); + + matrix = object2.matrix; + + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix ) vertexCopy.applyMatrix4( matrix ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); + + if ( normalMatrix ) { + + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + + } + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( normalMatrix ) { + + normal.applyMatrix3( normalMatrix ).normalize(); + + } + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faceCopy.centroid.copy( face.centroid ); + + if ( matrix ) { + + faceCopy.centroid.applyMatrix4( matrix ); + + } + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + // Get random point in triangle (via barycentric coordinates) + // (uniform distribution) + // http://www.cgafaq.info/wiki/Random_Point_In_Triangle + + randomPointInTriangle: function () { + + var vector = new THREE.Vector3(); + + return function ( vectorA, vectorB, vectorC ) { + + var point = new THREE.Vector3(); + + var a = THREE.Math.random16(); + var b = THREE.Math.random16(); + + if ( ( a + b ) > 1 ) { + + a = 1 - a; + b = 1 - b; + + } + + var c = 1 - a - b; + + point.copy( vectorA ); + point.multiplyScalar( a ); + + vector.copy( vectorB ); + vector.multiplyScalar( b ); + + point.add( vector ); + + vector.copy( vectorC ); + vector.multiplyScalar( c ); + + point.add( vector ); + + return point; + + }; + + }(), + + // Get random point in face (triangle / quad) + // (uniform distribution) + + randomPointInFace: function ( face, geometry, useCachedAreas ) { + + var vA, vB, vC, vD; + + vA = geometry.vertices[ face.a ]; + vB = geometry.vertices[ face.b ]; + vC = geometry.vertices[ face.c ]; + + return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); + + }, + + // Get uniformly distributed random points in mesh + // - create array with cumulative sums of face areas + // - pick random number from 0 to total area + // - find corresponding place in area array by binary search + // - get random point in face + + randomPointsInGeometry: function ( geometry, n ) { + + var face, i, + faces = geometry.faces, + vertices = geometry.vertices, + il = faces.length, + totalArea = 0, + cumulativeAreas = [], + vA, vB, vC, vD; + + // precompute face areas + + for ( i = 0; i < il; i ++ ) { + + face = faces[ i ]; + + vA = vertices[ face.a ]; + vB = vertices[ face.b ]; + vC = vertices[ face.c ]; + + face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); + + totalArea += face._area; + + cumulativeAreas[ i ] = totalArea; + + } + + // binary search cumulative areas array + + function binarySearchIndices( value ) { + + function binarySearch( start, end ) { + + // return closest larger index + // if exact number is not found + + if ( end < start ) + return start; + + var mid = start + Math.floor( ( end - start ) / 2 ); + + if ( cumulativeAreas[ mid ] > value ) { + + return binarySearch( start, mid - 1 ); + + } else if ( cumulativeAreas[ mid ] < value ) { + + return binarySearch( mid + 1, end ); + + } else { + + return mid; + + } + + } + + var result = binarySearch( 0, cumulativeAreas.length - 1 ) + return result; + + } + + // pick random face weighted by face area + + var r, index, + result = []; + + var stats = {}; + + for ( i = 0; i < n; i ++ ) { + + r = THREE.Math.random16() * totalArea; + + index = binarySearchIndices( r ); + + result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); + + if ( ! stats[ index ] ) { + + stats[ index ] = 1; + + } else { + + stats[ index ] += 1; + + } + + } + + return result; + + }, + + // Get triangle area (half of parallelogram) + // http://mathworld.wolfram.com/TriangleArea.html + + triangleArea: function () { + + var vector1 = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function ( vectorA, vectorB, vectorC ) { + + vector1.subVectors( vectorB, vectorA ); + vector2.subVectors( vectorC, vectorA ); + vector1.cross( vector2 ); + + return 0.5 * vector1.length(); + + }; + + }(), + + // Center geometry so that 0,0,0 is in center of bounding box + + center: function ( geometry ) { + + geometry.computeBoundingBox(); + + var bb = geometry.boundingBox; + + var offset = new THREE.Vector3(); + + offset.addVectors( bb.min, bb.max ); + offset.multiplyScalar( -0.5 ); + + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); + geometry.computeBoundingBox(); + + return offset; + + }, + + triangulateQuads: function ( geometry ) { + + var i, il, j, jl; + + var faces = []; + var faceVertexUvs = []; + + for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { + + faceVertexUvs[ i ] = []; + + } + + for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { + + var face = geometry.faces[ i ]; + + faces.push( face ); + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + + } + + } + + geometry.faces = faces; + geometry.faceVertexUvs = faceVertexUvs; + + geometry.computeCentroids(); + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + + if ( geometry.hasTangents ) geometry.computeTangents(); + + } + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageUtils = { + + crossOrigin: 'anonymous', + + loadTexture: function ( url, mapping, onLoad, onError ) { + + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; + + var texture = new THREE.Texture( undefined, mapping ); + + var image = loader.load( url, function () { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } ); + + texture.image = image; + texture.sourceFile = url; + + return texture; + + }, + + loadCompressedTexture: function ( url, mapping, onLoad, onError ) { + + var texture = new THREE.CompressedTexture(); + texture.mapping = mapping; + + var request = new XMLHttpRequest(); + + request.onload = function () { + + var buffer = request.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + texture.format = dds.format; + + texture.mipmaps = dds.mipmaps; + texture.image.width = dds.width; + texture.image.height = dds.height; + + // gl.generateMipmap fails for compressed textures + // mipmaps must be embedded in the DDS file + // or texture filters must not use mipmapping + + texture.generateMipmaps = false; + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + request.onerror = onError; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + return texture; + + }, + + loadTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + images.loadCount = 0; + + var texture = new THREE.Texture(); + texture.image = images; + if ( mapping !== undefined ) texture.mapping = mapping; + + // no flipping needed for cube textures + + texture.flipY = false; + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + var cubeImage = new Image(); + images[ i ] = cubeImage; + + cubeImage.onload = function () { + + images.loadCount += 1; + + if ( images.loadCount === 6 ) { + + texture.needsUpdate = true; + if ( onLoad ) onLoad( texture ); + + } + + }; + + cubeImage.onerror = onError; + + cubeImage.crossOrigin = this.crossOrigin; + cubeImage.src = array[ i ]; + + } + + return texture; + + }, + + loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + images.loadCount = 0; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + if ( mapping !== undefined ) texture.mapping = mapping; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + texture.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + texture.generateMipmaps = false; + + var generateCubeFaceCallback = function ( rq, img ) { + + return function () { + + var buffer = rq.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + img.format = dds.format; + + img.mipmaps = dds.mipmaps; + img.width = dds.width; + img.height = dds.height; + + images.loadCount += 1; + + if ( images.loadCount === 6 ) { + + texture.format = dds.format; + texture.needsUpdate = true; + if ( onLoad ) onLoad( texture ); + + } + + } + + } + + // compressed cubemap textures as 6 separate DDS files + + if ( array instanceof Array ) { + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + var cubeImage = {}; + images[ i ] = cubeImage; + + var request = new XMLHttpRequest(); + + request.onload = generateCubeFaceCallback( request, cubeImage ); + request.onerror = onError; + + var url = array[ i ]; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + } + + // compressed cubemap texture stored in a single DDS file + + } else { + + var url = array; + var request = new XMLHttpRequest(); + + request.onload = function( ) { + + var buffer = request.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + if ( dds.isCubemap ) { + + var faces = dds.mipmaps.length / dds.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] ); + images[ f ].format = dds.format; + images[ f ].width = dds.width; + images[ f ].height = dds.height; + + } + + } + + texture.format = dds.format; + texture.needsUpdate = true; + if ( onLoad ) onLoad( texture ); + + } + + } + + request.onerror = onError; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + } + + return texture; + + }, + + loadDDSTexture: function ( url, mapping, onLoad, onError ) { + + var images = []; + images.loadCount = 0; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + if ( mapping !== undefined ) texture.mapping = mapping; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + texture.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + texture.generateMipmaps = false; + + { + var request = new XMLHttpRequest(); + + request.onload = function( ) { + + var buffer = request.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + if ( dds.isCubemap ) { + + var faces = dds.mipmaps.length / dds.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] ); + images[ f ].format = dds.format; + images[ f ].width = dds.width; + images[ f ].height = dds.height; + + } + + } + + + } else { + texture.image.width = dds.width; + texture.image.height = dds.height; + texture.mipmaps = dds.mipmaps; + } + + texture.format = dds.format; + texture.needsUpdate = true; + if ( onLoad ) onLoad( texture ); + + } + + request.onerror = onError; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + } + + return texture; + + }, + + parseDDS: function ( buffer, loadMipmaps ) { + + var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; + + // Adapted from @toji's DDS utils + // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + + // All values and structures referenced from: + // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ + + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32( value ) { + + return value.charCodeAt(0) + + (value.charCodeAt(1) << 8) + + (value.charCodeAt(2) << 16) + + (value.charCodeAt(3) << 24); + + } + + function int32ToFourCC( value ) { + + return String.fromCharCode( + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff + ); + } + + function loadARGBMip( buffer, dataOffset, width, height ) { + var dataLength = width*height*4; + var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); + var byteArray = new Uint8Array( dataLength ); + var dst = 0; + var src = 0; + for ( var y = 0; y < height; y++ ) { + for ( var x = 0; x < width; x++ ) { + var b = srcBuffer[src]; src++; + var g = srcBuffer[src]; src++; + var r = srcBuffer[src]; src++; + var a = srcBuffer[src]; src++; + byteArray[dst] = r; dst++; //r + byteArray[dst] = g; dst++; //g + byteArray[dst] = b; dst++; //b + byteArray[dst] = a; dst++; //a + } + } + return byteArray; + } + + var FOURCC_DXT1 = fourCCToInt32("DXT1"); + var FOURCC_DXT3 = fourCCToInt32("DXT3"); + var FOURCC_DXT5 = fourCCToInt32("DXT5"); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + var off_RGBBitCount = 22; + var off_RBitMask = 23; + var off_GBitMask = 24; + var off_BBitMask = 25; + var off_ABitMask = 26; + + var off_caps = 27; + var off_caps2 = 28; + var off_caps3 = 29; + var off_caps4 = 30; + + // Parse header + + var header = new Int32Array( buffer, 0, headerLengthInt ); + + if ( header[ off_magic ] !== DDS_MAGIC ) { + + console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" ); + return dds; + + } + + if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + + console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" ); + return dds; + + } + + var blockBytes; + + var fourCC = header[ off_pfFourCC ]; + + var isRGBAUncompressed = false; + + switch ( fourCC ) { + + case FOURCC_DXT1: + + blockBytes = 8; + dds.format = THREE.RGB_S3TC_DXT1_Format; + break; + + case FOURCC_DXT3: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT3_Format; + break; + + case FOURCC_DXT5: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT5_Format; + break; + + default: + + if( header[off_RGBBitCount] ==32 + && header[off_RBitMask]&0xff0000 + && header[off_GBitMask]&0xff00 + && header[off_BBitMask]&0xff + && header[off_ABitMask]&0xff000000 ) { + isRGBAUncompressed = true; + blockBytes = 64; + dds.format = THREE.RGBAFormat; + } else { + console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) ); + return dds; + } + } + + dds.mipmapCount = 1; + + if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { + + dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); + + } + + //TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc. + + dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false; + + dds.width = header[ off_width ]; + dds.height = header[ off_height ]; + + var dataOffset = header[ off_size ] + 4; + + // Extract mipmaps buffers + + var width = dds.width; + var height = dds.height; + + var faces = dds.isCubemap ? 6 : 1; + + for ( var face = 0; face < faces; face ++ ) { + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + if( isRGBAUncompressed ) { + var byteArray = loadARGBMip( buffer, dataOffset, width, height ); + var dataLength = byteArray.length; + } else { + var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; + var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + } + + var mipmap = { "data": byteArray, "width": width, "height": height }; + dds.mipmaps.push( mipmap ); + + dataOffset += dataLength; + + width = Math.max( width * 0.5, 1 ); + height = Math.max( height * 0.5, 1 ); + + } + + width = dds.width; + height = dds.height; + + } + + return dds; + + }, + + getNormalMap: function ( image, depth ) { + + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + + var cross = function ( a, b ) { + + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + + } + + var subtract = function ( a, b ) { + + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + + } + + var normalize = function ( a ) { + + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + + } + + depth = depth | 1; + + var width = image.width; + var height = image.height; + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; + + for ( var x = 0; x < width; x ++ ) { + + for ( var y = 0; y < height; y ++ ) { + + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; + + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + + var normals = []; + var num_points = points.length; + + for ( var i = 0; i < num_points; i ++ ) { + + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); + + } + + var normal = [ 0, 0, 0 ]; + + for ( var i = 0; i < normals.length; i ++ ) { + + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; + + } + + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; + + var idx = ( y * width + x ) * 4; + + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + + } + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + generateDataTexture: function ( width, height, color ) { + + var size = width * height; + var data = new Uint8Array( 3 * size ); + + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); + + for ( var i = 0; i < size; i ++ ) { + + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + + } + + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + + return texture; + + } + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Object3D(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach : function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.FontUtils = { + + faces : {}, + + // Just for now. face[weight][style] + + face : "helvetiker", + weight: "normal", + style : "normal", + size : 150, + divisions : 10, + + getFace : function() { + + return this.faces[ this.face ][ this.weight ][ this.style ]; + + }, + + loadFace : function( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + drawText : function( text ) { + + var characterPts = [], allPts = []; + + // RenderText + + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + var fontPaths = []; + + for ( i = 0; i < length; i ++ ) { + + var path = new THREE.Path(); + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; + + fontPaths.push( ret.path ); + + } + + // get the width + + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } + + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; + + //extract.paths = fontPaths; + //extract.offset = width; + + return { paths : fontPaths, offset : width }; + + }, + + + + + extractGlyphPoints : function( c, face, scale, offset, path ) { + + var pts = []; + + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + + if ( !glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i ++ ]; + + //console.log( action ); + + switch( action ) { + + case 'm': + + // Move To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + + path.moveTo( x, y ); + break; + + case 'l': + + // Line To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + path.lineTo(x,y); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * scaleY; + + path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * -scaleY; + cpx2 = outline[ i++ ] * scaleX + offset; + cpy2 = outline[ i++ ] * -scaleY; + + path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + } + + + + return { offset: glyph.ha*scale, path:path}; + } + +}; + + +THREE.FontUtils.generateShapes = function( text, parameters ) { + + // Parameters + + parameters = parameters || {}; + + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4; + + var font = parameters.font !== undefined ? parameters.font : "helvetiker"; + var weight = parameters.weight !== undefined ? parameters.weight : "normal"; + var style = parameters.style !== undefined ? parameters.style : "normal"; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + var paths = data.paths; + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + +}; + + +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + +( function( namespace ) { + + var EPSILON = 0.0000000001; + + // takes in an contour array and returns + + var process = function( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count-- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.log( "Warning, unable to triangulate polygon!" ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, <u,v,w> */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for( s = v, t = v + 1; t < nv; s++, t++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv--; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + }; + + // calculate area of the contour polygon + + var area = function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for( var p = n - 1, q = 0; q < n; p = q++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }; + + var snip = function ( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p++ ) { + + if( (p === u) || (p === v) || (p === w) ) continue; + + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y + + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; + + // see if p is inside triangle abc + + aCROSSbp = aX*bpy - aY*bpx; + cCROSSap = cX*apy - cY*apx; + bCROSScp = bX*cpy - bY*cpx; + + if ( (aCROSSbp >= -EPSILON) && (bCROSScp >= -EPSILON) && (cCROSSap >= -EPSILON) ) return false; + + } + + return true; + + }; + + + namespace.Triangulate = process; + namespace.Triangulate.area = area; + + return namespace; + +})(THREE.FontUtils); + +// To use the typeface.js face files, hook up the API +self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +THREE.typeface_js = self._typeface_js; + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] + +THREE.Curve.prototype.getPoint = function ( t ) { + + console.log( "Warning, getPoint() not implemented!" ); + return null; + +}; + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] + +THREE.Curve.prototype.getPointAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + +}; + +// Get sequence of points using getPoint( t ) + +THREE.Curve.prototype.getPoints = function ( divisions ) { + + if ( !divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + +}; + +// Get sequence of points using getPointAt( u ) + +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + + if ( !divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + +}; + +// Get total curve arc length + +THREE.Curve.prototype.getLength = function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + +}; + +// Get list of cumulative segment lengths + +THREE.Curve.prototype.getLengths = function ( divisions ) { + + if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length == divisions + 1 ) + && !this.needsUpdate) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + +}; + + +THREE.Curve.prototype.updateArcLengths = function() { + this.needsUpdate = true; + this.getLengths(); +}; + +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + continue; + + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] == targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolatation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il -1 ); + + return t; + +}; + +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent derivation, +// 2 points a small delta apart will be used to find its gradient +// which seems to give a reasonable approximation + +THREE.Curve.prototype.getTangent = function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub(pt1); + return vec.normalize(); + +}; + + +THREE.Curve.prototype.getTangentAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + +}; + + + + + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Curve.Utils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + + return -3 * p0 * (1 - t) * (1 - t) + + 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + + 6 * t * p2 * (1-t) - 3 * t * t * p2 + + 3 * t * t * p3; + }, + + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = -6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; + +THREE.CurvePath.prototype.closePath = function() { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length-1].getPoint(1); + + if (!startPoint.equals(endPoint)) { + this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + break; + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 <d + +}; + +/* +THREE.CurvePath.prototype.getTangent = function( t ) { +};*/ + + +// We cannot use the default THREE.Curve getPoint() with getLength() because in +// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath +// getPoint() depends on getLength + +THREE.CurvePath.prototype.getLength = function() { + + var lens = this.getCurveLengths(); + return lens[ lens.length - 1 ]; + +}; + +// Compute lengths and cache them +// We cannot overwrite getLengths() because UtoT mapping uses it. + +THREE.CurvePath.prototype.getCurveLengths = function() { + + // We use cache values if curves and cache array are same length + + if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) { + + return this.cacheLengths; + + }; + + // Get length of subsurve + // Push sums into cached array + + var lengths = [], sums = 0; + var i, il = this.curves.length; + + for ( i = 0; i < il; i ++ ) { + + sums += this.curves[ i ].getLength(); + lengths.push( sums ); + + } + + this.cacheLengths = lengths; + + return lengths; + +}; + + + +// Returns min and max coordinates, as well as centroid + +THREE.CurvePath.prototype.getBoundingBox = function () { + + var points = this.getPoints(); + + var maxX, maxY, maxZ; + var minX, minY, minZ; + + maxX = maxY = Number.NEGATIVE_INFINITY; + minX = minY = Number.POSITIVE_INFINITY; + + var p, i, il, sum; + + var v3 = points[0] instanceof THREE.Vector3; + + sum = v3 ? new THREE.Vector3() : new THREE.Vector2(); + + for ( i = 0, il = points.length; i < il; i ++ ) { + + p = points[ i ]; + + if ( p.x > maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; + + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; + + if ( v3 ) { + + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; + + } + + sum.add( p ); + + } + + var ret = { + + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY, + centroid: sum.divideScalar( il ) + + }; + + if ( v3 ) { + + ret.maxZ = maxZ; + ret.minZ = minZ; + + } + + return ret; + +}; + +/************************************************************** + * Create Geometries Helpers + **************************************************************/ + +/// Generate geometry from path points (for Line or ParticleSystem objects) + +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +// Generate geometry from equidistance sampling along the path + +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +THREE.CurvePath.prototype.createGeometry = function( points ) { + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < points.length; i ++ ) { + + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + + } + + return geometry; + +}; + + +/************************************************************** + * Bend / Wrap Helper Methods + **************************************************************/ + +// Wrap path / Bend modifiers? + +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + + this.bends.push( bendpath ); + +}; + +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; + + if ( !bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + + var oldPts = this.getSpacedPoints( segments ); + + var i, il; + + if ( !bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + + var bounds = this.getBoundingBox(); + + var i, il, p, oldX, oldY, xNorm; + + for ( i = 0, il = oldPts.length; i < il; i ++ ) { + + p = oldPts[ i ]; + + oldX = p.x; + oldY = p.y; + + xNorm = oldX / bounds.maxX; + + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + + xNorm = path.getUtoTmapping( xNorm, oldX ); + + // check for out of bounds? + + var pathPt = path.getPoint( xNorm ); + var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY ); + + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; + + } + + return oldPts; + +}; + + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Gyroscope = function () { + + THREE.Object3D.call( this ); + +}; + +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Gyroscope.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 ); + + this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld ); + this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject ); + + this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld ); + + + } else { + + this.matrixWorld.copy( 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 ); + + } + +}; + +THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.translationObject = new THREE.Vector3(); +THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion(); +THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion(); +THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3(); + + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ + +THREE.Path = function ( points ) { + + THREE.CurvePath.call(this); + + this.actions = []; + + if ( points ) { + + this.fromPoints( points ); + + } + +}; + +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); + +THREE.PathActions = { + + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; + +// TODO Clean up PATH API + +// Create path using straight lines to connect all points +// - vectors: array of Vector2 + +THREE.Path.prototype.fromPoints = function ( vectors ) { + + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + + this.lineTo( vectors[ v ].x, vectors[ v ].y ); + + }; + +}; + +// startPath() endPath()? + +THREE.Path.prototype.moveTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + +}; + +THREE.Path.prototype.lineTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + +}; + +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; +//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); + + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + +}; + +// FUTURE: Change the API or follow canvas API? + +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absarc(aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + }; + +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var args = Array.prototype.slice.call( arguments ); + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + this.curves.push( curve ); + + var lastPoint = curve.getPoint(1); + args.push(lastPoint.x); + args.push(lastPoint.y); + + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + + }; + +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + + if ( ! divisions ) divisions = 40; + + var points = []; + + for ( var i = 0; i < divisions; i ++ ) { + + points.push( this.getPoint( i / divisions ) ); + + //if( !this.getPoint( i / divisions ) ) throw "DIE"; + + } + + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } + + return points; + +}; + +/* Return an array of vectors based on contour of the path */ + +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + + if (this.useSpacedPoints) { + console.log('tata'); + return this.getSpacedPoints( divisions, closedPath ); + } + + divisions = divisions || 12; + + var points = []; + + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + action = item.action; + args = item.args; + + switch( action ) { + + case THREE.PathActions.MOVE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.LINE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.QUADRATIC_CURVE_TO: + + cpx = args[ 2 ]; + cpy = args[ 3 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.BEZIER_CURVE_TO: + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.CSPLINE_THRU: + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ) ; + + } + + break; + + case THREE.PathActions.ARC: + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !!args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case THREE.PathActions.ELLIPSE: + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !!args[ 6 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1]; + var EPSILON = 0.0000000001; + if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && + Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) + points.splice( points.length - 1, 1); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// Breaks path into shapes + +THREE.Path.prototype.toShapes = function( isCCW ) { + + var i, il, item, action, args; + + var subPaths = [], lastPath = new THREE.Path(); + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + args = item.args; + action = item.action; + + if ( action == THREE.PathActions.MOVE_TO ) { + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + if ( subPaths.length == 0 ) return []; + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length == 1) { + + tmpPath = subPaths[0]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? !holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + if ( holesFirst ) { + + tmpShape = new THREE.Shape(); + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); + solid = isCCW ? !solid : solid; + + if ( solid ) { + + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + tmpShape = new THREE.Shape(); + + //console.log('cw', i); + + } else { + + tmpShape.holes.push( tmpPath ); + + //console.log('ccw', i); + + } + + } + + } else { + + // Shapes first + tmpShape = undefined; + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); + solid = isCCW ? !solid : solid; + + if ( solid ) { + + if ( tmpShape ) shapes.push( tmpShape ); + + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + } else { + + tmpShape.holes.push( tmpPath ); + + } + + } + + shapes.push( tmpShape ); + + } + + //console.log("shape", shapes); + + return shapes; + +}; + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + +// Get points of holes (spaced by regular distance) + +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + if (this.useSpacedPoints) { + return this.extractAllSpacedPoints(divisions); + } + + return this.extractAllPoints(divisions); + +}; + +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; + +// Get points of shape and holes (spaced by regular distance) + +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + + return { + + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) + + }; + +}; + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Shape.Utils = { + + /* + contour - array of vector2 for contour + holes - array of array of vector2 + */ + + removeHoles: function ( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var allpoints = shape.concat(); + + /* For each isolated shape, find the closest points and break to the hole to allow triangulation */ + + + var prevShapeVert, nextShapeVert, + prevHoleVert, nextHoleVert, + holeIndex, shapeIndex, + shapeId, shapeGroup, + h, h2, + hole, shortest, d, + p, pts1, pts2, + tmpShape1, tmpShape2, + tmpHole1, tmpHole2, + verts = []; + + for ( h = 0; h < holes.length; h ++ ) { + + hole = holes[ h ]; + + /* + shapeholes[ h ].concat(); // preserves original + holes.push( hole ); + */ + + Array.prototype.push.apply( allpoints, hole ); + + shortest = Number.POSITIVE_INFINITY; + + + // Find the shortest pair of pts between shape and hole + + // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n) + // Using distanceToSquared() intead of distanceTo() should speed a little + // since running square roots operations are reduced. + + for ( h2 = 0; h2 < hole.length; h2 ++ ) { + + pts1 = hole[ h2 ]; + var dist = []; + + for ( p = 0; p < shape.length; p++ ) { + + pts2 = shape[ p ]; + d = pts1.distanceToSquared( pts2 ); + dist.push( d ); + + if ( d < shortest ) { + + shortest = d; + holeIndex = h2; + shapeIndex = p; + + } + + } + + } + + //console.log("shortest", shortest, dist); + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + var areaapts = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var areaa = THREE.FontUtils.Triangulate.area( areaapts ); + + var areabpts = [ + + hole[ holeIndex ], + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + var areab = THREE.FontUtils.Triangulate.area( areabpts ); + + var shapeOffset = 1; + var holeOffset = -1; + + var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex; + shapeIndex += shapeOffset; + holeIndex += holeOffset; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= hole.length; + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + areaapts = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var areaa2 = THREE.FontUtils.Triangulate.area( areaapts ); + + areabpts = [ + + hole[ holeIndex ], + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + var areab2 = THREE.FontUtils.Triangulate.area( areabpts ); + //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 )); + + if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) { + + // In case areas are not correct. + //console.log("USE THIS"); + + shapeIndex = oldShapeIndex; + holeIndex = oldHoleIndex ; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= hole.length; + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + } else { + + //console.log("USE THAT ") + + } + + tmpShape1 = shape.slice( 0, shapeIndex ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex ); + + // Should check orders here again? + + var trianglea = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var triangleb = [ + + hole[ holeIndex ] , + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + verts.push( trianglea ); + verts.push( triangleb ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + } + + return { + + shape:shape, /* shape with no holes */ + isolatedPts: verts, /* isolated faces */ + allpoints: allpoints + + } + + + }, + + triangulateShape: function ( contour, holes ) { + + var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes ); + + var shape = shapeWithoutHoles.shape, + allpoints = shapeWithoutHoles.allpoints, + isolatedPts = shapeWithoutHoles.isolatedPts; + + var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + //console.log( "triangles",triangles, triangles.length ); + //console.log( "allpoints",allpoints, allpoints.length ); + + var i, il, f, face, + key, index, + allPointsMap = {}, + isolatedPointsMap = {}; + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.log( "Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + // check isolated points vertices against all points map + + for ( i = 0, il = isolatedPts.length; i < il; i ++ ) { + + face = isolatedPts[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat( isolatedPts ); + + }, // end triangulate shapes + + /* + triangulate2 : function( pts, holes ) { + + // For use with Poly2Tri.js + + var allpts = pts.concat(); + var shape = []; + for (var p in pts) { + shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y)); + } + + var swctx = new js.poly2tri.SweepContext(shape); + + for (var h in holes) { + var aHole = holes[h]; + var newHole = [] + for (i in aHole) { + newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y)); + allpts.push(aHole[i]); + } + swctx.AddHole(newHole); + } + + var find; + var findIndexForPt = function (pt) { + find = new THREE.Vector2(pt.x, pt.y); + var p; + for (p=0, pl = allpts.length; p<pl; p++) { + if (allpts[p].equals(find)) return p; + } + return -1; + }; + + // triangulate + js.poly2tri.sweep.Triangulate(swctx); + + var triangles = swctx.GetTriangles(); + var tr ; + var facesPts = []; + for (var t in triangles) { + tr = triangles[t]; + facesPts.push([ + findIndexForPt(tr.GetPoint(0)), + findIndexForPt(tr.GetPoint(1)), + findIndexForPt(tr.GetPoint(2)) + ]); + } + + + // console.log(facesPts); + // console.log("triangles", triangles.length, triangles); + + // Returns array of faces with 3 element each + return facesPts; + }, +*/ + + isClockWise: function ( pts ) { + + return THREE.FontUtils.Triangulate.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2p0: function ( t, p ) { + + var k = 1 - t; + return k * k * p; + + }, + + b2p1: function ( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + }, + + b2p2: function ( t, p ) { + + return t * t * p; + + }, + + b2: function ( t, p0, p1, p2 ) { + + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + + }, + + // Cubic Bezier Functions + + b3p0: function ( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + }, + + b3p1: function ( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + }, + + b3p2: function ( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + }, + + b3p3: function ( t, p ) { + + return t * t * t * p; + + }, + + b3: function ( t, p0, p1, p2, p3 ) { + + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + + } + +}; + + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().sub(this.v1); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub(this.v1); + + return tangent.normalize(); + +}; +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + + return new THREE.Vector2( tx, ty ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); + + // returns unit vector + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + return new THREE.Vector2( tx, ty ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = (points == undefined) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var v = new THREE.Vector2(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + + return v; + +}; +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var angle; + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + + if ( this.aClockwise === true ) { + + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + + } else { + + angle = this.aStartAngle + t * deltaAngle; + + } + + var tx = this.aX + this.xRadius * Math.cos( angle ); + var ty = this.aY + this.yRadius * Math.sin( angle ); + + return new THREE.Vector2( tx, ty ); + +}; + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var r = new THREE.Vector3(); + + + r.subVectors( this.v2, this.v1 ); // diff + r.multiplyScalar( t ); + r.add( this.v1 ); + + return r; + + } + +); + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + + var pt0 = points[ c[0] ], + pt1 = points[ c[1] ], + pt2 = points[ c[2] ], + pt3 = points[ c[3] ]; + + v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight); + v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight); + v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight); + + return v; + + } + +); + + +// THREE.SplineCurve3.prototype.getTangent = function(t) { +// var v = new THREE.Vector3(); +// var c = []; +// var points = this.points, point, intPoint, weight; +// point = ( points.length - 1 ) * t; + +// intPoint = Math.floor( point ); +// weight = point - intPoint; + +// c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; +// c[ 1 ] = intPoint; +// c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; +// c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + +// var pt0 = points[ c[0] ], +// pt1 = points[ c[1] ], +// pt2 = points[ c[2] ], +// pt3 = points[ c[3] ]; + +// // t = weight; +// v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x ); +// v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y ); +// v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z ); + +// return v; + +// } +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 0 ) * t; + // This needs to be from 0-length +1 + + intPoint = Math.floor( point ); + weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + c[ 0 ] = ( intPoint - 1 ) % points.length; + c[ 1 ] = ( intPoint ) % points.length; + c[ 2 ] = ( intPoint + 1 ) % points.length; + c[ 3 ] = ( intPoint + 2 ) % points.length; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight ); + + return v; + + } + +); +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.AnimationHandler = (function() { + + var playing = []; + var library = {}; + var that = {}; + + + //--- update --- + + that.update = function( deltaTimeMS ) { + + for( var i = 0; i < playing.length; i ++ ) + playing[ i ].update( deltaTimeMS ); + + }; + + + //--- add --- + + that.addToUpdate = function( animation ) { + + if ( playing.indexOf( animation ) === -1 ) + playing.push( animation ); + + }; + + + //--- remove --- + + that.removeFromUpdate = function( animation ) { + + var index = playing.indexOf( animation ); + + if( index !== -1 ) + playing.splice( index, 1 ); + + }; + + + //--- add --- + + that.add = function( data ) { + + if ( library[ data.name ] !== undefined ) + console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." ); + + library[ data.name ] = data; + initData( data ); + + }; + + + //--- get --- + + that.get = function( name ) { + + if ( typeof name === "string" ) { + + if ( library[ name ] ) { + + return library[ name ]; + + } else { + + console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name ); + return null; + + } + + } else { + + // todo: add simple tween library + + } + + }; + + //--- parse --- + + that.parse = function( root ) { + + // setup hierarchy + + var hierarchy = []; + + if ( root instanceof THREE.SkinnedMesh ) { + + for( var b = 0; b < root.bones.length; b++ ) { + + hierarchy.push( root.bones[ b ] ); + + } + + } else { + + parseRecurseHierarchy( root, hierarchy ); + + } + + return hierarchy; + + }; + + var parseRecurseHierarchy = function( root, hierarchy ) { + + hierarchy.push( root ); + + for( var c = 0; c < root.children.length; c++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); + + } + + + //--- init data --- + + var initData = function( data ) { + + if( data.initialized === true ) + return; + + + // loop through all keys + + for( var h = 0; h < data.hierarchy.length; h ++ ) { + + for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + // remove minus times + + if( data.hierarchy[ h ].keys[ k ].time < 0 ) + data.hierarchy[ h ].keys[ k ].time = 0; + + + // create quaternions + + if( data.hierarchy[ h ].keys[ k ].rot !== undefined && + !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] ); + + } + + } + + + // prepare morph target keys + + if( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + + // get all used + + var usedMorphTargets = {}; + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = -1; + + } + + } + + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + + + // set all used on all frames + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + var influences = {}; + + for ( var morphTargetName in usedMorphTargets ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; + + } + + } + + if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + + influences[ morphTargetName ] = 0; + + } + + } + + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + + } + + } + + + // remove all keys that are on the same time + + for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + + data.hierarchy[ h ].keys.splice( k, 1 ); + k --; + + } + + } + + + // set index + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + data.hierarchy[ h ].keys[ k ].index = k; + + } + + } + + + // JIT + + var lengthInFrames = parseInt( data.length * data.fps, 10 ); + + data.JIT = {}; + data.JIT.hierarchy = []; + + for( var h = 0; h < data.hierarchy.length; h ++ ) + data.JIT.hierarchy.push( new Array( lengthInFrames ) ); + + + // done + + data.initialized = true; + + }; + + + // interpolation types + + that.LINEAR = 0; + that.CATMULLROM = 1; + that.CATMULLROM_FORWARD = 2; + + return that; + +}()); + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Animation = function ( root, name, interpolationType ) { + + this.root = root; + this.data = THREE.AnimationHandler.get( name ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + + this.currentTime = 0; + this.timeScale = 1; + + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + + this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR; + + this.points = []; + this.target = new THREE.Vector3(); + +}; + +THREE.Animation.prototype.play = function ( loop, startTimeMS ) { + + if ( this.isPlaying === false ) { + + this.isPlaying = true; + this.loop = loop !== undefined ? loop : true; + this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; + + // reset key cache + + var h, hl = this.hierarchy.length, + object; + + for ( h = 0; h < hl; h ++ ) { + + object = this.hierarchy[ h ]; + + object.matrixAutoUpdate = true; + + if ( object.animationCache === undefined ) { + + object.animationCache = {}; + object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + + } + + var prevKey = object.animationCache.prevKey; + var nextKey = object.animationCache.nextKey; + + prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ]; + prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ]; + prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ]; + + nextKey.pos = this.getNextKeyWith( "pos", h, 1 ); + nextKey.rot = this.getNextKeyWith( "rot", h, 1 ); + nextKey.scl = this.getNextKeyWith( "scl", h, 1 ); + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.addToUpdate( this ); + +}; + + +THREE.Animation.prototype.pause = function() { + + if ( this.isPaused === true ) { + + THREE.AnimationHandler.addToUpdate( this ); + + } else { + + THREE.AnimationHandler.removeFromUpdate( this ); + + } + + this.isPaused = !this.isPaused; + +}; + + +THREE.Animation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + THREE.AnimationHandler.removeFromUpdate( this ); + +}; + + +THREE.Animation.prototype.update = function ( deltaTimeMS ) { + + // early out + + if ( this.isPlaying === false ) return; + + + // vars + + var types = [ "pos", "rot", "scl" ]; + var type; + var scale; + var vector; + var prevXYZ, nextXYZ; + var prevKey, nextKey; + var object; + var animationCache; + var frame; + var JIThierarchy = this.data.JIT.hierarchy; + var currentTime, unloopedCurrentTime; + var currentPoint, forwardPoint, angle; + + + this.currentTime += deltaTimeMS * this.timeScale; + + unloopedCurrentTime = this.currentTime; + currentTime = this.currentTime = this.currentTime % this.data.length; + frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); + + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + object = this.hierarchy[ h ]; + animationCache = object.animationCache; + + // loop through pos/rot/scl + + for ( var t = 0; t < 3; t ++ ) { + + // get keys + + type = types[ t ]; + prevKey = animationCache.prevKey[ type ]; + nextKey = animationCache.nextKey[ type ]; + + // switch keys? + + if ( nextKey.time <= unloopedCurrentTime ) { + + // did we loop? + + if ( currentTime < unloopedCurrentTime ) { + + if ( this.loop ) { + + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); + + while( nextKey.time < currentTime ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + } else { + + this.stop(); + return; + + } + + } else { + + do { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } while( nextKey.time < currentTime ) + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + + object.matrixAutoUpdate = true; + object.matrixWorldNeedsUpdate = true; + + scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); + prevXYZ = prevKey[ type ]; + nextXYZ = nextKey[ type ]; + + + // check scale error + + if ( scale < 0 || scale > 1 ) { + + console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h ); + scale = scale < 0 ? 0 : 1; + + } + + // interpolate + + if ( type === "pos" ) { + + vector = object.position; + + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + this.points[ 1 ] = prevXYZ; + this.points[ 2 ] = nextXYZ; + this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + currentPoint = this.interpolateCatmullRom( this.points, scale ); + + vector.x = currentPoint[ 0 ]; + vector.y = currentPoint[ 1 ]; + vector.z = currentPoint[ 2 ]; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 ); + + this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + this.target.sub( vector ); + this.target.y = 0; + this.target.normalize(); + + angle = Math.atan2( this.target.x, this.target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale ); + + } else if ( type === "scl" ) { + + vector = object.scale; + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } + + } + + } + +}; + +// Catmull-Rom spline + +THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + +}; + +THREE.Animation.prototype.interpolate = function ( 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; + +}; + + + +// Get next key with + +THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + +}; + +// Get previous key with + +THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + +}; + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ + +THREE.KeyFrameAnimation = function( root, data, JITCompile ) { + + this.root = root; + this.data = THREE.AnimationHandler.get( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + this.JITCompile = JITCompile !== undefined ? JITCompile : true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + obj = this.hierarchy[h]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s++ ) { + + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); + + if ( next ) { + + next.apply( sid ); + + } + + } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + +}; + +// Play + +THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) { + + if( !this.isPlaying ) { + + this.isPlaying = true; + this.loop = loop !== undefined ? loop : true; + this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; + this.startTimeMs = startTimeMS; + this.startTime = 10000000; + this.endTime = -this.startTime; + + + // reset key cache + + var h, hl = this.hierarchy.length, + object, + node; + + for ( h = 0; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + if ( node.animationCache === undefined ) { + + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + + } + + var keys = this.data.hierarchy[h].keys; + + if (keys.length) { + + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; + + this.startTime = Math.min( keys[0].time, this.startTime ); + this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + + } + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.addToUpdate( this ); + +}; + + + +// Pause + +THREE.KeyFrameAnimation.prototype.pause = function() { + + if( this.isPaused ) { + + THREE.AnimationHandler.addToUpdate( this ); + + } else { + + THREE.AnimationHandler.removeFromUpdate( this ); + + } + + this.isPaused = !this.isPaused; + +}; + + +// Stop + +THREE.KeyFrameAnimation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + THREE.AnimationHandler.removeFromUpdate( this ); + + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.data.hierarchy.length; h++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + if ( node.animationCache !== undefined ) { + + var original = node.animationCache.originalMatrix; + + if( obj instanceof THREE.Bone ) { + + original.copy( obj.skinMatrix ); + obj.skinMatrix = original; + + } else { + + original.copy( obj.matrix ); + obj.matrix = original; + + } + + delete node.animationCache; + + } + + } + +}; + + +// Update + +THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) { + + // early out + + if( !this.isPlaying ) return; + + + // vars + + var prevKey, nextKey; + var object; + var node; + var frame; + var JIThierarchy = this.data.JIT.hierarchy; + var currentTime, unloopedCurrentTime; + var looped; + + + // update + + this.currentTime += deltaTimeMS * this.timeScale; + + unloopedCurrentTime = this.currentTime; + currentTime = this.currentTime = this.currentTime % this.data.length; + + // if looped around, the current time should be based on the startTime + if ( currentTime < this.startTimeMs ) { + + currentTime = this.currentTime = this.startTimeMs + currentTime; + + } + + frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); + looped = currentTime < unloopedCurrentTime; + + if ( looped && !this.loop ) { + + // Set the animation to the last keyframes and stop + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + end = keys.length-1, + obj = this.hierarchy[h]; + + if ( keys.length ) { + + for ( var s = 0; s < sids.length; s++ ) { + + var sid = sids[ s ], + prev = this.getPrevKeyWith( sid, h, end ); + + if ( prev ) { + prev.apply( sid ); + + } + + } + + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + + this.stop(); + return; + + } + + // check pre-infinity + if ( currentTime < this.startTime ) { + + return; + + } + + // update + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + var keys = node.keys, + animationCache = node.animationCache; + + // use JIT? + + if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = JIThierarchy[ h ][ frame ]; + object.matrixWorldNeedsUpdate = false; + + } else { + + object.matrix = JIThierarchy[ h ][ frame ]; + object.matrixWorldNeedsUpdate = true; + + } + + // use interpolation + + } else if ( keys.length ) { + + // make sure so original matrix and not JIT matrix is set + + if ( this.JITCompile && animationCache ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = animationCache.originalMatrix; + + } else { + + object.matrix = animationCache.originalMatrix; + + } + + } + + prevKey = animationCache.prevKey; + nextKey = animationCache.nextKey; + + if ( prevKey && nextKey ) { + + // switch keys? + + if ( nextKey.time <= unloopedCurrentTime ) { + + // did we loop? + + if ( looped && this.loop ) { + + prevKey = keys[ 0 ]; + nextKey = keys[ 1 ]; + + while ( nextKey.time < currentTime ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + } else if ( !looped ) { + + var lastIndex = keys.length - 1; + + while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + } + + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; + + } + if(nextKey.time >= currentTime) + prevKey.interpolate( nextKey, currentTime ); + else + prevKey.interpolate( nextKey, nextKey.time); + + } + + this.data.hierarchy[h].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; + + } + + } + + // update JIT? + + if ( this.JITCompile ) { + + if ( JIThierarchy[ 0 ][ frame ] === undefined ) { + + this.hierarchy[ 0 ].updateMatrixWorld( true ); + + for ( var h = 0; h < this.hierarchy.length; h++ ) { + + if( this.hierarchy[ h ] instanceof THREE.Bone ) { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone(); + + } else { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone(); + + } + + } + + } + + } + +}; + +// Get next key with + +THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; + + for ( ; key < keys.length; key++ ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ 0 ]; + +}; + +// Get previous key with + +THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; + + for ( ; key >= 0; key-- ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ keys.length - 1 ]; + +}; + +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, -1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, -1, 0 ); + cameraNX.lookAt( new THREE.Vector3( -1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, -1 ); + cameraNY.lookAt( new THREE.Vector3( 0, -1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, -1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, -1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, -1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; + + renderTarget.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); + +/* + * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog + * + * A general perpose camera, for setting FOV, Lens Focal Length, + * and switching between perspective and orthographic views easily. + * Use this only if you do not wish to manage + * both a Orthographic and Perspective Camera + * + */ + + +THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) { + + THREE.Camera.call( this ); + + this.fov = fov; + + this.left = -width / 2; + this.right = width / 2 + this.top = height / 2; + this.bottom = -height / 2; + + // We could also handle the projectionMatrix internally, but just wanted to test nested camera objects + + this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, orthoNear, orthoFar ); + this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far ); + + this.zoom = 1; + + this.toPerspective(); + + var aspect = width/height; + +}; + +THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype ); + +THREE.CombinedCamera.prototype.toPerspective = function () { + + // Switches to the Perspective Camera + + this.near = this.cameraP.near; + this.far = this.cameraP.far; + + this.cameraP.fov = this.fov / this.zoom ; + + this.cameraP.updateProjectionMatrix(); + + this.projectionMatrix = this.cameraP.projectionMatrix; + + this.inPerspectiveMode = true; + this.inOrthographicMode = false; + +}; + +THREE.CombinedCamera.prototype.toOrthographic = function () { + + // Switches to the Orthographic camera estimating viewport from Perspective + + var fov = this.fov; + var aspect = this.cameraP.aspect; + var near = this.cameraP.near; + var far = this.cameraP.far; + + // The size that we set is the mid plane of the viewing frustum + + var hyperfocus = ( near + far ) / 2; + + var halfHeight = Math.tan( fov / 2 ) * hyperfocus; + var planeHeight = 2 * halfHeight; + var planeWidth = planeHeight * aspect; + var halfWidth = planeWidth / 2; + + halfHeight /= this.zoom; + halfWidth /= this.zoom; + + this.cameraO.left = -halfWidth; + this.cameraO.right = halfWidth; + this.cameraO.top = halfHeight; + this.cameraO.bottom = -halfHeight; + + // this.cameraO.left = -farHalfWidth; + // this.cameraO.right = farHalfWidth; + // this.cameraO.top = farHalfHeight; + // this.cameraO.bottom = -farHalfHeight; + + // this.cameraO.left = this.left / this.zoom; + // this.cameraO.right = this.right / this.zoom; + // this.cameraO.top = this.top / this.zoom; + // this.cameraO.bottom = this.bottom / this.zoom; + + this.cameraO.updateProjectionMatrix(); + + this.near = this.cameraO.near; + this.far = this.cameraO.far; + this.projectionMatrix = this.cameraO.projectionMatrix; + + this.inPerspectiveMode = false; + this.inOrthographicMode = true; + +}; + + +THREE.CombinedCamera.prototype.setSize = function( width, height ) { + + this.cameraP.aspect = width / height; + this.left = -width / 2; + this.right = width / 2 + this.top = height / 2; + this.bottom = -height / 2; + +}; + + +THREE.CombinedCamera.prototype.setFov = function( fov ) { + + this.fov = fov; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +// For mantaining similar API with PerspectiveCamera + +THREE.CombinedCamera.prototype.updateProjectionMatrix = function() { + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toPerspective(); + this.toOrthographic(); + + } + +}; + +/* +* 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.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + + this.setFov( fov ); + + return fov; +}; + + +THREE.CombinedCamera.prototype.setZoom = function( zoom ) { + + this.zoom = zoom; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +THREE.CombinedCamera.prototype.toFrontView = function() { + + this.rotation.x = 0; + this.rotation.y = 0; + this.rotation.z = 0; + + // should we be modifing the matrix instead? + + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBackView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toLeftView = function() { + + this.rotation.x = 0; + this.rotation.y = - Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toRightView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toTopView = function() { + + this.rotation.x = - Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBottomView = function() { + + this.rotation.x = Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.radius = radius = radius || 50; + this.segments = segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0; + this.thetaLength = thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var i, uvs = [], + center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); + + this.vertices.push(center); + uvs.push( centerUV ); + + for ( i = 0; i <= segments; i ++ ) { + + var vertex = new THREE.Vector3(); + var segment = thetaStart + i / segments * thetaLength; + + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 1; i <= segments; i ++ ) { + + var v1 = i; + var v2 = i + 1 ; + var v3 = 0; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] ); + + } + + this.computeCentroids(); + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.width = width; + this.height = height; + this.depth = depth; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var width_half = this.width / 2; + var height_half = this.height / 2; + var depth_half = this.depth / 2; + + buildPlane( 'z', 'y', - 1, - 1, this.depth, this.height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, this.depth, this.height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, this.width, this.depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, this.width, this.depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, this.width, this.height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, this.width, this.height, - depth_half, 5 ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = scope.depthSegments; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = scope.depthSegments; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy++ ) { + + for ( ix = 0; ix < gridX; ix++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + } + + this.computeCentroids(); + this.mergeVertices(); + +}; + +THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) { + + THREE.Geometry.call( this ); + + this.radiusTop = radiusTop = radiusTop !== undefined ? radiusTop : 20; + this.radiusBottom = radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + this.height = height = height !== undefined ? height : 100; + + this.radialSegments = radialSegments = radialSegments || 8; + this.heightSegments = heightSegments = heightSegments || 1; + + this.openEnded = openEnded = openEnded !== undefined ? openEnded : false; + + var heightHalf = height / 2; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * Math.PI * 2 ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * Math.PI * 2 ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < radialSegments; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < heightSegments; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2, uv3, uv4 ] ); + + } + + } + + // top cap + + if ( openEnded === false && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.u, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( openEnded === false && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.u, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + +} + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * size: <float>, // size of the text + * height: <float>, // thickness to extrude text + * curveSegments: <int>, // number of points on the curves + * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * amount: <int>, // Amount + * + * bevelEnabled: <bool>, // turn on bevel + * bevelThickness: <float>, // how deep into text bevel goes + * bevelSize: <float>, // how far from text outline is bevel + * bevelSegments: <int>, // number of bevel layers + * + * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals + * + * material: <int> // material index for front and back faces + * extrudeMaterial: <int> // material index for extrusion and beveled faces + * uvGenerator: <Object> // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + shapes = []; + return; + } + + THREE.Geometry.call( this ); + + shapes = shapes instanceof Array ? shapes : [ shapes ]; + + this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + + this.addShapeList( shapes, options ); + + this.computeCentroids(); + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; + this.addShape( shape, options ); + } +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + var material = options.material; + var extrudeMaterial = options.extrudeMaterial; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var shapebb = this.shapebb; + //shapebb = shape.getBoundingBox(); + + + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initalization + + var ahole, h, hl; // looping of holes + var scope = this; + var bevelPoints = []; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ; + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( !vec ) console.log( "die" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length, + cont, clen = contour.length; + + + // Find directions for point movement + + var RAD_TO_DEGREES = 180 / Math.PI; + + + function getBevelVec( pt_i, pt_j, pt_k ) { + + // Algorithm 2 + + return getBevelVec2( pt_i, pt_j, pt_k ); + + } + + function getBevelVec1( pt_i, pt_j, pt_k ) { + + var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x ); + var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x ); + + if ( anglea > angleb ) { + + angleb += Math.PI * 2; + + } + + var anglec = ( anglea + angleb ) / 2; + + + //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES); + + var x = - Math.cos( anglec ); + var y = - Math.sin( anglec ); + + var vec = new THREE.Vector2( x, y ); //.normalize(); + + return vec; + + } + + function getBevelVec2( pt_i, pt_j, pt_k ) { + + var a = THREE.ExtrudeGeometry.__v1, + b = THREE.ExtrudeGeometry.__v2, + v_hat = THREE.ExtrudeGeometry.__v3, + w_hat = THREE.ExtrudeGeometry.__v4, + p = THREE.ExtrudeGeometry.__v5, + q = THREE.ExtrudeGeometry.__v6, + v, w, + v_dot_w_hat, q_sub_p_dot_w_hat, + s, intersection; + + // good reading for line-line intersection + // http://sputsoft.com/blog/2010/03/line-line-intersection.html + + // define a as vector j->i + // define b as vectot k->i + + a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y ); + b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y ); + + // get unit vectors + + v = a.normalize(); + w = b.normalize(); + + // normals from pt i + + v_hat.set( -v.y, v.x ); + w_hat.set( w.y, -w.x ); + + // pts from i + + p.copy( pt_i ).add( v_hat ); + q.copy( pt_i ).add( w_hat ); + + if ( p.equals( q ) ) { + + //console.log("Warning: lines are straight"); + return w_hat.clone(); + + } + + // Points from j, k. helps prevents points cross overover most of the time + + p.copy( pt_j ).add( v_hat ); + q.copy( pt_k ).add( w_hat ); + + v_dot_w_hat = v.dot( w_hat ); + q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat ); + + // We should not reach these conditions + + if ( v_dot_w_hat === 0 ) { + + console.log( "Either infinite or no solutions!" ); + + if ( q_sub_p_dot_w_hat === 0 ) { + + console.log( "Its finite solutions." ); + + } else { + + console.log( "Too bad, no solutions." ); + + } + + } + + s = q_sub_p_dot_w_hat / v_dot_w_hat; + + if ( s < 0 ) { + + // in case of emergecy, revert to algorithm 1. + + return getBevelVec1( pt_i, pt_j, pt_k ); + + } + + intersection = v.multiplyScalar( s ).add( p ); + + return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly + + } + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + var pt_i = contour[ i ]; + var pt_j = contour[ j ]; + var pt_k = contour[ k ]; + + contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved + //bs = bevelSize * t ; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + //vert = scalePt( contour[ i ], contourCentroid, bs, false ); + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); + + v( vert.x, vert.y, -z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); + binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + + position2.copy( extrudePts[0] ).add(normal).add(binormal); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0 ; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + + } + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( --i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c, isBottom ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + // normal, color, material + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + + var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + + var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, + stepIndex, stepsLength, contourIndex1, contourIndex2 ); + + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y; + + return [ + new THREE.Vector2( ax, ay ), + new THREE.Vector2( bx, by ), + new THREE.Vector2( cx, cy ) + ]; + + }, + + generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + + return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); + + }, + + generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, + indexA, indexB, indexC, indexD, stepIndex, stepsLength, + contourIndex1, contourIndex2 ) { + + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + az = geometry.vertices[ indexA ].z, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + bz = geometry.vertices[ indexB ].z, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y, + cz = geometry.vertices[ indexC ].z, + + dx = geometry.vertices[ indexD ].x, + dy = geometry.vertices[ indexD ].y, + dz = geometry.vertices[ indexD ].z; + + if ( Math.abs( ay - by ) < 0.01 ) { + return [ + new THREE.Vector2( ax, 1 - az ), + new THREE.Vector2( bx, 1 - bz ), + new THREE.Vector2( cx, 1 - cz ), + new THREE.Vector2( dx, 1 - dz ) + ]; + } else { + return [ + new THREE.Vector2( ay, 1 - az ), + new THREE.Vector2( by, 1 - bz ), + new THREE.Vector2( cy, 1 - cz ), + new THREE.Vector2( dy, 1 - dz ) + ]; + } + } +}; + +THREE.ExtrudeGeometry.__v1 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v2 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v3 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v4 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v5 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v6 = new THREE.Vector2(); + +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: <int> // material index for front and back faces + * uvGenerator: <Object> // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + if ( shapes instanceof Array === false ) shapes = [ shapes ]; + + this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + + this.addShapeList( shapes, options ); + + this.computeCentroids(); + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + var shapebb = this.shapebb; + + // + + var i, l, hole, s; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = !THREE.Shape.Utils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i++ ) { + + hole = holes[ i ]; + + if ( THREE.Shape.Utils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + + // Vertices + + var contour = vertices; + + for ( i = 0, l = holes.length; i < l; i++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + var cont, clen = contour.length; + + for ( i = 0; i < vlen; i++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + + } + +}; + +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://exocortex.com + */ + +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + + THREE.Geometry.call( this ); + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { + + var phi = phiStart + i * inverseSegments * phiLength; + + var c = Math.cos( phi ), + s = Math.sin( phi ); + + for ( var j = 0, jl = points.length; j < jl; j ++ ) { + + var pt = points[ j ]; + + var vertex = new THREE.Vector3(); + + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; + + this.vertices.push( vertex ); + + } + + } + + var np = points.length; + + for ( var i = 0, il = segments; i < il; i ++ ) { + + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; + + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; + + this.faces.push( new THREE.Face3( a, b, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + + } + + } + + this.mergeVertices(); + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.Geometry.call( this ); + + this.width = width; + this.height = height; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + + var ix, iz; + var width_half = width / 2; + var height_half = height / 2; + + var gridX = this.widthSegments; + var gridZ = this.heightSegments; + + var gridX1 = gridX + 1; + var gridZ1 = gridZ + 1; + + var segment_width = this.width / gridX; + var segment_height = this.height / gridZ; + + var normal = new THREE.Vector3( 0, 0, 1 ); + + for ( iz = 0; iz < gridZ1; iz ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + var y = iz * segment_height - height_half; + + this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + + } + + } + + for ( iz = 0; iz < gridZ; iz ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iz; + var b = ix + gridX1 * ( iz + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); + var d = ( ix + 1 ) + gridX1 * iz; + + var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ ); + + var face = new THREE.Face3( a, b, d ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b, c, d ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + this.computeCentroids(); + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author Kaleb Murphy + */ + +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 3, phiSegments ) : 8; + + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + + for ( i = 0; i <= phiSegments; i ++ ) { // concentric circles inside ring + + for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle + + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) ); + } + + radius += radiusStep; + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring + + var thetaSegment = i * thetaSegments; + + for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment + i; + var v2 = segment + thetaSegments + i; + var v3 = segment + thetaSegments + 1 + i; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]); + + v1 = segment + i; + v2 = segment + thetaSegments + 1 + i; + v3 = segment + 1 + i; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.radius = radius = radius || 50; + + this.widthSegments = widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + this.heightSegments = heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + this.phiStart = phiStart = phiStart !== undefined ? phiStart : 0; + this.phiLength = phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0; + this.thetaLength = thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + for ( x = 0; x <= widthSegments; x ++ ) { + + var u = x / widthSegments; + var v = y / heightSegments; + + var vertex = new THREE.Vector3(); + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + for ( y = 0; y < this.heightSegments; y ++ ) { + + for ( x = 0; x < this.widthSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + var n1 = this.vertices[ v1 ].clone().normalize(); + var n2 = this.vertices[ v2 ].clone().normalize(); + var n3 = this.vertices[ v3 ].clone().normalize(); + var n4 = this.vertices[ v4 ].clone().normalize(); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x ].clone(); + var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + + if ( Math.abs( this.vertices[ v1 ].y ) === this.radius ) { + + this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + + } else if ( Math.abs( this.vertices[ v3 ].y ) === this.radius ) { + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } else { + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: <float>, // size of the text + * height: <float>, // thickness to extrude text + * curveSegments: <int>, // number of points on the curves + * + * font: <string>, // font name + * weight: <string>, // font weight (normal, bold) + * style: <string>, // font style (normal, italics) + * + * bevelEnabled: <bool>, // turn on bevel + * bevelThickness: <float>, // how deep into text bevel goes + * bevelSize: <float>, // how far from text outline is bevel + * } + * + */ + +/* Usage Examples + + // TextGeometry wrapper + + var text3d = new TextGeometry( text, options ); + + // Complete manner + + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); + +*/ + + +THREE.TextGeometry = function ( text, parameters ) { + + parameters = parameters || {}; + + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + + // translate parameters to ExtrudeGeometry API + + parameters.amount = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); + +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 100; + this.tube = tube || 40; + this.radialSegments = radialSegments || 8; + this.tubularSegments = tubularSegments || 6; + this.arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= this.radialSegments; j ++ ) { + + for ( var i = 0; i <= this.tubularSegments; i ++ ) { + + var u = i / this.tubularSegments * this.arc; + var v = j / this.radialSegments * Math.PI * 2; + + center.x = this.radius * Math.cos( u ); + center.y = this.radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = this.tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.Vector2( i / this.tubularSegments, j / this.radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); + + } + } + + + for ( var j = 1; j <= this.radialSegments; j ++ ) { + + for ( var i = 1; i <= this.tubularSegments; i ++ ) { + + var a = ( this.tubularSegments + 1 ) * j + i - 1; + var b = ( this.tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( this.tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( this.tubularSegments + 1 ) * j + i; + + var face = new THREE.Face3( a, b, d, [ normals[ a ], normals[ b ], normals[ d ] ] ); + face.normal.add( normals[ a ] ); + face.normal.add( normals[ b ] ); + face.normal.add( normals[ d ] ); + face.normal.normalize(); + + this.faces.push( face ); + + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + + face = new THREE.Face3( b, c, d, [ normals[ b ], normals[ c ], normals[ d ] ] ); + face.normal.add( normals[ b ] ); + face.normal.add( normals[ c ] ); + face.normal.add( normals[ d ] ); + face.normal.normalize(); + + this.faces.push( face ); + + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + } + + } + + this.computeCentroids(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 100; + this.tube = tube || 40; + this.radialSegments = radialSegments || 64; + this.tubularSegments = tubularSegments || 8; + this.p = p || 2; + this.q = q || 3; + this.heightScale = heightScale || 1; + this.grid = new Array( this.radialSegments ); + + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < this.radialSegments; ++ i ) { + + this.grid[ i ] = new Array( this.tubularSegments ); + var u = i / this.radialSegments * 2 * this.p * Math.PI; + var p1 = getPos( u, this.q, this.p, this.radius, this.heightScale ); + var p2 = getPos( u + 0.01, this.q, this.p, this.radius, this.heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); + + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); + + for ( var j = 0; j < this.tubularSegments; ++ j ) { + + var v = j / this.tubularSegments * 2 * Math.PI; + var cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = this.tube * Math.sin( v ); + + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; + + this.grid[ i ][ j ] = scope.vertices.push( pos ) - 1; + + } + + } + + for ( var i = 0; i < this.radialSegments; ++ i ) { + + for ( var j = 0; j < this.tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % this.radialSegments; + var jp = ( j + 1 ) % this.tubularSegments; + + var a = this.grid[ i ][ j ]; + var b = this.grid[ ip ][ j ]; + var c = this.grid[ ip ][ jp ]; + var d = this.grid[ i ][ jp ]; + + var uva = new THREE.Vector2( i / this.radialSegments, j / this.tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / this.radialSegments, j / this.tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / this.radialSegments, ( j + 1 ) / this.tubularSegments ); + var uvd = new THREE.Vector2( i / this.radialSegments, ( j + 1 ) / this.tubularSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + function getPos( u, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function( path, segments, radius, radialSegments, closed ) { + + THREE.Geometry.call( this ); + + this.path = path; + this.segments = segments || 64; + this.radius = radius || 1; + this.radialSegments = radialSegments || 8; + this.closed = closed || false; + + this.grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = this.segments + 1, + + x, y, z, + tx, ty, tz, + u, v, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( this.path, this.segments, this.closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + + // consruct the grid + + for ( i = 0; i < numpoints; i++ ) { + + this.grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + for ( j = 0; j < this.radialSegments; j++ ) { + + v = j / this.radialSegments * 2 * Math.PI; + + cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = this.radius * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + } + + + // construct the mesh + + for ( i = 0; i < this.segments; i++ ) { + + for ( j = 0; j < this.radialSegments; j++ ) { + + ip = ( this.closed ) ? (i + 1) % this.segments : i + 1; + jp = (j + 1) % this.radialSegments; + + a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = this.grid[ ip ][ j ]; + c = this.grid[ ip ][ jp ]; + d = this.grid[ i ][ jp ]; + + uva = new THREE.Vector2( i / this.segments, j / this.radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / this.segments, j / this.radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / this.segments, ( j + 1 ) / this.radialSegments ); + uvd = new THREE.Vector2( i / this.segments, ( j + 1 ) / this.radialSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) { + + var tangent = new THREE.Vector3(), + normal = new THREE.Vector3(), + binormal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + epsilon = 0.0001, + smallest, + + tx, ty, tz, + i, u, v; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + + function initialNormal3() { + // select an initial normal vector perpenicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + smallest = tx; + normal.set( 1, 0, 0 ); + } + + if ( ty <= smallest ) { + smallest = ty; + normal.set( 0, 1, 0 ); + } + + if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i++ ) { + + normals[ i ] = normals[ i-1 ].clone(); + + binormals[ i ] = binormals[ i-1 ].clone(); + + vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); + + if ( vec.length() > epsilon ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), -1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), -1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + + theta = -theta; + + } + + for ( i = 1; i < numpoints; i++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } +}; + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) { + + THREE.Geometry.call( this ); + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) ); + + } + + var midpoints = [], p = this.vertices; + + var f = []; + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var v1 = p[ faces[ i ][ 0 ] ]; + var v2 = p[ faces[ i ][ 1 ] ]; + var v3 = p[ faces[ i ][ 2 ] ]; + + f[ i ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + + } + + for ( var i = 0, l = f.length; i < l; i ++ ) { + + subdivide(f[ i ], detail); + + } + + + // Handle case when face straddles the seam + + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + + var uvs = this.faceVertexUvs[ 0 ][ i ]; + + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; + + var max = Math.max( x0, Math.max( x1, x2 ) ); + var min = Math.min( x0, Math.min( x1, x2 ) ); + + if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + + } + + } + + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Merge vertices + + this.mergeVertices(); + + this.computeCentroids(); + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3 ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + face.centroid.add( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + that.faces.push( face ); + + var azi = azimuth( face.centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide(face, detail ) { + + var cols = Math.pow(2, detail); + var cells = Math.pow(4, detail); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { + + v[ i ] = []; + + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; + + for ( var j = 0; j <= rows; j ++) { + + if ( j == 0 && i == cols ) { + + v[ i ][ j ] = aj; + + } else { + + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + + } + + } + + } + + // Construct all of the faces. + + for ( var i = 0; i < cols ; i ++ ) { + + for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + + var k = Math.floor( j / 2 ); + + if ( j % 2 == 0 ) { + + make( + v[ i ][ k + 1], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); + + } else { + + make( + v[ i ][ k + 1 ], + v[ i + 1][ k + 1], + v[ i + 1 ][ k ] + ); + + } + + } + + } + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, -vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( -vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); + + } + + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + this.radius = radius; + this.detail = detail; + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + [ -1, t, 0 ], [ 1, t, 0 ], [ -1, -t, 0 ], [ 1, -t, 0 ], + [ 0, -1, t ], [ 0, 1, t ], [ 0, -1, -t ], [ 0, 1, -t ], + [ t, 0, -1 ], [ t, 0, 1 ], [ -t, 0, -1 ], [ -t, 0, 1 ] + ]; + + var faces = [ + [ 0, 11, 5 ], [ 0, 5, 1 ], [ 0, 1, 7 ], [ 0, 7, 10 ], [ 0, 10, 11 ], + [ 1, 5, 9 ], [ 5, 11, 4 ], [ 11, 10, 2 ], [ 10, 7, 6 ], [ 7, 1, 8 ], + [ 3, 9, 4 ], [ 3, 4, 2 ], [ 3, 2, 6 ], [ 3, 6, 8 ], [ 3, 8, 9 ], + [ 4, 9, 5 ], [ 2, 4, 11 ], [ 6, 2, 10 ], [ 8, 6, 7 ], [ 9, 8, 1 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + var vertices = [ + [ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ] + ]; + + var faces = [ + [ 0, 2, 4 ], [ 0, 4, 3 ], [ 0, 3, 5 ], [ 0, 5, 2 ], [ 1, 2, 5 ], [ 1, 5, 3 ], [ 1, 3, 4 ], [ 1, 4, 2 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + [ 1, 1, 1 ], [ -1, -1, 1 ], [ -1, 1, -1 ], [ 1, -1, -1 ] + ]; + + var faces = [ + [ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks ) { + + THREE.Geometry.call( this ); + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + var i, il, j, p; + var u, v; + + var stackCount = stacks + 1; + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j + 1; + d = (i + 1) * sliceCount + j; + + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); + + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + size = size || 1; + + var geometry = new THREE.Geometry(); + + geometry.vertices.push( + new THREE.Vector3(), new THREE.Vector3( size, 0, 0 ), + new THREE.Vector3(), new THREE.Vector3( 0, size, 0 ), + new THREE.Vector3(), new THREE.Vector3( 0, 0, size ) + ); + + geometry.colors.push( + new THREE.Color( 0xff0000 ), new THREE.Color( 0xffaa00 ), + new THREE.Color( 0x00ff00 ), new THREE.Color( 0xaaff00 ), + new THREE.Color( 0x0000ff ), new THREE.Color( 0x00aaff ) + ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://exocortex.com + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * hex - color in hex value + */ + +THREE.ArrowHelper = function ( dir, origin, length, hex ) { + + // dir is assumed to be normalized + + THREE.Object3D.call( this ); + + if ( hex === undefined ) hex = 0xffff00; + if ( length === undefined ) length = 1; + + this.position = origin; + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) ); + lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) ); + + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 ); + coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.875, 0 ) ); + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length ); + +}; + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.ArrowHelper.prototype.setDirection = function () { + + var axis = new THREE.Vector3(); + var radians; + + return function ( dir ) { + + // dir is assumed to be normalized + + if ( dir.y > 0.99999 ) { + + this.quaternion.set( 0, 0, 0, 1 ); + + } else if ( dir.y < - 0.99999 ) { + + this.quaternion.set( 1, 0, 0, 0 ); + + } else { + + axis.set( dir.z, 0, - dir.x ).normalize(); + + radians = Math.acos( dir.y ); + + this.quaternion.setFromAxisAngle( axis, radians ); + + } + + }; + +}(); + +THREE.ArrowHelper.prototype.setLength = function ( length ) { + + this.scale.set( length, length, length ); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( hex ) { + + this.line.material.color.setHex( hex ); + this.cone.material.color.setHex( hex ); + +}; + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BoxHelper = function ( object ) { + + // 5____4 + // 1/___0/| + // | 6__|_7 + // 2/___3/ + + var vertices = [ + new THREE.Vector3( 1, 1, 1 ), + new THREE.Vector3( - 1, 1, 1 ), + new THREE.Vector3( - 1, - 1, 1 ), + new THREE.Vector3( 1, - 1, 1 ), + + new THREE.Vector3( 1, 1, - 1 ), + new THREE.Vector3( - 1, 1, - 1 ), + new THREE.Vector3( - 1, - 1, - 1 ), + new THREE.Vector3( 1, - 1, - 1 ) + ]; + + this.vertices = vertices; + + // TODO: Wouldn't be nice if Line had .segments? + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + vertices[ 0 ], vertices[ 1 ], + vertices[ 1 ], vertices[ 2 ], + vertices[ 2 ], vertices[ 3 ], + vertices[ 3 ], vertices[ 0 ], + + vertices[ 4 ], vertices[ 5 ], + vertices[ 5 ], vertices[ 6 ], + vertices[ 6 ], vertices[ 7 ], + vertices[ 7 ], vertices[ 4 ], + + vertices[ 0 ], vertices[ 4 ], + vertices[ 1 ], vertices[ 5 ], + vertices[ 2 ], vertices[ 6 ], + vertices[ 3 ], vertices[ 7 ] + ); + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); + + if ( object !== undefined ) { + + this.update( object ); + + } + +}; + +THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.BoxHelper.prototype.update = function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingBox === null ) { + + geometry.computeBoundingBox(); + + } + + var min = geometry.boundingBox.min; + var max = geometry.boundingBox.max; + var vertices = this.vertices; + + vertices[ 0 ].set( max.x, max.y, max.z ); + vertices[ 1 ].set( min.x, max.y, max.z ); + vertices[ 2 ].set( min.x, min.y, max.z ); + vertices[ 3 ].set( max.x, min.y, max.z ); + vertices[ 4 ].set( max.x, max.y, min.z ); + vertices[ 5 ].set( min.x, max.y, min.z ); + vertices[ 6 ].set( min.x, min.y, min.z ); + vertices[ 7 ].set( max.x, min.y, min.z ); + + this.geometry.computeBoundingSphere(); + this.geometry.verticesNeedUpdate = true; + + this.matrixAutoUpdate = false; + this.matrixWorld = object.matrixWorld; + +}; + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +// a helper to show the world-axis-aligned bounding box for an object + +THREE.BoundingBoxHelper = function ( object, hex ) { + + var color = hex || 0x888888; + + this.object = object; + + this.box = new THREE.Box3(); + + THREE.Mesh.call( this, new THREE.CubeGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + +}; + +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.BoundingBoxHelper.prototype.update = function () { + + this.box.setFromObject( this.object ); + + this.box.size( this.scale ); + + this.box.center( this.position ); + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + + var pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); + + if ( pointMap[ id ] === undefined ) { + + pointMap[ id ] = []; + + } + + pointMap[ id ].push( geometry.vertices.length - 1 ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + + this.camera = camera; + this.matrixWorld = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = pointMap; + + this.update(); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.CameraHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); + var projector = new THREE.Projector(); + + return function () { + + var scope = this; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + camera.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, -1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", -w, -h, -1 ); + setPoint( "n2", w, -h, -1 ); + setPoint( "n3", -w, h, -1 ); + setPoint( "n4", w, h, -1 ); + + // far + + setPoint( "f1", -w, -h, 1 ); + setPoint( "f2", w, -h, 1 ); + setPoint( "f3", -w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, -1 ); + setPoint( "u2", -w * 0.7, h * 1.1, -1 ); + setPoint( "u3", 0, h * 2, -1 ); + + // cross + + setPoint( "cf1", -w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, -h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", -w, 0, -1 ); + setPoint( "cn2", w, 0, -1 ); + setPoint( "cn3", 0, -h, -1 ); + setPoint( "cn4", 0, h, -1 ); + + function setPoint( point, x, y, z ) { + + vector.set( x, y, z ); + projector.unprojectVector( vector, camera ); + + var points = scope.pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + scope.geometry.vertices[ points[ i ] ].copy( vector ); + + } + + } + + } + + this.geometry.verticesNeedUpdate = true; + + }; + +}(); + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.DirectionalLightHelper = function ( light, size ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.PlaneGeometry( size, size ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.lightPlane = new THREE.Mesh( geometry, material ); + this.add( this.lightPlane ); + + geometry = new THREE.Geometry(); + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.computeLineDistances(); + + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); + + this.update(); + +}; + +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.DirectionalLightHelper.prototype.dispose = function () { + + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); +}; + +THREE.DirectionalLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + vector.getPositionFromMatrix( this.light.matrixWorld ).negate(); + + this.lightPlane.lookAt( vector ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine.geometry.vertices[ 1 ].copy( vector ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); + + } + +}(); + + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = size || 1; + + var color = hex || 0xffff00; + + var width = linewidth || 1; + + var geometry = new THREE.Geometry(); + + var faces = this.object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.FaceNormalsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function ( object ) { + + this.object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var vertices = this.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + v1.copy( face.normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + + var idx = 2 * i; + + vertices[ idx ].copy( face.centroid ).applyMatrix4( worldMatrix ); + + vertices[ idx + 1 ].addVectors( vertices[ idx ], v1 ); + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GridHelper = function ( size, step ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); + + for ( var i = - size; i <= size; i += step ) { + + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); + + var color = i === 0 ? this.color1 : this.color2; + + geometry.colors.push( color, color, color, color ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); + + this.geometry.colorsNeedUpdate = true; + +} + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + this.colors = [ new THREE.Color(), new THREE.Color() ]; + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + for ( var i = 0, il = 8; i < il; i ++ ) { + + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + + } + + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); + + this.update(); + +}; + +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); +}; + +THREE.HemisphereLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + + this.lightSphere.lookAt( vector.getPositionFromMatrix( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; + + } + +}(); + + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLightHelper = function ( light, sphereSize ) { + + this.light = light; + this.light.updateMatrixWorld(); + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + THREE.Mesh.call( this, geometry, material ); + + this.matrixWorld = this.light.matrixWorld; + this.matrixAutoUpdate = false; + + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + + var d = light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.scale.set( d, d, d ); + + } + + this.add( this.lightDistance ); + */ + +}; + +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.PointLightHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); +}; + +THREE.PointLightHelper.prototype.update = function () { + + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + /* + var d = this.light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + + } + */ + +}; + + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.SpotLightHelper = function ( light ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, -0.5, 0 ) ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); + + this.update(); + +}; + +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); + this.cone.material.dispose(); +}; + +THREE.SpotLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function () { + + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); + + this.cone.scale.set( coneWidth, coneWidth, coneLength ); + + vector.getPositionFromMatrix( this.light.matrixWorld ); + vector2.getPositionFromMatrix( this.light.target.matrixWorld ); + + this.cone.lookAt( vector2.sub( vector ) ); + + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + }; + +}(); + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = size || 1; + + var color = hex || 0xff0000; + + var width = linewidth || 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var normal = face.vertexNormals[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = size || 1; + + var color = hex || 0x0000ff; + + var width = linewidth || 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.update(); + +}; + +THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var tangent = face.vertexTangents[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeHelper = function ( object ) { + + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; + + var keys = [ 'a', 'b', 'c', 'd' ]; + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + geometry.vertices.push( vertices[ edge[ 0 ] ] ); + geometry.vertices.push( vertices[ edge[ 1 ] ] ); + + hash[ key ] = true; + + } + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffffff } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + this.matrixWorld = object.matrixWorld; + +}; + +THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImmediateRenderObject = function () { + + THREE.Object3D.call( this ); + + this.render = function ( renderCallback ) { }; + +}; + +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlare = function ( texture, size, distance, blending, color ) { + + THREE.Object3D.call( this ); + + this.lensFlares = []; + + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; + + if( texture !== undefined ) { + + this.add( texture, size, distance, blending, color ); + + } + +}; + +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + + if( size === undefined ) size = -1; + if( distance === undefined ) distance = 0; + if( opacity === undefined ) opacity = 1; + if( color === undefined ) color = new THREE.Color( 0xffffff ); + if( blending === undefined ) blending = THREE.NormalBlending; + + distance = Math.min( distance, Math.max( 0, distance ) ); + + this.lensFlares.push( { texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending } ); // blending + +}; + + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = -this.positionScreen.x * 2; + var vecY = -this.positionScreen.y * 2; + + for( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + + + + + + + + + + + + + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + startFrame: start, + endFrame: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + var num = chunks[ 2 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = -1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = -1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "animation[" + name + "] undefined" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= -1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } + +}; + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function () { + + var _gl, _renderer, _precision, _lensFlare = {}; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + _precision = renderer.getPrecision(); + + _lensFlare.vertices = new Float32Array( 8 + 8 ); + _lensFlare.faces = new Uint16Array( 6 ); + + var i = 0; + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc. + + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0; + + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1; + + i = 0; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3; + + // buffers + + _lensFlare.vertexBuffer = _gl.createBuffer(); + _lensFlare.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + + // textures + + _lensFlare.tempTexture = _gl.createTexture(); + _lensFlare.occlusionTexture = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + + _lensFlare.hasVertexTexture = false; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision ); + + } else { + + _lensFlare.hasVertexTexture = true; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision ); + + } + + _lensFlare.attributes = {}; + _lensFlare.uniforms = {}; + + _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); + _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); + + _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); + _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); + _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); + _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); + _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" ); + _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); + _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); + _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + + }; + + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + * Then _lensFlare.update_lensFlares() is called to re-position and + * update transparency of flares. Then they are rendered. + * + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + var flares = scene.__webglFlares, + nFlares = flares.length; + + if ( ! nFlares ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + var uniforms = _lensFlare.uniforms, + attributes = _lensFlare.attributes; + + // set _lensFlare program and reset blending + + _gl.useProgram( _lensFlare.program ); + + _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); + _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms + + _gl.uniform1i( uniforms.occlusionMap, 0 ); + _gl.uniform1i( uniforms.map, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + + _gl.disable( _gl.CULL_FACE ); + _gl.depthMask( false ); + + var i, j, jl, flare, sprite; + + for ( i = 0; i < nFlares; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ) + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( _lensFlare.hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + _gl.uniform1i( uniforms.renderType, 0 ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + _gl.disable( _gl.BLEND ); + _gl.enable( _gl.DEPTH_TEST ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + _gl.uniform1i( uniforms.renderType, 1 ); + _gl.disable( _gl.DEPTH_TEST ); + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ) + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + _gl.uniform1i( uniforms.renderType, 2 ); + _gl.enable( _gl.BLEND ); + + for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform1f( uniforms.rotation, sprite.rotation ); + + _gl.uniform1f( uniforms.opacity, sprite.opacity ); + _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + _renderer.setTexture( sprite.texture, 1 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( true ); + + }; + + function createProgram ( shader, precision ) { + + var program = _gl.createProgram(); + + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + + var prefix = "precision " + precision + " float;\n"; + + _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + _gl.compileShader( fragmentShader ); + _gl.compileShader( vertexShader ); + + _gl.attachShader( program, fragmentShader ); + _gl.attachShader( program, vertexShader ); + + _gl.linkProgram( program ); + + return program; + + }; + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShadowMapPlugin = function () { + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), + + _matrixPosition = new THREE.Vector3(); + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, + renderList, + + lights = [], + k = 0, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.setDepthTest( true ); + + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps + + for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { + + light = scene.__lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + + var virtualLight; + + if ( ! light.shadowCascadeArray[ n ] ) { + + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; + + var gyro = new THREE.Gyroscope(); + gyro.position = light.shadowCascadeOffset; + + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); + + camera.add( gyro ); + + light.shadowCascadeArray[ n ] = virtualLight; + + console.log( "Created virtualLight", virtualLight ); + + } else { + + virtualLight = light.shadowCascadeArray[ n ]; + + } + + updateVirtualLight( light, n ); + + lights[ k ] = virtualLight; + k ++; + + } + + } else { + + lights[ k ] = light; + k ++; + + } + + } + + // render depth map + + for ( i = 0, il = lights.length; i < il; i ++ ) { + + light = lights[ i ]; + + if ( ! light.shadowMap ) { + + var shadowFilter = THREE.LinearFilter; + + if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowFilter = THREE.NearestFilter; + + } + + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + + light.shadowMatrix = new THREE.Matrix4(); + + } + + if ( ! light.shadowCamera ) { + + if ( light instanceof THREE.SpotLight ) { + + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + + } else { + + console.error( "Unsupported light type for shadow" ); + continue; + + } + + scene.add( light.shadowCamera ); + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + } + + if ( light.shadowCameraVisible && ! light.cameraHelper ) { + + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + light.shadowCamera.add( light.cameraHelper ); + + } + + if ( light.isVirtual && virtualLight.originalCamera == camera ) { + + updateShadowCamera( camera, light ); + + } + + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; + + shadowCamera.position.getPositionFromMatrix( light.matrixWorld ); + _matrixPosition.getPositionFromMatrix( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); + + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); + + // compute shadow matrix + + shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render shadow map + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // set object matrices & frustum culling + + renderList = scene.__webglObjects; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible && object.castShadow ) { + + if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + webglObject.render = true; + + } + + } + + } + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + // culling is overriden globally for all objects + // while rendering depth map + + // need to deal with MeshFaceMaterial somehow + // in that case just use the first of material.materials for now + // (proper solution would require to break objects by materials + // similarly to regular rendering and then set corresponding + // depth materials per each chunk instead of just once per object) + + objectMaterial = getObjectMaterial( object ); + + useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } + + } + + } + + // set matrices and render immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + }; + + function createVirtualLight( light, cascade ) { + + var virtualLight = new THREE.DirectionalLight(); + + virtualLight.isVirtual = true; + + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; + + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; + + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; + virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + + virtualLight.pointsWorld = []; + virtualLight.pointsFrustum = []; + + var pointsWorld = virtualLight.pointsWorld, + pointsFrustum = virtualLight.pointsFrustum; + + for ( var i = 0; i < 8; i ++ ) { + + pointsWorld[ i ] = new THREE.Vector3(); + pointsFrustum[ i ] = new THREE.Vector3(); + + } + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + pointsFrustum[ 0 ].set( -1, -1, nearZ ); + pointsFrustum[ 1 ].set( 1, -1, nearZ ); + pointsFrustum[ 2 ].set( -1, 1, nearZ ); + pointsFrustum[ 3 ].set( 1, 1, nearZ ); + + pointsFrustum[ 4 ].set( -1, -1, farZ ); + pointsFrustum[ 5 ].set( 1, -1, farZ ); + pointsFrustum[ 6 ].set( -1, 1, farZ ); + pointsFrustum[ 7 ].set( 1, 1, farZ ); + + return virtualLight; + + } + + // Synchronize virtual light with the original light + + function updateVirtualLight( light, cascade ) { + + var virtualLight = light.shadowCascadeArray[ cascade ]; + + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + var pointsFrustum = virtualLight.pointsFrustum; + + pointsFrustum[ 0 ].z = nearZ; + pointsFrustum[ 1 ].z = nearZ; + pointsFrustum[ 2 ].z = nearZ; + pointsFrustum[ 3 ].z = nearZ; + + pointsFrustum[ 4 ].z = farZ; + pointsFrustum[ 5 ].z = farZ; + pointsFrustum[ 6 ].z = farZ; + pointsFrustum[ 7 ].z = farZ; + + } + + // Fit shadow camera's ortho frustum to camera frustum + + function updateShadowCamera( camera, light ) { + + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; + + _min.set( Infinity, Infinity, Infinity ); + _max.set( -Infinity, -Infinity, -Infinity ); + + for ( var i = 0; i < 8; i ++ ) { + + var p = pointsWorld[ i ]; + + p.copy( pointsFrustum[ i ] ); + THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); + + p.applyMatrix4( shadowCamera.matrixWorldInverse ); + + if ( p.x < _min.x ) _min.x = p.x; + if ( p.x > _max.x ) _max.x = p.x; + + if ( p.y < _min.y ) _min.y = p.y; + if ( p.y > _max.y ) _max.y = p.y; + + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; + + } + + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; + + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; + + shadowCamera.updateProjectionMatrix(); + + } + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use for shadow maps + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; + +}; + +THREE.ShadowMapPlugin.__projector = new THREE.Projector(); + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function () { + + var _gl, _renderer, _precision, _sprite = {}; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + _precision = renderer.getPrecision(); + + _sprite.vertices = new Float32Array( 8 + 8 ); + _sprite.faces = new Uint16Array( 6 ); + + var i = 0; + + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0 + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 0 + + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1 + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 1 + + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2 + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 2 + + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3 + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 3 + + i = 0; + + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2; + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3; + + _sprite.vertexBuffer = _gl.createBuffer(); + _sprite.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW ); + + _sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ], _precision ); + + _sprite.attributes = {}; + _sprite.uniforms = {}; + + _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" ); + _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" ); + + _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" ); + _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" ); + + _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" ); + _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" ); + _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" ); + + _sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" ); + _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" ); + _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" ); + + _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" ); + _sprite.uniforms.sizeAttenuation = _gl.getUniformLocation( _sprite.program, "sizeAttenuation" ); + _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" ); + _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" ); + _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" ); + + _sprite.uniforms.fogType = _gl.getUniformLocation( _sprite.program, "fogType" ); + _sprite.uniforms.fogDensity = _gl.getUniformLocation( _sprite.program, "fogDensity" ); + _sprite.uniforms.fogNear = _gl.getUniformLocation( _sprite.program, "fogNear" ); + _sprite.uniforms.fogFar = _gl.getUniformLocation( _sprite.program, "fogFar" ); + _sprite.uniforms.fogColor = _gl.getUniformLocation( _sprite.program, "fogColor" ); + + _sprite.uniforms.alphaTest = _gl.getUniformLocation( _sprite.program, "alphaTest" ); + + }; + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + var sprites = scene.__webglSprites, + nSprites = sprites.length; + + if ( ! nSprites ) return; + + var attributes = _sprite.attributes, + uniforms = _sprite.uniforms; + + var invAspect = viewportHeight / viewportWidth; + + var halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + // setup gl + + _gl.useProgram( _sprite.program ); + + _gl.enableVertexAttribArray( attributes.position ); + _gl.enableVertexAttribArray( attributes.uv ); + + _gl.disable( _gl.CULL_FACE ); + _gl.enable( _gl.BLEND ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + + _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + _gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + if ( fog instanceof THREE.Fog ) { + + _gl.uniform1f( uniforms.fogNear, fog.near ); + _gl.uniform1f( uniforms.fogFar, fog.far ); + + _gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + _gl.uniform1f( uniforms.fogDensity, fog.density ); + + _gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + _gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + + + // update positions and sort + + var i, sprite, material, screenPosition, size, fogType, scale = []; + + for( i = 0; i < nSprites; i ++ ) { + + sprite = sprites[ i ]; + material = sprite.material; + + if ( ! sprite.visible || material.opacity === 0 ) continue; + + if ( ! material.useScreenCoordinates ) { + + sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + + } else { + + sprite.z = - sprite.position.z; + + } + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + for( i = 0; i < nSprites; i ++ ) { + + sprite = sprites[ i ]; + material = sprite.material; + + if ( ! sprite.visible || material.opacity === 0 ) continue; + + if ( material.map && material.map.image && material.map.image.width ) { + + _gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + + if ( material.useScreenCoordinates === true ) { + + _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); + _gl.uniform3f( + uniforms.screenPosition, + ( ( sprite.position.x * _renderer.devicePixelRatio ) - halfViewportWidth ) / halfViewportWidth, + ( halfViewportHeight - ( sprite.position.y * _renderer.devicePixelRatio ) ) / halfViewportHeight, + Math.max( 0, Math.min( 1, sprite.position.z ) ) + ); + + scale[ 0 ] = _renderer.devicePixelRatio; + scale[ 1 ] = _renderer.devicePixelRatio; + + } else { + + _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); + _gl.uniform1i( uniforms.sizeAttenuation, material.sizeAttenuation ? 1 : 0 ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + + scale[ 0 ] = 1; + scale[ 1 ] = 1; + + } + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } else { + + fogType = 0; + + } + + if ( oldFogType !== fogType ) { + + _gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + size = 1 / ( material.scaleByViewport ? viewportHeight : 1 ); + + scale[ 0 ] *= size * invAspect * sprite.scale.x + scale[ 1 ] *= size * sprite.scale.y; + + _gl.uniform2f( uniforms.uvScale, material.uvScale.x, material.uvScale.y ); + _gl.uniform2f( uniforms.uvOffset, material.uvOffset.x, material.uvOffset.y ); + _gl.uniform2f( uniforms.alignment, material.alignment.x, material.alignment.y ); + + _gl.uniform1f( uniforms.opacity, material.opacity ); + _gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + _gl.uniform1f( uniforms.rotation, sprite.rotation ); + _gl.uniform2fv( uniforms.scale, scale ); + + _renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + _renderer.setDepthTest( material.depthTest ); + _renderer.setDepthWrite( material.depthWrite ); + _renderer.setTexture( material.map, 0 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + + }; + + function createProgram ( shader, precision ) { + + var program = _gl.createProgram(); + + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + + var prefix = "precision " + precision + " float;\n"; + + _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + _gl.compileShader( fragmentShader ); + _gl.compileShader( vertexShader ); + + _gl.attachShader( program, fragmentShader ); + _gl.attachShader( program, vertexShader ); + + _gl.linkProgram( program ); + + return program; + + }; + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + }; + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DepthPassPlugin = function () { + + this.enabled = false; + this.renderTarget = null; + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(); + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! this.enabled ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + program, buffer, material, + webglObject, object, light, + renderList, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _renderer.setDepthTest( true ); + + // update scene + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render depth map + + _renderer.setRenderTarget( this.renderTarget ); + _renderer.clear(); + + // set object matrices & frustum culling + + renderList = scene.__webglObjects; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible ) { + + if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + webglObject.render = true; + + } + + } + + } + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + // todo: create proper depth material for particles + + if ( object instanceof THREE.ParticleSystem && !object.customDepthMaterial ) continue; + + objectMaterial = getObjectMaterial( object ); + + if ( objectMaterial ) _renderer.setMaterialFaces( object.material ); + + useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object ); + + } + + } + + } + + // set matrices and render immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + }; + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; + +}; + + +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.ShaderFlares = { + + 'lensFlareVertexTexture': { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + ].join( "\n" ) + + }, + + + 'lensFlare': { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + } + +}; + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * + */ + +THREE.ShaderSprite = { + + 'sprite': { + + vertexShader: [ + + "uniform int useScreenCoordinates;", + "uniform int sizeAttenuation;", + "uniform vec3 screenPosition;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform float rotation;", + "uniform vec2 scale;", + "uniform vec2 alignment;", + "uniform vec2 uvOffset;", + "uniform vec2 uvScale;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uvOffset + uv * uvScale;", + + "vec2 alignedPosition = position + alignment;", + + "vec2 rotatedPosition;", + "rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;", + "rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;", + + "vec4 finalPosition;", + + "if( useScreenCoordinates != 0 ) {", + + "finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );", + + "} else {", + + "finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", + "finalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );", + + "}", + + "gl_Position = finalPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 color;", + "uniform sampler2D map;", + "uniform float opacity;", + + "uniform int fogType;", + "uniform vec3 fogColor;", + "uniform float fogDensity;", + "uniform float fogNear;", + "uniform float fogFar;", + "uniform float alphaTest;", + + "varying vec2 vUV;", + + "void main() {", + + "vec4 texture = texture2D( map, vUV );", + + "if ( texture.a < alphaTest ) discard;", + + "gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", + + "if ( fogType > 0 ) {", + + "float depth = gl_FragCoord.z / gl_FragCoord.w;", + "float fogFactor = 0.0;", + + "if ( fogType == 1 ) {", + + "fogFactor = smoothstep( fogNear, fogFar, depth );", + + "} else {", + + "const float LOG2 = 1.442695;", + "float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );", + "fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", + + "}", + + "gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );", + + "}", + + "}" + + ].join( "\n" ) + + } + +}; + diff --git a/examples/WebsocketGateway/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/examples/WebsocketGateway/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb0e43e97ef81da322db26141a6aa510dfa80269 Binary files /dev/null and b/examples/WebsocketGateway/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar differ diff --git a/examples/WebsocketGateway/lib/dis/open-dis7-enumerations-classes.jar b/examples/WebsocketGateway/lib/dis/open-dis7-enumerations-classes.jar new file mode 100644 index 0000000000000000000000000000000000000000..9ec23cbbe7938a89f63ff6d68e0c95e8710cbabe Binary files /dev/null and b/examples/WebsocketGateway/lib/dis/open-dis7-enumerations-classes.jar differ diff --git a/examples/WebsocketGateway/lib/dis/open-dis7-pdus-classes.jar b/examples/WebsocketGateway/lib/dis/open-dis7-pdus-classes.jar new file mode 100644 index 0000000000000000000000000000000000000000..412908eb3e0ba29136cc3d3814a589da38af5a70 Binary files /dev/null and b/examples/WebsocketGateway/lib/dis/open-dis7-pdus-classes.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-http-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-http-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..edd09e28d804da9a95322190239a849442f17b8e Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-http-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-io-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-io-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..aaa26bd23fd9c24f0ca51b6d2a7622ce69a47668 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-io-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-security-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-security-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..a08bffa3b90d2fd93960a59c9a71d4c65b948291 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-security-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-server-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-server-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..5a39bd08a32fad318270e8c7f22b8786a39ae622 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-server-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-servlet-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-servlet-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..3f7f82aaf82445478efa30b4871d00b0b3776251 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-servlet-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-util-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-util-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..97e9836dbc7fdd792af2e4cf62e21b513e292bf6 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-util-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-webapp-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-webapp-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..c3c0e062bf776a95f00f39ca748ca22e996000c6 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-webapp-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/jetty-xml-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/jetty-xml-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..568668f64c890db870fbbe70c72de4f488fa3b81 Binary files /dev/null and b/examples/WebsocketGateway/lib/jetty-xml-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/nblibraries.properties b/examples/WebsocketGateway/lib/nblibraries.properties new file mode 100644 index 0000000000000000000000000000000000000000..b5bd56c4eac526ba8d304455e7b56213c32434b2 --- /dev/null +++ b/examples/WebsocketGateway/lib/nblibraries.properties @@ -0,0 +1,4 @@ +libs.CopyLibs.classpath=\ + ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar +libs.CopyLibs.displayName=CopyLibs Task +libs.CopyLibs.prop-version=3.0 diff --git a/examples/WebsocketGateway/lib/servlet-api-3.1.jar b/examples/WebsocketGateway/lib/servlet-api-3.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..6b14c3d267867e76c04948bb31b3de18e01412ee Binary files /dev/null and b/examples/WebsocketGateway/lib/servlet-api-3.1.jar differ diff --git a/examples/WebsocketGateway/lib/slf4j-api-1.7.5.jar b/examples/WebsocketGateway/lib/slf4j-api-1.7.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..8766455d8756ebdac09d36eea2c08db90c18b124 Binary files /dev/null and b/examples/WebsocketGateway/lib/slf4j-api-1.7.5.jar differ diff --git a/examples/WebsocketGateway/lib/slf4j-jdk14-1.7.30.jar b/examples/WebsocketGateway/lib/slf4j-jdk14-1.7.30.jar new file mode 100644 index 0000000000000000000000000000000000000000..6254c48716a076800e5441d7cca0bcfefc32e4a4 Binary files /dev/null and b/examples/WebsocketGateway/lib/slf4j-jdk14-1.7.30.jar differ diff --git a/examples/WebsocketGateway/lib/websocket/websocket-api-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/websocket/websocket-api-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..230e9a2913afce813fc4dbbbbae5fa177ded3cb4 Binary files /dev/null and b/examples/WebsocketGateway/lib/websocket/websocket-api-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/websocket/websocket-common-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/websocket/websocket-common-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..9fbebb0f8dc71d3e7a390a2d79821780cfbc2571 Binary files /dev/null and b/examples/WebsocketGateway/lib/websocket/websocket-common-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/websocket/websocket-server-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/websocket/websocket-server-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..99b63cc3d20c344e1d54b53aa4d7e6c1553cf050 Binary files /dev/null and b/examples/WebsocketGateway/lib/websocket/websocket-server-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/lib/websocket/websocket-servlet-9.4.31.v20200723.jar b/examples/WebsocketGateway/lib/websocket/websocket-servlet-9.4.31.v20200723.jar new file mode 100644 index 0000000000000000000000000000000000000000..0df406f0bb8d969988e69dd62309ad312c82b897 Binary files /dev/null and b/examples/WebsocketGateway/lib/websocket/websocket-servlet-9.4.31.v20200723.jar differ diff --git a/examples/WebsocketGateway/manifest.mf b/examples/WebsocketGateway/manifest.mf new file mode 100644 index 0000000000000000000000000000000000000000..328e8e5bc3b7f1f7bad2bc0751a933e00c801983 --- /dev/null +++ b/examples/WebsocketGateway/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/examples/WebsocketGateway/nbproject/build-impl.xml b/examples/WebsocketGateway/nbproject/build-impl.xml new file mode 100644 index 0000000000000000000000000000000000000000..697a2dc384feda9b7ce739f97d093ddf32c54220 --- /dev/null +++ b/examples/WebsocketGateway/nbproject/build-impl.xml @@ -0,0 +1,1795 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +*** GENERATED FROM project.xml - DO NOT EDIT *** +*** EDIT ../build.xml INSTEAD *** + +For the purpose of easier reading the script +is divided into following sections: + + - initialization + - compilation + - jar + - execution + - debugging + - javadoc + - test compilation + - test execution + - test debugging + - applet + - cleanup + + --> +<project xmlns:if="ant:if" xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" xmlns:unless="ant:unless" basedir=".." default="default" name="WebsocketGateway-impl"> + <fail message="Please build using Ant 1.8.0 or higher."> + <condition> + <not> + <antversion atleast="1.8.0"/> + </not> + </condition> + </fail> + <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/> + <!-- + ====================== + INITIALIZATION SECTION + ====================== + --> + <target name="-pre-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-pre-init" name="-init-private"> + <property file="nbproject/private/config.properties"/> + <property file="nbproject/private/configs/${config}.properties"/> + <property file="nbproject/private/private.properties"/> + </target> + <target name="-pre-init-libraries"> + <property location="./lib/nblibraries.properties" name="libraries.path"/> + <dirname file="${libraries.path}" property="libraries.dir.nativedirsep"/> + <pathconvert dirsep="/" property="libraries.dir"> + <path path="${libraries.dir.nativedirsep}"/> + </pathconvert> + <basename file="${libraries.path}" property="libraries.basename" suffix=".properties"/> + <available file="${libraries.dir}/${libraries.basename}-private.properties" property="private.properties.available"/> + </target> + <target depends="-pre-init-libraries" if="private.properties.available" name="-init-private-libraries"> + <loadproperties encoding="ISO-8859-1" srcfile="${libraries.dir}/${libraries.basename}-private.properties"> + <filterchain> + <replacestring from="$${base}" to="${libraries.dir}"/> + <escapeunicode/> + </filterchain> + </loadproperties> + </target> + <target depends="-pre-init,-init-private,-init-private-libraries" name="-init-libraries"> + <loadproperties encoding="ISO-8859-1" srcfile="${libraries.path}"> + <filterchain> + <replacestring from="$${base}" to="${libraries.dir}"/> + <escapeunicode/> + </filterchain> + </loadproperties> + </target> + <target depends="-pre-init,-init-private,-init-libraries" name="-init-user"> + <property file="${user.properties.file}"/> + <!-- The two properties below are usually overridden --> + <!-- by the active platform. Just a fallback. --> + <property name="default.javac.source" value="1.6"/> + <property name="default.javac.target" value="1.6"/> + </target> + <target depends="-pre-init,-init-private,-init-libraries,-init-user" name="-init-project"> + <property file="nbproject/configs/${config}.properties"/> + <property file="nbproject/project.properties"/> + </target> + <target name="-init-modules-supported"> + <condition property="modules.supported.internal" value="true"> + <not> + <matches pattern="1\.[0-8](\..*)?" string="${javac.source}"/> + </not> + </condition> + </target> + <target depends="-init-modules-supported" if="modules.supported.internal" name="-init-macrodef-modulename"> + <macrodef name="modulename" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute name="property"/> + <attribute name="sourcepath"/> + <sequential> + <loadresource property="@{property}" quiet="true"> + <javaresource classpath="@{sourcepath}" name="module-info.java" parentFirst="false"/> + <filterchain> + <stripjavacomments/> + <linecontainsregexp> + <regexp pattern="module .* \{"/> + </linecontainsregexp> + <tokenfilter> + <linetokenizer/> + <replaceregex flags="s" pattern="(\s*module\s+)(\S*)(\s*\{.*)" replace="\2"/> + </tokenfilter> + <striplinebreaks/> + </filterchain> + </loadresource> + </sequential> + </macrodef> + </target> + <target depends="-init-modules-supported,-init-macrodef-modulename" if="modules.supported.internal" name="-init-source-module-properties"> + <fail message="Java 9 support requires Ant 1.10.0 or higher."> + <condition> + <not> + <antversion atleast="1.10.0"/> + </not> + </condition> + </fail> + <j2seproject3:modulename property="module.name" sourcepath="${src.dir}"/> + <condition property="named.module.internal"> + <and> + <isset property="module.name"/> + <length length="0" string="${module.name}" when="greater"/> + </and> + </condition> + <condition property="unnamed.module.internal"> + <not> + <isset property="named.module.internal"/> + </not> + </condition> + <property name="javac.modulepath" value=""/> + <property name="run.modulepath" value="${javac.modulepath}"/> + <property name="module.build.classes.dir" value="${build.classes.dir}"/> + <property name="debug.modulepath" value="${run.modulepath}"/> + <property name="javac.upgrademodulepath" value=""/> + <property name="run.upgrademodulepath" value="${javac.upgrademodulepath}"/> + <condition else="" property="javac.systemmodulepath.cmd.line.arg" value="--system '${javac.systemmodulepath}'"> + <and> + <isset property="javac.systemmodulepath"/> + <length length="0" string="${javac.systemmodulepath}" when="greater"/> + </and> + </condition> + <property name="dist.jlink.dir" value="${dist.dir}/jlink"/> + <property name="dist.jlink.output" value="${dist.jlink.dir}/${application.title}"/> + <property name="module.name" value=""/> + </target> + <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-init-macrodef-property,-init-modules-supported" name="-do-init"> + <property name="platform.java" value="${java.home}/bin/java"/> + <available file="${manifest.file}" property="manifest.available"/> + <condition property="splashscreen.available"> + <and> + <not> + <equals arg1="${application.splash}" arg2="" trim="true"/> + </not> + <available file="${application.splash}"/> + </and> + </condition> + <condition property="main.class.available"> + <and> + <isset property="main.class"/> + <not> + <equals arg1="${main.class}" arg2="" trim="true"/> + </not> + </and> + </condition> + <condition property="profile.available"> + <and> + <isset property="javac.profile"/> + <length length="0" string="${javac.profile}" when="greater"/> + <not> + <matches pattern="1\.[0-7](\..*)?" string="${javac.source}"/> + </not> + </and> + </condition> + <condition property="do.archive"> + <or> + <not> + <istrue value="${jar.archive.disabled}"/> + </not> + <istrue value="${not.archive.disabled}"/> + </or> + </condition> + <condition property="do.archive+manifest.available"> + <and> + <isset property="manifest.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+main.class.available"> + <and> + <isset property="main.class.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+splashscreen.available"> + <and> + <isset property="splashscreen.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+profile.available"> + <and> + <isset property="profile.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="have.tests"> + <or> + <available file="${test.src.dir}"/> + </or> + </condition> + <condition property="have.sources"> + <or> + <available file="${src.dir}"/> + </or> + </condition> + <condition property="netbeans.home+have.tests"> + <and> + <isset property="netbeans.home"/> + <isset property="have.tests"/> + </and> + </condition> + <condition property="no.javadoc.preview"> + <and> + <isset property="javadoc.preview"/> + <isfalse value="${javadoc.preview}"/> + </and> + </condition> + <property name="run.jvmargs" value=""/> + <property name="run.jvmargs.ide" value=""/> + <property name="javac.compilerargs" value=""/> + <property name="work.dir" value="${basedir}"/> + <condition property="no.deps"> + <and> + <istrue value="${no.dependencies}"/> + </and> + </condition> + <property name="javac.debug" value="true"/> + <property name="javadoc.preview" value="true"/> + <property name="application.args" value=""/> + <property name="source.encoding" value="${file.encoding}"/> + <property name="runtime.encoding" value="${source.encoding}"/> + <property name="manifest.encoding" value="${source.encoding}"/> + <condition property="javadoc.encoding.used" value="${javadoc.encoding}"> + <and> + <isset property="javadoc.encoding"/> + <not> + <equals arg1="${javadoc.encoding}" arg2=""/> + </not> + </and> + </condition> + <property name="javadoc.encoding.used" value="${source.encoding}"/> + <property name="includes" value="**"/> + <property name="excludes" value=""/> + <property name="do.depend" value="false"/> + <condition property="do.depend.true"> + <istrue value="${do.depend}"/> + </condition> + <path id="endorsed.classpath.path" path="${endorsed.classpath}"/> + <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'"> + <and> + <isset property="endorsed.classpath"/> + <not> + <equals arg1="${endorsed.classpath}" arg2="" trim="true"/> + </not> + </and> + </condition> + <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}"> + <isset property="profile.available"/> + </condition> + <condition else="false" property="jdkBug6558476"> + <and> + <matches pattern="1\.[56]" string="${java.specification.version}"/> + <not> + <os family="unix"/> + </not> + </and> + </condition> + <condition else="false" property="javac.fork"> + <or> + <istrue value="${jdkBug6558476}"/> + <istrue value="${javac.external.vm}"/> + </or> + </condition> + <property name="jar.index" value="false"/> + <property name="jar.index.metainf" value="${jar.index}"/> + <property name="copylibs.rebase" value="true"/> + <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/> + <condition property="junit.available"> + <or> + <available classname="org.junit.Test" classpath="${run.test.classpath}"/> + <available classname="junit.framework.Test" classpath="${run.test.classpath}"/> + </or> + </condition> + <condition property="testng.available"> + <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/> + </condition> + <condition property="junit+testng.available"> + <and> + <istrue value="${junit.available}"/> + <istrue value="${testng.available}"/> + </and> + </condition> + <condition else="testng" property="testng.mode" value="mixed"> + <istrue value="${junit+testng.available}"/> + </condition> + <condition else="" property="testng.debug.mode" value="-mixed"> + <istrue value="${junit+testng.available}"/> + </condition> + <property name="java.failonerror" value="true"/> + </target> + <target name="-post-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init" name="-init-check"> + <fail unless="src.dir">Must set src.dir</fail> + <fail unless="test.src.dir">Must set test.src.dir</fail> + <fail unless="build.dir">Must set build.dir</fail> + <fail unless="dist.dir">Must set dist.dir</fail> + <fail unless="build.classes.dir">Must set build.classes.dir</fail> + <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail> + <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail> + <fail unless="build.test.results.dir">Must set build.test.results.dir</fail> + <fail unless="build.classes.excludes">Must set build.classes.excludes</fail> + <fail unless="dist.jar">Must set dist.jar</fail> + </target> + <target name="-init-macrodef-property"> + <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute name="name"/> + <attribute name="value"/> + <sequential> + <property name="@{name}" value="${@{value}}"/> + </sequential> + </macrodef> + </target> + <target depends="-init-ap-cmdline-properties,-init-source-module-properties" if="modules.supported.internal" name="-init-macrodef-javac-with-module"> + <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <attribute default="${javac.modulepath}" name="modulepath"/> + <attribute default="${javac.upgrademodulepath}" name="upgrademodulepath"/> + <attribute default="${javac.processorpath}" name="processorpath"/> + <attribute default="${javac.processormodulepath}" name="processormodulepath"/> + <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="${javac.debug}" name="debug"/> + <attribute default="${empty.dir}" name="sourcepath" unless:set="named.module.internal"/> + <attribute default="${src.dir}" if:set="named.module.internal" name="sourcepath"/> + <attribute default="${empty.dir}" name="gensrcdir"/> + <element name="customize" optional="true"/> + <sequential> + <condition property="warn.excludes.internal"> + <and> + <isset property="named.module.internal"/> + <length length="0" string="@{excludes}" trim="true" when="greater"/> + </and> + </condition> + <echo if:set="warn.excludes.internal" level="warning" message="The javac excludes are not supported in the JDK 9 Named Module."/> + <property location="${build.dir}/empty" name="empty.dir"/> + <mkdir dir="${empty.dir}"/> + <mkdir dir="@{apgeneratedsrcdir}"/> + <condition property="processormodulepath.set"> + <resourcecount count="0" when="greater"> + <path> + <pathelement path="@{processormodulepath}"/> + </path> + </resourcecount> + </condition> + <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}"> + <src> + <dirset dir="@{gensrcdir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </src> + <classpath> + <path path="@{classpath}"/> + </classpath> + <modulepath> + <path path="@{modulepath}"/> + </modulepath> + <upgrademodulepath> + <path path="@{upgrademodulepath}"/> + </upgrademodulepath> + <compilerarg line="${javac.systemmodulepath.cmd.line.arg}"/> + <compilerarg line="${javac.profile.cmd.line.arg}"/> + <compilerarg line="${javac.compilerargs}"/> + <compilerarg if:set="processormodulepath.set" value="--processor-module-path"/> + <compilerarg if:set="processormodulepath.set" path="@{processormodulepath}"/> + <compilerarg unless:set="processormodulepath.set" value="-processorpath"/> + <compilerarg path="@{processorpath}:${empty.dir}" unless:set="processormodulepath.set"/> + <compilerarg line="${ap.processors.internal}"/> + <compilerarg line="${annotation.processing.processor.options}"/> + <compilerarg value="-s"/> + <compilerarg path="@{apgeneratedsrcdir}"/> + <compilerarg line="${ap.proc.none.internal}"/> + <customize/> + </javac> + </sequential> + </macrodef> + </target> + <target depends="-init-ap-cmdline-properties,-init-source-module-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors" unless="modules.supported.internal"> + <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <attribute default="${javac.modulepath}" name="modulepath"/> + <attribute default="${javac.upgrademodulepath}" name="upgrademodulepath"/> + <attribute default="${javac.processorpath}" name="processorpath"/> + <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="${javac.debug}" name="debug"/> + <attribute default="${empty.dir}" name="sourcepath"/> + <attribute default="${empty.dir}" name="gensrcdir"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.dir}/empty" name="empty.dir"/> + <mkdir dir="${empty.dir}"/> + <mkdir dir="@{apgeneratedsrcdir}"/> + <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}"> + <src> + <dirset dir="@{gensrcdir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </src> + <classpath> + <path path="@{classpath}"/> + </classpath> + <compilerarg line="${endorsed.classpath.cmd.line.arg}"/> + <compilerarg line="${javac.profile.cmd.line.arg}"/> + <compilerarg line="${javac.compilerargs}"/> + <compilerarg value="-processorpath"/> + <compilerarg path="@{processorpath}:${empty.dir}"/> + <compilerarg line="${ap.processors.internal}"/> + <compilerarg line="${annotation.processing.processor.options}"/> + <compilerarg value="-s"/> + <compilerarg path="@{apgeneratedsrcdir}"/> + <compilerarg line="${ap.proc.none.internal}"/> + <customize/> + </javac> + </sequential> + </macrodef> + </target> + <target depends="-init-ap-cmdline-properties,-init-source-module-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal"> + <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <attribute default="${javac.modulepath}" name="modulepath"/> + <attribute default="${javac.upgrademodulepath}" name="upgrademodulepath"/> + <attribute default="${javac.processorpath}" name="processorpath"/> + <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="${javac.debug}" name="debug"/> + <attribute default="${empty.dir}" name="sourcepath"/> + <attribute default="${empty.dir}" name="gensrcdir"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.dir}/empty" name="empty.dir"/> + <mkdir dir="${empty.dir}"/> + <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}"> + <src> + <dirset dir="@{gensrcdir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </src> + <classpath> + <path path="@{classpath}"/> + </classpath> + <compilerarg line="${endorsed.classpath.cmd.line.arg}"/> + <compilerarg line="${javac.profile.cmd.line.arg}"/> + <compilerarg line="${javac.compilerargs}"/> + <customize/> + </javac> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-javac-with-module,-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac"> + <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <sequential> + <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}"> + <classpath> + <path path="@{classpath}"/> + </classpath> + </depend> + </sequential> + </macrodef> + <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${build.classes.dir}" name="destdir"/> + <sequential> + <fail unless="javac.includes">Must set javac.includes</fail> + <pathconvert pathsep="${line.separator}" property="javac.includes.binary"> + <path> + <filelist dir="@{destdir}" files="${javac.includes}"/> + </path> + <globmapper from="*.java" to="*.class"/> + </pathconvert> + <tempfile deleteonexit="true" property="javac.includesfile.binary"/> + <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/> + <delete> + <files includesfile="${javac.includesfile.binary}"/> + </delete> + <delete> + <fileset file="${javac.includesfile.binary}"/> + </delete> + </sequential> + </macrodef> + </target> + <target if="${junit.available}" name="-init-macrodef-junit-init"> + <condition else="false" property="nb.junit.batch" value="true"> + <and> + <istrue value="${junit.available}"/> + <not> + <isset property="test.method"/> + </not> + </and> + </condition> + <condition else="false" property="nb.junit.single" value="true"> + <and> + <istrue value="${junit.available}"/> + <isset property="test.method"/> + </and> + </condition> + </target> + <target name="-init-test-properties"> + <property name="test.binaryincludes" value="<nothing>"/> + <property name="test.binarytestincludes" value=""/> + <property name="test.binaryexcludes" value=""/> + </target> + <target depends="-init-modules-supported" if="modules.supported.internal" name="-init-macrodef-junit-prototype-with-module"> + <macrodef name="junit-prototype" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <element name="customizePrototype" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <modulepath> + <path path="${run.test.modulepath}"/> + </modulepath> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="-ea"/> + <jvmarg line="${run.test.jvmargs}"/> + <customizePrototype/> + </junit> + </sequential> + </macrodef> + </target> + <target depends="-init-modules-supported" name="-init-macrodef-junit-prototype-without-module" unless="modules.supported.internal"> + <macrodef name="junit-prototype" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <element name="customizePrototype" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="-ea"/> + <customizePrototype/> + </junit> + </sequential> + </macrodef> + </target> + <target depends="-init-test-properties,-init-macrodef-junit-prototype-with-module,-init-macrodef-junit-prototype-without-module" if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}"> + <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <j2seproject3:junit-prototype> + <customizePrototype> + <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/> + <customize/> + </customizePrototype> + </j2seproject3:junit-prototype> + </sequential> + </macrodef> + </target> + <target depends="-init-test-properties,-init-macrodef-junit-prototype-with-module,-init-macrodef-junit-prototype-without-module" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}"> + <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <j2seproject3:junit-prototype> + <customizePrototype> + <batchtest todir="${build.test.results.dir}"> + <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}"> + <filename name="@{testincludes}"/> + </fileset> + <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}"> + <filename name="${test.binarytestincludes}"/> + </fileset> + </batchtest> + <customize/> + </customizePrototype> + </j2seproject3:junit-prototype> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/> + <target if="${testng.available}" name="-init-macrodef-testng"> + <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}"> + <isset property="test.method"/> + </condition> + <union id="test.set"> + <fileset dir="${test.src.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}"> + <filename name="@{testincludes}"/> + </fileset> + </union> + <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/> + <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="WebsocketGateway" testname="TestNG tests" workingDir="${work.dir}"> + <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/> + <propertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </propertyset> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <customize/> + </testng> + </sequential> + </macrodef> + </target> + <target name="-init-macrodef-test-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <echo>No tests executed.</echo> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize/> + </j2seproject3:junit> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize/> + </j2seproject3:testng> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test"> + <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <sequential> + <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + </customize> + </j2seproject3:test-impl> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-debug-impl"> + <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customizeDebuggee" optional="true"/> + <sequential> + <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize> + <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> + <customizeDebuggee/> + </customize> + </j2seproject3:junit> + </sequential> + </macrodef> + </target> + <target if="${testng.available}" name="-init-macrodef-testng-debug"> + <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <element name="customize2" optional="true"/> + <sequential> + <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}"> + <isset property="test.method"/> + </condition> + <condition else="-suitename WebsocketGateway -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}"> + <matches pattern=".*\.xml" string="@{testClass}"/> + </condition> + <delete dir="${build.test.results.dir}" quiet="true"/> + <mkdir dir="${build.test.results.dir}"/> + <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}"> + <customizeDebuggee> + <customize2/> + <jvmarg value="-ea"/> + <arg line="${testng.debug.mode}"/> + <arg line="-d ${build.test.results.dir}"/> + <arg line="-listener org.testng.reporters.VerboseReporter"/> + <arg line="${testng.cmd.args}"/> + </customizeDebuggee> + </j2seproject3:debug> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl"> + <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <element implicit="true" name="customize2" optional="true"/> + <sequential> + <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}"> + <customize2/> + </j2seproject3:testng-debug> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit"> + <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <sequential> + <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customizeDebuggee> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + </customizeDebuggee> + </j2seproject3:test-debug-impl> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng"> + <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <sequential> + <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}"> + <customize2> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + </customize2> + </j2seproject3:testng-debug-impl> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/> + <!-- + pre NB7.2 profiling section; consider it deprecated + --> + <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/> + <target if="profiler.info.jvmargs.agent" name="-profile-pre-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="profiler.info.jvmargs.agent" name="-profile-post-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile"> + <macrodef name="resolve"> + <attribute name="name"/> + <attribute name="value"/> + <sequential> + <property name="@{name}" value="${env.@{value}}"/> + </sequential> + </macrodef> + <macrodef name="profile"> + <attribute default="${main.class}" name="classname"/> + <element name="customize" optional="true"/> + <sequential> + <property environment="env"/> + <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/> + <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}"> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="${profiler.info.jvmargs.agent}"/> + <jvmarg line="${profiler.info.jvmargs}"/> + <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/> + <arg line="${application.args}"/> + <classpath> + <path path="${run.classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check"> + <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail> + <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail> + </target> + <!-- + end of pre NB7.2 profiling section + --> + <target depends="-init-debug-args" name="-init-macrodef-nbjpda"> + <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${main.class}" name="name"/> + <attribute default="${debug.modulepath}" name="modulepath"/> + <attribute default="${debug.classpath}" name="classpath"/> + <attribute default="" name="stopclassname"/> + <sequential> + <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}"> + <modulepath> + <path path="@{modulepath}"/> + </modulepath> + <classpath> + <path path="@{classpath}"/> + </classpath> + </nbjpdastart> + </sequential> + </macrodef> + <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${build.classes.dir}" name="dir"/> + <sequential> + <nbjpdareload> + <fileset dir="@{dir}" includes="${fix.classes}"> + <include name="${fix.includes}*.class"/> + </fileset> + </nbjpdareload> + </sequential> + </macrodef> + </target> + <target name="-init-debug-args"> + <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem"> + <os family="windows"/> + </condition> + <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}"> + <isset property="debug.transport"/> + </condition> + </target> + <target depends="-init-debug-args" name="-init-macrodef-debug"> + <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${module.name}" name="modulename"/> + <attribute default="${main.class}" name="classname"/> + <attribute default="${debug.modulepath}" name="modulepath"/> + <attribute default="${debug.classpath}" name="classpath"/> + <element name="customizeDebuggee" optional="true"/> + <sequential> + <j2seproject1:java classname="@{classname}" classpath="@{classpath}" modulename="@{modulename}" modulepath="@{modulepath}"> + <customize> + <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> + <customizeDebuggee/> + </customize> + </j2seproject1:java> + </sequential> + </macrodef> + </target> + <target depends="-init-source-module-properties" if="named.module.internal" name="-init-macrodef-java-with-module"> + <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${module.name}" name="modulename"/> + <attribute default="${main.class}" name="classname"/> + <attribute default="${run.modulepath}" name="modulepath"/> + <attribute default="${run.upgrademodulepath}" name="upgrademodulepath"/> + <attribute default="${run.classpath}" name="classpath"/> + <attribute default="jvm" name="jvm"/> + <element name="customize" optional="true"/> + <sequential> + <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true" module="@{modulename}"> + <classpath> + <path path="@{classpath}"/> + </classpath> + <modulepath> + <pathelement path="@{modulepath}"/> + <pathelement location="${module.build.classes.dir}"/> + </modulepath> + <upgrademodulepath> + <path path="@{upgrademodulepath}"/> + </upgrademodulepath> + <jvmarg value="-Dfile.encoding=${runtime.encoding}"/> + <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target depends="-init-source-module-properties" if="unnamed.module.internal" name="-init-macrodef-java-with-unnamed-module"> + <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="" name="modulename"/> + <attribute default="${main.class}" name="classname"/> + <attribute default="${run.modulepath}" name="modulepath"/> + <attribute default="${run.upgrademodulepath}" name="upgrademodulepath"/> + <attribute default="${run.classpath}" name="classpath"/> + <attribute default="jvm" name="jvm"/> + <element name="customize" optional="true"/> + <sequential> + <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true"> + <classpath> + <path path="@{classpath}"/> + </classpath> + <modulepath> + <path path="@{modulepath}"/> + </modulepath> + <upgrademodulepath> + <path path="@{upgrademodulepath}"/> + </upgrademodulepath> + <jvmarg value="-Dfile.encoding=${runtime.encoding}"/> + <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target depends="-init-source-module-properties" name="-init-macrodef-java-without-module" unless="modules.supported.internal"> + <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="" name="modulename"/> + <attribute default="${main.class}" name="classname"/> + <attribute default="" name="modulepath"/> + <attribute default="${run.classpath}" name="classpath"/> + <attribute default="jvm" name="jvm"/> + <element name="customize" optional="true"/> + <sequential> + <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true"> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="-Dfile.encoding=${runtime.encoding}"/> + <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + <classpath> + <path path="@{classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-java-with-module, -init-macrodef-java-with-unnamed-module, -init-macrodef-java-without-module" name="-init-macrodef-java"/> + <target name="-init-macrodef-copylibs"> + <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${manifest.file}" name="manifest"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> + <pathconvert property="run.classpath.without.build.classes.dir"> + <path path="${run.classpath}"/> + <map from="${build.classes.dir.resolved}" to=""/> + </pathconvert> + <pathconvert pathsep=" " property="jar.classpath"> + <path path="${run.classpath.without.build.classes.dir}"/> + <chainedmapper> + <flattenmapper/> + <filtermapper> + <replacestring from=" " to="%20"/> + </filtermapper> + <globmapper from="*" to="lib/*"/> + </chainedmapper> + </pathconvert> + <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/> + <copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" manifestencoding="UTF-8" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}"> + <fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/> + <manifest> + <attribute name="Class-Path" value="${jar.classpath}"/> + <customize/> + </manifest> + </copylibs> + </sequential> + </macrodef> + </target> + <target name="-init-presetdef-jar"> + <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1"> + <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8"> + <j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/> + </jar> + </presetdef> + </target> + <target name="-init-ap-cmdline-properties"> + <property name="annotation.processing.enabled" value="true"/> + <property name="annotation.processing.processors.list" value=""/> + <property name="annotation.processing.processor.options" value=""/> + <property name="annotation.processing.run.all.processors" value="true"/> + <property name="javac.processorpath" value="${javac.classpath}"/> + <property name="javac.test.processorpath" value="${javac.test.classpath}"/> + <condition property="ap.supported.internal" value="true"> + <not> + <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/> + </not> + </condition> + </target> + <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported"> + <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}"> + <isfalse value="${annotation.processing.run.all.processors}"/> + </condition> + <condition else="" property="ap.proc.none.internal" value="-proc:none"> + <isfalse value="${annotation.processing.enabled}"/> + </condition> + </target> + <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline"> + <property name="ap.cmd.line.internal" value=""/> + </target> + <target depends="-pre-init,-init-private,-init-libraries,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/> + <!-- + =================== + COMPILATION SECTION + =================== + --> + <target name="-deps-jar-init" unless="built-jar.properties"> + <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/> + <delete file="${built-jar.properties}" quiet="true"/> + </target> + <target if="already.built.jar.${basedir}" name="-warn-already-built-jar"> + <echo level="warn" message="Cycle detected: WebsocketGateway was already built"/> + </target> + <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps"> + <mkdir dir="${build.dir}"/> + <touch file="${built-jar.properties}" verbose="false"/> + <property file="${built-jar.properties}" prefix="already.built.jar."/> + <antcall target="-warn-already-built-jar"/> + <propertyfile file="${built-jar.properties}"> + <entry key="${basedir}" value=""/> + </propertyfile> + </target> + <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/> + <target depends="init" name="-check-automatic-build"> + <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/> + </target> + <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build"> + <antcall target="clean"> + <param name="no.dependencies" value="true"/> + </antcall> + </target> + <target depends="init,deps-jar" name="-pre-pre-compile"> + <mkdir dir="${build.classes.dir}"/> + </target> + <target name="-pre-compile"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="do.depend.true" name="-compile-depend"> + <pathconvert property="build.generated.subdirs"> + <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </pathconvert> + <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/> + </target> + <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile"> + <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/> + <copy todir="${build.classes.dir}"> + <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target if="has.persistence.xml" name="-copy-persistence-xml"> + <mkdir dir="${build.classes.dir}/META-INF"/> + <copy todir="${build.classes.dir}/META-INF"> + <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/> + </copy> + </target> + <target name="-post-compile"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/> + <target name="-pre-compile-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single"> + <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail> + <j2seproject3:force-recompile/> + <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}, module-info.java" sourcepath="${src.dir}"/> + </target> + <target name="-post-compile-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/> + <!-- + ==================== + JAR BUILDING SECTION + ==================== + --> + <target depends="init" name="-pre-pre-jar"> + <dirname file="${dist.jar}" property="dist.jar.dir"/> + <mkdir dir="${dist.jar.dir}"/> + </target> + <target name="-pre-jar"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile" name="-check-module-main-class"> + <pathconvert property="main.class.file"> + <string value="${main.class}"/> + <unpackagemapper from="*" to="*.class"/> + </pathconvert> + <condition property="do.module.main.class"> + <and> + <isset property="main.class.available"/> + <available file="${build.classes.dir}/module-info.class"/> + <available file="${build.classes.dir}/${main.class.file}"/> + <isset property="libs.CopyLibs.classpath"/> + <available classname="org.netbeans.modules.java.j2seproject.moduletask.ModuleMainClass" classpath="${libs.CopyLibs.classpath}"/> + </and> + </condition> + </target> + <target depends="-check-module-main-class" if="do.module.main.class" name="-set-module-main-class"> + <taskdef classname="org.netbeans.modules.java.j2seproject.moduletask.ModuleMainClass" classpath="${libs.CopyLibs.classpath}" name="modulemainclass"/> + <modulemainclass failonerror="false" mainclass="${main.class}" moduleinfo="${build.classes.dir}/module-info.class"/> + </target> + <target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available"> + <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/> + <touch file="${tmp.manifest.file}" verbose="false"/> + </target> + <target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest"> + <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/> + <copy encoding="${manifest.encoding}" file="${manifest.file}" outputencoding="UTF-8" tofile="${tmp.manifest.file}"/> + </target> + <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass"> + <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update"> + <attribute name="Main-Class" value="${main.class}"/> + </manifest> + </target> + <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile"> + <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update"> + <attribute name="Profile" value="${javac.profile}"/> + </manifest> + </target> + <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen"> + <basename file="${application.splash}" property="splashscreen.basename"/> + <mkdir dir="${build.classes.dir}/META-INF"/> + <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/> + <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update"> + <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/> + </manifest> + </target> + <target depends="init,compile" name="-check-do-mkdist"> + <condition property="do.mkdist"> + <and> + <isset property="do.archive"/> + <isset property="libs.CopyLibs.classpath"/> + <not> + <istrue value="${mkdist.disabled}"/> + </not> + <not> + <available file="${build.classes.dir}/module-info.class"/> + </not> + </and> + </condition> + </target> + <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-check-do-mkdist" if="do.mkdist" name="-do-jar-copylibs"> + <j2seproject3:copylibs manifest="${tmp.manifest.file}"/> + <echo level="info">To run this application from the command line without Ant, try:</echo> + <property location="${dist.jar}" name="dist.jar.resolved"/> + <echo level="info">java -jar "${dist.jar.resolved}"</echo> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-check-do-mkdist" if="do.archive" name="-do-jar-jar" unless="do.mkdist"> + <j2seproject1:jar manifest="${tmp.manifest.file}"/> + <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> + <property location="${dist.jar}" name="dist.jar.resolved"/> + <condition else="${dist.jar.resolved}" property="jar.usage.message.class.path.replacement" value=""> + <isset property="named.module.internal"/> + </condition> + <pathconvert property="run.classpath.with.dist.jar"> + <path path="${run.classpath}"/> + <map from="${build.classes.dir.resolved}" to="${jar.usage.message.class.path.replacement}"/> + </pathconvert> + <pathconvert property="run.modulepath.with.dist.jar"> + <path location="${dist.jar.resolved}"/> + <path path="${run.modulepath}"/> + <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/> + </pathconvert> + <condition else="${run.modulepath}" property="jar.usage.message.run.modulepath.with.dist.jar" value="${run.modulepath.with.dist.jar}"> + <isset property="named.module.internal"/> + </condition> + <condition else="" property="jar.usage.message.module.path" value=" -p ${jar.usage.message.run.modulepath.with.dist.jar}"> + <and> + <isset property="modules.supported.internal"/> + <length length="0" string="${jar.usage.message.run.modulepath.with.dist.jar}" when="greater"/> + </and> + </condition> + <condition else="" property="jar.usage.message.class.path" value=" -cp ${run.classpath.with.dist.jar}"> + <length length="0" string="${run.classpath.with.dist.jar}" when="greater"/> + </condition> + <condition else="/${main.class}" property="jar.usage.message.main.class.class.selector" value=""> + <isset property="do.module.main.class"/> + </condition> + <condition else=" ${main.class}" property="jar.usage.message.main.class" value=" -m ${module.name}${jar.usage.message.main.class.class.selector}"> + <isset property="named.module.internal"/> + </condition> + <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java}${jar.usage.message.module.path}${jar.usage.message.class.path}${jar.usage.message.main.class}"> + <isset property="main.class.available"/> + </condition> + <condition else="debug" property="jar.usage.level" value="info"> + <isset property="main.class.available"/> + </condition> + <echo level="${jar.usage.level}" message="${jar.usage.message}"/> + </target> + <target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest"> + <delete> + <fileset file="${tmp.manifest.file}"/> + </delete> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/> + <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/> + <target name="-post-jar"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-jar,-set-module-main-class,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/> + <target depends="init,compile,-pre-jar,-do-jar,-post-jar,deploy" description="Build JAR." name="jar"/> + <!-- + ================= + DEPLOY SECTION + ================= + --> + <target name="-pre-deploy"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init" name="-check-jlink"> + <condition property="do.jlink.internal"> + <and> + <istrue value="${do.jlink}"/> + <isset property="do.archive"/> + <isset property="named.module.internal"/> + </and> + </condition> + </target> + <target depends="init,-do-jar,-post-jar,-pre-deploy,-check-jlink" if="do.jlink.internal" name="-do-deploy"> + <delete dir="${dist.jlink.dir}" failonerror="false" quiet="true"/> + <property name="jlink.launcher.name" value="${application.title}"/> + <condition else="${module.name}" property="jlink.add.modules" value="${module.name},${jlink.additionalmodules}"> + <and> + <isset property="jlink.additionalmodules"/> + <length length="0" string="${jlink.additionalmodules}" when="greater"/> + </and> + </condition> + <condition property="jlink.do.strip.internal"> + <and> + <isset property="jlink.strip"/> + <istrue value="${jlink.strip}"/> + </and> + </condition> + <condition property="jlink.do.additionalparam.internal"> + <and> + <isset property="jlink.additionalparam"/> + <length length="0" string="${jlink.additionalparam}" when="greater"/> + </and> + </condition> + <condition property="jlink.do.launcher.internal"> + <and> + <istrue value="${jlink.launcher}"/> + <isset property="main.class.available"/> + </and> + </condition> + <property name="platform.jlink" value="${jdk.home}/bin/jlink"/> + <property name="jlink.systemmodules.internal" value="${jdk.home}/jmods"/> + <exec executable="${platform.jlink}"> + <arg value="--module-path"/> + <arg path="${jlink.systemmodules.internal}:${run.modulepath}:${dist.jar}"/> + <arg value="--add-modules"/> + <arg value="${jlink.add.modules}"/> + <arg if:set="jlink.do.strip.internal" value="--strip-debug"/> + <arg if:set="jlink.do.launcher.internal" value="--launcher"/> + <arg if:set="jlink.do.launcher.internal" value="${jlink.launcher.name}=${module.name}/${main.class}"/> + <arg if:set="jlink.do.additionalparam.internal" line="${jlink.additionalparam}"/> + <arg value="--output"/> + <arg value="${dist.jlink.output}"/> + </exec> + </target> + <target name="-post-deploy"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-do-jar,-post-jar,-pre-deploy,-do-deploy,-post-deploy" name="deploy"/> + <!-- + ================= + EXECUTION SECTION + ================= + --> + <target depends="init,compile" description="Run a main class." name="run"> + <j2seproject1:java> + <customize> + <arg line="${application.args}"/> + </customize> + </j2seproject1:java> + </target> + <target name="-do-not-recompile"> + <property name="javac.includes.binary" value=""/> + </target> + <target depends="init,compile-single" name="run-single"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <j2seproject1:java classname="${run.class}"/> + </target> + <target depends="init,compile-test-single" name="run-test-with-main"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/> + </target> + <!-- + ================= + DEBUGGING SECTION + ================= + --> + <target depends="init" if="netbeans.home" name="-debug-start-debugger"> + <j2seproject1:nbjpdastart name="${debug.class}"/> + </target> + <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test"> + <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/> + </target> + <target depends="init,compile" name="-debug-start-debuggee"> + <j2seproject3:debug> + <customizeDebuggee> + <arg line="${application.args}"/> + </customizeDebuggee> + </j2seproject3:debug> + </target> + <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/> + <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto"> + <j2seproject1:nbjpdastart stopclassname="${main.class}"/> + </target> + <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/> + <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single"> + <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail> + <j2seproject3:debug classname="${debug.class}"/> + </target> + <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/> + <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test"> + <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail> + <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/> + </target> + <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/> + <target depends="init" name="-pre-debug-fix"> + <fail unless="fix.includes">Must set fix.includes</fail> + <property name="javac.includes" value="${fix.includes}.java"/> + </target> + <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix"> + <j2seproject1:nbjpdareload/> + </target> + <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/> + <!-- + ================= + PROFILING SECTION + ================= + --> + <!-- + pre NB7.2 profiler integration + --> + <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile/> + </target> + <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72"> + <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile classname="${profile.class}"/> + </target> + <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile classname="sun.applet.AppletViewer"> + <customize> + <arg value="${applet.url}"/> + </customize> + </profile> + </target> + <target depends="-init-macrodef-junit,profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + </nbprofiledirect> + <j2seproject3:junit excludes="${excludes}" includes="${includes}" testincludes="${profile.class}" testmethods=""> + <customize> + <jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> + <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/> + <jvmarg value="${profiler.info.jvmargs.agent}"/> + <jvmarg line="${profiler.info.jvmargs}"/> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + </customize> + </j2seproject3:junit> + </target> + <!-- + end of pre NB72 profiling section + --> + <target if="netbeans.home" name="-profile-check"> + <condition property="profiler.configured"> + <or> + <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/> + <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/> + </or> + </condition> + </target> + <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent"> + <startprofiler/> + <antcall target="run"/> + </target> + <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <startprofiler/> + <antcall target="run-single"/> + </target> + <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/> + <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs"> + <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail> + <startprofiler/> + <antcall target="test-single"/> + </target> + <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <startprofiler/> + <antcall target="run-test-with-main"/> + </target> + <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <startprofiler/> + <antcall target="run-applet"/> + </target> + <!-- + =============== + JAVADOC SECTION + =============== + --> + <target depends="init" if="have.sources" name="-javadoc-build"> + <mkdir dir="${dist.javadoc.dir}"/> + <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}"> + <and> + <isset property="endorsed.classpath.cmd.line.arg"/> + <not> + <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/> + </not> + </and> + </condition> + <condition else="" property="bug5101868workaround" value="*.java"> + <matches pattern="1\.[56](\..*)?" string="${java.version}"/> + </condition> + <condition else="" property="javadoc.html5.cmd.line.arg" value="-html5"> + <and> + <isset property="javadoc.html5"/> + <available file="${jdk.home}${file.separator}lib${file.separator}jrt-fs.jar"/> + </and> + </condition> + <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}"> + <classpath> + <path path="${javac.classpath}"/> + </classpath> + <fileset dir="${src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}"> + <filename name="**/*.java"/> + </fileset> + <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="**/*.java"/> + <exclude name="*.java"/> + </fileset> + <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/> + <arg line="${javadoc.html5.cmd.line.arg}"/> + </javadoc> + <copy todir="${dist.javadoc.dir}"> + <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}"> + <filename name="**/doc-files/**"/> + </fileset> + <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="**/doc-files/**"/> + </fileset> + </copy> + </target> + <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview"> + <nbbrowse file="${dist.javadoc.dir}/index.html"/> + </target> + <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/> + <!-- + ========================= + TEST COMPILATION SECTION + ========================= + --> + <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test"> + <mkdir dir="${build.test.classes.dir}"/> + </target> + <target name="-pre-compile-test"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-init-source-module-properties" if="named.module.internal" name="-init-test-javac-module-properties-with-module"> + <j2seproject3:modulename property="test.module.name" sourcepath="${test.src.dir}"/> + <condition else="${empty.dir}" property="javac.test.sourcepath" value="${test.src.dir}"> + <and> + <isset property="test.module.name"/> + <length length="0" string="${test.module.name}" when="greater"/> + </and> + </condition> + <condition else="--patch-module ${module.name}=${test.src.dir} --add-reads ${module.name}=ALL-UNNAMED" property="javac.test.compilerargs" value="--add-reads ${test.module.name}=ALL-UNNAMED"> + <and> + <isset property="test.module.name"/> + <length length="0" string="${test.module.name}" when="greater"/> + </and> + </condition> + </target> + <target depends="-init-source-module-properties" if="named.module.internal" name="-init-test-run-module-properties"> + <condition else="${module.name}" property="run.test.addexport.source.module.internal" value="${test.module.name}"> + <and> + <isset property="test.module.name"/> + <length length="0" string="${test.module.name}" when="greater"/> + </and> + </condition> + <fileset dir="${build.test.classes.dir}" id="run.test.packages.internal" includes="**/*.class"/> + <property location="${build.test.classes.dir}" name="build.test.classes.dir.abs.internal"/> + <pathconvert pathsep=" " property="run.test.addexports.internal" refid="run.test.packages.internal"> + <chainedmapper> + <regexpmapper from="^(.*)\Q${file.separator}\E.*\.class$$" to="\1"/> + <filtermapper> + <uniqfilter/> + <replacestring from="${build.test.classes.dir.abs.internal}" to=""/> + </filtermapper> + <cutdirsmapper dirs="1"/> + <packagemapper from="*" to="--add-exports ${run.test.addexport.source.module.internal}/*=ALL-UNNAMED"/> + </chainedmapper> + </pathconvert> + <condition else="--patch-module ${module.name}=${build.test.classes.dir} --add-modules ${module.name} --add-reads ${module.name}=ALL-UNNAMED ${run.test.addexports.internal}" property="run.test.jvmargs" value="--add-modules ${test.module.name} --add-reads ${test.module.name}=ALL-UNNAMED ${run.test.addexports.internal}"> + <and> + <isset property="test.module.name"/> + <length length="0" string="${test.module.name}" when="greater"/> + </and> + </condition> + </target> + <target depends="-init-source-module-properties" name="-init-test-module-properties-without-module" unless="named.module.internal"> + <property name="javac.test.sourcepath" value="${empty.dir}"/> + <property name="javac.test.compilerargs" value=""/> + <property name="run.test.jvmargs" value=""/> + </target> + <target depends="-init-test-javac-module-properties-with-module,-init-test-module-properties-without-module" name="-init-test-module-properties"/> + <target if="do.depend.true" name="-compile-test-depend"> + <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/> + </target> + <target depends="init,deps-jar,compile,-init-test-module-properties,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test"> + <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" modulepath="${javac.test.modulepath}" processorpath="${javac.test.processorpath}" sourcepath="${javac.test.sourcepath}" srcdir="${test.src.dir}"> + <customize> + <compilerarg line="${javac.test.compilerargs}"/> + </customize> + </j2seproject3:javac> + <copy todir="${build.test.classes.dir}"> + <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target name="-post-compile-test"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/> + <target name="-pre-compile-test-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,compile,-init-test-module-properties,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single"> + <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail> + <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/> + <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}, module-info.java" modulepath="${javac.test.modulepath}" processorpath="${javac.test.processorpath}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"> + <customize> + <compilerarg line="${javac.test.compilerargs}"/> + </customize> + </j2seproject3:javac> + <copy todir="${build.test.classes.dir}"> + <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target name="-post-compile-test-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/> + <!-- + ======================= + TEST EXECUTION SECTION + ======================= + --> + <target depends="init" if="have.tests" name="-pre-test-run"> + <mkdir dir="${build.test.results.dir}"/> + </target> + <target depends="init,compile-test,-init-test-run-module-properties,-pre-test-run" if="have.tests" name="-do-test-run"> + <j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/> + </target> + <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init" if="have.tests" name="test-report"/> + <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/> + <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/> + <target depends="init" if="have.tests" name="-pre-test-run-single"> + <mkdir dir="${build.test.results.dir}"/> + </target> + <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single" if="have.tests" name="-do-test-run-single"> + <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail> + <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method"> + <fail unless="test.class">Must select some files in the IDE or set test.class</fail> + <fail unless="test.method">Must select some method in the IDE or set test.method</fail> + <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init,compile-test-single,-init-test-run-module-properties,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/> + <!-- + ======================= + TEST DEBUGGING SECTION + ======================= + --> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test"> + <fail unless="test.class">Must select one file in the IDE or set test.class</fail> + <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method"> + <fail unless="test.class">Must select one file in the IDE or set test.class</fail> + <fail unless="test.method">Must select some method in the IDE or set test.method</fail> + <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/> + </target> + <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test"> + <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/> + </target> + <target depends="init,compile-test-single,-init-test-run-module-properties,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/> + <target depends="init,compile-test-single,-init-test-run-module-properties,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/> + <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test"> + <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/> + </target> + <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/> + <!-- + ========================= + APPLET EXECUTION SECTION + ========================= + --> + <target depends="init,compile-single" name="run-applet"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <j2seproject1:java classname="sun.applet.AppletViewer"> + <customize> + <arg value="${applet.url}"/> + </customize> + </j2seproject1:java> + </target> + <!-- + ========================= + APPLET DEBUGGING SECTION + ========================= + --> + <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <j2seproject3:debug classname="sun.applet.AppletViewer"> + <customizeDebuggee> + <arg value="${applet.url}"/> + </customizeDebuggee> + </j2seproject3:debug> + </target> + <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/> + <!-- + =============== + CLEANUP SECTION + =============== + --> + <target name="-deps-clean-init" unless="built-clean.properties"> + <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/> + <delete file="${built-clean.properties}" quiet="true"/> + </target> + <target if="already.built.clean.${basedir}" name="-warn-already-built-clean"> + <echo level="warn" message="Cycle detected: WebsocketGateway was already built"/> + </target> + <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps"> + <mkdir dir="${build.dir}"/> + <touch file="${built-clean.properties}" verbose="false"/> + <property file="${built-clean.properties}" prefix="already.built.clean."/> + <antcall target="-warn-already-built-clean"/> + <propertyfile file="${built-clean.properties}"> + <entry key="${basedir}" value=""/> + </propertyfile> + </target> + <target depends="init" name="-do-clean"> + <delete dir="${build.dir}"/> + <delete dir="${dist.jlink.output}"/> + <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/> + </target> + <target name="-post-clean"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/> + <target name="-check-call-dep"> + <property file="${call.built.properties}" prefix="already.built."/> + <condition property="should.call.dep"> + <and> + <not> + <isset property="already.built.${call.subproject}"/> + </not> + <available file="${call.script}"/> + </and> + </condition> + </target> + <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep"> + <ant antfile="${call.script}" inheritall="false" target="${call.target}"> + <propertyset> + <propertyref prefix="transfer."/> + <mapper from="transfer.*" to="*" type="glob"/> + </propertyset> + </ant> + </target> +</project> diff --git a/examples/WebsocketGateway/nbproject/genfiles.properties b/examples/WebsocketGateway/nbproject/genfiles.properties new file mode 100644 index 0000000000000000000000000000000000000000..4ef909e30dfb07a1670ffc07db5bbe8a00e0635f --- /dev/null +++ b/examples/WebsocketGateway/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=9167008c +build.xml.script.CRC32=8264c696 +build.xml.stylesheet.CRC32=f85dc8f2@1.95.0.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=9167008c +nbproject/build-impl.xml.script.CRC32=e37e634c +nbproject/build-impl.xml.stylesheet.CRC32=f89f7d21@1.95.0.48 diff --git a/examples/WebsocketGateway/nbproject/project.properties b/examples/WebsocketGateway/nbproject/project.properties new file mode 100644 index 0000000000000000000000000000000000000000..b6cab25717d5acd031b7affe0ff9b1146230c89f --- /dev/null +++ b/examples/WebsocketGateway/nbproject/project.properties @@ -0,0 +1,143 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=WebsocketGateway +application.vendor=mcgredo +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.modulepath=\ + ${run.modulepath} +debug.test.classpath=\ + ${run.test.classpath} +debug.test.modulepath=\ + ${run.test.modulepath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/${application.title}.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jetty-http.jar=lib/jetty-http-9.4.31.v20200723.jar +file.reference.jetty-io.jar=lib/jetty-io-9.4.31.v20200723.jar +file.reference.jetty-security.jar=lib/jetty-security-9.4.31.v20200723.jar +file.reference.jetty-server.jar=lib/jetty-server-9.4.31.v20200723.jar +file.reference.jetty-servlet.jar=lib/jetty-servlet-9.4.31.v20200723.jar +file.reference.jetty-util.jar=lib/jetty-util-9.4.31.v20200723.jar +file.reference.jetty-webapp.jar=lib/jetty-webapp-9.4.31.v20200723.jar +file.reference.jetty-xml.jar=lib/jetty-xml-9.4.31.v20200723.jar +file.reference.servlet-api-3.1.jar=lib/servlet-api-3.1.jar +file.reference.slf4j-api-1.7.5.jar=lib/slf4j-api-1.7.5.jar +file.reference.slf4j-jdk14-1.7.30.jar=lib/slf4j-jdk14-1.7.30.jar +file.reference.open-dis7-enumerations-classes.jar=lib/dis/open-dis7-enumerations-classes.jar +file.reference.open-dis7-pdus-classes.jar=lib/dis/open-dis7-pdus-classes.jar +file.reference.websocket-api.jar=lib/websocket/websocket-api-9.4.31.v20200723.jar +file.reference.websocket-common.jar=lib/websocket/websocket-common-9.4.31.v20200723.jar +file.reference.websocket-server.jar=lib/websocket/websocket-server-9.4.31.v20200723.jar +file.reference.websocket-servlet.jar=lib/websocket/websocket-servlet-9.4.31.v20200723.jar +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.jetty-util.jar}:\ + ${file.reference.jetty-server.jar}:\ + ${file.reference.jetty-servlet.jar}:\ + ${file.reference.jetty-webapp.jar}:\ + ${file.reference.open-dis7-enumerations-classes.jar}:\ + ${file.reference.open-dis7-pdus-classes.jar}:\ + ${file.reference.servlet-api-3.1.jar}:\ + ${file.reference.websocket-api.jar}:\ + ${file.reference.websocket-servlet.jar}:\ + ${file.reference.websocket-server.jar} +# Space-separated list of extra javac options +javac.compilerargs=-Xlint:unchecked +javac.deprecation=true +javac.external.vm=false +javac.modulepath= +javac.processormodulepath= +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.modulepath=\ + ${javac.modulepath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=true +javadoc.encoding=${source.encoding} +javadoc.html5=false +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jlink.launcher=false +jlink.launcher.name=WebsocketGateway +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=edu.nps.moves.gateway.WebSocketServer +# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found. +manifest.custom.application.library.allowable.codebase= +# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts. +manifest.custom.caller.allowable.codebase= +# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed +manifest.custom.codebase= +# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) +manifest.custom.permissions= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +no.dependencies=true +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${file.reference.slf4j-api-1.7.5.jar}:\ + ${file.reference.slf4j-jdk14-1.7.30.jar}:\ + ${file.reference.jetty-http.jar}:\ + ${file.reference.jetty-io.jar}:\ + ${file.reference.jetty-security.jar}:\ + ${file.reference.jetty-xml.jar}:\ + ${file.reference.websocket-common.jar} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.modulepath=\ + ${javac.modulepath} +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +run.test.modulepath=\ + ${javac.test.modulepath} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/examples/WebsocketGateway/nbproject/project.xml b/examples/WebsocketGateway/nbproject/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..3656f217c025774b729d87f7b16a56576e05f876 --- /dev/null +++ b/examples/WebsocketGateway/nbproject/project.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://www.netbeans.org/ns/project/1"> + <type>org.netbeans.modules.java.j2seproject</type> + <configuration> + <data xmlns="http://www.netbeans.org/ns/j2se-project/3"> + <name>WebsocketGateway</name> + <source-roots> + <root id="src.dir"/> + </source-roots> + <test-roots> + <root id="test.src.dir"/> + </test-roots> + </data> + <libraries xmlns="http://www.netbeans.org/ns/ant-project-libraries/1"> + <definitions>./lib/nblibraries.properties</definitions> + </libraries> + </configuration> +</project> diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/ConnectionManager.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/ConnectionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..32f61047e0f3632ada0e0ec6ad5a441e465f31b9 --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/ConnectionManager.java @@ -0,0 +1,141 @@ +package edu.nps.moves.gateway; + +import java.util.Map; +import java.util.concurrent.*; + +/** + * Manages the various connections established by web pages. There may be + * N web pages connected to the gateway. This manages the connections + * established by the web pages and provides a central point for sending + * messages to all connected pages.<p> + * + * We need to be careful about concurrency here--things are happening + * async and we can be multithreaded. This is a singleton. + * + * @author DMcG + */ +public class ConnectionManager +{ + /** Singleton */ + public static ConnectionManager connectionManager = null; + + /** Every connection established by a web page is kept in this list. This should + * be a concurrentHashSet, but there doesn't seem to be one out of the box. + */ + private Map<DISEndpoint, DISEndpoint> connections; + + /** Use this to get the single, shared connectionManager instance + * @return the single, shared instance of ConnectionManager + */ + public static synchronized ConnectionManager getConnectionManager() + { + if(connectionManager == null) + { + connectionManager = new ConnectionManager(); + } + + return connectionManager; + } + + /** Use the static method above to retrieve the single, shared connection manager + */ + private ConnectionManager() + { + this.connections = new ConcurrentHashMap<>(); + } + + /** + * Add a client connection to the list. Messages will be + * relayed to this client. + * + * @param aConnection + */ + public synchronized void addConnection(DISEndpoint aConnection) + { + connections.put(aConnection, aConnection); + } + + /** + * Remove a connection from the list of active connections. This is + * typically called when a client closes the connection. We remove it + * from the list of active connections. + * + * @param aConnection + */ + public synchronized void removeConnection(DISEndpoint aConnection) + { + connections.remove(aConnection); + } + + /** + * Send a message we have received from the sender out to all the other + * clients, but do NOT send it back to the client that sent it. Typically + * the message is in string JSON format. + * + * @param message message received from sender + * @param sender the client that sent the message. The message is not sent to this client. If the + * sender is null, the message is sent to all connected clients. + */ + public void repeatMessage(String message, DISEndpoint sender) + { + try + { + // Optionally we can convert the string into a Java JSON + // object and examine it for various reasons, just as + // area of interest management. This can be a fairly expensive + // operation in high traffic environments. + + //JSONObject jsonMessage = new JSONObject(message); + + // Loop through all the connections, repeating the message out + // (except to the connection that sent it). Note that the + // DISEndpoint may actually be a native network DIS connection. + + for (DISEndpoint aConnection : connections.keySet()) { + // Repeat the message to all clients, except the client that sent it. + // If the sender is null, always send it to all of the connections + if(sender == null || !aConnection.equals(sender)) + { + aConnection.sendToClient(message); + } + } + } + catch(Exception e) + { + System.err.println(e); + e.printStackTrace(System.err); + } + } + + /** + * Send a binary message we have received from the sender out to all the other + * clients, but do NOT send it back to the client that sent it. The message + * is typically in IEEE DIS binary format. + * + * @param data message received from sender + * @param sender the client that sent the message. The message is not sent to this client. + * If the sender is null, the message is sent to all clients. + */ + public void repeatBinaryMessage(byte[] data, DISEndpoint sender) + { + try + { + // Optionally we can convert the data into a Java object + for (DISEndpoint aConnection : connections.keySet()) { + // Repeat the message to all clients, except the client that sent it. + // If the sender is null, always send it to all of the connections + if(sender == null || !aConnection.equals(sender)) + { + aConnection.sendBinaryToClient(data); + } + } + } + catch(Exception e) + { + System.err.println(e); + e.printStackTrace(System.err); + } + + } + +} diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/DISEndpoint.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DISEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..dc45ea206dd6bbe2d4031344afeb3b4aa796f221 --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DISEndpoint.java @@ -0,0 +1,21 @@ +package edu.nps.moves.gateway; + +/** + * interface for things that can read or send binary or JSON format DIS packets. + * This may be a web page connected via a web socket or, on the server side, a + * native socket that reads and writes bcast or mcast DIS. + * + * @author DMcG + */ +public interface DISEndpoint +{ + /** Send binary format DIS to an endpoint. + * @param buf + */ + void sendBinaryToClient(byte[] buf); + + /** Send JSON format DIS to a client + * @param aMessage + */ + void sendToClient(String aMessage); +} diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisNative.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisNative.java new file mode 100644 index 0000000000000000000000000000000000000000..7c24ab90aa2daf107de557333b01d90164e802c1 --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisNative.java @@ -0,0 +1,277 @@ +package edu.nps.moves.gateway; + +import edu.nps.moves.dis7.pdus.EntityID; +import edu.nps.moves.dis7.pdus.EntityStatePdu; +import edu.nps.moves.dis7.pdus.Pdu; +import edu.nps.moves.dis7.utilities.PduFactory; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Reads and writes DIS from a native socket on the local network. This + * implements the DISEndpoint interface, which allows it to interoperate with + * the ConnectionManager. + * + * @author DMcG + */ +public class DisNative implements Runnable, DISEndpoint { + + public static final int PORT = 3000; + + /** + * Max size of UDP DIS packets to receive. May need to be bigger for bundled + * PDUs, in which multiple PDUs are placed in one UDP packet. VBS2 sends + * some large-ish packets with a lot of articulation parameters, which can + * blow past the typical UDP MTU packet size of 1500 bytes. + */ + private static final int MAX_UDP_PACKET_SIZE = 8 * 1024; + + /** + * socket can be used for either bcast or multicast + */ + private MulticastSocket multicastSocket; + + /** + * If reading from multicast, use this address + */ + private InetAddress multicastAddress; + + /** + * Port socket listens on for DIS traffic + */ + private int port; + + /** + * Used for performance instrumentation + */ + private int messageCount = 0; + + /** + * Used for performance instrumentation + */ + private Date startTime = new Date(); + + /** + * 255.255.255.255 is by definition the bcast address on whatever network + * you're on, no matter what the subnet mask + */ + private InetAddress broadcastAddress; + + /** + * Converts IEEE binary DIS to a Java DIS PDU object + */ + private PduFactory pduFactory = new PduFactory(); + + /** + * Unique ID placed in the padding of PDUs sent. This is a cheat, intended + * to prevent routing loops. We want the gateway ID to be non-zero. + */ + private short gatewayID = (short) ((Math.random() * 254) + 1); + + private boolean quit = false; + + /** + * Constructor + * + * @param socket socket we read and write on. + * @param multicastAddress If null we read & write bcast. Otherwise mcast on + * this address + * @param port + */ + public DisNative(MulticastSocket socket, InetAddress multicastAddress, int port) { + this.multicastSocket = socket; + this.multicastAddress = multicastAddress; + this.port = port; + try { + this.broadcastAddress = InetAddress.getByName("255.255.255.255"); + } catch (UnknownHostException e) { + System.err.println(e); + e.printStackTrace(System.err); + } + + // Some OSes require the socket to explictly be put into bcast mode + try { + if (multicastAddress == null && !multicastSocket.getBroadcast()) { + this.multicastSocket.setBroadcast(true); + } + } catch (SocketException e) { + System.err.println("Cant put socket into bcast mode " + e); + e.printStackTrace(System.err); + } + } + + /** + * Entry point for thread. Endlessly loop, reading UDP DIS packets. + */ + @Override + public void run() { + // Get the manager for all things that can read and write DIS, including + // web pages connected via a web socket and, like this object, a native + // socket reading and writing DIS from the local network. + ConnectionManager connectionManager = ConnectionManager.getConnectionManager(); + + // Performance monitoring + Map<EntityID, Pdu> entities = new Hashtable<>(); + + // Read DIS from the local network. Send native DIS packets to all web + // pages that are connected via web sockets. + ByteBuffer byteBuffer = ByteBuffer.allocate(MAX_UDP_PACKET_SIZE); + DatagramPacket packet = new DatagramPacket(byteBuffer.array(), byteBuffer.capacity()); + byte[] trimmedData; + Pdu aPdu; + EntityStatePdu espdu; + Date endTime; + long elapsedTime; + + try { + while (!quit) { + multicastSocket.receive(packet); + + //byte[] contents = packet.getData(); + //System.out.print( "var data = new Array(" ); + //for(int idx = 0; idx < packet.getLength(); idx++) + //{ + // System.out.print(contents[idx] + ", "); + //} + //System.out.println(");"); + // getLength() returns the length of the data actually received. + //System.out.println("Received packet size:" + packet.getLength()); + //System.out.println("Got DIS packet from native network"); + // Turn the DIS binary into a Java object. This is optional, + // but useful; we can perhaps do filtering based on the location, + // and right now we only send ESPDU's to web clients. In contrast + // to JSON, this is pretty cheap in the cosmic scheme of things. + // Meaning it's not all that effcient, but get a life. + aPdu = pduFactory.createPdu(packet.getData()); + + // Can't interepret the packet? It's probably not DIS, or it's not + // a supported PDU type, so don't forward it to the web clients. + if (aPdu == null) { + continue; + } + + // one problem we can have is routing loops. For example, a web + // client sends a PDU. We receive it and send it out on the native + // interface. Via the rules of bcast and mcast, we also _receive_ + // that PDU. We may accidentally send another copy to all the + // web clients. So we set the padding field to contain a value, + // and discard the pdu if we're the one that sent it. + if (aPdu.getLength() == gatewayID) { + //System.out.println("Discarding looped packet, id=" + gatewayID); +// continue; + } + + messageCount++; + if (messageCount % 10000 == 0) { + endTime = new Date(); + elapsedTime = endTime.getTime() - startTime.getTime(); + System.out.println("Elapsed time = " + elapsedTime); + System.out.println("Packets per second received, 10K packets: " + 10000.0 / (elapsedTime / 1000.0)); + System.out.println("Entity world count: " + entities.keySet().size()); + messageCount = 0; + startTime = endTime; + } + + if (aPdu instanceof EntityStatePdu) { + espdu = (EntityStatePdu) aPdu; + if (entities.get(espdu.getEntityID()) == null) { + entities.put(espdu.getEntityID(), aPdu); + } + } + + // We know this is an PDU. Forward out the PDU to all clients, + // including any web pages. Limit the size of the binary data + // to only the length of the data received. + trimmedData = new byte[packet.getLength()]; + System.arraycopy(byteBuffer.array(), 0, trimmedData, 0, packet.getLength()); + //System.out.println("Sending binary message length" + trimmedData.length); + //String phonyMessage = "\"{'protocolVersion':6,'exerciseID':1,'pduType':1,'protocolFamily':1,'timestamp':280742925,'pduLength':144,'padding':-18884,'entityID':{'site':7,'application':155,'entity':38008},'forceId':3,'numberOfArticulationParameters':0,'entityType':{'entityKind':1,'domain':3,'country':205,'category':27,'subcategory':4,'spec':0,'extra':6},'alternativeEntityType':{'entityKind':0,'domain':0,'country':0,'category':0,'subcategory':0,'spec':0,'extra':0},'entityLinearVelocity':{'x':-0.18839877843856812,'y':-0.21264395117759705,'z':0.22128817439079285},'entityLocation':{'x':5099250.912415399,'y':-560530.1508509029,'z':3777289.9119291855},'entityOrientation':{'psi':-2.2958126068115234,'theta':-0.6617516875267029,'phi':2.4262077808380127},'entityAppearance':0,'deadReckoningParameters':{'deadReckoningAlgorithm':2,'otherParameters':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],'entityLinearAcceleration':{'x':0,'y':0,'z':0},'entityAngularVelocity':{'x':0,'y':0,'z':0}},'marking':{'characterSet':1,'characters':[78,74,79,82,68,32,82,0,0,0,0]},'capabilities':0,'articulationParameters':[]}\""; + //connectionManager.repeatMessage(phonyMessage, null); + //connectionManager.repeatMessage(phonyMessage, null); + connectionManager.repeatBinaryMessage(trimmedData, null); + //connectionManager.repeatBinaryMessage(trimmedData, null); + + // We can also convert this to JSON if we like. + //JSONObject obj = new JSONObject(aPdu); + //System.out.println(obj.toString()); + byteBuffer.clear(); + } + } catch (IOException e) { + System.err.println(e); + } finally { + SocketAddress addr = multicastSocket.getLocalSocketAddress(); + try { + if (((InetSocketAddress)addr).getAddress().isMulticastAddress()) { + NetworkInterface netIf = multicastSocket.getNetworkInterface(); + multicastSocket.leaveGroup(addr, netIf); + } + multicastSocket.disconnect(); + multicastSocket.close(); + } catch (IOException ex) { + Logger.getLogger(DisNative.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + /** + * Sending strings to the local network is probably a bad idea. For now we + * simply no-op this because it doesn't make sense to send a non-standard + * string to the network. + * + * @param aMessage + */ + @Override + public void sendToClient(String aMessage) { + } + + /** + * Send native DIS to the local native network. Typically we receive DIS + * from some web page, and send it to the local network. + * + * @param buf DIS data + */ + @Override + public void sendBinaryToClient(byte[] buf) { + // Send out on all bcast addresses on all interfaces, or send out + // in mcast format. + + Pdu aPdu = pduFactory.createPdu(buf); + + // Not DIS, or at least not decodable by open-dis as DIS? Drop it. + if (aPdu == null) { + return; + } + + // Helps prevent routing loops. We stick a number in an unused portion + // of the PDU, so when we read it back we know we sent it. This is kind + // of bogus, but we're combining bridging and routing in one layer, so + // there's no good way around it. + aPdu.setLength(gatewayID); + GregorianCalendar date = new GregorianCalendar(); + aPdu.setTimestamp((int) date.getTime().getTime()); + try { + byte[] data = aPdu.marshal(); + DatagramPacket packet; + + // Send to multicast if we have a multicast address set + if (multicastAddress != null) { + packet = new DatagramPacket(data, data.length, multicastAddress, port); + } else // send bcast + { + packet = new DatagramPacket(data, data.length, broadcastAddress, port); + } + multicastSocket.send(packet); + } catch (Exception ex) { + Logger.getLogger(DisNative.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void quit() { + quit = true; + } +} diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisSocketFactory.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisSocketFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a00a2e15048d111fc95a9307750becb9d65efc4d --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/DisSocketFactory.java @@ -0,0 +1,54 @@ +package edu.nps.moves.gateway; + +import edu.nps.moves.dis7.utilities.DisThreadedNetworkInterface; + +import java.io.IOException; +import java.net.*; + +/** + * Creates a multicast socket. Uses SO_REUSE_ADDR, so the socket can be + * opened even if another program is already listening on that port. + * + * @author DMcG + * @author <a href="mailto:tdnorbra@nps.edu?subject=edu.nps.moves.gateway.DisSocketFactory">Terry Norbraten, NPS MOVES</a> + */ +public class DisSocketFactory +{ + public static MulticastSocket getDisSocket(int port, String multicastGroup) + { + MulticastSocket socket = null; + + try + { + // Some fancy footwork to allow us to open a UDP socket even if + // anther program has already opened a socket on that port. Opens + // on the wildcard address, ie all configured interfaces--both wired + // and wireless, typically. + + // We want do do this because often we have another application on this + // host generating traffic, or listening for traffic, also on the default + // DIS port. + socket = new MulticastSocket(port); + socket.setReuseAddress(true); + + if(multicastGroup != null) + { + InetAddress group = InetAddress.getByName(multicastGroup); + if(group.isMulticastAddress()) + { + SocketAddress mcastaddr = new InetSocketAddress(group, port); + socket.joinGroup(mcastaddr, DisThreadedNetworkInterface.findIpv4Interface()); + } + } else { + socket.setBroadcast(true); + } + + } + catch(IOException e) + { + System.err.println(e); + } + + return socket; + } +} diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebPageConnection.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebPageConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..5a1291a72a205888332372180ad6e7ded7fd86c7 --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebPageConnection.java @@ -0,0 +1,156 @@ +package edu.nps.moves.gateway; + +import org.eclipse.jetty.websocket.api.RemoteEndpoint; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; + +import java.io.IOException; +import java.nio.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; + +/** + * A class that corresponds to one connection to one web page client. This is created when + * a client connects a websocket to us, one instance per connection. Typically there's one + * websocket opened from the web page to the server. + * + * Lifted from http://amilamanoj.blogspot.com/2013/06/secure-websockets-with-jetty.html + * + * @author DMcG + */ +@WebSocket +public class WebPageConnection implements DISEndpoint +{ + /** The far side of the connection */ + private RemoteEndpoint remote; + + /** Current server session */ + private Session session; + + /** Asynchronous message handler */ + private Future<Void> fut = null; + + /** Fired when the websocket from the client connects. Add it to the ConnectionManager's + * list of connections. + * + * @param session The session with the web client + */ + @OnWebSocketConnect + public void onConnect(Session session) + { + System.out.println("WebSocket Opened"); + this.session = session; + this.remote = session.getRemote(); + + ConnectionManager.getConnectionManager().addConnection(this); + } + + /** Fired when the client sends a message. Tell the connection manager to repeat the + * message to all other clients, but do not loop it back to the sender. + * + * @param message the (text) message sent from the client to the server, typical in JSON format + */ + @OnWebSocketMessage + public void onMessage(String message) + { + //System.out.println("Message from Client: " + message); + ConnectionManager.getConnectionManager().repeatMessage(message, this); + } + + /** Fired when a client sends a binary message. + * + * @param buf binary data + * @param offset + * @param length + */ + @OnWebSocketMessage + public void onBinaryMessage(byte buf[], int offset, int length) + { + //PduFactory factory = new PduFactory(); + //Pdu aPdu = factory.createPdu(buf); + //System.out.println("Got web pdu of type " + aPdu.getClass().getName() ); + if (session.isOpen()) + ConnectionManager.getConnectionManager().repeatBinaryMessage(buf, this); + } + + @OnWebSocketError + public void onWebSocketError(Throwable error) { + if (fut != null) + fut.cancel(true); + + System.err.println(error); + } + + /** Fired when the web socket closes. The client informs us, and we remove + * the client from the list of active clients in ConnectionManager + * + * @param statusCode reason for closure + * @param reason string reason for closure + */ + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + if (fut != null) + fut.cancel(true); + + ConnectionManager.getConnectionManager().removeConnection(this); + System.out.println("WebSocket Closed. Code:" + statusCode + " Reason:" + reason); + } + + /** + * Sends data in string format to the client. This is typically in + * JSON format. This is usually called by the ConnectionManager. + * + * @param message data to be sent + */ + @Override + public void sendToClient(String message) + { + try + { + remote.sendString(message); + } + catch(IOException e) + { + System.err.println(e); + } + } + + /** + * Sends data in binary format to the client. The end client is typically + * another web page. + * + * @param buf + */ + @Override + public void sendBinaryToClient(byte[] buf) + { + try + { + if (session.isOpen()) { + fut = remote.sendBytesByFuture(ByteBuffer.wrap(buf)); + fut.get(500, TimeUnit.MILLISECONDS); +// remote.sendBytes(ByteBuffer.wrap(buf)); + } + } +// catch (IOException ex) { +// Logger.getLogger(WebPageConnection.class.getName()).log(Level.SEVERE, null, ex); +// } + catch(InterruptedException | ExecutionException | TimeoutException ex) { + if (fut != null) + fut.cancel(true); + + Logger.getLogger(WebPageConnection.class.getName()).log(Level.SEVERE, null, ex); + } + + } + +} diff --git a/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebSocketServer.java b/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebSocketServer.java new file mode 100644 index 0000000000000000000000000000000000000000..738e0a23cee31741fa9f7a32cba4f10eb398cc02 --- /dev/null +++ b/examples/WebsocketGateway/src/edu/nps/moves/gateway/WebSocketServer.java @@ -0,0 +1,226 @@ +package edu.nps.moves.gateway; + +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.server.handler.RequestLogHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.server.WebSocketHandler; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; + +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This is a full-blown web server that happens to also handle websocket + * connections on the server side. Websocket connections are handed off + * to a subclass for handling. + * + * Lifted from http://amilamanoj.blogspot.com/2013/06/secure-websockets-with-jetty.html + * + * @author DMcG + * @author <a href="mailto:tdnorbra@nps.edu?subject=edu.nps.moves.gateway.WebSocketServer">Terry Norbraten, NPS MOVES</a> + */ +public class WebSocketServer +{ + /** The default port the webserver listens on. */ + public static final int DEFAULT_WEBSERVER_PORT = 8282; + + /** The web server we're creating */ + private Server server; + + /** The handlers--the type of content the web server can serve up */ + private List<Handler> webSocketHandlerList = new ArrayList<>(); + + /** DIS binary repeater */ + private DisNative listener; + + /** New instance of WebSocketServer */ + private WebSocketServer() { + try { + init(); + } catch (Exception ex) { + Logger.getLogger(WebSocketServer.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void init() throws Exception { + + // Get config properties + Properties config = new Properties(); + try (InputStream in = new FileInputStream("GatewayConfiguration.properties")) { + config.load(in); + } + + System.out.println("Gateway configuration properties loaded"); + + // Basic Jetty server + server = new Server(); + server.setStopAtShutdown(true); + + // Add handlers for the various things a web server can do: basic + // http, websockets, etc. + + // Http server + HttpConfiguration httpConfiguration = new HttpConfiguration(); + ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration)); + int webserverPort = DEFAULT_WEBSERVER_PORT; + try + { + webserverPort = Integer.parseInt(config.getProperty("webserverPort")); + } + catch(NumberFormatException e) + { + System.err.println("webserver port not specified in GatewayConfiguration.properties, using default of " + DEFAULT_WEBSERVER_PORT); + } + http.setPort(webserverPort); + + // Tell server about connections + Connector[] connectors = {http}; + server.setConnectors(connectors); + + // Set up a websocket handler. Incoming requests to ws:// + // will be handed off to this class. + WebSocketHandler wsHandler = new WebSocketHandler() + { + @Override + public void configure(WebSocketServletFactory webSocketServletFactory) + { + webSocketServletFactory.register(WebPageConnection.class); + } + }; + + // Add it to the handler list + ContextHandler wsContextHandler = new ContextHandler(); + wsContextHandler.setHandler(wsHandler); + webSocketHandlerList.add(wsHandler); + + // Add a static content (html) handler. Html and other files + // go in the content directory. + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setDirectoriesListed(true); + resourceHandler.setWelcomeFiles(new String[] {"index.html", "index.htm"} ); + resourceHandler.setResourceBase("./content"); + webSocketHandlerList.add(resourceHandler); + + // Add a JSP handler. JSP can be handy for determining the internal + // state of this server from a web page and displaying it to the user + WebAppContext jspContext = new WebAppContext(); + jspContext.setWelcomeFiles(new String[] {"index.jsp"}); + jspContext.setResourceBase("./content"); + jspContext.setContextPath("/"); + webSocketHandlerList.add(jspContext); + + // Create a default handler for everything else + DefaultHandler defaultHandler = new DefaultHandler(); + webSocketHandlerList.add(defaultHandler); + + // Logging + RequestLogHandler requestLogHandler = new RequestLogHandler(); + webSocketHandlerList.add(requestLogHandler); + + RequestLogWriter writer = new RequestLogWriter("./logs/jetty-yyyy_mm_dd.request.log"); + CustomRequestLog requestLog = new CustomRequestLog(writer, CustomRequestLog.EXTENDED_NCSA_FORMAT); + writer.setRetainDays(90); + writer.setAppend(true); + requestLogHandler.setRequestLog(requestLog); + + // Add the handlers we created above to the server. The order in which they're + // added is significant; the web server travels down the handler list until + // it finds a match. + HandlerCollection handlerCollection = new HandlerCollection(); + handlerCollection.setHandlers(webSocketHandlerList.toArray(new Handler[0])); + server.setHandler(handlerCollection); + + // We've configured the web server to manage several types of content: html, + // jsp pages, and web sockets. + + // Start listening for native DIS on the local TCP/IP network. + int port = Integer.parseInt(config.getProperty("disPort")); + String mcastString = config.getProperty("multicastAddress"); + MulticastSocket s = DisSocketFactory.getDisSocket(port, mcastString); // bcast if null + + // No mcast group specified in config file? Use bcast. + if(mcastString == null) + { + listener = new DisNative(s, null, port); + } + else + { + InetAddress mcast = InetAddress.getByName(mcastString); + listener = new DisNative(s, mcast, port); + } + + // Add the native DIS network to the list of things that will be notified + // if a packet arrives from a web client + ConnectionManager.getConnectionManager().addConnection(listener); + + // Run the native listening thread + Thread aThread = new Thread(listener, listener.getClass().getName()); + aThread.setDaemon(true); + aThread.start(); + System.out.println("Started listening for DIS on UDP port " + port); + + // Start the http server + System.out.println("Starting websocket server on TCP port " + webserverPort); + server.start(); + } + + /** + * Entry point.Create a new Server class, initialize it, and start it. Give + * user a clean way to exit program gracefully by typing a "q" on the + * command line. + * @param args + */ + public static void main(String[] args) + { + WebSocketServer wss = new WebSocketServer(); + + Runnable r = () -> { + + Scanner scan = new Scanner(System.in); + String line; + while (true) { + System.out.println("Type q/enter to quit"); + line = scan.nextLine(); + if (line.equalsIgnoreCase("q")) { + + // This should clear the queue for the ConnectionManager + ConnectionManager.getConnectionManager().removeConnection(wss.listener); + wss.listener.quit(); + + System.out.println("... QUIT"); + + try { + wss.server.stop(); + wss.server.join(); + } catch (Exception ex) { + Logger.getLogger(WebSocketServer.class.getName()).log(Level.SEVERE, null, ex); + } + + wss.webSocketHandlerList.clear(); + break; + } + } + }; + + Thread t = new Thread(r, "Quit Thread"); + t.setDaemon(true); + t.start(); + + try { + wss.server.join(); + } catch (InterruptedException ex) { + Logger.getLogger(WebSocketServer.class.getName()).log(Level.SEVERE, null, ex); + } + } + +}