├── .gitignore ├── README.md ├── geometryangle.js └── geometryangle.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | /css/ 2 | /js/ 3 | /init.php 4 | /index.php 5 | /sitemap.xml 6 | /test.html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Geometryangle 2 | 3 | [jQuery](http://jquery.com/) plugin that lets you create beautiful and responsive backgrounds to your site. **To get started, check out http://geometryangle.tritoncode.com/** 4 | 5 | Please consider that the project is still in beta. The current status of the milestones can be found [here](https://github.com/TritonCode/Geometryangle/milestones). 6 | 7 | ## Quick start 8 | 9 | Download the [latest release](https://github.com/TritonCode/Geometryangle/zipball/master). 10 | 11 | Put the script at the [bottom](https://developer.yahoo.com/performance/rules.html#js_bottom) of your markup right after jQuery: 12 | 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | To create a fixed background, do the following: 19 | 20 | ```html 21 | 22 |
23 |
24 | Content... 25 |
26 | 27 | ``` 28 | 29 | Call the [plugin](http://learn.jquery.com/plugins/) function and your navigation widget is ready. 30 | 31 | ```javascript 32 | $(document).ready(function(){ 33 | $('body').Geometryangle({mesh:{}, lights: [{}], line: {}, vertex: {}}); 34 | }); 35 | ``` 36 | 37 | ## Documentation 38 | 39 | The documentation is publicly available at http://geometryangle.tritoncode.com/ 40 | 41 | ## Contributing 42 | 43 | The [issue tracker](https://github.com/TritonCode/Geometryangle/issues) is the preferred channel for bug reports, features requests and submitting pull requests. 44 | 45 | **Please do not use the issue tracker for personal support requests. Stack Overflow ([`Geometryangle`](http://stackoverflow.com/questions/tagged/Geometryangle)) is a better place to get help.** 46 | 47 | ### Bug reports 48 | 49 | A bug is a **demonstrable problem** that is caused by the code in the repository. Good bug reports are extremely helpful, so thanks! 50 | 51 | Guidelines for bug reports: 52 | 53 | 1. Use the GitHub issue search — check if the issue has already been reported. 54 | 55 | 2. Check if the issue has been fixed — try to reproduce it using the latest `develop` branch in the repository. 56 | 57 | 3. Isolate the problem — ideally create a reduced test case and a live example. This [JSFiddle](http://jsfiddle.net/eqbL6vLb/) and this [JS Bin](http://jsbin.com/xuxozu/1) are helpful templates you can fork or clone. 58 | 59 | Example: 60 | 61 | > Short and descriptive example bug report title 62 | > 63 | > A summary of the issue and the browser/OS environment in which it occurs. If suitable, include the steps required to reproduce the bug. 64 | > 65 | > 1. This is the first step 66 | > 2. This is the second step 67 | > 3. Further steps, etc. 68 | > 69 | > `` - a link to the reduced test case 70 | > 71 | > Any other information you want to share that is relevant to the issue being reported. This might include the lines of code that you have identified as causing the bug, and potential solutions (and your opinions on their merits). 72 | 73 | ### Feature requests 74 | 75 | Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible. 76 | 77 | ### Pull requests 78 | 79 | Good pull requests are a fantastic help. They should remain focused in scope and avoid containing unrelated commits. 80 | 81 | **Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. 82 | 83 | Adhering to the following process is the best way to get your work included in the project: 84 | 85 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: 86 | 87 | ```bash 88 | git clone https://github.com//Geometryangle.git 89 | cd Geometryangle 90 | git remote add upstream https://github.com/TritonCode/Geometryangle.git 91 | ``` 92 | 93 | 2. If you cloned a while ago, get the latest changes from upstream: 94 | 95 | ```bash 96 | git checkout develop 97 | git pull [--rebase] upstream develop 98 | ``` 99 | 100 | 3. Create a new topic branch (off the main project `develop` branch) to contain your feature, change, or fix: 101 | 102 | ```bash 103 | git checkout -b 104 | ``` 105 | 106 | 4. Build the distribution before committing to ensure your changes follow the coding standards and all build files are up to date. 107 | 108 | ```bash 109 | grunt dist 110 | ``` 111 | 112 | 5. Commit your changes in logical chunks. Please adhere to these [guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. 113 | 114 | 6. Locally merge (or rebase) the upstream development branch into your topic branch: 115 | 116 | ```bash 117 | git pull [--rebase] upstream develop 118 | ``` 119 | 120 | 7. Push your topic branch up to your fork: 121 | 122 | ```bash 123 | git push origin 124 | ``` 125 | 126 | 8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description against the `develop` branch. 127 | 128 | **By submitting a patch, you agree to allow the project owner to 129 | license your work under the terms of the [MIT License](LICENSE).** 130 | 131 | ## License 132 | 133 | The code and the documentation are released under the [MIT License](LICENSE). 134 | -------------------------------------------------------------------------------- /geometryangle.js: -------------------------------------------------------------------------------- 1 | //============================================================ 2 | // 3 | // Copyright below. 4 | // 5 | // CoAuthor: Patrick Geyer 6 | // 7 | // Twitter: https://twitter.com/PatrickGeyer_ 8 | // 9 | //============================================================ 10 | 11 | //============================================================ 12 | // 13 | // Copyright (C) 2013 Matthew Wagerfield 14 | // 15 | // Twitter: https://twitter.com/mwagerfield 16 | // 17 | // Permission is hereby granted, free of charge, to any 18 | // person obtaining a copy of this software and associated 19 | // documentation files (the "Software"), to deal in the 20 | // Software without restriction, including without limitation 21 | // the rights to use, copy, modify, merge, publish, distribute, 22 | // sublicense, and/or sell copies of the Software, and to 23 | // permit persons to whom the Software is furnished to do 24 | // so, subject to the following conditions: 25 | // 26 | // The above copyright notice and this permission notice 27 | // shall be included in all copies or substantial portions 28 | // of the Software. 29 | // 30 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 31 | // OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 32 | // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 33 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 34 | // EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 35 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 36 | // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 38 | // OR OTHER DEALINGS IN THE SOFTWARE. 39 | // 40 | //============================================================ 41 | 42 | /** 43 | * Defines the Flat Surface Shader namespace for all the awesomeness to exist upon. 44 | * @author Matthew Wagerfield 45 | */ 46 | FSS = { 47 | FRONT: 0, 48 | BACK: 1, 49 | DOUBLE: 2, 50 | SVGNS: 'http://www.w3.org/2000/svg' 51 | }; 52 | 53 | /** 54 | * @class Array 55 | * @author Matthew Wagerfield 56 | */ 57 | FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; 58 | 59 | /** 60 | * @class Utils 61 | * @author Matthew Wagerfield 62 | */ 63 | FSS.Utils = { 64 | isNumber: function (value) { 65 | return !isNaN(parseFloat(value)) && isFinite(value); 66 | } 67 | }; 68 | 69 | /** 70 | * Request Animation Frame Polyfill. 71 | * @author Paul Irish 72 | * @see https://gist.github.com/paulirish/1579671 73 | */ 74 | (function () { 75 | 76 | var lastTime = 0; 77 | var vendors = ['ms', 'moz', 'webkit', 'o']; 78 | 79 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 80 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 81 | window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; 82 | } 83 | 84 | if (!window.requestAnimationFrame) { 85 | window.requestAnimationFrame = function (callback, element) { 86 | var currentTime = new Date().getTime(); 87 | var timeToCall = Math.max(0, 16 - (currentTime - lastTime)); 88 | var id = window.setTimeout(function () { 89 | callback(currentTime + timeToCall); 90 | }, timeToCall); 91 | lastTime = currentTime + timeToCall; 92 | return id; 93 | }; 94 | } 95 | 96 | if (!window.cancelAnimationFrame) { 97 | window.cancelAnimationFrame = function (id) { 98 | clearTimeout(id); 99 | }; 100 | } 101 | 102 | }()); 103 | 104 | /** 105 | * @object Math Augmentation 106 | * @author Matthew Wagerfield 107 | */ 108 | Math.PIM2 = Math.PI * 2; 109 | Math.PID2 = Math.PI / 2; 110 | Math.randomInRange = function (min, max) { 111 | return min + (max - min) * Math.random(); 112 | }; 113 | Math.clamp = function (value, min, max) { 114 | value = Math.max(value, min); 115 | value = Math.min(value, max); 116 | return value; 117 | }; 118 | 119 | /** 120 | * @object Vector3 121 | * @author Matthew Wagerfield 122 | */ 123 | FSS.Vector3 = { 124 | create: function (x, y, z) { 125 | var vector = new FSS.Array(3); 126 | this.set(vector, x, y, z); 127 | return vector; 128 | }, 129 | clone: function (a) { 130 | var vector = this.create(); 131 | this.copy(vector, a); 132 | return vector; 133 | }, 134 | set: function (target, x, y, z) { 135 | target[0] = x || 0; 136 | target[1] = y || 0; 137 | target[2] = z || 0; 138 | return this; 139 | }, 140 | setX: function (target, x) { 141 | target[0] = x || 0; 142 | return this; 143 | }, 144 | setY: function (target, y) { 145 | target[1] = y || 0; 146 | return this; 147 | }, 148 | setZ: function (target, z) { 149 | target[2] = z || 0; 150 | return this; 151 | }, 152 | copy: function (target, a) { 153 | target[0] = a[0]; 154 | target[1] = a[1]; 155 | target[2] = a[2]; 156 | return this; 157 | }, 158 | add: function (target, a) { 159 | target[0] += a[0]; 160 | target[1] += a[1]; 161 | target[2] += a[2]; 162 | return this; 163 | }, 164 | addVectors: function (target, a, b) { 165 | target[0] = a[0] + b[0]; 166 | target[1] = a[1] + b[1]; 167 | target[2] = a[2] + b[2]; 168 | return this; 169 | }, 170 | addScalar: function (target, s) { 171 | target[0] += s; 172 | target[1] += s; 173 | target[2] += s; 174 | return this; 175 | }, 176 | subtract: function (target, a) { 177 | target[0] -= a[0]; 178 | target[1] -= a[1]; 179 | target[2] -= a[2]; 180 | return this; 181 | }, 182 | subtractVectors: function (target, a, b) { 183 | target[0] = a[0] - b[0]; 184 | target[1] = a[1] - b[1]; 185 | target[2] = a[2] - b[2]; 186 | return this; 187 | }, 188 | subtractScalar: function (target, s) { 189 | target[0] -= s; 190 | target[1] -= s; 191 | target[2] -= s; 192 | return this; 193 | }, 194 | multiply: function (target, a) { 195 | target[0] *= a[0]; 196 | target[1] *= a[1]; 197 | target[2] *= a[2]; 198 | return this; 199 | }, 200 | multiplyVectors: function (target, a, b) { 201 | target[0] = a[0] * b[0]; 202 | target[1] = a[1] * b[1]; 203 | target[2] = a[2] * b[2]; 204 | return this; 205 | }, 206 | multiplyScalar: function (target, s) { 207 | target[0] *= s; 208 | target[1] *= s; 209 | target[2] *= s; 210 | return this; 211 | }, 212 | divide: function (target, a) { 213 | target[0] /= a[0]; 214 | target[1] /= a[1]; 215 | target[2] /= a[2]; 216 | return this; 217 | }, 218 | divideVectors: function (target, a, b) { 219 | target[0] = a[0] / b[0]; 220 | target[1] = a[1] / b[1]; 221 | target[2] = a[2] / b[2]; 222 | return this; 223 | }, 224 | divideScalar: function (target, s) { 225 | if (s !== 0) { 226 | target[0] /= s; 227 | target[1] /= s; 228 | target[2] /= s; 229 | } else { 230 | target[0] = 0; 231 | target[1] = 0; 232 | target[2] = 0; 233 | } 234 | return this; 235 | }, 236 | cross: function (target, a) { 237 | var x = target[0]; 238 | var y = target[1]; 239 | var z = target[2]; 240 | target[0] = y * a[2] - z * a[1]; 241 | target[1] = z * a[0] - x * a[2]; 242 | target[2] = x * a[1] - y * a[0]; 243 | return this; 244 | }, 245 | crossVectors: function (target, a, b) { 246 | target[0] = a[1] * b[2] - a[2] * b[1]; 247 | target[1] = a[2] * b[0] - a[0] * b[2]; 248 | target[2] = a[0] * b[1] - a[1] * b[0]; 249 | return this; 250 | }, 251 | min: function (target, value) { 252 | if (target[0] < value) { 253 | target[0] = value; 254 | } 255 | if (target[1] < value) { 256 | target[1] = value; 257 | } 258 | if (target[2] < value) { 259 | target[2] = value; 260 | } 261 | return this; 262 | }, 263 | max: function (target, value) { 264 | if (target[0] > value) { 265 | target[0] = value; 266 | } 267 | if (target[1] > value) { 268 | target[1] = value; 269 | } 270 | if (target[2] > value) { 271 | target[2] = value; 272 | } 273 | return this; 274 | }, 275 | clamp: function (target, min, max) { 276 | this.min(target, min); 277 | this.max(target, max); 278 | return this; 279 | }, 280 | limit: function (target, min, max) { 281 | var length = this.length(target); 282 | if (min !== null && length < min) { 283 | this.setLength(target, min); 284 | } else if (max !== null && length > max) { 285 | this.setLength(target, max); 286 | } 287 | return this; 288 | }, 289 | dot: function (a, b) { 290 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 291 | }, 292 | normalise: function (target) { 293 | return this.divideScalar(target, this.length(target)); 294 | }, 295 | negate: function (target) { 296 | return this.multiplyScalar(target, -1); 297 | }, 298 | distanceSquared: function (a, b) { 299 | var dx = a[0] - b[0]; 300 | var dy = a[1] - b[1]; 301 | var dz = a[2] - b[2]; 302 | return dx * dx + dy * dy + dz * dz; 303 | }, 304 | distance: function (a, b) { 305 | return Math.sqrt(this.distanceSquared(a, b)); 306 | }, 307 | lengthSquared: function (a) { 308 | return a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; 309 | }, 310 | length: function (a) { 311 | return Math.sqrt(this.lengthSquared(a)); 312 | }, 313 | setLength: function (target, l) { 314 | var length = this.length(target); 315 | if (length !== 0 && l !== length) { 316 | this.multiplyScalar(target, l / length); 317 | } 318 | return this; 319 | }, 320 | floor: function (target) { 321 | target[0] = Math.floor(target[0]); 322 | target[1] = Math.floor(target[1]); 323 | target[2] = Math.floor(target[2]); 324 | return target; 325 | } 326 | }; 327 | 328 | /** 329 | * @object Vector4 330 | * @author Matthew Wagerfield 331 | */ 332 | FSS.Vector4 = { 333 | create: function (x, y, z, w) { 334 | var vector = new FSS.Array(4); 335 | this.set(vector, x, y, z); 336 | return vector; 337 | }, 338 | set: function (target, x, y, z, w) { 339 | target[0] = x || 0; 340 | target[1] = y || 0; 341 | target[2] = z || 0; 342 | target[3] = w || 0; 343 | return this; 344 | }, 345 | setX: function (target, x) { 346 | target[0] = x || 0; 347 | return this; 348 | }, 349 | setY: function (target, y) { 350 | target[1] = y || 0; 351 | return this; 352 | }, 353 | setZ: function (target, z) { 354 | target[2] = z || 0; 355 | return this; 356 | }, 357 | setW: function (target, w) { 358 | target[3] = w || 0; 359 | return this; 360 | }, 361 | add: function (target, a) { 362 | target[0] += a[0]; 363 | target[1] += a[1]; 364 | target[2] += a[2]; 365 | target[3] += a[3]; 366 | return this; 367 | }, 368 | multiplyVectors: function (target, a, b) { 369 | target[0] = a[0] * b[0]; 370 | target[1] = a[1] * b[1]; 371 | target[2] = a[2] * b[2]; 372 | target[3] = a[3] * b[3]; 373 | return this; 374 | }, 375 | multiplyScalar: function (target, s) { 376 | target[0] *= s; 377 | target[1] *= s; 378 | target[2] *= s; 379 | target[3] *= s; 380 | return this; 381 | }, 382 | min: function (target, value) { 383 | if (target[0] < value) { 384 | target[0] = value; 385 | } 386 | if (target[1] < value) { 387 | target[1] = value; 388 | } 389 | if (target[2] < value) { 390 | target[2] = value; 391 | } 392 | if (target[3] < value) { 393 | target[3] = value; 394 | } 395 | return this; 396 | }, 397 | max: function (target, value) { 398 | if (target[0] > value) { 399 | target[0] = value; 400 | } 401 | if (target[1] > value) { 402 | target[1] = value; 403 | } 404 | if (target[2] > value) { 405 | target[2] = value; 406 | } 407 | if (target[3] > value) { 408 | target[3] = value; 409 | } 410 | return this; 411 | }, 412 | clamp: function (target, min, max) { 413 | this.min(target, min); 414 | this.max(target, max); 415 | return this; 416 | } 417 | }; 418 | 419 | /** 420 | * @class Color 421 | * @author Matthew Wagerfield 422 | */ 423 | FSS.Color = function (color, opacity) { 424 | this.rgba = []; 425 | this.color = color || '#000000'; 426 | this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; 427 | this.set(this.color, this.opacity); 428 | }; 429 | 430 | FSS.Color.prototype = { 431 | set: function (color, opacity) { 432 | if (color.indexOf("#") === -1) { 433 | if (color.indexOf('rgb(') === 0) { 434 | var pars = color.indexOf(','); 435 | this.rgba[0] = parseInt(color.substr(4, pars)); 436 | this.rgba[1] = parseInt(color.substr(pars + 1, color.indexOf(',', pars))); 437 | this.rgba[2] = parseInt(color.substr(color.indexOf(',', pars + 1) + 1, color.indexOf(')'))); 438 | this.rgba[3] = 1; 439 | } else if (color.indexOf('rgba(') === 0) { 440 | 441 | var pars = color.indexOf(','); 442 | var repars = color.indexOf(',', pars + 1); 443 | this.rgba[0] = parseInt(color.substr(5, pars)); 444 | this.rgba[1] = parseInt(color.substr(pars + 1, repars)); 445 | this.rgba[2] = parseInt(color.substr(color.indexOf(',', pars + 1) + 1, color.indexOf(',', repars))); 446 | this.rgba[3] = parseFloat(color.substr(color.indexOf(',', repars + 1) + 1, color.indexOf(')'))); 447 | } 448 | } else { 449 | color = color.replace('#', ''); 450 | var size = color.length / 3; 451 | this.rgba[0] = parseInt(color.substring(size * 0, size * 1), 16) / 255; 452 | this.rgba[1] = parseInt(color.substring(size * 1, size * 2), 16) / 255; 453 | this.rgba[2] = parseInt(color.substring(size * 2, size * 3), 16) / 255; 454 | this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; 455 | } 456 | 457 | return this; 458 | }, 459 | // hexify: function (channel) { 460 | // var hex = Math.ceil(channel * 255).toString(16); 461 | // if (hex.length === 1) { 462 | // hex = '0' + hex; 463 | // } 464 | // return hex; 465 | // }, 466 | format: function () { 467 | return "rgba(" + this.rgba[0] + "," + this.rgba[1] + "," + this.rgba[2] + "," + this.rgba[3] + ")"; //this.hex 468 | // var r = this.hexify(this.rgba[0]); 469 | // var g = this.hexify(this.rgba[1]); 470 | // var b = this.hexify(this.rgba[2]); 471 | // this.hex = '#' + r + g + b; 472 | // return this.hex; 473 | } 474 | }; 475 | 476 | /** 477 | * @class Object 478 | * @author Matthew Wagerfield 479 | */ 480 | FSS.Object = function () { 481 | this.position = FSS.Vector3.create(); 482 | }; 483 | 484 | FSS.Object.prototype = { 485 | setPosition: function (x, y, z) { 486 | FSS.Vector3.set(this.position, x, y, z); 487 | return this; 488 | } 489 | }; 490 | 491 | /** 492 | * @class Light 493 | * @author Matthew Wagerfield 494 | */ 495 | FSS.Light = function (ambient, diffuse) { 496 | FSS.Object.call(this); 497 | this.ambient = new FSS.Color(ambient || '#FFFFFF'); 498 | this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); 499 | this.ray = FSS.Vector3.create(); 500 | }; 501 | 502 | FSS.Light.prototype = Object.create(FSS.Object.prototype); 503 | 504 | /** 505 | * @class Vertex 506 | * @author Matthew Wagerfield 507 | */ 508 | FSS.Vertex = function (x, y, z) { 509 | this.position = FSS.Vector3.create(x, y, z); 510 | }; 511 | 512 | FSS.Vertex.prototype = { 513 | setPosition: function (x, y, z) { 514 | FSS.Vector3.set(this.position, x, y, z); 515 | return this; 516 | } 517 | }; 518 | 519 | /** 520 | * @class Triangle 521 | * @author Matthew Wagerfield 522 | */ 523 | FSS.Triangle = function (a, b, c) { 524 | this.a = a || new FSS.Vertex(); 525 | this.b = b || new FSS.Vertex(); 526 | this.c = c || new FSS.Vertex(); 527 | this.vertices = [this.a, this.b, this.c]; 528 | this.u = FSS.Vector3.create(); 529 | this.v = FSS.Vector3.create(); 530 | this.centroid = FSS.Vector3.create(); 531 | this.normal = FSS.Vector3.create(); 532 | this.color = new FSS.Color(); 533 | this.polygon = document.createElementNS(FSS.SVGNS, 'polygon'); 534 | this.polygon.setAttributeNS(null, 'stroke-linejoin', 'round'); 535 | this.polygon.setAttributeNS(null, 'stroke-miterlimit', '1'); 536 | this.polygon.setAttributeNS(null, 'stroke-width', '1'); 537 | this.computeCentroid(); 538 | this.computeNormal(); 539 | }; 540 | 541 | FSS.Triangle.prototype = { 542 | computeCentroid: function () { 543 | this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; 544 | this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; 545 | this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; 546 | FSS.Vector3.divideScalar(this.centroid, 3); 547 | return this; 548 | }, 549 | computeNormal: function () { 550 | FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); 551 | FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); 552 | FSS.Vector3.crossVectors(this.normal, this.u, this.v); 553 | FSS.Vector3.normalise(this.normal); 554 | return this; 555 | } 556 | }; 557 | 558 | /** 559 | * @class Geometry 560 | * @author Matthew Wagerfield 561 | */ 562 | FSS.Geometry = function () { 563 | this.vertices = []; 564 | this.triangles = []; 565 | this.dirty = false; 566 | }; 567 | 568 | FSS.Geometry.prototype = { 569 | update: function () { 570 | if (this.dirty) { 571 | var t, triangle; 572 | for (t = this.triangles.length - 1; t >= 0; t--) { 573 | triangle = this.triangles[t]; 574 | triangle.computeCentroid(); 575 | triangle.computeNormal(); 576 | } 577 | this.dirty = false; 578 | } 579 | return this; 580 | } 581 | }; 582 | 583 | /** 584 | * @class Plane 585 | * @author Matthew Wagerfield 586 | */ 587 | FSS.Plane = function (width, height, segments, slices) { 588 | FSS.Geometry.call(this); 589 | this.width = width || 100; 590 | this.height = height || 100; 591 | this.segments = segments || 4; 592 | this.slices = slices || 4; 593 | this.segmentWidth = this.width / this.segments; 594 | this.sliceHeight = this.height / this.slices; 595 | 596 | // Cache Variables 597 | var x, y, v0, v1, v2, v3, 598 | vertex, triangle, vertices = [], 599 | offsetX = this.width * -0.5, 600 | offsetY = this.height * 0.5; 601 | 602 | // Add Vertices 603 | for (x = 0; x <= this.segments; x++) { 604 | vertices.push([]); 605 | for (y = 0; y <= this.slices; y++) { 606 | vertex = new FSS.Vertex(offsetX + x * this.segmentWidth, offsetY - y * this.sliceHeight); 607 | vertices[x].push(vertex); 608 | this.vertices.push(vertex); 609 | } 610 | } 611 | 612 | // Add Triangles 613 | for (x = 0; x < this.segments; x++) { 614 | for (y = 0; y < this.slices; y++) { 615 | v0 = vertices[x + 0][y + 0]; 616 | v1 = vertices[x + 0][y + 1]; 617 | v2 = vertices[x + 1][y + 0]; 618 | v3 = vertices[x + 1][y + 1]; 619 | t0 = new FSS.Triangle(v0, v1, v2); 620 | t1 = new FSS.Triangle(v2, v1, v3); 621 | this.triangles.push(t0, t1); 622 | } 623 | } 624 | }; 625 | 626 | FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); 627 | 628 | /** 629 | * @class Material 630 | * @author Matthew Wagerfield 631 | */ 632 | FSS.Material = function (ambient, diffuse) { 633 | this.ambient = new FSS.Color(ambient || 'rgba(68,68,68, 1)'); 634 | this.diffuse = new FSS.Color(diffuse || 'rgba(255,255,255, 1)'); 635 | this.slave = new FSS.Color(); 636 | }; 637 | 638 | /** 639 | * @class Mesh 640 | * @author Matthew Wagerfield 641 | */ 642 | FSS.Mesh = function (geometry, material) { 643 | FSS.Object.call(this); 644 | this.geometry = geometry || new FSS.Geometry(); 645 | this.material = material || new FSS.Material(); 646 | this.side = FSS.FRONT; 647 | this.visible = true; 648 | }; 649 | 650 | FSS.Mesh.prototype = Object.create(FSS.Object.prototype); 651 | 652 | FSS.Mesh.prototype.update = function (lights, calculate) { 653 | var t, triangle, l, light, illuminance, light_count; 654 | light_count = lights.length; 655 | 656 | // Update Geometry 657 | this.geometry.update(); 658 | 659 | // Calculate the triangle colors 660 | if (calculate) { 661 | 662 | // Iterate through Triangles 663 | for (t = this.geometry.triangles.length - 1; t >= 0; t--) { 664 | triangle = this.geometry.triangles[t]; 665 | 666 | // Reset Triangle Color 667 | FSS.Vector4.set(triangle.color.rgba); 668 | 669 | // Iterate through Lights 670 | for (l = lights.length - 1; l >= 0; l--) { 671 | light = lights[l]; 672 | 673 | 674 | // Calculate Illuminance 675 | FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); 676 | FSS.Vector3.normalise(light.ray); 677 | illuminance = FSS.Vector3.dot(triangle.normal, light.ray); 678 | if (this.side === FSS.FRONT) { 679 | illuminance = Math.max(illuminance, 0); 680 | } else if (this.side === FSS.BACK) { 681 | illuminance = Math.abs(Math.min(illuminance, 0)); 682 | } else if (this.side === FSS.DOUBLE) { 683 | illuminance = Math.max(Math.abs(illuminance), 0); 684 | } 685 | 686 | 687 | 688 | // Calculate Ambient Light 689 | for (var i = 0; i < 3; i++) { 690 | this.material.slave.rgba[i] = (((1 / light_count) * this.material.ambient.rgba[i]) * ((1 / light_count) * light.ambient.rgba[i])) / 128; 691 | if (i !== 3) { 692 | this.material.slave.rgba[i] = Math.round(this.material.slave.rgba[i]); 693 | } 694 | } 695 | /* Add the resultant values to the triangle color vector. Not required to factor illuminance because it is ambient light. */ 696 | FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); 697 | 698 | // Calculate Diffuse Light 699 | for (var i = 0; i < 3; i++) { 700 | this.material.slave.rgba[i] = ((1 / light_count) * this.material.diffuse.rgba[i] * (1 / light_count) * light.diffuse.rgba[i]) / 128; 701 | if (i !== 3) { 702 | this.material.slave.rgba[i] = Math.round(this.material.slave.rgba[i]); 703 | } 704 | } 705 | 706 | // FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, light.diffuse.rgba); 707 | // FSS.Vector4.multiplyScalar(this.material.slave.rgba, illuminance); 708 | for (var i = 0; i < 3; i++) { 709 | this.material.slave.rgba[i] = Math.round(this.material.slave.rgba[i] * illuminance); 710 | } 711 | FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); 712 | } 713 | 714 | 715 | 716 | // Clamp & Format Color 717 | FSS.Vector4.clamp(triangle.color.rgba, 0, 255); 718 | triangle.color.rgba[3] = this.material.diffuse.rgba[3]; //Math.min(triangle.color.rgba[3], 1); 719 | } 720 | } 721 | return this; 722 | }; 723 | 724 | /** 725 | * @class Scene 726 | * @author Matthew Wagerfield 727 | */ 728 | FSS.Scene = function () { 729 | this.meshes = []; 730 | this.lights = []; 731 | }; 732 | 733 | FSS.Scene.prototype = { 734 | add: function (object) { 735 | if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { 736 | this.meshes.push(object); 737 | } else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { 738 | this.lights.push(object); 739 | } 740 | return this; 741 | }, 742 | remove: function (object) { 743 | if (object instanceof FSS.Mesh && ~this.meshes.indexOf(object)) { 744 | this.meshes.splice(this.meshes.indexOf(object), 1); 745 | } else if (object instanceof FSS.Light && ~this.lights.indexOf(object)) { 746 | this.lights.splice(this.lights.indexOf(object), 1); 747 | } 748 | return this; 749 | } 750 | }; 751 | 752 | /** 753 | * @class Renderer 754 | * @author Matthew Wagerfield 755 | */ 756 | FSS.Renderer = function () { 757 | this.width = 0; 758 | this.height = 0; 759 | this.halfWidth = 0; 760 | this.halfHeight = 0; 761 | }; 762 | 763 | FSS.Renderer.prototype = { 764 | setSize: function (width, height) { 765 | if (this.width === width && this.height === height) 766 | return; 767 | this.width = width; 768 | this.height = height; 769 | this.halfWidth = this.width * 0.5; 770 | this.halfHeight = this.height * 0.5; 771 | return this; 772 | }, 773 | clear: function () { 774 | return this; 775 | }, 776 | render: function (scene) { 777 | return this; 778 | } 779 | }; 780 | 781 | /** 782 | * @class Canvas Renderer 783 | * @author Matthew Wagerfield 784 | */ 785 | FSS.CanvasRenderer = function () { 786 | FSS.Renderer.call(this); 787 | this.element = document.createElement('canvas'); 788 | /* this.element.style.display = 'block'; */ 789 | this.element.style.zIndex = "-100"; 790 | this.element.style.pointerEvents = "none"; 791 | this.context = this.element.getContext('2d'); 792 | this.setSize(this.element.width, this.element.height); 793 | }; 794 | 795 | FSS.CanvasRenderer.prototype = Object.create(FSS.Renderer.prototype); 796 | 797 | FSS.CanvasRenderer.prototype.setSize = function (width, height) { 798 | FSS.Renderer.prototype.setSize.call(this, width, height); 799 | this.element.width = width; 800 | this.element.height = height; 801 | this.context.setTransform(1, 0, 0, 1, 0, 0); 802 | return this; 803 | }; 804 | 805 | FSS.CanvasRenderer.prototype.clear = function () { 806 | FSS.Renderer.prototype.clear.call(this); 807 | this.context.clearRect(0, 0, this.width, this.height); 808 | return this; 809 | }; 810 | 811 | var opacity = []; 812 | FSS.CanvasRenderer.prototype.render = function (scene) { 813 | FSS.Renderer.prototype.render.call(this, scene); 814 | var m, mesh, t, triangle, color; 815 | var pi2 = 2 * Math.PI; 816 | 817 | // Clear Context 818 | this.clear(); 819 | 820 | // Configure Context 821 | this.context.lineJoin = 'round'; 822 | this.context.lineWidth = 0; 823 | 824 | // Update Meshes 825 | for (m = scene.meshes.length - 1; m >= 0; m--) { 826 | mesh = scene.meshes[m]; 827 | if (typeof opacity[m] == "undefined") { 828 | opacity[m] = []; 829 | } 830 | if (mesh.visible) { 831 | mesh.update(scene.lights, true); 832 | 833 | // Render Triangles 834 | for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { 835 | 836 | var now = Date.now(); 837 | if (typeof opacity[m][t] === "undefined") { 838 | opacity[m][t] = {}; 839 | opacity[m][t].step = FSS.Vector3.create( 840 | Math.randomInRange(0.2, 1.0), 841 | Math.randomInRange(0.2, 1.0), 842 | Math.randomInRange(0.2, 1.0) 843 | ); 844 | opacity[m][t].time = Math.randomInRange(0, Math.PIM2); 845 | opacity[m][t].line = 0; 846 | } else { 847 | opacity[m][t].line = Math.sin(opacity[m][t].time + opacity[m][t].step[0] * now * (scene.LINE.fluctuationSpeed / 100)) * scene.LINE.fluctuationIntensity; 848 | opacity[m][t].vertex = Math.sin(opacity[m][t].time + opacity[m][t].step[0] * now * (scene.VERTEX.fluctuationSpeed / 100)) * scene.VERTEX.fluctuationIntensity; 849 | opacity[m][t].mesh = Math.sin(opacity[m][t].time + opacity[m][t].step[0] * now * (scene.MESH.fluctuationSpeed / 100)) * scene.MESH.fluctuationIntensity; 850 | } 851 | 852 | 853 | triangle = mesh.geometry.triangles[t]; 854 | if (scene.MESH.draw !== false) { 855 | c = triangle.color.rgba; 856 | color = "rgba(" + c[0] + "," + c[1] + ", " + c[2] + "," + c[3] + ")"; 857 | 858 | this.context.beginPath(); 859 | this.context.moveTo(triangle.a.position[0], triangle.a.position[1]); 860 | this.context.lineTo(triangle.b.position[0], triangle.b.position[1]); 861 | this.context.lineTo(triangle.c.position[0], triangle.c.position[1]); 862 | this.context.closePath(); 863 | this.context.fillStyle = color; //Color of triangle 864 | this.context.fill(); 865 | } 866 | 867 | 868 | if (scene.LINE.draw !== false) { 869 | var c = new FSS.Color(scene.LINE.fill); 870 | c = c.rgba; 871 | c[3] = c[3] * (1 - opacity[m][t].line); 872 | c = "rgba(" + c[0] + "," + c[1] + ", " + c[2] + "," + c[3] + ")"; 873 | 874 | this.context.beginPath(); 875 | this.context.moveTo(triangle.a.position[0], triangle.a.position[1]); 876 | this.context.lineTo(triangle.b.position[0], triangle.b.position[1]); 877 | this.context.lineWidth = scene.LINE.thickness; 878 | this.context.fillStyle = c; 879 | this.context.fill(); 880 | this.context.strokeStyle = c; 881 | this.context.stroke(); 882 | } 883 | 884 | if (scene.VERTEX.draw !== false) { 885 | // var grd = this.context.createRadialGradient(triangle.a.position[0], triangle.a.position[1], scene.vertex.radius + 100, triangle.a.position[0], triangle.a.position[1], scene.vertex.radius + 105); 886 | // light blue 887 | // grd.addColorStop(0, '#8ED6FF'); 888 | // dark blue 889 | // grd.addColorStop(1, '#004CB3'); 890 | 891 | var c = new FSS.Color(scene.VERTEX.fill); 892 | c = c.rgba; 893 | c[3] = c[3] * (1 - opacity[m][t].vertex); 894 | c = "rgba(" + c[0] + "," + c[1] + ", " + c[2] + "," + c[3] + ")"; 895 | var c1 = new FSS.Color(scene.VERTEX.strokeColor); 896 | c1 = c1.rgba; 897 | c1[3] = c1[3] * (1 - opacity[m][t].vertex); 898 | c1 = "rgba(" + c1[0] + "," + c1[1] + ", " + c1[2] + "," + c1[3] + ")"; 899 | this.context.beginPath(); 900 | this.context.arc(triangle.a.position[0], triangle.a.position[1], scene.VERTEX.radius, 0, pi2, false); 901 | this.context.fillStyle = c; //scene.VERTEX.fill; 902 | this.context.fill(); 903 | this.context.lineWidth = scene.VERTEX.strokeWidth; 904 | this.context.strokeStyle = c1; 905 | this.context.stroke(); 906 | } 907 | 908 | } 909 | } 910 | } 911 | return this; 912 | }; 913 | 914 | /** 915 | * @class WebGL Renderer 916 | * @author Matthew Wagerfield 917 | */ 918 | FSS.WebGLRenderer = function () { 919 | FSS.Renderer.call(this); 920 | this.element = document.createElement('canvas'); 921 | this.element.style.display = 'block'; 922 | 923 | // Set initial vertex and light count 924 | this.vertices = null; 925 | this.lights = null; 926 | 927 | // Create parameters object 928 | var parameters = { 929 | preserveDrawingBuffer: false, 930 | premultipliedAlpha: true, 931 | antialias: true, 932 | stencil: true, 933 | alpha: true 934 | }; 935 | 936 | // Create and configure the gl context 937 | this.gl = this.getContext(this.element, parameters); 938 | 939 | // Set the internal support flag 940 | this.unsupported = !this.gl; 941 | 942 | // Setup renderer 943 | if (this.unsupported) { 944 | return 'WebGL is not supported by your browser.'; 945 | } else { 946 | this.gl.clearColor(0.0, 0.0, 0.0, 0.0); 947 | this.gl.enable(this.gl.DEPTH_TEST); 948 | this.setSize(this.element.width, this.element.height); 949 | } 950 | }; 951 | 952 | FSS.WebGLRenderer.prototype = Object.create(FSS.Renderer.prototype); 953 | 954 | FSS.WebGLRenderer.prototype.getContext = function (canvas, parameters) { 955 | var context = false; 956 | try { 957 | if (!(context = canvas.getContext('experimental-webgl', parameters))) { 958 | throw 'Error creating WebGL context.'; 959 | } 960 | } catch (error) { 961 | console.error(error); 962 | } 963 | return context; 964 | }; 965 | 966 | FSS.WebGLRenderer.prototype.setSize = function (width, height) { 967 | FSS.Renderer.prototype.setSize.call(this, width, height); 968 | if (this.unsupported) 969 | return; 970 | 971 | // Set the size of the canvas element 972 | this.element.width = width; 973 | this.element.height = height; 974 | 975 | // Set the size of the gl viewport 976 | this.gl.viewport(0, 0, width, height); 977 | return this; 978 | }; 979 | 980 | FSS.WebGLRenderer.prototype.clear = function () { 981 | FSS.Renderer.prototype.clear.call(this); 982 | if (this.unsupported) 983 | return; 984 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); 985 | return this; 986 | }; 987 | 988 | FSS.WebGLRenderer.prototype.render = function (scene) { 989 | FSS.Renderer.prototype.render.call(this, scene); 990 | if (this.unsupported) 991 | return; 992 | var m, mesh, t, tl, triangle, l, light, 993 | attribute, uniform, buffer, data, location, 994 | update = false, 995 | lights = scene.lights.length, 996 | index, v, vl, vetex, vertices = 0; 997 | 998 | // Clear context 999 | this.clear(); 1000 | 1001 | // Build the shader program 1002 | if (this.lights !== lights) { 1003 | this.lights = lights; 1004 | if (this.lights > 0) { 1005 | this.buildProgram(lights); 1006 | } else { 1007 | return; 1008 | } 1009 | } 1010 | 1011 | // Update program 1012 | if (!!this.program) { 1013 | 1014 | // Increment vertex counter 1015 | for (m = scene.meshes.length - 1; m >= 0; m--) { 1016 | mesh = scene.meshes[m]; 1017 | if (mesh.geometry.dirty) 1018 | update = true; 1019 | mesh.update(scene.lights, false); 1020 | vertices += mesh.geometry.triangles.length * 3; 1021 | } 1022 | 1023 | // Compare vertex counter 1024 | if (update || this.vertices !== vertices) { 1025 | this.vertices = vertices; 1026 | 1027 | // Build buffers 1028 | for (attribute in this.program.attributes) { 1029 | buffer = this.program.attributes[attribute]; 1030 | buffer.data = new FSS.Array(vertices * buffer.size); 1031 | 1032 | // Reset vertex index 1033 | index = 0; 1034 | 1035 | // Update attribute buffer data 1036 | for (m = scene.meshes.length - 1; m >= 0; m--) { 1037 | mesh = scene.meshes[m]; 1038 | 1039 | for (t = 0, tl = mesh.geometry.triangles.length; t < tl; t++) { 1040 | triangle = mesh.geometry.triangles[t]; 1041 | 1042 | for (v = 0, vl = triangle.vertices.length; v < vl; v++) { 1043 | vertex = triangle.vertices[v]; 1044 | switch (attribute) { 1045 | case 'side': 1046 | this.setBufferData(index, buffer, mesh.side); 1047 | break; 1048 | case 'position': 1049 | this.setBufferData(index, buffer, vertex.position); 1050 | break; 1051 | case 'centroid': 1052 | this.setBufferData(index, buffer, triangle.centroid); 1053 | break; 1054 | case 'normal': 1055 | this.setBufferData(index, buffer, triangle.normal); 1056 | break; 1057 | case 'ambient': 1058 | this.setBufferData(index, buffer, mesh.material.ambient.rgba); 1059 | break; 1060 | case 'diffuse': 1061 | this.setBufferData(index, buffer, mesh.material.diffuse.rgba); 1062 | break; 1063 | } 1064 | index++; 1065 | } 1066 | } 1067 | } 1068 | 1069 | // Upload attribute buffer data 1070 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer.buffer); 1071 | this.gl.bufferData(this.gl.ARRAY_BUFFER, buffer.data, this.gl.DYNAMIC_DRAW); 1072 | this.gl.enableVertexAttribArray(buffer.location); 1073 | this.gl.vertexAttribPointer(buffer.location, buffer.size, this.gl.FLOAT, false, 0, 0); 1074 | } 1075 | } 1076 | 1077 | // Build uniform buffers 1078 | this.setBufferData(0, this.program.uniforms.resolution, [this.width, this.height, this.width]); 1079 | for (l = lights - 1; l >= 0; l--) { 1080 | light = scene.lights[l]; 1081 | this.setBufferData(l, this.program.uniforms.lightPosition, light.position); 1082 | this.setBufferData(l, this.program.uniforms.lightAmbient, light.ambient.rgba); 1083 | this.setBufferData(l, this.program.uniforms.lightDiffuse, light.diffuse.rgba); 1084 | } 1085 | 1086 | // Update uniforms 1087 | for (uniform in this.program.uniforms) { 1088 | buffer = this.program.uniforms[uniform]; 1089 | location = buffer.location; 1090 | data = buffer.data; 1091 | switch (buffer.structure) { 1092 | case '3f': 1093 | this.gl.uniform3f(location, data[0], data[1], data[2]); 1094 | break; 1095 | case '3fv': 1096 | this.gl.uniform3fv(location, data); 1097 | break; 1098 | case '4fv': 1099 | this.gl.uniform4fv(location, data); 1100 | break; 1101 | } 1102 | } 1103 | } 1104 | 1105 | // Draw those lovely triangles 1106 | this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertices); 1107 | return this; 1108 | }; 1109 | 1110 | FSS.WebGLRenderer.prototype.setBufferData = function (index, buffer, value) { 1111 | if (FSS.Utils.isNumber(value)) { 1112 | buffer.data[index * buffer.size] = value; 1113 | } else { 1114 | for (var i = value.length - 1; i >= 0; i--) { 1115 | buffer.data[index * buffer.size + i] = value[i]; 1116 | } 1117 | } 1118 | }; 1119 | 1120 | /** 1121 | * Concepts taken from three.js WebGLRenderer 1122 | * @see https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js 1123 | */ 1124 | FSS.WebGLRenderer.prototype.buildProgram = function (lights) { 1125 | if (this.unsupported) 1126 | return; 1127 | 1128 | // Create shader source 1129 | var vs = FSS.WebGLRenderer.VS(lights); 1130 | var fs = FSS.WebGLRenderer.FS(lights); 1131 | 1132 | // Derive the shader fingerprint 1133 | var code = vs + fs; 1134 | 1135 | // Check if the program has already been compiled 1136 | if (!!this.program && this.program.code === code) 1137 | return; 1138 | 1139 | // Create the program and shaders 1140 | var program = this.gl.createProgram(); 1141 | var vertexShader = this.buildShader(this.gl.VERTEX_SHADER, vs); 1142 | var fragmentShader = this.buildShader(this.gl.FRAGMENT_SHADER, fs); 1143 | 1144 | // Attach an link the shader 1145 | this.gl.attachShader(program, vertexShader); 1146 | this.gl.attachShader(program, fragmentShader); 1147 | this.gl.linkProgram(program); 1148 | 1149 | // Add error handling 1150 | if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) { 1151 | var error = this.gl.getError(); 1152 | var status = this.gl.getProgramParameter(program, this.gl.VALIDATE_STATUS); 1153 | console.error('Could not initialise shader.\nVALIDATE_STATUS: ' + status + '\nERROR: ' + error); 1154 | return null; 1155 | } 1156 | 1157 | // Delete the shader 1158 | this.gl.deleteShader(fragmentShader); 1159 | this.gl.deleteShader(vertexShader); 1160 | 1161 | // Set the program code 1162 | program.code = code; 1163 | 1164 | // Add the program attributes 1165 | program.attributes = { 1166 | side: this.buildBuffer(program, 'attribute', 'aSide', 1, 'f'), 1167 | position: this.buildBuffer(program, 'attribute', 'aPosition', 3, 'v3'), 1168 | centroid: this.buildBuffer(program, 'attribute', 'aCentroid', 3, 'v3'), 1169 | normal: this.buildBuffer(program, 'attribute', 'aNormal', 3, 'v3'), 1170 | ambient: this.buildBuffer(program, 'attribute', 'aAmbient', 4, 'v4'), 1171 | diffuse: this.buildBuffer(program, 'attribute', 'aDiffuse', 4, 'v4') 1172 | }; 1173 | 1174 | // Add the program uniforms 1175 | program.uniforms = { 1176 | resolution: this.buildBuffer(program, 'uniform', 'uResolution', 3, '3f', 1), 1177 | lightPosition: this.buildBuffer(program, 'uniform', 'uLightPosition', 3, '3fv', lights), 1178 | lightAmbient: this.buildBuffer(program, 'uniform', 'uLightAmbient', 4, '4fv', lights), 1179 | lightDiffuse: this.buildBuffer(program, 'uniform', 'uLightDiffuse', 4, '4fv', lights) 1180 | }; 1181 | 1182 | // Set the renderer program 1183 | this.program = program; 1184 | 1185 | // Enable program 1186 | this.gl.useProgram(this.program); 1187 | 1188 | // Return the program 1189 | return program; 1190 | }; 1191 | 1192 | FSS.WebGLRenderer.prototype.buildShader = function (type, source) { 1193 | if (this.unsupported) 1194 | return; 1195 | 1196 | // Create and compile shader 1197 | var shader = this.gl.createShader(type); 1198 | this.gl.shaderSource(shader, source); 1199 | this.gl.compileShader(shader); 1200 | 1201 | // Add error handling 1202 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 1203 | console.error(this.gl.getShaderInfoLog(shader)); 1204 | return null; 1205 | } 1206 | 1207 | // Return the shader 1208 | return shader; 1209 | }; 1210 | 1211 | FSS.WebGLRenderer.prototype.buildBuffer = function (program, type, identifier, size, structure, count) { 1212 | var buffer = { 1213 | buffer: this.gl.createBuffer(), 1214 | size: size, 1215 | structure: structure, 1216 | data: null 1217 | }; 1218 | 1219 | // Set the location 1220 | switch (type) { 1221 | case 'attribute': 1222 | buffer.location = this.gl.getAttribLocation(program, identifier); 1223 | break; 1224 | case 'uniform': 1225 | buffer.location = this.gl.getUniformLocation(program, identifier); 1226 | break; 1227 | } 1228 | 1229 | // Create the buffer if count is provided 1230 | if (!!count) { 1231 | buffer.data = new FSS.Array(count * size); 1232 | } 1233 | 1234 | // Return the buffer 1235 | return buffer; 1236 | }; 1237 | 1238 | FSS.WebGLRenderer.VS = function (lights) { 1239 | var shader = [ 1240 | // Precision 1241 | 'precision mediump float;', 1242 | // Lights 1243 | '#define LIGHTS ' + lights, 1244 | // Attributes 1245 | 'attribute float aSide;', 1246 | 'attribute vec3 aPosition;', 1247 | 'attribute vec3 aCentroid;', 1248 | 'attribute vec3 aNormal;', 1249 | 'attribute vec4 aAmbient;', 1250 | 'attribute vec4 aDiffuse;', 1251 | // Uniforms 1252 | 'uniform vec3 uResolution;', 1253 | 'uniform vec3 uLightPosition[LIGHTS];', 1254 | 'uniform vec4 uLightAmbient[LIGHTS];', 1255 | 'uniform vec4 uLightDiffuse[LIGHTS];', 1256 | // Varyings 1257 | 'varying vec4 vColor;', 1258 | // Main 1259 | 'void main() {', 1260 | // Create color 1261 | 'vColor = vec4(0.0);', 1262 | // Calculate the vertex position 1263 | 'vec3 position = aPosition / uResolution * 2.0;', 1264 | // Iterate through lights 1265 | 'for (int i = 0; i < LIGHTS; i++) {', 1266 | 'vec3 lightPosition = uLightPosition[i];', 1267 | 'vec4 lightAmbient = uLightAmbient[i];', 1268 | 'vec4 lightDiffuse = uLightDiffuse[i];', 1269 | // Calculate illuminance 1270 | 'vec3 ray = normalize(lightPosition - aCentroid);', 1271 | 'float illuminance = dot(aNormal, ray);', 1272 | 'if (aSide == 0.0) {', 1273 | 'illuminance = max(illuminance, 0.0);', 1274 | '} else if (aSide == 1.0) {', 1275 | 'illuminance = abs(min(illuminance, 0.0));', 1276 | '} else if (aSide == 2.0) {', 1277 | 'illuminance = max(abs(illuminance), 0.0);', 1278 | '}', 1279 | // Calculate ambient light 1280 | 'vColor += aAmbient * lightAmbient;', 1281 | // Calculate diffuse light 1282 | 'vColor += aDiffuse * lightDiffuse * illuminance;', 1283 | '}', 1284 | // Clamp color 1285 | 'vColor = clamp(vColor, 0.0, 1.0);', 1286 | // Set gl_Position 1287 | 'gl_Position = vec4(position, 1.0);', 1288 | '}' 1289 | 1290 | // Return the shader 1291 | ].join('\n'); 1292 | return shader; 1293 | }; 1294 | 1295 | FSS.WebGLRenderer.FS = function (lights) { 1296 | var shader = [ 1297 | // Precision 1298 | 'precision mediump float;', 1299 | // Varyings 1300 | 'varying vec4 vColor;', 1301 | // Main 1302 | 'void main() {', 1303 | // Set gl_FragColor 1304 | 'gl_FragColor = vColor;', 1305 | '}' 1306 | 1307 | // Return the shader 1308 | ].join('\n'); 1309 | return shader; 1310 | }; 1311 | 1312 | /** 1313 | * @class SVG Renderer 1314 | * @author Matthew Wagerfield 1315 | */ 1316 | FSS.SVGRenderer = function () { 1317 | FSS.Renderer.call(this); 1318 | this.element = document.createElementNS(FSS.SVGNS, 'svg'); 1319 | this.element.setAttribute('xmlns', FSS.SVGNS); 1320 | this.element.setAttribute('version', '1.1'); 1321 | this.element.style.display = 'block'; 1322 | this.setSize(300, 150); 1323 | }; 1324 | 1325 | FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); 1326 | 1327 | FSS.SVGRenderer.prototype.setSize = function (width, height) { 1328 | FSS.Renderer.prototype.setSize.call(this, width, height); 1329 | this.element.setAttribute('width', width); 1330 | this.element.setAttribute('height', height); 1331 | return this; 1332 | }; 1333 | 1334 | FSS.SVGRenderer.prototype.clear = function () { 1335 | FSS.Renderer.prototype.clear.call(this); 1336 | for (var i = this.element.childNodes.length - 1; i >= 0; i--) { 1337 | this.element.removeChild(this.element.childNodes[i]); 1338 | } 1339 | return this; 1340 | }; 1341 | 1342 | FSS.SVGRenderer.prototype.render = function (scene) { 1343 | FSS.Renderer.prototype.render.call(this, scene); 1344 | var m, mesh, t, triangle, points, style; 1345 | 1346 | // Update Meshes 1347 | for (m = scene.meshes.length - 1; m >= 0; m--) { 1348 | mesh = scene.meshes[m]; 1349 | if (mesh.visible) { 1350 | mesh.update(scene.lights, true); 1351 | 1352 | // Render Triangles 1353 | for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { 1354 | triangle = mesh.geometry.triangles[t]; 1355 | if (triangle.polygon.parentNode !== this.element) { 1356 | this.element.appendChild(triangle.polygon); 1357 | } 1358 | points = this.formatPoint(triangle.a) + ' '; 1359 | points += this.formatPoint(triangle.b) + ' '; 1360 | points += this.formatPoint(triangle.c); 1361 | style = this.formatStyle(triangle.color.format()); 1362 | triangle.polygon.setAttributeNS(null, 'points', points); 1363 | triangle.polygon.setAttributeNS(null, 'style', style); 1364 | } 1365 | } 1366 | } 1367 | return this; 1368 | }; 1369 | 1370 | FSS.SVGRenderer.prototype.formatPoint = function (vertex) { 1371 | return (vertex.position[0]) + ',' + (vertex.position[1]); 1372 | }; 1373 | 1374 | FSS.SVGRenderer.prototype.formatStyle = function (color) { 1375 | var style = 'fill:' + color + ';'; 1376 | style += 'stroke:' + color + ';'; 1377 | return style; 1378 | }; 1379 | 1380 | (function () { 1381 | $.fn.Geometryangle = function (opt) { 1382 | var fss = []; 1383 | var element = $(this); 1384 | var len = element.length, k; 1385 | for (var j = 0; j < len; j++) { 1386 | k = element[j]; 1387 | fss[fss.length] = FSS_Worker(opt, k); 1388 | } 1389 | return (fss.length == 1 ? fss[0] : fss); 1390 | }; 1391 | var FSS_Worker = function (opt, k) { 1392 | 1393 | opt = opt || {}; 1394 | var MESH = {}, LIGHT = [{}], 1395 | VERTEX = {}, LINE = {}; 1396 | 1397 | var getDataAttr = function (element) { 1398 | var data_option = [ 1399 | [], 1400 | [] 1401 | ]; 1402 | // var keys = Object.keys(option); 1403 | var d = [MESH, LIGHT]; 1404 | $.each(d, function (u) { 1405 | $.each(d[u], function (i, val) { 1406 | if (typeof element.getAttribute('data-fss-' [i]) !== "undefined" && typeof element.getAttribute('data-fss-' + [i]) !== false) { 1407 | try { 1408 | data_option[u][i] = $.parseJSON(element.getAttribute('data-fss-' + [i])); 1409 | return true; //continue; 1410 | } catch (e) { 1411 | } 1412 | if (typeof data_option[u][i] !== "object") { 1413 | data_option[u][i] = element.getAttribute('data-fss-' + [i]); 1414 | } 1415 | } 1416 | }); 1417 | 1418 | var data_json = (typeof element.getAttribute('data-fss') !== "undefined" && typeof element.getAttribute('data-fss') !== false ? $.parseJSON(element.getAttribute('data-fss')) : []); 1419 | 1420 | $.extend(true, data_option[u], data_json); 1421 | }); 1422 | 1423 | return data_option; 1424 | }; 1425 | 1426 | var rgbaToRgb = function (rgba) { 1427 | try { 1428 | var bits = rgba.split("("); 1429 | } catch (e) { 1430 | return; 1431 | } 1432 | if (typeof bits[1] !== "undefined") { 1433 | bits = bits[1].split(")")[0].split(","); 1434 | return "rgb(" + bits[0] + "," + bits[1] + "," + bits[2] + ")"; 1435 | } 1436 | return; 1437 | }; 1438 | 1439 | //------------------------------ 1440 | // Mesh Properties 1441 | //------------------------------ 1442 | var mesh_default = { 1443 | width: 1.2, 1444 | height: 1.2, 1445 | depth: 10, 1446 | columns: undefined, 1447 | columns_auto: true, 1448 | rows: undefined, 1449 | rows_auto: true, 1450 | zoom: 1, 1451 | xRange: 0.8, 1452 | yRange: 0.1, 1453 | zRange: 1.0, 1454 | ambient: 'rgba(85, 85, 85, 1)', 1455 | diffuse: 'rgba(255, 255, 255, 1)', 1456 | background: 'rgb(255, 255, 255)', 1457 | speed: 0.0002, 1458 | fluctuationSpeed: 0.5, 1459 | fluctuationIntensity: 0, 1460 | onRender: function () { 1461 | }, 1462 | floorPosition: false, 1463 | draw: true 1464 | }; 1465 | 1466 | var vertex_default = { 1467 | radius: 0, 1468 | fill: "rgba(0, 0, 0, 0)", 1469 | fluctuationSpeed: 0.5, 1470 | fluctuationIntensity: 0, 1471 | strokeWidth: 0, 1472 | strokeColor: "rgba(0, 0, 0, 0)", 1473 | draw: false 1474 | }; 1475 | 1476 | var line_default = { 1477 | fill: "rgba(0, 0, 0, 0)", 1478 | thickness: 1, 1479 | fluctuationIntensity: 0, 1480 | fluctuationSpeed: 0.5, 1481 | draw: false 1482 | }; 1483 | 1484 | //------------------------------ 1485 | // Light Properties 1486 | //------------------------------ 1487 | var light_default = { 1488 | count: 1, 1489 | xyScalar: 1, 1490 | zOffset: 100, 1491 | ambient: 'rgba(255,0,102, 1)', 1492 | diffuse: 'rgba(255,136,0, 1)', 1493 | speed: 0.010, 1494 | gravity: 1200, 1495 | dampening: 0.95, 1496 | minLimit: 10, 1497 | maxLimit: null, 1498 | minDistance: 20, 1499 | maxDistance: 400, 1500 | autopilot: false, 1501 | draw: false, //show circle 1502 | bounds: FSS.Vector3.create(), 1503 | step: FSS.Vector3.create( 1504 | Math.randomInRange(0.2, 1.0), 1505 | Math.randomInRange(0.2, 1.0), 1506 | Math.randomInRange(0.2, 1.0) 1507 | ) 1508 | }; 1509 | 1510 | var self = k; 1511 | 1512 | var createValues = function (opt) { 1513 | opt.mesh = opt.mesh || MESH; 1514 | opt.lights = opt.lights || LIGHT; 1515 | opt.vertex = opt.vertex || VERTEX; 1516 | opt.line = opt.line || LINE; 1517 | 1518 | MESH = $.extend(true, mesh_default, MESH, opt.mesh); 1519 | VERTEX = $.extend(true, vertex_default, VERTEX, opt.vertex); 1520 | LINE = $.extend(true, line_default, LINE, opt.line); 1521 | for (var i = 0; i < LIGHT.length; i++) { 1522 | LIGHT[i] = $.extend(true, light_default, LIGHT[i], opt.lights[i]); 1523 | } 1524 | var box_data_option = getDataAttr(self); 1525 | MESH = $.extend(true, box_data_option[0], MESH); 1526 | 1527 | MESH.columns_auto = (typeof opt.mesh.columns === "undefined"); 1528 | MESH.rows_auto = (typeof opt.mesh.rows === "undefined"); 1529 | }; 1530 | createValues({ 1531 | mesh: mesh_default, 1532 | line: line_default, 1533 | vertex: vertex_default, 1534 | lights: [light_default] 1535 | }); 1536 | createValues(opt); 1537 | 1538 | var container = document.createElement("div"); 1539 | container.style.position = "absolute"; 1540 | container.style.left = "0"; 1541 | container.style.right = "0"; 1542 | container.style.top = "0"; 1543 | container.style.bottom = "0"; 1544 | container.style.background = MESH.background; 1545 | container.style.zIndex = "-100"; 1546 | container.setAttribute('class', 'fss-output'); 1547 | self.insertBefore(container, null); 1548 | 1549 | 1550 | 1551 | //------------------------------ 1552 | // Render Properties 1553 | //------------------------------ 1554 | var WEBGL = 'webgl'; 1555 | var CANVAS = 'canvas'; 1556 | var SVG = 'svg'; 1557 | var RENDER = { 1558 | renderer: CANVAS 1559 | }; 1560 | 1561 | //------------------------------ 1562 | // UI Properties 1563 | //------------------------------ 1564 | var UI = { 1565 | show: true 1566 | }; 1567 | 1568 | //------------------------------ 1569 | // Global Properties 1570 | //------------------------------ 1571 | var now, start = Date.now(); 1572 | var center = FSS.Vector3.create(); 1573 | var attractor = FSS.Vector3.create(); 1574 | //var container = document.getElementById('container'); -- taken from JQuery element 1575 | /* var output = document.getElementById('output'); */ 1576 | var ui = document.getElementById('ui'); 1577 | var renderer, scene, mesh, geometry, material; 1578 | var webglRenderer, canvasRenderer, svgRenderer; 1579 | var gui, autopilotController; 1580 | 1581 | //------------------------------ 1582 | // Methods 1583 | //------------------------------ 1584 | function initialise() { 1585 | createRenderer(); 1586 | createScene(); 1587 | createMesh(); 1588 | createLights(); 1589 | addEventListeners(); 1590 | callbacks.resize(container.offsetWidth, container.offsetHeight); 1591 | animate(); 1592 | } 1593 | 1594 | function createRenderer() { 1595 | webglRenderer = new FSS.WebGLRenderer(); 1596 | canvasRenderer = new FSS.CanvasRenderer(); 1597 | svgRenderer = new FSS.SVGRenderer(); 1598 | setRenderer(RENDER.renderer); 1599 | } 1600 | 1601 | function setRenderer(index) { 1602 | if (renderer) { 1603 | /* output.removeChild(renderer.element); */ 1604 | } 1605 | switch (index) { 1606 | case WEBGL: 1607 | renderer = webglRenderer; 1608 | break; 1609 | case CANVAS: 1610 | renderer = canvasRenderer; 1611 | break; 1612 | case SVG: 1613 | renderer = svgRenderer; 1614 | break; 1615 | } 1616 | renderer.setSize(container.offsetWidth, container.offsetHeight); 1617 | container.insertBefore(renderer.element, null); 1618 | 1619 | var style = window.getComputedStyle(self); 1620 | 1621 | if (style.getPropertyValue('position') == 'static' || style.getPropertyValue('position').length == 0) { 1622 | self.style.position = 'relative'; 1623 | } 1624 | 1625 | } 1626 | 1627 | function createScene() { 1628 | scene = new FSS.Scene(); 1629 | scene.VERTEX = VERTEX; 1630 | scene.LINE = LINE; 1631 | scene.MESH = MESH; 1632 | } 1633 | 1634 | function createMesh() { 1635 | scene.remove(mesh); 1636 | renderer.clear(); 1637 | geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.columns, MESH.rows); 1638 | material = new FSS.Material(MESH.ambient, MESH.diffuse); 1639 | mesh = new FSS.Mesh(geometry, material); 1640 | scene.add(mesh); 1641 | 1642 | // Augment vertices for animation 1643 | var v, vertex; 1644 | for (v = geometry.vertices.length - 1; v >= 0; v--) { 1645 | vertex = geometry.vertices[v]; 1646 | vertex.anchor = FSS.Vector3.floor(FSS.Vector3.clone(vertex.position)); 1647 | vertex.step = FSS.Vector3.create( 1648 | Math.randomInRange(0.2, 1.0), 1649 | Math.randomInRange(0.2, 1.0), 1650 | Math.randomInRange(0.2, 1.0) 1651 | ); 1652 | vertex.time = Math.randomInRange(0, Math.PIM2); 1653 | } 1654 | } 1655 | 1656 | function createLights() { 1657 | var l, light; 1658 | for (l = scene.lights.length - 1; l >= 0; l--) { 1659 | light = scene.lights[l]; 1660 | scene.remove(light); 1661 | } 1662 | renderer.clear(); 1663 | for (l = 0; l < LIGHT.length; l++) { 1664 | for (var u = 0; u < LIGHT[l].count; u++) { 1665 | light = new FSS.Light(LIGHT[l].ambient, LIGHT[l].diffuse); 1666 | scene.add(light); 1667 | 1668 | // Augment light for animation 1669 | light.mass = Math.randomInRange(0.5, 1); 1670 | light.velocity = FSS.Vector3.create(); 1671 | light.acceleration = FSS.Vector3.create(); 1672 | light.force = FSS.Vector3.create(); 1673 | 1674 | // Ring SVG Circle 1675 | light.ring = document.createElementNS(FSS.SVGNS, 'circle'); 1676 | light.ring.setAttributeNS(null, 'stroke', light.ambient); 1677 | light.ring.setAttributeNS(null, 'stroke-width', '0.5'); 1678 | light.ring.setAttributeNS(null, 'fill', 'none'); 1679 | light.ring.setAttributeNS(null, 'r', '10'); 1680 | 1681 | // Core SVG Circle 1682 | light.core = document.createElementNS(FSS.SVGNS, 'circle'); 1683 | light.core.setAttributeNS(null, 'fill', light.diffuseHex); 1684 | light.core.setAttributeNS(null, 'r', '4'); 1685 | } 1686 | } 1687 | } 1688 | var callbacks = { 1689 | resize: function (width, height) { 1690 | 1691 | if (typeof width == "undefined" || typeof width === undefined) { 1692 | width = self.width(); 1693 | } 1694 | if (typeof height == "undefined" || typeof height === undefined) { 1695 | height = self.height(); 1696 | } 1697 | var ratio_x = width / 1000; 1698 | var ratio_y = height / 1000; 1699 | var x_tiles = Math.round(ratio_x * 10) * MESH.zoom; 1700 | var y_tiles = Math.round(ratio_y * 10) * MESH.zoom; 1701 | MESH.columns = (MESH.columns_auto === true ? x_tiles : MESH.columns); 1702 | MESH.rows = (MESH.rows_auto === true ? y_tiles : MESH.rows); 1703 | renderer.setSize(width, height); 1704 | FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight); 1705 | createMesh(); 1706 | }, 1707 | update: function (opt) { 1708 | createValues(opt); 1709 | scene.vertex = VERTEX; 1710 | scene.line = LINE; 1711 | //Ambient 1712 | for (i = 0, l = scene.meshes.length; i < l; i++) { 1713 | scene.meshes[i].material.ambient.set(MESH.ambient); 1714 | scene.meshes[i].material.diffuse.set(MESH.diffuse); 1715 | } 1716 | //width 1717 | if (geometry.width !== MESH.width * renderer.width) { 1718 | createMesh(); 1719 | } 1720 | if (geometry.height !== MESH.height * renderer.height) { 1721 | createMesh(); 1722 | } 1723 | if (geometry.segments !== MESH.columns) { 1724 | createMesh(); 1725 | } 1726 | if (geometry.slices !== MESH.rows) { 1727 | createMesh(); 1728 | } 1729 | 1730 | var light_index = 0; 1731 | 1732 | for (l = 0; l < LIGHT.length; l++) { 1733 | 1734 | for (var i = 0; i < LIGHT[l].count; i++) { 1735 | light = scene.lights[light_index]; 1736 | light.ambient.set(LIGHT[l].ambient); 1737 | 1738 | light = scene.lights[light_index]; 1739 | light.diffuse.set(LIGHT[l].diffuse); 1740 | 1741 | light_index++; 1742 | } 1743 | } 1744 | 1745 | if (scene.lights.length !== light_index) { 1746 | createLights(); 1747 | } 1748 | 1749 | }, 1750 | animateValues: function (colors) { 1751 | 1752 | var body = document.body, 1753 | html = document.documentElement, scrollTop = ((window.pageYOffset || html.scrollTop) - (html.clientTop || 0)); 1754 | 1755 | var height = Math.max(body.scrollHeight, body.offsetHeight, 1756 | html.clientHeight, html.scrollHeight, html.offsetHeight); 1757 | 1758 | 1759 | var length = colors.length; 1760 | var height = Math.round(height / length); // Height of the segment between two colors 1761 | var i = Math.floor(scrollTop / height); // Start color index 1762 | var d = scrollTop % height / height; // Which part of the segment between start color and end color is passed 1763 | var c1 = colors[i]; // Start color 1764 | var c2 = colors[(i + 1) % length]; // End color 1765 | var result = []; 1766 | for (var i = 0; i < c1.length; i++) { 1767 | result[i] = c1[i] + ((c2[i] - c1[i]) * d); 1768 | if (i !== 3) { 1769 | result[i] = Math.round(result[i]); 1770 | } 1771 | } 1772 | return result; 1773 | }, 1774 | formatRGBA: function (a) { 1775 | var string = "rgba(" + a[0] + "," + a[1] + "," + a[2] + "," + a[3] + ")"; 1776 | return string; 1777 | } 1778 | }; 1779 | 1780 | 1781 | function animate() { 1782 | now = Date.now() - start; 1783 | update(); 1784 | render(); 1785 | requestAnimationFrame(animate); 1786 | } 1787 | 1788 | function update() { 1789 | var ox, oy, oz, l, light, v, vertex, offset = MESH.depth / 2; 1790 | var light_index = 0; 1791 | var render_vector = FSS.Vector3.floor(FSS.Vector3.create(renderer.halfWidth, renderer.halfHeight, 0)); 1792 | 1793 | // Animate Lights 1794 | for (l = 0; l < LIGHT.length; l++) { 1795 | 1796 | for (var i = 0; i < LIGHT[l].count; i++) { 1797 | 1798 | light = scene.lights[light_index]; 1799 | 1800 | // Update Bounds 1801 | FSS.Vector3.copy(LIGHT[l].bounds, center); 1802 | FSS.Vector3.multiplyScalar(LIGHT[l].bounds, LIGHT[l].xyScalar); 1803 | 1804 | // Update Attractor 1805 | FSS.Vector3.setZ(attractor, LIGHT[l].zOffset); 1806 | 1807 | // Overwrite the Attractor position 1808 | if (LIGHT[l].autopilot && typeof LIGHT[l].position === "undefined") { 1809 | ox = Math.sin(LIGHT[l].step[0] * now * LIGHT[l].speed); 1810 | oy = Math.cos(LIGHT[l].step[1] * now * LIGHT[l].speed); 1811 | FSS.Vector3.set(attractor, 1812 | LIGHT[l].bounds[0] * ox, 1813 | LIGHT[l].bounds[1] * oy, 1814 | LIGHT[l].zOffset); 1815 | } 1816 | 1817 | // Reset the z position of the light 1818 | FSS.Vector3.setZ(light.position, LIGHT[l].zOffset); 1819 | 1820 | if (typeof LIGHT[l].position !== "undefined") { 1821 | FSS.Vector3.set(light.position); 1822 | FSS.Vector3.add(light.position, FSS.Vector3.create(LIGHT[l].position[0], LIGHT[l].position[1], LIGHT[l].zOffset)); 1823 | } else { 1824 | // Calculate the force Luke! 1825 | var D = Math.clamp(FSS.Vector3.distanceSquared(light.position, attractor), LIGHT[l].minDistance, LIGHT[l].maxDistance); 1826 | var F = LIGHT[l].gravity * light.mass / D; 1827 | FSS.Vector3.subtractVectors(light.force, attractor, light.position); 1828 | FSS.Vector3.normalise(light.force); 1829 | FSS.Vector3.multiplyScalar(light.force, F); 1830 | // Update the light position 1831 | FSS.Vector3.set(light.acceleration); 1832 | FSS.Vector3.add(light.acceleration, light.force); 1833 | FSS.Vector3.add(light.velocity, light.acceleration); 1834 | FSS.Vector3.multiplyScalar(light.velocity, LIGHT[l].dampening); 1835 | FSS.Vector3.limit(light.velocity, LIGHT[l].minLimit, LIGHT[l].maxLimit); 1836 | FSS.Vector3.add(light.position, light.velocity); 1837 | } 1838 | 1839 | light_index++; 1840 | } 1841 | } 1842 | 1843 | 1844 | 1845 | // Animate Vertices 1846 | for (v = geometry.vertices.length - 1; v >= 0; v--) { 1847 | vertex = geometry.vertices[v]; 1848 | ox = Math.sin(vertex.time + vertex.step[0] * now * MESH.speed); 1849 | oy = Math.cos(vertex.time + vertex.step[1] * now * MESH.speed); 1850 | oz = Math.sin(vertex.time + vertex.step[2] * now * MESH.speed); 1851 | vertex.position = FSS.Vector3.create( 1852 | MESH.xRange * geometry.segmentWidth * ox, 1853 | MESH.yRange * geometry.sliceHeight * oy, 1854 | MESH.zRange * offset * oz - offset); 1855 | if (MESH.positionFloor === true) { 1856 | vertex.position = FSS.Vector3.floor(vertex.position); 1857 | } 1858 | FSS.Vector3.add(vertex.position, vertex.anchor); 1859 | FSS.Vector3.add(vertex.position, render_vector); 1860 | } 1861 | 1862 | // Set the Geometry to dirty 1863 | geometry.dirty = true; 1864 | } 1865 | 1866 | function render() { 1867 | renderer.render(scene); 1868 | 1869 | // Draw Lights 1870 | if (LIGHT.draw) { 1871 | var l, lx, ly, light; 1872 | for (l = scene.lights.length - 1; l >= 0; l--) { 1873 | light = scene.lights[l]; 1874 | lx = light.position[0]; 1875 | ly = light.position[1]; 1876 | switch (RENDER.renderer) { 1877 | case CANVAS: 1878 | renderer.context.lineWidth = 0.5; 1879 | renderer.context.beginPath(); 1880 | renderer.context.arc(lx, ly, 10, 0, Math.PIM2); 1881 | renderer.context.strokeStyle = light.ambient; 1882 | renderer.context.stroke(); 1883 | renderer.context.beginPath(); 1884 | renderer.context.arc(lx, ly, 4, 0, Math.PIM2); 1885 | renderer.context.fillStyle = light.diffuse; 1886 | renderer.context.fill(); 1887 | break; 1888 | case SVG: 1889 | /* lx += renderer.halfWidth; */ 1890 | /* ly = renderer.halfHeight - ly; */ 1891 | light.core.setAttributeNS(null, 'fill', light.diffuse); 1892 | light.core.setAttributeNS(null, 'cx', lx); 1893 | light.core.setAttributeNS(null, 'cy', ly); 1894 | renderer.element.appendChild(light.core); 1895 | light.ring.setAttributeNS(null, 'stroke', light.ambient); 1896 | light.ring.setAttributeNS(null, 'cx', lx); 1897 | light.ring.setAttributeNS(null, 'cy', ly); 1898 | renderer.element.appendChild(light.ring); 1899 | break; 1900 | } 1901 | } 1902 | } 1903 | MESH.onRender(scene, renderer.context); 1904 | } 1905 | 1906 | function addEventListeners() { 1907 | if(window.attachEvent) { 1908 | window.addEventHandler = window.attachEvent; 1909 | } 1910 | window.addEventListener('resize', onWindowResize, false); 1911 | self.addEventListener('click', onMouseClick, false); 1912 | self.addEventListener('mousemove', onMouseMove, true); 1913 | } 1914 | 1915 | //------------------------------ 1916 | // Callbacks 1917 | //------------------------------ 1918 | function onMouseClick(event) { 1919 | FSS.Vector3.set(attractor, event.x, event.y); 1920 | /* FSS.Vector3.subtract(attractor, center); */ 1921 | LIGHT.autopilot = !LIGHT.autopilot; 1922 | } 1923 | 1924 | function onMouseMove(event) { 1925 | console.log(event); 1926 | FSS.Vector3.set(attractor, event.x, event.y); 1927 | /* FSS.Vector3.subtract(attractor, center); */ 1928 | } 1929 | 1930 | function onWindowResize(event) { 1931 | callbacks.resize(self.offsetWidth, self.offsetHeight); 1932 | render(); 1933 | } 1934 | 1935 | 1936 | // Let there be light! 1937 | initialise(); 1938 | return callbacks; 1939 | }; 1940 | })(); -------------------------------------------------------------------------------- /geometryangle.min.js: -------------------------------------------------------------------------------- 1 | FSS={FRONT:0,BACK:1,DOUBLE:2,SVGNS:"http://www.w3.org/2000/svg"};FSS.Array=typeof Float32Array==="function"?Float32Array:Array;FSS.Utils={isNumber:function(a){return !isNaN(parseFloat(a))&&isFinite(a)}};(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;aa){b[0]=a}if(b[1]>a){b[1]=a}if(b[2]>a){b[2]=a}return this},clamp:function(c,b,a){this.min(c,b);this.max(c,a);return this},limit:function(d,b,a){var c=this.length(d);if(b!==null&&ca){this.setLength(d,a)}}return this},dot:function(d,c){return d[0]*c[0]+d[1]*c[1]+d[2]*c[2]},normalise:function(a){return this.divideScalar(a,this.length(a))},negate:function(a){return this.multiplyScalar(a,-1)},distanceSquared:function(f,d){var g=f[0]-d[0];var e=f[1]-d[1];var c=f[2]-d[2];return g*g+e*e+c*c},distance:function(d,c){return Math.sqrt(this.distanceSquared(d,c))},lengthSquared:function(b){return b[0]*b[0]+b[1]*b[1]+b[2]*b[2]},length:function(b){return Math.sqrt(this.lengthSquared(b))},setLength:function(c,a){var b=this.length(c);if(b!==0&&a!==b){this.multiplyScalar(c,a/b)}return this},floor:function(a){a[0]=Math.floor(a[0]);a[1]=Math.floor(a[1]);a[2]=Math.floor(a[2]);return a}};FSS.Vector4={create:function(a,e,d,c){var b=new FSS.Array(4);this.set(b,a,e,d);return b},set:function(c,a,e,d,b){c[0]=a||0;c[1]=e||0;c[2]=d||0;c[3]=b||0;return this},setX:function(b,a){b[0]=a||0;return this},setY:function(a,b){a[1]=b||0;return this},setZ:function(a,b){a[2]=b||0;return this},setW:function(b,a){b[3]=a||0;return this},add:function(c,b){c[0]+=b[0];c[1]+=b[1];c[2]+=b[2];c[3]+=b[3];return this},multiplyVectors:function(e,d,c){e[0]=d[0]*c[0];e[1]=d[1]*c[1];e[2]=d[2]*c[2];e[3]=d[3]*c[3];return this},multiplyScalar:function(b,a){b[0]*=a;b[1]*=a;b[2]*=a;b[3]*=a;return this},min:function(b,a){if(b[0]a){b[0]=a}if(b[1]>a){b[1]=a}if(b[2]>a){b[2]=a}if(b[3]>a){b[3]=a}return this},clamp:function(c,b,a){this.min(c,b);this.max(c,a);return this}};FSS.Color=function(a,b){this.rgba=[];this.color=a||"#000000";this.opacity=FSS.Utils.isNumber(b)?b:1;this.set(this.color,this.opacity)};FSS.Color.prototype={set:function(a,c){if(a.indexOf("#")===-1){if(a.indexOf("rgb(")===0){var e=a.indexOf(",");this.rgba[0]=parseInt(a.substr(4,e));this.rgba[1]=parseInt(a.substr(e+1,a.indexOf(",",e)));this.rgba[2]=parseInt(a.substr(a.indexOf(",",e+1)+1,a.indexOf(")")));this.rgba[3]=1}else{if(a.indexOf("rgba(")===0){var e=a.indexOf(",");var b=a.indexOf(",",e+1);this.rgba[0]=parseInt(a.substr(5,e));this.rgba[1]=parseInt(a.substr(e+1,b));this.rgba[2]=parseInt(a.substr(a.indexOf(",",e+1)+1,a.indexOf(",",b)));this.rgba[3]=parseFloat(a.substr(a.indexOf(",",b+1)+1,a.indexOf(")")))}}}else{a=a.replace("#","");var d=a.length/3;this.rgba[0]=parseInt(a.substring(d*0,d*1),16)/255;this.rgba[1]=parseInt(a.substring(d*1,d*2),16)/255;this.rgba[2]=parseInt(a.substring(d*2,d*3),16)/255;this.rgba[3]=FSS.Utils.isNumber(c)?c:this.rgba[3]}return this},format:function(){return"rgba("+this.rgba[0]+","+this.rgba[1]+","+this.rgba[2]+","+this.rgba[3]+")"}};FSS.Object=function(){this.position=FSS.Vector3.create()};FSS.Object.prototype={setPosition:function(a,c,b){FSS.Vector3.set(this.position,a,c,b);return this}};FSS.Light=function(b,a){FSS.Object.call(this);this.ambient=new FSS.Color(b||"#FFFFFF");this.diffuse=new FSS.Color(a||"#FFFFFF");this.ray=FSS.Vector3.create()};FSS.Light.prototype=Object.create(FSS.Object.prototype);FSS.Vertex=function(a,c,b){this.position=FSS.Vector3.create(a,c,b)};FSS.Vertex.prototype={setPosition:function(a,c,b){FSS.Vector3.set(this.position,a,c,b);return this}};FSS.Triangle=function(e,d,f){this.a=e||new FSS.Vertex();this.b=d||new FSS.Vertex();this.c=f||new FSS.Vertex();this.vertices=[this.a,this.b,this.c];this.u=FSS.Vector3.create();this.v=FSS.Vector3.create();this.centroid=FSS.Vector3.create();this.normal=FSS.Vector3.create();this.color=new FSS.Color();this.polygon=document.createElementNS(FSS.SVGNS,"polygon");this.polygon.setAttributeNS(null,"stroke-linejoin","round");this.polygon.setAttributeNS(null,"stroke-miterlimit","1");this.polygon.setAttributeNS(null,"stroke-width","1");this.computeCentroid();this.computeNormal()};FSS.Triangle.prototype={computeCentroid:function(){this.centroid[0]=this.a.position[0]+this.b.position[0]+this.c.position[0];this.centroid[1]=this.a.position[1]+this.b.position[1]+this.c.position[1];this.centroid[2]=this.a.position[2]+this.b.position[2]+this.c.position[2];FSS.Vector3.divideScalar(this.centroid,3);return this},computeNormal:function(){FSS.Vector3.subtractVectors(this.u,this.b.position,this.a.position);FSS.Vector3.subtractVectors(this.v,this.c.position,this.a.position);FSS.Vector3.crossVectors(this.normal,this.u,this.v);FSS.Vector3.normalise(this.normal);return this}};FSS.Geometry=function(){this.vertices=[];this.triangles=[];this.dirty=false};FSS.Geometry.prototype={update:function(){if(this.dirty){var a,b;for(a=this.triangles.length-1;a>=0;a--){b=this.triangles[a];b.computeCentroid();b.computeNormal()}this.dirty=false}return this}};FSS.Plane=function(a,p,e,n){FSS.Geometry.call(this);this.width=a||100;this.height=p||100;this.segments=e||4;this.slices=n||4;this.segmentWidth=this.width/this.segments;this.sliceHeight=this.height/this.slices;var k,i,o,m,j,h,f,d,g=[],c=this.width*-0.5,b=this.height*0.5;for(k=0;k<=this.segments;k++){g.push([]);for(i=0;i<=this.slices;i++){f=new FSS.Vertex(c+k*this.segmentWidth,b-i*this.sliceHeight);g[k].push(f);this.vertices.push(f)}}for(k=0;k=0;j--){h=this.geometry.triangles[j];FSS.Vector4.set(h.color.rgba);for(c=e.length-1;c>=0;c--){f=e[c];FSS.Vector3.subtractVectors(f.ray,f.position,h.centroid);FSS.Vector3.normalise(f.ray);a=FSS.Vector3.dot(h.normal,f.ray);if(this.side===FSS.FRONT){a=Math.max(a,0)}else{if(this.side===FSS.BACK){a=Math.abs(Math.min(a,0))}else{if(this.side===FSS.DOUBLE){a=Math.max(Math.abs(a),0)}}}for(var g=0;g<3;g++){this.material.slave.rgba[g]=(((1/b)*this.material.ambient.rgba[g])*((1/b)*f.ambient.rgba[g]))/128;if(g!==3){this.material.slave.rgba[g]=Math.round(this.material.slave.rgba[g])}}FSS.Vector4.add(h.color.rgba,this.material.slave.rgba);for(var g=0;g<3;g++){this.material.slave.rgba[g]=((1/b)*this.material.diffuse.rgba[g]*(1/b)*f.diffuse.rgba[g])/128;if(g!==3){this.material.slave.rgba[g]=Math.round(this.material.slave.rgba[g])}}for(var g=0;g<3;g++){this.material.slave.rgba[g]=Math.round(this.material.slave.rgba[g]*a)}FSS.Vector4.add(h.color.rgba,this.material.slave.rgba)}FSS.Vector4.clamp(h.color.rgba,0,255);h.color.rgba[3]=this.material.diffuse.rgba[3]}}return this};FSS.Scene=function(){this.meshes=[];this.lights=[]};FSS.Scene.prototype={add:function(a){if(a instanceof FSS.Mesh&&!~this.meshes.indexOf(a)){this.meshes.push(a)}else{if(a instanceof FSS.Light&&!~this.lights.indexOf(a)){this.lights.push(a)}}return this},remove:function(a){if(a instanceof FSS.Mesh&&~this.meshes.indexOf(a)){this.meshes.splice(this.meshes.indexOf(a),1)}else{if(a instanceof FSS.Light&&~this.lights.indexOf(a)){this.lights.splice(this.lights.indexOf(a),1)}}return this}};FSS.Renderer=function(){this.width=0;this.height=0;this.halfWidth=0;this.halfHeight=0};FSS.Renderer.prototype={setSize:function(b,a){if(this.width===b&&this.height===a){return}this.width=b;this.height=a;this.halfWidth=this.width*0.5;this.halfHeight=this.height*0.5;return this},clear:function(){return this},render:function(a){return this}};FSS.CanvasRenderer=function(){FSS.Renderer.call(this);this.element=document.createElement("canvas");this.element.style.zIndex="-100";this.element.style.pointerEvents="none";this.context=this.element.getContext("2d");this.setSize(this.element.width,this.element.height)};FSS.CanvasRenderer.prototype=Object.create(FSS.Renderer.prototype);FSS.CanvasRenderer.prototype.setSize=function(b,a){FSS.Renderer.prototype.setSize.call(this,b,a);this.element.width=b;this.element.height=a;this.context.setTransform(1,0,0,1,0,0);return this};FSS.CanvasRenderer.prototype.clear=function(){FSS.Renderer.prototype.clear.call(this);this.context.clearRect(0,0,this.width,this.height);return this};var opacity=[];FSS.CanvasRenderer.prototype.render=function(f){FSS.Renderer.prototype.render.call(this,f);var b,k,j,g,d;var h=2*Math.PI;this.clear();this.context.lineJoin="round";this.context.lineWidth=0;for(b=f.meshes.length-1;b>=0;b--){k=f.meshes[b];if(typeof opacity[b]=="undefined"){opacity[b]=[]}if(k.visible){k.update(f.lights,true);for(j=k.geometry.triangles.length-1;j>=0;j--){var a=Date.now();if(typeof opacity[b][j]==="undefined"){opacity[b][j]={};opacity[b][j].step=FSS.Vector3.create(Math.randomInRange(0.2,1),Math.randomInRange(0.2,1),Math.randomInRange(0.2,1));opacity[b][j].time=Math.randomInRange(0,Math.PIM2);opacity[b][j].line=0}else{opacity[b][j].line=Math.sin(opacity[b][j].time+opacity[b][j].step[0]*a*(f.LINE.fluctuationSpeed/100))*f.LINE.fluctuationIntensity;opacity[b][j].vertex=Math.sin(opacity[b][j].time+opacity[b][j].step[0]*a*(f.VERTEX.fluctuationSpeed/100))*f.VERTEX.fluctuationIntensity;opacity[b][j].mesh=Math.sin(opacity[b][j].time+opacity[b][j].step[0]*a*(f.MESH.fluctuationSpeed/100))*f.MESH.fluctuationIntensity}g=k.geometry.triangles[j];if(f.MESH.draw!==false){i=g.color.rgba;d="rgba("+i[0]+","+i[1]+", "+i[2]+","+i[3]+")";this.context.beginPath();this.context.moveTo(g.a.position[0],g.a.position[1]);this.context.lineTo(g.b.position[0],g.b.position[1]);this.context.lineTo(g.c.position[0],g.c.position[1]);this.context.closePath();this.context.fillStyle=d;this.context.fill()}if(f.LINE.draw!==false){var i=new FSS.Color(f.LINE.fill);i=i.rgba;i[3]=i[3]*(1-opacity[b][j].line);i="rgba("+i[0]+","+i[1]+", "+i[2]+","+i[3]+")";this.context.beginPath();this.context.moveTo(g.a.position[0],g.a.position[1]);this.context.lineTo(g.b.position[0],g.b.position[1]);this.context.lineWidth=f.LINE.thickness;this.context.fillStyle=i;this.context.fill();this.context.strokeStyle=i;this.context.stroke()}if(f.VERTEX.draw!==false){var i=new FSS.Color(f.VERTEX.fill);i=i.rgba;i[3]=i[3]*(1-opacity[b][j].vertex);i="rgba("+i[0]+","+i[1]+", "+i[2]+","+i[3]+")";var e=new FSS.Color(f.VERTEX.strokeColor);e=e.rgba;e[3]=e[3]*(1-opacity[b][j].vertex);e="rgba("+e[0]+","+e[1]+", "+e[2]+","+e[3]+")";this.context.beginPath();this.context.arc(g.a.position[0],g.a.position[1],f.VERTEX.radius,0,h,false);this.context.fillStyle=i;this.context.fill();this.context.lineWidth=f.VERTEX.strokeWidth;this.context.strokeStyle=e;this.context.stroke()}}}}return this};FSS.WebGLRenderer=function(){FSS.Renderer.call(this);this.element=document.createElement("canvas");this.element.style.display="block";this.vertices=null;this.lights=null;var a={preserveDrawingBuffer:false,premultipliedAlpha:true,antialias:true,stencil:true,alpha:true};this.gl=this.getContext(this.element,a);this.unsupported=!this.gl;if(this.unsupported){return"WebGL is not supported by your browser."}else{this.gl.clearColor(0,0,0,0);this.gl.enable(this.gl.DEPTH_TEST);this.setSize(this.element.width,this.element.height)}};FSS.WebGLRenderer.prototype=Object.create(FSS.Renderer.prototype);FSS.WebGLRenderer.prototype.getContext=function(b,d){var c=false;try{if(!(c=b.getContext("experimental-webgl",d))){throw"Error creating WebGL context."}}catch(a){console.error(a)}return c};FSS.WebGLRenderer.prototype.setSize=function(b,a){FSS.Renderer.prototype.setSize.call(this,b,a);if(this.unsupported){return}this.element.width=b;this.element.height=a;this.gl.viewport(0,0,b,a);return this};FSS.WebGLRenderer.prototype.clear=function(){FSS.Renderer.prototype.clear.call(this);if(this.unsupported){return}this.gl.clear(this.gl.COLOR_BUFFER_BIT|this.gl.DEPTH_BUFFER_BIT);return this};FSS.WebGLRenderer.prototype.render=function(u){FSS.Renderer.prototype.render.call(this,u);if(this.unsupported){return}var k,a,j,c,p,n,d,i,q,o,x,b,g=false,w=u.lights.length,f,h,s,r,e=0;this.clear();if(this.lights!==w){this.lights=w;if(this.lights>0){this.buildProgram(w)}else{return}}if(!!this.program){for(k=u.meshes.length-1;k>=0;k--){a=u.meshes[k];if(a.geometry.dirty){g=true}a.update(u.lights,false);e+=a.geometry.triangles.length*3}if(g||this.vertices!==e){this.vertices=e;for(i in this.program.attributes){o=this.program.attributes[i];o.data=new FSS.Array(e*o.size);f=0;for(k=u.meshes.length-1;k>=0;k--){a=u.meshes[k];for(j=0,c=a.geometry.triangles.length;j=0;n--){d=u.lights[n];this.setBufferData(n,this.program.uniforms.lightPosition,d.position);this.setBufferData(n,this.program.uniforms.lightAmbient,d.ambient.rgba);this.setBufferData(n,this.program.uniforms.lightDiffuse,d.diffuse.rgba)}for(q in this.program.uniforms){o=this.program.uniforms[q];b=o.location;x=o.data;switch(o.structure){case"3f":this.gl.uniform3f(b,x[0],x[1],x[2]);break;case"3fv":this.gl.uniform3fv(b,x);break;case"4fv":this.gl.uniform4fv(b,x);break}}}this.gl.drawArrays(this.gl.TRIANGLES,0,this.vertices);return this};FSS.WebGLRenderer.prototype.setBufferData=function(b,a,d){if(FSS.Utils.isNumber(d)){a.data[b*a.size]=d}else{for(var c=d.length-1;c>=0;c--){a.data[b*a.size+c]=d[c]}}};FSS.WebGLRenderer.prototype.buildProgram=function(d){if(this.unsupported){return}var i=FSS.WebGLRenderer.VS(d);var f=FSS.WebGLRenderer.FS(d);var a=i+f;if(!!this.program&&this.program.code===a){return}var e=this.gl.createProgram();var h=this.buildShader(this.gl.VERTEX_SHADER,i);var b=this.buildShader(this.gl.FRAGMENT_SHADER,f);this.gl.attachShader(e,h);this.gl.attachShader(e,b);this.gl.linkProgram(e);if(!this.gl.getProgramParameter(e,this.gl.LINK_STATUS)){var g=this.gl.getError();var c=this.gl.getProgramParameter(e,this.gl.VALIDATE_STATUS);console.error("Could not initialise shader.\nVALIDATE_STATUS: "+c+"\nERROR: "+g);return null}this.gl.deleteShader(b);this.gl.deleteShader(h);e.code=a;e.attributes={side:this.buildBuffer(e,"attribute","aSide",1,"f"),position:this.buildBuffer(e,"attribute","aPosition",3,"v3"),centroid:this.buildBuffer(e,"attribute","aCentroid",3,"v3"),normal:this.buildBuffer(e,"attribute","aNormal",3,"v3"),ambient:this.buildBuffer(e,"attribute","aAmbient",4,"v4"),diffuse:this.buildBuffer(e,"attribute","aDiffuse",4,"v4")};e.uniforms={resolution:this.buildBuffer(e,"uniform","uResolution",3,"3f",1),lightPosition:this.buildBuffer(e,"uniform","uLightPosition",3,"3fv",d),lightAmbient:this.buildBuffer(e,"uniform","uLightAmbient",4,"4fv",d),lightDiffuse:this.buildBuffer(e,"uniform","uLightDiffuse",4,"4fv",d)};this.program=e;this.gl.useProgram(this.program);return e};FSS.WebGLRenderer.prototype.buildShader=function(a,c){if(this.unsupported){return}var b=this.gl.createShader(a);this.gl.shaderSource(b,c);this.gl.compileShader(b);if(!this.gl.getShaderParameter(b,this.gl.COMPILE_STATUS)){console.error(this.gl.getShaderInfoLog(b));return null}return b};FSS.WebGLRenderer.prototype.buildBuffer=function(c,f,d,e,b,g){var a={buffer:this.gl.createBuffer(),size:e,structure:b,data:null};switch(f){case"attribute":a.location=this.gl.getAttribLocation(c,d);break;case"uniform":a.location=this.gl.getUniformLocation(c,d);break}if(!!g){a.data=new FSS.Array(g*e)}return a};FSS.WebGLRenderer.VS=function(a){var b=["precision mediump float;","#define LIGHTS "+a,"attribute float aSide;","attribute vec3 aPosition;","attribute vec3 aCentroid;","attribute vec3 aNormal;","attribute vec4 aAmbient;","attribute vec4 aDiffuse;","uniform vec3 uResolution;","uniform vec3 uLightPosition[LIGHTS];","uniform vec4 uLightAmbient[LIGHTS];","uniform vec4 uLightDiffuse[LIGHTS];","varying vec4 vColor;","void main() {","vColor = vec4(0.0);","vec3 position = aPosition / uResolution * 2.0;","for (int i = 0; i < LIGHTS; i++) {","vec3 lightPosition = uLightPosition[i];","vec4 lightAmbient = uLightAmbient[i];","vec4 lightDiffuse = uLightDiffuse[i];","vec3 ray = normalize(lightPosition - aCentroid);","float illuminance = dot(aNormal, ray);","if (aSide == 0.0) {","illuminance = max(illuminance, 0.0);","} else if (aSide == 1.0) {","illuminance = abs(min(illuminance, 0.0));","} else if (aSide == 2.0) {","illuminance = max(abs(illuminance), 0.0);","}","vColor += aAmbient * lightAmbient;","vColor += aDiffuse * lightDiffuse * illuminance;","}","vColor = clamp(vColor, 0.0, 1.0);","gl_Position = vec4(position, 1.0);","}"].join("\n");return b};FSS.WebGLRenderer.FS=function(a){var b=["precision mediump float;","varying vec4 vColor;","void main() {","gl_FragColor = vColor;","}"].join("\n");return b};FSS.SVGRenderer=function(){FSS.Renderer.call(this);this.element=document.createElementNS(FSS.SVGNS,"svg");this.element.setAttribute("xmlns",FSS.SVGNS);this.element.setAttribute("version","1.1");this.element.style.display="block";this.setSize(300,150)};FSS.SVGRenderer.prototype=Object.create(FSS.Renderer.prototype);FSS.SVGRenderer.prototype.setSize=function(b,a){FSS.Renderer.prototype.setSize.call(this,b,a);this.element.setAttribute("width",b);this.element.setAttribute("height",a);return this};FSS.SVGRenderer.prototype.clear=function(){FSS.Renderer.prototype.clear.call(this);for(var a=this.element.childNodes.length-1;a>=0;a--){this.element.removeChild(this.element.childNodes[a])}return this};FSS.SVGRenderer.prototype.render=function(f){FSS.Renderer.prototype.render.call(this,f);var a,g,b,e,d,c;for(a=f.meshes.length-1;a>=0;a--){g=f.meshes[a];if(g.visible){g.update(f.lights,true);for(b=g.geometry.triangles.length-1;b>=0;b--){e=g.geometry.triangles[b];if(e.polygon.parentNode!==this.element){this.element.appendChild(e.polygon)}d=this.formatPoint(e.a)+" ";d+=this.formatPoint(e.b)+" ";d+=this.formatPoint(e.c);c=this.formatStyle(e.color.format());e.polygon.setAttributeNS(null,"points",d);e.polygon.setAttributeNS(null,"style",c)}}}return this};FSS.SVGRenderer.prototype.formatPoint=function(a){return(a.position[0])+","+(a.position[1])};FSS.SVGRenderer.prototype.formatStyle=function(a){var b="fill:"+a+";";b+="stroke:"+a+";";return b};(function(){$.fn.Geometryangle=function(g){var e=[];var f=$(this);var b=f.length,c;for(var d=0;d=0;k--){aa=o.vertices[k];aa.anchor=FSS.Vector3.floor(FSS.Vector3.clone(aa.position));aa.step=FSS.Vector3.create(Math.randomInRange(0.2,1),Math.randomInRange(0.2,1),Math.randomInRange(0.2,1));aa.time=Math.randomInRange(0,Math.PIM2)}}function s(){var aa,k;for(aa=z.lights.length-1;aa>=0;aa--){k=z.lights[aa];z.remove(k)}H.clear();for(aa=0;aa=0;ak--){aj=o.vertices[ak];ad=Math.sin(aj.time+aj.step[0]*R*M.speed);ac=Math.cos(aj.time+aj.step[1]*R*M.speed);aa=Math.sin(aj.time+aj.step[2]*R*M.speed);aj.position=FSS.Vector3.create(M.xRange*o.segmentWidth*ad,M.yRange*o.sliceHeight*ac,M.zRange*ag*aa-ag);if(M.positionFloor===true){aj.position=FSS.Vector3.floor(aj.position)}FSS.Vector3.add(aj.position,aj.anchor);FSS.Vector3.add(aj.position,ab)}o.dirty=true}function D(){H.render(z);if(B.draw){var aa,ac,ab,k;for(aa=z.lights.length-1;aa>=0;aa--){k=z.lights[aa];ac=k.position[0];ab=k.position[1];switch(n.renderer){case L:H.context.lineWidth=0.5;H.context.beginPath();H.context.arc(ac,ab,10,0,Math.PIM2);H.context.strokeStyle=k.ambient;H.context.stroke();H.context.beginPath();H.context.arc(ac,ab,4,0,Math.PIM2);H.context.fillStyle=k.diffuse;H.context.fill();break;case C:k.core.setAttributeNS(null,"fill",k.diffuse);k.core.setAttributeNS(null,"cx",ac);k.core.setAttributeNS(null,"cy",ab);H.element.appendChild(k.core);k.ring.setAttributeNS(null,"stroke",k.ambient);k.ring.setAttributeNS(null,"cx",ac);k.ring.setAttributeNS(null,"cy",ab);H.element.appendChild(k.ring);break}}}M.onRender(z,H.context)}function v(){if(window.attachEvent){window.addEventHandler=window.attachEvent}window.addEventListener("resize",d,false);i.addEventListener("click",e,false);i.addEventListener("mousemove",b,true)}function e(k){FSS.Vector3.set(Q,k.x,k.y);B.autopilot=!B.autopilot}function b(k){console.log(k);FSS.Vector3.set(Q,k.x,k.y)}function d(k){f.resize(i.offsetWidth,i.offsetHeight);D()}t();return f}})(); --------------------------------------------------------------------------------