├── assets
├── icon.png
├── nc93322.mp3
└── background.svg
├── .github
└── FUNDING.yml
├── LICENSE
├── server.js
├── readme.md
├── index.html
├── styles.css
├── main.js
├── libs
├── teal.js
└── cannon.min.js
└── dice.js
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarahRosannaBusch/dice/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/nc93322.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarahRosannaBusch/dice/HEAD/assets/nc93322.mp3
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: sarahRosannaBusch
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright © 2022 dice authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @file server.js
5 | * @brief basic nodeJS web server
6 | * @author Sarah Rosanna Busch
7 | * @date 19 July 2022
8 | */
9 |
10 | const http = require('http');
11 | const url = require('url');
12 | const fs = require('fs');
13 |
14 | const PORT = 9999;
15 |
16 | // follow the steps at https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTPS-server/
17 | // to use https instead of http
18 | //
19 | // const https = require('https');
20 | // const options = {
21 | // key: fs.readFileSync('key.pem'),
22 | // cert: fs.readFileSync('cert.pem')
23 | // };
24 | //
25 | // const server = new https.createServer(options, function (req, res) {
26 |
27 | const server = new http.createServer(function (req, res) {
28 | var query = url.parse(req.url, true);
29 | var filename = __dirname + query.pathname;
30 |
31 | if(req.method === 'POST') {
32 | req.setEncoding('utf8');
33 | req.on('data', function(data) {
34 | console.log(data);
35 | res.write(JSON.stringify({ack:true}));
36 | res.end();
37 | });
38 | } else if(req.method === 'GET') {
39 | fs.readFile(filename, function(err, data) {
40 | if(err) {
41 | res.writeHead(404, {'Content-Type': 'text'});
42 | return res.end("404 File Not Found: " + filename);
43 | }
44 | var mimeType = filename.match(/(?:html|js|css|svg)$/i);
45 | if(mimeType && mimeType[0] === 'svg') {
46 | mimeType = 'image/svg+xml';
47 | } else {
48 | mimeType = mimeType ? 'text/' + mimeType : 'text/plain';
49 | }
50 |
51 | console.log('serving: ' + filename);
52 | res.writeHead(200, {'Content-Type': mimeType });
53 | res.write(data);
54 | res.end();
55 | });
56 | }
57 |
58 | });
59 |
60 | server.listen(PORT);
61 |
62 | server.once('listening', function() {
63 | console.log('server listening on port ' + PORT);
64 | });
65 |
66 | server.on('error', function(e) {
67 | console.log('error code: ' + e.code);
68 | });
69 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # 3D DICE ROLLER
2 |
3 | My goal here is to make a truly satisfying dice roller; One that's the next best thing to rolling real dice. This library animates polyhedrons with numbers on each face that are visible while rolling, and includes a sound effect. Supports d4, d6, d8, d10, d12, d20, and d100 plus modifiers. Can be used to create stand-alone dice roller apps, or to integrate dice rolling into your javascript-based game. Visit https://sarahrosannabusch.ca/#diceRoller to see a demo of how it works.
4 |
5 | # Usage
6 |
7 | Clone this repo and open index.html in a web browser. (Note that the index.html, main.js, and styles.css files included here are intended as a demo and should be replaced with your own application.)
8 |
9 | ## dice.js
10 |
11 | Create a new dice_box and pass it the dom element you want to contain it. (The dice box canvas will fill to the size of the container element.)
12 |
13 | ```javascript
14 | var box = new DICE.dice_box(elem);
15 | ```
16 |
17 | Set the dice to be rolled by passing a string in the format "1d4+2d6+3".
18 |
19 | ```javascript
20 | box.setDice(string);
21 | ```
22 |
23 | Call start_throw() to roll the current set of dice.
24 |
25 | ```javascript
26 | box.start_throw();
27 | ```
28 |
29 | Or bind throw to a swipe event on the element passed into bind_swipe()
30 |
31 | ```javascript
32 | box.bind_swipe(elem);
33 | ```
34 |
35 | Optional callback functions can be passed into both start_throw() and bind_swipe().
36 |
37 | ```javascript
38 | box.start_throw(before_roll, after_roll);
39 | box.bind_swipe(elem, before_roll, after_roll);
40 |
41 | // @brief callback function called when dice roll event starts
42 | // @param notation indicates which dice are going to roll
43 | // @return null for random result || array of desired results
44 | function before_roll(notation) {
45 | console.log('before_roll notation: ' + JSON.stringify(notation));
46 | //do some stuff before roll starts
47 | return null;
48 | }
49 |
50 | // @brief callback function called once dice stop moving
51 | // @param notation now includes results
52 | function after_roll(notation) {
53 | console.log('after_roll notation: ' + JSON.stringify(notation));
54 | //do something with the results
55 | }
56 | ```
57 |
58 | Dice notation object structured as follows:
59 |
60 | ```javascript
61 | {
62 | "set":["d100","d10","d4","d6","d8","d12","d20"],
63 | "constant":0,
64 | "result":[10,9,1,5,2,8,18],
65 | "resultTotal":53,
66 | "resultString":"10 9 1 5 2 8 18 = 53",
67 | "error":false
68 | }
69 | ```
70 |
71 |
72 | # CREDITS
73 |
74 | This project is derived from the work of Anton Natarov (aka Teal) found at http://www.teall.info/2014/01/online-3d-dice-roller.html and uses [three.js](https://github.com/mrdoob/three.js/) and [canon.js](https://github.com/schteppe/cannon.js) for the geometry and physics.
75 |
76 | Sound Effect courtesy of http://commons.nicovideo.jp/material/nc93322 and https://github.com/chukwumaijem/roll-a-die.
77 |
78 |
79 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 3D Dice Roller
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
33 |
34 |
35 | Wow that's a lot of dice!
36 | [Limit: 20]
37 |
38 |
39 |
40 |
Swipe to roll dice
41 |
42 |
43 |
44 |
45 |
46 | | del |
47 | bksp |
48 |
49 |
50 | | 7 |
51 | 8 |
52 | 9 |
53 | + |
54 |
55 |
56 | | 4 |
57 | 5 |
58 | 6 |
59 |
60 |
61 | | 1 |
62 | 2 |
63 | 3 |
64 | - |
65 |
66 |
67 | | 0 |
68 | d |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | /* @brief 3d dice roller app stylesheet
2 | @author Sarah Rosanna Busch
3 | @date 10 Aug 2023
4 | @version 0.1
5 | */
6 |
7 | * {
8 | box-sizing: border-box; /* padding and border are included in total width/height of all elements */
9 | margin: 0px;
10 | font-size: 14pt;
11 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
12 | }
13 |
14 | body {
15 | margin: 0;
16 | overflow-x: hidden;
17 | background: #202020;
18 | background-image: url("assets/background.svg");
19 | background-repeat: no-repeat;
20 | background-position: right bottom;
21 | background-size: cover;
22 | height: 100vh;
23 | width: 100vw;
24 | }
25 |
26 | header {
27 | text-align: center;
28 | color: white;
29 | padding: 5px;
30 | }
31 |
32 | #helpBtn {
33 | position: absolute;
34 | top: 5px;
35 | right: 5px;
36 | width: 1.2rem;
37 | height: 1.2rem;
38 | border: none;
39 | border-radius: 0.6rem;
40 | }
41 |
42 | /* ******* DICE ROLLER *********** */
43 |
44 | #diceRoller {
45 | position: absolute;
46 | top: 0;
47 | height: calc(100% - 4.5rem);
48 | width: 100%;
49 | margin-top: 1.5rem;
50 | z-index:-1;
51 | overflow: hidden;
52 | }
53 |
54 | #canvas {
55 | width: 100%;
56 | height: 100%;
57 | }
58 |
59 | /* ************* UI ************* */
60 |
61 | #diceRollerUI {
62 | width: 100vw;
63 | height: 100vh;
64 | margin: 0;
65 | overflow: hidden;
66 | display: flex;
67 | flex-direction: column;
68 | }
69 |
70 | #diceLimit {
71 | text-align: center;
72 | color: red;
73 | background-color: #050505;
74 | padding: 0.5rem;
75 | font-size: 10pt;
76 | font-weight: bold;
77 | }
78 |
79 | .top_field {
80 | text-align: center;
81 | width: 100%;
82 | background-color: rgba(150,150,150,0.1);
83 | }
84 |
85 | #textInput {
86 | text-align: center;
87 | border: none;
88 | color: rgba(0, 0, 0, 0.8);
89 | background-color: rgba(199, 199, 199, 0.7);
90 | width: 100%;
91 | text-overflow: ellipsis;
92 | padding: 0.2em;
93 | text-decoration: none;
94 | }
95 |
96 | #textInput:focus {
97 | background-color: rgba(255, 255, 255, 0.7);
98 | outline: none;
99 | }
100 |
101 | .center_field {
102 | position: relative;
103 | flex: 1;
104 | text-align: center;
105 | height: 80%;
106 | width: 100%;
107 | z-index: 100;
108 | }
109 |
110 | .center_field * {
111 | position: relative;
112 | background-color: rgba(255, 255, 255, 0.6);
113 | padding: 5px 15px;
114 | user-select: none;
115 | }
116 |
117 | #instructions {
118 | background: none;
119 | height: 100%;
120 | width: 100%;
121 | }
122 |
123 | #instructions p {
124 | color: rgb(255, 255, 255);
125 | background: none;
126 | margin: auto;
127 | top: 35%;
128 | padding: 1em;
129 | }
130 |
131 | #numPad { /* container */
132 | position: absolute;
133 | width: 200px;
134 | height: auto;
135 | top: 50%;
136 | left: 50%;
137 | transform: translate(-50%, -50%);
138 | }
139 |
140 | .numPad { /* table */
141 | width: 200px;
142 | margin: auto;
143 | padding-top: 15px;
144 | padding-bottom: 15px;
145 | }
146 |
147 | .numPad td {
148 | cursor: pointer;
149 | }
150 |
151 | #numPad button {
152 | padding: 0.5em;
153 | margin-top: 5px;
154 | width: 97px;
155 | border: none;
156 | background-color: rgba(255, 255, 255, 0.8);
157 | color: black;
158 | }
159 |
160 | .bottom_field {
161 | position: absolute;
162 | bottom: 0;
163 | text-align: center;
164 | min-height: 3rem;
165 | background: rgba(0, 0, 0, 0.4);
166 | width: inherit;
167 | padding: 0px;
168 | }
169 |
170 | #result {
171 | position: relative;
172 | display: block;
173 | min-height: 1.5em;
174 | bottom: 5px;
175 | word-spacing: 0.5em;
176 | color: rgba(255,255,255,0.9);
177 | padding: 0.5em;
178 | margin-left: 5px;
179 | margin-right: 5px;
180 | }
181 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /** @brief 3d dice roller web app
4 | * @author Sarah Rosanna Busch
5 | * @date 10 Aug 2023
6 | * @version 0.1
7 | */
8 |
9 | window.onkeydown = function(e) {
10 | //console.log(e.code);
11 | if(e.code === "Enter" || e.code === "Escape") {
12 | main.setInput(); //closes numPad
13 | }
14 | }
15 |
16 | var main = (function() {
17 | var that = {};
18 | var elem = {};
19 | var vars = {
20 | numpadShowing: false,
21 | lastVal: '',
22 | userTyping: false
23 | }
24 | var box = null;
25 |
26 | that.init = function() {
27 | elem.container = $t.id('diceRoller');
28 | elem.result = $t.id('result');
29 | elem.textInput = $t.id('textInput');
30 | elem.numPad = $t.id('numPad');
31 | elem.instructions = $t.id('instructions');
32 | elem.center_div = $t.id('center_div');
33 | elem.diceLimit = $t.id('diceLimit');
34 |
35 | box = new DICE.dice_box(elem.container);
36 | box.bind_swipe(elem.center_div, before_roll, after_roll);
37 |
38 | $t.bind(elem.textInput, 'change', function(ev) { //shows instructions
39 | show_instructions();
40 | });
41 | $t.bind(elem.textInput, 'input', function(ev) {
42 | let size = elem.textInput.value.length;
43 | elem.textInput.size = size > 0 ? size : 1;
44 | box.setDice(textInput.value);
45 | });
46 | $t.bind(elem.textInput, 'focus', function(ev) {
47 | elem.diceLimit.style.display = 'none';
48 | //ev.preventDefault();
49 | if(!vars.numpadShowing) {
50 | show_instructions(false);
51 | show_numPad(true);
52 | } else if(vars.userTyping) {
53 | _handleInput();
54 | vars.userTyping = false;
55 | }
56 | });
57 | $t.bind(elem.textInput, 'blur', function(ev) {
58 | //necessary to do this here for iOS compatibility
59 | //because they put cursor back to zero on blur
60 | vars.caretPos = elem.textInput.selectionStart;
61 | vars.selectionEnd = elem.textInput.selectionEnd;
62 | });
63 | $t.bind(elem.textInput, 'mouseup', function(ev) {
64 | ev.preventDefault();
65 | });
66 |
67 | box.setDice(textInput.value);
68 | //box.start_throw(); //start by throwing all the dice on the table
69 |
70 | show_instructions(true);
71 | }
72 |
73 | that.setInput = function() {
74 | let inputVal = elem.textInput.value;
75 | //check for d100 and add tens place die
76 | if(inputVal.includes('d100')) {
77 | let dIdx = inputVal.indexOf('d100');
78 | let numD100 = '';
79 | for(let i = dIdx - 1; i >= 0; i--) {
80 | let digit = inputVal[i];
81 | if(!isNaN(digit)) {
82 | numD100 = digit + numD100;
83 | } else {
84 | break;
85 | }
86 | }
87 | if(numD100 === '') numD100 = '1';
88 | //console.log('num d100s: ' + numD100);
89 | for(let i = 0; i < numD100; i++) {
90 | inputVal += '+d9';
91 | }
92 | }
93 | //check for too many dice
94 | let d = DICE.parse_notation(inputVal);
95 | let numDice = d.set.length;
96 | if(numDice > 20) {
97 | elem.diceLimit.style.display = 'block';
98 | } else {
99 | box.setDice(inputVal);
100 | show_numPad(false);
101 | show_instructions(true);
102 | }
103 | }
104 |
105 | that.clearInput = function() {
106 | elem.textInput.value = '';
107 | }
108 |
109 | //called from numPad onclicks
110 | that.input = function(value) {
111 | vars.lastVal = value;
112 | vars.userTyping = true;
113 | elem.textInput.focus();
114 | }
115 |
116 | function _handleInput() {
117 | let text = elem.textInput.value;
118 | let selectedText = (vars.caretPos === vars.selectionEnd) ? false : true;
119 | if(vars.lastVal === "del") {
120 | if(selectedText) {
121 | deleteText();
122 | } else {
123 | text = text.substring(0, vars.caretPos) + text.substring(vars.caretPos+1, text.length);
124 | }
125 | } else if(vars.lastVal === "bksp") {
126 | if(selectedText) {
127 | deleteText();
128 | } else {
129 | text = text.substring(0, vars.caretPos-1) + text.substring(vars.caretPos, text.length);
130 | vars.caretPos--;
131 | }
132 | } else {
133 | deleteText();
134 | text = text.substring(0, vars.caretPos) + vars.lastVal + text.substring(vars.caretPos, text.length);
135 | vars.caretPos++;
136 | }
137 | elem.textInput.value = text;
138 | setTimeout(() => {
139 | elem.textInput.setSelectionRange(vars.caretPos, vars.caretPos);
140 | }, 1);
141 |
142 | function deleteText() {
143 | text = text.substring(0, vars.caretPos) + text.substring(vars.selectionEnd, text.length);
144 | setTimeout(() => {
145 | elem.textInput.setSelectionRange(vars.caretPos, vars.caretPos);
146 | }, 1);
147 | }
148 | }
149 |
150 | // show 'Roll Dice' swipe instructions
151 | // param show = bool
152 | function show_instructions(show) {
153 | if(show) {
154 | elem.instructions.style.display = 'inline-block';
155 | } else {
156 | elem.instructions.style.display = 'none';
157 | }
158 | }
159 |
160 | // show input options
161 | // param show = bool
162 | function show_numPad(show) {
163 | if(show) {
164 | vars.numpadShowing = true;
165 | elem.numPad.style.display = 'inline-block';
166 | elem.textInput.focus();
167 | } else {
168 | vars.numpadShowing = false;
169 | elem.textInput.blur();
170 | elem.numPad.style.display = 'none';
171 | }
172 | }
173 |
174 | // @brief callback function called when dice roll event starts
175 | // @param notation indicates which dice are going to roll
176 | // @return null for random result || array of desired results
177 | function before_roll(notation) {
178 | //console.log('before_roll notation: ' + JSON.stringify(notation));
179 | show_instructions(false);
180 | elem.result.innerHTML = '';
181 | return null;
182 | }
183 |
184 | // @brief callback function called once dice stop moving
185 | // @param notation now includes results
186 | function after_roll(notation) {
187 | //console.log('after_roll notation: ' + JSON.stringify(notation));
188 | if(notation.result[0] < 0) {
189 | elem.result.innerHTML = "Oops, your dice fell off the table.
Refresh and roll again."
190 | } else {
191 | elem.result.innerHTML = notation.resultString;
192 | }
193 | }
194 |
195 | return that;
196 | }());
197 |
--------------------------------------------------------------------------------
/libs/teal.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | window.teal = {};
4 | window.$t = window.teal;
5 |
6 | teal.copyto = function(obj, res) {
7 | if (obj == null || typeof obj !== 'object') return obj;
8 | if (obj instanceof Array) {
9 | for (var i = obj.length - 1; i >= 0; --i)
10 | res[i] = $t.copy(obj[i]);
11 | }
12 | else {
13 | for (var i in obj) {
14 | if (obj.hasOwnProperty(i))
15 | res[i] = $t.copy(obj[i]);
16 | }
17 | }
18 | return res;
19 | }
20 |
21 | teal.copy = function(obj) {
22 | if (!obj) return obj;
23 | return teal.copyto(obj, new obj.constructor());
24 | }
25 |
26 | teal.element = function(name, props, place, content) {
27 | var dom = document.createElement(name);
28 | if (props) for (var i in props) dom.setAttribute(i, props[i]);
29 | if (place) place.appendChild(dom);
30 | if (content !== undefined) $t.inner(content, dom);
31 | return dom;
32 | }
33 |
34 | teal.inner = function(obj, sel) {
35 | sel.appendChild(obj.nodeName != undefined ? obj : document.createTextNode(obj));
36 | return sel;
37 | }
38 |
39 | teal.id = function(id) {
40 | return document.getElementById(id);
41 | }
42 |
43 | teal.set = function(sel, props) {
44 | for (var i in props) sel.setAttribute(i, props[i]);
45 | return sel;
46 | }
47 |
48 | teal.clas = function(sel, oldclass, newclass) {
49 | var oc = oldclass ? oldclass.split(/\s+/) : [],
50 | nc = newclass ? newclass.split(/\s+/) : [],
51 | classes = (sel.getAttribute('class') || '').split(/\s+/);
52 | if (!classes[0]) classes = [];
53 | for (var i in oc) {
54 | var ind = classes.indexOf(oc[i]);
55 | if (ind >= 0) classes.splice(ind, 1);
56 | }
57 | for (var i in nc) {
58 | if (nc[i] && classes.indexOf(nc[i]) < 0) classes.push(nc[i]);
59 | }
60 | sel.setAttribute('class', classes.join(' '));
61 | }
62 |
63 | teal.empty = function(sel) {
64 | if (sel.childNodes)
65 | while (sel.childNodes.length)
66 | sel.removeChild(sel.firstChild);
67 | }
68 |
69 | teal.remove = function(sel) {
70 | if (sel) {
71 | if (sel.parentNode) sel.parentNode.removeChild(sel);
72 | else for (var i = sel.length - 1; i >= 0; --i)
73 | sel[i].parentNode.removeChild(sel[i]);
74 | }
75 | }
76 |
77 | teal.bind = function(sel, eventname, func, bubble) {
78 | if (!sel) return;
79 | if (eventname.constructor === Array) {
80 | for (var i in eventname)
81 | sel.addEventListener(eventname[i], func, bubble ? bubble : false);
82 | }
83 | else
84 | sel.addEventListener(eventname, func, bubble ? bubble : false);
85 | }
86 |
87 | teal.unbind = function(sel, eventname, func, bubble) {
88 | if (eventname.constructor === Array) {
89 | for (var i in eventname)
90 | sel.removeEventListener(eventname[i], func, bubble ? bubble : false);
91 | }
92 | else
93 | sel.removeEventListener(eventname, func, bubble ? bubble : false);
94 | }
95 |
96 | teal.one = function(sel, eventname, func, bubble) {
97 | var one_func = function(e) {
98 | func.call(this, e);
99 | teal.unbind(sel, eventname, one_func, bubble);
100 | };
101 | teal.bind(sel, eventname, one_func, bubble);
102 | }
103 |
104 | teal.raise_event = function(sel, eventname, bubble, cancelable) {
105 | var evt = document.createEvent('UIEvents');
106 | evt.initEvent(eventname, bubble == undefined ? true : bubble,
107 | cancelable == undefined ? true : cancelable);
108 | sel.dispatchEvent(evt);
109 | }
110 |
111 | teal.raise = function(sel, eventname, params, bubble, cancelable) {
112 | var ev = document.createEvent("CustomEvent");
113 | ev.initCustomEvent(eventname, bubble, cancelable, params);
114 | sel.dispatchEvent(ev);
115 | }
116 |
117 | if (!document.getElementsByClassName) {
118 | teal.get_elements_by_class = function(classes, node) {
119 | var node = node || document,
120 | list = node.getElementsByTagName('*'),
121 | cl = classes.split(/\s+/),
122 | result = [];
123 |
124 | for (var i = list.length - 1; i >= 0; --i) {
125 | for (var j = cl.length - 1; j >= 0; --j) {
126 | var clas = list[i].getAttribute('class');
127 | if (clas && clas.search('\\b' + cl[j] + '\\b') != -1) {
128 | result.push(list[i]);
129 | break;
130 | }
131 | }
132 | }
133 | return result;
134 | }
135 | }
136 | else {
137 | teal.get_elements_by_class = function(classes, node) {
138 | return (node || document).getElementsByClassName(classes);
139 | }
140 | }
141 |
142 | teal.rpc = function(params, callback, noparse) {
143 | var ajax = new XMLHttpRequest();
144 | ajax.open("post", 'f', true);
145 | ajax.onreadystatechange = function() {
146 | if (ajax.readyState == 4)
147 | callback.call(ajax, noparse ? ajax.responseText : JSON.parse(ajax.responseText));
148 | };
149 | ajax.send(JSON.stringify(params));
150 | }
151 |
152 | teal.uuid = function() {
153 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
154 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
155 | return v.toString(16);
156 | });
157 | }
158 |
159 | teal.get_url_params = function() {
160 | var params = window.location.search.substring(1).split("&");
161 | var res = {};
162 | for (var i in params) {
163 | var keyvalue = params[i].split("=");
164 | res[keyvalue[0]] = decodeURI(keyvalue[1]);
165 | }
166 | return res;
167 | }
168 |
169 | teal.get_mouse_coords = function(ev) {
170 | var touches = ev.changedTouches;
171 | if (touches) return { x: touches[0].clientX, y: touches[0].clientY };
172 | return { x: ev.clientX, y: ev.clientY };
173 | }
174 |
175 | teal.deferred = function() {
176 | var solved = false, callbacks = [], args = [];
177 | function solve() {
178 | while (callbacks.length) {
179 | callbacks.shift().apply(this, args);
180 | }
181 | }
182 | return {
183 | promise: function() {
184 | return {
185 | then: function(callback) {
186 | var deferred = teal.deferred(), promise = deferred.promise();
187 | callbacks.push(function() {
188 | var res = callback.apply(this, arguments);
189 | if (res && 'done' in res) res.done(deferred.resolve);
190 | else deferred.resolve.apply(this, arguments);
191 | });
192 | return promise;
193 | },
194 | done: function(callback) {
195 | callbacks.push(callback);
196 | if (solved) solve();
197 | return this;
198 | },
199 | cancel: function() {
200 | callbacks = [];
201 | }
202 | };
203 | },
204 | resolve: function() {
205 | solved = true;
206 | args = Array.prototype.slice.call(arguments, 0);
207 | solve();
208 | }
209 | };
210 | }
211 |
212 | teal.when = function(promises) {
213 | var deferred = teal.deferred();
214 | var count = promises.length, ind = 0;
215 | if (count == 0) deferred.resolve();
216 | for (var i = 0; i < count; ++i) {
217 | promises[i].done(function() {
218 | if (++ind == count) deferred.resolve();
219 | });
220 | }
221 | return deferred.promise();
222 | }
223 |
224 |
--------------------------------------------------------------------------------
/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
248 |
--------------------------------------------------------------------------------
/dice.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /**
4 | * @brief generates polyhedral dice with roll animation and result calculation
5 | * @author Anton Natarov aka Teal (original author)
6 | * @author Sarah Rosanna Busch (refactor, see changelog)
7 | * @date 10 Aug 2023
8 | * @version 1.1
9 | * @dependencies teal.js, cannon.js, three.js
10 | */
11 |
12 | /**
13 | * CHANGELOG
14 | * - tweaked scaling to make dice look nice on mobile
15 | * - removed dice selector feature (separating UI from dice roller)
16 | * - file reorg (moving variable declarations to top, followed by public then private functions)
17 | * - removing true random option (was cool but not worth the extra dependencies or complexity)
18 | * - removing mouse event bindings (separating UI from dice roller)
19 | * - refactoring to module pattern and reducing publically available properties/methods
20 | * - removing dice notation getter callback in favour of setting dice to roll directly
21 | * - adding sound effect
22 | * - adding roll results to notation returned in after_roll callback
23 | * - adding 'd9' option (d10 to be added to d100 properly)
24 | */
25 |
26 | const DICE = (function() {
27 | var that = {};
28 |
29 | var vars = { //todo: make these configurable on init
30 | frame_rate: 1 / 60,
31 | scale: 100, //dice size
32 |
33 | material_options: {
34 | specular: 0x172022,
35 | color: 0xf0f0f0,
36 | shininess: 40,
37 | shading: THREE.FlatShading,
38 | },
39 | label_color: '#aaaaaa', //numbers on dice
40 | dice_color: '#202020',
41 | ambient_light_color: 0xf0f0f0,
42 | spot_light_color: 0xefefef,
43 | desk_color: '#101010', //canvas background
44 | desk_opacity: 0.5,
45 | use_shadows: true,
46 | use_adapvite_timestep: true //todo: setting this to false improves performace a lot. but the dice rolls don't look as natural...
47 |
48 | }
49 |
50 | const CONSTS = {
51 | known_types: ['d4', 'd6', 'd8', 'd9', 'd10', 'd12', 'd20', 'd100'],
52 | dice_face_range: { 'd4': [1, 4], 'd6': [1, 6], 'd8': [1, 8], 'd9': [0, 9], 'd10': [0, 9],
53 | 'd12': [1, 12], 'd20': [1, 20], 'd100': [0, 9] },
54 | dice_mass: { 'd4': 300, 'd6': 300, 'd8': 340, 'd9': 350, 'd10': 350, 'd12': 350, 'd20': 400, 'd100': 350 },
55 | dice_inertia: { 'd4': 5, 'd6': 13, 'd8': 10, 'd9': 9, 'd10': 9, 'd12': 8, 'd20': 6, 'd100': 9 },
56 |
57 | standart_d20_dice_face_labels: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
58 | '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'],
59 | standart_d100_dice_face_labels: [' ', '00', '10', '20', '30', '40', '50',
60 | '60', '70', '80', '90'],
61 |
62 | d4_labels: [
63 | [[], [0, 0, 0], [2, 4, 3], [1, 3, 4], [2, 1, 4], [1, 2, 3]],
64 | [[], [0, 0, 0], [2, 3, 4], [3, 1, 4], [2, 4, 1], [3, 2, 1]],
65 | [[], [0, 0, 0], [4, 3, 2], [3, 4, 1], [4, 2, 1], [3, 1, 2]],
66 | [[], [0, 0, 0], [4, 2, 3], [1, 4, 3], [4, 1, 2], [1, 3, 2]]
67 | ]
68 | }
69 |
70 | // DICE BOX OBJECT
71 |
72 | // @brief constructor; create a new instance of this to initialize the canvas
73 | // @param container element to contain canvas; canvas will fill container
74 | that.dice_box = function(container) {
75 | this.dices = [];
76 | this.scene = new THREE.Scene();
77 | this.world = new CANNON.World();
78 | this.diceToRoll = ''; //user input
79 | this.container = container;
80 |
81 | this.renderer = window.WebGLRenderingContext
82 | ? new THREE.WebGLRenderer({ antialias: true, alpha: true })
83 | : new THREE.CanvasRenderer({ antialias: true, alpha: true });
84 | container.appendChild(this.renderer.domElement);
85 | this.renderer.shadowMap.enabled = true;
86 | this.renderer.shadowMap.type = THREE.PCFShadowMap;
87 | this.renderer.setClearColor(0xffffff, 0); //color, alpha
88 |
89 | this.reinit(container);
90 | $t.bind(container, 'resize', function() {
91 | //todo: this doesn't work :(
92 | this.reinit(elem.canvas);
93 | });
94 |
95 | this.world.gravity.set(0, 0, -9.8 * 800);
96 | this.world.broadphase = new CANNON.NaiveBroadphase();
97 | this.world.solver.iterations = 16;
98 |
99 | var ambientLight = new THREE.AmbientLight(vars.ambient_light_color);
100 | this.scene.add(ambientLight);
101 |
102 | this.dice_body_material = new CANNON.Material();
103 | var desk_body_material = new CANNON.Material();
104 | var barrier_body_material = new CANNON.Material();
105 | this.world.addContactMaterial(new CANNON.ContactMaterial(
106 | desk_body_material, this.dice_body_material, 0.01, 0.5));
107 | this.world.addContactMaterial(new CANNON.ContactMaterial(
108 | barrier_body_material, this.dice_body_material, 0, 1.0));
109 | this.world.addContactMaterial(new CANNON.ContactMaterial(
110 | this.dice_body_material, this.dice_body_material, 0, 0.5));
111 |
112 | this.world.add(new CANNON.RigidBody(0, new CANNON.Plane(), desk_body_material));
113 | var barrier;
114 | barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
115 | barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2);
116 | barrier.position.set(0, this.h * 0.93, 0);
117 | this.world.add(barrier);
118 |
119 | barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
120 | barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
121 | barrier.position.set(0, -this.h * 0.93, 0);
122 | this.world.add(barrier);
123 |
124 | barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
125 | barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
126 | barrier.position.set(this.w * 0.93, 0, 0);
127 | this.world.add(barrier);
128 |
129 | barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
130 | barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), Math.PI / 2);
131 | barrier.position.set(-this.w * 0.93, 0, 0);
132 | this.world.add(barrier);
133 |
134 | this.last_time = 0;
135 | this.running = false;
136 |
137 | this.renderer.render(this.scene, this.camera);
138 | }
139 |
140 | // called on init and window resize
141 | that.dice_box.prototype.reinit = function(container) {
142 | this.cw = container.clientWidth / 2;
143 | this.ch = container.clientHeight / 2;
144 | this.w = this.cw;
145 | this.h = this.ch;
146 | this.aspect = Math.min(this.cw / this.w, this.ch / this.h);
147 | vars.scale = Math.sqrt(this.w * this.w + this.h * this.h) / 8;
148 | //console.log('scale = ' + vars.scale);
149 |
150 | this.renderer.setSize(this.cw * 2, this.ch * 2);
151 |
152 | this.wh = this.ch / this.aspect / Math.tan(10 * Math.PI / 180);
153 | if (this.camera) this.scene.remove(this.camera);
154 | this.camera = new THREE.PerspectiveCamera(20, this.cw / this.ch, 1, this.wh * 1.3);
155 | this.camera.position.z = this.wh;
156 |
157 | var mw = Math.max(this.w, this.h);
158 | if (this.light) this.scene.remove(this.light);
159 | this.light = new THREE.SpotLight(vars.spot_light_color, 2.0);
160 | this.light.position.set(-mw / 2, mw / 2, mw * 2);
161 | this.light.target.position.set(0, 0, 0);
162 | this.light.distance = mw * 5;
163 | this.light.castShadow = true;
164 | this.light.shadowCameraNear = mw / 10;
165 | this.light.shadowCameraFar = mw * 5;
166 | this.light.shadowCameraFov = 50;
167 | this.light.shadowBias = 0.001;
168 | this.light.shadowDarkness = 1.1;
169 | this.light.shadowMapWidth = 1024;
170 | this.light.shadowMapHeight = 1024;
171 | this.scene.add(this.light);
172 |
173 | if (this.desk) this.scene.remove(this.desk);
174 | this.desk = new THREE.Mesh(new THREE.PlaneGeometry(this.w * 2, this.h * 2, 1, 1),
175 | new THREE.MeshPhongMaterial({ color: vars.desk_color, opacity: vars.desk_opacity, transparent: true }));
176 | this.desk.receiveShadow = vars.use_shadows;
177 | this.scene.add(this.desk);
178 |
179 | this.renderer.render(this.scene, this.camera);
180 | }
181 |
182 | // @param diceToRoll (string), ex: "1d100+1d10+1d4+1d6+1d8+1d12+1d20"
183 | that.dice_box.prototype.setDice = function(diceToRoll) {
184 | this.diceToRoll = diceToRoll;
185 | }
186 |
187 | //call this to roll dice programatically or from click
188 | that.dice_box.prototype.start_throw = function(before_roll, after_roll) {
189 | var box = this;
190 | if (box.rolling) return;
191 |
192 | var vector = { x: (rnd() * 2 - 1) * box.w, y: -(rnd() * 2 - 1) * box.h };
193 | var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
194 | var boost = (rnd() + 3) * dist;
195 | throw_dices(box, vector, boost, dist, before_roll, after_roll);
196 | }
197 |
198 | //call this to roll dice from swipe (will throw dice in direction swiped)
199 | that.dice_box.prototype.bind_swipe = function(container, before_roll, after_roll) {
200 | let box = this;
201 | $t.bind(container, ['mousedown', 'touchstart'], function(ev) {
202 | ev.preventDefault();
203 | box.mouse_time = (new Date()).getTime();
204 | box.mouse_start = $t.get_mouse_coords(ev);
205 | });
206 | $t.bind(container, ['mouseup', 'touchend'], function(ev) {
207 | if (box.rolling) return;
208 | if (box.mouse_start == undefined) return;
209 | var m = $t.get_mouse_coords(ev);
210 | var vector = { x: m.x - box.mouse_start.x, y: -(m.y - box.mouse_start.y) };
211 | box.mouse_start = undefined;
212 | var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
213 | if (dist < Math.sqrt(box.w * box.h * 0.01)) return;
214 | var time_int = (new Date()).getTime() - box.mouse_time;
215 | if (time_int > 2000) time_int = 2000;
216 | var boost = Math.sqrt((2500 - time_int) / 2500) * dist * 2;
217 | throw_dices(box, vector, boost, dist, before_roll, after_roll);
218 | });
219 | }
220 |
221 | function throw_dices(box, vector, boost, dist, before_roll, after_roll) {
222 | var uat = vars.use_adapvite_timestep;
223 |
224 | vector.x /= dist; vector.y /= dist;
225 | var notation = that.parse_notation(box.diceToRoll);
226 | if (notation.set.length == 0) return;
227 | //TODO: how do large numbers of vectors affect performance?
228 | var vectors = box.generate_vectors(notation, vector, boost);
229 | box.rolling = true;
230 | let request_results = null;
231 |
232 | let numDice = vectors.length;
233 | numDice = numDice > 10 ? 10 : numDice;
234 | for(let i = 0; i < numDice; i++) {
235 | let volume = i/10;
236 | if(volume <= 0) volume = 0.1;
237 | if(volume > 1) volume = 1;
238 | playSound(box.container, volume);
239 | //todo: find a better way to do this
240 | }
241 |
242 | if (before_roll) {
243 | request_results = before_roll(notation);
244 | }
245 | roll(request_results);
246 |
247 | //@param request_results (optional) - pass in an array of desired roll results
248 | //todo: when this param is used, animation isn't as smooth (uat not used?)
249 | function roll(request_results) {
250 | box.clear();
251 | box.roll(vectors, request_results || notation.result, function(result) {
252 | notation.result = result;
253 | var res = result.join(' ');
254 | if (notation.constant) {
255 | if (notation.constant > 0) res += ' +' + notation.constant;
256 | else res += ' -' + Math.abs(notation.constant);
257 | }
258 | notation.resultTotal = (result.reduce(function(s, a) { return s + a; }) + notation.constant);
259 | if (result.length > 1 || notation.constant) {
260 | res += ' = ' + notation.resultTotal;
261 | }
262 | notation.resultString = res;
263 |
264 | if (after_roll) after_roll(notation);
265 |
266 | box.rolling = false;
267 | vars.use_adapvite_timestep = uat;
268 | });
269 | }
270 | }
271 |
272 | //todo: the rest of these don't need to be public, but need to read the this properties
273 | that.dice_box.prototype.generate_vectors = function(notation, vector, boost) {
274 | var vectors = [];
275 | for (var i in notation.set) {
276 | var vec = make_random_vector(vector);
277 | var pos = {
278 | x: this.w * (vec.x > 0 ? -1 : 1) * 0.9,
279 | y: this.h * (vec.y > 0 ? -1 : 1) * 0.9,
280 | z: rnd() * 200 + 200
281 | };
282 | var projector = Math.abs(vec.x / vec.y);
283 | if (projector > 1.0) pos.y /= projector; else pos.x *= projector;
284 | var velvec = make_random_vector(vector);
285 | var velocity = { x: velvec.x * boost, y: velvec.y * boost, z: -10 };
286 | var inertia = CONSTS.dice_inertia[notation.set[i]];
287 | var angle = {
288 | x: -(rnd() * vec.y * 5 + inertia * vec.y),
289 | y: rnd() * vec.x * 5 + inertia * vec.x,
290 | z: 0
291 | };
292 | var axis = { x: rnd(), y: rnd(), z: rnd(), a: rnd() };
293 | vectors.push({ set: notation.set[i], pos: pos, velocity: velocity, angle: angle, axis: axis });
294 | }
295 | return vectors;
296 | }
297 |
298 | that.dice_box.prototype.create_dice = function(type, pos, velocity, angle, axis) {
299 | var dice = threeD_dice['create_' + type]();
300 | dice.castShadow = true;
301 | dice.dice_type = type;
302 | dice.body = new CANNON.RigidBody(CONSTS.dice_mass[type],
303 | dice.geometry.cannon_shape, this.dice_body_material);
304 | dice.body.position.set(pos.x, pos.y, pos.z);
305 | dice.body.quaternion.setFromAxisAngle(new CANNON.Vec3(axis.x, axis.y, axis.z), axis.a * Math.PI * 2);
306 | dice.body.angularVelocity.set(angle.x, angle.y, angle.z);
307 | dice.body.velocity.set(velocity.x, velocity.y, velocity.z);
308 | dice.body.linearDamping = 0.1;
309 | dice.body.angularDamping = 0.1;
310 | this.scene.add(dice);
311 | this.dices.push(dice);
312 | this.world.add(dice.body);
313 | }
314 |
315 | that.dice_box.prototype.check_if_throw_finished = function() {
316 | var res = true;
317 | var e = 6;
318 | if (this.iteration < 10 / vars.frame_rate) {
319 | for (var i = 0; i < this.dices.length; ++i) {
320 | var dice = this.dices[i];
321 | if (dice.dice_stopped === true) continue;
322 | var a = dice.body.angularVelocity, v = dice.body.velocity;
323 | if (Math.abs(a.x) < e && Math.abs(a.y) < e && Math.abs(a.z) < e &&
324 | Math.abs(v.x) < e && Math.abs(v.y) < e && Math.abs(v.z) < e) {
325 | if (dice.dice_stopped) {
326 | if (this.iteration - dice.dice_stopped > 3) {
327 | dice.dice_stopped = true;
328 | continue;
329 | }
330 | }
331 | else dice.dice_stopped = this.iteration;
332 | res = false;
333 | }
334 | else {
335 | dice.dice_stopped = undefined;
336 | res = false;
337 | }
338 | }
339 | }
340 | return res;
341 | }
342 |
343 | that.dice_box.prototype.emulate_throw = function() {
344 | while (!this.check_if_throw_finished()) {
345 | ++this.iteration;
346 | this.world.step(vars.frame_rate);
347 | }
348 | return get_dice_values(this.dices);
349 | }
350 |
351 | that.dice_box.prototype.__animate = function(threadid) {
352 | var time = (new Date()).getTime();
353 | var time_diff = (time - this.last_time) / 1000;
354 | if (time_diff > 3) time_diff = vars.frame_rate;
355 | ++this.iteration;
356 | if (vars.use_adapvite_timestep) {
357 | while (time_diff > vars.frame_rate * 1.1) {
358 | this.world.step(vars.frame_rate);
359 | time_diff -= vars.frame_rate;
360 | }
361 | this.world.step(time_diff);
362 | }
363 | else {
364 | this.world.step(vars.frame_rate);
365 | }
366 | for (var i in this.scene.children) {
367 | var interact = this.scene.children[i];
368 | if (interact.body != undefined) {
369 | interact.position.copy(interact.body.position);
370 | interact.quaternion.copy(interact.body.quaternion);
371 | }
372 | }
373 | this.renderer.render(this.scene, this.camera);
374 | this.last_time = this.last_time ? time : (new Date()).getTime();
375 | if (this.running == threadid && this.check_if_throw_finished()) {
376 | this.running = false;
377 | if (this.callback) this.callback.call(this, get_dice_values(this.dices));
378 | }
379 | if (this.running == threadid) {
380 | (function(t, tid, uat) {
381 | if (!uat && time_diff < vars.frame_rate) {
382 | setTimeout(function() { requestAnimationFrame(function() { t.__animate(tid); }); },
383 | (vars.frame_rate - time_diff) * 1000);
384 | }
385 | else requestAnimationFrame(function() { t.__animate(tid); });
386 | })(this, threadid, vars.use_adapvite_timestep);
387 | }
388 | }
389 |
390 | that.dice_box.prototype.clear = function() {
391 | this.running = false;
392 | var dice;
393 | while (dice = this.dices.pop()) {
394 | this.scene.remove(dice);
395 | if (dice.body) this.world.remove(dice.body);
396 | }
397 | if (this.pane) this.scene.remove(this.pane);
398 | this.renderer.render(this.scene, this.camera);
399 | var box = this;
400 | setTimeout(function() { box.renderer.render(box.scene, box.camera); }, 100);
401 | }
402 |
403 | that.dice_box.prototype.prepare_dices_for_roll = function(vectors) {
404 | this.clear();
405 | this.iteration = 0;
406 | for (var i in vectors) {
407 | this.create_dice(vectors[i].set, vectors[i].pos, vectors[i].velocity,
408 | vectors[i].angle, vectors[i].axis);
409 | }
410 | }
411 |
412 | that.dice_box.prototype.roll = function(vectors, values, callback) {
413 | this.prepare_dices_for_roll(vectors);
414 | if (values != undefined && values.length) {
415 | vars.use_adapvite_timestep = false;
416 | var res = this.emulate_throw();
417 | this.prepare_dices_for_roll(vectors);
418 | for (var i in res)
419 | shift_dice_faces(this.dices[i], values[i], res[i]);
420 | }
421 | this.callback = callback;
422 | this.running = (new Date()).getTime();
423 | this.last_time = 0;
424 | this.__animate(this.running);
425 | }
426 |
427 | that.dice_box.prototype.search_dice_by_mouse = function(ev) {
428 | var m = $t.get_mouse_coords(ev);
429 | var intersects = (new THREE.Raycaster(this.camera.position,
430 | (new THREE.Vector3((m.x - this.cw) / this.aspect,
431 | 1 - (m.y - this.ch) / this.aspect, this.w / 9))
432 | .sub(this.camera.position).normalize())).intersectObjects(this.dices);
433 | if (intersects.length) return intersects[0].object.userData;
434 | }
435 |
436 |
437 | // PUBLIC FUNCTIONS
438 |
439 | //validates dice notation input
440 | //notation should be in format "1d4+2d6"
441 | that.parse_notation = function(notation) {
442 | var no = notation.split('@');
443 | var dr0 = /\s*(\d*)([a-z]+)(\d+)(\s*(\+|\-)\s*(\d+)){0,1}\s*(\+|$)/gi;
444 | var dr1 = /(\b)*(\d+)(\b)*/gi;
445 | var ret = {
446 | set: [], //set of dice to roll
447 | constant: 0, //modifier to add to result
448 | result: [], //array of results of each die
449 | resultTotal: 0, //dice results + constant
450 | resultString: '', //printable result
451 | error: false //input errors are ignored gracefully
452 | };
453 | var res;
454 | //looks at each peice of the notation and adds dice and constants to results
455 | while (res = dr0.exec(no[0])) {
456 | var command = res[2];
457 | if (command != 'd') { ret.error = true; continue; }
458 | var count = parseInt(res[1]);
459 | if (res[1] == '') count = 1;
460 | var type = 'd' + res[3];
461 | if (CONSTS.known_types.indexOf(type) == -1) { ret.error = true; continue; }
462 | while (count--) ret.set.push(type);
463 | if (res[5] && res[6]) {
464 | if (res[5] == '+') ret.constant += parseInt(res[6]);
465 | else ret.constant -= parseInt(res[6]);
466 | }
467 | }
468 | while (res = dr1.exec(no[1])) {
469 | ret.result.push(parseInt(res[2]));
470 | }
471 | return ret;
472 | }
473 |
474 | that.stringify_notation = function(nn) {
475 | var dict = {}, notation = '';
476 | for (var i in nn.set)
477 | if (!dict[nn.set[i]]) dict[nn.set[i]] = 1; else ++dict[nn.set[i]];
478 | for (var i in dict) {
479 | if (notation.length) notation += ' + ';
480 | notation += (dict[i] > 1 ? dict[i] : '') + i;
481 | }
482 | if (nn.constant) {
483 | if (nn.constant > 0) notation += ' + ' + nn.constant;
484 | else notation += ' - ' + Math.abs(nn.constant);
485 | }
486 | return notation;
487 | }
488 |
489 | // PRIVATE FUNCTIONS
490 |
491 | // dice geometries
492 | let threeD_dice = {};
493 |
494 | threeD_dice.create_d4 = function() {
495 | if (!this.d4_geometry) this.d4_geometry = create_d4_geometry(vars.scale * 1.2);
496 | if (!this.d4_material) this.d4_material = new THREE.MeshFaceMaterial(
497 | create_d4_materials(vars.scale / 2, vars.scale * 2, CONSTS.d4_labels[0]));
498 | return new THREE.Mesh(this.d4_geometry, this.d4_material);
499 | }
500 |
501 | threeD_dice.create_d6 = function() {
502 | if (!this.d6_geometry) this.d6_geometry = create_d6_geometry(vars.scale * 1.1);
503 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
504 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 0.9));
505 | return new THREE.Mesh(this.d6_geometry, this.dice_material);
506 | }
507 |
508 | threeD_dice.create_d8 = function() {
509 | if (!this.d8_geometry) this.d8_geometry = create_d8_geometry(vars.scale);
510 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
511 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 1.4));
512 | return new THREE.Mesh(this.d8_geometry, this.dice_material);
513 | }
514 |
515 | threeD_dice.create_d9 = function() {
516 | if (!this.d10_geometry) this.d10_geometry = create_d10_geometry(vars.scale * 0.9);
517 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
518 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 1.0));
519 | return new THREE.Mesh(this.d10_geometry, this.dice_material);
520 | }
521 |
522 | threeD_dice.create_d10 = function() {
523 | if (!this.d10_geometry) this.d10_geometry = create_d10_geometry(vars.scale * 0.9);
524 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
525 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 1.0));
526 | return new THREE.Mesh(this.d10_geometry, this.dice_material);
527 | }
528 |
529 | threeD_dice.create_d12 = function() {
530 | if (!this.d12_geometry) this.d12_geometry = create_d12_geometry(vars.scale * 0.9);
531 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
532 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 1.0));
533 | return new THREE.Mesh(this.d12_geometry, this.dice_material);
534 | }
535 |
536 | threeD_dice.create_d20 = function() {
537 | if (!this.d20_geometry) this.d20_geometry = create_d20_geometry(vars.scale);
538 | if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
539 | create_dice_materials(CONSTS.standart_d20_dice_face_labels, vars.scale / 2, 1.2));
540 | return new THREE.Mesh(this.d20_geometry, this.dice_material);
541 | }
542 |
543 | threeD_dice.create_d100 = function() {
544 | if (!this.d10_geometry) this.d10_geometry = create_d10_geometry(vars.scale * 0.9);
545 | if (!this.d100_material) this.d100_material = new THREE.MeshFaceMaterial(
546 | create_dice_materials(CONSTS.standart_d100_dice_face_labels, vars.scale / 2, 1.5));
547 | return new THREE.Mesh(this.d10_geometry, this.d100_material);
548 | }
549 |
550 | function create_dice_materials(face_labels, size, margin) {
551 | function create_text_texture(text, color, back_color) {
552 | if (text == undefined) return null;
553 | var canvas = document.createElement("canvas");
554 | var context = canvas.getContext("2d");
555 | var ts = calc_texture_size(size + size * 2 * margin) * 2;
556 | canvas.width = canvas.height = ts;
557 | context.font = ts / (1 + 2 * margin) + "pt Arial";
558 | context.fillStyle = back_color;
559 | context.fillRect(0, 0, canvas.width, canvas.height);
560 | context.textAlign = "center";
561 | context.textBaseline = "middle";
562 | context.fillStyle = color;
563 | context.fillText(text, canvas.width / 2, canvas.height / 2);
564 | if (text == '6' || text == '9') {
565 | context.fillText(' .', canvas.width / 2, canvas.height / 2);
566 | }
567 | var texture = new THREE.Texture(canvas);
568 | texture.needsUpdate = true;
569 | return texture;
570 | }
571 | var materials = [];
572 | for (var i = 0; i < face_labels.length; ++i)
573 | materials.push(new THREE.MeshPhongMaterial($t.copyto(vars.material_options,
574 | { map: create_text_texture(face_labels[i], vars.label_color, vars.dice_color) })));
575 | return materials;
576 | }
577 |
578 | function create_d4_materials(size, margin, labels) {
579 | function create_d4_text(text, color, back_color) {
580 | var canvas = document.createElement("canvas");
581 | var context = canvas.getContext("2d");
582 | var ts = calc_texture_size(size + margin) * 2;
583 | canvas.width = canvas.height = ts;
584 | context.font = (ts - margin) * 0.5 + "pt Arial";
585 | context.fillStyle = back_color;
586 | context.fillRect(0, 0, canvas.width, canvas.height);
587 | context.textAlign = "center";
588 | context.textBaseline = "middle";
589 | context.fillStyle = color;
590 | for (var i in text) {
591 | context.fillText(text[i], canvas.width / 2,
592 | canvas.height / 2 - ts * 0.3);
593 | context.translate(canvas.width / 2, canvas.height / 2);
594 | context.rotate(Math.PI * 2 / 3);
595 | context.translate(-canvas.width / 2, -canvas.height / 2);
596 | }
597 | var texture = new THREE.Texture(canvas);
598 | texture.needsUpdate = true;
599 | return texture;
600 | }
601 | var materials = [];
602 | for (var i = 0; i < labels.length; ++i)
603 | materials.push(new THREE.MeshPhongMaterial($t.copyto(vars.material_options,
604 | { map: create_d4_text(labels[i], vars.label_color, vars.dice_color) })));
605 | return materials;
606 | }
607 |
608 | function create_d4_geometry(radius) {
609 | var vertices = [[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]];
610 | var faces = [[1, 0, 2, 1], [0, 1, 3, 2], [0, 3, 2, 3], [1, 2, 3, 4]];
611 | return create_geom(vertices, faces, radius, -0.1, Math.PI * 7 / 6, 0.96);
612 | }
613 |
614 | function create_d6_geometry(radius) {
615 | var vertices = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
616 | [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]];
617 | var faces = [[0, 3, 2, 1, 1], [1, 2, 6, 5, 2], [0, 1, 5, 4, 3],
618 | [3, 7, 6, 2, 4], [0, 4, 7, 3, 5], [4, 5, 6, 7, 6]];
619 | return create_geom(vertices, faces, radius, 0.1, Math.PI / 4, 0.96);
620 | }
621 |
622 | function create_d8_geometry(radius) {
623 | var vertices = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]];
624 | var faces = [[0, 2, 4, 1], [0, 4, 3, 2], [0, 3, 5, 3], [0, 5, 2, 4], [1, 3, 4, 5],
625 | [1, 4, 2, 6], [1, 2, 5, 7], [1, 5, 3, 8]];
626 | return create_geom(vertices, faces, radius, 0, -Math.PI / 4 / 2, 0.965);
627 | }
628 |
629 | function create_d10_geometry(radius) {
630 | var a = Math.PI * 2 / 10, k = Math.cos(a), h = 0.105, v = -1;
631 | var vertices = [];
632 | for (var i = 0, b = 0; i < 10; ++i, b += a)
633 | vertices.push([Math.cos(b), Math.sin(b), h * (i % 2 ? 1 : -1)]);
634 | vertices.push([0, 0, -1]); vertices.push([0, 0, 1]);
635 | var faces = [[5, 7, 11, 0], [4, 2, 10, 1], [1, 3, 11, 2], [0, 8, 10, 3], [7, 9, 11, 4],
636 | [8, 6, 10, 5], [9, 1, 11, 6], [2, 0, 10, 7], [3, 5, 11, 8], [6, 4, 10, 9],
637 | [1, 0, 2, v], [1, 2, 3, v], [3, 2, 4, v], [3, 4, 5, v], [5, 4, 6, v],
638 | [5, 6, 7, v], [7, 6, 8, v], [7, 8, 9, v], [9, 8, 0, v], [9, 0, 1, v]];
639 | return create_geom(vertices, faces, radius, 0, Math.PI * 6 / 5, 0.945);
640 | }
641 |
642 | function create_d12_geometry(radius) {
643 | var p = (1 + Math.sqrt(5)) / 2, q = 1 / p;
644 | var vertices = [[0, q, p], [0, q, -p], [0, -q, p], [0, -q, -p], [p, 0, q],
645 | [p, 0, -q], [-p, 0, q], [-p, 0, -q], [q, p, 0], [q, -p, 0], [-q, p, 0],
646 | [-q, -p, 0], [1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1], [-1, 1, 1],
647 | [-1, 1, -1], [-1, -1, 1], [-1, -1, -1]];
648 | var faces = [[2, 14, 4, 12, 0, 1], [15, 9, 11, 19, 3, 2], [16, 10, 17, 7, 6, 3], [6, 7, 19, 11, 18, 4],
649 | [6, 18, 2, 0, 16, 5], [18, 11, 9, 14, 2, 6], [1, 17, 10, 8, 13, 7], [1, 13, 5, 15, 3, 8],
650 | [13, 8, 12, 4, 5, 9], [5, 4, 14, 9, 15, 10], [0, 12, 8, 10, 16, 11], [3, 19, 7, 17, 1, 12]];
651 | return create_geom(vertices, faces, radius, 0.2, -Math.PI / 4 / 2, 0.968);
652 | }
653 |
654 | function create_d20_geometry(radius) {
655 | var t = (1 + Math.sqrt(5)) / 2;
656 | var vertices = [[-1, t, 0], [1, t, 0 ], [-1, -t, 0], [1, -t, 0],
657 | [0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t],
658 | [t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1]];
659 | var faces = [[0, 11, 5, 1], [0, 5, 1, 2], [0, 1, 7, 3], [0, 7, 10, 4], [0, 10, 11, 5],
660 | [1, 5, 9, 6], [5, 11, 4, 7], [11, 10, 2, 8], [10, 7, 6, 9], [7, 1, 8, 10],
661 | [3, 9, 4, 11], [3, 4, 2, 12], [3, 2, 6, 13], [3, 6, 8, 14], [3, 8, 9, 15],
662 | [4, 9, 5, 16], [2, 4, 11, 17], [6, 2, 10, 18], [8, 6, 7, 19], [9, 8, 1, 20]];
663 | return create_geom(vertices, faces, radius, -0.2, -Math.PI / 4 / 2, 0.955);
664 | }
665 |
666 | // HELPERS
667 |
668 | function rnd() {
669 | return Math.random();
670 | }
671 |
672 | function create_shape(vertices, faces, radius) {
673 | var cv = new Array(vertices.length), cf = new Array(faces.length);
674 | for (var i = 0; i < vertices.length; ++i) {
675 | var v = vertices[i];
676 | cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius);
677 | }
678 | for (var i = 0; i < faces.length; ++i) {
679 | cf[i] = faces[i].slice(0, faces[i].length - 1);
680 | }
681 | return new CANNON.ConvexPolyhedron(cv, cf);
682 | }
683 |
684 | function make_geom(vertices, faces, radius, tab, af) {
685 | var geom = new THREE.Geometry();
686 | for (var i = 0; i < vertices.length; ++i) {
687 | var vertex = vertices[i].multiplyScalar(radius);
688 | vertex.index = geom.vertices.push(vertex) - 1;
689 | }
690 | for (var i = 0; i < faces.length; ++i) {
691 | var ii = faces[i], fl = ii.length - 1;
692 | var aa = Math.PI * 2 / fl;
693 | for (var j = 0; j < fl - 2; ++j) {
694 | geom.faces.push(new THREE.Face3(ii[0], ii[j + 1], ii[j + 2], [geom.vertices[ii[0]],
695 | geom.vertices[ii[j + 1]], geom.vertices[ii[j + 2]]], 0, ii[fl] + 1));
696 | geom.faceVertexUvs[0].push([
697 | new THREE.Vector2((Math.cos(af) + 1 + tab) / 2 / (1 + tab),
698 | (Math.sin(af) + 1 + tab) / 2 / (1 + tab)),
699 | new THREE.Vector2((Math.cos(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab),
700 | (Math.sin(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab)),
701 | new THREE.Vector2((Math.cos(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab),
702 | (Math.sin(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab))]);
703 | }
704 | }
705 | geom.computeFaceNormals();
706 | geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius);
707 | return geom;
708 | }
709 |
710 | function chamfer_geom(vectors, faces, chamfer) {
711 | var chamfer_vectors = [], chamfer_faces = [], corner_faces = new Array(vectors.length);
712 | for (var i = 0; i < vectors.length; ++i) corner_faces[i] = [];
713 | for (var i = 0; i < faces.length; ++i) {
714 | var ii = faces[i], fl = ii.length - 1;
715 | var center_point = new THREE.Vector3();
716 | var face = new Array(fl);
717 | for (var j = 0; j < fl; ++j) {
718 | var vv = vectors[ii[j]].clone();
719 | center_point.add(vv);
720 | corner_faces[ii[j]].push(face[j] = chamfer_vectors.push(vv) - 1);
721 | }
722 | center_point.divideScalar(fl);
723 | for (var j = 0; j < fl; ++j) {
724 | var vv = chamfer_vectors[face[j]];
725 | vv.subVectors(vv, center_point).multiplyScalar(chamfer).addVectors(vv, center_point);
726 | }
727 | face.push(ii[fl]);
728 | chamfer_faces.push(face);
729 | }
730 | for (var i = 0; i < faces.length - 1; ++i) {
731 | for (var j = i + 1; j < faces.length; ++j) {
732 | var pairs = [], lastm = -1;
733 | for (var m = 0; m < faces[i].length - 1; ++m) {
734 | var n = faces[j].indexOf(faces[i][m]);
735 | if (n >= 0 && n < faces[j].length - 1) {
736 | if (lastm >= 0 && m != lastm + 1) pairs.unshift([i, m], [j, n]);
737 | else pairs.push([i, m], [j, n]);
738 | lastm = m;
739 | }
740 | }
741 | if (pairs.length != 4) continue;
742 | chamfer_faces.push([chamfer_faces[pairs[0][0]][pairs[0][1]],
743 | chamfer_faces[pairs[1][0]][pairs[1][1]],
744 | chamfer_faces[pairs[3][0]][pairs[3][1]],
745 | chamfer_faces[pairs[2][0]][pairs[2][1]], -1]);
746 | }
747 | }
748 | for (var i = 0; i < corner_faces.length; ++i) {
749 | var cf = corner_faces[i], face = [cf[0]], count = cf.length - 1;
750 | while (count) {
751 | for (var m = faces.length; m < chamfer_faces.length; ++m) {
752 | var index = chamfer_faces[m].indexOf(face[face.length - 1]);
753 | if (index >= 0 && index < 4) {
754 | if (--index == -1) index = 3;
755 | var next_vertex = chamfer_faces[m][index];
756 | if (cf.indexOf(next_vertex) >= 0) {
757 | face.push(next_vertex);
758 | break;
759 | }
760 | }
761 | }
762 | --count;
763 | }
764 | face.push(-1);
765 | chamfer_faces.push(face);
766 | }
767 | return { vectors: chamfer_vectors, faces: chamfer_faces };
768 | }
769 |
770 | function create_geom(vertices, faces, radius, tab, af, chamfer) {
771 | var vectors = new Array(vertices.length);
772 | for (var i = 0; i < vertices.length; ++i) {
773 | vectors[i] = (new THREE.Vector3).fromArray(vertices[i]).normalize();
774 | }
775 | var cg = chamfer_geom(vectors, faces, chamfer);
776 | var geom = make_geom(cg.vectors, cg.faces, radius, tab, af);
777 | //var geom = make_geom(vectors, faces, radius, tab, af); // Without chamfer
778 | geom.cannon_shape = create_shape(vectors, faces, radius);
779 | return geom;
780 | }
781 |
782 | function calc_texture_size(approx) {
783 | return Math.pow(2, Math.floor(Math.log(approx) / Math.log(2)));
784 | }
785 |
786 | function make_random_vector(vector) {
787 | var random_angle = rnd() * Math.PI / 5 - Math.PI / 5 / 2;
788 | var vec = {
789 | x: vector.x * Math.cos(random_angle) - vector.y * Math.sin(random_angle),
790 | y: vector.x * Math.sin(random_angle) + vector.y * Math.cos(random_angle)
791 | };
792 | if (vec.x == 0) vec.x = 0.01;
793 | if (vec.y == 0) vec.y = 0.01;
794 | return vec;
795 | }
796 |
797 | //determines which face is up after roll animation
798 | function get_dice_value(dice) {
799 | var vector = new THREE.Vector3(0, 0, dice.dice_type == 'd4' ? -1 : 1);
800 | var closest_face, closest_angle = Math.PI * 2;
801 | for (var i = 0, l = dice.geometry.faces.length; i < l; ++i) {
802 | var face = dice.geometry.faces[i];
803 | if (face.materialIndex == 0) continue;
804 | var angle = face.normal.clone().applyQuaternion(dice.body.quaternion).angleTo(vector);
805 | if (angle < closest_angle) {
806 | closest_angle = angle;
807 | closest_face = face;
808 | }
809 | }
810 | var matindex = closest_face ? closest_face.materialIndex - 1 : -1; //todo: bug thrown here, sometimes closest_face = undefined
811 | if (dice.dice_type == 'd100') matindex *= 10;
812 | if (dice.dice_type == 'd10' && matindex == 0) matindex = 10;
813 | return matindex;
814 | }
815 |
816 | function get_dice_values(dices) {
817 | var values = [];
818 | for (var i = 0, l = dices.length; i < l; ++i) {
819 | values.push(get_dice_value(dices[i]));
820 | }
821 | return values;
822 | }
823 |
824 | function shift_dice_faces(dice, value, res) {
825 | var r = CONSTS.dice_face_range[dice.dice_type];
826 | if (dice.dice_type == 'd10' && value == 10) value = 0;
827 | if (!(value >= r[0] && value <= r[1])) return;
828 | var num = value - res;
829 | var geom = dice.geometry.clone();
830 | for (var i = 0, l = geom.faces.length; i < l; ++i) {
831 | var matindex = geom.faces[i].materialIndex;
832 | if (matindex == 0) continue;
833 | matindex += num - 1;
834 | while (matindex > r[1]) matindex -= r[1];
835 | while (matindex < r[0]) matindex += r[1];
836 | geom.faces[i].materialIndex = matindex + 1;
837 | }
838 | if (dice.dice_type == 'd4' && num != 0) {
839 | if (num < 0) num += 4;
840 | dice.material = new THREE.MeshFaceMaterial(
841 | create_d4_materials(vars.scale / 2, vars.scale * 2, CONSTS.d4_labels[num]));
842 | }
843 | dice.geometry = geom;
844 | }
845 |
846 | //playSound function and audio file copied from
847 | //https://github.com/chukwumaijem/roll-a-die
848 | function playSound(outerContainer, soundVolume) {
849 | if (soundVolume === 0) return;
850 | const audio = document.createElement('audio');
851 | outerContainer.appendChild(audio);
852 | audio.src = 'assets/nc93322.mp3'; //todo: make this configurable
853 | audio.volume = soundVolume;
854 | audio.play();
855 | audio.onended = () => {
856 | audio.remove();
857 | };
858 | }
859 |
860 | return that;
861 | }());
862 |
863 |
--------------------------------------------------------------------------------
/libs/cannon.min.js:
--------------------------------------------------------------------------------
1 | (function(){var t=t||{};this.Int32Array||(this.Int32Array=Array,this.Float32Array=Array),t.Mat3=function(t){this.elements=t?t:[0,0,0,0,0,0,0,0,0]},t.Mat3.prototype.identity=function(){this.elements[0]=1,this.elements[1]=0,this.elements[2]=0,this.elements[3]=0,this.elements[4]=1,this.elements[5]=0,this.elements[6]=0,this.elements[7]=0,this.elements[8]=1},t.Mat3.prototype.setZero=function(){var t=this.elements;t[0]=0,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t[8]=0},t.Mat3.prototype.setTrace=function(t){var e=this.elements;e[0]=t.x,e[4]=t.y,e[8]=t.z},t.Mat3.prototype.vmult=function(e,i){i=i||new t.Vec3;var n=this.elements,o=e.x,a=e.y,s=e.z;return i.x=n[0]*o+n[1]*a+n[2]*s,i.y=n[3]*o+n[4]*a+n[5]*s,i.z=n[6]*o+n[7]*a+n[8]*s,i},t.Mat3.prototype.smult=function(t){for(var e=0;this.elements.length>e;e++)this.elements[e]*=t},t.Mat3.prototype.mmult=function(e){for(var i=new t.Mat3,n=0;3>n;n++)for(var o=0;3>o;o++){for(var a=0,s=0;3>s;s++)a+=e.elements[n+3*s]*this.elements[s+3*o];i.elements[n+3*o]=a}return i},t.Mat3.prototype.solve=function(e,i){i=i||new t.Vec3;for(var n=3,o=4,a=[],s=0;n*o>s;s++)a.push(0);var s,r;for(s=0;3>s;s++)for(r=0;3>r;r++)a[s+o*r]=this.elements[s+3*r];a[3]=e.x,a[7]=e.y,a[11]=e.z;var h,c,l=3,u=l,p=4;do{if(s=u-l,0===a[s+o*s])for(r=s+1;u>r;r++)if(0!==a[s+o*r]){h=p;do c=p-h,a[c+o*s]+=a[c+o*r];while(--h);break}if(0!==a[s+o*s])for(r=s+1;u>r;r++){var d=a[s+o*r]/a[s+o*s];h=p;do c=p-h,a[c+o*r]=s>=c?0:a[c+o*r]-a[c+o*s]*d;while(--h)}}while(--l);if(i.z=a[2*o+3]/a[2*o+2],i.y=(a[1*o+3]-a[1*o+2]*i.z)/a[1*o+1],i.x=(a[0*o+3]-a[0*o+2]*i.z-a[0*o+1]*i.y)/a[0*o+0],isNaN(i.x)||isNaN(i.y)||isNaN(i.z)||1/0===i.x||1/0===i.y||1/0===i.z)throw"Could not solve equation! Got x=["+(""+i)+"], b=["+(""+e)+"], A=["+(""+this)+"]";return i},t.Mat3.prototype.e=function(t,e,i){return void 0===i?this.elements[e+3*t]:(this.elements[e+3*t]=i,void 0)},t.Mat3.prototype.copy=function(e){e=e||new t.Mat3;for(var i=0;this.elements.length>i;i++)e.elements[i]=this.elements[i];return e},t.Mat3.prototype.toString=function(){for(var t="",e=",",i=0;9>i;i++)t+=this.elements[i]+e;return t},t.Mat3.prototype.reverse=function(e){e=e||new t.Mat3;for(var i=3,n=6,o=[],a=0;i*n>a;a++)o.push(0);var a,s;for(a=0;3>a;a++)for(s=0;3>s;s++)o[a+n*s]=this.elements[a+3*s];o[3]=1,o[9]=0,o[15]=0,o[4]=0,o[10]=1,o[16]=0,o[5]=0,o[11]=0,o[17]=1;var r,h,c=3,l=c,u=n;do{if(a=l-c,0===o[a+n*a])for(s=a+1;l>s;s++)if(0!==o[a+n*s]){r=u;do h=u-r,o[h+n*a]+=o[h+n*s];while(--r);break}if(0!==o[a+n*a])for(s=a+1;l>s;s++){var p=o[a+n*s]/o[a+n*a];r=u;do h=u-r,o[h+n*s]=a>=h?0:o[h+n*s]-o[h+n*a]*p;while(--r)}}while(--c);a=2;do{s=a-1;do{var p=o[a+n*s]/o[a+n*a];r=n;do h=n-r,o[h+n*s]=o[h+n*s]-o[h+n*a]*p;while(--r)}while(s--)}while(--a);a=2;do{var p=1/o[a+n*a];r=n;do h=n-r,o[h+n*a]=o[h+n*a]*p;while(--r)}while(a--);a=2;do{s=2;do{if(h=o[i+s+n*a],isNaN(h)||1/0===h)throw"Could not reverse! A=["+(""+this)+"]";e.e(a,s,h)}while(s--)}while(a--);return e},t.Vec3=function(t,e,i){this.x=t||0,this.y=e||0,this.z=i||0},t.Vec3.prototype.cross=function(e,i){var n=e.x,o=e.y,a=e.z,s=this.x,r=this.y,h=this.z;return i=i||new t.Vec3,i.x=r*a-h*o,i.y=h*n-s*a,i.z=s*o-r*n,i},t.Vec3.prototype.set=function(t,e,i){return this.x=t,this.y=e,this.z=i,this},t.Vec3.prototype.vadd=function(e,i){return i?(i.x=e.x+this.x,i.y=e.y+this.y,i.z=e.z+this.z,void 0):new t.Vec3(this.x+e.x,this.y+e.y,this.z+e.z)},t.Vec3.prototype.vsub=function(e,i){return i?(i.x=this.x-e.x,i.y=this.y-e.y,i.z=this.z-e.z,void 0):new t.Vec3(this.x-e.x,this.y-e.y,this.z-e.z)},t.Vec3.prototype.crossmat=function(){return new t.Mat3([0,-this.z,this.y,this.z,0,-this.x,-this.y,this.x,0])},t.Vec3.prototype.normalize=function(){var t=this.x,e=this.y,i=this.z,n=Math.sqrt(t*t+e*e+i*i);if(n>0){var o=1/n;this.x*=o,this.y*=o,this.z*=o}else this.x=0,this.y=0,this.z=0;return n},t.Vec3.prototype.unit=function(e){e=e||new t.Vec3;var i=this.x,n=this.y,o=this.z,a=Math.sqrt(i*i+n*n+o*o);return a>0?(a=1/a,e.x=i*a,e.y=n*a,e.z=o*a):(e.x=1,e.y=0,e.z=0),e},t.Vec3.prototype.norm=function(){var t=this.x,e=this.y,i=this.z;return Math.sqrt(t*t+e*e+i*i)},t.Vec3.prototype.norm2=function(){return this.dot(this)},t.Vec3.prototype.distanceTo=function(t){var e=this.x,i=this.y,n=this.z,o=t.x,a=t.y,s=t.z;return Math.sqrt((o-e)*(o-e)+(a-i)*(a-i)+(s-n)*(s-n))},t.Vec3.prototype.mult=function(e,i){i=i||new t.Vec3;var n=this.x,o=this.y,a=this.z;return i.x=e*n,i.y=e*o,i.z=e*a,i},t.Vec3.prototype.dot=function(t){return this.x*t.x+this.y*t.y+this.z*t.z},t.Vec3.prototype.isZero=function(){return 0===this.x&&0===this.y&&0===this.z},t.Vec3.prototype.negate=function(e){return e=e||new t.Vec3,e.x=-this.x,e.y=-this.y,e.z=-this.z,e};var e=new t.Vec3,i=new t.Vec3;t.Vec3.prototype.tangents=function(t,n){var o=this.norm();if(o>0){var a=e,s=1/o;a.set(this.x*s,this.y*s,this.z*s);var r=i;.9>Math.abs(a.x)?(r.set(1,0,0),a.cross(r,t)):(r.set(0,1,0),a.cross(r,t)),a.cross(t,n)}else t.set(1,0,0).normalize(),n.set(0,1,0).normalize()},t.Vec3.prototype.toString=function(){return this.x+","+this.y+","+this.z},t.Vec3.prototype.copy=function(e){return e=e||new t.Vec3,e.x=this.x,e.y=this.y,e.z=this.z,e},t.Vec3.prototype.lerp=function(t,e,i){var n=this.x,o=this.y,a=this.z;i.x=n+(t.x-n)*e,i.y=o+(t.y-o)*e,i.z=a+(t.z-a)*e},t.Vec3.prototype.almostEquals=function(t,e){return void 0===e&&(e=1e-6),Math.abs(this.x-t.x)>e||Math.abs(this.y-t.y)>e||Math.abs(this.z-t.z)>e?!1:!0},t.Vec3.prototype.almostZero=function(t){return void 0===t&&(t=1e-6),Math.abs(this.x)>t||Math.abs(this.y)>t||Math.abs(this.z)>t?!1:!0},t.Quaternion=function(t,e,i,n){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0,this.z=void 0!==i?i:0,this.w=void 0!==n?n:1},t.Quaternion.prototype.set=function(t,e,i,n){this.x=t,this.y=e,this.z=i,this.w=n},t.Quaternion.prototype.toString=function(){return this.x+","+this.y+","+this.z+","+this.w},t.Quaternion.prototype.setFromAxisAngle=function(t,e){var i=Math.sin(.5*e);this.x=t.x*i,this.y=t.y*i,this.z=t.z*i,this.w=Math.cos(.5*e)},t.Quaternion.prototype.toAxisAngle=function(e){e=e||new t.Vec3,this.normalize();var i=2*Math.acos(this.w),n=Math.sqrt(1-this.w*this.w);return.001>n?(e.x=this.x,e.y=this.y,e.z=this.z):(e.x=this.x/n,e.y=this.y/n,e.z=this.z/n),[e,i]},t.Quaternion.prototype.setFromVectors=function(t,e){var i=t.cross(e);this.x=i.x,this.y=i.y,this.z=i.z,this.w=Math.sqrt(Math.pow(t.norm(),2)*Math.pow(e.norm(),2))+t.dot(e),this.normalize()};var n=new t.Vec3,o=new t.Vec3,a=new t.Vec3;t.Quaternion.prototype.mult=function(e,i){i=i||new t.Quaternion;var s=this.w,r=n,h=o,c=a;return r.set(this.x,this.y,this.z),h.set(e.x,e.y,e.z),i.w=s*e.w-r.dot(h),r.cross(h,c),i.x=s*h.x+e.w*r.x+c.x,i.y=s*h.y+e.w*r.y+c.y,i.z=s*h.z+e.w*r.z+c.z,i},t.Quaternion.prototype.inverse=function(e){var i=this.x,n=this.y,o=this.z,a=this.w;e=e||new t.Quaternion,this.conjugate(e);var s=1/(i*i+n*n+o*o+a*a);return e.x*=s,e.y*=s,e.z*=s,e.w*=s,e},t.Quaternion.prototype.conjugate=function(e){return e=e||new t.Quaternion,e.x=-this.x,e.y=-this.y,e.z=-this.z,e.w=this.w,e},t.Quaternion.prototype.normalize=function(){var t=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);0===t?(this.x=0,this.y=0,this.z=0,this.w=0):(t=1/t,this.x*=t,this.y*=t,this.z*=t,this.w*=t)},t.Quaternion.prototype.normalizeFast=function(){var t=(3-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2;0===t?(this.x=0,this.y=0,this.z=0,this.w=0):(this.x*=t,this.y*=t,this.z*=t,this.w*=t)},t.Quaternion.prototype.vmult=function(e,i){if(i=i||new t.Vec3,0===this.w)i.x=e.x,i.y=e.y,i.z=e.z;else{var n=e.x,o=e.y,a=e.z,s=this.x,r=this.y,h=this.z,c=this.w,l=c*n+r*a-h*o,u=c*o+h*n-s*a,p=c*a+s*o-r*n,d=-s*n-r*o-h*a;i.x=l*c+d*-s+u*-h-p*-r,i.y=u*c+d*-r+p*-s-l*-h,i.z=p*c+d*-h+l*-r-u*-s}return i},t.Quaternion.prototype.copy=function(t){t.x=this.x,t.y=this.y,t.z=this.z,t.w=this.w},t.Quaternion.prototype.toEuler=function(t,e){e=e||"YZX";var i,n,o,a=this.x,s=this.y,r=this.z,h=this.w;switch(e){case"YZX":var c=a*s+r*h;if(c>.499&&(i=2*Math.atan2(a,h),n=Math.PI/2,o=0),-.499>c&&(i=-2*Math.atan2(a,h),n=-Math.PI/2,o=0),isNaN(i)){var l=a*a,u=s*s,p=r*r;i=Math.atan2(2*s*h-2*a*r,1-2*u-2*p),n=Math.asin(2*c),o=Math.atan2(2*a*h-2*s*r,1-2*l-2*p)}break;default:throw Error("Euler order "+e+" not supported yet.")}t.y=i,t.z=n,t.x=o},t.EventTarget=function(){var t={};this.addEventListener=function(e,i){void 0===t[e]&&(t[e]=[]),-1===t[e].indexOf(i)&&t[e].push(i)},this.dispatchEvent=function(e){for(var i in t[e.type])t[e.type][i](e)},this.removeEventListener=function(e,i){var n=t[e].indexOf(i);-1!==n&&t[e].splice(n,1)}},t.ObjectPool=function(){this.objects=[],this.type=Object},t.ObjectPool.prototype.release=function(){for(var t=arguments.length,e=0;e!==t;e++)this.objects.push(arguments[e])},t.ObjectPool.prototype.get=function(){return 0===this.objects.length?this.constructObject():this.objects.pop()},t.ObjectPool.prototype.constructObject=function(){throw Error("constructObject() not implemented in this ObjectPool subclass yet!")},t.Vec3Pool=function(){t.ObjectPool.call(this),this.type=t.Vec3},t.Vec3Pool.prototype=new t.ObjectPool,t.Vec3Pool.prototype.constructObject=function(){return new t.Vec3},t.Shape=function(){this.type=0,this.aabbmin=new t.Vec3,this.aabbmax=new t.Vec3,this.boundingSphereRadius=0,this.boundingSphereRadiusNeedsUpdate=!0},t.Shape.prototype.constructor=t.Shape,t.Shape.prototype.computeBoundingSphereRadius=function(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type},t.Shape.prototype.getBoundingSphereRadius=function(){return this.boundingSphereRadiusNeedsUpdate&&this.computeBoundingSphereRadius(),this.boundingSphereRadius},t.Shape.prototype.volume=function(){throw"volume() not implemented for shape type "+this.type},t.Shape.prototype.calculateLocalInertia=function(){throw"calculateLocalInertia() not implemented for shape type "+this.type};var s=new t.Vec3,r=new t.Vec3;t.Shape.prototype.calculateTransformedInertia=function(e,i,n){n=n||new t.Vec3;var o=s,a=r;return this.calculateLocalInertia(e,o),i.vmult(o,a),n.x=Math.abs(a.x),n.y=Math.abs(a.y),n.z=Math.abs(a.z),n},t.Shape.calculateLocalAABB=function(){throw Error(".calculateLocalAABB is not implemented for this Shape yet!")},t.Shape.types={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16},t.Body=function(e){t.EventTarget.apply(this),this.type=e,this.world=null,this.preStep=null,this.postStep=null,this.vlambda=new t.Vec3,this.collisionFilterGroup=1,this.collisionFilterMask=1},t.Body.DYNAMIC=1,t.Body.STATIC=2,t.Body.KINEMATIC=4,t.Particle=function(e,i){if("number"!=typeof e)throw Error("Argument 1 (mass) must be a number.");if(i!==void 0&&!(i instanceof t.Material))throw Error("Argument 3 (material) must be an instance of CANNON.Material.");t.Body.call(this,"particle"),this.position=new t.Vec3,this.initPosition=new t.Vec3,this.velocity=new t.Vec3,this.initVelocity=new t.Vec3,this.force=new t.Vec3,this.mass=e,this.invMass=e>0?1/e:0,this.material=i,this.linearDamping=.01,this.motionstate=0>=e?t.Body.STATIC:t.Body.DYNAMIC,this.allowSleep=!0,this.sleepState=0,this.sleepSpeedLimit=.1,this.sleepTimeLimit=1,this.timeLastSleepy=0},t.Particle.prototype=new t.Body,t.Particle.prototype.constructor=t.Particle,t.Particle.prototype.isAwake=function(){return 0===this.sleepState},t.Particle.prototype.isSleepy=function(){return 1===this.sleepState},t.Particle.prototype.isSleeping=function(){return 2===this.sleepState},t.Particle.prototype.wakeUp=function(){var t=this.sleepState;this.sleepState=0,2===t&&this.dispatchEvent({type:"wakeup"})},t.Particle.prototype.sleep=function(){this.sleepState=2},t.Particle.prototype.sleepTick=function(t){if(this.allowSleep){var e=this.sleepState,i=this.velocity.norm2(),n=Math.pow(this.sleepSpeedLimit,2);0===e&&n>i?(this.sleepState=1,this.timeLastSleepy=t,this.dispatchEvent({type:"sleepy"})):1===e&&i>n?this.wakeUp():1===e&&t-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleepState=2,this.dispatchEvent({type:"sleep"}))}},t.RigidBody=function(e,i,n){if("number"!=typeof e)throw Error("Argument 1 (mass) must be a number.");if(n!==void 0&&!(n instanceof t.Material))throw Error("Argument 3 (material) must be an instance of CANNON.Material.");t.Particle.call(this,e,n),this.tau=new t.Vec3,this.quaternion=new t.Quaternion,this.initQuaternion=new t.Quaternion,this.angularVelocity=new t.Vec3,this.initAngularVelocity=new t.Vec3,this.shape=i,this.inertia=new t.Vec3,i.calculateLocalInertia(e,this.inertia),this.inertiaWorld=new t.Vec3,this.inertia.copy(this.inertiaWorld),this.inertiaWorldAutoUpdate=!1,this.invInertia=new t.Vec3(this.inertia.x>0?1/this.inertia.x:0,this.inertia.y>0?1/this.inertia.y:0,this.inertia.z>0?1/this.inertia.z:0),this.invInertiaWorld=new t.Vec3,this.invInertia.copy(this.invInertiaWorld),this.invInertiaWorldAutoUpdate=!1,this.angularDamping=.01,this.aabbmin=new t.Vec3,this.aabbmax=new t.Vec3,this.aabbNeedsUpdate=!0,this.wlambda=new t.Vec3},t.RigidBody.prototype=new t.Particle(0),t.RigidBody.prototype.constructor=t.RigidBody,t.RigidBody.prototype.computeAABB=function(){this.shape.calculateWorldAABB(this.position,this.quaternion,this.aabbmin,this.aabbmax),this.aabbNeedsUpdate=!1};var h=new t.Vec3,c=new t.Vec3;t.RigidBody.prototype.applyForce=function(t,e){var i=h;e.vsub(this.position,i);var n=c;i.cross(t,n),this.force.vadd(t,this.force),this.tau.vadd(n,this.tau)};var l=new t.Vec3,u=new t.Vec3,p=new t.Vec3;t.RigidBody.prototype.applyImpulse=function(t,e){var i=l;e.vsub(this.position,i);var n=u;t.copy(n),n.mult(this.invMass,n),this.velocity.vadd(n,this.velocity);var o=p;i.cross(t,o),o.x*=this.invInertia.x,o.y*=this.invInertia.y,o.z*=this.invInertia.z,this.angularVelocity.vadd(o,this.angularVelocity)},t.Sphere=function(e){t.Shape.call(this),this.radius=void 0!==e?Number(e):1,this.type=t.Shape.types.SPHERE},t.Sphere.prototype=new t.Shape,t.Sphere.prototype.constructor=t.Sphere,t.Sphere.prototype.calculateLocalInertia=function(e,i){i=i||new t.Vec3;var n=2*e*this.radius*this.radius/5;return i.x=n,i.y=n,i.z=n,i},t.Sphere.prototype.volume=function(){return 4*Math.PI*this.radius/3},t.Sphere.prototype.computeBoundingSphereRadius=function(){this.boundingSphereRadiusNeedsUpdate=!1,this.boundingSphereRadius=this.radius},t.Sphere.prototype.calculateWorldAABB=function(t,e,i,n){for(var o=this.radius,a=["x","y","z"],s=0;a.length>s;s++){var r=a[s];i[r]=t[r]-o,n[r]=t[r]+o}},t.SPHSystem=function(){this.particles=[],this.density=1,this.smoothingRadius=1,this.speedOfSound=1,this.viscosity=.01,this.eps=1e-6,this.pressures=[],this.densities=[],this.neighbors=[]},t.SPHSystem.prototype.add=function(t){this.particles.push(t),this.neighbors.lengththis.particles.length&&this.neighbors.pop())};var d=new t.Vec3;t.SPHSystem.prototype.getNeighbors=function(t,e){for(var i=this.particles.length,n=t.id,o=this.smoothingRadius*this.smoothingRadius,a=d,s=0;s!==i;s++){var r=this.particles[s];r.position.vsub(t.position,a),n!==r.id&&o>a.norm2()&&e.push(r)}};var v=new t.Vec3,y=new t.Vec3,f=new t.Vec3,m=new t.Vec3,w=new t.Vec3,b=new t.Vec3;t.SPHSystem.prototype.update=function(){for(var t=this.particles.length,e=v,i=this.speedOfSound,n=this.eps,o=0;o!==t;o++){var a=this.particles[o],s=this.neighbors[o];s.length=0,this.getNeighbors(a,s),s.push(this.particles[o]);for(var r=s.length,h=0,c=0;c!==r;c++){a.position.vsub(s[c].position,e);var l=e.norm(),u=this.w(l);h+=s[c].mass*u}this.densities[o]=h,this.pressures[o]=i*i*(this.densities[o]-this.density)}for(var p=y,d=f,x=m,g=w,V=b,o=0;o!==t;o++){var z=this.particles[o];p.set(0,0,0),d.set(0,0,0);for(var E,S,s=this.neighbors[o],r=s.length,c=0;c!==r;c++){var N=s[c];z.position.vsub(N.position,g);var M=g.norm();E=-N.mass*(this.pressures[o]/(this.densities[o]*this.densities[o]+n)+this.pressures[c]/(this.densities[c]*this.densities[c]+n)),this.gradw(g,x),x.mult(E,x),p.vadd(x,p),N.velocity.vsub(z.velocity,V),V.mult(1/(1e-4+this.densities[o]*this.densities[c])*this.viscosity*N.mass,V),S=this.nablaw(M),V.mult(S,V),d.vadd(V,d)}d.mult(z.mass,d),p.mult(z.mass,p),z.force.vadd(d,z.force),z.force.vadd(p,z.force)}},t.SPHSystem.prototype.w=function(t){var e=this.smoothingRadius;return 315/(64*Math.PI*Math.pow(e,9))*Math.pow(e*e-t*t,3)},t.SPHSystem.prototype.gradw=function(t,e){var i=t.norm(),n=this.smoothingRadius;t.mult(945/(32*Math.PI*Math.pow(n,9))*Math.pow(n*n-i*i,2),e)},t.SPHSystem.prototype.nablaw=function(t){var e=this.smoothingRadius,i=945/(32*Math.PI*Math.pow(e,9))*(e*e-t*t)*(7*t*t-3*e*e);return i},t.Box=function(e){t.Shape.call(this),this.halfExtents=e,this.type=t.Shape.types.BOX,this.convexPolyhedronRepresentation=null,this.updateConvexPolyhedronRepresentation()},t.Box.prototype=new t.Shape,t.Box.prototype.constructor=t.Box,t.Box.prototype.updateConvexPolyhedronRepresentation=function(){var e=this.halfExtents.x,i=this.halfExtents.y,n=this.halfExtents.z,o=t.Vec3,a=new t.ConvexPolyhedron([new o(-e,-i,-n),new o(e,-i,-n),new o(e,i,-n),new o(-e,i,-n),new o(-e,-i,n),new o(e,-i,n),new o(e,i,n),new o(-e,i,n)],[[3,2,1,0],[4,5,6,7],[5,4,1,0],[2,3,6,7],[0,4,7,3],[1,2,5,6]],[new o(0,0,-1),new o(0,0,1),new o(0,-1,0),new o(0,1,0),new o(-1,0,0),new o(1,0,0)]);this.convexPolyhedronRepresentation=a},t.Box.prototype.calculateLocalInertia=function(e,i){i=i||new t.Vec3;var n=this.halfExtents;return i.x=1/12*e*(2*2*n.y*n.y+2*2*n.z*n.z),i.y=1/12*e*(2*2*n.x*n.x+2*2*n.z*n.z),i.z=1/12*e*(2*2*n.y*n.y+2*2*n.x*n.x),i},t.Box.prototype.getSideNormals=function(t,e){var i=t,n=this.halfExtents;if(i[0].set(n.x,0,0),i[1].set(0,n.y,0),i[2].set(0,0,n.z),i[3].set(-n.x,0,0),i[4].set(0,-n.y,0),i[5].set(0,0,-n.z),void 0!==e)for(var o=0;o!==i.length;o++)e.vmult(i[o],i[o]);return i},t.Box.prototype.volume=function(){return 8*this.halfExtents.x*this.halfExtents.y*this.halfExtents.z},t.Box.prototype.computeBoundingSphereRadius=function(){this.boundingSphereRadius=this.halfExtents.norm(),this.boundingSphereRadiusNeedsUpdate=!1};var x=new t.Vec3;new t.Vec3,t.Box.prototype.forEachWorldCorner=function(t,e,i){for(var n=this.halfExtents,o=[[n.x,n.y,n.z],[-n.x,n.y,n.z],[-n.x,-n.y,n.z],[-n.x,-n.y,-n.z],[n.x,-n.y,-n.z],[n.x,n.y,-n.z],[-n.x,n.y,-n.z],[n.x,-n.y,n.z]],a=0;o.length>a;a++)x.set(o[a][0],o[a][1],o[a][2]),e.vmult(x,x),t.vadd(x,x),i(x.x,x.y,x.z)},t.Box.prototype.calculateWorldAABB=function(t,e,i,n){i.set(1/0,1/0,1/0),n.set(-1/0,-1/0,-1/0),this.forEachWorldCorner(t,e,function(t,e,o){t>n.x&&(n.x=t),e>n.y&&(n.y=e),o>n.z&&(n.z=o),i.x>t&&(i.x=t),i.y>e&&(i.y=e),i.z>o&&(i.z=o)})},t.Plane=function(){t.Shape.call(this),this.type=t.Shape.types.PLANE,this.worldNormal=new t.Vec3,this.worldNormalNeedsUpdate=!0},t.Plane.prototype=new t.Shape,t.Plane.prototype.constructor=t.Plane,t.Plane.prototype.computeWorldNormal=function(t){var e=this.worldNormal;e.set(0,0,1),t.vmult(e,e),this.worldNormalNeedsUpdate=!1},t.Plane.prototype.calculateLocalInertia=function(e,i){return i=i||new t.Vec3},t.Plane.prototype.volume=function(){return 1/0};var g=new t.Vec3;t.Plane.prototype.calculateWorldAABB=function(t,e,i,n){g.set(0,0,1),e.vmult(g,g),i.set(-1/0,-1/0,-1/0),n.set(1/0,1/0,1/0),1===g.x&&(n.x=t.x),1===g.y&&(n.y=t.y),1===g.z&&(n.z=t.z),-1===g.x&&(i.x=t.x),-1===g.y&&(i.y=t.y),-1===g.z&&(i.z=t.z)},t.Compound=function(){t.Shape.call(this),this.type=t.Shape.types.COMPOUND,this.childShapes=[],this.childOffsets=[],this.childOrientations=[]},t.Compound.prototype=new t.Shape,t.Compound.prototype.constructor=t.Compound,t.Compound.prototype.addChild=function(e,i,n){i=i||new t.Vec3,n=n||new t.Quaternion,this.childShapes.push(e),this.childOffsets.push(i),this.childOrientations.push(n)},t.Compound.prototype.volume=function(){for(var t=0,e=this.childShapes.length,i=0;i!==e;i++)t+=this.childShapes[i].volume();return t};var V=new t.Vec3,z=new t.Vec3;t.Compound.prototype.calculateLocalInertia=function(e,i){i=i||new t.Vec3;for(var n=this.volume(),o=z,a=0,s=this.childShapes.length;a!==s;a++){var r=this.childShapes[a],h=this.childOffsets[a];this.childOrientations[a];var c=r.volume()/n*e;r.calculateLocalInertia(c,o),i.vadd(o,i);var l=V;l.set(c*h.x*h.x,c*h.y*h.y,c*h.z*h.z),i.vadd(l,i)}return i},t.Compound.prototype.computeBoundingSphereRadius=function(){for(var t=0,e=0;this.childShapes.length>e;e++){var i=this.childShapes[e];i.boundingSphereRadiusNeedsUpdate&&i.computeBoundingSphereRadius();var n=this.childOffsets[e].norm()+i.boundingSphereRadius;n>t&&(t=n)}this.boundingSphereRadius=t,this.boundingSphereRadiusNeedsUpdate=!1};var E=new t.Vec3,S=new t.Vec3,N=new t.Vec3,M=new t.Quaternion;t.Compound.prototype.calculateWorldAABB=function(t,e,i,n){var o=this.childShapes.length;i.set(1/0,1/0,1/0),n.set(-1/0,-1/0,-1/0);for(var a=0;a!==o;a++)this.childOffsets[a].copy(N),e.vmult(N,N),t.vadd(N,N),e.mult(this.childOrientations[a],M),this.childShapes[a].calculateWorldAABB(N,M,S,E),S.xn.x&&(n.x=E.x),E.y>n.y&&(n.y=E.y),E.z>n.z&&(n.z=E.z)},t.ConvexPolyhedron=function(e,i){function n(t,e,i,n){e.vsub(t,c),i.vsub(e,h),h.cross(c,n),n.isZero()||n.normalize()}function o(t,e,i,n,o){for(var a=t.vertices.length,s=null,r=null,h=t.vertices,c=0;a>c;c++){h[c].copy(V),n.vmult(V,V),V.vadd(i,V);var l=V.dot(e);(null===s||l>s)&&(s=l),(null===r||r>l)&&(r=l)}if(r>s){var u=r;r=s,s=u}o[0]=s,o[1]=r}function a(t,e){var i=r.faces[t],o=r.vertices[i[0]],a=r.vertices[i[1]],s=r.vertices[i[2]];return n(o,a,s,e)}function s(t){var e=r.faces[t],i=r.faceNormals[t],n=r.vertices[e[0]],o=-i.dot(n);return o}var r=this;t.Shape.call(this),this.type=t.Shape.types.CONVEXPOLYHEDRON;var h=new t.Vec3,c=new t.Vec3;this.vertices=e||[],this.worldVertices=[],this.worldVerticesNeedsUpdate=!0,this.faces=i||[],this.faceNormals=[];for(var l=0;this.faces.length>l;l++){for(var u=0;this.faces[l].length>u;u++)if(!this.vertices[this.faces[l][u]])throw Error("Vertex "+this.faces[l][u]+" not found!");var p=new t.Vec3;a(l,p),p.negate(p),this.faceNormals.push(p);var d=this.vertices[this.faces[l][0]];if(0>p.dot(d)){console.warn("Face normal "+l+" ("+(""+p)+") looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.");for(var u=0;this.faces[l].length>u;u++)console.warn("Vertex "+this.faces[l][u]+": ("+(""+this.vertices[i[l][u]])+")")}}this.worldFaceNormalsNeedsUpdate=!0,this.worldFaceNormals=[],this.uniqueEdges=[];for(var v=this.vertices.length,y=0;v>y;y++){var f=this.vertices[y];if(!(f instanceof t.Vec3))throw"Argument 1 must be instance of CANNON.Vec3";this.uniqueEdges.push(f)}for(var l=0;this.faces.length>l;l++)for(var m=this.faces[l].length,w=m,u=0;w>u;u++){var b=(u+1)%m,x=new t.Vec3;this.vertices[this.faces[l][u]].vsub(this.vertices[this.faces[l][b]],x),x.normalize();for(var g=!1,f=0;this.uniqueEdges.length>f;f++)if(this.uniqueEdges[f].almostEquals(x)||this.uniqueEdges[f].almostEquals(x)){g=!0;break}g||this.uniqueEdges.push(x),x&&(x.face1=l)}var V=new t.Vec3;this.testSepAxis=function(t,e,i,n,a,s){var r=[],h=[],c=this;o(c,t,i,n,r),o(e,t,a,s,h);var l=r[0],u=r[1],p=h[0],d=h[1];if(d>l||u>p)return!1;var v=l-d,y=p-u,f=y>v?v:y;return f};var z=new t.Vec3,E=new t.Vec3,S=new t.Vec3,N=new t.Vec3,M=new t.Vec3,P=new t.Vec3;this.findSeparatingAxis=function(t,e,i,n,o,a){for(var s=1/0,r=this,h=0,c=r.faces.length,l=0;c>l;l++){r.faceNormals[l].copy(z),i.vmult(z,z);var u=r.testSepAxis(z,t,e,i,n,o);if(u===!1)return!1;s>u&&(s=u,z.copy(a))}for(var p=t.faces.length,l=0;p>l;l++){t.faceNormals[l].copy(E),o.vmult(E,E),h++;var u=r.testSepAxis(E,t,e,i,n,o);if(u===!1)return!1;s>u&&(s=u,E.copy(a))}for(var d=0,v=0;r.uniqueEdges.length>v;v++){r.uniqueEdges[v].copy(N),i.vmult(N,N);for(var y=0;t.uniqueEdges.length>y;y++)if(t.uniqueEdges[y].copy(M),o.vmult(M,M),N.cross(M,P),d++,!P.almostZero()){P.normalize();var f=r.testSepAxis(P,t,e,i,n,o);if(f===!1)return!1;s>f&&(s=f,P.copy(a))}}return n.vsub(e,S),S.dot(a)>0&&a.negate(a),!0};var q=new t.Vec3;this.clipAgainstHull=function(e,i,n,o,a,s,r,h,c){if(!(e instanceof t.Vec3))throw Error("posA must be Vec3");if(!(i instanceof t.Quaternion))throw Error("quatA must be Quaternion");for(var l=-1,u=-1/0,p=0;n.faces.length>p;p++){n.faceNormals[p].copy(q),a.vmult(q,q);var d=q.dot(s);d>u&&(u=d,l=p)}for(var v=[],y=n.faces[l],f=y.length,m=0;f>m;m++){var w=n.vertices[y[m]],b=new t.Vec3;w.copy(b),a.vmult(b,b),o.vadd(b,b),v.push(b)}l>=0&&this.clipFaceAgainstHull(s,e,i,v,r,h,c)};var j=new t.Vec3,C=new t.Vec3,B=new t.Vec3,I=new t.Vec3,R=new t.Vec3,O=new t.Vec3,A=new t.Vec3,T=new t.Vec3;this.clipFaceAgainstHull=function(e,i,n,o,a,r,h){if(!(e instanceof t.Vec3))throw Error("sep normal must be vector");if(!(o instanceof Array))throw Error("world verts must be array");a=Number(a),r=Number(r);for(var c=this,l=[],u=o,p=l,d=-1,v=1/0,y=0;c.faces.length>y;y++){c.faceNormals[y].copy(j),n.vmult(j,j);var f=j.dot(e);v>f&&(v=f,d=y)}if(0>d)return console.log("--- did not find any closest face... ---"),void 0;var m=c.faces[d];m.connectedFaces=[];for(var w=0;c.faces.length>w;w++)for(var b=0;c.faces[w].length>b;b++)-1!==m.indexOf(c.faces[w][b])&&w!==d&&-1===m.connectedFaces.indexOf(w)&&m.connectedFaces.push(w);u.length;for(var x=m.length,g=0;x>g;g++){var V=c.vertices[m[g]],z=c.vertices[m[(g+1)%x]];V.vsub(z,C),C.copy(B),n.vmult(B,B),i.vadd(B,B),this.faceNormals[d].copy(I),n.vmult(I,I),i.vadd(I,I),B.cross(I,R),R.negate(R),V.copy(O),n.vmult(O,O),i.vadd(O,O);var E,S=(-O.dot(R),m.connectedFaces[g]);this.faceNormals[S].copy(A);var N=s(S);A.copy(T),n.vmult(T,T);var E=N-T.dot(i);for(this.clipFaceAgainstPlane(u,p,T,E);u.length;)u.shift();for(;p.length;)u.push(p.shift())}this.faceNormals[d].copy(A);var N=s(d);A.copy(T),n.vmult(T,T);for(var E=N-T.dot(i),w=0;u.length>w;w++){var M=T.dot(u[w])+E;if(a>=M&&(console.log("clamped: depth="+M+" to minDist="+(a+"")),M=a),r>=M){var P=u[w];if(0>=M){var q={point:P,normal:T,depth:M};h.push(q)}}}},this.clipFaceAgainstPlane=function(e,i,n,o){if(!(n instanceof t.Vec3))throw Error("planeNormal must be Vec3, "+n+" given");if(!(e instanceof Array))throw Error("invertices must be Array, "+e+" given");if(!(i instanceof Array))throw Error("outvertices must be Array, "+i+" given");var a,s,r=e.length;if(2>r)return i;var h=e[e.length-1],c=e[0];a=n.dot(h)+o;for(var l=0;r>l;l++){if(c=e[l],s=n.dot(c)+o,0>a)if(0>s){var u=new t.Vec3;c.copy(u),i.push(u)}else{var u=new t.Vec3;h.lerp(c,a/(a-s),u),i.push(u)}else if(0>s){var u=new t.Vec3;h.lerp(c,a/(a-s),u),i.push(u),i.push(c)}h=c,a=s}return i};var r=this;this.calculateLocalInertia=function(t,e){r.computeAABB();var i=this.aabbmax.x-this.aabbmin.x,n=this.aabbmax.y-this.aabbmin.y,o=this.aabbmax.z-this.aabbmin.z;e.x=1/12*t*(2*2*n*n+2*2*o*o),e.y=1/12*t*(2*2*i*i+2*2*o*o),e.z=1/12*t*(2*2*n*n+2*2*i*i)},new t.Vec3,this.computeAABB=function(){var t=this.vertices.length,e=this.aabbmin,i=this.aabbmax,n=this.vertices;e.set(1/0,1/0,1/0),i.set(-1/0,-1/0,-1/0);for(var o=0;t>o;o++){var a=n[o];a.xi.x&&(i.x=a.x),a.yi.y&&(i.y=a.y),a.zi.z&&(i.z=a.z)}}},t.ConvexPolyhedron.prototype=new t.Shape,t.ConvexPolyhedron.prototype.constructor=t.ConvexPolyhedron,t.ConvexPolyhedron.prototype.computeWorldVertices=function(e,i){for(var n=this.vertices.length;n>this.worldVertices.length;)this.worldVertices.push(new t.Vec3);for(var o=this.vertices,a=this.worldVertices,s=0;s!==n;s++)i.vmult(o[s],a[s]),e.vadd(a[s],a[s]);this.worldVerticesNeedsUpdate=!1},t.ConvexPolyhedron.prototype.computeWorldFaceNormals=function(e){for(var i=this.faceNormals.length;i>this.worldFaceNormals.length;)this.worldFaceNormals.push(new t.Vec3);for(var n=this.faceNormals,o=this.worldFaceNormals,a=0;a!==i;a++)e.vmult(n[a],o[a]);this.worldFaceNormalsNeedsUpdate=!1},t.ConvexPolyhedron.prototype.computeBoundingSphereRadius=function(){for(var t=0,e=this.vertices,i=0,n=e.length;i!==n;i++){var o=e[i].norm2();o>t&&(t=o)}this.boundingSphereRadius=Math.sqrt(t),this.boundingSphereRadiusNeedsUpdate=!1};var P=new t.Vec3;t.ConvexPolyhedron.prototype.calculateWorldAABB=function(t,e,i,n){for(var o,a,s,r,h,c,l=this.vertices.length,u=this.vertices,p=0;l>p;p++){u[p].copy(P),e.vmult(P,P),t.vadd(P,P);var d=P;o>d.x||void 0===o?o=d.x:(d.x>r||void 0===r)&&(r=d.x),a>d.y||void 0===a?a=d.y:(d.y>h||void 0===h)&&(h=d.y),s>d.z||void 0===s?s=d.z:(d.z>c||void 0===c)&&(c=d.z)}i.set(o,a,s),n.set(r,h,c)},t.ConvexPolyhedron.prototype.volume=function(){return this.boundingSphereRadiusNeedsUpdate&&this.computeBoundingSphereRadius(),4*Math.PI*this.boundingSphereRadius/3},t.ConvexPolyhedron.prototype.getAveragePointLocal=function(e){e=e||new t.Vec3;for(var i=this.vertices.length,n=this.vertices,o=0;i>o;o++)e.vadd(n[o],e);return e.mult(1/i,e),e},t.ConvexPolyhedron.prototype.transformAllPoints=function(t,e){var i=this.vertices.length,n=this.vertices;if(e){for(var o=0;i>o;o++){var a=n[o];e.vmult(a,a)}for(var o=0;this.faceNormals.length>o;o++){var a=this.faceNormals[o];e.vmult(a,a)}}if(t)for(var o=0;i>o;o++){var a=n[o];a.vadd(t,a)}};var q=new t.Vec3,j=new t.Vec3,C=new t.Vec3;t.ConvexPolyhedron.prototype.pointIsInside=function(t){var e=this.vertices.length,i=this.vertices,n=this.faces,o=this.faceNormals,a=null,s=this.faces.length,r=q;this.getAveragePointLocal(r);for(var h=0;s>h;h++){this.faces[h].length;var e=o[h],c=i[n[h][0]],l=j;t.vsub(c,l);var u=e.dot(l),p=C;r.vsub(c,p);var d=e.dot(p);if(0>u&&d>0||u>0&&0>d)return!1}return a?1:-1},t.Cylinder=function(e,i,n,o){var a=o,s=[],r=[],h=[],c=[],l=[],u=Math.cos,p=Math.sin;s.push(new t.Vec3(i*u(0),i*p(0),.5*-n)),c.push(0),s.push(new t.Vec3(e*u(0),e*p(0),.5*n)),l.push(1);for(var d=0;a>d;d++){var v=2*Math.PI/a*(d+1),y=2*Math.PI/a*(d+.5);a-1>d?(s.push(new t.Vec3(i*u(v),i*p(v),.5*-n)),c.push(2*d+2),s.push(new t.Vec3(e*u(v),e*p(v),.5*n)),l.push(2*d+3),r.push(new t.Vec3(u(y),p(y),0)),h.push([2*d+2,2*d+3,2*d+1,2*d])):(h.push([0,1,2*d+1,2*d]),r.push(new t.Vec3(u(y),p(y),0)))}h.push(l),r.push(new t.Vec3(0,0,1));for(var f=[],d=0;c.length>d;d++)f.push(c[c.length-d-1]);h.push(f),r.push(new t.Vec3(0,0,-1)),this.type=t.Shape.types.CONVEXPOLYHEDRON,t.ConvexPolyhedron.call(this,s,h,r)},t.Cylinder.prototype=new t.ConvexPolyhedron,t.Ray=function(e,i){function n(t,e,i){return i.vsub(t,V),p=V.dot(e),e.mult(p,z),z.vadd(t,z),d=i.distanceTo(z)}function o(t,e,i,n){return n.vsub(e,V),i.vsub(e,E),t.vsub(e,S),v=V.dot(V),y=V.dot(E),f=V.dot(S),m=E.dot(E),w=E.dot(S),b=1/(v*m-y*y),x=(m*f-y*w)*b,g=(v*w-y*f)*b,x>=0&&g>=0&&1>x+g}this.origin=e||new t.Vec3,this.direction=i||new t.Vec3;var a=1e-4;this.setPrecision=function(t){a=t};var s=new t.Vec3,r=new t.Vec3,h=new t.Vec3;new t.Vec3,new t.Vec3;var c=new t.Vec3,l=new t.Vec3,u=new t.Vec3;this.intersectBody=function(e){return e.shape instanceof t.ConvexPolyhedron?this.intersectShape(e.shape,e.quaternion,e.position,e):e.shape instanceof t.Box?this.intersectShape(e.shape.convexPolyhedronRepresentation,e.quaternion,e.position,e):(console.warn("Ray intersection is this far only implemented for ConvexPolyhedron and Box shapes."),void 0)},this.intersectShape=function(e,i,p,d){var v,y=[];if(e instanceof t.ConvexPolyhedron){var f=n(this.origin,this.direction,p);if(f>e.getBoundingSphereRadius())return y;for(var m,w,b=e.faces,x=e.vertices,g=e.faceNormals,V=0;b.length>V;V++){var z=b[V],E=g[V],S=i,N=p;if(x[z[0]].copy(c),S.vmult(c,c),c.vadd(N,c),c.vsub(this.origin,c),S.vmult(E,l),m=this.direction.dot(l),!(a>Math.abs(m))&&(w=l.dot(c)/m,!(0>w)&&0>m)){this.direction.mult(w,u),u.vadd(this.origin,u),x[z[0]].copy(s),S.vmult(s,s),N.vadd(s,s);for(var M=1;z.length-1>M;M++)if(x[z[M]].copy(r),x[z[M+1]].copy(h),S.vmult(r,r),S.vmult(h,h),N.vadd(r,r),N.vadd(h,h),o(u,s,r,h)){v={distance:this.origin.distanceTo(u),point:u.copy(),face:z,body:d},y.push(v);break}}}}return y},this.intersectBodies=function(t){for(var e=[],i=0,n=t.length;n>i;i++){var o=this.intersectBody(t[i]);Array.prototype.push.apply(e,o)}return e.sort(function(t,e){return t.distance-e.distance}),e};var p,d,v,y,f,m,w,b,x,g,V=new t.Vec3,z=new t.Vec3,E=new t.Vec3,S=new t.Vec3},t.Ray.prototype.constructor=t.Ray,t.Broadphase=function(){this.world=null,this.useBoundingBoxes=!1},t.Broadphase.prototype.constructor=t.BroadPhase,t.Broadphase.prototype.collisionPairs=function(){throw Error("collisionPairs not implemented for this BroadPhase class!")
2 | };var B=t.Body.STATIC|t.Body.KINEMATIC;t.Broadphase.prototype.needBroadphaseCollision=function(e,i){return 0===(e.collisionFilterGroup&i.collisionFilterMask)||0===(i.collisionFilterGroup&e.collisionFilterMask)?!1:0===(e.motionstate&B)&&!e.isSleeping()||0===(i.motionstate&B)&&!i.isSleeping()?e.shape||i.shape?e.shape instanceof t.Plane&&i.shape instanceof t.Plane?!1:!0:!1:!1},t.Broadphase.prototype.intersectionTest=function(t,e,i,n){this.useBoundingBoxes?this.doBoundingBoxBroadphase(t,e,i,n):this.doBoundingSphereBroadphase(t,e,i,n)};var I=new t.Vec3,R=new t.Vec3,O=(new t.Quaternion,new t.Vec3);t.Broadphase.prototype.doBoundingSphereBroadphase=function(e,i,n,o){var a=t.Shape.types,s=a.SPHERE|a.BOX|a.COMPOUND|a.CONVEXPOLYHEDRON,r=a.PLANE;t.Body.STATIC|t.Body.KINEMATIC;var h=I,c=R,l=O,u=e.shape,p=i.shape;if(u&&p){var d=u.type,v=p.type;if(d&s&&v&s){i.position.vsub(e.position,h),u.boundingSphereRadiusNeedsUpdate&&u.computeBoundingSphereRadius(),p.boundingSphereRadiusNeedsUpdate&&p.computeBoundingSphereRadius();var y=u.boundingSphereRadius+p.boundingSphereRadius;y*y>h.norm2()&&(n.push(e),o.push(i))}else if(d&s&&v&a.PLANE||v&s&&d&a.PLANE){var f=d===r?e:i,m=d!==r?e:i,w=m.shape,b=f.shape;m.position.vsub(f.position,h),b.worldNormalNeedsUpdate&&b.computeWorldNormal(f.quaternion),c=b.worldNormal,w.boundingSphereRadiusNeedsUpdate&&w.computeBoundingSphereRadius();var x=h.dot(c)-w.boundingSphereRadius;0>x&&(n.push(e),o.push(i))}}else if(u||p){var g=u?i:e,V=u?e:i,w=V.shape,z=w.type;if(z&s){if(z===a.SPHERE)g.position.vsub(V.position,l),w.radius*w.radius>=l.norm2()&&(n.push(g),o.push(V));else if(z===a.CONVEXPOLYHEDRON||z===a.BOX||z===a.COMPOUND){w.boundingSphereRadiusNeedsUpdate&&w.computeBoundingSphereRadius();var E=w.boundingSphereRadius;g.position.vsub(V.position,l),E*E>=l.norm2()&&(n.push(g),o.push(V))}}else if(z===a.PLANE){var S=V;c.set(0,0,1),S.quaternion.vmult(c,c),g.position.vsub(S.position,l),0>=c.dot(l)&&(n.push(g),o.push(V))}}else;},t.Broadphase.prototype.doBoundingBoxBroadphase=function(e,i,n,o){var a=e.shape,s=i.shape;if(e.aabbNeedsUpdate&&e.computeAABB(),i.aabbNeedsUpdate&&i.computeAABB(),a&&s)e.aabbmax.xi.aabbmax.x||e.aabbmin.y>i.aabbmax.y||e.aabbmin.z>i.aabbmax.z||(n.push(e),o.push(i));else if(a||s){var r=a?i:e,h=a?e:i;h.shape instanceof t.Plane,r.position.xh.aabbmax.x||r.position.y>h.aabbmax.y||r.position.z>h.aabbmax.z||(n.push(e),o.push(i))}else;};var A={},T=[],F=[];t.Broadphase.prototype.makePairsUnique=function(t,e){for(var i=A,n=T,o=F,a=t.length,s=0;s!==a;s++)n[s]=t[s],o[s]=e[s];t.length=0,e.length=0;for(var s=0;s!==a;s++){var r=n[s].id,h=o[s].id,c=h>r?r+","+h:h+","+r;i[c]=s}for(var c in i){var s=i[c];t.push(n[s]),e.push(o[s]),delete i[c]}},t.NaiveBroadphase=function(){t.Broadphase.apply(this)},t.NaiveBroadphase.prototype=new t.Broadphase,t.NaiveBroadphase.prototype.constructor=t.NaiveBroadphase,t.NaiveBroadphase.prototype.collisionPairs=function(t,e,i){var n,o,a,s,r=t.bodies,h=r.length;for(n=0;n!==h;n++)for(o=0;o!==n;o++)a=r[n],s=r[o],this.needBroadphaseCollision(a,s)&&this.intersectionTest(a,s,e,i)},t.GridBroadphase=function(e,i,n,o,a){t.Broadphase.apply(this),this.nx=n||10,this.ny=o||10,this.nz=a||10,this.aabbMin=e||new t.Vec3(100,100,100),this.aabbMax=i||new t.Vec3(-100,-100,-100),this.bins=[]},t.GridBroadphase.prototype=new t.Broadphase,t.GridBroadphase.prototype.constructor=t.GridBroadphase;var k=new t.Vec3,U=new t.Vec3;t.GridBroadphase.prototype.collisionPairs=function(e,i,n){var o=e.numObjects(),a=e.bodies,s=this.aabbMax,r=this.aabbMin,h=this.nx,c=this.ny,l=this.nz,u=s.x,p=s.y,d=s.z,v=r.x,y=r.y,f=r.z,m=h/(u-v),w=c/(p-y),b=l/(d-f),x=(u-v)/h,g=(p-y)/c,V=(d-f)/l,z=t.Shape.types,E=z.SPHERE,S=z.PLANE;z.BOX,z.COMPOUND,z.CONVEXPOLYHEDRON;for(var N=this.bins,M=h*c*l,P=N.length-1;P!==M;P++)N.push([]);for(var P=0;P!==M;P++)N[P].length=0;for(var q=Math.floor,P=0;P!==o;P++){var j=a[P],C=j.shape;switch(C.type){case E:for(var B=j.position.x,I=j.position.y,R=j.position.z,O=C.radius,A=q(m*(B-O-v)),T=q(w*(I-O-y)),F=q(b*(R-O-f)),W=q(m*(B+O-v)),L=q(w*(I+O-y)),D=q(b*(R+O-f)),H=A;H!==W+1;H++)for(var Q=T;Q!==L+1;Q++)for(var X=F;X!==D+1;X++){var G=H,Y=Q,Z=X,_=G*(c-1)*(l-1)+Y*(l-1)+Z;_>=0&&M>_&&N[_].push(j)}break;case S:var K=k,J=U,$=.25*(x*x+g*g+V*V),te=C.worldNormal;C.worldNormalNeedsUpdate&&C.computeWorldNormal(j.quaternion);for(var H=0;H!==h;H++)for(var Q=0;Q!==c;Q++)for(var X=0;X!==l;X++){var G=H,Y=Q,Z=X;if(J.set(G*x+v,Y*g+y,Z*V+f),J.vsub(j.position,K),$>K.dot(te)){var _=G*(c-1)*(l-1)+Y*(l-1)+Z;N[_].push(j)}}break;default:console.warn("Shape "+C.type+" not supported in GridBroadphase!")}}for(var P=0;P!==M;P++)for(var ee=N[P],H=0,ie=ee.length;H!==ie;H++)for(var j=ee[H],Q=0;Q!==H;Q++){var ne=ee[Q];this.needBroadphaseCollision(j,ne)&&this.intersectionTest(j,ne,i,n)}this.makePairsUnique(i,n)},t.Solver=function(){this.equations=[]},t.Solver.prototype.solve=function(){return 0},t.Solver.prototype.addEquation=function(t){this.equations.push(t)},t.Solver.prototype.removeEquation=function(t){var e=this.equations,i=e.indexOf(t);-1!==i&&e.splice(i,1)},t.Solver.prototype.removeAllEquations=function(){this.equations.length=0},t.GSSolver=function(){t.Solver.call(this),this.iterations=10,this.tolerance=0},t.GSSolver.prototype=new t.Solver;var W=[],L=[],D=[];t.GSSolver.prototype.solve=function(t,e){var i,n,o,a,s,r,h=(this.d,this.k,0),c=this.iterations,l=this.tolerance*this.tolerance,u=(this.a,this.b),p=this.equations,d=p.length,v=e.bodies,y=v.length,f=t,m=L,w=D,b=W;m.length=0,w.length=0,b.length=0;for(var x=0;x!==d;x++){var g=p[x];g.spookParamsNeedsUpdate&&(g.updateSpookParams(f),g.spookParamsNeedsUpdate=!1),b[x]=0,w[x]=g.computeB(f),m[x]=1/g.computeC()}if(0!==d){for(var x=0;x!==y;x++){var u=v[x],V=u.vlambda,z=u.wlambda;V.set(0,0,0),z&&z.set(0,0,0)}for(h=0;h!==c;h++){a=0;for(var E=0;E!==d;E++){var g=p[E];i=w[E],n=m[E],r=b[E],s=g.computeGWlambda(),o=n*(i-s-g.eps*r),g.minForce>r+o?o=g.minForce-r:r+o>g.maxForce&&(o=g.maxForce-r),b[E]+=o,a+=o>0?o:-o,g.addToWlambda(o)}if(l>a*a)break}for(var x=0;x!==y;x++){var u=v[x],S=u.velocity,N=u.angularVelocity;S.vadd(u.vlambda,S),N&&N.vadd(u.wlambda,N)}}return h},t.SplitSolver=function(e){t.Solver.call(this),this.subsolver=e},t.SplitSolver.prototype=new t.Solver;var H=[],Q=[],X=[],G={bodies:null};t.SplitSolver.prototype.solve=function(e,i){function n(t){for(var e=t.length,i=0;i!==e;i++){var n=t[i];if(!(n.visited||n.body.motionstate&x))return n}return!1}function o(t,e){var i=[];for(i.push(t),t.visited=!0,e(t);i.length;)for(var o,a=i.pop();o=n(a.children);)o.visited=!0,e(o),i.push(o)}function a(t){z.push(t.body);for(var e=t.eqs.length,i=0;i!==e;i++){var n=t.eqs[i];-1===V.indexOf(n)&&V.push(n)}}for(var s=H,r=i.bodies,h=this.equations,c=h.length,l=r.length,u=this.subsolver,p=s.length;p!==l;p++)s.push({body:r[p],children:[],eqs:[],visited:!1});for(var p=0;p!==l;p++){var d=s[p];d.body=r[p],d.children.length=0,d.eqs.length=0,d.visited=!1}for(var v=0;v!==c;v++){var y=h[v],p=r.indexOf(y.bi),f=r.indexOf(y.bj),m=s[p],w=s[f];m.children.push(w),m.eqs.push(y),w.children.push(m),w.eqs.push(y)}for(var b,x=t.Body.STATIC,g=0,V=Q,z=X,E=G;b=n(s);){V.length=0,z.length=0,o(b,a);for(var S=V.length,p=0;p!==S;p++)u.addEquation(V[p]);E.bodies=z,u.solve(e,E),u.removeAllEquations(),g++}return g},t.Material=function(t){this.name=t,this.id=-1},t.ContactMaterial=function(t,e,i,n){this.id=-1,this.materials=[t,e],this.friction=void 0!==i?Number(i):.3,this.restitution=void 0!==n?Number(n):.3,this.contactEquationStiffness=1e7,this.contactEquationRegularizationTime=3,this.frictionEquationStiffness=1e7,this.frictionEquationRegularizationTime=3},t.World=function(){t.EventTarget.apply(this),this.allowSleep=!1,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=0,this.quatNormalizeFast=!1,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.last_dt=this.default_dt,this.nextId=0,this.gravity=new t.Vec3,this.broadphase=null,this.bodies=[],this.solver=new t.GSSolver,this.constraints=[],this.contactgen=new t.ContactGenerator,this.collisionMatrix=[],this.collisionMatrixPrevious=[],this.materials=[],this.contactmaterials=[],this.mats2cmat=[],this.defaultMaterial=new t.Material("default"),this.defaultContactMaterial=new t.ContactMaterial(this.defaultMaterial,this.defaultMaterial,.3,0),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,nearphase:0},this.subsystems=[]},t.World.prototype.getContactMaterial=function(e,i){if(e instanceof t.Material&&i instanceof t.Material){var n=e.id,o=i.id;if(o>n){var a=n;n=o,o=a}return this.contactmaterials[this.mats2cmat[n+o*this.materials.length]]}},t.World.prototype.numObjects=function(){return this.bodies.length},t.World.prototype.collisionMatrixGet=function(t,e,i){if(e>t){var n=e;e=t,t=n}return t=(t*(t+1)>>1)+e-1,i===void 0||i?this.collisionMatrix[t]:this.collisionMatrixPrevious[t]},t.World.prototype.collisionMatrixSet=function(t,e,i,n){if(e>t){var o=e;e=t,t=o}t=(t*(t+1)>>1)+e-1,n===void 0||n?this.collisionMatrix[t]=i:this.collisionMatrixPrevious[t]=i},t.World.prototype.collisionMatrixTick=function(){var t=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=t;for(var e=0,i=this.collisionMatrix.length;e!==i;e++)this.collisionMatrix[e]=0},t.World.prototype.add=function(e){e.id=this.id(),e.index=this.bodies.length,this.bodies.push(e),e.world=this,e.position.copy(e.initPosition),e.velocity.copy(e.initVelocity),e.timeLastSleepy=this.time,e instanceof t.RigidBody&&(e.angularVelocity.copy(e.initAngularVelocity),e.quaternion.copy(e.initQuaternion));var i=this.numObjects();this.collisionMatrix.length=i*(i-1)>>1},t.World.prototype.addConstraint=function(t){this.constraints.push(t),t.id=this.id()},t.World.prototype.removeConstraint=function(t){var e=this.constraints.indexOf(t);-1!==e&&this.constraints.splice(e,1)},t.World.prototype.id=function(){return this.nextId++},t.World.prototype.remove=function(t){t.world=null;var e=this.numObjects()-1,i=this.bodies;i.splice(t.index,1);for(var n=t.index;e>n;n++)i[n].index=n;this.collisionMatrixPrevious.length=this.collisionMatrix.length=e*(e-1)>>1},t.World.prototype.addMaterial=function(t){if(-1===t.id){var e=this.materials.length;this.materials.push(t),t.id=this.materials.length-1;for(var i=0;i!==2*e+1;i++)this.mats2cmat.push(-1)}},t.World.prototype.addContactMaterial=function(t){this.addMaterial(t.materials[0]),this.addMaterial(t.materials[1]);var e,i;t.materials[0].id>t.materials[1].id?(e=t.materials[0].id,i=t.materials[1].id):(i=t.materials[0].id,e=t.materials[1].id),this.contactmaterials.push(t),t.id=this.contactmaterials.length-1,this.mats2cmat[e+this.materials.length*i]=t.id},t.World.prototype._now=function(){return window.performance.webkitNow?window.performance.webkitNow():Date.now()};var Y={type:"postStep"},Z={type:"collide","with":null,contact:null},_=[],K=[],J=[],$=[],te=new t.Vec3,ee=(new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Quaternion,new t.Quaternion),ie=new t.Quaternion;t.World.prototype.step=function(e){var i,n=this.contacts,o=J,a=$,s=this.numObjects(),r=this.bodies,h=this.solver,c=this.gravity,l=this.doProfiling,u=this.profile,p=t.Body.DYNAMIC,d=this._now,v=this.constraints,y=t.FrictionEquation,f=K,m=c.norm(),w=c.x,b=c.y,x=c.z,g=0;for(l&&(i=d()),void 0===e&&(e=this.last_dt||this.default_dt),g=0;g!==s;g++){var V=r[g];if(V.motionstate&p){var z=V.force,E=V.mass;z.x+=E*w,z.y+=E*b,z.z+=E*x}}for(var g=0,S=this.subsystems.length;g!==S;g++)this.subsystems[g].update();l&&(i=d()),o.length=0,a.length=0,this.broadphase.collisionPairs(this,o,a),l&&(u.broadphase=d()-i),this.collisionMatrixTick(),l&&(i=d());var N=_,M=n.length;for(g=0;g!==M;g++)N.push(n[g]);n.length=0,this.contactgen.getContacts(o,a,this,n,N),l&&(u.nearphase=d()-i),l&&(i=d());var P=n.length,q=this.frictionEquations.length;for(g=0;g!==q;g++)f.push(this.frictionEquations[g]);this.frictionEquations.length=0;for(var j=0;j!==P;j++){var C=n[j],V=C.bi,B=C.bj,g=r.indexOf(V),I=r.indexOf(B),R=this.getContactMaterial(V.material,B.material)||this.defaultContactMaterial,O=R.friction;R.restitution;var A=te;A.set(B.position.x+C.rj.x-V.position.x-C.ri.x,B.position.y+C.rj.y-V.position.y-C.ri.y,B.position.z+C.rj.z-V.position.z-C.ri.z);var T=A.dot(C.ni);if(0>T){if(C.restitution=R.restitution,C.penetration=T,C.stiffness=R.contactEquationStiffness,C.regularizationTime=R.contactEquationRegularizationTime,h.addEquation(C),O>0){var F=O*m,k=V.invMass+B.invMass;k>0&&(k=1/k);var U=f,W=U.length?U.pop():new y(V,B,F*k),L=U.length?U.pop():new y(V,B,F*k);this.frictionEquations.push(W),this.frictionEquations.push(L),W.bi=L.bi=V,W.bj=L.bj=B,W.minForce=L.minForce=-F*k,W.maxForce=L.maxForce=F*k,C.ri.copy(W.ri),C.rj.copy(W.rj),C.ri.copy(L.ri),C.rj.copy(L.rj),C.ni.tangents(W.t,L.t),h.addEquation(W),h.addEquation(L)}this.collisionMatrixSet(g,I,1,!0),this.collisionMatrixGet(g,I,!0)!==this.collisionMatrixGet(g,I,!1)&&(Z.with=B,Z.contact=C,V.dispatchEvent(Z),Z.with=V,B.dispatchEvent(Z),V.wakeUp(),B.wakeUp())}}l&&(u.makeContactConstraints=d()-i),l&&(i=d());var D=v.length;for(g=0;g!==D;g++){var C=v[g];C.update();for(var I=0,H=C.equations.length;I!==H;I++){var Q=C.equations[I];h.addEquation(Q)}}h.solve(e,this),l&&(u.solve=d()-i),h.removeAllEquations();var X=Math.pow;for(g=0;g!==s;g++){var V=r[g];if(V.motionstate&p){var G=X(1-V.linearDamping,e),ne=V.velocity;ne.mult(G,ne);var oe=V.angularVelocity;if(oe){var ae=X(1-V.angularDamping,e);oe.mult(ae,oe)}}}for(this.dispatchEvent(Y),g=0;g!==s;g++){var V=r[g];V.preStep&&V.preStep.call(V)}l&&(i=d());var se=ee,re=ie,he=this.stepnumber,ce=t.Body.DYNAMIC|t.Body.KINEMATIC,le=0===he%(this.quatNormalizeSkip+1),ue=this.quatNormalizeFast,pe=.5*e,de=t.Shape.types.PLANE,ve=t.Shape.types.CONVEXPOLYHEDRON;for(g=0;g!==s;g++){var ye=r[g],fe=ye.shape,me=ye.force,we=ye.tau;if(ye.motionstate&ce){var be=ye.velocity,xe=ye.angularVelocity,ge=ye.position,Ve=ye.quaternion,ze=ye.invMass,Ee=ye.invInertia;if(be.x+=me.x*ze*e,be.y+=me.y*ze*e,be.z+=me.z*ze*e,ye.angularVelocity&&(xe.x+=we.x*Ee.x*e,xe.y+=we.y*Ee.y*e,xe.z+=we.z*Ee.z*e),ye.isSleeping()||(ge.x+=be.x*e,ge.y+=be.y*e,ge.z+=be.z*e,ye.angularVelocity&&(se.set(xe.x,xe.y,xe.z,0),se.mult(Ve,re),Ve.x+=pe*re.x,Ve.y+=pe*re.y,Ve.z+=pe*re.z,Ve.w+=pe*re.w,le&&(ue?Ve.normalizeFast():Ve.normalize())),ye.aabbmin&&(ye.aabbNeedsUpdate=!0)),fe)switch(fe.type){case de:fe.worldNormalNeedsUpdate=!0;break;case ve:fe.worldFaceNormalsNeedsUpdate=!0,fe.worldVerticesNeedsUpdate=!0}}ye.force.set(0,0,0),ye.tau&&ye.tau.set(0,0,0)}for(l&&(u.integrate=d()-i),this.time+=e,this.stepnumber+=1,this.dispatchEvent(Y),g=0;g!==s;g++){var V=r[g],Se=V.postStep;Se&&Se.call(V)}for(g=0;g!==s;g++){var ye=r[g];ye.inertiaWorldAutoUpdate&&ye.quaternion.vmult(ye.inertia,ye.inertiaWorld),ye.invInertiaWorldAutoUpdate&&ye.quaternion.vmult(ye.invInertia,ye.invInertiaWorld)}if(this.allowSleep)for(g=0;g!==s;g++)r[g].sleepTick(this.time)},t.ContactGenerator=function(){function e(e,i){if(f.length){var n=f.pop();return n.bi=e,n.bj=i,n}return new t.ContactEquation(e,i)}function i(t){var e;e=t.ri,t.ri=t.rj,t.rj=e,t.ni.negate(t.ni),e=t.bi,t.bi=t.bj,t.bj=e}function n(t,i,n,o,a,s,r,h,c){var l=e(h,c);c.position.vsub(o,l.ni),l.ni.normalize(),l.ni.copy(l.ri),l.ni.copy(l.rj),l.ri.mult(i.radius,l.ri),l.rj.mult(-n.radius,l.rj),t.push(l)}function o(t,i,n,o,a,s,r,h,c){var l=e(h,c);l.ni.set(0,0,1),r.vmult(l.ni,l.ni),l.ni.negate(l.ni),l.ni.normalize(),l.ni.mult(i.radius,l.ri),o.vsub(a,w),l.ni.mult(l.ni.dot(w),b),w.vsub(b,l.rj),b.norm2()<=i.radius*i.radius&&t.push(l)}function a(t,e,i){for(var n=null,o=t.length,a=0;a!==o;a++){var s=t[a],r=x;t[(a+1)%o].vsub(s,r);var h=g;r.cross(e,h);var c=V;i.vsub(s,c);var l=h.dot(c);if(!(null===n||l>0&&n===!0||0>=l&&n===!1))return!1;null===n&&(n=l>0)}return!0}function s(t,i,n,o,a,s,r,h,c){var l=M;o.vsub(a,z),n.getSideNormals(l,r);for(var u=i.radius,p=!1,d=q,v=j,y=C,f=null,w=0,b=0,x=0,g=null,V=0,B=l.length;V!==B&&p===!1;V++){var I=E;l[V].copy(I);var R=I.norm();I.normalize();var O=z.dot(I);if(R+u>O&&O>0){var A=S,T=N;l[(V+1)%3].copy(A),l[(V+2)%3].copy(T);var F=A.norm(),k=T.norm();A.normalize(),T.normalize();var U=z.dot(A),W=z.dot(T);if(F>U&&U>-F&&k>W&&W>-k){var L=Math.abs(O-R-u);(null===g||g>L)&&(g=L,b=U,x=W,f=R,I.copy(d),A.copy(v),T.copy(y),w++)}}}if(w){p=!0;var D=e(h,c);d.mult(-u,D.ri),d.copy(D.ni),D.ni.negate(D.ni),d.mult(f,d),v.mult(b,v),d.vadd(v,d),y.mult(x,y),d.vadd(y,D.rj),t.push(D)}for(var H=m.get(),Q=P,X=0;2!==X&&!p;X++)for(var G=0;2!==G&&!p;G++)for(var Y=0;2!==Y&&!p;Y++)if(H.set(0,0,0),X?H.vadd(l[0],H):H.vsub(l[0],H),G?H.vadd(l[1],H):H.vsub(l[1],H),Y?H.vadd(l[2],H):H.vsub(l[2],H),a.vadd(H,Q),Q.vsub(o,Q),u*u>Q.norm2()){p=!0;var D=e(h,c);Q.copy(D.ri),D.ri.normalize(),D.ri.copy(D.ni),D.ri.mult(u,D.ri),H.copy(D.rj),t.push(D)}m.release(H),H=null;for(var Z=m.get(),_=m.get(),D=m.get(),K=m.get(),L=m.get(),J=l.length,X=0;X!==J&&!p;X++)for(var G=0;G!==J&&!p;G++)if(X%3!==G%3){l[G].cross(l[X],Z),Z.normalize(),l[X].vadd(l[G],_),o.copy(D),D.vsub(_,D),D.vsub(a,D);var $=D.dot(Z);Z.mult($,K);for(var Y=0;Y===X%3||Y===G%3;)Y++;o.copy(L),L.vsub(K,L),L.vsub(_,L),L.vsub(a,L);var te=Math.abs($),ee=L.norm();if(l[Y].norm()>te&&u>ee){p=!0;var ie=e(h,c);_.vadd(K,ie.rj),ie.rj.copy(ie.rj),L.negate(ie.ni),ie.ni.normalize(),ie.rj.copy(ie.ri),ie.ri.vadd(a,ie.ri),ie.ri.vsub(o,ie.ri),ie.ri.normalize(),ie.ri.mult(u,ie.ri),t.push(ie)}}m.release(Z,_,D,K,L)}function r(t,i,n,o,s,r,h,c,l){o.vsub(s,B);for(var u=n.faceNormals,p=n.faces,d=n.vertices,v=i.radius,y=0;y!==d.length;y++){var f=d[y],w=A;h.vmult(f,w),s.vadd(w,w);var b=O;if(w.vsub(o,b),v*v>b.norm2()){g=!0;var x=e(c,l);return b.copy(x.ri),x.ri.normalize(),x.ri.copy(x.ni),x.ri.mult(v,x.ri),w.vsub(s,x.rj),t.push(x),void 0}}for(var g=!1,y=0,V=p.length;y!==V&&g===!1;y++){var z=u[y],E=p[y],S=T;h.vmult(z,S);var N=F;h.vmult(d[E[0]],N),N.vadd(s,N);var M=k;S.mult(-v,M),o.vadd(M,M);var P=U;M.vsub(N,P);var q=P.dot(S),j=W;if(o.vsub(N,j),0>q&&j.dot(S)>0){for(var C=[],L=0,D=E.length;L!==D;L++){var H=m.get();h.vmult(d[E[L]],H),s.vadd(H,H),C.push(H)}if(a(C,S,o)){g=!0;var x=e(c,l);S.mult(-v,x.ri),S.negate(x.ni);var Q=m.get();S.mult(-q,Q);var X=m.get();S.mult(-v,X),o.vsub(s,x.rj),x.rj.vadd(X,x.rj),x.rj.vadd(Q,x.rj),m.release(Q),m.release(X),t.push(x);for(var L=0,G=C.length;L!==G;L++)m.release(C[L]);return}for(var L=0;L!==E.length;L++){var Y=m.get(),Z=m.get();h.vmult(d[E[(L+1)%E.length]],Y),h.vmult(d[E[(L+2)%E.length]],Z),s.vadd(Y,Y),s.vadd(Z,Z);var _=I;Z.vsub(Y,_);var K=R;_.unit(K);var J=m.get(),$=m.get();o.vsub(Y,$);var te=$.dot(K);K.mult(te,J),J.vadd(Y,J);var ee=m.get();if(J.vsub(o,ee),te>0&&_.norm2()>te*te&&v*v>ee.norm2()){var x=e(c,l);J.vsub(s,x.rj),J.vsub(o,x.ni),x.ni.normalize(),x.ni.mult(v,x.ri),t.push(x);for(var L=0,G=C.length;L!==G;L++)m.release(C[L]);return m.release(Y),m.release(Z),m.release(J),m.release(ee),m.release($),void 0}m.release(Y),m.release(Z),m.release(J),m.release(ee),m.release($)}for(var L=0,G=C.length;L!==G;L++)m.release(C[L])}}}function h(t,e,i,n,o,a,s,r,h){l(t,e,i.convexPolyhedronRepresentation,n,o,a,s,r,h)}function c(e,i,n,o,a,s,r,h,c){for(var l=L,u=D,p=0,d=0,v=n.childShapes.length;d!==v;d++){var f=[],m=u.pop()||new t.Quaternion,w=l.pop()||new t.Vec3;r.mult(n.childOrientations[d],m),m.normalize(),r.vmult(n.childOffsets[d],w),a.vadd(w,w),y(f,i,n.childShapes[d],o,w,s,m,h,c),u.push(m);var b=w;i||(p+=f.length);for(var x=0;x!==f.length;x++)r.vmult(n.childOffsets[d],b),f[x].rj.vadd(b,f[x].rj),e.push(f[x]);l.push(w)}}function l(t,i,n,o,a,s,r,h,c){var l=H,u=Q;u.set(0,0,1),s.vmult(u,u);for(var p=X,d=0;d!==n.vertices.length;d++){n.vertices[d].copy(l),r.vmult(l,l),a.vadd(l,l),l.vsub(o,p);var v=u.dot(p);if(0>=v){var y=G;u.mult(u.dot(l),y),l.vsub(y,y);var f=e(h,c);u.copy(f.ni),y.copy(f.ri),l.vsub(a,f.rj),t.push(f)}}}function u(t,i,n,o,a,s,r,h,c){var l=Y;if(i.findSeparatingAxis(n,o,s,a,r,l)){var u=[],p=Z;i.clipAgainstHull(o,s,n,a,r,l,-100,100,u);for(var d=0;d!==u.length;d++){var v=e(h,c);l.negate(v.ni),u[d].normal.negate(p),p.mult(u[d].depth,p),u[d].point.vadd(p,v.ri),u[d].point.copy(v.rj),v.rj.vsub(a,v.rj),v.ri.vsub(o,v.ri),t.push(v)}}}function p(t,i,n,o,a,s,r,h,c){var l=_;l.set(0,0,1),c.quaternion.vmult(l,l);var u=K;o.vsub(c.position,u);var p=l.dot(u);if(0>=p){var d=e(h,c);l.copy(d.ni),d.ni.negate(d.ni),d.ri.set(0,0,0);var v=J;l.mult(l.dot(o),v),o.vsub(v,v),v.copy(d.rj),t.push(d)}}function d(t,i,n,o,a,s,r,h,c){var l=$;l.set(0,0,1),o.vsub(a,l);var u=l.norm2();if(n.radius*n.radius>=u){var p=e(h,c);l.normalize(),l.copy(p.rj),p.rj.mult(n.radius,p.rj),l.copy(p.ni),p.ni.negate(p.ni),p.ri.set(0,0,0),t.push(p)}}function v(t,i,n,o,a,s,r,h,c){var l=-1,u=ie,p=oe,d=null,v=0,y=ee;if(o.copy(y),y.vsub(a,y),r.conjugate(te),te.vmult(y,y),n.pointIsInside(y)){n.worldVerticesNeedsUpdate&&n.computeWorldVertices(a,r),n.worldFaceNormalsNeedsUpdate&&n.computeWorldFaceNormals(r);for(var f=0,m=n.faces.length;f!==m;f++){var w=[n.worldVertices[n.faces[f][0]]],b=n.worldFaceNormals[f];o.vsub(w[0],ne);var x=-b.dot(ne);(null===d||Math.abs(x)f.type){var j;j=f,f=a,a=j,j=w,w=m,m=j,j=x,x=b,b=j,j=V,V=g,g=j,z=!0}}else if(a&&!f){var j;j=f,f=a,a=j,j=w,w=m,m=j,j=x,x=b,b=j,j=V,V=g,g=j,z=!0}if(a&&f){if(a.type===S)switch(f.type){case S:n(e,a,f,m,w,b,x,g,V);break;case N:o(e,a,f,m,w,b,x,g,V);break;case M:s(e,a,f,m,w,b,x,g,V);break;case P:c(e,a,f,m,w,b,x,g,V);break;case q:r(e,a,f,m,w,b,x,g,V);break;default:console.warn("Collision between CANNON.Shape.types.SPHERE and "+f.type+" not implemented yet.")}else if(a.type===E.PLANE)switch(f.type){case E.PLANE:throw Error("Plane-plane collision... wait, you did WHAT?");case E.BOX:h(e,a,f,m,w,b,x,g,V);break;case E.COMPOUND:c(e,a,f,m,w,b,x,g,V);break;case E.CONVEXPOLYHEDRON:l(e,a,f,m,w,b,x,g,V);break;default:console.warn("Collision between CANNON.Shape.types.PLANE and "+f.type+" not implemented yet.")}else if(a.type===E.BOX)switch(f.type){case E.BOX:y(e,a.convexPolyhedronRepresentation,f.convexPolyhedronRepresentation,m,w,b,x,g,V);break;case E.COMPOUND:c(e,a,f,m,w,b,x,g,V);break;case E.CONVEXPOLYHEDRON:y(e,a.convexPolyhedronRepresentation,f,m,w,b,x,g,V);break;default:console.warn("Collision between CANNON.Shape.types.BOX and "+f.type+" not implemented yet.")}else if(a.type===E.COMPOUND)switch(f.type){case E.COMPOUND:c(e,a,f,m,w,b,x,g,V);break;case E.CONVEXPOLYHEDRON:var C=[];c(C,f,a,w,m,x,b,V,g);for(var B=0;B!==C.length;B++)i(C[B]),e.push(C[B]);break;default:console.warn("Collision between CANNON.Shape.types.COMPOUND and "+f.type+" not implemented yet.")}else if(a.type===E.CONVEXPOLYHEDRON)switch(f.type){case E.CONVEXPOLYHEDRON:u(e,a,f,m,w,b,x,g,V);break;default:console.warn("Collision between CANNON.Shape.types.CONVEXPOLYHEDRON and "+f.type+" not implemented yet.")}}else switch(f.type){case E.PLANE:p(e,a,f,m,w,b,x,g,V);break;case E.SPHERE:d(e,a,f,m,w,b,x,g,V);break;case E.BOX:v(e,a,f.convexPolyhedronRepresentation,m,w,b,x,g,V);break;case E.CONVEXPOLYHEDRON:v(e,a,f,m,w,b,x,g,V);break;case E.COMPOUND:c(e,a,f,m,w,b,x,g,V);break;default:console.warn("Collision between CANNON.Particle and "+f.type+" not implemented yet.")}for(var I=0,R=e.length;z&&I!==R;I++)i(e[I])}this.contactReduction=!1;var f=[],m=new t.Vec3Pool,w=new t.Vec3,b=new t.Vec3,x=new t.Vec3,g=new t.Vec3,V=new t.Vec3,z=new t.Vec3,E=new t.Vec3,S=new t.Vec3,N=new t.Vec3,M=[new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3,new t.Vec3],P=new t.Vec3,q=new t.Vec3,j=new t.Vec3,C=new t.Vec3,B=new t.Vec3,I=new t.Vec3,R=new t.Vec3,O=new t.Vec3,A=new t.Vec3,T=new t.Vec3,F=new t.Vec3,k=new t.Vec3,U=new t.Vec3,W=new t.Vec3;new t.Vec3,new t.Vec3;var L=[],D=[],H=new t.Vec3,Q=new t.Vec3,X=new t.Vec3,G=new t.Vec3,Y=new t.Vec3,Z=new t.Vec3,_=new t.Vec3,K=new t.Vec3,J=new t.Vec3,$=new t.Vec3,te=new t.Quaternion,ee=new t.Vec3;new t.Vec3;var ie=new t.Vec3,ne=new t.Vec3,oe=new t.Vec3;this.reduceContacts=function(){},this.getContacts=function(t,e,i,n,o){f=o;for(var a=0,s=t.length;a!==s;a++){var r=t[a],h=e[a];y(n,r.shape,h.shape,r.position,h.position,r.quaternion,h.quaternion,r,h)}}},t.Equation=function(t,e,i,n){this.id=-1,this.minForce=i===void 0?-1e6:i,this.maxForce=n===void 0?1e6:n,this.bi=t,this.bj=e,this.stiffness=1e7,this.regularizationTime=5,this.a=0,this.b=0,this.eps=0,this.spookParamsNeedsUpdate=!0},t.Equation.prototype.constructor=t.Equation,t.Equation.prototype.updateSpookParams=function(t){var e=this.regularizationTime,i=this.stiffness;this.a=4/(t*(1+4*e)),this.b=4*e/(1+4*e),this.eps=4/(t*t*i*(1+4*e))},t.ContactEquation=function(e,i){t.Equation.call(this,e,i,0,1e6),this.restitution=0,this.ri=new t.Vec3,this.rj=new t.Vec3,this.penetrationVec=new t.Vec3,this.ni=new t.Vec3,this.rixn=new t.Vec3,this.rjxn=new t.Vec3,this.invIi=new t.Mat3,this.invIj=new t.Mat3,this.biInvInertiaTimesRixn=new t.Vec3,this.bjInvInertiaTimesRjxn=new t.Vec3},t.ContactEquation.prototype=new t.Equation,t.ContactEquation.prototype.constructor=t.ContactEquation,t.ContactEquation.prototype.reset=function(){this.invInertiaTimesRxnNeedsUpdate=!0};var ne=new t.Vec3,oe=new t.Vec3,ae=new t.Vec3;t.ContactEquation.prototype.computeB=function(t){var e=this.a,i=this.b,n=this.bi,o=this.bj,a=this.ri,s=this.rj,r=this.rixn,h=this.rjxn,c=ae,l=n.velocity,u=n.angularVelocity?n.angularVelocity:c,p=n.force,d=n.tau?n.tau:c,v=o.velocity,y=o.angularVelocity?o.angularVelocity:c,f=o.force,m=o.tau?o.tau:c,w=this.penetrationVec,b=n.invMass,x=o.invMass,g=this.invIi,V=this.invIj;n.invInertia?g.setTrace(n.invInertia):g.identity(),o.invInertia?V.setTrace(o.invInertia):V.identity();var z=this.ni;a.cross(z,r),s.cross(z,h);var w=this.penetrationVec;w.set(0,0,0),w.vadd(o.position,w),w.vadd(s,w),w.vsub(n.position,w),w.vsub(a,w);var E=z.dot(w),S=ne,N=oe;g.vmult(d,S),V.vmult(m,N);var M=this.restitution+1,P=M*v.dot(z)-M*l.dot(z)+y.dot(h)-u.dot(r),q=f.dot(z)*x-p.dot(z)*b+h.dot(N)-r.dot(S),j=-E*e-P*i-t*q;return j},new t.Vec3,new t.Vec3,t.ContactEquation.prototype.computeC=function(){var t=this.bi,e=this.bj,i=this.rixn,n=this.rjxn,o=t.invMass,a=e.invMass,s=o+a+this.eps,r=this.invIi,h=this.invIj;return r.vmult(i,this.biInvInertiaTimesRixn),h.vmult(n,this.bjInvInertiaTimesRjxn),s+=this.biInvInertiaTimesRixn.dot(i),s+=this.bjInvInertiaTimesRjxn.dot(n)};var se=new t.Vec3;t.ContactEquation.prototype.computeGWlambda=function(){var t=this.bi,e=this.bj,i=se,n=0;return e.vlambda.vsub(t.vlambda,i),n+=i.dot(this.ni),t.wlambda&&(n-=t.wlambda.dot(this.rixn)),e.wlambda&&(n+=e.wlambda.dot(this.rjxn)),n};var re=new t.Vec3,he=new t.Vec3;t.ContactEquation.prototype.addToWlambda=function(t){var e=this.bi,i=this.bj,n=(this.rixn,this.rjxn,e.invMass),o=i.invMass,a=this.ni,s=re,r=he;a.mult(n*t,r),e.vlambda.vsub(r,e.vlambda),a.mult(o*t,r),i.vlambda.vadd(r,i.vlambda),void 0!==e.wlambda&&(this.biInvInertiaTimesRixn.mult(t,s),e.wlambda.vsub(s,e.wlambda)),void 0!==i.wlambda&&(this.bjInvInertiaTimesRjxn.mult(t,s),i.wlambda.vadd(s,i.wlambda))},t.FrictionEquation=function(e,i,n){t.Equation.call(this,e,i,-n,n),this.ri=new t.Vec3,this.rj=new t.Vec3,this.t=new t.Vec3,this.rixt=new t.Vec3,this.rjxt=new t.Vec3,this.wixri=new t.Vec3,this.wjxrj=new t.Vec3,this.invIi=new t.Mat3,this.invIj=new t.Mat3,this.relVel=new t.Vec3,this.relForce=new t.Vec3,this.biInvInertiaTimesRixt=new t.Vec3,this.bjInvInertiaTimesRjxt=new t.Vec3},t.FrictionEquation.prototype=new t.Equation,t.FrictionEquation.prototype.constructor=t.FrictionEquation;var ce=new t.Vec3,le=new t.Vec3,ue=new t.Vec3;t.FrictionEquation.prototype.computeB=function(t){var e=this.a,i=this.b,n=this.bi,o=this.bj,a=this.ri,s=this.rj,r=this.rixt,h=this.rjxt,c=this.wixri,l=this.wjxrj,u=ue,p=n.velocity,d=n.angularVelocity?n.angularVelocity:u,v=n.force,y=n.tau?n.tau:u,f=o.velocity,m=o.angularVelocity?o.angularVelocity:u,w=o.force,b=o.tau?o.tau:u,x=(this.relVel,this.relForce,n.invMass),g=o.invMass,V=this.invIi,z=this.invIj,E=this.t,S=ce,N=le;n.invInertia&&V.setTrace(n.invInertia),o.invInertia&&z.setTrace(o.invInertia),a.cross(E,r),s.cross(E,h),d.cross(a,c),m.cross(s,l),V.vmult(y,S),z.vmult(b,N);var M=0,P=f.dot(E)-p.dot(E)+l.dot(E)-c.dot(E),q=w.dot(E)*g-v.dot(E)*x+h.dot(N)-r.dot(S),j=-M*e-P*i-t*q;return j},t.FrictionEquation.prototype.computeC=function(){var t=this.bi,e=this.bj,i=this.rixt,n=this.rjxt,o=t.invMass,a=e.invMass,s=o+a+this.eps,r=this.invIi,h=this.invIj;return r.vmult(i,this.biInvInertiaTimesRixt),h.vmult(n,this.bjInvInertiaTimesRjxt),s+=this.biInvInertiaTimesRixt.dot(i),s+=this.bjInvInertiaTimesRjxt.dot(n)};var pe=new t.Vec3;t.FrictionEquation.prototype.computeGWlambda=function(){var t=this.bi,e=this.bj,i=0,n=pe;return e.vlambda.vsub(t.vlambda,n),i+=n.dot(this.t),t.wlambda&&(i-=t.wlambda.dot(this.rixt)),e.wlambda&&(i+=e.wlambda.dot(this.rjxt)),i};var de=new t.Vec3;t.FrictionEquation.prototype.addToWlambda=function(t){var e=this.bi,i=this.bj,n=(this.rixt,this.rjxt,e.invMass),o=i.invMass,a=this.t,s=de,r=e.wlambda,h=i.wlambda;a.mult(n*t,s),e.vlambda.vsub(s,e.vlambda),a.mult(o*t,s),i.vlambda.vadd(s,i.vlambda),r&&(this.biInvInertiaTimesRixt.mult(t,s),r.vsub(s,r)),h&&(this.bjInvInertiaTimesRjxt.mult(t,s),h.vadd(s,h))},t.RotationalEquation=function(e,i){t.Equation.call(this,e,i,-1e6,1e6),this.ni=new t.Vec3,this.nj=new t.Vec3,this.nixnj=new t.Vec3,this.njxni=new t.Vec3,this.invIi=new t.Mat3,this.invIj=new t.Mat3,this.relVel=new t.Vec3,this.relForce=new t.Vec3},t.RotationalEquation.prototype=new t.Equation,t.RotationalEquation.prototype.constructor=t.RotationalEquation,t.RotationalEquation.prototype.computeB=function(e){var i=this.a,n=this.b,o=this.bi,a=this.bj,s=this.ni,r=this.nj,h=this.nixnj,c=this.njxni;o.velocity;var l=o.angularVelocity?o.angularVelocity:new t.Vec3;o.force,o.tau?o.tau:new t.Vec3,a.velocity;var u=a.angularVelocity?a.angularVelocity:new t.Vec3;a.force,a.tau?a.tau:new t.Vec3,o.invMass,a.invMass;var p=this.invIi,d=this.invIj;o.invInertia?p.setTrace(o.invInertia):p.identity(),a.invInertia?d.setTrace(a.invInertia):d.identity(),s.cross(r,h),r.cross(s,c);var v=-s.dot(r),y=c.dot(l)+h.dot(u),f=0,m=-v*i-y*n-e*f;return m},t.RotationalEquation.prototype.computeC=function(){var t=this.bi,e=this.bj,i=this.nixnj,n=this.njxni;t.invMass,e.invMass;var o=this.eps,a=this.invIi,s=this.invIj;return t.invInertia?a.setTrace(t.invInertia):a.identity(),e.invInertia?s.setTrace(e.invInertia):s.identity(),o+=a.vmult(n).dot(n),o+=s.vmult(i).dot(i)};var se=new t.Vec3;t.RotationalEquation.prototype.computeGWlambda=function(){var t=this.bi,e=this.bj,i=0;return t.wlambda&&(i+=t.wlambda.dot(this.njxni)),e.wlambda&&(i+=e.wlambda.dot(this.nixnj)),i},t.RotationalEquation.prototype.addToWlambda=function(t){var e=this.bi,i=this.bj,n=this.nixnj;if(this.njxni,e.invMass,i.invMass,e.wlambda){var o=this.invIi;e.wlambda.vsub(o.vmult(n).mult(t),e.wlambda)}if(i.wlambda){var o=this.invIj;i.wlambda.vadd(o.vmult(n).mult(t),i.wlambda)}},t.Constraint=function(t,e){this.equations=[],this.bodyA=t,this.bodyB=e},t.Constraint.prototype.update=function(){throw Error("method update() not implmemented in this Constraint subclass!")},t.DistanceConstraint=function(e,i,n,o){t.Constraint.call(this,e,i),o===void 0&&(o=1e6);var a=this.equations=[new t.ContactEquation(e,i)],s=a[0];s.minForce=-o,s.maxForce=o,this.update=function(){i.position.vsub(e.position,s.ni),s.ni.normalize(),s.ni.mult(.5*n,s.ri),s.ni.mult(.5*-n,s.rj)}},t.DistanceConstraint.prototype=new t.Constraint,t.RotationalMotorEquation=function(e,i,n){n=n||1e6,t.Equation.call(this,e,i,-n,n),this.axisA=new t.Vec3,this.axisB=new t.Vec3,this.invIi=new t.Mat3,this.invIj=new t.Mat3,this.targetVelocity=0},t.RotationalMotorEquation.prototype=new t.Equation,t.RotationalMotorEquation.prototype.constructor=t.RotationalMotorEquation,t.RotationalMotorEquation.prototype.computeB=function(e){var i=this.a,n=this.b,o=this.bi,a=this.bj,s=this.axisA,r=this.axisB;
3 | o.velocity;var h=o.angularVelocity?o.angularVelocity:new t.Vec3;o.force,o.tau?o.tau:new t.Vec3,a.velocity;var c=a.angularVelocity?a.angularVelocity:new t.Vec3;a.force,a.tau?a.tau:new t.Vec3,o.invMass,a.invMass;var l=this.invIi,u=this.invIj;o.invInertia?l.setTrace(o.invInertia):l.identity(),a.invInertia?u.setTrace(a.invInertia):u.identity();var p=0,d=s.dot(h)+r.dot(c)+this.targetVelocity,v=0,y=-p*i-d*n-e*v;return y},t.RotationalMotorEquation.prototype.computeC=function(){var t=this.bi,e=this.bj,i=this.axisA,n=this.axisB;t.invMass,e.invMass;var o=this.eps,a=this.invIi,s=this.invIj;return t.invInertia?a.setTrace(t.invInertia):a.identity(),e.invInertia?s.setTrace(e.invInertia):s.identity(),o+=a.vmult(i).dot(n),o+=s.vmult(n).dot(n)};var se=new t.Vec3;t.RotationalMotorEquation.prototype.computeGWlambda=function(){var t=this.bi,e=this.bj,i=this.axisA,n=this.axisB,o=0;return t.wlambda&&(o+=t.wlambda.dot(i)),e.wlambda&&(o+=e.wlambda.dot(n)),o},t.RotationalMotorEquation.prototype.addToWlambda=function(t){var e=this.bi,i=this.bj,n=this.axisA,o=this.axisB;if(e.invMass,i.invMass,e.wlambda){var a=this.invIi;e.wlambda.vsub(a.vmult(n).mult(t),e.wlambda)}if(i.wlambda){var a=this.invIj;i.wlambda.vadd(a.vmult(o).mult(t),i.wlambda)}},t.HingeConstraint=function(e,i,n,o,a,s,r){t.Constraint.call(this,e,o),r=r||1e6;var h=this,c=this.equations=[new t.RotationalEquation(e,o),new t.RotationalEquation(e,o),new t.ContactEquation(e,o),new t.ContactEquation(e,o),new t.ContactEquation(e,o)];this.getRotationalEquation1=function(){return c[0]},this.getRotationalEquation2=function(){return c[1]},this.getPointToPointEquation1=function(){return c[2]},this.getPointToPointEquation2=function(){return c[3]},this.getPointToPointEquation3=function(){return c[4]};var l,u=this.getRotationalEquation1(),p=this.getRotationalEquation2(),d=this.getPointToPointEquation1(),v=this.getPointToPointEquation2(),y=this.getPointToPointEquation3();v.minForce=y.minForce=d.minForce=-r,v.maxForce=y.maxForce=d.maxForce=r;var f=i.unit(),m=a.unit(),w=new t.Vec3,b=new t.Vec3,x=new t.Vec3;n.cross(f,w),n.cross(w,b),s.cross(m,x),w.normalize(),x.normalize();var g=!1;this.motorTargetVelocity=0,this.motorMinForce=-r,this.motorMaxForce=r,this.enableMotor=function(){g||(l=new t.RotationalMotorEquation(e,o,r),c.push(l),g=!0)},this.disableMotor=function(){g&&(g=!1,l=null,c.pop())},this.update=function(){d.ni.set(1,0,0),v.ni.set(0,1,0),y.ni.set(0,0,1),e.quaternion.vmult(i,d.ri),o.quaternion.vmult(a,d.rj),d.ri.copy(v.ri),d.rj.copy(v.rj),d.ri.copy(y.ri),d.rj.copy(y.rj),e.quaternion.vmult(w,u.ni),o.quaternion.vmult(s,u.nj),e.quaternion.vmult(b,p.ni),o.quaternion.vmult(s,p.nj),g&&(e.quaternion.vmult(n,l.axisA),o.quaternion.vmult(s,l.axisB),l.targetVelocity=h.motorTargetVelocity,l.maxForce=h.motorMaxForce,l.minForce=h.motorMinForce)}},t.HingeConstraint.prototype=new t.Constraint,t.PointToPointConstraint=function(e,i,n,o,a){t.Constraint.call(this,e,n);var s=this.equations=[new t.ContactEquation(e,n),new t.ContactEquation(e,n),new t.ContactEquation(e,n)],r=s[0],h=s[1],c=s[2];h.minForce=c.minForce=r.minForce=-a,h.maxForce=c.maxForce=r.maxForce=a,this.update=function(){n.position.vsub(e.position,r.ni),r.ni.normalize(),e.quaternion.vmult(i,r.ri),n.quaternion.vmult(o,r.rj),r.ni.tangents(h.ni,c.ni),r.ri.copy(h.ri),r.rj.copy(h.rj),r.ri.copy(c.ri),r.rj.copy(c.rj)}},t.PointToPointConstraint.prototype=new t.Constraint,"undefined"!=typeof module?module.exports=t:this.CANNON=t}).apply(this);
--------------------------------------------------------------------------------