├── .editorconfig
├── .gitattributes
├── .github
├── flame-ball.jpeg
└── preview.png
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── dist_github
├── app.js
├── images
│ └── circle-particle.png
├── js
│ ├── OrbitControl.js
│ └── stats.d.ts
└── shader
│ ├── fragmentFlameShader.glsl
│ ├── fragmentParticleShader.glsl
│ ├── vertexFlameShader.glsl
│ └── vertexParticleShader.glsl
├── fire_simulation_report.pdf
├── index.html
├── package.json
├── src
├── animation
│ ├── Interpolation.ts
│ ├── explosionController.ts
│ └── flameAnimation.ts
├── assetsManager.ts
├── constants.ts
├── controller.ts
├── images
│ ├── circle-particle.png
│ └── spark.png
├── js
│ ├── OrbitControl.js
│ └── stats.d.ts
├── main.ts
├── object
│ ├── flameSphere.ts
│ └── flareParticle.ts
├── renderer.ts
├── shader
│ ├── fragmentFlameShader.glsl
│ ├── fragmentParticleShader.glsl
│ ├── vertexFlameShader.glsl
│ └── vertexParticleShader.glsl
└── utils.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = space
3 | indent_size = 2
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Exclude Vendored
2 | dist_github/* linguist-vendored
3 | src/js/OrbitControl.js linguist-vendored
4 |
--------------------------------------------------------------------------------
/.github/flame-ball.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/.github/flame-ball.jpeg
--------------------------------------------------------------------------------
/.github/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/.github/preview.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | .DS_Store
6 |
7 | # Visual Studio Project
8 | .vscode
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Dependency directories
17 | node_modules
18 |
19 | # Optional npm cache directory
20 | .npm
21 |
22 | # TypeScript Ignore
23 | .baseDir.ts
24 | .tscache/
25 | tscommand-*.tmp.txt
26 |
27 | # Build Directory
28 | src/*.js
29 | !src/js/
30 | dist/
31 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | grunt.initConfig({
4 | webpack: {
5 | default: {
6 | entry: "./dist/main.js",
7 | output: {
8 | path: "dist/",
9 | filename: "app.js",
10 | }
11 | }
12 | },
13 | copy: {
14 | main: {
15 | files: [{
16 | expand: true,
17 | cwd: 'src/',
18 | src: ['js/**/*','shader/**/*', 'images/**/*'],
19 | dest: 'dist/'
20 | }],
21 | },
22 | },
23 | ts: {
24 | default: {
25 | src: ["src/**/*.ts", "!node_modules/**"],
26 | outDir: "dist/",
27 | options: {
28 | sourceMap: false
29 | }
30 | }
31 | },
32 | watch: {
33 | scripts: {
34 | files: ['src/**/*'],
35 | tasks: ['copy', 'ts', 'webpack'],
36 | options: {
37 | spawn: false,
38 | },
39 | },
40 | }
41 | });
42 |
43 | grunt.loadNpmTasks('grunt-contrib-copy');
44 | grunt.loadNpmTasks('grunt-contrib-watch');
45 | grunt.loadNpmTasks('grunt-webpack');
46 | grunt.loadNpmTasks('grunt-ts');
47 |
48 | grunt.registerTask('default', ['copy', 'ts', 'webpack']);
49 | };
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Kosate Limpongsa
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Fire Simulation
2 | ===
3 |
4 |
5 |
6 | *This is my original project for propose to 2110594 Computer Graphics and Physics Simulation class
7 | in Chulalongkorn University.*
8 |
9 | Fire simulation base on WebGL using THREE.JS library. Using volumetric fire technique for this implementation. Fully customizing on fire color and particle system in real-time.
10 | The simulator are run smoothly with 60 fps frame rate,
11 | optmized by object pooling method.
12 |
13 | ## Demo
14 |
15 | :point_right: Available demo : [https://neungkl.github.io/fire-simulation/](https://neungkl.github.io/fire-simulation/)
16 |
17 | ## Report
18 |
19 | :page_facing_up: You can read full PDF report in [fire_simulation_report.pdf](fire_simulation_report.pdf)
20 |
21 |
22 | ## Works
23 |
24 | - I starting with building a volumetric fire called flame ball, with customize vertex shader and fragment shader,
25 | the result look similar to below figure.
26 |
27 |
28 |
29 | - The flame ball part I using [Perlin Noise](https://en.wikipedia.org/wiki/Perlin_noise) algorithm for making the
30 | randomize texture of flame ball and apply the color depends on the deep
31 | of surface.
32 | - Combine each flame ball to a large fire using [Interpolation](https://en.wikipedia.org/wiki/Interpolation) for animation
33 | each frame of flame ball.
34 | - Add particle system for spark system.
35 | - Fully customization for fire color and spark color.
36 | - Renderer time scale included.
37 | - Optimized by using object pooling approach.
38 |
39 | ## Usage
40 |
41 | ### Run the simulator
42 |
43 | 1. Enter following command.
44 |
45 | ```sh
46 | $ npm install -g typescript grunt
47 | $ npm install
48 | ```
49 | 2. Run build script
50 |
51 | ```
52 | $ grunt
53 | ```
54 | 3. Change code in `index.html`
55 |
56 | ```html
57 |
58 |
59 |
60 |
61 |
62 | ```
63 | 4. Open file `index.html`
64 |
65 | ### Developement
66 |
67 | ```
68 | $ grunt watch
69 | ```
70 |
71 | ## Thanks
72 |
73 | **Reference**
74 |
75 | - Palin Noise [https://en.wikipedia.org/wiki/Perlin_noise](https://en.wikipedia.org/wiki/Perlin_noise)
76 | - Vertex Displacement with a noise function using GLSL and three.js [https://www.clicktorelease.com/blog/vertex-displacement-noise-3d-webgl-glsl-three-js](https://www.clicktorelease.com/blog/vertex-displacement-noise-3d-webgl-glsl-three-js)
77 | - Easing Equation by Robert Penner [http://gizma.com/easing/](http://gizma.com/easing/)
78 | - Object Pool Pattern [https://en.wikipedia.org/wiki/Object_pool_pattern](https://en.wikipedia.org/wiki/Object_pool_pattern)
79 |
80 | **Tools**
81 |
82 | - THREE.js — [https://threejs.org/](https://threejs.org/)
83 | - Dat GUI — [https://github.com/dataarts/dat.gui](https://github.com/dataarts/dat.gui)
84 | - Stats.js — [https://github.com/mrdoob/stats.js/](https://github.com/mrdoob/stats.js/)
85 | - TypeScript — [https://www.typescriptlang.org/](https://www.typescriptlang.org/)
86 |
87 | ## License
88 |
89 | [MIT](LICENSE) © Kosate Limpongsa
90 |
--------------------------------------------------------------------------------
/dist_github/app.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 | "use strict";
48 | var renderer_1 = __webpack_require__(1);
49 | var controller_1 = __webpack_require__(3);
50 | var explosionController_1 = __webpack_require__(4);
51 | window.onload = function () {
52 | var time = Date.now();
53 | var timeScale;
54 | var stats = new Stats();
55 | stats.showPanel(0);
56 | document.getElementById("stats").appendChild(stats.domElement);
57 | controller_1.Controller.init();
58 | renderer_1.Renderer.init();
59 | explosionController_1.ExplosionController.init();
60 | timeScale = controller_1.Controller.getParams().TimeScale;
61 | var onRequestAnimationFrame = function () {
62 | requestAnimationFrame(onRequestAnimationFrame);
63 | stats.begin();
64 | renderer_1.Renderer.animate();
65 | stats.end();
66 | };
67 | var deltaTimeMaximum = 1000 / 65;
68 | renderer_1.Renderer.setUpdateFunc(function () {
69 | var timeDiff = (Date.now() - time);
70 | explosionController_1.ExplosionController.update(timeDiff > deltaTimeMaximum ? deltaTimeMaximum : timeDiff);
71 | time = Date.now();
72 | });
73 | controller_1.Controller.setRestartFunc(function () {
74 | explosionController_1.ExplosionController.reset();
75 | });
76 | requestAnimationFrame(onRequestAnimationFrame);
77 | window.addEventListener('resize', function () { renderer_1.Renderer.onWindowResize(); }, false);
78 | };
79 |
80 |
81 | /***/ },
82 | /* 1 */
83 | /***/ function(module, exports, __webpack_require__) {
84 |
85 | "use strict";
86 | __webpack_require__(2);
87 | var controller_1 = __webpack_require__(3);
88 | var Renderer = (function () {
89 | function Renderer() {
90 | }
91 | Renderer.init = function () {
92 | var _this = this;
93 | this.scene = new THREE.Scene();
94 | this.scene.background = new THREE.Color(0xf8f8f8);
95 | this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
96 | this.renderer = new THREE.WebGLRenderer({ antialias: true });
97 | this.renderer.setSize(window.innerWidth, window.innerHeight);
98 | document.body.appendChild(this.renderer.domElement);
99 | this.gridHelper = new THREE.GridHelper(100, 40, 0xdddddd, 0xdddddd);
100 | this.scene.add(this.gridHelper);
101 | this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
102 | this.controls.enableDamping = true;
103 | this.controls.dampingFactor = 0.25;
104 | this.controls.enableZoom = true;
105 | this.camera.position.z = 75;
106 | this.camera.position.y = 75;
107 | this.camera.position.x = 75;
108 | controller_1.Controller.attachEvent(controller_1.Controller.INVERTED_BACKGROUND, function (value) {
109 | if (value) {
110 | _this.scene.background = new THREE.Color(0x111111);
111 | _this.scene.remove(_this.gridHelper);
112 | _this.gridHelper = new THREE.GridHelper(100, 40, 0x444444, 0x444444);
113 | _this.scene.add(_this.gridHelper);
114 | }
115 | else {
116 | _this.scene.background = new THREE.Color(0xf8f8f8);
117 | _this.scene.remove(_this.gridHelper);
118 | _this.gridHelper = new THREE.GridHelper(100, 40, 0xdddddd, 0xdddddd);
119 | _this.scene.add(_this.gridHelper);
120 | }
121 | });
122 | controller_1.Controller.attachEvent(controller_1.Controller.SHOW_GRID, function (value) {
123 | _this.gridHelper.visible = value;
124 | });
125 | };
126 | Renderer.animate = function () {
127 | this.controls.update();
128 | if (this.updateCallback != null) {
129 | this.updateCallback();
130 | }
131 | this.renderer.render(this.scene, this.camera);
132 | };
133 | Renderer.addToScene = function (obj) {
134 | this.scene.add(obj);
135 | };
136 | Renderer.removeFromScene = function (obj) {
137 | this.scene.remove(obj);
138 | };
139 | Renderer.setUpdateFunc = function (func) {
140 | this.updateCallback = func;
141 | };
142 | Renderer.onWindowResize = function () {
143 | this.camera.aspect = window.innerWidth / window.innerHeight;
144 | this.camera.updateProjectionMatrix();
145 | this.renderer.setSize(window.innerWidth, window.innerHeight);
146 | };
147 | Renderer.updateCallback = null;
148 | return Renderer;
149 | }());
150 | exports.Renderer = Renderer;
151 |
152 |
153 | /***/ },
154 | /* 2 */
155 | /***/ function(module, exports) {
156 |
157 | /**
158 | * @author qiao / https://github.com/qiao
159 | * @author mrdoob / http://mrdoob.com
160 | * @author alteredq / http://alteredqualia.com/
161 | * @author WestLangley / http://github.com/WestLangley
162 | * @author erich666 / http://erichaines.com
163 | */
164 |
165 | // This set of controls performs orbiting, dollying (zooming), and panning.
166 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
167 | //
168 | // Orbit - left mouse / touch: one finger move
169 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
170 | // Pan - right mouse, or arrow keys / touch: three finter swipe
171 |
172 | THREE.OrbitControls = function ( object, domElement ) {
173 |
174 | this.object = object;
175 |
176 | this.domElement = ( domElement !== undefined ) ? domElement : document;
177 |
178 | // Set to false to disable this control
179 | this.enabled = true;
180 |
181 | // "target" sets the location of focus, where the object orbits around
182 | this.target = new THREE.Vector3();
183 |
184 | // How far you can dolly in and out ( PerspectiveCamera only )
185 | this.minDistance = 0;
186 | this.maxDistance = Infinity;
187 |
188 | // How far you can zoom in and out ( OrthographicCamera only )
189 | this.minZoom = 0;
190 | this.maxZoom = Infinity;
191 |
192 | // How far you can orbit vertically, upper and lower limits.
193 | // Range is 0 to Math.PI radians.
194 | this.minPolarAngle = 0; // radians
195 | this.maxPolarAngle = Math.PI; // radians
196 |
197 | // How far you can orbit horizontally, upper and lower limits.
198 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
199 | this.minAzimuthAngle = - Infinity; // radians
200 | this.maxAzimuthAngle = Infinity; // radians
201 |
202 | // Set to true to enable damping (inertia)
203 | // If damping is enabled, you must call controls.update() in your animation loop
204 | this.enableDamping = false;
205 | this.dampingFactor = 0.25;
206 |
207 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
208 | // Set to false to disable zooming
209 | this.enableZoom = true;
210 | this.zoomSpeed = 1.0;
211 |
212 | // Set to false to disable rotating
213 | this.enableRotate = true;
214 | this.rotateSpeed = 1.0;
215 |
216 | // Set to false to disable panning
217 | this.enablePan = true;
218 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
219 |
220 | // Set to true to automatically rotate around the target
221 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
222 | this.autoRotate = false;
223 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
224 |
225 | // Set to false to disable use of the keys
226 | this.enableKeys = true;
227 |
228 | // The four arrow keys
229 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
230 |
231 | // Mouse buttons
232 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
233 |
234 | // for reset
235 | this.target0 = this.target.clone();
236 | this.position0 = this.object.position.clone();
237 | this.zoom0 = this.object.zoom;
238 |
239 | //
240 | // public methods
241 | //
242 |
243 | this.getPolarAngle = function () {
244 |
245 | return spherical.phi;
246 |
247 | };
248 |
249 | this.getAzimuthalAngle = function () {
250 |
251 | return spherical.theta;
252 |
253 | };
254 |
255 | this.reset = function () {
256 |
257 | scope.target.copy( scope.target0 );
258 | scope.object.position.copy( scope.position0 );
259 | scope.object.zoom = scope.zoom0;
260 |
261 | scope.object.updateProjectionMatrix();
262 | scope.dispatchEvent( changeEvent );
263 |
264 | scope.update();
265 |
266 | state = STATE.NONE;
267 |
268 | };
269 |
270 | // this method is exposed, but perhaps it would be better if we can make it private...
271 | this.update = function() {
272 |
273 | var offset = new THREE.Vector3();
274 |
275 | // so camera.up is the orbit axis
276 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
277 | var quatInverse = quat.clone().inverse();
278 |
279 | var lastPosition = new THREE.Vector3();
280 | var lastQuaternion = new THREE.Quaternion();
281 |
282 | return function update () {
283 |
284 | var position = scope.object.position;
285 |
286 | offset.copy( position ).sub( scope.target );
287 |
288 | // rotate offset to "y-axis-is-up" space
289 | offset.applyQuaternion( quat );
290 |
291 | // angle from z-axis around y-axis
292 | spherical.setFromVector3( offset );
293 |
294 | if ( scope.autoRotate && state === STATE.NONE ) {
295 |
296 | rotateLeft( getAutoRotationAngle() );
297 |
298 | }
299 |
300 | spherical.theta += sphericalDelta.theta;
301 | spherical.phi += sphericalDelta.phi;
302 |
303 | // restrict theta to be between desired limits
304 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
305 |
306 | // restrict phi to be between desired limits
307 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
308 |
309 | spherical.makeSafe();
310 |
311 |
312 | spherical.radius *= scale;
313 |
314 | // restrict radius to be between desired limits
315 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
316 |
317 | // move target to panned location
318 | scope.target.add( panOffset );
319 |
320 | offset.setFromSpherical( spherical );
321 |
322 | // rotate offset back to "camera-up-vector-is-up" space
323 | offset.applyQuaternion( quatInverse );
324 |
325 | position.copy( scope.target ).add( offset );
326 |
327 | scope.object.lookAt( scope.target );
328 |
329 | if ( scope.enableDamping === true ) {
330 |
331 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
332 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
333 |
334 | } else {
335 |
336 | sphericalDelta.set( 0, 0, 0 );
337 |
338 | }
339 |
340 | scale = 1;
341 | panOffset.set( 0, 0, 0 );
342 |
343 | // update condition is:
344 | // min(camera displacement, camera rotation in radians)^2 > EPS
345 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
346 |
347 | if ( zoomChanged ||
348 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
349 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
350 |
351 | scope.dispatchEvent( changeEvent );
352 |
353 | lastPosition.copy( scope.object.position );
354 | lastQuaternion.copy( scope.object.quaternion );
355 | zoomChanged = false;
356 |
357 | return true;
358 |
359 | }
360 |
361 | return false;
362 |
363 | };
364 |
365 | }();
366 |
367 | this.dispose = function() {
368 |
369 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
370 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
371 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
372 |
373 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
374 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
375 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
376 |
377 | document.removeEventListener( 'mousemove', onMouseMove, false );
378 | document.removeEventListener( 'mouseup', onMouseUp, false );
379 |
380 | window.removeEventListener( 'keydown', onKeyDown, false );
381 |
382 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
383 |
384 | };
385 |
386 | //
387 | // internals
388 | //
389 |
390 | var scope = this;
391 |
392 | var changeEvent = { type: 'change' };
393 | var startEvent = { type: 'start' };
394 | var endEvent = { type: 'end' };
395 |
396 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
397 |
398 | var state = STATE.NONE;
399 |
400 | var EPS = 0.000001;
401 |
402 | // current position in spherical coordinates
403 | var spherical = new THREE.Spherical();
404 | var sphericalDelta = new THREE.Spherical();
405 |
406 | var scale = 1;
407 | var panOffset = new THREE.Vector3();
408 | var zoomChanged = false;
409 |
410 | var rotateStart = new THREE.Vector2();
411 | var rotateEnd = new THREE.Vector2();
412 | var rotateDelta = new THREE.Vector2();
413 |
414 | var panStart = new THREE.Vector2();
415 | var panEnd = new THREE.Vector2();
416 | var panDelta = new THREE.Vector2();
417 |
418 | var dollyStart = new THREE.Vector2();
419 | var dollyEnd = new THREE.Vector2();
420 | var dollyDelta = new THREE.Vector2();
421 |
422 | function getAutoRotationAngle() {
423 |
424 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
425 |
426 | }
427 |
428 | function getZoomScale() {
429 |
430 | return Math.pow( 0.95, scope.zoomSpeed );
431 |
432 | }
433 |
434 | function rotateLeft( angle ) {
435 |
436 | sphericalDelta.theta -= angle;
437 |
438 | }
439 |
440 | function rotateUp( angle ) {
441 |
442 | sphericalDelta.phi -= angle;
443 |
444 | }
445 |
446 | var panLeft = function() {
447 |
448 | var v = new THREE.Vector3();
449 |
450 | return function panLeft( distance, objectMatrix ) {
451 |
452 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
453 | v.multiplyScalar( - distance );
454 |
455 | panOffset.add( v );
456 |
457 | };
458 |
459 | }();
460 |
461 | var panUp = function() {
462 |
463 | var v = new THREE.Vector3();
464 |
465 | return function panUp( distance, objectMatrix ) {
466 |
467 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
468 | v.multiplyScalar( distance );
469 |
470 | panOffset.add( v );
471 |
472 | };
473 |
474 | }();
475 |
476 | // deltaX and deltaY are in pixels; right and down are positive
477 | var pan = function() {
478 |
479 | var offset = new THREE.Vector3();
480 |
481 | return function pan ( deltaX, deltaY ) {
482 |
483 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
484 |
485 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
486 |
487 | // perspective
488 | var position = scope.object.position;
489 | offset.copy( position ).sub( scope.target );
490 | var targetDistance = offset.length();
491 |
492 | // half of the fov is center to top of screen
493 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
494 |
495 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
496 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
497 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
498 |
499 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
500 |
501 | // orthographic
502 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
503 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
504 |
505 | } else {
506 |
507 | // camera neither orthographic nor perspective
508 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
509 | scope.enablePan = false;
510 |
511 | }
512 |
513 | };
514 |
515 | }();
516 |
517 | function dollyIn( dollyScale ) {
518 |
519 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
520 |
521 | scale /= dollyScale;
522 |
523 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
524 |
525 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
526 | scope.object.updateProjectionMatrix();
527 | zoomChanged = true;
528 |
529 | } else {
530 |
531 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
532 | scope.enableZoom = false;
533 |
534 | }
535 |
536 | }
537 |
538 | function dollyOut( dollyScale ) {
539 |
540 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
541 |
542 | scale *= dollyScale;
543 |
544 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
545 |
546 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
547 | scope.object.updateProjectionMatrix();
548 | zoomChanged = true;
549 |
550 | } else {
551 |
552 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
553 | scope.enableZoom = false;
554 |
555 | }
556 |
557 | }
558 |
559 | //
560 | // event callbacks - update the object state
561 | //
562 |
563 | function handleMouseDownRotate( event ) {
564 |
565 | //console.log( 'handleMouseDownRotate' );
566 |
567 | rotateStart.set( event.clientX, event.clientY );
568 |
569 | }
570 |
571 | function handleMouseDownDolly( event ) {
572 |
573 | //console.log( 'handleMouseDownDolly' );
574 |
575 | dollyStart.set( event.clientX, event.clientY );
576 |
577 | }
578 |
579 | function handleMouseDownPan( event ) {
580 |
581 | //console.log( 'handleMouseDownPan' );
582 |
583 | panStart.set( event.clientX, event.clientY );
584 |
585 | }
586 |
587 | function handleMouseMoveRotate( event ) {
588 |
589 | //console.log( 'handleMouseMoveRotate' );
590 |
591 | rotateEnd.set( event.clientX, event.clientY );
592 | rotateDelta.subVectors( rotateEnd, rotateStart );
593 |
594 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
595 |
596 | // rotating across whole screen goes 360 degrees around
597 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
598 |
599 | // rotating up and down along whole screen attempts to go 360, but limited to 180
600 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
601 |
602 | rotateStart.copy( rotateEnd );
603 |
604 | scope.update();
605 |
606 | }
607 |
608 | function handleMouseMoveDolly( event ) {
609 |
610 | //console.log( 'handleMouseMoveDolly' );
611 |
612 | dollyEnd.set( event.clientX, event.clientY );
613 |
614 | dollyDelta.subVectors( dollyEnd, dollyStart );
615 |
616 | if ( dollyDelta.y > 0 ) {
617 |
618 | dollyIn( getZoomScale() );
619 |
620 | } else if ( dollyDelta.y < 0 ) {
621 |
622 | dollyOut( getZoomScale() );
623 |
624 | }
625 |
626 | dollyStart.copy( dollyEnd );
627 |
628 | scope.update();
629 |
630 | }
631 |
632 | function handleMouseMovePan( event ) {
633 |
634 | //console.log( 'handleMouseMovePan' );
635 |
636 | panEnd.set( event.clientX, event.clientY );
637 |
638 | panDelta.subVectors( panEnd, panStart );
639 |
640 | pan( panDelta.x, panDelta.y );
641 |
642 | panStart.copy( panEnd );
643 |
644 | scope.update();
645 |
646 | }
647 |
648 | function handleMouseUp( event ) {
649 |
650 | //console.log( 'handleMouseUp' );
651 |
652 | }
653 |
654 | function handleMouseWheel( event ) {
655 |
656 | //console.log( 'handleMouseWheel' );
657 |
658 | if ( event.deltaY < 0 ) {
659 |
660 | dollyOut( getZoomScale() );
661 |
662 | } else if ( event.deltaY > 0 ) {
663 |
664 | dollyIn( getZoomScale() );
665 |
666 | }
667 |
668 | scope.update();
669 |
670 | }
671 |
672 | function handleKeyDown( event ) {
673 |
674 | //console.log( 'handleKeyDown' );
675 |
676 | switch ( event.keyCode ) {
677 |
678 | case scope.keys.UP:
679 | pan( 0, scope.keyPanSpeed );
680 | scope.update();
681 | break;
682 |
683 | case scope.keys.BOTTOM:
684 | pan( 0, - scope.keyPanSpeed );
685 | scope.update();
686 | break;
687 |
688 | case scope.keys.LEFT:
689 | pan( scope.keyPanSpeed, 0 );
690 | scope.update();
691 | break;
692 |
693 | case scope.keys.RIGHT:
694 | pan( - scope.keyPanSpeed, 0 );
695 | scope.update();
696 | break;
697 |
698 | }
699 |
700 | }
701 |
702 | function handleTouchStartRotate( event ) {
703 |
704 | //console.log( 'handleTouchStartRotate' );
705 |
706 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
707 |
708 | }
709 |
710 | function handleTouchStartDolly( event ) {
711 |
712 | //console.log( 'handleTouchStartDolly' );
713 |
714 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
715 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
716 |
717 | var distance = Math.sqrt( dx * dx + dy * dy );
718 |
719 | dollyStart.set( 0, distance );
720 |
721 | }
722 |
723 | function handleTouchStartPan( event ) {
724 |
725 | //console.log( 'handleTouchStartPan' );
726 |
727 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
728 |
729 | }
730 |
731 | function handleTouchMoveRotate( event ) {
732 |
733 | //console.log( 'handleTouchMoveRotate' );
734 |
735 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
736 | rotateDelta.subVectors( rotateEnd, rotateStart );
737 |
738 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
739 |
740 | // rotating across whole screen goes 360 degrees around
741 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
742 |
743 | // rotating up and down along whole screen attempts to go 360, but limited to 180
744 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
745 |
746 | rotateStart.copy( rotateEnd );
747 |
748 | scope.update();
749 |
750 | }
751 |
752 | function handleTouchMoveDolly( event ) {
753 |
754 | //console.log( 'handleTouchMoveDolly' );
755 |
756 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
757 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
758 |
759 | var distance = Math.sqrt( dx * dx + dy * dy );
760 |
761 | dollyEnd.set( 0, distance );
762 |
763 | dollyDelta.subVectors( dollyEnd, dollyStart );
764 |
765 | if ( dollyDelta.y > 0 ) {
766 |
767 | dollyOut( getZoomScale() );
768 |
769 | } else if ( dollyDelta.y < 0 ) {
770 |
771 | dollyIn( getZoomScale() );
772 |
773 | }
774 |
775 | dollyStart.copy( dollyEnd );
776 |
777 | scope.update();
778 |
779 | }
780 |
781 | function handleTouchMovePan( event ) {
782 |
783 | //console.log( 'handleTouchMovePan' );
784 |
785 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
786 |
787 | panDelta.subVectors( panEnd, panStart );
788 |
789 | pan( panDelta.x, panDelta.y );
790 |
791 | panStart.copy( panEnd );
792 |
793 | scope.update();
794 |
795 | }
796 |
797 | function handleTouchEnd( event ) {
798 |
799 | //console.log( 'handleTouchEnd' );
800 |
801 | }
802 |
803 | //
804 | // event handlers - FSM: listen for events and reset state
805 | //
806 |
807 | function onMouseDown( event ) {
808 |
809 | if ( scope.enabled === false ) return;
810 |
811 | event.preventDefault();
812 |
813 | if ( event.button === scope.mouseButtons.ORBIT ) {
814 |
815 | if ( scope.enableRotate === false ) return;
816 |
817 | handleMouseDownRotate( event );
818 |
819 | state = STATE.ROTATE;
820 |
821 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
822 |
823 | if ( scope.enableZoom === false ) return;
824 |
825 | handleMouseDownDolly( event );
826 |
827 | state = STATE.DOLLY;
828 |
829 | } else if ( event.button === scope.mouseButtons.PAN ) {
830 |
831 | if ( scope.enablePan === false ) return;
832 |
833 | handleMouseDownPan( event );
834 |
835 | state = STATE.PAN;
836 |
837 | }
838 |
839 | if ( state !== STATE.NONE ) {
840 |
841 | document.addEventListener( 'mousemove', onMouseMove, false );
842 | document.addEventListener( 'mouseup', onMouseUp, false );
843 |
844 | scope.dispatchEvent( startEvent );
845 |
846 | }
847 |
848 | }
849 |
850 | function onMouseMove( event ) {
851 |
852 | if ( scope.enabled === false ) return;
853 |
854 | event.preventDefault();
855 |
856 | if ( state === STATE.ROTATE ) {
857 |
858 | if ( scope.enableRotate === false ) return;
859 |
860 | handleMouseMoveRotate( event );
861 |
862 | } else if ( state === STATE.DOLLY ) {
863 |
864 | if ( scope.enableZoom === false ) return;
865 |
866 | handleMouseMoveDolly( event );
867 |
868 | } else if ( state === STATE.PAN ) {
869 |
870 | if ( scope.enablePan === false ) return;
871 |
872 | handleMouseMovePan( event );
873 |
874 | }
875 |
876 | }
877 |
878 | function onMouseUp( event ) {
879 |
880 | if ( scope.enabled === false ) return;
881 |
882 | handleMouseUp( event );
883 |
884 | document.removeEventListener( 'mousemove', onMouseMove, false );
885 | document.removeEventListener( 'mouseup', onMouseUp, false );
886 |
887 | scope.dispatchEvent( endEvent );
888 |
889 | state = STATE.NONE;
890 |
891 | }
892 |
893 | function onMouseWheel( event ) {
894 |
895 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
896 |
897 | event.preventDefault();
898 | event.stopPropagation();
899 |
900 | handleMouseWheel( event );
901 |
902 | scope.dispatchEvent( startEvent ); // not sure why these are here...
903 | scope.dispatchEvent( endEvent );
904 |
905 | }
906 |
907 | function onKeyDown( event ) {
908 |
909 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
910 |
911 | handleKeyDown( event );
912 |
913 | }
914 |
915 | function onTouchStart( event ) {
916 |
917 | if ( scope.enabled === false ) return;
918 |
919 | switch ( event.touches.length ) {
920 |
921 | case 1: // one-fingered touch: rotate
922 |
923 | if ( scope.enableRotate === false ) return;
924 |
925 | handleTouchStartRotate( event );
926 |
927 | state = STATE.TOUCH_ROTATE;
928 |
929 | break;
930 |
931 | case 2: // two-fingered touch: dolly
932 |
933 | if ( scope.enableZoom === false ) return;
934 |
935 | handleTouchStartDolly( event );
936 |
937 | state = STATE.TOUCH_DOLLY;
938 |
939 | break;
940 |
941 | case 3: // three-fingered touch: pan
942 |
943 | if ( scope.enablePan === false ) return;
944 |
945 | handleTouchStartPan( event );
946 |
947 | state = STATE.TOUCH_PAN;
948 |
949 | break;
950 |
951 | default:
952 |
953 | state = STATE.NONE;
954 |
955 | }
956 |
957 | if ( state !== STATE.NONE ) {
958 |
959 | scope.dispatchEvent( startEvent );
960 |
961 | }
962 |
963 | }
964 |
965 | function onTouchMove( event ) {
966 |
967 | if ( scope.enabled === false ) return;
968 |
969 | event.preventDefault();
970 | event.stopPropagation();
971 |
972 | switch ( event.touches.length ) {
973 |
974 | case 1: // one-fingered touch: rotate
975 |
976 | if ( scope.enableRotate === false ) return;
977 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
978 |
979 | handleTouchMoveRotate( event );
980 |
981 | break;
982 |
983 | case 2: // two-fingered touch: dolly
984 |
985 | if ( scope.enableZoom === false ) return;
986 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
987 |
988 | handleTouchMoveDolly( event );
989 |
990 | break;
991 |
992 | case 3: // three-fingered touch: pan
993 |
994 | if ( scope.enablePan === false ) return;
995 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
996 |
997 | handleTouchMovePan( event );
998 |
999 | break;
1000 |
1001 | default:
1002 |
1003 | state = STATE.NONE;
1004 |
1005 | }
1006 |
1007 | }
1008 |
1009 | function onTouchEnd( event ) {
1010 |
1011 | if ( scope.enabled === false ) return;
1012 |
1013 | handleTouchEnd( event );
1014 |
1015 | scope.dispatchEvent( endEvent );
1016 |
1017 | state = STATE.NONE;
1018 |
1019 | }
1020 |
1021 | function onContextMenu( event ) {
1022 |
1023 | event.preventDefault();
1024 |
1025 | }
1026 |
1027 | //
1028 |
1029 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
1030 |
1031 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
1032 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
1033 |
1034 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
1035 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
1036 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
1037 |
1038 | window.addEventListener( 'keydown', onKeyDown, false );
1039 |
1040 | // force an update at start
1041 |
1042 | this.update();
1043 |
1044 | };
1045 |
1046 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1047 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1048 |
1049 | Object.defineProperties( THREE.OrbitControls.prototype, {
1050 |
1051 | center: {
1052 |
1053 | get: function () {
1054 |
1055 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
1056 | return this.target;
1057 |
1058 | }
1059 |
1060 | },
1061 |
1062 | // backward compatibility
1063 |
1064 | noZoom: {
1065 |
1066 | get: function () {
1067 |
1068 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1069 | return ! this.enableZoom;
1070 |
1071 | },
1072 |
1073 | set: function ( value ) {
1074 |
1075 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1076 | this.enableZoom = ! value;
1077 |
1078 | }
1079 |
1080 | },
1081 |
1082 | noRotate: {
1083 |
1084 | get: function () {
1085 |
1086 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1087 | return ! this.enableRotate;
1088 |
1089 | },
1090 |
1091 | set: function ( value ) {
1092 |
1093 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1094 | this.enableRotate = ! value;
1095 |
1096 | }
1097 |
1098 | },
1099 |
1100 | noPan: {
1101 |
1102 | get: function () {
1103 |
1104 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1105 | return ! this.enablePan;
1106 |
1107 | },
1108 |
1109 | set: function ( value ) {
1110 |
1111 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1112 | this.enablePan = ! value;
1113 |
1114 | }
1115 |
1116 | },
1117 |
1118 | noKeys: {
1119 |
1120 | get: function () {
1121 |
1122 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1123 | return ! this.enableKeys;
1124 |
1125 | },
1126 |
1127 | set: function ( value ) {
1128 |
1129 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1130 | this.enableKeys = ! value;
1131 |
1132 | }
1133 |
1134 | },
1135 |
1136 | staticMoving : {
1137 |
1138 | get: function () {
1139 |
1140 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1141 | return ! this.enableDamping;
1142 |
1143 | },
1144 |
1145 | set: function ( value ) {
1146 |
1147 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1148 | this.enableDamping = ! value;
1149 |
1150 | }
1151 |
1152 | },
1153 |
1154 | dynamicDampingFactor : {
1155 |
1156 | get: function () {
1157 |
1158 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1159 | return this.dampingFactor;
1160 |
1161 | },
1162 |
1163 | set: function ( value ) {
1164 |
1165 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1166 | this.dampingFactor = value;
1167 |
1168 | }
1169 |
1170 | }
1171 |
1172 | } );
1173 |
1174 |
1175 | /***/ },
1176 | /* 3 */
1177 | /***/ function(module, exports) {
1178 |
1179 | "use strict";
1180 | var Controller = (function () {
1181 | function Controller() {
1182 | }
1183 | Controller.init = function () {
1184 | this.eventListener = [];
1185 | var ControlParam = function () {
1186 | this.LightColor2 = '#ff8700';
1187 | this.LightColor = '#f7f342';
1188 | this.NormalColor = '#f7a90e';
1189 | this.DarkColor2 = '#ff9800';
1190 | this.GreyColor = '#3c342f';
1191 | this.DarkColor = "#181818";
1192 | this.TimeScale = 3;
1193 | this.ParticleSpread = 1;
1194 | this.ParticleColor = '#ffb400';
1195 | this.InvertedBackground = false;
1196 | this.ShowGrid = true;
1197 | this.restart = function () { };
1198 | };
1199 | var params = new ControlParam();
1200 | var gui = new dat.GUI();
1201 | var f1 = gui.addFolder('Spawn Color');
1202 | this.eventListener[Controller.DARK_COLOR] = f1.addColor(params, 'DarkColor');
1203 | this.eventListener[Controller.DARK_COLOR_2] = f1.addColor(params, 'GreyColor');
1204 | this.eventListener[Controller.DARK_COLOR_2] = f1.addColor(params, 'DarkColor2');
1205 | this.eventListener[Controller.NORMAL_COLOR] = f1.addColor(params, 'NormalColor');
1206 | this.eventListener[Controller.LIGHT_COLOR] = f1.addColor(params, 'LightColor');
1207 | this.eventListener[Controller.LIGHT_COLOR_2] = f1.addColor(params, 'LightColor2');
1208 | f1.open();
1209 | var f2 = gui.addFolder('Flare Particle');
1210 | this.eventListener[Controller.PARTICLE_SPREAD] = f2.add(params, 'ParticleSpread', 0, 2);
1211 | this.eventListener[Controller.PARTICLE_COLOR] = f2.addColor(params, 'ParticleColor');
1212 | f2.open();
1213 | this.eventListener[Controller.INVERTED_BACKGROUND] = gui.add(params, 'InvertedBackground');
1214 | this.eventListener[Controller.SHOW_GRID] = gui.add(params, 'ShowGrid');
1215 | this.eventListener[Controller.TIME_SCALE] = gui.add(params, 'TimeScale', 0, 10);
1216 | gui.add(params, 'restart');
1217 | this.gui = gui;
1218 | this.params = params;
1219 | };
1220 | Controller.getParams = function () {
1221 | return this.params;
1222 | };
1223 | Controller.setRestartFunc = function (func) {
1224 | this.params.restart = func;
1225 | };
1226 | Controller.attachEvent = function (key, callback) {
1227 | this.eventListener[key].onChange(callback);
1228 | };
1229 | Controller.DARK_COLOR = 0;
1230 | Controller.NORMAL_COLOR = 1;
1231 | Controller.LIGHT_COLOR = 2;
1232 | Controller.LIGHT_COLOR_2 = 3;
1233 | Controller.DARK_COLOR_2 = 4;
1234 | Controller.RESTART = 5;
1235 | Controller.TIME_SCALE = 6;
1236 | Controller.PARTICLE_SPREAD = 7;
1237 | Controller.PARTICLE_COLOR = 8;
1238 | Controller.INVERTED_BACKGROUND = 9;
1239 | Controller.SHOW_GRID = 10;
1240 | return Controller;
1241 | }());
1242 | exports.Controller = Controller;
1243 |
1244 |
1245 | /***/ },
1246 | /* 4 */
1247 | /***/ function(module, exports, __webpack_require__) {
1248 |
1249 | "use strict";
1250 | var flameSphere_1 = __webpack_require__(5);
1251 | var flameAnimation_1 = __webpack_require__(8);
1252 | var flareParticle_1 = __webpack_require__(9);
1253 | var controller_1 = __webpack_require__(3);
1254 | var renderer_1 = __webpack_require__(1);
1255 | var ExplosionController = (function () {
1256 | function ExplosionController() {
1257 | }
1258 | ExplosionController.init = function () {
1259 | var _this = this;
1260 | this.objs = [];
1261 | this.objectPool = [];
1262 | this.spawnTime = 0;
1263 | this.flareParticle = new flareParticle_1.FlareParticle();
1264 | this.spawnNewFlame();
1265 | controller_1.Controller.attachEvent(controller_1.Controller.DARK_COLOR, function (value) {
1266 | for (var i = 0; i < _this.objs.length; i++) {
1267 | _this.currentCol['colDark'] = value;
1268 | _this.objs[i].instance.setColor({ colDark: value });
1269 | }
1270 | });
1271 | controller_1.Controller.attachEvent(controller_1.Controller.NORMAL_COLOR, function (value) {
1272 | for (var i = 0; i < _this.objs.length; i++) {
1273 | _this.currentCol['colNormal'] = value;
1274 | _this.objs[i].instance.setColor({ colNormal: value });
1275 | }
1276 | });
1277 | controller_1.Controller.attachEvent(controller_1.Controller.LIGHT_COLOR, function (value) {
1278 | for (var i = 0; i < _this.objs.length; i++) {
1279 | _this.currentCol['colLight'] = value;
1280 | _this.objs[i].instance.setColor({ colLight: value });
1281 | }
1282 | });
1283 | this.reset();
1284 | };
1285 | ExplosionController.reset = function () {
1286 | for (var i = 0; i < this.objs.length; i++) {
1287 | this.objs[i].reset();
1288 | renderer_1.Renderer.removeFromScene(this.objs[i].instance.getMesh());
1289 | }
1290 | this.objectPool = [];
1291 | this.objs = [];
1292 | this.flareParticle.reset();
1293 | };
1294 | ExplosionController.spawnNewFlame = function () {
1295 | var i = this.objs.length;
1296 | if (this.objectPool.length > 0) {
1297 | i = this.objectPool.shift();
1298 | this.objs[i].instance.getMesh().visible = true;
1299 | this.objs[i].instance.setColor(this.currentCol);
1300 | this.objs[i].reset();
1301 | }
1302 | else {
1303 | var obj = new flameAnimation_1.FlameAnimation(new flameSphere_1.FlameSphere(Math.random() * 5 + 8), Math.random() * 7 - 4, Math.random() * 7 - 4, Math.random() * 0.4 + 0.35, Math.random() * 0.4 + 0.3);
1304 | obj.instance.setColor(this.currentCol);
1305 | this.objs.push(obj);
1306 | renderer_1.Renderer.addToScene(this.objs[i].instance.getMesh());
1307 | }
1308 | };
1309 | ExplosionController.update = function (deltaTime) {
1310 | var timeScale = controller_1.Controller.getParams().TimeScale;
1311 | this.spawnTime += deltaTime * timeScale;
1312 | if (this.spawnTime > 200) {
1313 | while (this.spawnTime > 200)
1314 | this.spawnTime -= 200;
1315 | this.spawnNewFlame();
1316 | }
1317 | for (var i = 0; i < this.objs.length; i++) {
1318 | if (this.objs[i].isDie()) {
1319 | if (this.objs[i].inPolling())
1320 | continue;
1321 | this.objs[i].setInPolling(true);
1322 | this.objs[i].instance.getMesh().visible = false;
1323 | this.objectPool.push(i);
1324 | }
1325 | else {
1326 | this.objs[i].update(deltaTime);
1327 | }
1328 | }
1329 | this.flareParticle.update(deltaTime * timeScale);
1330 | };
1331 | ExplosionController.currentCol = {};
1332 | return ExplosionController;
1333 | }());
1334 | exports.ExplosionController = ExplosionController;
1335 |
1336 |
1337 | /***/ },
1338 | /* 5 */
1339 | /***/ function(module, exports, __webpack_require__) {
1340 |
1341 | "use strict";
1342 | var assetsManager_1 = __webpack_require__(6);
1343 | var utils_1 = __webpack_require__(7);
1344 | var FlameSphere = (function () {
1345 | function FlameSphere(radius) {
1346 | this.flowRatio = 1;
1347 | radius = radius || 20;
1348 | var glsl = assetsManager_1.AssetsManager.instance.getTexture();
1349 | this.material = new THREE.ShaderMaterial({
1350 | uniforms: {
1351 | time: {
1352 | type: "f",
1353 | value: 0.0
1354 | },
1355 | seed: {
1356 | type: 'f',
1357 | value: Math.random() * 1000.0
1358 | },
1359 | detail: {
1360 | type: 'f',
1361 | value: Math.random() * 3.5 + 5
1362 | },
1363 | opacity: {
1364 | type: 'f',
1365 | value: 1
1366 | },
1367 | colLight: {
1368 | value: utils_1.Utils.hexToVec3(FlameSphere.defaultColor.colLight)
1369 | },
1370 | colNormal: {
1371 | value: utils_1.Utils.hexToVec3(FlameSphere.defaultColor.colNormal)
1372 | },
1373 | colDark: {
1374 | value: utils_1.Utils.hexToVec3(FlameSphere.defaultColor.colDark)
1375 | }
1376 | },
1377 | vertexShader: glsl.vertexFlameShader,
1378 | fragmentShader: glsl.fragmentFlameShader
1379 | });
1380 | this.material.transparent = true;
1381 | this.mesh = new THREE.Mesh(new THREE.IcosahedronGeometry(radius, 3), this.material);
1382 | this.mesh.position.set(0, 0, 0);
1383 | }
1384 | FlameSphere.prototype.setColor = function (prop) {
1385 | if (prop.colDark != null) {
1386 | if (typeof prop.colDark === 'string') {
1387 | this.material.uniforms['colDark'].value = utils_1.Utils.hexToVec3(prop.colDark);
1388 | }
1389 | else {
1390 | this.material.uniforms['colDark'].value = prop.colDark;
1391 | }
1392 | }
1393 | if (prop.colNormal != null) {
1394 | if (typeof prop.colNormal === 'string') {
1395 | this.material.uniforms['colNormal'].value = utils_1.Utils.hexToVec3(prop.colNormal);
1396 | }
1397 | else {
1398 | this.material.uniforms['colNormal'].value = prop.colNormal;
1399 | }
1400 | }
1401 | if (prop.colLight != null) {
1402 | if (typeof prop.colLight === 'string') {
1403 | this.material.uniforms['colLight'].value = utils_1.Utils.hexToVec3(prop.colLight);
1404 | }
1405 | else {
1406 | this.material.uniforms['colLight'].value = prop.colLight;
1407 | }
1408 | }
1409 | };
1410 | FlameSphere.prototype.setOpacity = function (value) {
1411 | this.material.uniforms['opacity'].value = value;
1412 | };
1413 | FlameSphere.prototype.setDetail = function (value) {
1414 | this.material.uniforms['detail'].value = value;
1415 | };
1416 | FlameSphere.prototype.update = function (timeDiff) {
1417 | this.material.uniforms['time'].value += .0005 * timeDiff * this.flowRatio;
1418 | };
1419 | FlameSphere.prototype.setFlowRatio = function (val) {
1420 | this.flowRatio = val;
1421 | };
1422 | FlameSphere.prototype.getMesh = function () {
1423 | return this.mesh;
1424 | };
1425 | FlameSphere.defaultColor = {
1426 | colDark: '#000000',
1427 | colNormal: '#f7a90e',
1428 | colLight: '#ede92a'
1429 | };
1430 | return FlameSphere;
1431 | }());
1432 | exports.FlameSphere = FlameSphere;
1433 |
1434 |
1435 | /***/ },
1436 | /* 6 */
1437 | /***/ function(module, exports) {
1438 |
1439 | "use strict";
1440 | var AssetsManager = (function () {
1441 | function AssetsManager() {
1442 | var _this = this;
1443 | this.vertexFlameShader = null;
1444 | this.fragmentFlameShader = null;
1445 | this.vertexParticleShader = null;
1446 | this.fragmentParticleShader = null;
1447 | $.ajax({
1448 | url: './dist_github/shader/vertexFlameShader.glsl',
1449 | async: false,
1450 | success: function (vs) {
1451 | _this.vertexFlameShader = vs;
1452 | }
1453 | });
1454 | $.ajax({
1455 | url: './dist_github/shader/fragmentFlameShader.glsl',
1456 | async: false,
1457 | success: function (fs) {
1458 | _this.fragmentFlameShader = fs;
1459 | }
1460 | });
1461 | $.ajax({
1462 | url: './dist_github/shader/vertexParticleShader.glsl',
1463 | async: false,
1464 | success: function (fs) {
1465 | _this.vertexParticleShader = fs;
1466 | }
1467 | });
1468 | $.ajax({
1469 | url: './dist_github/shader/fragmentParticleShader.glsl',
1470 | async: false,
1471 | success: function (fs) {
1472 | _this.fragmentParticleShader = fs;
1473 | }
1474 | });
1475 | }
1476 | AssetsManager.prototype.getTexture = function () {
1477 | return {
1478 | vertexFlameShader: this.vertexFlameShader,
1479 | fragmentFlameShader: this.fragmentFlameShader,
1480 | vectexParticleShader: this.vertexParticleShader,
1481 | fragmentParticleShader: this.fragmentParticleShader
1482 | };
1483 | };
1484 | AssetsManager.instance = new AssetsManager();
1485 | return AssetsManager;
1486 | }());
1487 | exports.AssetsManager = AssetsManager;
1488 |
1489 |
1490 | /***/ },
1491 | /* 7 */
1492 | /***/ function(module, exports) {
1493 |
1494 | "use strict";
1495 | var Utils = (function () {
1496 | function Utils() {
1497 | }
1498 | Utils.hexToVec3 = function (col) {
1499 | var num = parseInt(col.substr(1), 16);
1500 | var r = (num / 256 / 256) % 256;
1501 | var g = (num / 256) % 256;
1502 | var b = num % 256;
1503 | return [r / 255.0, g / 255.0, b / 255.0];
1504 | };
1505 | Utils.formatZero = function (val) {
1506 | if (val.length == 1)
1507 | return '0' + val;
1508 | return val;
1509 | };
1510 | Utils.vec3ToHex = function (col) {
1511 | return '#' +
1512 | this.formatZero(col[0].toString(16)) +
1513 | this.formatZero(col[1].toString(16)) +
1514 | this.formatZero(col[2].toString(16));
1515 | };
1516 | Utils.vec3Blend = function (cola, colb, t) {
1517 | var a = this.hexToVec3(cola);
1518 | var b = this.hexToVec3(colb);
1519 | return [
1520 | a[0] + (b[0] - a[0]) * t,
1521 | a[1] + (b[1] - a[1]) * t,
1522 | a[2] + (b[2] - a[2]) * t
1523 | ];
1524 | };
1525 | return Utils;
1526 | }());
1527 | exports.Utils = Utils;
1528 |
1529 |
1530 | /***/ },
1531 | /* 8 */
1532 | /***/ function(module, exports, __webpack_require__) {
1533 |
1534 | "use strict";
1535 | var controller_1 = __webpack_require__(3);
1536 | var utils_1 = __webpack_require__(7);
1537 | var FlameAnimation = (function () {
1538 | function FlameAnimation(instance, distX, distZ, yRatio, animationTimeRatio) {
1539 | distX = distX || 0;
1540 | distZ = distZ || 0;
1541 | yRatio = yRatio || 1;
1542 | animationTimeRatio = animationTimeRatio || 1;
1543 | this.instance = instance;
1544 | this.distX = distX;
1545 | this.distZ = distZ;
1546 | this.yRatio = yRatio;
1547 | this.animationTimeRatio = animationTimeRatio;
1548 | this.reset();
1549 | }
1550 | FlameAnimation.prototype.reset = function () {
1551 | this.randFlyX = Math.random() * 0.1 - 0.05;
1552 | this.randFlyZ = Math.random() * 0.1 - 0.05;
1553 | this.posX = -1;
1554 | this.currentTime = 0;
1555 | this.timeCount = 0;
1556 | this.spawnTime = 0;
1557 | this.isObjDie = false;
1558 | this.isInPooling = false;
1559 | this.currentState = FlameAnimation.STATE_BEFORE_START;
1560 | this.colorTransitionRandom = Math.random() * 2000 - 1000;
1561 | this.instance.getMesh().position.set(0, 0, 0);
1562 | this.instance.getMesh().scale.set(0, 0, 0);
1563 | this.instance.setFlowRatio(1);
1564 | this.instance.setOpacity(1);
1565 | };
1566 | FlameAnimation.prototype.setColor = function () {
1567 | var params = controller_1.Controller.getParams();
1568 | var tc = this.timeCount + this.colorTransitionRandom;
1569 | if (tc < 2500 + this.colorTransitionRandom) {
1570 | var t = tc / 2500 + this.colorTransitionRandom;
1571 | this.instance.setColor({
1572 | colDark: params.NormalColor,
1573 | colNormal: params.LightColor,
1574 | colLight: params.LightColor2
1575 | });
1576 | }
1577 | else if (tc < 4000) {
1578 | var t = (tc - 2500) / 1500;
1579 | this.instance.setColor({
1580 | colDark: utils_1.Utils.vec3Blend(params.NormalColor, params.DarkColor2, t),
1581 | colNormal: utils_1.Utils.vec3Blend(params.LightColor, params.NormalColor, t),
1582 | colLight: utils_1.Utils.vec3Blend(params.LightColor2, params.LightColor, t)
1583 | });
1584 | }
1585 | else if (tc < 7000) {
1586 | var t = (tc - 4000) / 3000;
1587 | this.instance.setColor({
1588 | colDark: utils_1.Utils.vec3Blend(params.DarkColor2, params.DarkColor2, t),
1589 | colNormal: utils_1.Utils.vec3Blend(params.NormalColor, params.NormalColor, t),
1590 | colLight: utils_1.Utils.vec3Blend(params.LightColor, params.LightColor, t)
1591 | });
1592 | }
1593 | else if (tc < 12000) {
1594 | var t = Math.min(1, (tc - 7000) / 5000);
1595 | this.instance.setColor({
1596 | colDark: utils_1.Utils.vec3Blend(params.DarkColor2, params.DarkColor, t),
1597 | colNormal: utils_1.Utils.vec3Blend(params.NormalColor, params.DarkColor2, t),
1598 | colLight: utils_1.Utils.vec3Blend(params.LightColor, params.NormalColor, t)
1599 | });
1600 | }
1601 | else if (tc < 17000) {
1602 | var t = Math.min(1, (tc - 12000) / 5000);
1603 | this.instance.setColor({
1604 | colDark: utils_1.Utils.vec3Blend(params.DarkColor, params.DarkColor, t),
1605 | colNormal: utils_1.Utils.vec3Blend(params.DarkColor2, params.DarkColor, t),
1606 | colLight: utils_1.Utils.vec3Blend(params.NormalColor, params.DarkColor2, t)
1607 | });
1608 | }
1609 | else {
1610 | var t = Math.min(1, (tc - 17000) / 6000);
1611 | this.instance.setColor({
1612 | colDark: utils_1.Utils.vec3Blend(params.DarkColor, params.GreyColor, t),
1613 | colNormal: utils_1.Utils.vec3Blend(params.DarkColor, params.GreyColor, t),
1614 | colLight: utils_1.Utils.vec3Blend(params.DarkColor2, params.DarkColor, t)
1615 | });
1616 | }
1617 | };
1618 | FlameAnimation.prototype.updateState = function (deltaTime) {
1619 | var cTime = this.currentTime + deltaTime;
1620 | if (this.currentState == FlameAnimation.STATE_BEFORE_START) {
1621 | if (cTime > FlameAnimation.BEFORE_INTERVAL) {
1622 | cTime -= FlameAnimation.BEFORE_INTERVAL;
1623 | this.currentState = FlameAnimation.STATE_SPAWN;
1624 | }
1625 | }
1626 | else if (this.currentState == FlameAnimation.STATE_SPAWN) {
1627 | if (cTime > FlameAnimation.SPAWN_INTERVAL) {
1628 | cTime -= FlameAnimation.SPAWN_INTERVAL;
1629 | this.posX = -1;
1630 | this.currentState = FlameAnimation.STATE_SPAWN_DOWN;
1631 | }
1632 | }
1633 | else if (this.currentState == FlameAnimation.STATE_SPAWN_DOWN) {
1634 | if (cTime > FlameAnimation.SPAWN_DOWN_INTERVAL) {
1635 | cTime -= FlameAnimation.SPAWN_DOWN_INTERVAL;
1636 | this.currentState = FlameAnimation.STATE_FLOATING;
1637 | }
1638 | }
1639 | else if (this.currentState == FlameAnimation.STATE_FLOATING) {
1640 | if (cTime > FlameAnimation.FLOATING_INTERVAL) {
1641 | this.randFlyX += Math.random() * 0.2;
1642 | this.randFlyZ += Math.random() * 0.2;
1643 | cTime -= FlameAnimation.FLOATING_INTERVAL;
1644 | this.posX = -1;
1645 | this.currentState = FlameAnimation.STATE_IDLE;
1646 | }
1647 | }
1648 | else if (this.currentState == FlameAnimation.STATE_IDLE) {
1649 | if (cTime > FlameAnimation.IDLE_INTERVAL) {
1650 | this.isObjDie = true;
1651 | }
1652 | }
1653 | this.currentTime = cTime;
1654 | };
1655 | FlameAnimation.prototype.update = function (deltaTime) {
1656 | if (this.isObjDie)
1657 | return;
1658 | var mesh = this.instance.getMesh();
1659 | var timeScale = controller_1.Controller.getParams().TimeScale;
1660 | this.updateState(deltaTime * timeScale);
1661 | this.timeCount += deltaTime * timeScale;
1662 | if (this.currentState == FlameAnimation.STATE_SPAWN) {
1663 | var t = this.currentTime / FlameAnimation.SPAWN_INTERVAL;
1664 | var t2 = this.currentTime / (FlameAnimation.SPAWN_INTERVAL + FlameAnimation.SPAWN_DOWN_INTERVAL);
1665 | mesh.position.set(this.distX * t2, mesh.position.y + t * 0.4 * this.yRatio * timeScale, this.distZ * t2);
1666 | var scale = t;
1667 | mesh.scale.set(scale, scale, scale);
1668 | }
1669 | else if (this.currentState == FlameAnimation.STATE_SPAWN_DOWN) {
1670 | var t2 = (this.currentTime + FlameAnimation.SPAWN_INTERVAL) /
1671 | (FlameAnimation.SPAWN_INTERVAL + FlameAnimation.SPAWN_DOWN_INTERVAL);
1672 | mesh.position.set(this.distX * t2, mesh.position.y +
1673 | (0.6 * timeScale *
1674 | (1 - this.currentTime / FlameAnimation.SPAWN_DOWN_INTERVAL) +
1675 | 0.2 * timeScale) * this.yRatio, this.distZ * t2);
1676 | }
1677 | else if (this.currentState == FlameAnimation.STATE_FLOATING) {
1678 | if (this.posX == -1) {
1679 | this.posX = mesh.position.x;
1680 | this.posY = mesh.position.y;
1681 | this.posZ = mesh.position.z;
1682 | this.instance.setFlowRatio(0.5);
1683 | }
1684 | mesh.position.set(mesh.position.x + this.randFlyX * timeScale, mesh.position.y + 0.2 * timeScale, mesh.position.z + this.randFlyZ * timeScale);
1685 | var scale = mesh.scale.x + 0.003 * timeScale;
1686 | mesh.scale.set(scale, scale, scale);
1687 | }
1688 | else if (this.currentState == FlameAnimation.STATE_IDLE) {
1689 | if (this.posX == -1) {
1690 | this.posX = mesh.position.x;
1691 | this.posY = mesh.position.y;
1692 | this.posZ = mesh.position.z;
1693 | this.instance.setFlowRatio(0.2);
1694 | }
1695 | mesh.position.setY(this.posY + this.currentTime / 100);
1696 | if (this.currentTime > FlameAnimation.IDLE_INTERVAL - 5000) {
1697 | this.instance.setOpacity(1 - (this.currentTime - (FlameAnimation.IDLE_INTERVAL - 5000)) / 5000);
1698 | }
1699 | var scale = mesh.scale.x + 0.002 * timeScale;
1700 | mesh.scale.set(scale, scale, scale);
1701 | }
1702 | this.setColor();
1703 | this.instance.update(deltaTime * timeScale * this.animationTimeRatio);
1704 | };
1705 | FlameAnimation.prototype.isDie = function () {
1706 | return this.isObjDie;
1707 | };
1708 | FlameAnimation.prototype.inPolling = function () {
1709 | return this.isInPooling;
1710 | };
1711 | FlameAnimation.prototype.setInPolling = function (val) {
1712 | this.isInPooling = val;
1713 | };
1714 | FlameAnimation.STATE_BEFORE_START = 0;
1715 | FlameAnimation.STATE_SPAWN = 1;
1716 | FlameAnimation.STATE_SPAWN_DOWN = 2;
1717 | FlameAnimation.STATE_FLOATING = 3;
1718 | FlameAnimation.STATE_IDLE = 4;
1719 | FlameAnimation.BEFORE_INTERVAL = 300;
1720 | FlameAnimation.SPAWN_INTERVAL = 400;
1721 | FlameAnimation.SPAWN_DOWN_INTERVAL = 2000;
1722 | FlameAnimation.FLOATING_INTERVAL = 8000;
1723 | FlameAnimation.IDLE_INTERVAL = 20000;
1724 | return FlameAnimation;
1725 | }());
1726 | exports.FlameAnimation = FlameAnimation;
1727 |
1728 |
1729 | /***/ },
1730 | /* 9 */
1731 | /***/ function(module, exports, __webpack_require__) {
1732 |
1733 | "use strict";
1734 | var renderer_1 = __webpack_require__(1);
1735 | var assetsManager_1 = __webpack_require__(6);
1736 | var controller_1 = __webpack_require__(3);
1737 | var constants_1 = __webpack_require__(10);
1738 | var utils_1 = __webpack_require__(7);
1739 | var FlareParticle = (function () {
1740 | function FlareParticle() {
1741 | var _this = this;
1742 | this.particlesNumber = 500;
1743 | var shaderMaterial = new THREE.ShaderMaterial({
1744 | uniforms: {
1745 | color: { value: new THREE.Color(0xffffff) },
1746 | texture: { value: new THREE.TextureLoader().load("./dist_github/images/circle-particle.png") }
1747 | },
1748 | vertexShader: assetsManager_1.AssetsManager.instance.getTexture().vectexParticleShader,
1749 | fragmentShader: assetsManager_1.AssetsManager.instance.getTexture().fragmentParticleShader,
1750 | blending: THREE.NormalBlending,
1751 | depthTest: false,
1752 | transparent: true
1753 | });
1754 | this.geometry = new THREE.BufferGeometry();
1755 | var positions = new Float32Array(this.particlesNumber * 3);
1756 | var colors = new Float32Array(this.particlesNumber * 3);
1757 | var sizes = new Float32Array(this.particlesNumber);
1758 | this.needsUpdate = [];
1759 | this.originalSizes = new Float32Array(this.particlesNumber);
1760 | this.moveDest = new Float32Array(this.particlesNumber * 3);
1761 | this.particleTime = new Float32Array(this.particlesNumber);
1762 | this.particleColor = utils_1.Utils.hexToVec3(controller_1.Controller.getParams().ParticleColor);
1763 | for (var i = 0, i3 = 0; i < this.particlesNumber; i++, i3 += 3) {
1764 | positions[i3 + 0] = 0;
1765 | positions[i3 + 1] = 0;
1766 | positions[i3 + 2] = 0;
1767 | this.moveDest[i3] = Math.random() * 200 - 100;
1768 | this.moveDest[i3 + 1] = Math.random() * 0.3 + 0.45;
1769 | this.moveDest[i3 + 2] = Math.random() * 200 - 100;
1770 | colors[i3 + 0] = this.particleColor[0];
1771 | colors[i3 + 1] = this.particleColor[1];
1772 | colors[i3 + 2] = this.particleColor[2];
1773 | sizes[i] = Math.random() * 1 + 0.5;
1774 | this.originalSizes[i] = sizes[i];
1775 | }
1776 | this.geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
1777 | this.geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
1778 | this.geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));
1779 | this.particleSystem = new THREE.Points(this.geometry, shaderMaterial);
1780 | renderer_1.Renderer.addToScene(this.particleSystem);
1781 | this.reset();
1782 | FlareParticle.setController();
1783 | controller_1.Controller.attachEvent(controller_1.Controller.PARTICLE_COLOR, function (value) {
1784 | _this.particleColor = utils_1.Utils.hexToVec3(value);
1785 | });
1786 | }
1787 | FlareParticle.setController = function () {
1788 | var _this = this;
1789 | this.particleSpreadingRatio = controller_1.Controller.getParams().ParticleSpread;
1790 | controller_1.Controller.attachEvent(controller_1.Controller.PARTICLE_SPREAD, function (value) {
1791 | _this.particleSpreadingRatio = value;
1792 | });
1793 | };
1794 | FlareParticle.prototype.reset = function () {
1795 | this.time = 0;
1796 | this.spawnParticleTime = 0;
1797 | this.spawnParticleInterval = 1;
1798 | var sizes = this.geometry.attributes['size'].array;
1799 | var positions = this.geometry.attributes['position'].array;
1800 | for (var i = 0; i < this.particlesNumber; i++) {
1801 | sizes[i] = 0;
1802 | positions[i * 3] = 0;
1803 | positions[i * 3 + 1] = 0;
1804 | positions[i * 3 + 2] = 0;
1805 | this.needsUpdate[i] = false;
1806 | this.particleTime[i] = 0;
1807 | }
1808 | this.geometry.attributes['size'].needsUpdate = true;
1809 | this.geometry.attributes['position'].needsUpdate = true;
1810 | };
1811 | FlareParticle.prototype.spawnParticle = function () {
1812 | for (var i = 0; i < this.particlesNumber; i++) {
1813 | if (this.needsUpdate[i] == false) {
1814 | this.needsUpdate[i] = true;
1815 | return;
1816 | }
1817 | }
1818 | };
1819 | FlareParticle.prototype.update = function (deltaTime) {
1820 | this.spawnParticleTime += deltaTime;
1821 | if (this.spawnParticleTime > this.spawnParticleInterval) {
1822 | this.spawnParticleTime = 0;
1823 | this.spawnParticleInterval = Math.random() * 300 + 50;
1824 | this.spawnParticle();
1825 | }
1826 | deltaTime /= 1000;
1827 | this.time += deltaTime;
1828 | this.particleSystem.rotation.y += 0.01 * deltaTime;
1829 | var timeScale = controller_1.Controller.getParams().TimeScale / 3;
1830 | var sizes = this.geometry.attributes['size'].array;
1831 | var positions = this.geometry.attributes['position'].array;
1832 | var colors = this.geometry.attributes['customColor'].array;
1833 | for (var i = 0, i3 = 0; i < this.particlesNumber; i++, i3 += 3) {
1834 | if (this.needsUpdate[i]) {
1835 | if (this.particleTime[i] > constants_1.Constants.MAXIMUM_LIVE_TIME / 1000) {
1836 | positions[i3] = 0;
1837 | positions[i3 + 1] = 0;
1838 | positions[i3 + 2] = 0;
1839 | this.particleTime[i] = 0;
1840 | sizes[i] = 0.01;
1841 | }
1842 | else {
1843 | var ac = FlareParticle.particleSpreadingRatio *
1844 | this.particleTime[i] / (constants_1.Constants.MAXIMUM_LIVE_TIME / 1000) +
1845 | 0.01 * Math.sin(this.time);
1846 | var randDist = (10 * Math.sin(0.3 * i + this.time + Math.random() / 10)) * timeScale;
1847 | sizes[i] = this.originalSizes[i] * (3 + Math.sin(0.4 * i + this.time));
1848 | positions[i3] = ac * this.moveDest[i3] + randDist;
1849 | positions[i3 + 1] += (Math.random() * 0.4 + 0.9) * this.moveDest[i3 + 1] * timeScale;
1850 | positions[i3 + 2] = ac * this.moveDest[i3 + 2] + randDist;
1851 | this.particleTime[i] += deltaTime;
1852 | }
1853 | }
1854 | colors[i3] = this.particleColor[0];
1855 | colors[i3 + 1] = this.particleColor[1];
1856 | colors[i3 + 2] = this.particleColor[2];
1857 | }
1858 | this.geometry.attributes['customColor'].needsUpdate = true;
1859 | this.geometry.attributes['size'].needsUpdate = true;
1860 | this.geometry.attributes['position'].needsUpdate = true;
1861 | };
1862 | return FlareParticle;
1863 | }());
1864 | exports.FlareParticle = FlareParticle;
1865 |
1866 |
1867 | /***/ },
1868 | /* 10 */
1869 | /***/ function(module, exports) {
1870 |
1871 | "use strict";
1872 | var Constants = (function () {
1873 | function Constants() {
1874 | }
1875 | Constants.MAXIMUM_LIVE_TIME = 20000;
1876 | return Constants;
1877 | }());
1878 | exports.Constants = Constants;
1879 |
1880 |
1881 | /***/ }
1882 | /******/ ]);
--------------------------------------------------------------------------------
/dist_github/images/circle-particle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/dist_github/images/circle-particle.png
--------------------------------------------------------------------------------
/dist_github/js/OrbitControl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return spherical.phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return spherical.theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function update () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 | spherical.setFromVector3( offset );
137 |
138 | if ( scope.autoRotate && state === STATE.NONE ) {
139 |
140 | rotateLeft( getAutoRotationAngle() );
141 |
142 | }
143 |
144 | spherical.theta += sphericalDelta.theta;
145 | spherical.phi += sphericalDelta.phi;
146 |
147 | // restrict theta to be between desired limits
148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
149 |
150 | // restrict phi to be between desired limits
151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
152 |
153 | spherical.makeSafe();
154 |
155 |
156 | spherical.radius *= scale;
157 |
158 | // restrict radius to be between desired limits
159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
160 |
161 | // move target to panned location
162 | scope.target.add( panOffset );
163 |
164 | offset.setFromSpherical( spherical );
165 |
166 | // rotate offset back to "camera-up-vector-is-up" space
167 | offset.applyQuaternion( quatInverse );
168 |
169 | position.copy( scope.target ).add( offset );
170 |
171 | scope.object.lookAt( scope.target );
172 |
173 | if ( scope.enableDamping === true ) {
174 |
175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
177 |
178 | } else {
179 |
180 | sphericalDelta.set( 0, 0, 0 );
181 |
182 | }
183 |
184 | scale = 1;
185 | panOffset.set( 0, 0, 0 );
186 |
187 | // update condition is:
188 | // min(camera displacement, camera rotation in radians)^2 > EPS
189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
190 |
191 | if ( zoomChanged ||
192 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
194 |
195 | scope.dispatchEvent( changeEvent );
196 |
197 | lastPosition.copy( scope.object.position );
198 | lastQuaternion.copy( scope.object.quaternion );
199 | zoomChanged = false;
200 |
201 | return true;
202 |
203 | }
204 |
205 | return false;
206 |
207 | };
208 |
209 | }();
210 |
211 | this.dispose = function() {
212 |
213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
215 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
216 |
217 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
218 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
219 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
220 |
221 | document.removeEventListener( 'mousemove', onMouseMove, false );
222 | document.removeEventListener( 'mouseup', onMouseUp, false );
223 |
224 | window.removeEventListener( 'keydown', onKeyDown, false );
225 |
226 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
227 |
228 | };
229 |
230 | //
231 | // internals
232 | //
233 |
234 | var scope = this;
235 |
236 | var changeEvent = { type: 'change' };
237 | var startEvent = { type: 'start' };
238 | var endEvent = { type: 'end' };
239 |
240 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
241 |
242 | var state = STATE.NONE;
243 |
244 | var EPS = 0.000001;
245 |
246 | // current position in spherical coordinates
247 | var spherical = new THREE.Spherical();
248 | var sphericalDelta = new THREE.Spherical();
249 |
250 | var scale = 1;
251 | var panOffset = new THREE.Vector3();
252 | var zoomChanged = false;
253 |
254 | var rotateStart = new THREE.Vector2();
255 | var rotateEnd = new THREE.Vector2();
256 | var rotateDelta = new THREE.Vector2();
257 |
258 | var panStart = new THREE.Vector2();
259 | var panEnd = new THREE.Vector2();
260 | var panDelta = new THREE.Vector2();
261 |
262 | var dollyStart = new THREE.Vector2();
263 | var dollyEnd = new THREE.Vector2();
264 | var dollyDelta = new THREE.Vector2();
265 |
266 | function getAutoRotationAngle() {
267 |
268 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
269 |
270 | }
271 |
272 | function getZoomScale() {
273 |
274 | return Math.pow( 0.95, scope.zoomSpeed );
275 |
276 | }
277 |
278 | function rotateLeft( angle ) {
279 |
280 | sphericalDelta.theta -= angle;
281 |
282 | }
283 |
284 | function rotateUp( angle ) {
285 |
286 | sphericalDelta.phi -= angle;
287 |
288 | }
289 |
290 | var panLeft = function() {
291 |
292 | var v = new THREE.Vector3();
293 |
294 | return function panLeft( distance, objectMatrix ) {
295 |
296 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
297 | v.multiplyScalar( - distance );
298 |
299 | panOffset.add( v );
300 |
301 | };
302 |
303 | }();
304 |
305 | var panUp = function() {
306 |
307 | var v = new THREE.Vector3();
308 |
309 | return function panUp( distance, objectMatrix ) {
310 |
311 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
312 | v.multiplyScalar( distance );
313 |
314 | panOffset.add( v );
315 |
316 | };
317 |
318 | }();
319 |
320 | // deltaX and deltaY are in pixels; right and down are positive
321 | var pan = function() {
322 |
323 | var offset = new THREE.Vector3();
324 |
325 | return function pan ( deltaX, deltaY ) {
326 |
327 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
328 |
329 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
330 |
331 | // perspective
332 | var position = scope.object.position;
333 | offset.copy( position ).sub( scope.target );
334 | var targetDistance = offset.length();
335 |
336 | // half of the fov is center to top of screen
337 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
338 |
339 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
340 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
341 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
342 |
343 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
344 |
345 | // orthographic
346 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
347 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
348 |
349 | } else {
350 |
351 | // camera neither orthographic nor perspective
352 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
353 | scope.enablePan = false;
354 |
355 | }
356 |
357 | };
358 |
359 | }();
360 |
361 | function dollyIn( dollyScale ) {
362 |
363 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
364 |
365 | scale /= dollyScale;
366 |
367 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
368 |
369 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
370 | scope.object.updateProjectionMatrix();
371 | zoomChanged = true;
372 |
373 | } else {
374 |
375 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
376 | scope.enableZoom = false;
377 |
378 | }
379 |
380 | }
381 |
382 | function dollyOut( dollyScale ) {
383 |
384 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
385 |
386 | scale *= dollyScale;
387 |
388 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
389 |
390 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
391 | scope.object.updateProjectionMatrix();
392 | zoomChanged = true;
393 |
394 | } else {
395 |
396 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
397 | scope.enableZoom = false;
398 |
399 | }
400 |
401 | }
402 |
403 | //
404 | // event callbacks - update the object state
405 | //
406 |
407 | function handleMouseDownRotate( event ) {
408 |
409 | //console.log( 'handleMouseDownRotate' );
410 |
411 | rotateStart.set( event.clientX, event.clientY );
412 |
413 | }
414 |
415 | function handleMouseDownDolly( event ) {
416 |
417 | //console.log( 'handleMouseDownDolly' );
418 |
419 | dollyStart.set( event.clientX, event.clientY );
420 |
421 | }
422 |
423 | function handleMouseDownPan( event ) {
424 |
425 | //console.log( 'handleMouseDownPan' );
426 |
427 | panStart.set( event.clientX, event.clientY );
428 |
429 | }
430 |
431 | function handleMouseMoveRotate( event ) {
432 |
433 | //console.log( 'handleMouseMoveRotate' );
434 |
435 | rotateEnd.set( event.clientX, event.clientY );
436 | rotateDelta.subVectors( rotateEnd, rotateStart );
437 |
438 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
439 |
440 | // rotating across whole screen goes 360 degrees around
441 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
442 |
443 | // rotating up and down along whole screen attempts to go 360, but limited to 180
444 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
445 |
446 | rotateStart.copy( rotateEnd );
447 |
448 | scope.update();
449 |
450 | }
451 |
452 | function handleMouseMoveDolly( event ) {
453 |
454 | //console.log( 'handleMouseMoveDolly' );
455 |
456 | dollyEnd.set( event.clientX, event.clientY );
457 |
458 | dollyDelta.subVectors( dollyEnd, dollyStart );
459 |
460 | if ( dollyDelta.y > 0 ) {
461 |
462 | dollyIn( getZoomScale() );
463 |
464 | } else if ( dollyDelta.y < 0 ) {
465 |
466 | dollyOut( getZoomScale() );
467 |
468 | }
469 |
470 | dollyStart.copy( dollyEnd );
471 |
472 | scope.update();
473 |
474 | }
475 |
476 | function handleMouseMovePan( event ) {
477 |
478 | //console.log( 'handleMouseMovePan' );
479 |
480 | panEnd.set( event.clientX, event.clientY );
481 |
482 | panDelta.subVectors( panEnd, panStart );
483 |
484 | pan( panDelta.x, panDelta.y );
485 |
486 | panStart.copy( panEnd );
487 |
488 | scope.update();
489 |
490 | }
491 |
492 | function handleMouseUp( event ) {
493 |
494 | //console.log( 'handleMouseUp' );
495 |
496 | }
497 |
498 | function handleMouseWheel( event ) {
499 |
500 | //console.log( 'handleMouseWheel' );
501 |
502 | if ( event.deltaY < 0 ) {
503 |
504 | dollyOut( getZoomScale() );
505 |
506 | } else if ( event.deltaY > 0 ) {
507 |
508 | dollyIn( getZoomScale() );
509 |
510 | }
511 |
512 | scope.update();
513 |
514 | }
515 |
516 | function handleKeyDown( event ) {
517 |
518 | //console.log( 'handleKeyDown' );
519 |
520 | switch ( event.keyCode ) {
521 |
522 | case scope.keys.UP:
523 | pan( 0, scope.keyPanSpeed );
524 | scope.update();
525 | break;
526 |
527 | case scope.keys.BOTTOM:
528 | pan( 0, - scope.keyPanSpeed );
529 | scope.update();
530 | break;
531 |
532 | case scope.keys.LEFT:
533 | pan( scope.keyPanSpeed, 0 );
534 | scope.update();
535 | break;
536 |
537 | case scope.keys.RIGHT:
538 | pan( - scope.keyPanSpeed, 0 );
539 | scope.update();
540 | break;
541 |
542 | }
543 |
544 | }
545 |
546 | function handleTouchStartRotate( event ) {
547 |
548 | //console.log( 'handleTouchStartRotate' );
549 |
550 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
551 |
552 | }
553 |
554 | function handleTouchStartDolly( event ) {
555 |
556 | //console.log( 'handleTouchStartDolly' );
557 |
558 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
559 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
560 |
561 | var distance = Math.sqrt( dx * dx + dy * dy );
562 |
563 | dollyStart.set( 0, distance );
564 |
565 | }
566 |
567 | function handleTouchStartPan( event ) {
568 |
569 | //console.log( 'handleTouchStartPan' );
570 |
571 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
572 |
573 | }
574 |
575 | function handleTouchMoveRotate( event ) {
576 |
577 | //console.log( 'handleTouchMoveRotate' );
578 |
579 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
580 | rotateDelta.subVectors( rotateEnd, rotateStart );
581 |
582 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
583 |
584 | // rotating across whole screen goes 360 degrees around
585 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
586 |
587 | // rotating up and down along whole screen attempts to go 360, but limited to 180
588 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
589 |
590 | rotateStart.copy( rotateEnd );
591 |
592 | scope.update();
593 |
594 | }
595 |
596 | function handleTouchMoveDolly( event ) {
597 |
598 | //console.log( 'handleTouchMoveDolly' );
599 |
600 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
601 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
602 |
603 | var distance = Math.sqrt( dx * dx + dy * dy );
604 |
605 | dollyEnd.set( 0, distance );
606 |
607 | dollyDelta.subVectors( dollyEnd, dollyStart );
608 |
609 | if ( dollyDelta.y > 0 ) {
610 |
611 | dollyOut( getZoomScale() );
612 |
613 | } else if ( dollyDelta.y < 0 ) {
614 |
615 | dollyIn( getZoomScale() );
616 |
617 | }
618 |
619 | dollyStart.copy( dollyEnd );
620 |
621 | scope.update();
622 |
623 | }
624 |
625 | function handleTouchMovePan( event ) {
626 |
627 | //console.log( 'handleTouchMovePan' );
628 |
629 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
630 |
631 | panDelta.subVectors( panEnd, panStart );
632 |
633 | pan( panDelta.x, panDelta.y );
634 |
635 | panStart.copy( panEnd );
636 |
637 | scope.update();
638 |
639 | }
640 |
641 | function handleTouchEnd( event ) {
642 |
643 | //console.log( 'handleTouchEnd' );
644 |
645 | }
646 |
647 | //
648 | // event handlers - FSM: listen for events and reset state
649 | //
650 |
651 | function onMouseDown( event ) {
652 |
653 | if ( scope.enabled === false ) return;
654 |
655 | event.preventDefault();
656 |
657 | if ( event.button === scope.mouseButtons.ORBIT ) {
658 |
659 | if ( scope.enableRotate === false ) return;
660 |
661 | handleMouseDownRotate( event );
662 |
663 | state = STATE.ROTATE;
664 |
665 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
666 |
667 | if ( scope.enableZoom === false ) return;
668 |
669 | handleMouseDownDolly( event );
670 |
671 | state = STATE.DOLLY;
672 |
673 | } else if ( event.button === scope.mouseButtons.PAN ) {
674 |
675 | if ( scope.enablePan === false ) return;
676 |
677 | handleMouseDownPan( event );
678 |
679 | state = STATE.PAN;
680 |
681 | }
682 |
683 | if ( state !== STATE.NONE ) {
684 |
685 | document.addEventListener( 'mousemove', onMouseMove, false );
686 | document.addEventListener( 'mouseup', onMouseUp, false );
687 |
688 | scope.dispatchEvent( startEvent );
689 |
690 | }
691 |
692 | }
693 |
694 | function onMouseMove( event ) {
695 |
696 | if ( scope.enabled === false ) return;
697 |
698 | event.preventDefault();
699 |
700 | if ( state === STATE.ROTATE ) {
701 |
702 | if ( scope.enableRotate === false ) return;
703 |
704 | handleMouseMoveRotate( event );
705 |
706 | } else if ( state === STATE.DOLLY ) {
707 |
708 | if ( scope.enableZoom === false ) return;
709 |
710 | handleMouseMoveDolly( event );
711 |
712 | } else if ( state === STATE.PAN ) {
713 |
714 | if ( scope.enablePan === false ) return;
715 |
716 | handleMouseMovePan( event );
717 |
718 | }
719 |
720 | }
721 |
722 | function onMouseUp( event ) {
723 |
724 | if ( scope.enabled === false ) return;
725 |
726 | handleMouseUp( event );
727 |
728 | document.removeEventListener( 'mousemove', onMouseMove, false );
729 | document.removeEventListener( 'mouseup', onMouseUp, false );
730 |
731 | scope.dispatchEvent( endEvent );
732 |
733 | state = STATE.NONE;
734 |
735 | }
736 |
737 | function onMouseWheel( event ) {
738 |
739 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
740 |
741 | event.preventDefault();
742 | event.stopPropagation();
743 |
744 | handleMouseWheel( event );
745 |
746 | scope.dispatchEvent( startEvent ); // not sure why these are here...
747 | scope.dispatchEvent( endEvent );
748 |
749 | }
750 |
751 | function onKeyDown( event ) {
752 |
753 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
754 |
755 | handleKeyDown( event );
756 |
757 | }
758 |
759 | function onTouchStart( event ) {
760 |
761 | if ( scope.enabled === false ) return;
762 |
763 | switch ( event.touches.length ) {
764 |
765 | case 1: // one-fingered touch: rotate
766 |
767 | if ( scope.enableRotate === false ) return;
768 |
769 | handleTouchStartRotate( event );
770 |
771 | state = STATE.TOUCH_ROTATE;
772 |
773 | break;
774 |
775 | case 2: // two-fingered touch: dolly
776 |
777 | if ( scope.enableZoom === false ) return;
778 |
779 | handleTouchStartDolly( event );
780 |
781 | state = STATE.TOUCH_DOLLY;
782 |
783 | break;
784 |
785 | case 3: // three-fingered touch: pan
786 |
787 | if ( scope.enablePan === false ) return;
788 |
789 | handleTouchStartPan( event );
790 |
791 | state = STATE.TOUCH_PAN;
792 |
793 | break;
794 |
795 | default:
796 |
797 | state = STATE.NONE;
798 |
799 | }
800 |
801 | if ( state !== STATE.NONE ) {
802 |
803 | scope.dispatchEvent( startEvent );
804 |
805 | }
806 |
807 | }
808 |
809 | function onTouchMove( event ) {
810 |
811 | if ( scope.enabled === false ) return;
812 |
813 | event.preventDefault();
814 | event.stopPropagation();
815 |
816 | switch ( event.touches.length ) {
817 |
818 | case 1: // one-fingered touch: rotate
819 |
820 | if ( scope.enableRotate === false ) return;
821 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
822 |
823 | handleTouchMoveRotate( event );
824 |
825 | break;
826 |
827 | case 2: // two-fingered touch: dolly
828 |
829 | if ( scope.enableZoom === false ) return;
830 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
831 |
832 | handleTouchMoveDolly( event );
833 |
834 | break;
835 |
836 | case 3: // three-fingered touch: pan
837 |
838 | if ( scope.enablePan === false ) return;
839 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
840 |
841 | handleTouchMovePan( event );
842 |
843 | break;
844 |
845 | default:
846 |
847 | state = STATE.NONE;
848 |
849 | }
850 |
851 | }
852 |
853 | function onTouchEnd( event ) {
854 |
855 | if ( scope.enabled === false ) return;
856 |
857 | handleTouchEnd( event );
858 |
859 | scope.dispatchEvent( endEvent );
860 |
861 | state = STATE.NONE;
862 |
863 | }
864 |
865 | function onContextMenu( event ) {
866 |
867 | event.preventDefault();
868 |
869 | }
870 |
871 | //
872 |
873 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
874 |
875 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
876 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
877 |
878 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
879 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
880 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
881 |
882 | window.addEventListener( 'keydown', onKeyDown, false );
883 |
884 | // force an update at start
885 |
886 | this.update();
887 |
888 | };
889 |
890 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
891 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
892 |
893 | Object.defineProperties( THREE.OrbitControls.prototype, {
894 |
895 | center: {
896 |
897 | get: function () {
898 |
899 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
900 | return this.target;
901 |
902 | }
903 |
904 | },
905 |
906 | // backward compatibility
907 |
908 | noZoom: {
909 |
910 | get: function () {
911 |
912 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
913 | return ! this.enableZoom;
914 |
915 | },
916 |
917 | set: function ( value ) {
918 |
919 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
920 | this.enableZoom = ! value;
921 |
922 | }
923 |
924 | },
925 |
926 | noRotate: {
927 |
928 | get: function () {
929 |
930 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
931 | return ! this.enableRotate;
932 |
933 | },
934 |
935 | set: function ( value ) {
936 |
937 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
938 | this.enableRotate = ! value;
939 |
940 | }
941 |
942 | },
943 |
944 | noPan: {
945 |
946 | get: function () {
947 |
948 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
949 | return ! this.enablePan;
950 |
951 | },
952 |
953 | set: function ( value ) {
954 |
955 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
956 | this.enablePan = ! value;
957 |
958 | }
959 |
960 | },
961 |
962 | noKeys: {
963 |
964 | get: function () {
965 |
966 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
967 | return ! this.enableKeys;
968 |
969 | },
970 |
971 | set: function ( value ) {
972 |
973 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
974 | this.enableKeys = ! value;
975 |
976 | }
977 |
978 | },
979 |
980 | staticMoving : {
981 |
982 | get: function () {
983 |
984 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
985 | return ! this.enableDamping;
986 |
987 | },
988 |
989 | set: function ( value ) {
990 |
991 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
992 | this.enableDamping = ! value;
993 |
994 | }
995 |
996 | },
997 |
998 | dynamicDampingFactor : {
999 |
1000 | get: function () {
1001 |
1002 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1003 | return this.dampingFactor;
1004 |
1005 | },
1006 |
1007 | set: function ( value ) {
1008 |
1009 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1010 | this.dampingFactor = value;
1011 |
1012 | }
1013 |
1014 | }
1015 |
1016 | } );
1017 |
--------------------------------------------------------------------------------
/dist_github/js/stats.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for Stats.js 0.16.0
2 | // Project: http://github.com/mrdoob/stats.js
3 | // Definitions by: Gregory Dalton , Harm Berntsen
4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5 |
6 | declare class Stats {
7 | REVISION: number;
8 | domElement: HTMLDivElement;
9 |
10 | /**
11 | * @param value 0:fps, 1: ms, 2: mb, 3+: custom
12 | */
13 | showPanel(value: number): void;
14 | begin(): void;
15 | end(): number;
16 | update(): void;
17 | }
18 |
19 | declare module "stats.js" {
20 | export = Stats;
21 | }
22 |
--------------------------------------------------------------------------------
/dist_github/shader/fragmentFlameShader.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | varying float noise;
3 | uniform vec3 colLight;
4 | uniform vec3 colNormal;
5 | uniform vec3 colDark;
6 | uniform float opacity;
7 |
8 | vec3 blend( vec3 cola, vec3 colb, float percent ) {
9 | return vec3(
10 | cola.r + (colb.r - cola.r) * percent,
11 | cola.g + (colb.g - cola.g) * percent,
12 | cola.b + (colb.b - cola.b) * percent
13 | );
14 | }
15 |
16 | void main() {
17 |
18 | vec3 col;
19 | float range = 1.0 * noise;
20 |
21 | if(range > .6) col = colDark;
22 | else if(range > .4) col = blend(colNormal, colDark, (range - .4) / .2);
23 | else col = blend(colLight, colNormal, range / .4);
24 |
25 | gl_FragColor = vec4( col, opacity );
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/dist_github/shader/fragmentParticleShader.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 color;
2 | uniform sampler2D texture;
3 | varying vec3 vColor;
4 |
5 | void main() {
6 | gl_FragColor = vec4( vColor, 1.0 );
7 | gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
8 | }
--------------------------------------------------------------------------------
/dist_github/shader/vertexFlameShader.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // GLSL textureless classic 3D noise "cnoise",
3 | // with an RSL-style periodic variant "pnoise".
4 | // Author: Stefan Gustavson (stefan.gustavson@liu.se)
5 | // Version: 2011-10-11
6 | //
7 | // Many thanks to Ian McEwan of Ashima Arts for the
8 | // ideas for permutation and gradient selection.
9 | //
10 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved.
11 | // Distributed under the MIT license. See LICENSE file.
12 | // https://github.com/ashima/webgl-noise
13 | //
14 |
15 | vec3 mod289(vec3 x)
16 | {
17 | return x - floor(x * (1.0 / 289.0)) * 289.0;
18 | }
19 |
20 | vec4 mod289(vec4 x)
21 | {
22 | return x - floor(x * (1.0 / 289.0)) * 289.0;
23 | }
24 |
25 | vec4 permute(vec4 x)
26 | {
27 | return mod289(((x*34.0)+1.0)*x);
28 | }
29 |
30 | vec4 taylorInvSqrt(vec4 r)
31 | {
32 | return 1.79284291400159 - 0.85373472095314 * r;
33 | }
34 |
35 | vec3 fade(vec3 t) {
36 | return t*t*t*(t*(t*6.0-15.0)+10.0);
37 | }
38 |
39 | // Classic Perlin noise
40 | float cnoise(vec3 P)
41 | {
42 | vec3 Pi0 = floor(P); // Integer part for indexing
43 | vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
44 | Pi0 = mod289(Pi0);
45 | Pi1 = mod289(Pi1);
46 | vec3 Pf0 = fract(P); // Fractional part for interpolation
47 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
48 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
49 | vec4 iy = vec4(Pi0.yy, Pi1.yy);
50 | vec4 iz0 = Pi0.zzzz;
51 | vec4 iz1 = Pi1.zzzz;
52 |
53 | vec4 ixy = permute(permute(ix) + iy);
54 | vec4 ixy0 = permute(ixy + iz0);
55 | vec4 ixy1 = permute(ixy + iz1);
56 |
57 | vec4 gx0 = ixy0 * (1.0 / 7.0);
58 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
59 | gx0 = fract(gx0);
60 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
61 | vec4 sz0 = step(gz0, vec4(0.0));
62 | gx0 -= sz0 * (step(0.0, gx0) - 0.5);
63 | gy0 -= sz0 * (step(0.0, gy0) - 0.5);
64 |
65 | vec4 gx1 = ixy1 * (1.0 / 7.0);
66 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
67 | gx1 = fract(gx1);
68 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
69 | vec4 sz1 = step(gz1, vec4(0.0));
70 | gx1 -= sz1 * (step(0.0, gx1) - 0.5);
71 | gy1 -= sz1 * (step(0.0, gy1) - 0.5);
72 |
73 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
74 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
75 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
76 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
77 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
78 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
79 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
80 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
81 |
82 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
83 | g000 *= norm0.x;
84 | g010 *= norm0.y;
85 | g100 *= norm0.z;
86 | g110 *= norm0.w;
87 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
88 | g001 *= norm1.x;
89 | g011 *= norm1.y;
90 | g101 *= norm1.z;
91 | g111 *= norm1.w;
92 |
93 | float n000 = dot(g000, Pf0);
94 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
95 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
96 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
97 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
98 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
99 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
100 | float n111 = dot(g111, Pf1);
101 |
102 | vec3 fade_xyz = fade(Pf0);
103 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
104 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
105 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
106 | return 2.2 * n_xyz;
107 | }
108 |
109 | // Classic Perlin noise, periodic variant
110 | float pnoise(vec3 P, vec3 rep)
111 | {
112 | vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
113 | vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
114 | Pi0 = mod289(Pi0);
115 | Pi1 = mod289(Pi1);
116 | vec3 Pf0 = fract(P); // Fractional part for interpolation
117 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
118 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
119 | vec4 iy = vec4(Pi0.yy, Pi1.yy);
120 | vec4 iz0 = Pi0.zzzz;
121 | vec4 iz1 = Pi1.zzzz;
122 |
123 | vec4 ixy = permute(permute(ix) + iy);
124 | vec4 ixy0 = permute(ixy + iz0);
125 | vec4 ixy1 = permute(ixy + iz1);
126 |
127 | vec4 gx0 = ixy0 * (1.0 / 7.0);
128 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
129 | gx0 = fract(gx0);
130 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
131 | vec4 sz0 = step(gz0, vec4(0.0));
132 | gx0 -= sz0 * (step(0.0, gx0) - 0.5);
133 | gy0 -= sz0 * (step(0.0, gy0) - 0.5);
134 |
135 | vec4 gx1 = ixy1 * (1.0 / 7.0);
136 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
137 | gx1 = fract(gx1);
138 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
139 | vec4 sz1 = step(gz1, vec4(0.0));
140 | gx1 -= sz1 * (step(0.0, gx1) - 0.5);
141 | gy1 -= sz1 * (step(0.0, gy1) - 0.5);
142 |
143 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
144 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
145 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
146 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
147 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
148 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
149 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
150 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
151 |
152 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
153 | g000 *= norm0.x;
154 | g010 *= norm0.y;
155 | g100 *= norm0.z;
156 | g110 *= norm0.w;
157 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
158 | g001 *= norm1.x;
159 | g011 *= norm1.y;
160 | g101 *= norm1.z;
161 | g111 *= norm1.w;
162 |
163 | float n000 = dot(g000, Pf0);
164 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
165 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
166 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
167 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
168 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
169 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
170 | float n111 = dot(g111, Pf1);
171 |
172 | vec3 fade_xyz = fade(Pf0);
173 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
174 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
175 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
176 | return 2.2 * n_xyz;
177 | }
178 |
179 | // Include the Ashima code here!
180 |
181 | varying float noise;
182 | uniform float time;
183 | uniform float seed;
184 | uniform float detail;
185 |
186 | float turbulence( vec3 p ) {
187 | float w = 100.0;
188 | float t = -.5;
189 | for (float f = 1.0 ; f <= 10.0 ; f++ ){
190 | float power = pow( 2.0, f );
191 | t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );
192 | }
193 | return t;
194 | }
195 |
196 | void main() {
197 |
198 | noise = detail * -.10 * turbulence( 0.6 * normal + time + seed );
199 | float b = 2.0 * pnoise( 0.05 * position + vec3( 2.0 * time ), vec3( 100.0 ) );
200 | float displacement = - 10. * noise + b;
201 |
202 | vec3 newPosition = position + normal * displacement;
203 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/dist_github/shader/vertexParticleShader.glsl:
--------------------------------------------------------------------------------
1 | attribute float size;
2 | attribute vec3 customColor;
3 | varying vec3 vColor;
4 |
5 | void main() {
6 | vColor = customColor;
7 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
8 | float cameraDist = length(mvPosition.xyz - position.xyz);
9 | gl_PointSize = size * 300.0 / cameraDist;
10 | gl_Position = projectionMatrix * mvPosition;
11 | }
--------------------------------------------------------------------------------
/fire_simulation_report.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/fire_simulation_report.pdf
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Explosion Simulator
6 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "explosion-simulator",
3 | "version": "1.0.0",
4 | "description": "Simulation of Explosion",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/neungkl/explosion-simulator.git"
12 | },
13 | "author": "Kosate Limpongsa",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/neungkl/explosion-simulator/issues"
17 | },
18 | "homepage": "https://github.com/neungkl/explosion-simulator#readme",
19 | "dependencies": {
20 | "dat-gui": "^0.5.0",
21 | "jquery": "^3.1.1",
22 | "three": "^0.82.1"
23 | },
24 | "devDependencies": {
25 | "@types/dat-gui": "^0.6.1",
26 | "@types/es6-promise": "0.0.32",
27 | "@types/jquery": "^2.0.34",
28 | "@types/three": "0.0.27",
29 | "grunt": "^1.0.1",
30 | "grunt-contrib-copy": "^1.0.0",
31 | "grunt-contrib-watch": "^1.0.0",
32 | "grunt-ts": "^6.0.0-beta.3",
33 | "grunt-webpack": "^1.0.18",
34 | "typescript": "^2.0.10",
35 | "webpack": "^1.14.0",
36 | "webpack-dev-server": "^1.16.2"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/animation/Interpolation.ts:
--------------------------------------------------------------------------------
1 | export class Interpolation {
2 | public static easeOutCubic(percent, start, end) {
3 | let t = percent;
4 | t--;
5 | return (end - start) * (t*t*t + 1) + start;
6 | }
7 |
8 | public static easeInCubic(percent, start, end) {
9 | let t = percent;
10 | return (end - start)*t*t*t + start;
11 | };
12 |
13 | public static easeOutSine(percent, start, end) {
14 | return (end - start) * Math.sin(percent * (Math.PI/2)) + start;
15 | };
16 | }
--------------------------------------------------------------------------------
/src/animation/explosionController.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import { FlameSphere } from "../object/flameSphere";
4 | import { FlameAnimation } from "./flameAnimation";
5 | import { FlareParticle } from "../object/flareParticle";
6 | import { Interpolation } from "./interpolation";
7 | import { Controller } from "../controller";
8 | import { Renderer } from "../renderer";
9 |
10 | class ExplosionController {
11 |
12 | private static objs: FlameAnimation[];
13 | private static objectPool: number[];
14 | private static spawnTime;
15 |
16 | private static flareParticle: FlareParticle;
17 |
18 | private static currentCol = {};
19 |
20 | public static init() {
21 |
22 | this.objs = [];
23 | this.objectPool = [];
24 | this.spawnTime = 0;
25 | this.flareParticle = new FlareParticle();
26 |
27 | this.spawnNewFlame();
28 |
29 | Controller.attachEvent(Controller.DARK_COLOR, (value) => {
30 | for(let i=0; i {
37 | for(let i=0; i {
44 | for(let i=0; i 0) {
68 | i = this.objectPool.shift();
69 | this.objs[i].instance.getMesh().visible = true;
70 | this.objs[i].instance.setColor(this.currentCol);
71 | this.objs[i].reset();
72 | } else {
73 | let obj = new FlameAnimation(
74 | new FlameSphere(Math.random() * 5 + 8),
75 | Math.random() * 7 - 4,
76 | Math.random() * 7 - 4,
77 | Math.random() * 0.4 + 0.35,
78 | Math.random() * 0.4 + 0.3
79 | );
80 | obj.instance.setColor(this.currentCol);
81 | this.objs.push(obj);
82 | Renderer.addToScene(this.objs[i].instance.getMesh());
83 | }
84 | }
85 |
86 | public static update(deltaTime: number) {
87 |
88 | let timeScale = Controller.getParams().TimeScale;
89 | this.spawnTime += deltaTime * timeScale;
90 | if(this.spawnTime > 200) {
91 | while(this.spawnTime > 200) this.spawnTime -= 200;
92 | this.spawnNewFlame();
93 | }
94 |
95 | for(let i=0; i FlameAnimation.BEFORE_INTERVAL) {
136 | cTime -= FlameAnimation.BEFORE_INTERVAL;
137 | this.currentState = FlameAnimation.STATE_SPAWN;
138 | }
139 | } else if (this.currentState == FlameAnimation.STATE_SPAWN) {
140 | if (cTime > FlameAnimation.SPAWN_INTERVAL) {
141 | cTime -= FlameAnimation.SPAWN_INTERVAL;
142 | this.posX = -1;
143 | this.currentState = FlameAnimation.STATE_SPAWN_DOWN;
144 | }
145 | } else if (this.currentState == FlameAnimation.STATE_SPAWN_DOWN) {
146 | if (cTime > FlameAnimation.SPAWN_DOWN_INTERVAL) {
147 | cTime -= FlameAnimation.SPAWN_DOWN_INTERVAL;
148 | this.currentState = FlameAnimation.STATE_FLOATING;
149 | }
150 | } else if (this.currentState == FlameAnimation.STATE_FLOATING) {
151 | if (cTime > FlameAnimation.FLOATING_INTERVAL) {
152 | this.randFlyX += Math.random() * 0.2;
153 | this.randFlyZ += Math.random() * 0.2;
154 | cTime -= FlameAnimation.FLOATING_INTERVAL;
155 | this.posX = -1;
156 | this.currentState = FlameAnimation.STATE_IDLE
157 | }
158 | } else if (this.currentState == FlameAnimation.STATE_IDLE) {
159 | if (cTime > FlameAnimation.IDLE_INTERVAL) {
160 | this.isObjDie = true;
161 | }
162 | }
163 |
164 | this.currentTime = cTime;
165 | }
166 |
167 | public update(deltaTime: number) {
168 |
169 | if (this.isObjDie) return;
170 |
171 | let mesh = this.instance.getMesh();
172 | let timeScale = Controller.getParams().TimeScale;
173 |
174 | this.updateState(deltaTime * timeScale);
175 | this.timeCount += deltaTime * timeScale;
176 |
177 | if (this.currentState == FlameAnimation.STATE_SPAWN) {
178 |
179 | let t = this.currentTime / FlameAnimation.SPAWN_INTERVAL;
180 |
181 | let t2 = this.currentTime / (FlameAnimation.SPAWN_INTERVAL + FlameAnimation.SPAWN_DOWN_INTERVAL);
182 |
183 | mesh.position.set(
184 | this.distX * t2,
185 | mesh.position.y + t * 0.4 * this.yRatio * timeScale,
186 | this.distZ * t2
187 | );
188 |
189 | let scale = t;
190 | mesh.scale.set(scale, scale, scale);
191 | }
192 | else if (this.currentState == FlameAnimation.STATE_SPAWN_DOWN) {
193 |
194 | let t2 = (this.currentTime + FlameAnimation.SPAWN_INTERVAL) /
195 | (FlameAnimation.SPAWN_INTERVAL + FlameAnimation.SPAWN_DOWN_INTERVAL);
196 |
197 | mesh.position.set(
198 | this.distX * t2,
199 | mesh.position.y +
200 | (0.6 * timeScale *
201 | (1 - this.currentTime / FlameAnimation.SPAWN_DOWN_INTERVAL) +
202 | 0.2 * timeScale) * this.yRatio,
203 | this.distZ * t2
204 | );
205 | }
206 | else if (this.currentState == FlameAnimation.STATE_FLOATING) {
207 | if (this.posX == -1) {
208 | this.posX = mesh.position.x;
209 | this.posY = mesh.position.y;
210 | this.posZ = mesh.position.z;
211 | this.instance.setFlowRatio(0.5);
212 | }
213 | mesh.position.set(
214 | mesh.position.x + this.randFlyX * timeScale,
215 | mesh.position.y + 0.2 * timeScale,
216 | mesh.position.z + this.randFlyZ * timeScale
217 | );
218 |
219 | let scale = mesh.scale.x + 0.003 * timeScale;
220 | mesh.scale.set(scale, scale, scale);
221 | }
222 | else if (this.currentState == FlameAnimation.STATE_IDLE) {
223 | if (this.posX == -1) {
224 | this.posX = mesh.position.x;
225 | this.posY = mesh.position.y;
226 | this.posZ = mesh.position.z;
227 | this.instance.setFlowRatio(0.2);
228 | }
229 | mesh.position.setY(this.posY + this.currentTime / 100);
230 |
231 | if (this.currentTime > FlameAnimation.IDLE_INTERVAL - 5000) {
232 | this.instance.setOpacity(1 - (this.currentTime - (FlameAnimation.IDLE_INTERVAL - 5000)) / 5000);
233 | }
234 |
235 | let scale = mesh.scale.x + 0.002 * timeScale;
236 | mesh.scale.set(scale, scale, scale);
237 | }
238 |
239 | this.setColor();
240 | this.instance.update(deltaTime * timeScale * this.animationTimeRatio);
241 | }
242 |
243 | public isDie(): boolean {
244 | return this.isObjDie;
245 | }
246 |
247 | public inPolling(): boolean {
248 | return this.isInPooling;
249 | }
250 | public setInPolling(val: boolean): void {
251 | this.isInPooling = val;
252 | }
253 | }
254 |
255 | export { FlameAnimation }
--------------------------------------------------------------------------------
/src/assetsManager.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | class AssetsManager {
4 |
5 | public static instance: AssetsManager = new AssetsManager();
6 |
7 | private vertexFlameShader = null;
8 | private fragmentFlameShader = null;
9 | private vertexParticleShader = null;
10 | private fragmentParticleShader = null;
11 |
12 | constructor() {
13 | $.ajax({
14 | url: './dist/shader/vertexFlameShader.glsl',
15 | async: false,
16 | success: (vs) => {
17 | this.vertexFlameShader = vs;
18 | }
19 | });
20 |
21 | $.ajax({
22 | url: './dist/shader/fragmentFlameShader.glsl',
23 | async: false,
24 | success: (fs) => {
25 | this.fragmentFlameShader = fs;
26 | }
27 | });
28 |
29 | $.ajax({
30 | url: './dist/shader/vertexParticleShader.glsl',
31 | async: false,
32 | success: (fs) => {
33 | this.vertexParticleShader = fs;
34 | }
35 | });
36 |
37 | $.ajax({
38 | url: './dist/shader/fragmentParticleShader.glsl',
39 | async: false,
40 | success: (fs) => {
41 | this.fragmentParticleShader = fs;
42 | }
43 | });
44 | }
45 |
46 | public getTexture() {
47 | return {
48 | vertexFlameShader: this.vertexFlameShader,
49 | fragmentFlameShader: this.fragmentFlameShader,
50 | vectexParticleShader: this.vertexParticleShader,
51 | fragmentParticleShader: this.fragmentParticleShader
52 | };
53 | }
54 | }
55 |
56 | export { AssetsManager };
57 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | class Constants {
2 | public static MAXIMUM_LIVE_TIME = 20000;
3 | }
4 |
5 | export { Constants }
--------------------------------------------------------------------------------
/src/controller.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | class Controller {
4 |
5 | public static DARK_COLOR: number = 0;
6 | public static NORMAL_COLOR: number = 1;
7 | public static LIGHT_COLOR: number = 2;
8 | public static LIGHT_COLOR_2: number = 3;
9 | public static DARK_COLOR_2: number = 4;
10 | public static RESTART: number = 5;
11 | public static TIME_SCALE: number = 6;
12 | public static PARTICLE_SPREAD: number = 7;
13 | public static PARTICLE_COLOR: number = 8;
14 | public static INVERTED_BACKGROUND: number = 9;
15 | public static SHOW_GRID: number = 10;
16 |
17 | private static gui;
18 | private static eventListener;
19 |
20 | private static params;
21 |
22 | public static init() {
23 |
24 | this.eventListener = [];
25 |
26 | let ControlParam = function() {
27 |
28 | this.LightColor2 = '#ff8700';
29 | this.LightColor = '#f7f342';
30 | this.NormalColor = '#f7a90e';
31 | this.DarkColor2 = '#ff9800';
32 | this.GreyColor = '#3c342f';
33 | this.DarkColor = "#181818";
34 |
35 | this.TimeScale = 3;
36 |
37 | this.ParticleSpread = 1;
38 | this.ParticleColor = '#ffb400';
39 |
40 | this.InvertedBackground = false;
41 | this.ShowGrid = true;
42 |
43 | this.restart = function() { }
44 | };
45 |
46 | let params = new ControlParam();
47 | var gui = new dat.GUI();
48 |
49 | var f1 = gui.addFolder('Spawn Color');
50 | this.eventListener[Controller.DARK_COLOR] = f1.addColor(params, 'DarkColor');
51 | this.eventListener[Controller.DARK_COLOR_2] = f1.addColor(params, 'GreyColor');
52 | this.eventListener[Controller.DARK_COLOR_2] = f1.addColor(params, 'DarkColor2');
53 | this.eventListener[Controller.NORMAL_COLOR] = f1.addColor(params, 'NormalColor');
54 | this.eventListener[Controller.LIGHT_COLOR] = f1.addColor(params, 'LightColor');
55 | this.eventListener[Controller.LIGHT_COLOR_2] = f1.addColor(params, 'LightColor2');
56 | f1.open();
57 |
58 | var f2 = gui.addFolder('Flare Particle')
59 | this.eventListener[Controller.PARTICLE_SPREAD] = f2.add(params, 'ParticleSpread', 0, 2);
60 | this.eventListener[Controller.PARTICLE_COLOR] = f2.addColor(params, 'ParticleColor');
61 | f2.open();
62 |
63 | this.eventListener[Controller.INVERTED_BACKGROUND] = gui.add(params, 'InvertedBackground');
64 | this.eventListener[Controller.SHOW_GRID] = gui.add(params, 'ShowGrid');
65 | this.eventListener[Controller.TIME_SCALE] = gui.add(params, 'TimeScale', 0, 10);
66 |
67 | gui.add(params, 'restart');
68 |
69 | this.gui = gui;
70 | this.params = params;
71 | }
72 |
73 | public static getParams() {
74 | return this.params;
75 | }
76 |
77 | public static setRestartFunc(func: Function) {
78 | this.params.restart = func;
79 | }
80 |
81 | public static attachEvent(key, callback) {
82 | this.eventListener[key].onChange(callback);
83 | }
84 | }
85 |
86 | export { Controller }
--------------------------------------------------------------------------------
/src/images/circle-particle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/src/images/circle-particle.png
--------------------------------------------------------------------------------
/src/images/spark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/neungkl/fire-simulation/52b75e8685694a9cb320be7d827ba2b1b546002f/src/images/spark.png
--------------------------------------------------------------------------------
/src/js/OrbitControl.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 |
9 | // This set of controls performs orbiting, dollying (zooming), and panning.
10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
11 | //
12 | // Orbit - left mouse / touch: one finger move
13 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
14 | // Pan - right mouse, or arrow keys / touch: three finter swipe
15 |
16 | THREE.OrbitControls = function ( object, domElement ) {
17 |
18 | this.object = object;
19 |
20 | this.domElement = ( domElement !== undefined ) ? domElement : document;
21 |
22 | // Set to false to disable this control
23 | this.enabled = true;
24 |
25 | // "target" sets the location of focus, where the object orbits around
26 | this.target = new THREE.Vector3();
27 |
28 | // How far you can dolly in and out ( PerspectiveCamera only )
29 | this.minDistance = 0;
30 | this.maxDistance = Infinity;
31 |
32 | // How far you can zoom in and out ( OrthographicCamera only )
33 | this.minZoom = 0;
34 | this.maxZoom = Infinity;
35 |
36 | // How far you can orbit vertically, upper and lower limits.
37 | // Range is 0 to Math.PI radians.
38 | this.minPolarAngle = 0; // radians
39 | this.maxPolarAngle = Math.PI; // radians
40 |
41 | // How far you can orbit horizontally, upper and lower limits.
42 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
43 | this.minAzimuthAngle = - Infinity; // radians
44 | this.maxAzimuthAngle = Infinity; // radians
45 |
46 | // Set to true to enable damping (inertia)
47 | // If damping is enabled, you must call controls.update() in your animation loop
48 | this.enableDamping = false;
49 | this.dampingFactor = 0.25;
50 |
51 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
52 | // Set to false to disable zooming
53 | this.enableZoom = true;
54 | this.zoomSpeed = 1.0;
55 |
56 | // Set to false to disable rotating
57 | this.enableRotate = true;
58 | this.rotateSpeed = 1.0;
59 |
60 | // Set to false to disable panning
61 | this.enablePan = true;
62 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
63 |
64 | // Set to true to automatically rotate around the target
65 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
66 | this.autoRotate = false;
67 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
68 |
69 | // Set to false to disable use of the keys
70 | this.enableKeys = true;
71 |
72 | // The four arrow keys
73 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
74 |
75 | // Mouse buttons
76 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
77 |
78 | // for reset
79 | this.target0 = this.target.clone();
80 | this.position0 = this.object.position.clone();
81 | this.zoom0 = this.object.zoom;
82 |
83 | //
84 | // public methods
85 | //
86 |
87 | this.getPolarAngle = function () {
88 |
89 | return spherical.phi;
90 |
91 | };
92 |
93 | this.getAzimuthalAngle = function () {
94 |
95 | return spherical.theta;
96 |
97 | };
98 |
99 | this.reset = function () {
100 |
101 | scope.target.copy( scope.target0 );
102 | scope.object.position.copy( scope.position0 );
103 | scope.object.zoom = scope.zoom0;
104 |
105 | scope.object.updateProjectionMatrix();
106 | scope.dispatchEvent( changeEvent );
107 |
108 | scope.update();
109 |
110 | state = STATE.NONE;
111 |
112 | };
113 |
114 | // this method is exposed, but perhaps it would be better if we can make it private...
115 | this.update = function() {
116 |
117 | var offset = new THREE.Vector3();
118 |
119 | // so camera.up is the orbit axis
120 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
121 | var quatInverse = quat.clone().inverse();
122 |
123 | var lastPosition = new THREE.Vector3();
124 | var lastQuaternion = new THREE.Quaternion();
125 |
126 | return function update () {
127 |
128 | var position = scope.object.position;
129 |
130 | offset.copy( position ).sub( scope.target );
131 |
132 | // rotate offset to "y-axis-is-up" space
133 | offset.applyQuaternion( quat );
134 |
135 | // angle from z-axis around y-axis
136 | spherical.setFromVector3( offset );
137 |
138 | if ( scope.autoRotate && state === STATE.NONE ) {
139 |
140 | rotateLeft( getAutoRotationAngle() );
141 |
142 | }
143 |
144 | spherical.theta += sphericalDelta.theta;
145 | spherical.phi += sphericalDelta.phi;
146 |
147 | // restrict theta to be between desired limits
148 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
149 |
150 | // restrict phi to be between desired limits
151 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
152 |
153 | spherical.makeSafe();
154 |
155 |
156 | spherical.radius *= scale;
157 |
158 | // restrict radius to be between desired limits
159 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
160 |
161 | // move target to panned location
162 | scope.target.add( panOffset );
163 |
164 | offset.setFromSpherical( spherical );
165 |
166 | // rotate offset back to "camera-up-vector-is-up" space
167 | offset.applyQuaternion( quatInverse );
168 |
169 | position.copy( scope.target ).add( offset );
170 |
171 | scope.object.lookAt( scope.target );
172 |
173 | if ( scope.enableDamping === true ) {
174 |
175 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
176 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
177 |
178 | } else {
179 |
180 | sphericalDelta.set( 0, 0, 0 );
181 |
182 | }
183 |
184 | scale = 1;
185 | panOffset.set( 0, 0, 0 );
186 |
187 | // update condition is:
188 | // min(camera displacement, camera rotation in radians)^2 > EPS
189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
190 |
191 | if ( zoomChanged ||
192 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
194 |
195 | scope.dispatchEvent( changeEvent );
196 |
197 | lastPosition.copy( scope.object.position );
198 | lastQuaternion.copy( scope.object.quaternion );
199 | zoomChanged = false;
200 |
201 | return true;
202 |
203 | }
204 |
205 | return false;
206 |
207 | };
208 |
209 | }();
210 |
211 | this.dispose = function() {
212 |
213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
215 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
216 |
217 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
218 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
219 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
220 |
221 | document.removeEventListener( 'mousemove', onMouseMove, false );
222 | document.removeEventListener( 'mouseup', onMouseUp, false );
223 |
224 | window.removeEventListener( 'keydown', onKeyDown, false );
225 |
226 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
227 |
228 | };
229 |
230 | //
231 | // internals
232 | //
233 |
234 | var scope = this;
235 |
236 | var changeEvent = { type: 'change' };
237 | var startEvent = { type: 'start' };
238 | var endEvent = { type: 'end' };
239 |
240 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
241 |
242 | var state = STATE.NONE;
243 |
244 | var EPS = 0.000001;
245 |
246 | // current position in spherical coordinates
247 | var spherical = new THREE.Spherical();
248 | var sphericalDelta = new THREE.Spherical();
249 |
250 | var scale = 1;
251 | var panOffset = new THREE.Vector3();
252 | var zoomChanged = false;
253 |
254 | var rotateStart = new THREE.Vector2();
255 | var rotateEnd = new THREE.Vector2();
256 | var rotateDelta = new THREE.Vector2();
257 |
258 | var panStart = new THREE.Vector2();
259 | var panEnd = new THREE.Vector2();
260 | var panDelta = new THREE.Vector2();
261 |
262 | var dollyStart = new THREE.Vector2();
263 | var dollyEnd = new THREE.Vector2();
264 | var dollyDelta = new THREE.Vector2();
265 |
266 | function getAutoRotationAngle() {
267 |
268 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
269 |
270 | }
271 |
272 | function getZoomScale() {
273 |
274 | return Math.pow( 0.95, scope.zoomSpeed );
275 |
276 | }
277 |
278 | function rotateLeft( angle ) {
279 |
280 | sphericalDelta.theta -= angle;
281 |
282 | }
283 |
284 | function rotateUp( angle ) {
285 |
286 | sphericalDelta.phi -= angle;
287 |
288 | }
289 |
290 | var panLeft = function() {
291 |
292 | var v = new THREE.Vector3();
293 |
294 | return function panLeft( distance, objectMatrix ) {
295 |
296 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
297 | v.multiplyScalar( - distance );
298 |
299 | panOffset.add( v );
300 |
301 | };
302 |
303 | }();
304 |
305 | var panUp = function() {
306 |
307 | var v = new THREE.Vector3();
308 |
309 | return function panUp( distance, objectMatrix ) {
310 |
311 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix
312 | v.multiplyScalar( distance );
313 |
314 | panOffset.add( v );
315 |
316 | };
317 |
318 | }();
319 |
320 | // deltaX and deltaY are in pixels; right and down are positive
321 | var pan = function() {
322 |
323 | var offset = new THREE.Vector3();
324 |
325 | return function pan ( deltaX, deltaY ) {
326 |
327 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
328 |
329 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
330 |
331 | // perspective
332 | var position = scope.object.position;
333 | offset.copy( position ).sub( scope.target );
334 | var targetDistance = offset.length();
335 |
336 | // half of the fov is center to top of screen
337 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
338 |
339 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
340 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
341 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
342 |
343 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
344 |
345 | // orthographic
346 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
347 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
348 |
349 | } else {
350 |
351 | // camera neither orthographic nor perspective
352 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
353 | scope.enablePan = false;
354 |
355 | }
356 |
357 | };
358 |
359 | }();
360 |
361 | function dollyIn( dollyScale ) {
362 |
363 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
364 |
365 | scale /= dollyScale;
366 |
367 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
368 |
369 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
370 | scope.object.updateProjectionMatrix();
371 | zoomChanged = true;
372 |
373 | } else {
374 |
375 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
376 | scope.enableZoom = false;
377 |
378 | }
379 |
380 | }
381 |
382 | function dollyOut( dollyScale ) {
383 |
384 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
385 |
386 | scale *= dollyScale;
387 |
388 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
389 |
390 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
391 | scope.object.updateProjectionMatrix();
392 | zoomChanged = true;
393 |
394 | } else {
395 |
396 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
397 | scope.enableZoom = false;
398 |
399 | }
400 |
401 | }
402 |
403 | //
404 | // event callbacks - update the object state
405 | //
406 |
407 | function handleMouseDownRotate( event ) {
408 |
409 | //console.log( 'handleMouseDownRotate' );
410 |
411 | rotateStart.set( event.clientX, event.clientY );
412 |
413 | }
414 |
415 | function handleMouseDownDolly( event ) {
416 |
417 | //console.log( 'handleMouseDownDolly' );
418 |
419 | dollyStart.set( event.clientX, event.clientY );
420 |
421 | }
422 |
423 | function handleMouseDownPan( event ) {
424 |
425 | //console.log( 'handleMouseDownPan' );
426 |
427 | panStart.set( event.clientX, event.clientY );
428 |
429 | }
430 |
431 | function handleMouseMoveRotate( event ) {
432 |
433 | //console.log( 'handleMouseMoveRotate' );
434 |
435 | rotateEnd.set( event.clientX, event.clientY );
436 | rotateDelta.subVectors( rotateEnd, rotateStart );
437 |
438 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
439 |
440 | // rotating across whole screen goes 360 degrees around
441 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
442 |
443 | // rotating up and down along whole screen attempts to go 360, but limited to 180
444 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
445 |
446 | rotateStart.copy( rotateEnd );
447 |
448 | scope.update();
449 |
450 | }
451 |
452 | function handleMouseMoveDolly( event ) {
453 |
454 | //console.log( 'handleMouseMoveDolly' );
455 |
456 | dollyEnd.set( event.clientX, event.clientY );
457 |
458 | dollyDelta.subVectors( dollyEnd, dollyStart );
459 |
460 | if ( dollyDelta.y > 0 ) {
461 |
462 | dollyIn( getZoomScale() );
463 |
464 | } else if ( dollyDelta.y < 0 ) {
465 |
466 | dollyOut( getZoomScale() );
467 |
468 | }
469 |
470 | dollyStart.copy( dollyEnd );
471 |
472 | scope.update();
473 |
474 | }
475 |
476 | function handleMouseMovePan( event ) {
477 |
478 | //console.log( 'handleMouseMovePan' );
479 |
480 | panEnd.set( event.clientX, event.clientY );
481 |
482 | panDelta.subVectors( panEnd, panStart );
483 |
484 | pan( panDelta.x, panDelta.y );
485 |
486 | panStart.copy( panEnd );
487 |
488 | scope.update();
489 |
490 | }
491 |
492 | function handleMouseUp( event ) {
493 |
494 | //console.log( 'handleMouseUp' );
495 |
496 | }
497 |
498 | function handleMouseWheel( event ) {
499 |
500 | //console.log( 'handleMouseWheel' );
501 |
502 | if ( event.deltaY < 0 ) {
503 |
504 | dollyOut( getZoomScale() );
505 |
506 | } else if ( event.deltaY > 0 ) {
507 |
508 | dollyIn( getZoomScale() );
509 |
510 | }
511 |
512 | scope.update();
513 |
514 | }
515 |
516 | function handleKeyDown( event ) {
517 |
518 | //console.log( 'handleKeyDown' );
519 |
520 | switch ( event.keyCode ) {
521 |
522 | case scope.keys.UP:
523 | pan( 0, scope.keyPanSpeed );
524 | scope.update();
525 | break;
526 |
527 | case scope.keys.BOTTOM:
528 | pan( 0, - scope.keyPanSpeed );
529 | scope.update();
530 | break;
531 |
532 | case scope.keys.LEFT:
533 | pan( scope.keyPanSpeed, 0 );
534 | scope.update();
535 | break;
536 |
537 | case scope.keys.RIGHT:
538 | pan( - scope.keyPanSpeed, 0 );
539 | scope.update();
540 | break;
541 |
542 | }
543 |
544 | }
545 |
546 | function handleTouchStartRotate( event ) {
547 |
548 | //console.log( 'handleTouchStartRotate' );
549 |
550 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
551 |
552 | }
553 |
554 | function handleTouchStartDolly( event ) {
555 |
556 | //console.log( 'handleTouchStartDolly' );
557 |
558 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
559 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
560 |
561 | var distance = Math.sqrt( dx * dx + dy * dy );
562 |
563 | dollyStart.set( 0, distance );
564 |
565 | }
566 |
567 | function handleTouchStartPan( event ) {
568 |
569 | //console.log( 'handleTouchStartPan' );
570 |
571 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
572 |
573 | }
574 |
575 | function handleTouchMoveRotate( event ) {
576 |
577 | //console.log( 'handleTouchMoveRotate' );
578 |
579 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
580 | rotateDelta.subVectors( rotateEnd, rotateStart );
581 |
582 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
583 |
584 | // rotating across whole screen goes 360 degrees around
585 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
586 |
587 | // rotating up and down along whole screen attempts to go 360, but limited to 180
588 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
589 |
590 | rotateStart.copy( rotateEnd );
591 |
592 | scope.update();
593 |
594 | }
595 |
596 | function handleTouchMoveDolly( event ) {
597 |
598 | //console.log( 'handleTouchMoveDolly' );
599 |
600 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
601 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
602 |
603 | var distance = Math.sqrt( dx * dx + dy * dy );
604 |
605 | dollyEnd.set( 0, distance );
606 |
607 | dollyDelta.subVectors( dollyEnd, dollyStart );
608 |
609 | if ( dollyDelta.y > 0 ) {
610 |
611 | dollyOut( getZoomScale() );
612 |
613 | } else if ( dollyDelta.y < 0 ) {
614 |
615 | dollyIn( getZoomScale() );
616 |
617 | }
618 |
619 | dollyStart.copy( dollyEnd );
620 |
621 | scope.update();
622 |
623 | }
624 |
625 | function handleTouchMovePan( event ) {
626 |
627 | //console.log( 'handleTouchMovePan' );
628 |
629 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
630 |
631 | panDelta.subVectors( panEnd, panStart );
632 |
633 | pan( panDelta.x, panDelta.y );
634 |
635 | panStart.copy( panEnd );
636 |
637 | scope.update();
638 |
639 | }
640 |
641 | function handleTouchEnd( event ) {
642 |
643 | //console.log( 'handleTouchEnd' );
644 |
645 | }
646 |
647 | //
648 | // event handlers - FSM: listen for events and reset state
649 | //
650 |
651 | function onMouseDown( event ) {
652 |
653 | if ( scope.enabled === false ) return;
654 |
655 | event.preventDefault();
656 |
657 | if ( event.button === scope.mouseButtons.ORBIT ) {
658 |
659 | if ( scope.enableRotate === false ) return;
660 |
661 | handleMouseDownRotate( event );
662 |
663 | state = STATE.ROTATE;
664 |
665 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
666 |
667 | if ( scope.enableZoom === false ) return;
668 |
669 | handleMouseDownDolly( event );
670 |
671 | state = STATE.DOLLY;
672 |
673 | } else if ( event.button === scope.mouseButtons.PAN ) {
674 |
675 | if ( scope.enablePan === false ) return;
676 |
677 | handleMouseDownPan( event );
678 |
679 | state = STATE.PAN;
680 |
681 | }
682 |
683 | if ( state !== STATE.NONE ) {
684 |
685 | document.addEventListener( 'mousemove', onMouseMove, false );
686 | document.addEventListener( 'mouseup', onMouseUp, false );
687 |
688 | scope.dispatchEvent( startEvent );
689 |
690 | }
691 |
692 | }
693 |
694 | function onMouseMove( event ) {
695 |
696 | if ( scope.enabled === false ) return;
697 |
698 | event.preventDefault();
699 |
700 | if ( state === STATE.ROTATE ) {
701 |
702 | if ( scope.enableRotate === false ) return;
703 |
704 | handleMouseMoveRotate( event );
705 |
706 | } else if ( state === STATE.DOLLY ) {
707 |
708 | if ( scope.enableZoom === false ) return;
709 |
710 | handleMouseMoveDolly( event );
711 |
712 | } else if ( state === STATE.PAN ) {
713 |
714 | if ( scope.enablePan === false ) return;
715 |
716 | handleMouseMovePan( event );
717 |
718 | }
719 |
720 | }
721 |
722 | function onMouseUp( event ) {
723 |
724 | if ( scope.enabled === false ) return;
725 |
726 | handleMouseUp( event );
727 |
728 | document.removeEventListener( 'mousemove', onMouseMove, false );
729 | document.removeEventListener( 'mouseup', onMouseUp, false );
730 |
731 | scope.dispatchEvent( endEvent );
732 |
733 | state = STATE.NONE;
734 |
735 | }
736 |
737 | function onMouseWheel( event ) {
738 |
739 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
740 |
741 | event.preventDefault();
742 | event.stopPropagation();
743 |
744 | handleMouseWheel( event );
745 |
746 | scope.dispatchEvent( startEvent ); // not sure why these are here...
747 | scope.dispatchEvent( endEvent );
748 |
749 | }
750 |
751 | function onKeyDown( event ) {
752 |
753 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
754 |
755 | handleKeyDown( event );
756 |
757 | }
758 |
759 | function onTouchStart( event ) {
760 |
761 | if ( scope.enabled === false ) return;
762 |
763 | switch ( event.touches.length ) {
764 |
765 | case 1: // one-fingered touch: rotate
766 |
767 | if ( scope.enableRotate === false ) return;
768 |
769 | handleTouchStartRotate( event );
770 |
771 | state = STATE.TOUCH_ROTATE;
772 |
773 | break;
774 |
775 | case 2: // two-fingered touch: dolly
776 |
777 | if ( scope.enableZoom === false ) return;
778 |
779 | handleTouchStartDolly( event );
780 |
781 | state = STATE.TOUCH_DOLLY;
782 |
783 | break;
784 |
785 | case 3: // three-fingered touch: pan
786 |
787 | if ( scope.enablePan === false ) return;
788 |
789 | handleTouchStartPan( event );
790 |
791 | state = STATE.TOUCH_PAN;
792 |
793 | break;
794 |
795 | default:
796 |
797 | state = STATE.NONE;
798 |
799 | }
800 |
801 | if ( state !== STATE.NONE ) {
802 |
803 | scope.dispatchEvent( startEvent );
804 |
805 | }
806 |
807 | }
808 |
809 | function onTouchMove( event ) {
810 |
811 | if ( scope.enabled === false ) return;
812 |
813 | event.preventDefault();
814 | event.stopPropagation();
815 |
816 | switch ( event.touches.length ) {
817 |
818 | case 1: // one-fingered touch: rotate
819 |
820 | if ( scope.enableRotate === false ) return;
821 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?...
822 |
823 | handleTouchMoveRotate( event );
824 |
825 | break;
826 |
827 | case 2: // two-fingered touch: dolly
828 |
829 | if ( scope.enableZoom === false ) return;
830 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?...
831 |
832 | handleTouchMoveDolly( event );
833 |
834 | break;
835 |
836 | case 3: // three-fingered touch: pan
837 |
838 | if ( scope.enablePan === false ) return;
839 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?...
840 |
841 | handleTouchMovePan( event );
842 |
843 | break;
844 |
845 | default:
846 |
847 | state = STATE.NONE;
848 |
849 | }
850 |
851 | }
852 |
853 | function onTouchEnd( event ) {
854 |
855 | if ( scope.enabled === false ) return;
856 |
857 | handleTouchEnd( event );
858 |
859 | scope.dispatchEvent( endEvent );
860 |
861 | state = STATE.NONE;
862 |
863 | }
864 |
865 | function onContextMenu( event ) {
866 |
867 | event.preventDefault();
868 |
869 | }
870 |
871 | //
872 |
873 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
874 |
875 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
876 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
877 |
878 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
879 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
880 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
881 |
882 | window.addEventListener( 'keydown', onKeyDown, false );
883 |
884 | // force an update at start
885 |
886 | this.update();
887 |
888 | };
889 |
890 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
891 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
892 |
893 | Object.defineProperties( THREE.OrbitControls.prototype, {
894 |
895 | center: {
896 |
897 | get: function () {
898 |
899 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' );
900 | return this.target;
901 |
902 | }
903 |
904 | },
905 |
906 | // backward compatibility
907 |
908 | noZoom: {
909 |
910 | get: function () {
911 |
912 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
913 | return ! this.enableZoom;
914 |
915 | },
916 |
917 | set: function ( value ) {
918 |
919 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
920 | this.enableZoom = ! value;
921 |
922 | }
923 |
924 | },
925 |
926 | noRotate: {
927 |
928 | get: function () {
929 |
930 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
931 | return ! this.enableRotate;
932 |
933 | },
934 |
935 | set: function ( value ) {
936 |
937 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
938 | this.enableRotate = ! value;
939 |
940 | }
941 |
942 | },
943 |
944 | noPan: {
945 |
946 | get: function () {
947 |
948 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
949 | return ! this.enablePan;
950 |
951 | },
952 |
953 | set: function ( value ) {
954 |
955 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
956 | this.enablePan = ! value;
957 |
958 | }
959 |
960 | },
961 |
962 | noKeys: {
963 |
964 | get: function () {
965 |
966 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
967 | return ! this.enableKeys;
968 |
969 | },
970 |
971 | set: function ( value ) {
972 |
973 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
974 | this.enableKeys = ! value;
975 |
976 | }
977 |
978 | },
979 |
980 | staticMoving : {
981 |
982 | get: function () {
983 |
984 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
985 | return ! this.enableDamping;
986 |
987 | },
988 |
989 | set: function ( value ) {
990 |
991 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
992 | this.enableDamping = ! value;
993 |
994 | }
995 |
996 | },
997 |
998 | dynamicDampingFactor : {
999 |
1000 | get: function () {
1001 |
1002 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1003 | return this.dampingFactor;
1004 |
1005 | },
1006 |
1007 | set: function ( value ) {
1008 |
1009 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1010 | this.dampingFactor = value;
1011 |
1012 | }
1013 |
1014 | }
1015 |
1016 | } );
1017 |
--------------------------------------------------------------------------------
/src/js/stats.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for Stats.js 0.16.0
2 | // Project: http://github.com/mrdoob/stats.js
3 | // Definitions by: Gregory Dalton , Harm Berntsen
4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5 |
6 | declare class Stats {
7 | REVISION: number;
8 | domElement: HTMLDivElement;
9 |
10 | /**
11 | * @param value 0:fps, 1: ms, 2: mb, 3+: custom
12 | */
13 | showPanel(value: number): void;
14 | begin(): void;
15 | end(): number;
16 | update(): void;
17 | }
18 |
19 | declare module "stats.js" {
20 | export = Stats;
21 | }
22 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | "use strict";
4 |
5 | import { Renderer } from "./renderer";
6 | import { AssetsManager } from "./assetsManager";
7 | import { FlameSphere } from "./object/flameSphere";
8 | import { Controller } from "./controller";
9 | import { ExplosionController } from "./animation/explosionController";
10 |
11 | window.onload = () => {
12 |
13 | var time = Date.now();
14 | var timeScale;
15 |
16 | var stats = new Stats();
17 | stats.showPanel(0);
18 |
19 | document.getElementById("stats").appendChild(stats.domElement);
20 |
21 | // Initialize
22 | Controller.init();
23 | Renderer.init();
24 | ExplosionController.init();
25 |
26 | timeScale = Controller.getParams().TimeScale;
27 |
28 | const onRequestAnimationFrame = () => {
29 | requestAnimationFrame(onRequestAnimationFrame);
30 | stats.begin();
31 | Renderer.animate();
32 | stats.end();
33 | }
34 |
35 | let deltaTimeMaximum = 1000 / 65;
36 |
37 | Renderer.setUpdateFunc(() => {
38 | let timeDiff = (Date.now() - time);
39 | ExplosionController.update(timeDiff > deltaTimeMaximum ? deltaTimeMaximum : timeDiff);
40 | time = Date.now();
41 | });
42 | Controller.setRestartFunc(() => {
43 | ExplosionController.reset();
44 | });
45 |
46 | requestAnimationFrame(onRequestAnimationFrame);
47 |
48 | window.addEventListener('resize', () => { Renderer.onWindowResize() }, false);
49 | };
50 |
--------------------------------------------------------------------------------
/src/object/flameSphere.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import { AssetsManager } from "../assetsManager";
4 | import { Controller } from "../controller";
5 | import { Utils } from "../utils";
6 |
7 | class FlameSphere {
8 |
9 | private mesh: THREE.Mesh;
10 | private flowRatio: number = 1;
11 | private material;
12 |
13 | private static defaultColor = {
14 | colDark: '#000000',
15 | colNormal: '#f7a90e',
16 | colLight: '#ede92a'
17 | };
18 |
19 | constructor(radius?: number) {
20 |
21 | radius = radius || 20;
22 |
23 | let glsl = AssetsManager.instance.getTexture();
24 |
25 | this.material = new THREE.ShaderMaterial({
26 | uniforms: {
27 | time: {
28 | type: "f",
29 | value: 0.0
30 | },
31 | seed: {
32 | type: 'f',
33 | value: Math.random() * 1000.0
34 | },
35 | detail: {
36 | type: 'f',
37 | value: Math.random() * 3.5 + 5
38 | },
39 | opacity: {
40 | type: 'f',
41 | value: 1
42 | },
43 | colLight: {
44 | value: Utils.hexToVec3(FlameSphere.defaultColor.colLight)
45 | },
46 | colNormal: {
47 | value: Utils.hexToVec3(FlameSphere.defaultColor.colNormal)
48 | },
49 | colDark: {
50 | value: Utils.hexToVec3(FlameSphere.defaultColor.colDark)
51 | }
52 | },
53 | vertexShader: glsl.vertexFlameShader,
54 | fragmentShader: glsl.fragmentFlameShader
55 | });
56 | this.material.transparent = true;
57 |
58 | this.mesh = new THREE.Mesh(
59 | new THREE.IcosahedronGeometry( radius, 3 ),
60 | this.material
61 | );
62 |
63 | this.mesh.position.set(0, 0, 0);
64 | }
65 |
66 | public setColor(prop) {
67 | if(prop.colDark != null) {
68 | if(typeof prop.colDark === 'string') {
69 | this.material.uniforms['colDark'].value = Utils.hexToVec3(prop.colDark);
70 | } else {
71 | this.material.uniforms['colDark'].value = prop.colDark;
72 | }
73 | }
74 | if(prop.colNormal != null) {
75 | if(typeof prop.colNormal === 'string') {
76 | this.material.uniforms['colNormal'].value = Utils.hexToVec3(prop.colNormal);
77 | } else {
78 | this.material.uniforms['colNormal'].value = prop.colNormal;
79 | }
80 | }
81 | if(prop.colLight != null) {
82 | if(typeof prop.colLight === 'string') {
83 | this.material.uniforms['colLight'].value = Utils.hexToVec3(prop.colLight);
84 | } else {
85 | this.material.uniforms['colLight'].value = prop.colLight;
86 | }
87 | }
88 | }
89 |
90 | public setOpacity(value: number) {
91 | this.material.uniforms['opacity'].value = value;
92 | }
93 |
94 | public setDetail(value: number) {
95 | this.material.uniforms['detail'].value = value;
96 | }
97 |
98 | public update(timeDiff: number): void {
99 | this.material.uniforms['time'].value += .0005 * timeDiff * this.flowRatio;
100 | }
101 |
102 | public setFlowRatio(val: number): void {
103 | this.flowRatio = val;
104 | }
105 |
106 | public getMesh() {
107 | return this.mesh;
108 | }
109 |
110 | }
111 |
112 | export { FlameSphere }
113 |
--------------------------------------------------------------------------------
/src/object/flareParticle.ts:
--------------------------------------------------------------------------------
1 | import { Renderer } from "../renderer";
2 | import { AssetsManager } from "../assetsManager";
3 | import { Controller } from "../controller";
4 | import { Constants } from "../constants";
5 | import { Utils } from "../utils";
6 |
7 | class FlareParticle {
8 |
9 | private geometry: THREE.BufferGeometry;
10 | private particlesNumber = 500;
11 | private particleSystem;
12 |
13 | private time: number;
14 | private spawnParticleTime: number;
15 | private spawnParticleInterval: number;
16 | private needsUpdate: boolean[];
17 | private positions: Float32Array;
18 | private originalSizes: Float32Array;
19 | private moveDest: Float32Array;
20 | private particleTime: Float32Array;
21 | private particleColor: number[];
22 |
23 | private static particleSpreadingRatio;
24 |
25 | constructor() {
26 |
27 | let shaderMaterial = new THREE.ShaderMaterial({
28 | uniforms: {
29 | color: { value: new THREE.Color(0xffffff) },
30 | texture: { value: new THREE.TextureLoader().load("./dist/images/circle-particle.png") }
31 | },
32 | vertexShader: AssetsManager.instance.getTexture().vectexParticleShader,
33 | fragmentShader: AssetsManager.instance.getTexture().fragmentParticleShader,
34 | blending: THREE.NormalBlending,
35 | depthTest: false,
36 | transparent: true
37 | });
38 |
39 | this.geometry = new THREE.BufferGeometry();
40 |
41 | let positions = new Float32Array(this.particlesNumber * 3);
42 | let colors = new Float32Array(this.particlesNumber * 3);
43 | let sizes = new Float32Array(this.particlesNumber);
44 |
45 | this.needsUpdate = [];
46 | this.originalSizes = new Float32Array(this.particlesNumber);
47 | this.moveDest = new Float32Array(this.particlesNumber * 3);
48 | this.particleTime = new Float32Array(this.particlesNumber);
49 | this.particleColor = Utils.hexToVec3(Controller.getParams().ParticleColor);
50 |
51 | for (let i = 0, i3 = 0; i < this.particlesNumber; i++ , i3 += 3) {
52 | positions[i3 + 0] = 0;
53 | positions[i3 + 1] = 0;
54 | positions[i3 + 2] = 0;
55 |
56 | this.moveDest[i3] = Math.random() * 200 - 100;
57 | this.moveDest[i3 + 1] = Math.random() * 0.3 + 0.45;
58 | this.moveDest[i3 + 2] = Math.random() * 200 - 100;
59 |
60 | colors[i3 + 0] = this.particleColor[0];
61 | colors[i3 + 1] = this.particleColor[1];
62 | colors[i3 + 2] = this.particleColor[2];
63 | sizes[i] = Math.random() * 1 + 0.5;
64 | this.originalSizes[i] = sizes[i];
65 | }
66 | this.geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
67 | this.geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
68 | this.geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));
69 | this.particleSystem = new THREE.Points(this.geometry, shaderMaterial);
70 |
71 | Renderer.addToScene(this.particleSystem);
72 |
73 | this.reset();
74 | FlareParticle.setController();
75 |
76 | Controller.attachEvent(Controller.PARTICLE_COLOR, (value) => {
77 | this.particleColor = Utils.hexToVec3(value);
78 | });
79 | }
80 |
81 | private static setController() {
82 | this.particleSpreadingRatio = Controller.getParams().ParticleSpread;
83 | Controller.attachEvent(Controller.PARTICLE_SPREAD, (value) => {
84 | this.particleSpreadingRatio = value;
85 | });
86 | }
87 |
88 | public reset() {
89 | this.time = 0;
90 | this.spawnParticleTime = 0;
91 | this.spawnParticleInterval = 1;
92 |
93 | let sizes = this.geometry.attributes['size'].array;
94 | let positions = this.geometry.attributes['position'].array;
95 |
96 | for (let i = 0; i < this.particlesNumber; i++) {
97 | sizes[i] = 0;
98 | positions[i*3] = 0;
99 | positions[i*3 + 1] = 0;
100 | positions[i*3 + 2] = 0;
101 | this.needsUpdate[i] = false;
102 | this.particleTime[i] = 0;
103 | }
104 |
105 | this.geometry.attributes['size'].needsUpdate = true;
106 | this.geometry.attributes['position'].needsUpdate = true;
107 | }
108 |
109 | private spawnParticle() {
110 | for (let i = 0; i < this.particlesNumber; i++) {
111 | if (this.needsUpdate[i] == false) {
112 | this.needsUpdate[i] = true;
113 | return;
114 | }
115 | }
116 | }
117 |
118 | public update(deltaTime: number) {
119 |
120 | this.spawnParticleTime += deltaTime;
121 | if (this.spawnParticleTime > this.spawnParticleInterval) {
122 | this.spawnParticleTime = 0;
123 | this.spawnParticleInterval = Math.random() * 300 + 50;
124 | this.spawnParticle();
125 | }
126 |
127 | deltaTime /= 1000;
128 | this.time += deltaTime;
129 |
130 | this.particleSystem.rotation.y += 0.01 * deltaTime;
131 | let timeScale = Controller.getParams().TimeScale / 3;
132 | let sizes = this.geometry.attributes['size'].array;
133 | let positions = this.geometry.attributes['position'].array;
134 | let colors = this.geometry.attributes['customColor'].array;
135 |
136 | for (let i = 0, i3 = 0; i < this.particlesNumber; i++ , i3 += 3) {
137 | if (this.needsUpdate[i]) {
138 | if (this.particleTime[i] > Constants.MAXIMUM_LIVE_TIME / 1000) {
139 | positions[i3] = 0;
140 | positions[i3 + 1] = 0;
141 | positions[i3 + 2] = 0;
142 | this.particleTime[i] = 0;
143 | sizes[i] = 0.01;
144 | } else {
145 | let ac = FlareParticle.particleSpreadingRatio *
146 | this.particleTime[i] / (Constants.MAXIMUM_LIVE_TIME / 1000) +
147 | 0.01 * Math.sin(this.time);
148 | let randDist = (10 * Math.sin(0.3 * i + this.time + Math.random() / 10)) * timeScale;
149 | sizes[i] = this.originalSizes[i] * (3 + Math.sin(0.4 * i + this.time));
150 | positions[i3] = ac * this.moveDest[i3] + randDist;
151 | positions[i3 + 1] += (Math.random() * 0.4 + 0.9) * this.moveDest[i3 + 1] * timeScale;
152 | positions[i3 + 2] = ac * this.moveDest[i3 + 2] + randDist;
153 | this.particleTime[i] += deltaTime;
154 | }
155 | }
156 |
157 | colors[i3] = this.particleColor[0];
158 | colors[i3 + 1] = this.particleColor[1];
159 | colors[i3 + 2] = this.particleColor[2];
160 | }
161 |
162 | this.geometry.attributes['customColor'].needsUpdate = true;
163 | this.geometry.attributes['size'].needsUpdate = true;
164 | this.geometry.attributes['position'].needsUpdate = true;
165 | }
166 |
167 | }
168 |
169 | export { FlareParticle };
--------------------------------------------------------------------------------
/src/renderer.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import "./js/OrbitControl.js";
4 | import { Controller } from "./controller";
5 |
6 | class Renderer {
7 | private static scene: THREE.Scene;
8 | private static camera: THREE.PerspectiveCamera;
9 | private static renderer: THREE.WebGLRenderer;
10 | private static controls: THREE.OrbitControls;
11 |
12 | private static updateCallback = null;
13 |
14 | private static vertexFlameShader;
15 | private static fragmentFlameShader;
16 |
17 | private static gridHelper: THREE.GridHelper;
18 |
19 | public static init() {
20 |
21 | this.scene = new THREE.Scene();
22 | this.scene.background = new THREE.Color(0xf8f8f8);
23 |
24 | this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
25 |
26 | this.renderer = new THREE.WebGLRenderer({ antialias: true });
27 | this.renderer.setSize(window.innerWidth, window.innerHeight);
28 | document.body.appendChild(this.renderer.domElement);
29 |
30 | // Grid Helper
31 | this.gridHelper = new THREE.GridHelper(100, 40, 0xdddddd, 0xdddddd);
32 | this.scene.add(this.gridHelper);
33 |
34 | this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
35 | // add this only if there is no animation loop (requestAnimationFrame)
36 | this.controls.enableDamping = true;
37 | this.controls.dampingFactor = 0.25;
38 | this.controls.enableZoom = true;
39 |
40 | this.camera.position.z = 75;
41 | this.camera.position.y = 75;
42 | this.camera.position.x = 75;
43 |
44 | Controller.attachEvent(Controller.INVERTED_BACKGROUND, (value) => {
45 | if(value) {
46 | this.scene.background = new THREE.Color(0x111111);
47 | this.scene.remove(this.gridHelper);
48 | this.gridHelper = new THREE.GridHelper(100, 40, 0x444444, 0x444444);
49 | this.scene.add(this.gridHelper);
50 | } else {
51 | this.scene.background = new THREE.Color(0xf8f8f8);
52 | this.scene.remove(this.gridHelper);
53 | this.gridHelper = new THREE.GridHelper(100, 40, 0xdddddd, 0xdddddd);
54 | this.scene.add(this.gridHelper);
55 | }
56 | });
57 |
58 | Controller.attachEvent(Controller.SHOW_GRID, (value) => {
59 | this.gridHelper.visible = value;
60 | });
61 | }
62 |
63 | public static animate() {
64 | this.controls.update();
65 |
66 | if (this.updateCallback != null) {
67 | this.updateCallback();
68 | }
69 |
70 | this.renderer.render(this.scene, this.camera);
71 | }
72 |
73 | public static addToScene(obj: any) {
74 | this.scene.add(obj);
75 | }
76 |
77 | public static removeFromScene(obj) {
78 | this.scene.remove(obj);
79 | }
80 |
81 | public static setUpdateFunc(func) {
82 | this.updateCallback = func;
83 | }
84 |
85 | public static onWindowResize() {
86 | this.camera.aspect = window.innerWidth / window.innerHeight;
87 | this.camera.updateProjectionMatrix();
88 | this.renderer.setSize(window.innerWidth, window.innerHeight);
89 | }
90 | }
91 |
92 | export { Renderer };
93 |
--------------------------------------------------------------------------------
/src/shader/fragmentFlameShader.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | varying float noise;
3 | uniform vec3 colLight;
4 | uniform vec3 colNormal;
5 | uniform vec3 colDark;
6 | uniform float opacity;
7 |
8 | vec3 blend( vec3 cola, vec3 colb, float percent ) {
9 | return vec3(
10 | cola.r + (colb.r - cola.r) * percent,
11 | cola.g + (colb.g - cola.g) * percent,
12 | cola.b + (colb.b - cola.b) * percent
13 | );
14 | }
15 |
16 | void main() {
17 |
18 | vec3 col;
19 | float range = 1.0 * noise;
20 |
21 | if(range > .6) col = colDark;
22 | else if(range > .4) col = blend(colNormal, colDark, (range - .4) / .2);
23 | else col = blend(colLight, colNormal, range / .4);
24 |
25 | gl_FragColor = vec4( col, opacity );
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/shader/fragmentParticleShader.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 color;
2 | uniform sampler2D texture;
3 | varying vec3 vColor;
4 |
5 | void main() {
6 | gl_FragColor = vec4( vColor, 1.0 );
7 | gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
8 | }
--------------------------------------------------------------------------------
/src/shader/vertexFlameShader.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // GLSL textureless classic 3D noise "cnoise",
3 | // with an RSL-style periodic variant "pnoise".
4 | // Author: Stefan Gustavson (stefan.gustavson@liu.se)
5 | // Version: 2011-10-11
6 | //
7 | // Many thanks to Ian McEwan of Ashima Arts for the
8 | // ideas for permutation and gradient selection.
9 | //
10 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved.
11 | // Distributed under the MIT license. See LICENSE file.
12 | // https://github.com/ashima/webgl-noise
13 | //
14 |
15 | vec3 mod289(vec3 x)
16 | {
17 | return x - floor(x * (1.0 / 289.0)) * 289.0;
18 | }
19 |
20 | vec4 mod289(vec4 x)
21 | {
22 | return x - floor(x * (1.0 / 289.0)) * 289.0;
23 | }
24 |
25 | vec4 permute(vec4 x)
26 | {
27 | return mod289(((x*34.0)+1.0)*x);
28 | }
29 |
30 | vec4 taylorInvSqrt(vec4 r)
31 | {
32 | return 1.79284291400159 - 0.85373472095314 * r;
33 | }
34 |
35 | vec3 fade(vec3 t) {
36 | return t*t*t*(t*(t*6.0-15.0)+10.0);
37 | }
38 |
39 | // Classic Perlin noise
40 | float cnoise(vec3 P)
41 | {
42 | vec3 Pi0 = floor(P); // Integer part for indexing
43 | vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
44 | Pi0 = mod289(Pi0);
45 | Pi1 = mod289(Pi1);
46 | vec3 Pf0 = fract(P); // Fractional part for interpolation
47 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
48 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
49 | vec4 iy = vec4(Pi0.yy, Pi1.yy);
50 | vec4 iz0 = Pi0.zzzz;
51 | vec4 iz1 = Pi1.zzzz;
52 |
53 | vec4 ixy = permute(permute(ix) + iy);
54 | vec4 ixy0 = permute(ixy + iz0);
55 | vec4 ixy1 = permute(ixy + iz1);
56 |
57 | vec4 gx0 = ixy0 * (1.0 / 7.0);
58 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
59 | gx0 = fract(gx0);
60 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
61 | vec4 sz0 = step(gz0, vec4(0.0));
62 | gx0 -= sz0 * (step(0.0, gx0) - 0.5);
63 | gy0 -= sz0 * (step(0.0, gy0) - 0.5);
64 |
65 | vec4 gx1 = ixy1 * (1.0 / 7.0);
66 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
67 | gx1 = fract(gx1);
68 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
69 | vec4 sz1 = step(gz1, vec4(0.0));
70 | gx1 -= sz1 * (step(0.0, gx1) - 0.5);
71 | gy1 -= sz1 * (step(0.0, gy1) - 0.5);
72 |
73 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
74 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
75 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
76 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
77 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
78 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
79 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
80 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
81 |
82 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
83 | g000 *= norm0.x;
84 | g010 *= norm0.y;
85 | g100 *= norm0.z;
86 | g110 *= norm0.w;
87 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
88 | g001 *= norm1.x;
89 | g011 *= norm1.y;
90 | g101 *= norm1.z;
91 | g111 *= norm1.w;
92 |
93 | float n000 = dot(g000, Pf0);
94 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
95 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
96 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
97 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
98 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
99 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
100 | float n111 = dot(g111, Pf1);
101 |
102 | vec3 fade_xyz = fade(Pf0);
103 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
104 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
105 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
106 | return 2.2 * n_xyz;
107 | }
108 |
109 | // Classic Perlin noise, periodic variant
110 | float pnoise(vec3 P, vec3 rep)
111 | {
112 | vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
113 | vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
114 | Pi0 = mod289(Pi0);
115 | Pi1 = mod289(Pi1);
116 | vec3 Pf0 = fract(P); // Fractional part for interpolation
117 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
118 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
119 | vec4 iy = vec4(Pi0.yy, Pi1.yy);
120 | vec4 iz0 = Pi0.zzzz;
121 | vec4 iz1 = Pi1.zzzz;
122 |
123 | vec4 ixy = permute(permute(ix) + iy);
124 | vec4 ixy0 = permute(ixy + iz0);
125 | vec4 ixy1 = permute(ixy + iz1);
126 |
127 | vec4 gx0 = ixy0 * (1.0 / 7.0);
128 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
129 | gx0 = fract(gx0);
130 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
131 | vec4 sz0 = step(gz0, vec4(0.0));
132 | gx0 -= sz0 * (step(0.0, gx0) - 0.5);
133 | gy0 -= sz0 * (step(0.0, gy0) - 0.5);
134 |
135 | vec4 gx1 = ixy1 * (1.0 / 7.0);
136 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
137 | gx1 = fract(gx1);
138 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
139 | vec4 sz1 = step(gz1, vec4(0.0));
140 | gx1 -= sz1 * (step(0.0, gx1) - 0.5);
141 | gy1 -= sz1 * (step(0.0, gy1) - 0.5);
142 |
143 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
144 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
145 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
146 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
147 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
148 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
149 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
150 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
151 |
152 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
153 | g000 *= norm0.x;
154 | g010 *= norm0.y;
155 | g100 *= norm0.z;
156 | g110 *= norm0.w;
157 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
158 | g001 *= norm1.x;
159 | g011 *= norm1.y;
160 | g101 *= norm1.z;
161 | g111 *= norm1.w;
162 |
163 | float n000 = dot(g000, Pf0);
164 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
165 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
166 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
167 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
168 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
169 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
170 | float n111 = dot(g111, Pf1);
171 |
172 | vec3 fade_xyz = fade(Pf0);
173 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
174 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
175 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
176 | return 2.2 * n_xyz;
177 | }
178 |
179 | // Include the Ashima code here!
180 |
181 | varying float noise;
182 | uniform float time;
183 | uniform float seed;
184 | uniform float detail;
185 |
186 | float turbulence( vec3 p ) {
187 | float w = 100.0;
188 | float t = -.5;
189 | for (float f = 1.0 ; f <= 10.0 ; f++ ){
190 | float power = pow( 2.0, f );
191 | t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );
192 | }
193 | return t;
194 | }
195 |
196 | void main() {
197 |
198 | noise = detail * -.10 * turbulence( 0.6 * normal + time + seed );
199 | float b = 2.0 * pnoise( 0.05 * position + vec3( 2.0 * time ), vec3( 100.0 ) );
200 | float displacement = - 10. * noise + b;
201 |
202 | vec3 newPosition = position + normal * displacement;
203 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/src/shader/vertexParticleShader.glsl:
--------------------------------------------------------------------------------
1 | attribute float size;
2 | attribute vec3 customColor;
3 | varying vec3 vColor;
4 |
5 | void main() {
6 | vColor = customColor;
7 | vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
8 | float cameraDist = length(mvPosition.xyz - position.xyz);
9 | gl_PointSize = size * 300.0 / cameraDist;
10 | gl_Position = projectionMatrix * mvPosition;
11 | }
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | class Utils {
4 | public static hexToVec3(col: string): number[] {
5 | let num = parseInt(col.substr(1), 16);
6 | let r = (num / 256 / 256) % 256;
7 | let g = (num / 256) % 256;
8 | let b = num % 256;
9 | return [r / 255.0, g / 255.0, b / 255.0];
10 | }
11 |
12 | public static formatZero(val: string) {
13 | if (val.length == 1) return '0' + val;
14 | return val;
15 | }
16 |
17 | public static vec3ToHex(col: number[]): string {
18 | return '#' +
19 | this.formatZero(col[0].toString(16)) +
20 | this.formatZero(col[1].toString(16)) +
21 | this.formatZero(col[2].toString(16));
22 | }
23 |
24 | public static vec3Blend(cola: string, colb: string, t: number) {
25 | let a = this.hexToVec3(cola);
26 | let b = this.hexToVec3(colb);
27 | return [
28 | a[0] + (b[0] - a[0]) * t,
29 | a[1] + (b[1] - a[1]) * t,
30 | a[2] + (b[2] - a[2]) * t
31 | ];
32 | }
33 | }
34 |
35 | export { Utils }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "src/**/*"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------