├── .gitignore ├── .jshintrc ├── LICENSE.md ├── README.md ├── app ├── css │ ├── index.css │ └── normalize.css ├── favicon.ico ├── images │ ├── diffuse.jpg │ ├── env.jpg │ ├── logo.png │ ├── screenshot.jpg │ └── share.jpg ├── index.html ├── js │ └── three.r74.dev.js └── models │ ├── LeePerrySmith.json │ └── horse.json ├── build.js ├── build.sh ├── dev.js ├── dev.sh ├── package.json └── src ├── 3d ├── fbo.js ├── ground.js ├── head.js ├── lights.js └── vignette.js ├── controls └── OrbitControls.js ├── core └── settings.js ├── fallback └── mobile.js ├── glsl ├── fbo.vert ├── fboThrough.frag ├── head.frag ├── head.vert ├── headDepth.frag ├── headDepth.vert ├── noise.glsl ├── particle.frag ├── particle.vert ├── position.frag ├── velocity.frag ├── vignette.frag └── vignette.vert ├── helpers └── shaderParse.js ├── index.js ├── libs └── global_three │ ├── index.js │ └── package.json └── utils ├── ease.js └── math.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.pyc 3 | *.pyo 4 | ======= 5 | # Compiled source # 6 | ################### 7 | *.com 8 | *.class 9 | *.dll 10 | *.exe 11 | *.o 12 | *.so 13 | .sass-cache 14 | 15 | 16 | # Packages # 17 | ############ 18 | # it's better to unpack these files and commit the raw source 19 | # git has its own built in compression methods 20 | *.7z 21 | *.dmg 22 | *.gz 23 | *.iso 24 | *.jar 25 | *.rar 26 | *.tar 27 | # Logs and databases # 28 | ###################### 29 | *.log 30 | #*.sql 31 | *.sqlite 32 | 33 | # OS generated files # 34 | ###################### 35 | .DS_Store 36 | .DS_Store? 37 | ._* 38 | .Spotlight-V100 39 | .Trashes 40 | ehthumbs.db 41 | Thumbs.db 42 | 43 | *.sublime-project 44 | *.sublime-workspace 45 | 46 | /node_modules 47 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : false, // true: Require triple equals (===) for comparison 12 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 4, // {int} Number of spaces to use for indentation 16 | "latedef" : false, // true: Require variables/functions to be defined before being used 17 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` & `--` 23 | "quotmark" : false, // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : true, // Unused variables: 30 | // true : all variables, last function parameter 31 | // "vars" : all variables only 32 | // "strict" : all variables, all function parameters 33 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode 34 | "maxparams" : false, // {int} Max number of formal params allowed per function 35 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 36 | "maxstatements" : false, // {int} Max number statements per function 37 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 38 | "maxlen" : false, // {int} Max number of characters per line 39 | 40 | // Relaxing 41 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 42 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 43 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 44 | "eqnull" : false, // true: Tolerate use of `== null` 45 | "es5" : true, // true: Allow ES5 syntax (ex: getters and setters) 46 | "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) 47 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 48 | // (ex: `for each`, multiple try/catch, function expression…) 49 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 50 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 51 | "funcscope" : false, // true: Tolerate defining variables inside control statements 52 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 53 | "iterator" : false, // true: Tolerate using the `__iterator__` property 54 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 55 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 56 | "laxcomma" : false, // true: Tolerate comma-first style coding 57 | "loopfunc" : false, // true: Tolerate functions being defined in loops 58 | "multistr" : false, // true: Tolerate multi-line strings 59 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 60 | "notypeof" : false, // true: Tolerate invalid typeof operator values 61 | "proto" : false, // true: Tolerate using the `__proto__` property 62 | "scripturl" : false, // true: Tolerate script-targeted URLs 63 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 64 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 65 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 66 | "validthis" : true, // true: Tolerate using this in a non-constructor function 67 | 68 | // Environments 69 | "browser" : true, // Web Browser (window, document, etc) 70 | "browserify" : true, // Browserify (node.js code in the browser) 71 | "couch" : false, // CouchDB 72 | "devel" : true, // Development/debugging (alert, confirm, etc) 73 | "dojo" : false, // Dojo Toolkit 74 | "jasmine" : false, // Jasmine 75 | "jquery" : false, // jQuery 76 | "mocha" : true, // Mocha 77 | "mootools" : false, // MooTools 78 | "node" : false, // Node.js 79 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 80 | "phantom" : false, // PhantomJS 81 | "prototypejs" : false, // Prototype and Scriptaculous 82 | "qunit" : false, // QUnit 83 | "rhino" : false, // Rhino 84 | "shelljs" : false, // ShellJS 85 | "typed" : false, // Globals for typed array constructions 86 | "worker" : false, // Web Workers 87 | "wsh" : false, // Windows Scripting Host 88 | "yui" : false, // Yahoo User Interface 89 | 90 | // Custom Globals 91 | "predef" : [ 92 | ] // additional predefined global variables 93 | } 94 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Edan Kwan 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SMASHING Mega Scene 2 | 3 | ![](https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/master/app/images/screenshot.jpg) 4 | 5 | [Live demo](http://www.edankwan.com/experiments/smashing-mega-scene/) | [Video](https://www.youtube.com/watch?v=nR7uJdP_HRk) 6 | 7 | It uses **1024^2(Mega)** particles to form the 3d model and turns it into a **smashable** mesh. 8 | 9 | This repo is for personal demo only and the 3d model was obtained [here](http://graphics.cs.williams.edu/data/meshes.xml#14) and it is owned by its original author. 10 | 11 | ## Development and deployment 12 | - dev: `node dev` 13 | - deploy: `node build` 14 | 15 | ## License 16 | This experiment is under MIT License. 17 | 18 | -------------------------------------------------------------------------------- /app/css/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | overflow: hidden; 6 | font-family: 'Lato', sans-serif; 7 | background-color: #222; 8 | } 9 | 10 | body { 11 | position: relative; 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | * { 17 | box-sizing: border-box; 18 | } 19 | 20 | .mobile { 21 | display: none; 22 | position: absolute; 23 | width: 100%; 24 | top: 50%; 25 | padding: 0 30px; 26 | 27 | font-size: 14px; 28 | color: #fff; 29 | 30 | transform: translate3d(0, -50%, 0); 31 | } 32 | 33 | .mobile span { 34 | color: #ccc; 35 | text-decoration: underline; 36 | } 37 | 38 | .mobile a { 39 | color: #ccc; 40 | } 41 | 42 | .logo { 43 | position: absolute; 44 | left: 50%; 45 | top: 50%; 46 | width: 400px; 47 | height: 87px; 48 | margin-left: -200px; 49 | margin-top: -43px; 50 | background-size: 400px 87px; 51 | background-image: url(../images/logo.png); 52 | pointer-events: none; 53 | opacity: 0; 54 | } 55 | /*@media (max-width: 960px) { 56 | .logo { 57 | left: 50%; 58 | } 59 | }*/ 60 | 61 | .footer { 62 | display: none; 63 | position: absolute; 64 | text-align: right; 65 | left: 0; 66 | width: 100%; 67 | bottom: 20px; 68 | padding-right: 20px; 69 | padding-left: 20px; 70 | 71 | font-size: 16px; 72 | color: #999; 73 | } 74 | 75 | .credit { 76 | float: left; 77 | } 78 | 79 | .footer span { 80 | display: inline-block; 81 | transform: translate3d(0, 50px, 0); 82 | } 83 | 84 | .footer a { 85 | color: #bbb; 86 | } 87 | 88 | html.is-white .footer { 89 | color: #000; 90 | } 91 | html.is-white .footer a { 92 | color: #121212; 93 | } 94 | -------------------------------------------------------------------------------- /app/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS and IE text size adjust after device orientation change, 6 | * without disabling user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability of focused elements when they are also in an 95 | * active/hover state. 96 | */ 97 | 98 | a:active, 99 | a:hover { 100 | outline: 0; 101 | } 102 | 103 | /* Text-level semantics 104 | ========================================================================== */ 105 | 106 | /** 107 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 108 | */ 109 | 110 | abbr[title] { 111 | border-bottom: 1px dotted; 112 | } 113 | 114 | /** 115 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 116 | */ 117 | 118 | b, 119 | strong { 120 | font-weight: bold; 121 | } 122 | 123 | /** 124 | * Address styling not present in Safari and Chrome. 125 | */ 126 | 127 | dfn { 128 | font-style: italic; 129 | } 130 | 131 | /** 132 | * Address variable `h1` font-size and margin within `section` and `article` 133 | * contexts in Firefox 4+, Safari, and Chrome. 134 | */ 135 | 136 | h1 { 137 | font-size: 2em; 138 | margin: 0.67em 0; 139 | } 140 | 141 | /** 142 | * Address styling not present in IE 8/9. 143 | */ 144 | 145 | mark { 146 | background: #ff0; 147 | color: #000; 148 | } 149 | 150 | /** 151 | * Address inconsistent and variable font size in all browsers. 152 | */ 153 | 154 | small { 155 | font-size: 80%; 156 | } 157 | 158 | /** 159 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 160 | */ 161 | 162 | sub, 163 | sup { 164 | font-size: 75%; 165 | line-height: 0; 166 | position: relative; 167 | vertical-align: baseline; 168 | } 169 | 170 | sup { 171 | top: -0.5em; 172 | } 173 | 174 | sub { 175 | bottom: -0.25em; 176 | } 177 | 178 | /* Embedded content 179 | ========================================================================== */ 180 | 181 | /** 182 | * Remove border when inside `a` element in IE 8/9/10. 183 | */ 184 | 185 | img { 186 | border: 0; 187 | } 188 | 189 | /** 190 | * Correct overflow not hidden in IE 9/10/11. 191 | */ 192 | 193 | svg:not(:root) { 194 | overflow: hidden; 195 | } 196 | 197 | /* Grouping content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Address margin not present in IE 8/9 and Safari. 202 | */ 203 | 204 | figure { 205 | margin: 1em 40px; 206 | } 207 | 208 | /** 209 | * Address differences between Firefox and other browsers. 210 | */ 211 | 212 | hr { 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. 354 | */ 355 | 356 | input[type="search"] { 357 | -webkit-appearance: textfield; /* 1 */ 358 | box-sizing: content-box; /* 2 */ 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 363 | * Safari (but not Chrome) clips the cancel button when the search input has 364 | * padding (and `textfield` appearance). 365 | */ 366 | 367 | input[type="search"]::-webkit-search-cancel-button, 368 | input[type="search"]::-webkit-search-decoration { 369 | -webkit-appearance: none; 370 | } 371 | 372 | /** 373 | * Define consistent border, margin, and padding. 374 | */ 375 | 376 | fieldset { 377 | border: 1px solid #c0c0c0; 378 | margin: 0 2px; 379 | padding: 0.35em 0.625em 0.75em; 380 | } 381 | 382 | /** 383 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 385 | */ 386 | 387 | legend { 388 | border: 0; /* 1 */ 389 | padding: 0; /* 2 */ 390 | } 391 | 392 | /** 393 | * Remove default vertical scrollbar in IE 8/9/10/11. 394 | */ 395 | 396 | textarea { 397 | overflow: auto; 398 | } 399 | 400 | /** 401 | * Don't inherit the `font-weight` (applied by a rule above). 402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 403 | */ 404 | 405 | optgroup { 406 | font-weight: bold; 407 | } 408 | 409 | /* Tables 410 | ========================================================================== */ 411 | 412 | /** 413 | * Remove most spacing between table cells. 414 | */ 415 | 416 | table { 417 | border-collapse: collapse; 418 | border-spacing: 0; 419 | } 420 | 421 | td, 422 | th { 423 | padding: 0; 424 | } 425 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/favicon.ico -------------------------------------------------------------------------------- /app/images/diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/images/diffuse.jpg -------------------------------------------------------------------------------- /app/images/env.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/images/env.jpg -------------------------------------------------------------------------------- /app/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/images/logo.png -------------------------------------------------------------------------------- /app/images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/images/screenshot.jpg -------------------------------------------------------------------------------- /app/images/share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edankwan/SMASHING-Mega-Scene/dd914d9c380c8841c458cb68e79701f4b98bc39b/app/images/share.jpg -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SMASHING Mega Scene 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 |
32 |

Sorry, this experiment is not designed for mobile devices. Please check out the video capture of this experiment instead. The source code is also accessible at Github.

33 |

Sorry for any inconvenience caused.

34 |

Cheers,

35 |

Edan Kwan.

36 |
37 | 38 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | global.Promise = require('pinkie-promise'); 2 | const browserify = require('browserify'); 3 | const fs = require('fs'); 4 | const UglifyJS = require('uglify-js'); 5 | 6 | Promise.all(['index.js'].map(runBuild)).catch(function (err) { 7 | console.error(err); 8 | }).then(function () { 9 | console.log("Finished"); 10 | }); 11 | 12 | function runBuild (f) { 13 | return new Promise(function (resolve, reject) { 14 | console.log('Bundling', f); 15 | var b = browserify('src/' + f, { 16 | debug: false, 17 | // noparse: [ 'three' ] 18 | }); 19 | // b.transform(require('babelify').configure({ presets: 'es2015' })); 20 | b.plugin(require('bundle-collapser/plugin')); 21 | var transforms = [['glslify', { global: true }]]; 22 | transforms.forEach(function (t) { 23 | b.transform(t); 24 | }); 25 | b.bundle(function (err, src) { 26 | if (err) return reject(err); 27 | console.log('Compressing', f); 28 | var result = UglifyJS.minify(src.toString(), { fromString: true }); 29 | console.log('Writing', f); 30 | fs.writeFile('app/js/' + f, result.code, function (err) { 31 | if (err) return reject(err); 32 | resolve(); 33 | }); 34 | }); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | npm run build 2 | -------------------------------------------------------------------------------- /dev.js: -------------------------------------------------------------------------------- 1 | const budo = require('budo'); 2 | const path = require('path'); 3 | const opn = require('opn'); 4 | const fs = require('fs'); 5 | const simpleHtml = require('simple-html-index'); 6 | 7 | var entryPath = path.resolve('src', 'index.js'); 8 | budo(entryPath, { 9 | serve: 'js/index.js', 10 | live: true, 11 | dir: __dirname + '/app', 12 | stream: process.stdout, 13 | defaultIndex: function (opt) { 14 | var html = 'index.html'; 15 | if (!fs.existsSync(html)) return simpleHtml(opt); 16 | return fs.createReadStream(html); 17 | }, 18 | browserify: { 19 | transform: [ 20 | [ 'installify', { save: true } ], 21 | ['glslify', { global: true }] 22 | ] 23 | } 24 | }).on('connect', function(ev) { 25 | const uri = ev.uri + 'index.html'; 26 | opn(uri); 27 | }); 28 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | npm run dev 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "budo-boilderplate", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "main": "index.js", 7 | "dependencies": { 8 | "computed-style": "0.3.0", 9 | "dat-gui": "0.5.0", 10 | "dom-css": "^1.1.1", 11 | "gl-matrix": "^2.3.1", 12 | "glsl-random": "^0.0.5", 13 | "glsl-noise": "^0.0.0", 14 | "incremental-convex-hull": "^1.0.1", 15 | "min-signal": "^0.0.5", 16 | "mout": "0.11", 17 | "quick-loader": "^0.1.4", 18 | "raf": "3.0.0", 19 | "stats.js": "mrdoob/stats.js", 20 | "three": "file:./src/libs/global_three/", 21 | "underscore": "^1.8.3", 22 | "ws": "^0.8.0" 23 | }, 24 | "devDependencies": { 25 | "browserify": "^12.0.1", 26 | "budo": "^6.0.4", 27 | "bundle-collapser": "^1.2.1", 28 | "dirlist": "^1.0.2", 29 | "glslify": "^2.3.1", 30 | "hasha": "^2.0.2", 31 | "installify": "^1.0.2", 32 | "map-limit": "0.0.1", 33 | "minifyify": "^7.1.0", 34 | "minimist": "^1.2.0", 35 | "opn": "^3.0.2", 36 | "pinkie-promise": "^1.0.0", 37 | "simple-html-index": "^1.1.2", 38 | "uglify-js": "^2.5.0" 39 | }, 40 | "scripts": { 41 | "dev": "node dev.js", 42 | "build": "node build.js" 43 | }, 44 | "author": "", 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /src/3d/fbo.js: -------------------------------------------------------------------------------- 1 | var settings = require('../core/settings'); 2 | var THREE = require('three'); 3 | 4 | var undef; 5 | 6 | var glslify = require('glslify'); 7 | var shaderParse = require('../helpers/shaderParse'); 8 | 9 | var _copyShader; 10 | var _velocityShader; 11 | var _positionShader; 12 | var _velocityRenderTarget; 13 | var _velocityRenderTarget2; 14 | var _positionRenderTarget; 15 | var _positionRenderTarget2; 16 | 17 | var _renderer; 18 | var _fboMesh; 19 | var _fboScene; 20 | var _fboCamera; 21 | 22 | var TEXTURE_WIDTH = exports.TEXTURE_WIDTH = settings.textureWidth; 23 | var TEXTURE_HEIGHT = exports.TEXTURE_HEIGHT = settings.textureHeight; 24 | var AMOUNT = exports.AMOUNT = TEXTURE_WIDTH * TEXTURE_HEIGHT; 25 | 26 | exports.init = init; 27 | exports.update = update; 28 | 29 | exports.velocityUniforms = undef; 30 | 31 | exports.positionRenderTarget = undef; 32 | var defaultPositionRenderTarget = exports.positionRenderTarget = undef; 33 | 34 | function init(renderer) { 35 | 36 | _renderer = renderer; 37 | 38 | var gl = _renderer.getContext(); 39 | if ( !gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) ) { 40 | alert( 'No support for vertex shader textures!' ); 41 | return; 42 | } 43 | if ( !gl.getExtension( 'OES_texture_float' )) { 44 | alert( 'No OES_texture_float support for float textures!' ); 45 | return; 46 | } 47 | 48 | _fboScene = new THREE.Scene(); 49 | _fboCamera = new THREE.Camera(); 50 | _fboCamera.position.z = 1; 51 | 52 | _copyShader = new THREE.ShaderMaterial({ 53 | uniforms: { 54 | resolution: { type: 'v2', value: new THREE.Vector2( TEXTURE_WIDTH, TEXTURE_HEIGHT ) }, 55 | texture: { type: 't', value: undef } 56 | }, 57 | vertexShader: shaderParse(glslify('../glsl/fbo.vert')), 58 | fragmentShader: shaderParse(glslify('../glsl/fboThrough.frag')) 59 | }); 60 | 61 | _velocityShader = new THREE.ShaderMaterial({ 62 | uniforms: exports.velocityUniforms ={ 63 | resolution: { type: 'v2', value: new THREE.Vector2( TEXTURE_WIDTH, TEXTURE_HEIGHT ) }, 64 | mouse3d: { type: 'v3', value: new THREE.Vector3() }, 65 | mouse3dVelocity: { type: 'v3', value: new THREE.Vector3() }, 66 | textureDefaultPosition: { type: 't', value: undef }, 67 | texturePosition: { type: 't', value: undef }, 68 | textureVelocity: { type: 't', value: undef }, 69 | isPhysicsActive: { type: 'f', value: 0 }, 70 | mouseForce: { type: 'f', value: 0.2 }, 71 | mouseRadius: { type: 'f', value: 120 }, 72 | gravity: { type: 'f', value: 0.15 }, 73 | resetAnimation: { type: 'f', value: 0 } 74 | }, 75 | vertexShader: shaderParse(glslify('../glsl/fbo.vert')), 76 | fragmentShader: shaderParse(glslify('../glsl/velocity.frag')), 77 | blending: THREE.NoBlending, 78 | transparent: false, 79 | depthWrite: false, 80 | depthTest: false 81 | }); 82 | 83 | _positionShader = new THREE.ShaderMaterial({ 84 | uniforms: { 85 | resolution: { type: 'v2', value: new THREE.Vector2( TEXTURE_WIDTH, TEXTURE_HEIGHT ) }, 86 | texturePosition: { type: 't', value: undef }, 87 | textureDefaultPosition: { type: 't', value: undef }, 88 | textureVelocity: { type: 't', value: undef }, 89 | resetAnimation: { type: 'f', value: 0 } 90 | }, 91 | vertexShader: shaderParse(glslify('../glsl/fbo.vert')), 92 | fragmentShader: shaderParse(glslify('../glsl/position.frag')), 93 | blending: THREE.NoBlending, 94 | transparent: false, 95 | depthWrite: false, 96 | depthTest: false 97 | }); 98 | 99 | _fboMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), _copyShader ); 100 | _fboScene.add( _fboMesh ); 101 | 102 | _velocityRenderTarget = new THREE.WebGLRenderTarget( TEXTURE_WIDTH, TEXTURE_HEIGHT, { 103 | wrapS: THREE.ClampToEdgeWrapping, 104 | wrapT: THREE.ClampToEdgeWrapping, 105 | minFilter: THREE.NearestFilter, 106 | magFilter: THREE.NearestFilter, 107 | format: THREE.RGBFormat, 108 | type: THREE.FloatType, 109 | depthBuffer: false, 110 | stencilBuffer: false 111 | }); 112 | _velocityRenderTarget2 = _velocityRenderTarget.clone(); 113 | _copyTexture(_createVelocityTexture(), _velocityRenderTarget); 114 | _copyTexture(_velocityRenderTarget, _velocityRenderTarget2); 115 | 116 | _positionRenderTarget = new THREE.WebGLRenderTarget(TEXTURE_WIDTH, TEXTURE_HEIGHT, { 117 | wrapS: THREE.ClampToEdgeWrapping, 118 | wrapT: THREE.ClampToEdgeWrapping, 119 | minFilter: THREE.NearestFilter, 120 | magFilter: THREE.NearestFilter, 121 | format: THREE.RGBFormat, 122 | type: THREE.FloatType, 123 | depthWrite: false, 124 | depthBuffer: false, 125 | stencilBuffer: false 126 | }); 127 | _positionRenderTarget2 = _positionRenderTarget.clone(); 128 | defaultPositionRenderTarget = exports.defaultPositionRenderTarget = _positionRenderTarget.clone(); 129 | 130 | _copyTexture(_createPositionTexture(), defaultPositionRenderTarget); 131 | _copyTexture(defaultPositionRenderTarget, _positionRenderTarget); 132 | _copyTexture(defaultPositionRenderTarget, _positionRenderTarget2); 133 | 134 | } 135 | 136 | function _updateVelocity(dt) { 137 | 138 | var mouse3dUniformValue = _velocityShader.uniforms.mouse3d.value; 139 | var mouse3dVelocityUniformValue = _velocityShader.uniforms.mouse3dVelocity.value; 140 | if(settings.isMouseActive && settings.isMouseVisible) { 141 | if(mouse3dUniformValue.z < -9000) { 142 | mouse3dVelocityUniformValue.set(0, 0, 0); 143 | } else { 144 | mouse3dVelocityUniformValue.copy(settings.mouse3d).sub(mouse3dUniformValue); 145 | } 146 | mouse3dUniformValue.copy(settings.mouse3d); 147 | 148 | } else { 149 | mouse3dUniformValue.set(0.0, 0.0, -9999); 150 | } 151 | 152 | _velocityShader.uniforms.isPhysicsActive.value = +settings.isPhysicsActive; 153 | 154 | // swap 155 | var tmp = _velocityRenderTarget; 156 | _velocityRenderTarget = _velocityRenderTarget2; 157 | _velocityRenderTarget2 = tmp; 158 | 159 | _fboMesh.material = _velocityShader; 160 | _velocityShader.uniforms.textureDefaultPosition.value = defaultPositionRenderTarget; 161 | _velocityShader.uniforms.textureVelocity.value = _velocityRenderTarget2; 162 | _velocityShader.uniforms.texturePosition.value = _positionRenderTarget; 163 | _velocityShader.uniforms.resetAnimation.value = settings.resetAnimation; 164 | _renderer.render( _fboScene, _fboCamera, _velocityRenderTarget ); 165 | } 166 | 167 | function _updatePosition(dt) { 168 | 169 | // swap 170 | var tmp = _positionRenderTarget; 171 | _positionRenderTarget = _positionRenderTarget2; 172 | _positionRenderTarget2 = tmp; 173 | 174 | _fboMesh.material = _positionShader; 175 | _positionShader.uniforms.textureVelocity.value = _velocityRenderTarget; 176 | _positionShader.uniforms.texturePosition.value = _positionRenderTarget2; 177 | _positionShader.uniforms.textureDefaultPosition.value = defaultPositionRenderTarget; 178 | _positionShader.uniforms.resetAnimation.value = settings.resetAnimation; 179 | _renderer.render( _fboScene, _fboCamera, _positionRenderTarget ); 180 | } 181 | 182 | function _copyTexture(input, output) { 183 | _fboMesh.material = _copyShader; 184 | _copyShader.uniforms.texture.value = input; 185 | _renderer.render( _fboScene, _fboCamera, output ); 186 | } 187 | 188 | function _createVelocityTexture() { 189 | var texture = new THREE.DataTexture( new Float32Array( AMOUNT * 3 ), TEXTURE_WIDTH, TEXTURE_HEIGHT, THREE.RGBFormat, THREE.FloatType ); 190 | texture.minFilter = THREE.NearestFilter; 191 | texture.magFilter = THREE.NearestFilter; 192 | texture.needsUpdate = true; 193 | texture.generateMipmaps = false; 194 | texture.flipY = false; 195 | return texture; 196 | } 197 | 198 | 199 | function _createPositionTexture() { 200 | var texture = new THREE.DataTexture( settings.headVertexPositions, TEXTURE_WIDTH, TEXTURE_HEIGHT, THREE.RGBFormat, THREE.FloatType ); 201 | texture.minFilter = THREE.NearestFilter; 202 | texture.magFilter = THREE.NearestFilter; 203 | texture.needsUpdate = true; 204 | texture.generateMipmaps = false; 205 | texture.flipY = false; 206 | return texture; 207 | } 208 | 209 | function update(dt) { 210 | 211 | _updateVelocity(dt); 212 | _renderer.autoClearColor = false; 213 | _updatePosition(dt); 214 | _renderer.autoClearColor = true; 215 | 216 | exports.positionRenderTarget = _positionRenderTarget; 217 | } 218 | 219 | 220 | -------------------------------------------------------------------------------- /src/3d/ground.js: -------------------------------------------------------------------------------- 1 | var settings = require('../core/settings'); 2 | var THREE = require('three'); 3 | 4 | var undef; 5 | 6 | var mesh = exports.mesh = undef; 7 | exports.init = init; 8 | exports.update = update; 9 | 10 | var _geometry; 11 | var _material; 12 | 13 | var BLACK = new THREE.Color(0x111111); 14 | var WHITE = new THREE.Color(0xcccccc); 15 | 16 | function init() { 17 | _geometry = new THREE.PlaneGeometry( 4000, 4000, 10, 10 ); 18 | _material = new THREE.MeshPhongMaterial( { 19 | color: new THREE.Color(0x555555), 20 | transparent: true, 21 | shininess: 5 22 | }); 23 | 24 | 25 | mesh = exports.mesh = new THREE.Mesh( _geometry, _material ); 26 | mesh.position.y = -200; 27 | mesh.rotation.x = -1.57; 28 | mesh.castShadow = false; 29 | mesh.receiveShadow = true; 30 | 31 | } 32 | 33 | function update() { 34 | mesh.visible = !settings.useReflectedGround; 35 | _material.color.copy(BLACK).lerp(WHITE, settings.whiteRatio); 36 | } 37 | -------------------------------------------------------------------------------- /src/3d/head.js: -------------------------------------------------------------------------------- 1 | var settings = require('../core/settings'); 2 | var THREE = require('three'); 3 | var shaderParse = require('../helpers/shaderParse'); 4 | var glslify = require('glslify'); 5 | var fbo = require('./fbo'); 6 | 7 | var undef; 8 | 9 | var mesh = exports.mesh = undef; 10 | exports.init = init; 11 | exports.update = update; 12 | exports.useDiffuse = false; 13 | exports.useEnv = false; 14 | exports.normalNoise = 0.2; 15 | 16 | var _geometry; 17 | var _materials = {}; 18 | var _depthMaterial; 19 | 20 | var TEXTURE_WIDTH = settings.textureWidth; 21 | var PARTICLES_AMOUNT = settings.textureWidth * settings.textureHeight; 22 | 23 | function init() { 24 | settings.headData.scale = 1/1000; 25 | var geometry = (new THREE.JSONLoader()).parse(settings.headData).geometry; 26 | 27 | var geometryFaces = geometry.faces; 28 | var geometryVertices = geometry.vertices; 29 | var geometryFaceUVs = geometry.faceVertexUvs[0]; 30 | var areaTotal = 0; 31 | var areaThresholds = []; 32 | var face, vertex, area; 33 | var p0x, p0y, p0z; 34 | var p1x, p1y, p1z; 35 | var p2x, p2y, p2z; 36 | var v0x, v0y, v0z; 37 | var v1x, v1y, v1z; 38 | var v2x, v2y, v2z; 39 | 40 | for(var i = 0, len = geometryFaces.length; i < len; i++) { 41 | face = geometryFaces[i]; 42 | vertex = geometryVertices[face.a]; 43 | p0x = vertex.x; 44 | p0y = vertex.y; 45 | p0z = vertex.z; 46 | vertex = geometryVertices[face.b]; 47 | p1x = vertex.x; 48 | p1y = vertex.y; 49 | p1z = vertex.z; 50 | vertex = geometryVertices[face.c]; 51 | p2x = vertex.x; 52 | p2y = vertex.y; 53 | p2z = vertex.z; 54 | v0x = p1x - p0x; 55 | v0y = p1y - p0y; 56 | v0z = p1z - p0z; 57 | v1x = p2x - p0x; 58 | v1y = p2y - p0y; 59 | v1z = p2z - p0z; 60 | v2x = v0y * v1z - v0z * v1y; 61 | v2y = v0z * v1x - v0x * v1z; 62 | v2z = v0x * v1y - v0y * v1x; 63 | area = 0.5 * Math.sqrt(v2x*v2x + v2y*v2y + v2z*v2z); 64 | areaTotal += area; 65 | areaThresholds[i] = areaTotal; 66 | } 67 | 68 | // probably not good to ignore the same vertices with different normal 69 | // but it shouldnt look so different... so meh 70 | 71 | var remainder = PARTICLES_AMOUNT - geometryVertices.length; 72 | var areaPerParticle = areaTotal / remainder; 73 | var usedVertexMap = new Int8Array(geometryVertices.length); 74 | var vertices = new Float32Array(PARTICLES_AMOUNT * 3); 75 | var uvs = new Float32Array(PARTICLES_AMOUNT * 2); 76 | var normals = new Float32Array(PARTICLES_AMOUNT * 3); 77 | var index2 = 0; 78 | var index3 = 0; 79 | var uv, normal, areaThreshold, amount; 80 | 81 | var vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy; 82 | var v0, v1, v2, n0, n1, n2, uv0, uv1, uv2; 83 | 84 | var areaSum = 0; 85 | 86 | function addPoint(pointAmount, 87 | v0x, v0y, v0z, n0x, n0y, n0z, uv0x, uv0y, 88 | v1x, v1y, v1z, n1x, n1y, n1z, uv1x, uv1y, 89 | v2x, v2y, v2z, n2x, n2y, n2z, uv2x, uv2y) { 90 | if(pointAmount < 1) return 0; 91 | 92 | vertices[index3] = vcx = (v0x + v1x + v2x) / 3; 93 | vertices[index3 + 1] = vcy = (v0y + v1y + v2y) / 3; 94 | vertices[index3 + 2] = vcz = (v0z + v1z + v2z) / 3; 95 | normals[index3] = ncx = (n0x + n1x + n2x) / 3; 96 | normals[index3 + 1] = ncy = (n0y + n1y + n2y) / 3; 97 | normals[index3 + 2] = ncz = (n0z + n1z + n2z) / 3; 98 | index3 += 3; 99 | uvs[index2] = uvcx = (uv0x + uv1x + uv2x) / 3; 100 | uvs[index2 + 1] = uvcy = (uv0y + uv1y + uv2y) / 3; 101 | index2 += 2; 102 | pointAmount--; 103 | 104 | if(pointAmount % 2) { 105 | var d3 = ~~(pointAmount / 3); 106 | var f3 = pointAmount - d3 * 3; 107 | addPoint(d3 + (f3 > 0 ? 1: 0), 108 | v0x, v0y, v0z, n0x, n0y, n0z, uv0x, uv0y, 109 | v1x, v1y, v1z, n1x, n1y, n1z, uv1x, uv1y, 110 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 111 | addPoint(d3 + (f3 > 1 ? 1: 0), 112 | v1x, v1y, v1z, n1x, n1y, n1z, uv1x, uv1y, 113 | v2x, v2y, v2z, n2x, n2y, n2z, uv2x, uv2y, 114 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 115 | addPoint(d3, 116 | v2x, v2y, v2z, n2x, n2y, n2z, uv2x, uv2y, 117 | v0x, v0y, v0z, n0x, n0y, n0z, uv0x, uv0y, 118 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 119 | } else { 120 | 121 | var d6 = ~~(pointAmount / 6); 122 | var f6 = pointAmount - d6 * 6; 123 | 124 | addPoint(d6 + (f6 > 0 ? 1: 0), 125 | v2x, v2y, v2z, n2x, n2y, n2z, uv2x, uv2y, 126 | (v2x + v0x) / 2, (v2y + v0y) / 2, (v2z + v0z) / 2, (n2x + n0x) / 2, (n2y + n0y) / 2, (n2z + n0z) / 2, (uv2x + uv0x) / 2, (uv2y + uv0y) / 2, 127 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 128 | addPoint(d6 + (f6 > 1 ? 1: 0), 129 | v0x, v0y, v0z, n0x, n0y, n0z, uv0x, uv0y, 130 | (v2x + v0x) / 2, (v2y + v0y) / 2, (v2z + v0z) / 2, (n2x + n0x) / 2, (n2y + n0y) / 2, (n2z + n0z) / 2, (uv2x + uv0x) / 2, (uv2y + uv0y) / 2, 131 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 132 | 133 | addPoint(d6 + (f6 > 2 ? 1: 0), 134 | v1x, v1y, v1z, n1x, n1y, n1z, uv1x, uv1y, 135 | (v1x + v2x) / 2, (v1y + v2y) / 2, (v1z + v2z) / 2, (n1x + n2x) / 2, (n1y + n2y) / 2, (n1z + n2z) / 2, (uv1x + uv2x) / 2, (uv1y + uv2y) / 2, 136 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 137 | addPoint(d6 + (f6 > 3 ? 1: 0), 138 | v2x, v2y, v2z, n2x, n2y, n2z, uv2x, uv2y, 139 | (v1x + v2x) / 2, (v1y + v2y) / 2, (v1z + v2z) / 2, (n1x + n2x) / 2, (n1y + n2y) / 2, (n1z + n2z) / 2, (uv1x + uv2x) / 2, (uv1y + uv2y) / 2, 140 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 141 | 142 | addPoint(d6 + (f6 > 4 ? 1: 0), 143 | v0x, v0y, v0z, n0x, n0y, n0z, uv0x, uv0y, 144 | (v0x + v1x) / 2, (v0y + v1y) / 2, (v0z + v1z) / 2, (n0x + n1x) / 2, (n0y + n1y) / 2, (n0z + n1z) / 2, (uv0x + uv1x) / 2, (uv0y + uv1y) / 2, 145 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 146 | addPoint(d6 + (f6 > 5 ? 1: 0), 147 | v1x, v1y, v1z, n1x, n1y, n1z, uv1x, uv1y, 148 | (v0x + v1x) / 2, (v0y + v1y) / 2, (v0z + v1z) / 2, (n0x + n1x) / 2, (n0y + n1y) / 2, (n0z + n1z) / 2, (uv0x + uv1x) / 2, (uv0y + uv1y) / 2, 149 | vcx, vcy, vcz, ncx, ncy, ncz, uvcx, uvcy); 150 | } 151 | 152 | } 153 | for(i = 0, len = geometryFaces.length; i < len; i++) { 154 | 155 | face = geometryFaces[i]; 156 | 157 | if(!usedVertexMap[face.a]) { 158 | usedVertexMap[face.a] = 1; 159 | vertex = geometryVertices[face.a]; 160 | vertices[index3] = vertex.x; 161 | vertices[index3 + 1] = vertex.y; 162 | vertices[index3 + 2] = vertex.z; 163 | normal = face.vertexNormals[0]; 164 | normals[index3] = normal.x; 165 | normals[index3 + 1] = normal.y; 166 | normals[index3 + 2] = normal.z; 167 | index3 += 3; 168 | uv = geometryFaceUVs[i][0]; 169 | uvs[index2] = uv.x; 170 | uvs[index2 + 1] = uv.y; 171 | index2 += 2; 172 | } 173 | if(!usedVertexMap[face.b]) { 174 | usedVertexMap[face.b] = 1; 175 | vertex = geometryVertices[face.b]; 176 | vertices[index3] = vertex.x; 177 | vertices[index3 + 1] = vertex.y; 178 | vertices[index3 + 2] = vertex.z; 179 | normal = face.vertexNormals[1]; 180 | normals[index3] = normal.x; 181 | normals[index3 + 1] = normal.y; 182 | normals[index3 + 2] = normal.z; 183 | index3 += 3; 184 | uv = geometryFaceUVs[i][1]; 185 | uvs[index2] = uv.x; 186 | uvs[index2 + 1] = uv.y; 187 | index2 += 2; 188 | } 189 | if(!usedVertexMap[face.c]) { 190 | usedVertexMap[face.c] = 1; 191 | vertex = geometryVertices[face.c]; 192 | vertices[index3] = vertex.x; 193 | vertices[index3 + 1] = vertex.y; 194 | vertices[index3 + 2] = vertex.z; 195 | normal = face.vertexNormals[2]; 196 | normals[index3] = normal.x; 197 | normals[index3 + 1] = normal.y; 198 | normals[index3 + 2] = normal.z; 199 | index3 += 3; 200 | uv = geometryFaceUVs[i][2]; 201 | uvs[index2] = uv.x; 202 | uvs[index2 + 1] = uv.y; 203 | index2 += 2; 204 | } 205 | 206 | areaThreshold = areaThresholds[i]; 207 | amount = 0; 208 | while(((i === len - 1) || (areaSum + areaPerParticle <= areaThreshold)) && remainder > 0) { 209 | areaSum += areaPerParticle; 210 | amount++; 211 | remainder--; 212 | } 213 | if(amount > 0) { 214 | v0 = geometryVertices[face.a]; 215 | n0 = face.vertexNormals[0]; 216 | uv0 = geometryFaceUVs[i][0]; 217 | v1 = geometryVertices[face.b]; 218 | n1 = face.vertexNormals[1]; 219 | uv1 = geometryFaceUVs[i][1]; 220 | v2 = geometryVertices[face.c]; 221 | n2 = face.vertexNormals[2]; 222 | uv2 = geometryFaceUVs[i][2]; 223 | addPoint(amount, 224 | v0.x, v0.y, v0.z, n0.x, n0.y, n0.z, uv0.x, uv0.y, 225 | v1.x, v1.y, v1.z, n1.x, n1.y, n1.z, uv1.x, uv1.y, 226 | v2.x, v2.y, v2.z, n2.x, n2.y, n2.z, uv2.x, uv2.y); 227 | } 228 | } 229 | 230 | settings.headVertexPositions = vertices; 231 | 232 | var position = new Float32Array(PARTICLES_AMOUNT * 3); 233 | var i3; 234 | for( i = 0; i < PARTICLES_AMOUNT; i++ ) { 235 | i3 = i * 3; 236 | position[ i3 + 0] = (i % TEXTURE_WIDTH) / TEXTURE_WIDTH; 237 | position[ i3 + 1 ] = ~~(i / TEXTURE_WIDTH) / TEXTURE_WIDTH; 238 | position[ i3 + 2 ] = Math.random(); 239 | } 240 | 241 | _geometry = new THREE.BufferGeometry(); 242 | _geometry.addAttribute( 'position', new THREE.BufferAttribute( position, 3 )); 243 | _geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 )); 244 | _geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 )); 245 | 246 | var diffuseMap = (new THREE.TextureLoader()).load('images/diffuse.jpg'); 247 | diffuseMap.wrapS = diffuseMap.wrapT = THREE.RepeatWrapping; 248 | diffuseMap.format = THREE.RGBFormat; 249 | 250 | var material; 251 | 252 | var vs = shaderParse(glslify('../glsl/head.vert')); 253 | var fs = shaderParse(glslify('../glsl/head.frag')); 254 | 255 | function cloneMaterial(useDiffuse, useEnv) { 256 | material = _materials[(+useDiffuse << 1) + (+useEnv)] = new THREE.MeshPhongMaterial(); 257 | material.type = 'ShaderMaterial'; 258 | material.color.setHex(useDiffuse ? 0x777777 : 0x444444); 259 | material.specular.setHex(useDiffuse ? 0x222222 : 0x555555); 260 | material.shininess = 12; 261 | material.reflectivity = (!useDiffuse && useEnv) ? 0.5 : 0.25; 262 | material.blending = THREE.NoBlending; 263 | 264 | var uniforms = THREE.UniformsUtils.merge( [THREE.ShaderLib.phong.uniforms] ); 265 | uniforms.texturePosition = {type: 't', value: undef}; 266 | uniforms.textureDefaultPosition = {type: 't', value: undef}; 267 | uniforms.normalNoise = {type: 'f', value: exports.normalNoise}; 268 | material.uniforms = uniforms; 269 | material.vertexShader = vs; 270 | material.fragmentShader = fs; 271 | material.map = useDiffuse ? diffuseMap : undef; 272 | material.envMap = useEnv ? settings.envMap : undef; 273 | } 274 | for( i = 0; i < 4; i++ ) { 275 | cloneMaterial((i & 2) >> 1, i & 1); 276 | } 277 | 278 | mesh = exports.mesh = new THREE.Points(_geometry, material); 279 | mesh.customDepthMaterial = _depthMaterial = new THREE.ShaderMaterial( { 280 | uniforms: { 281 | time: { type: 'f', value: 0 }, 282 | realCameraPosition: { type: 'v3', value: settings.cameraPosition }, 283 | texturePosition: { type: 't', value: undef }, 284 | textureDefaultPosition: { type: 't', value: undef } 285 | }, 286 | vertexShader: shaderParse(glslify('../glsl/headDepth.vert')), 287 | fragmentShader: shaderParse(glslify('../glsl/headDepth.frag')), 288 | blending: THREE.NoBlending, 289 | depthTest: true, 290 | depthWrite: true 291 | }); 292 | mesh.castShadow = true; 293 | mesh.receiveShadow = true; 294 | 295 | geometry.dispose(); 296 | // var material = new THREE.MeshPhongMaterial( { 297 | // color: 0x666666, 298 | // specular: 0x222222, 299 | // shininess: 12 300 | // }); 301 | // var baseMesh = exports.baseMesh = new THREE.Mesh(geometry, material); 302 | // baseMesh.castShadow = true; 303 | // baseMesh.receiveShadow = true; 304 | // baseMesh.visible = false; 305 | // mesh.add(baseMesh); 306 | 307 | } 308 | 309 | function update(dt) { 310 | mesh.material = _materials[(+exports.useDiffuse << 1) + (+exports.useEnv)]; 311 | mesh.material.uniforms.textureDefaultPosition.value = fbo.defaultPositionRenderTarget; 312 | mesh.material.uniforms.texturePosition.value = fbo.positionRenderTarget; 313 | mesh.material.uniforms.normalNoise.value = exports.normalNoise; 314 | _depthMaterial.uniforms.textureDefaultPosition.value = fbo.defaultPositionRenderTarget; 315 | _depthMaterial.uniforms.texturePosition.value = fbo.positionRenderTarget; 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/3d/lights.js: -------------------------------------------------------------------------------- 1 | var settings = require('../core/settings'); 2 | var THREE = require('three'); 3 | 4 | var undef; 5 | 6 | var mesh = exports.mesh = undef; 7 | var spot = exports.spot = undef; 8 | exports.init = init; 9 | exports.update = update; 10 | 11 | var _moveTime = 0; 12 | 13 | function init() { 14 | 15 | mesh = exports.mesh = new THREE.Object3D(); 16 | 17 | var ambient = new THREE.AmbientLight( 0xcccccc ); 18 | mesh.add( ambient ); 19 | 20 | spot = exports.spot = new THREE.SpotLight( 0xffffff, 1, 0, Math.PI / 2, 1 ); 21 | spot.position.x = 400; 22 | spot.position.y = 700; 23 | spot.position.z = 200; 24 | spot.target.position.set( 0, 0, 0 ); 25 | 26 | spot.castShadow = true; 27 | 28 | spot.shadowCameraNear = 100; 29 | spot.shadowCameraFar = 2500; 30 | spot.shadowCameraFov = 120; 31 | 32 | spot.shadowBias = 0.0003; 33 | spot.shadowDarkness = 1; 34 | 35 | spot.shadowMapWidth = 1024; 36 | spot.shadowMapHeight = 2048; 37 | 38 | mesh.add( spot ); 39 | 40 | } 41 | 42 | function update(dt, camera) { 43 | _moveTime += 0;//dt * settings.lightSpeed; 44 | var angle = _moveTime * 0.0005 - 0.2; 45 | // mesh.position.x = Math.cos(angle) * 400; 46 | // mesh.position.z = Math.sin(angle) * 400; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/3d/vignette.js: -------------------------------------------------------------------------------- 1 | var settings = require('../core/settings'); 2 | var THREE = require('three'); 3 | 4 | var undef; 5 | 6 | var glslify = require('glslify'); 7 | var shaderParse = require('../helpers/shaderParse'); 8 | 9 | var mesh = exports.mesh = undef; 10 | 11 | exports.init = init; 12 | exports.resize = resize; 13 | exports.update = update; 14 | 15 | var _resolution; 16 | var _uTime; 17 | 18 | function init() { 19 | var geometry = new THREE.PlaneBufferGeometry( 2, 2); 20 | var material = new THREE.ShaderMaterial( { 21 | uniforms: { 22 | uAlpha : exports.alphaUniform = {type : 'f', value: 1 }, 23 | uTime : _uTime = {type : 'f', value: 0 }, 24 | uResolution : {type : 'v2', value: _resolution = new THREE.Vector2() } 25 | }, 26 | vertexShader: shaderParse(glslify('../glsl/vignette.vert')), 27 | fragmentShader: shaderParse(glslify('../glsl/vignette.frag')), 28 | blending: THREE.NormalBlending, 29 | transparent: true, 30 | depthWrite: false, 31 | depthTest: false 32 | }); 33 | 34 | mesh = exports.mesh = new THREE.Mesh( geometry, material ); 35 | mesh.frustumCulled = false; 36 | mesh.renderOrder = 1024; 37 | 38 | } 39 | 40 | function resize(width, height) { 41 | _resolution.set(width, height); 42 | } 43 | 44 | function update(dt) { 45 | _uTime.value = (_uTime.value + dt) % 1512.21; 46 | } 47 | -------------------------------------------------------------------------------- /src/controls/OrbitControls.js: -------------------------------------------------------------------------------- 1 | //var THREE = require('three'); 2 | 3 | /** 4 | * @author qiao / https://github.com/qiao 5 | * @author mrdoob / http://mrdoob.com 6 | * @author alteredq / http://alteredqualia.com/ 7 | * @author WestLangley / http://github.com/WestLangley 8 | * @author erich666 / http://erichaines.com 9 | */ 10 | /*global THREE, console */ 11 | 12 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 13 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 14 | // supported. 15 | // 16 | // Orbit - left mouse / touch: one finger move 17 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 18 | // Pan - right mouse, or arrow keys / touch: three finter swipe 19 | 20 | THREE.OrbitControls = function ( object, domElement ) { 21 | 22 | this.object = object; 23 | this.domElement = ( domElement !== undefined ) ? domElement : document; 24 | 25 | // API 26 | 27 | // Set to false to disable this control 28 | this.enabled = true; 29 | 30 | // "target" sets the location of focus, where the control orbits around 31 | // and where it pans with respect to. 32 | this.target = new THREE.Vector3(); 33 | 34 | // center is old, deprecated; use "target" instead 35 | this.center = this.target; 36 | 37 | // This option actually enables dollying in and out; left as "zoom" for 38 | // backwards compatibility 39 | this.noZoom = false; 40 | this.zoomSpeed = 1.0; 41 | 42 | // Limits to how far you can dolly in and out ( PerspectiveCamera only ) 43 | this.minDistance = 0; 44 | this.maxDistance = Infinity; 45 | 46 | // Limits to how far you can zoom in and out ( OrthographicCamera only ) 47 | this.minZoom = 0; 48 | this.maxZoom = Infinity; 49 | 50 | // Set to true to disable this control 51 | this.noRotate = false; 52 | this.rotateSpeed = 1.0; 53 | 54 | // Set to true to disable this control 55 | this.noPan = false; 56 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 57 | 58 | // Set to true to automatically rotate around the target 59 | this.autoRotate = false; 60 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 61 | 62 | // How far you can orbit vertically, upper and lower limits. 63 | // Range is 0 to Math.PI radians. 64 | this.minPolarAngle = 0; // radians 65 | this.maxPolarAngle = Math.PI; // radians 66 | 67 | // How far you can orbit horizontally, upper and lower limits. 68 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 69 | this.minAzimuthAngle = - Infinity; // radians 70 | this.maxAzimuthAngle = Infinity; // radians 71 | 72 | // Set to true to disable use of the keys 73 | this.noKeys = false; 74 | 75 | // The four arrow keys 76 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 77 | 78 | // Mouse buttons 79 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 80 | 81 | //////////// 82 | // internals 83 | 84 | var scope = this; 85 | 86 | var EPS = 0.000001; 87 | 88 | var rotateStart = new THREE.Vector2(); 89 | var rotateEnd = new THREE.Vector2(); 90 | var rotateDelta = new THREE.Vector2(); 91 | 92 | var panStart = new THREE.Vector2(); 93 | var panEnd = new THREE.Vector2(); 94 | var panDelta = new THREE.Vector2(); 95 | var panOffset = new THREE.Vector3(); 96 | 97 | var offset = new THREE.Vector3(); 98 | 99 | var dollyStart = new THREE.Vector2(); 100 | var dollyEnd = new THREE.Vector2(); 101 | var dollyDelta = new THREE.Vector2(); 102 | 103 | var theta; 104 | var phi; 105 | var phiDelta = 0; 106 | var thetaDelta = 0; 107 | var scale = 1; 108 | var pan = new THREE.Vector3(); 109 | 110 | var lastPosition = new THREE.Vector3(); 111 | var lastQuaternion = new THREE.Quaternion(); 112 | 113 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 114 | 115 | var state = STATE.NONE; 116 | 117 | // for reset 118 | 119 | this.target0 = this.target.clone(); 120 | this.position0 = this.object.position.clone(); 121 | this.zoom0 = this.object.zoom; 122 | 123 | // so camera.up is the orbit axis 124 | 125 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 126 | var quatInverse = quat.clone().inverse(); 127 | 128 | // events 129 | 130 | var changeEvent = { type: 'change' }; 131 | var startEvent = { type: 'start' }; 132 | var endEvent = { type: 'end' }; 133 | 134 | this.rotateLeft = function ( angle ) { 135 | 136 | if ( angle === undefined ) { 137 | 138 | angle = getAutoRotationAngle(); 139 | 140 | } 141 | 142 | thetaDelta -= angle; 143 | 144 | }; 145 | 146 | this.rotateUp = function ( angle ) { 147 | 148 | if ( angle === undefined ) { 149 | 150 | angle = getAutoRotationAngle(); 151 | 152 | } 153 | 154 | phiDelta -= angle; 155 | 156 | }; 157 | 158 | // pass in distance in world space to move left 159 | this.panLeft = function ( distance ) { 160 | 161 | var te = this.object.matrix.elements; 162 | 163 | // get X column of matrix 164 | panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); 165 | panOffset.multiplyScalar( - distance ); 166 | 167 | pan.add( panOffset ); 168 | 169 | }; 170 | 171 | // pass in distance in world space to move up 172 | this.panUp = function ( distance ) { 173 | 174 | var te = this.object.matrix.elements; 175 | 176 | // get Y column of matrix 177 | panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); 178 | panOffset.multiplyScalar( distance ); 179 | 180 | pan.add( panOffset ); 181 | 182 | }; 183 | 184 | // pass in x,y of change desired in pixel space, 185 | // right and down are positive 186 | this.pan = function ( deltaX, deltaY ) { 187 | 188 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 189 | 190 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 191 | 192 | // perspective 193 | var position = scope.object.position; 194 | var offset = position.clone().sub( scope.target ); 195 | var targetDistance = offset.length(); 196 | 197 | // half of the fov is center to top of screen 198 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 199 | 200 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 201 | scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); 202 | scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); 203 | 204 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 205 | 206 | // orthographic 207 | scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); 208 | scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); 209 | 210 | } else { 211 | 212 | // camera neither orthographic or perspective 213 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 214 | 215 | } 216 | 217 | }; 218 | 219 | this.dollyIn = function ( dollyScale ) { 220 | 221 | if ( dollyScale === undefined ) { 222 | 223 | dollyScale = getZoomScale(); 224 | 225 | } 226 | 227 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 228 | 229 | scale /= dollyScale; 230 | 231 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 232 | 233 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); 234 | scope.object.updateProjectionMatrix(); 235 | scope.dispatchEvent( changeEvent ); 236 | 237 | } else { 238 | 239 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 240 | 241 | } 242 | 243 | }; 244 | 245 | this.dollyOut = function ( dollyScale ) { 246 | 247 | if ( dollyScale === undefined ) { 248 | 249 | dollyScale = getZoomScale(); 250 | 251 | } 252 | 253 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 254 | 255 | scale *= dollyScale; 256 | 257 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 258 | 259 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); 260 | scope.object.updateProjectionMatrix(); 261 | scope.dispatchEvent( changeEvent ); 262 | 263 | } else { 264 | 265 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 266 | 267 | } 268 | 269 | }; 270 | 271 | this.update = function () { 272 | 273 | var position = this.object.position; 274 | 275 | offset.copy( position ).sub( this.target ); 276 | 277 | // rotate offset to "y-axis-is-up" space 278 | offset.applyQuaternion( quat ); 279 | 280 | // angle from z-axis around y-axis 281 | 282 | theta = Math.atan2( offset.x, offset.z ); 283 | 284 | // angle from y-axis 285 | 286 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 287 | 288 | if ( this.autoRotate && state === STATE.NONE ) { 289 | 290 | this.rotateLeft( getAutoRotationAngle() ); 291 | 292 | } 293 | 294 | theta += thetaDelta; 295 | phi += phiDelta; 296 | 297 | // restrict theta to be between desired limits 298 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); 299 | 300 | // restrict phi to be between desired limits 301 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 302 | 303 | // restrict phi to be betwee EPS and PI-EPS 304 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 305 | 306 | var radius = offset.length() * scale; 307 | 308 | // restrict radius to be between desired limits 309 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 310 | 311 | // move target to panned location 312 | this.target.add( pan ); 313 | 314 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 315 | offset.y = radius * Math.cos( phi ); 316 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 317 | 318 | // rotate offset back to "camera-up-vector-is-up" space 319 | offset.applyQuaternion( quatInverse ); 320 | 321 | position.copy( this.target ).add( offset ); 322 | 323 | this.object.lookAt( this.target ); 324 | 325 | thetaDelta = 0; 326 | phiDelta = 0; 327 | scale = 1; 328 | pan.set( 0, 0, 0 ); 329 | 330 | // update condition is: 331 | // min(camera displacement, camera rotation in radians)^2 > EPS 332 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 333 | 334 | if ( lastPosition.distanceToSquared( this.object.position ) > EPS 335 | || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) { 336 | 337 | this.dispatchEvent( changeEvent ); 338 | 339 | lastPosition.copy( this.object.position ); 340 | lastQuaternion.copy (this.object.quaternion ); 341 | 342 | } 343 | 344 | }; 345 | 346 | 347 | this.reset = function () { 348 | 349 | state = STATE.NONE; 350 | 351 | this.target.copy( this.target0 ); 352 | this.object.position.copy( this.position0 ); 353 | this.object.zoom = this.zoom0; 354 | 355 | this.object.updateProjectionMatrix(); 356 | this.dispatchEvent( changeEvent ); 357 | 358 | this.update(); 359 | 360 | }; 361 | 362 | this.getPolarAngle = function () { 363 | 364 | return phi; 365 | 366 | }; 367 | 368 | this.getAzimuthalAngle = function () { 369 | 370 | return theta 371 | 372 | }; 373 | 374 | function getAutoRotationAngle() { 375 | 376 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 377 | 378 | } 379 | 380 | function getZoomScale() { 381 | 382 | return Math.pow( 0.95, scope.zoomSpeed ); 383 | 384 | } 385 | 386 | function onMouseDown( event ) { 387 | 388 | if ( scope.enabled === false ) return; 389 | event.preventDefault(); 390 | 391 | if ( event.button === scope.mouseButtons.ORBIT ) { 392 | if ( scope.noRotate === true ) return; 393 | 394 | state = STATE.ROTATE; 395 | 396 | rotateStart.set( event.clientX, event.clientY ); 397 | 398 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 399 | if ( scope.noZoom === true ) return; 400 | 401 | state = STATE.DOLLY; 402 | 403 | dollyStart.set( event.clientX, event.clientY ); 404 | 405 | } else if ( event.button === scope.mouseButtons.PAN ) { 406 | if ( scope.noPan === true ) return; 407 | 408 | state = STATE.PAN; 409 | 410 | panStart.set( event.clientX, event.clientY ); 411 | 412 | } 413 | 414 | if ( state !== STATE.NONE ) { 415 | document.addEventListener( 'mousemove', onMouseMove, false ); 416 | document.addEventListener( 'mouseup', onMouseUp, false ); 417 | scope.dispatchEvent( startEvent ); 418 | } 419 | 420 | } 421 | 422 | function onMouseMove( event ) { 423 | 424 | if ( scope.enabled === false ) return; 425 | 426 | event.preventDefault(); 427 | 428 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 429 | 430 | if ( state === STATE.ROTATE ) { 431 | 432 | if ( scope.noRotate === true ) return; 433 | 434 | rotateEnd.set( event.clientX, event.clientY ); 435 | rotateDelta.subVectors( rotateEnd, rotateStart ); 436 | 437 | // rotating across whole screen goes 360 degrees around 438 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 439 | 440 | // rotating up and down along whole screen attempts to go 360, but limited to 180 441 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 442 | 443 | rotateStart.copy( rotateEnd ); 444 | 445 | } else if ( state === STATE.DOLLY ) { 446 | 447 | if ( scope.noZoom === true ) return; 448 | 449 | dollyEnd.set( event.clientX, event.clientY ); 450 | dollyDelta.subVectors( dollyEnd, dollyStart ); 451 | 452 | if ( dollyDelta.y > 0 ) { 453 | 454 | scope.dollyIn(); 455 | 456 | } else if ( dollyDelta.y < 0 ) { 457 | 458 | scope.dollyOut(); 459 | 460 | } 461 | 462 | dollyStart.copy( dollyEnd ); 463 | 464 | } else if ( state === STATE.PAN ) { 465 | 466 | if ( scope.noPan === true ) return; 467 | 468 | panEnd.set( event.clientX, event.clientY ); 469 | panDelta.subVectors( panEnd, panStart ); 470 | 471 | scope.pan( panDelta.x, panDelta.y ); 472 | 473 | panStart.copy( panEnd ); 474 | 475 | } 476 | 477 | if ( state !== STATE.NONE ) scope.update(); 478 | 479 | } 480 | 481 | function onMouseUp( /* event */ ) { 482 | 483 | if ( scope.enabled === false ) return; 484 | 485 | document.removeEventListener( 'mousemove', onMouseMove, false ); 486 | document.removeEventListener( 'mouseup', onMouseUp, false ); 487 | scope.dispatchEvent( endEvent ); 488 | state = STATE.NONE; 489 | 490 | } 491 | 492 | function onMouseWheel( event ) { 493 | 494 | if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return; 495 | 496 | event.preventDefault(); 497 | event.stopPropagation(); 498 | 499 | var delta = 0; 500 | 501 | if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 502 | 503 | delta = event.wheelDelta; 504 | 505 | } else if ( event.detail !== undefined ) { // Firefox 506 | 507 | delta = - event.detail; 508 | 509 | } 510 | 511 | if ( delta > 0 ) { 512 | 513 | scope.dollyOut(); 514 | 515 | } else if ( delta < 0 ) { 516 | 517 | scope.dollyIn(); 518 | 519 | } 520 | 521 | scope.update(); 522 | scope.dispatchEvent( startEvent ); 523 | scope.dispatchEvent( endEvent ); 524 | 525 | } 526 | 527 | function onKeyDown( event ) { 528 | 529 | if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; 530 | 531 | switch ( event.keyCode ) { 532 | 533 | case scope.keys.UP: 534 | scope.pan( 0, scope.keyPanSpeed ); 535 | scope.update(); 536 | break; 537 | 538 | case scope.keys.BOTTOM: 539 | scope.pan( 0, - scope.keyPanSpeed ); 540 | scope.update(); 541 | break; 542 | 543 | case scope.keys.LEFT: 544 | scope.pan( scope.keyPanSpeed, 0 ); 545 | scope.update(); 546 | break; 547 | 548 | case scope.keys.RIGHT: 549 | scope.pan( - scope.keyPanSpeed, 0 ); 550 | scope.update(); 551 | break; 552 | 553 | } 554 | 555 | } 556 | 557 | function touchstart( event ) { 558 | 559 | if ( scope.enabled === false ) return; 560 | 561 | switch ( event.touches.length ) { 562 | 563 | case 1: // one-fingered touch: rotate 564 | 565 | if ( scope.noRotate === true ) return; 566 | 567 | state = STATE.TOUCH_ROTATE; 568 | 569 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 570 | break; 571 | 572 | case 2: // two-fingered touch: dolly 573 | 574 | if ( scope.noZoom === true ) return; 575 | 576 | state = STATE.TOUCH_DOLLY; 577 | 578 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 579 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 580 | var distance = Math.sqrt( dx * dx + dy * dy ); 581 | dollyStart.set( 0, distance ); 582 | break; 583 | 584 | case 3: // three-fingered touch: pan 585 | 586 | if ( scope.noPan === true ) return; 587 | 588 | state = STATE.TOUCH_PAN; 589 | 590 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 591 | break; 592 | 593 | default: 594 | 595 | state = STATE.NONE; 596 | 597 | } 598 | 599 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); 600 | 601 | } 602 | 603 | function touchmove( event ) { 604 | 605 | if ( scope.enabled === false ) return; 606 | 607 | event.preventDefault(); 608 | event.stopPropagation(); 609 | 610 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 611 | 612 | switch ( event.touches.length ) { 613 | 614 | case 1: // one-fingered touch: rotate 615 | 616 | if ( scope.noRotate === true ) return; 617 | if ( state !== STATE.TOUCH_ROTATE ) return; 618 | 619 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 620 | rotateDelta.subVectors( rotateEnd, rotateStart ); 621 | 622 | // rotating across whole screen goes 360 degrees around 623 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 624 | // rotating up and down along whole screen attempts to go 360, but limited to 180 625 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 626 | 627 | rotateStart.copy( rotateEnd ); 628 | 629 | scope.update(); 630 | break; 631 | 632 | case 2: // two-fingered touch: dolly 633 | 634 | if ( scope.noZoom === true ) return; 635 | if ( state !== STATE.TOUCH_DOLLY ) return; 636 | 637 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 638 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 639 | var distance = Math.sqrt( dx * dx + dy * dy ); 640 | 641 | dollyEnd.set( 0, distance ); 642 | dollyDelta.subVectors( dollyEnd, dollyStart ); 643 | 644 | if ( dollyDelta.y > 0 ) { 645 | 646 | scope.dollyOut(); 647 | 648 | } else if ( dollyDelta.y < 0 ) { 649 | 650 | scope.dollyIn(); 651 | 652 | } 653 | 654 | dollyStart.copy( dollyEnd ); 655 | 656 | scope.update(); 657 | break; 658 | 659 | case 3: // three-fingered touch: pan 660 | 661 | if ( scope.noPan === true ) return; 662 | if ( state !== STATE.TOUCH_PAN ) return; 663 | 664 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 665 | panDelta.subVectors( panEnd, panStart ); 666 | 667 | scope.pan( panDelta.x, panDelta.y ); 668 | 669 | panStart.copy( panEnd ); 670 | 671 | scope.update(); 672 | break; 673 | 674 | default: 675 | 676 | state = STATE.NONE; 677 | 678 | } 679 | 680 | } 681 | 682 | function touchend( /* event */ ) { 683 | 684 | if ( scope.enabled === false ) return; 685 | 686 | scope.dispatchEvent( endEvent ); 687 | state = STATE.NONE; 688 | 689 | } 690 | 691 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 692 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 693 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 694 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 695 | 696 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 697 | this.domElement.addEventListener( 'touchend', touchend, false ); 698 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 699 | 700 | window.addEventListener( 'keydown', onKeyDown, false ); 701 | 702 | // force an update at start 703 | this.update(); 704 | 705 | }; 706 | 707 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 708 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 709 | 710 | 711 | module.exports = THREE.OrbitControls; 712 | -------------------------------------------------------------------------------- /src/core/settings.js: -------------------------------------------------------------------------------- 1 | // need restart 2 | exports.useStats = false; 3 | exports.textureWidth = 1024; 4 | exports.textureHeight = 1024; 5 | exports.isPhysicsActive = false; 6 | exports.isMouseActive = true; 7 | exports.isMouseVisible = true; 8 | exports.gravityAllOnClick = false; 9 | exports.mouseZRatio = 0.95; 10 | exports.resetAnimation = 0; 11 | 12 | -------------------------------------------------------------------------------- /src/fallback/mobile.js: -------------------------------------------------------------------------------- 1 | var isMobile = /(iPad|iPhone|Android)/i.test(navigator.userAgent); 2 | 3 | exports.pass = pass; 4 | 5 | var _callback; 6 | 7 | function pass(func) { 8 | if(isMobile) { 9 | _callback = func; 10 | init(); 11 | } else { 12 | func(); 13 | } 14 | } 15 | 16 | var _container; 17 | var _bypass; 18 | 19 | function init() { 20 | _container = document.querySelector('.mobile'); 21 | _container.style.display = 'block'; 22 | 23 | _bypass = document.querySelector('.mobile-bypass'); 24 | if(_bypass) _bypass.addEventListener('click', _onByPassClick); 25 | } 26 | 27 | function _onByPassClick() { 28 | _container.parentNode.removeChild(_container); 29 | _callback(); 30 | } 31 | -------------------------------------------------------------------------------- /src/glsl/fbo.vert: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_Position = vec4( position, 1.0 ); 3 | } 4 | -------------------------------------------------------------------------------- /src/glsl/fboThrough.frag: -------------------------------------------------------------------------------- 1 | uniform vec2 resolution; 2 | uniform sampler2D texture; 3 | 4 | void main() { 5 | vec2 uv = gl_FragCoord.xy / resolution.xy; 6 | vec3 color = texture2D( texture, uv ).xyz; 7 | gl_FragColor = vec4( color, 1.0 ); 8 | } 9 | -------------------------------------------------------------------------------- /src/glsl/head.frag: -------------------------------------------------------------------------------- 1 | #define PHONG 2 | 3 | uniform vec3 diffuse; 4 | uniform vec3 emissive; 5 | uniform vec3 specular; 6 | uniform float shininess; 7 | uniform float opacity; 8 | uniform float normalNoise; 9 | 10 | //chunk(common); 11 | //chunk(color_pars_fragment); 12 | //chunk(uv_pars_fragment); 13 | //chunk(uv2_pars_fragment); 14 | //chunk(map_pars_fragment); 15 | //chunk(alphamap_pars_fragment); 16 | //chunk(aomap_pars_fragment); 17 | //chunk(lightmap_pars_fragment); 18 | //chunk(emissivemap_pars_fragment); 19 | //chunk(envmap_pars_fragment); 20 | //chunk(fog_pars_fragment); 21 | //chunk(bsdfs); 22 | //chunk(lights_pars); 23 | //chunk(lights_phong_pars_fragment); 24 | //chunk(shadowmap_pars_fragment); 25 | //chunk(bumpmap_pars_fragment); 26 | //chunk(normalmap_pars_fragment); 27 | //chunk(specularmap_pars_fragment); 28 | //chunk(logdepthbuf_pars_fragment); 29 | 30 | vec3 rotateY(vec3 v, float x) 31 | { 32 | return vec3( 33 | cos(x)*v.x - sin(x)*v.z, 34 | v.y, 35 | sin(x)*v.x + cos(x)*v.z 36 | ); 37 | } 38 | 39 | vec3 rotateX(vec3 v, float x) 40 | { 41 | return vec3( 42 | v.x, 43 | v.y*cos(x) - v.z*sin(x), 44 | v.y*sin(x) + v.z*cos(x) 45 | ); 46 | } 47 | 48 | vec3 rotateZ(vec3 v, float x) 49 | { 50 | return vec3( 51 | v.x*cos(x) - v.y*sin(x), 52 | v.x*sin(x) + v.y*cos(x), 53 | v.z 54 | ); 55 | } 56 | 57 | #pragma glslify: snoise3 = require(glsl-noise/simplex/3d) 58 | #pragma glslify: random = require(glsl-random) 59 | 60 | void main() { 61 | 62 | float d = min(1.0, length(gl_PointCoord.xy - .5) * 2.0); 63 | 64 | vec4 diffuseColor = vec4( diffuse, opacity ); 65 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); 66 | vec3 totalEmissiveLight = emissive; 67 | 68 | //chunk(logdepthbuf_fragment); 69 | //chunk(map_fragment); 70 | //chunk(color_fragment); 71 | //chunk(alphamap_fragment); 72 | //chunk(alphatest_fragment); 73 | //chunk(specularmap_fragment); 74 | //chunk(normal_fragment); 75 | 76 | 77 | normal = rotateZ(normal, (random(gl_PointCoord.xy + 2.0) - 0.5) * normalNoise); 78 | normal = rotateY(normal, (random(gl_PointCoord.yx) - 0.5) * normalNoise); 79 | normal = normalize(normal); 80 | 81 | //chunk(emissivemap_fragment); 82 | //chunk(shadowmap_fragment); 83 | 84 | #ifdef USE_SHADOWMAP 85 | // for ( int i = 0; i < NUM_SHADOWS; i ++ ) { 86 | 87 | // } 88 | #endif 89 | 90 | // accumulation 91 | //chunk(lights_phong_fragment); 92 | //chunk(lights_template); 93 | //_chunk(lightmap_fragment); 94 | 95 | // modulation 96 | //chunk(aomap_fragment); 97 | 98 | vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight; 99 | 100 | //chunk(envmap_fragment); 101 | //chunk(linear_to_gamma_fragment); 102 | 103 | //chunk(fog_fragment); 104 | 105 | 106 | 107 | gl_FragColor = vec4( outgoingLight, diffuseColor.a ); 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/glsl/head.vert: -------------------------------------------------------------------------------- 1 | #define PHONG 2 | 3 | uniform sampler2D textureDefaultPosition; 4 | uniform sampler2D texturePosition; 5 | 6 | varying vec3 vViewPosition; 7 | 8 | const float EPS = 0.0001; 9 | 10 | vec3 rotateY(vec3 v, float x) 11 | { 12 | return vec3( 13 | cos(x)*v.x - sin(x)*v.z, 14 | v.y, 15 | sin(x)*v.x + cos(x)*v.z 16 | ); 17 | } 18 | 19 | vec3 rotateX(vec3 v, float x) 20 | { 21 | return vec3( 22 | v.x, 23 | v.y*cos(x) - v.z*sin(x), 24 | v.y*sin(x) + v.z*cos(x) 25 | ); 26 | } 27 | 28 | vec3 rotateZ(vec3 v, float x) 29 | { 30 | return vec3( 31 | v.x*cos(x) - v.y*sin(x), 32 | v.x*sin(x) + v.y*cos(x), 33 | v.z 34 | ); 35 | } 36 | 37 | 38 | #ifndef FLAT_SHADED 39 | 40 | varying vec3 vNormal; 41 | 42 | #endif 43 | 44 | //chunk(common); 45 | //chunk(uv_pars_vertex); 46 | //chunk(uv2_pars_vertex); 47 | //chunk(displacementmap_pars_vertex); 48 | //chunk(envmap_pars_vertex); 49 | //chunk(lights_phong_pars_vertex); 50 | //chunk(color_pars_vertex); 51 | //chunk(morphtarget_pars_vertex); 52 | //chunk(skinning_pars_vertex); 53 | //chunk(shadowmap_pars_vertex); 54 | //chunk(logdepthbuf_pars_vertex); 55 | 56 | void main() { 57 | 58 | //chunk(uv_vertex); 59 | //chunk(uv2_vertex); 60 | //chunk(color_vertex); 61 | 62 | //chunk(beginnormal_vertex); 63 | //chunk(morphnormal_vertex); 64 | //chunk(skinbase_vertex); 65 | //chunk(skinnormal_vertex); 66 | //chunk(defaultnormal_vertex); 67 | 68 | #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED 69 | 70 | vec3 pos = texture2D( texturePosition, position.xy ).xyz; 71 | vec3 defaultPos = texture2D( textureDefaultPosition, position.xy ).xyz; 72 | float offsetDistance = distance(pos, defaultPos); 73 | 74 | pos += normal * 0.5; 75 | 76 | transformedNormal = rotateX(transformedNormal, offsetDistance * 0.02 * sin(fract(position.z * 21321.5125))); 77 | transformedNormal = rotateY(transformedNormal, offsetDistance * 0.02 * sin(fract(position.z * 51211.41))); 78 | 79 | vNormal = normalize( transformedNormal ); 80 | 81 | #endif 82 | 83 | //chunk(begin_vertex); 84 | transformed = pos; 85 | 86 | //chunk(displacementmap_vertex); 87 | //chunk(morphtarget_vertex); 88 | //chunk(skinning_vertex); 89 | //chunk(project_vertex); 90 | //chunk(logdepthbuf_vertex); 91 | 92 | vViewPosition = - mvPosition.xyz; 93 | 94 | 95 | 96 | //chunk(worldpos_vertex); 97 | //chunk(envmap_vertex); 98 | //chunk(lights_phong_vertex); 99 | //chunk(shadowmap_vertex); 100 | 101 | gl_PointSize = mix(2600.0, 800.0, smoothstep(EPS, 2.0, offsetDistance)) / length( mvPosition.xyz ); 102 | } 103 | -------------------------------------------------------------------------------- /src/glsl/headDepth.frag: -------------------------------------------------------------------------------- 1 | 2 | 3 | varying float vDepthOffset; 4 | 5 | //chunk(common); 6 | //chunk(logdepthbuf_pars_fragment); 7 | 8 | vec4 pack_depth( const in float depth ) { 9 | 10 | const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 ); 11 | const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 ); 12 | vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 ); 13 | res -= res.xxyz * bit_mask; 14 | return res; 15 | 16 | } 17 | 18 | void main() { 19 | 20 | // float d = min(1.0, length(gl_PointCoord.xy - .5) * 2.0); 21 | 22 | //chunk(logdepthbuf_fragment); 23 | 24 | #ifdef USE_LOGDEPTHBUF_EXT 25 | 26 | gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT ); 27 | 28 | #else 29 | 30 | //chunk(skinbase_vertex); 31 | gl_FragData[ 0 ] = pack_depth(gl_FragCoord.z - 1.0); 32 | 33 | #endif 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/glsl/headDepth.vert: -------------------------------------------------------------------------------- 1 | 2 | uniform sampler2D textureDefaultPosition; 3 | uniform sampler2D texturePosition; 4 | 5 | const float EPS = 0.0001; 6 | 7 | //chunk(common); 8 | //chunk(morphtarget_pars_vertex); 9 | //chunk(skinning_pars_vertex); 10 | //chunk(logdepthbuf_pars_vertex); 11 | 12 | void main() { 13 | 14 | //chunk(skinbase_vertex); 15 | 16 | //chunk(begin_vertex); 17 | 18 | vec3 pos = texture2D( texturePosition, position.xy ).xyz; 19 | vec3 defaultPos = texture2D( textureDefaultPosition, position.xy ).xyz; 20 | float offsetDistance = distance(pos, defaultPos); 21 | transformed = pos; 22 | 23 | //chunk(morphtarget_vertex); 24 | //chunk(skinning_vertex); 25 | //chunk(project_vertex); 26 | //chunk(logdepthbuf_vertex); 27 | 28 | gl_PointSize = mix(1800.0, 400.0, smoothstep(EPS, 2.0, offsetDistance)) / length( mvPosition.xyz ); 29 | } 30 | -------------------------------------------------------------------------------- /src/glsl/noise.glsl: -------------------------------------------------------------------------------- 1 | float rand(float n){return fract(sin(n) * 43758.5453123);} 2 | 3 | float noise(float p){ 4 | float fl = floor(p); 5 | float fc = fract(p); 6 | return mix(rand(fl), rand(fl + 1.0), fc); 7 | } 8 | 9 | 10 | #pragma glslify: export(noise) 11 | -------------------------------------------------------------------------------- /src/glsl/particle.frag: -------------------------------------------------------------------------------- 1 | 2 | // chunk(common); 3 | // chunk(fog_pars_fragment); 4 | // chunk(shadowmap_pars_fragment); 5 | 6 | #ifdef USE_BILLBOARD 7 | 8 | #else 9 | 10 | varying float vAlpha; 11 | 12 | #endif 13 | 14 | void main() { 15 | 16 | vec3 outgoingLight = vec3(1.0); 17 | 18 | // chunk(shadowmap_fragment); 19 | 20 | outgoingLight *= 0.1 + pow(shadowMask, vec3(1.5)) * 0.9; 21 | 22 | // chunk(fog_fragment); 23 | // chunk(linear_to_gamma_fragment); 24 | 25 | 26 | #ifdef USE_BILLBOARD 27 | 28 | gl_FragColor = vec4( outgoingLight, 1.0 ); 29 | 30 | #else 31 | 32 | float d = length(gl_PointCoord.xy - .5) * 2.0; 33 | 34 | gl_FragColor = vec4( outgoingLight, vAlpha ) * (1.0 - step(1.0, d)) ; 35 | 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /src/glsl/particle.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 fboUV; 2 | uniform sampler2D texturePosition; 3 | 4 | #ifdef USE_BILLBOARD 5 | 6 | attribute vec3 positionFlip; 7 | uniform float flipRatio; 8 | 9 | #else 10 | 11 | varying float vAlpha; 12 | 13 | #endif 14 | 15 | // chunk(shadowmap_pars_vertex); 16 | 17 | void main() { 18 | 19 | vec4 posInfo = texture2D( texturePosition, fboUV ); 20 | vec3 pos = posInfo.xyz; 21 | 22 | vec4 worldPosition = modelMatrix * vec4( pos, 1.0 ); 23 | vec4 mvPosition = viewMatrix * worldPosition; 24 | 25 | #ifdef USE_BILLBOARD 26 | 27 | vec4 flipOffset = vec4(mix(position, positionFlip, flipRatio) * 0.5, 1.0); 28 | worldPosition += flipOffset; 29 | 30 | #else 31 | gl_PointSize = ( 500.0 / length( mvPosition.xyz ) ); 32 | mvPosition.y += gl_PointSize * 0.5; 33 | 34 | #endif 35 | 36 | // chunk(shadowmap_vertex); 37 | 38 | 39 | #ifdef USE_BILLBOARD 40 | 41 | gl_Position = projectionMatrix * (mvPosition + flipOffset); 42 | 43 | #else 44 | 45 | vAlpha = smoothstep(0.0, 0.1, posInfo.w); 46 | gl_Position = projectionMatrix * mvPosition; 47 | 48 | #endif 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/glsl/position.frag: -------------------------------------------------------------------------------- 1 | uniform vec2 resolution; 2 | 3 | uniform sampler2D textureVelocity; 4 | uniform sampler2D texturePosition; 5 | uniform sampler2D textureDefaultPosition; 6 | 7 | const float EPS = 0.0001; 8 | 9 | uniform float resetAnimation; 10 | 11 | void main() { 12 | 13 | vec2 uv = gl_FragCoord.xy / resolution.xy; 14 | 15 | vec3 velocity = texture2D( textureVelocity, uv ).xyz; 16 | vec3 position = texture2D( texturePosition, uv ).xyz; 17 | vec3 defaultPosition = texture2D( textureDefaultPosition, uv ).xyz; 18 | 19 | position += velocity; 20 | 21 | position += (defaultPosition - position) * pow(smoothstep(EPS, 1.0, resetAnimation), 3.0); 22 | 23 | position.y = max(position.y, -200.0); 24 | 25 | gl_FragColor = vec4(position, 1.0); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/glsl/velocity.frag: -------------------------------------------------------------------------------- 1 | uniform vec2 resolution; 2 | uniform vec3 mouse3d; 3 | uniform vec3 mouse3dVelocity; 4 | uniform float isPhysicsActive; 5 | uniform float resetAnimation; 6 | 7 | uniform float mouseForce; 8 | uniform float mouseRadius; 9 | uniform float gravity; 10 | 11 | uniform sampler2D textureVelocity; 12 | uniform sampler2D texturePosition; 13 | uniform sampler2D textureDefaultPosition; 14 | 15 | const float EPS = 0.0001; 16 | 17 | #pragma glslify: random = require(glsl-random) 18 | 19 | void main() { 20 | 21 | vec2 uv = gl_FragCoord.xy / resolution.xy; 22 | 23 | vec3 velocity = texture2D( textureVelocity, uv ).xyz; 24 | vec3 defaultPosition = texture2D( textureDefaultPosition, uv ).xyz; 25 | vec3 position = texture2D( texturePosition, uv ).xyz; 26 | 27 | float positionOffset = distance(position, defaultPosition); 28 | 29 | float toMouseStrength = length(position - mouse3d) /mouseRadius; 30 | toMouseStrength = step(-1.0, -toMouseStrength); 31 | 32 | if(position.y + velocity.y < -199.0) { 33 | float strength = abs(velocity.y) * 0.2; 34 | velocity.y *= -0.4 - random(uv + 2.0) * 0.2; 35 | velocity.x += (random(position.xy + strength) - 0.5); 36 | velocity.z += (random(position.zy) - 0.5); 37 | velocity.xz *= strength; 38 | } else { 39 | velocity.xz *= 0.99; 40 | velocity.y -= step(EPS, positionOffset + isPhysicsActive) * ( (1.0 - (defaultPosition.y + 200.0) / 500.0) + random(defaultPosition.xy)) * gravity; 41 | } 42 | 43 | velocity += (normalize(position - mouse3d) + mouse3dVelocity * 0.2) * pow(toMouseStrength, 2.0) * (1.0 + random(vec2(position.x + position.y, position.z)) * 0.3) * mouseForce; 44 | velocity *= 1.0 - step(EPS, resetAnimation); 45 | 46 | gl_FragColor = vec4(velocity, 1.0); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/glsl/vignette.frag: -------------------------------------------------------------------------------- 1 | varying vec2 vUv; 2 | uniform vec2 uResolution; 3 | uniform float uAlpha; 4 | uniform float uTime; 5 | 6 | const float sqrt2 = 1.41421356237309; 7 | #pragma glslify: noise = require(./noise) 8 | 9 | void main() { 10 | 11 | vec2 toCenter = gl_FragCoord.xy / uResolution - 0.5; 12 | float angle = atan(toCenter.y / toCenter.x); 13 | 14 | float a = smoothstep(0.3 + 0.3 * noise(angle * 32313.12513 + uTime) , sqrt2, max(length(toCenter) * 2.0, 0.0)); 15 | 16 | gl_FragColor = vec4(0.0, 0.0, 0.0, a * uAlpha); 17 | } 18 | -------------------------------------------------------------------------------- /src/glsl/vignette.vert: -------------------------------------------------------------------------------- 1 | void main() { 2 | vec2 pos = position.xy; 3 | gl_Position = vec4( pos, 0.0, 1.0 ); 4 | } 5 | -------------------------------------------------------------------------------- /src/helpers/shaderParse.js: -------------------------------------------------------------------------------- 1 | var THREE = require('three'); 2 | 3 | var threeChunkReplaceRegExp = /\/\/\s?chunk_replace\s(.+)([\d\D]+)\/\/\s?end_chunk_replace/gm; 4 | var threeChunkRegExp = /\/\/\s?chunk\(\s?(\w+)\s?\);/g; 5 | var glslifyBugFixRegExp = /(_\d+_\d+)(_\d+_\d+)+/g; 6 | var glslifyGlobalRegExp = /GLOBAL_VAR_(.+)(_\d+_\d+)+/g; 7 | 8 | var _chunkReplaceObj; 9 | 10 | function _storeChunkReplaceParse(shader) { 11 | _chunkReplaceObj = {}; 12 | return shader.replace(threeChunkReplaceRegExp, _storeChunkReplaceFunc); 13 | } 14 | 15 | function _threeChunkParse(shader) { 16 | return shader.replace(threeChunkRegExp, _replaceThreeChunkFunc); 17 | } 18 | 19 | function _glslifyBugFixParse(shader) { 20 | return shader.replace(glslifyBugFixRegExp, _returnFirst); 21 | } 22 | 23 | function _glslifyGlobalParse(shader) { 24 | return shader.replace(glslifyGlobalRegExp, _returnFirst); 25 | } 26 | 27 | function _storeChunkReplaceFunc(a, b, c) { 28 | _chunkReplaceObj[b.trim()] = c; 29 | return ''; 30 | } 31 | 32 | function _replaceThreeChunkFunc(a, b) { 33 | var str = THREE.ShaderChunk[b] + '\n'; 34 | for(var id in _chunkReplaceObj) { 35 | str = str.replace(id, _chunkReplaceObj[id]); 36 | } 37 | return str; 38 | } 39 | 40 | function _returnFirst(a, b) { 41 | return b; 42 | } 43 | 44 | function parse(shader) { 45 | shader = _storeChunkReplaceParse(shader); 46 | shader = _threeChunkParse(shader); 47 | shader = _glslifyBugFixParse(shader); 48 | return _glslifyGlobalParse(shader); 49 | } 50 | 51 | module.exports = parse; 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var quickLoader = require('quick-loader'); 2 | var dat = require('dat-gui'); 3 | var Stats = require('stats.js'); 4 | var css = require('dom-css'); 5 | var raf = require('raf'); 6 | 7 | var THREE = require('three'); 8 | 9 | var OrbitControls = require('./controls/OrbitControls'); 10 | var settings = require('./core/settings'); 11 | 12 | var head = require('./3d/head'); 13 | var ground = require('./3d/ground'); 14 | var vignette = require('./3d/vignette'); 15 | var lights = require('./3d/lights'); 16 | var fbo = require('./3d/fbo'); 17 | 18 | var math = require('./utils/math'); 19 | var ease = require('./utils/ease'); 20 | var mobile = require('./fallback/mobile'); 21 | 22 | var undef; 23 | var _gui; 24 | var _stats; 25 | 26 | var _width = 0; 27 | var _height = 0; 28 | 29 | var _control; 30 | var _camera; 31 | var _scene; 32 | var _renderer; 33 | var _mouseMesh; 34 | 35 | var _renderingGui; 36 | var _similuationGui; 37 | var _envGui; 38 | 39 | var _time = 0; 40 | var _ray = new THREE.Ray(); 41 | 42 | var _initAnimation = 0; 43 | var _hasSetDefault = false; 44 | 45 | var _logo; 46 | var _footerItems; 47 | 48 | var EPS = 0.00001; 49 | 50 | function init() { 51 | 52 | if(settings.useStats) { 53 | _stats = new Stats(); 54 | css(_stats.domElement, { 55 | position : 'absolute', 56 | left : '0px', 57 | top : '0px', 58 | zIndex : 2048 59 | }); 60 | 61 | document.body.appendChild( _stats.domElement ); 62 | } 63 | 64 | settings.mouse = new THREE.Vector2(-9999,0); 65 | settings.mouse3d = _ray.origin; 66 | 67 | _renderer = new THREE.WebGLRenderer({ 68 | // antialias : true 69 | }); 70 | _renderer.setClearColor(0x444444); 71 | _renderer.shadowMap.type = THREE.PCFSoftShadowMap; 72 | _renderer.shadowMap.enabled = true; 73 | document.body.appendChild(_renderer.domElement); 74 | 75 | _scene = new THREE.Scene(); 76 | _scene.fog = new THREE.FogExp2( 0x444444, 0.001 ); 77 | 78 | _camera = new THREE.PerspectiveCamera( 45, 1, 200, 3000); 79 | _camera.position.set(134, 5, 800).normalize().multiplyScalar(1500); 80 | settings.cameraPosition = _camera.position; 81 | 82 | _control = new OrbitControls( _camera, _renderer.domElement ); 83 | // _control.minDistance = 600; 84 | _control.maxDistance = 1500; 85 | _control.minPolarAngle = 0.3; 86 | _control.maxPolarAngle = Math.PI / 2; 87 | _control.noPan = true; 88 | _control.update(); 89 | 90 | lights.init(); 91 | _scene.add(lights.mesh); 92 | 93 | var envMap = settings.envMap = (new THREE.TextureLoader()).load('images/env.jpg'); 94 | envMap.format = THREE.RGBFormat; 95 | envMap.wrapS = envMap.wrapT = THREE.MirroredRepeatWrapping; 96 | envMap.mapping = THREE.EquirectangularReflectionMapping; 97 | envMap.magFilter = THREE.LinearFilter; 98 | envMap.minFilter = THREE.LinearMipMapLinearFilter; 99 | 100 | head.init(); 101 | _scene.add(head.mesh); 102 | 103 | fbo.init(_renderer); 104 | 105 | ground.init(); 106 | _scene.add(ground.mesh); 107 | 108 | vignette.init(); 109 | _scene.add(vignette.mesh); 110 | 111 | var mouseGeometry = new THREE.IcosahedronGeometry(1, 3); 112 | _mouseMesh = new THREE.Mesh(mouseGeometry, new THREE.MeshPhongMaterial({ 113 | side: THREE.BackSide, 114 | color: 0x999999, 115 | specular: 0x444444, 116 | shininess: 20, 117 | envMap: envMap, 118 | transparent: true, 119 | opacity: 0.3 120 | })); 121 | _mouseMesh.add(new THREE.Mesh(mouseGeometry, new THREE.MeshPhongMaterial({ 122 | transparent: true, 123 | color: 0x999999, 124 | specular: 0x444444, 125 | shininess: 20, 126 | envMap: envMap, 127 | opacity: 0.05 128 | }))); 129 | 130 | 131 | _scene.add(_mouseMesh); 132 | 133 | var guiFunctions = { 134 | resetParticles : _resetParticles 135 | }; 136 | 137 | _gui = new dat.GUI(); 138 | _renderingGui = _gui.addFolder('rendering'); 139 | _renderingGui.add(head, 'normalNoise', 0, 1, 0.001); 140 | _renderingGui.add(head, 'useDiffuse').name('diffuse map'); 141 | _renderingGui.add(head, 'useEnv').name('env map'); 142 | _similuationGui = _gui.addFolder('similuation'); 143 | _similuationGui.add(settings, 'isMouseVisible').name('mouse'); 144 | _similuationGui.add(fbo.velocityUniforms.mouseRadius, 'value', 50, 300).name('mouse radius').listen(); 145 | _similuationGui.add(fbo.velocityUniforms.mouseForce, 'value', 0.01, 0.5).name('mouse force'); 146 | _similuationGui.add(settings, 'mouseZRatio', 0.8, 1.2).name('mouse z ratio'); 147 | _similuationGui.add(fbo.velocityUniforms.gravity, 'value', -1, 1).name('gravity'); 148 | _similuationGui.add(settings, 'gravityAllOnClick').name('destory on click').listen(); 149 | _envGui = _gui.addFolder('env'); 150 | _envGui.add(vignette.mesh, 'visible').name('vignette'); 151 | _envGui.add(_mouseMesh, 'visible').name('mouse visibility'); 152 | _gui.add(guiFunctions, 'resetParticles'); 153 | 154 | if(window.screen.width > 480) { 155 | _renderingGui.open(); 156 | _similuationGui.open(); 157 | _envGui.open(); 158 | } 159 | 160 | _logo = document.querySelector('.logo'); 161 | document.querySelector('.footer').style.display = 'block'; 162 | _footerItems = document.querySelectorAll('.footer span'); 163 | 164 | _gui.domElement.addEventListener('mousedown', _stopPropagation); 165 | // _gui.domElement.addEventListener('mousemove', _stopPropagation); 166 | _gui.domElement.addEventListener('touchstart', _stopPropagation); 167 | // _gui.domElement.addEventListener('touchmove', _stopPropagation); 168 | 169 | window.addEventListener('resize', _onResize); 170 | window.addEventListener('mousedown', _onDown); 171 | window.addEventListener('mousemove', _onMove); 172 | // window.addEventListener('mouseup', _onUp); 173 | window.addEventListener('touchstart', _bindTouch(_onDown)); 174 | window.addEventListener('touchmove', _bindTouch(_onMove)); 175 | // window.addEventListener('touchend', _onUp); 176 | 177 | _time = Date.now(); 178 | _onResize(); 179 | _loop(); 180 | 181 | } 182 | 183 | function _stopPropagation(evt) { 184 | evt.stopPropagation(); 185 | } 186 | 187 | function _bindTouch(func) { 188 | return function (evt) { 189 | func(evt.changedTouches[0]); 190 | }; 191 | } 192 | 193 | function _onDown(evt) { 194 | settings.isMouseActive = true; 195 | if(settings.gravityAllOnClick) { 196 | settings.isPhysicsActive = true; 197 | } 198 | _onMove(evt); 199 | } 200 | 201 | function _onMove(evt) { 202 | settings.mouse.x = (evt.pageX / _width) * 2 - 1; 203 | settings.mouse.y = -(evt.pageY / _height) * 2 + 1; 204 | } 205 | 206 | function _onResize() { 207 | _width = window.innerWidth; 208 | _height = window.innerHeight; 209 | 210 | vignette.resize(_width, _height); 211 | 212 | _camera.aspect = _width / _height; 213 | _camera.updateProjectionMatrix(); 214 | _renderer.setSize(_width, _height); 215 | 216 | } 217 | 218 | function _loop() { 219 | var newTime = Date.now(); 220 | raf(_loop); 221 | if(settings.useStats) _stats.begin(); 222 | _render(newTime - _time); 223 | if(settings.useStats) _stats.end(); 224 | _time = newTime; 225 | } 226 | 227 | function _resetParticles() { 228 | settings.resetAnimation = EPS; 229 | settings.isMouseActive = false; 230 | settings.isPhysicsActive = false; 231 | } 232 | 233 | function _setDefault() { 234 | _hasSetDefault = true; 235 | _resetParticles(); 236 | settings.gravityAllOnClick = false; 237 | settings.isPhysicsActive = false; 238 | } 239 | 240 | function _render(dt) { 241 | 242 | var ratio, ratio2, ratio3; 243 | _initAnimation = Math.min(_initAnimation + dt * 0.00015, 1); 244 | 245 | vignette.alphaUniform.value = math.lerp(0.7, 0.3, math.unLerp(0, 0.5, _initAnimation)); 246 | 247 | _control.maxDistance = _initAnimation === 1 ? 1500 : math.lerp(1500, 700, ease.easeOutSine(_initAnimation)); 248 | if(_initAnimation < 1) { 249 | _control.object.position.y = math.lerp(800, 5, ease.easeOutSine(_initAnimation)); 250 | } 251 | _control.update(); 252 | 253 | // update mouse3d 254 | _camera.updateMatrixWorld(); 255 | _ray.origin.setFromMatrixPosition( _camera.matrixWorld ); 256 | _ray.direction.set( settings.mouse.x, settings.mouse.y, 0.5 ).unproject( _camera ).sub( _ray.origin ).normalize(); 257 | var distance = _ray.origin.length() / Math.cos(Math.PI - _ray.direction.angleTo(_ray.origin)); 258 | _ray.origin.add( _ray.direction.multiplyScalar(distance * settings.mouseZRatio)); 259 | 260 | if(_initAnimation < 1) { 261 | ratio = ease.easeInOutBack(math.unLerp(0, 0.4, _initAnimation)); 262 | ratio2 = math.unLerp(0.3, 0.9, _initAnimation); 263 | ratio3 = ease.easeOutSine(math.unLerp(0.7, 1, _initAnimation)); 264 | _ray.origin.x = math.lerp(Math.sin(ratio2 * Math.PI * 4) * 150, _ray.origin.x, ratio3); 265 | _ray.origin.y = math.lerp((1 - ratio) * 400 + Math.sin(ratio2 * Math.PI * 5) * 150, _ray.origin.y, ratio3); 266 | _ray.origin.z = math.lerp(Math.sin(ratio2 * Math.PI * 6) * 150, _ray.origin.z, ratio3); 267 | fbo.velocityUniforms.mouseRadius.value = (1 - ratio) * 60 + Math.sin(ratio2 * Math.PI * 6) * 90 * ratio3 + 105; 268 | } else { 269 | if(!_hasSetDefault) { 270 | _setDefault(); 271 | } 272 | } 273 | 274 | ratio = Math.min((1 - Math.abs(_initAnimation - 0.5) * 2) * 1.2, 1); 275 | var blur = (1 - ratio) * 10; 276 | _logo.style.display = ratio ? 'block' : 'none'; 277 | if(ratio) { 278 | _logo.style.opacity = ratio; 279 | _logo.style.webkitFilter = 'blur(' + blur + 'px)'; 280 | ratio = (0.8 + Math.pow(_initAnimation, 1.5) * 0.3); 281 | if(_width < 580) ratio *= 0.5; 282 | _logo.style.transform = 'scale3d(' + ratio + ',' + ratio + ',1)'; 283 | } 284 | 285 | for(var i = 0, len = _footerItems.length; i < len; i++) { 286 | ratio = math.unLerp(0.5 + i * 0.01, 0.6 + i * 0.01, _initAnimation); 287 | _footerItems[i].style.transform = 'translate3d(0,' + ((1 - Math.pow(ratio, 3)) * 50) + 'px,0)'; 288 | } 289 | 290 | if(settings.resetAnimation > 0) { 291 | settings.resetAnimation += dt * 0.0005; 292 | } 293 | fbo.update(dt); 294 | 295 | if(settings.resetAnimation > 1) { 296 | settings.resetAnimation = 0; 297 | } 298 | 299 | head.update(dt); 300 | 301 | vignette.alphaUniform.value = 0.5; 302 | vignette.update(dt); 303 | 304 | // console.log(_camera.position.x, _camera.position.y, _camera.position.z); 305 | var mouseRadius = fbo.velocityUniforms.mouseRadius.value; 306 | _mouseMesh.scale.set(mouseRadius, mouseRadius, mouseRadius); 307 | _mouseMesh.position.copy(_ray.origin); 308 | 309 | _renderer.render(_scene, _camera); 310 | } 311 | 312 | mobile.pass(function() { 313 | // quickLoader.add('images/env.jpg', { 314 | // onLoad: function(img) { 315 | // settings.envMapImg = img; 316 | // } 317 | // }); 318 | // quickLoader.add('images/diffuse.jpg', { 319 | // onLoad: function(img) { 320 | // settings.diffuseMapImg = img; 321 | // } 322 | // }); 323 | 324 | quickLoader.add('models/LeePerrySmith.json', { 325 | onLoad: function(data) { 326 | settings.headData = data; 327 | } 328 | }); 329 | quickLoader.start(function(percent) { 330 | if(percent === 1) { 331 | init(); 332 | } 333 | }); 334 | }); 335 | -------------------------------------------------------------------------------- /src/libs/global_three/index.js: -------------------------------------------------------------------------------- 1 | module.exports = window.THREE; 2 | -------------------------------------------------------------------------------- /src/libs/global_three/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three", 3 | "version": "0.73.0", 4 | "private": true, 5 | "description": "", 6 | "main": "index.js", 7 | "dependencies": { 8 | }, 9 | "devDependencies": { 10 | }, 11 | "scripts": { 12 | }, 13 | "author": "", 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/ease.js: -------------------------------------------------------------------------------- 1 | // from https://github.com/kaelzhang/easing-functions/ 2 | var basic = { 3 | Linear: { 4 | None: function(e) { 5 | return e; 6 | } 7 | }, 8 | Quad: { 9 | In: function(e) { 10 | return e * e; 11 | }, 12 | Out: function(e) { 13 | return e * (2 - e); 14 | }, 15 | InOut: function(e) { 16 | if ((e *= 2) < 1) return 0.5 * e * e; 17 | return - 0.5 * (--e * (e - 2) - 1); 18 | } 19 | }, 20 | Cubic: { 21 | In: function(e) { 22 | return e * e * e; 23 | }, 24 | Out: function(e) { 25 | return --e * e * e + 1; 26 | }, 27 | InOut: function(e) { 28 | if ((e *= 2) < 1) return 0.5 * e * e * e; 29 | return 0.5 * ((e -= 2) * e * e + 2); 30 | } 31 | }, 32 | Quart: { 33 | In: function(e) { 34 | return e * e * e * e; 35 | }, 36 | Out: function(e) { 37 | return 1 - --e * e * e * e; 38 | }, 39 | InOut: function(e) { 40 | if ((e *= 2) < 1) return 0.5 * e * e * e * e; 41 | return - 0.5 * ((e -= 2) * e * e * e - 2); 42 | } 43 | }, 44 | Quint: { 45 | In: function(e) { 46 | return e * e * e * e * e; 47 | }, 48 | Out: function(e) { 49 | return --e * e * e * e * e + 1; 50 | }, 51 | InOut: function(e) { 52 | if ((e *= 2) < 1) return 0.5 * e * e * e * e * e; 53 | return 0.5 * ((e -= 2) * e * e * e * e + 2); 54 | } 55 | }, 56 | Sine: { 57 | In: function(e) { 58 | return 1 - Math.cos(e * Math.PI / 2); 59 | }, 60 | Out: function(e) { 61 | return Math.sin(e * Math.PI / 2); 62 | }, 63 | InOut: function(e) { 64 | return 0.5 * (1 - Math.cos(Math.PI * e)); 65 | } 66 | }, 67 | Expo: { 68 | In: function(e) { 69 | return e === 0 ? 0 : Math.pow(1024, e - 1); 70 | }, 71 | Out: function(e) { 72 | return e === 1 ? 1 : 1 - Math.pow(2, -10 * e); 73 | }, 74 | InOut: function(e) { 75 | if (e === 0) return 0; 76 | if (e === 1) return 1; 77 | if ((e *= 2) < 1) return 0.5 * Math.pow(1024, e - 1); 78 | return 0.5 * (-Math.pow(2, -10 * (e - 1)) + 2); 79 | } 80 | }, 81 | Circ: { 82 | In: function(e) { 83 | return 1 - Math.sqrt(1 - e * e); 84 | }, 85 | Out: function(e) { 86 | return Math.sqrt(1 - --e * e); 87 | }, 88 | InOut: function(e) { 89 | if ((e *= 2) < 1) return - 0.5 * (Math.sqrt(1 - e * e) - 1); 90 | return 0.5 * (Math.sqrt(1 - (e -= 2) * e) + 1); 91 | } 92 | }, 93 | Elastic: { 94 | In: function(e) { 95 | var t, n =0.1, 96 | r =0.4; 97 | if (e === 0) return 0; 98 | if (e === 1) return 1; 99 | if (!n || n < 1) { 100 | n = 1; 101 | t = r / 4; 102 | } else t = r * Math.asin(1 / n) / (2 * Math.PI); 103 | return -(n * Math.pow(2, 10 * (e -= 1)) * Math.sin((e - t) * 2 * Math.PI / r)); 104 | }, 105 | Out: function(e) { 106 | var t, n =0.1, 107 | r =0.4; 108 | if (e === 0) return 0; 109 | if (e === 1) return 1; 110 | if (!n || n < 1) { 111 | n = 1; 112 | t = r / 4; 113 | } else t = r * Math.asin(1 / n) / (2 * Math.PI); 114 | return n * Math.pow(2, -10 * e) * Math.sin((e - t) * 2 * Math.PI / r) + 1; 115 | }, 116 | InOut: function(e) { 117 | var t, n =0.1, 118 | r =0.4; 119 | if (e === 0) return 0; 120 | if (e === 1) return 1; 121 | if (!n || n < 1) { 122 | n = 1; 123 | t = r / 4; 124 | } else { 125 | t = r * Math.asin(1 / n) / (2 * Math.PI); 126 | } 127 | if ((e *= 2) < 1) return - 0.5 * n * Math.pow(2, 10 * (e -= 1)) * Math.sin((e - t) * 2 * Math.PI / r); 128 | return n * Math.pow(2, -10 * (e -= 1)) * Math.sin((e - t) * 2 * Math.PI / r) *0.5 + 1; 129 | } 130 | }, 131 | Back: { 132 | In: function(e) { 133 | var t = 1.70158; 134 | return e * e * ((t + 1) * e - t); 135 | }, 136 | Out: function(e) { 137 | var t = 1.70158; 138 | return --e * e * ((t + 1) * e + t) + 1; 139 | }, 140 | InOut: function(e) { 141 | var t = 1.70158 * 1.525; 142 | if ((e *= 2) < 1) return 0.5 * e * e * ((t + 1) * e - t); 143 | return 0.5 * ((e -= 2) * e * ((t + 1) * e + t) + 2); 144 | } 145 | }, 146 | Bounce: { 147 | In: function(e) { 148 | return 1 - basic.Bounce.Out(1 - e); 149 | }, 150 | Out: function(e) { 151 | if (e < 1 / 2.75) { 152 | return 7.5625 * e * e; 153 | } else if (e < 2 / 2.75) { 154 | return 7.5625 * (e -= 1.5 / 2.75) * e +0.75; 155 | } else if (e < 2.5 / 2.75) { 156 | return 7.5625 * (e -= 2.25 / 2.75) * e +0.9375; 157 | } else { 158 | return 7.5625 * (e -= 2.625 / 2.75) * e +0.984375; 159 | } 160 | }, 161 | InOut: function(e) { 162 | if (e <0.5) return basic.Bounce.In(e * 2) *0.5; 163 | return basic.Bounce.Out(e * 2 - 1) *0.5 +0.5; 164 | } 165 | } 166 | }; 167 | 168 | exports.basic = basic; 169 | exports.linear = basic.Linear; 170 | 171 | var id, list; 172 | for(id in basic) { 173 | if(id !== 'Linear') { 174 | list = basic[id]; 175 | exports['easeIn' + id] = list.In; 176 | exports['easeOut' + id] = list.Out; 177 | exports['easeInOut' + id] = list.InOut; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/utils/math.js: -------------------------------------------------------------------------------- 1 | for(var id in Math) { 2 | exports[id] = Math[id]; 3 | } 4 | 5 | exports.step = step; 6 | exports.smoothstep = smoothstep; 7 | exports.clamp = clamp; 8 | exports.mix = exports.lerp = mix; 9 | exports.unMix = exports.unLerp = unMix; 10 | exports.unClampedMix = exports.unClampedLerp = unClampedMix; 11 | exports.upClampedUnMix = exports.unClampedUnLerp = upClampedUnMix; 12 | exports.fract = fract; 13 | exports.hash = hash; 14 | exports.hash2 = hash2; 15 | exports.sign = sign; 16 | 17 | var PI = Math.PI; 18 | var TAU = exports.TAU = PI * 2; 19 | 20 | function step ( edge, val ) { 21 | return val < edge ? 0 : 1; 22 | } 23 | 24 | function smoothstep ( edge0, edge1, val ) { 25 | val = unMix( edge0, edge1, val ); 26 | return val * val ( 3 - val * 2 ); 27 | } 28 | 29 | function clamp ( val, min, max ) { 30 | return val < min ? min : val > max ? max : val; 31 | } 32 | 33 | function mix ( min, max, val ) { 34 | return val <= 0 ? min : val >= 1 ? max : min + ( max - min ) * val; 35 | } 36 | 37 | function unMix ( min, max, val ) { 38 | return val <= min ? 0 : val >= max ? 1 : ( val - min ) / ( max - min ); 39 | } 40 | 41 | function unClampedMix ( min, max, val ) { 42 | return min + ( max - min ) * val; 43 | } 44 | 45 | function upClampedUnMix ( min, max, val ) { 46 | return ( val - min ) / ( max - min ); 47 | } 48 | 49 | function fract ( val ) { 50 | return val - Math.floor( val ); 51 | } 52 | 53 | function hash (val) { 54 | return fract( Math.sin( val ) * 43758.5453123 ); 55 | } 56 | 57 | function hash2 (val1, val2) { 58 | return fract( Math.sin( val1 * 12.9898 + val2 * 4.1414 ) * 43758.5453 ); 59 | } 60 | 61 | function sign (val) { 62 | return val ? val < 0 ? - 1 : 1 : 0; 63 | } 64 | --------------------------------------------------------------------------------