Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit b2483ce3ccbd3b8f55e34c06ae2092c5f13a8a8f @dli committed Sep 20, 2015
Showing with 2,760 additions and 0 deletions.
  1. +21 −0 LICENSE
  2. +7 −0 README.md
  3. +41 −0 buttons.js
  4. +142 −0 camera.js
  5. +157 −0 filamentsgeometry.js
  6. +46 −0 index.html
  7. +57 −0 main.js
  8. +386 −0 mathutilities.js
  9. +516 −0 shaders.js
  10. +10 −0 sharedvortexfilamentsconstants.js
  11. +54 −0 slider.js
  12. +132 −0 spheregeometry.js
  13. +9 −0 utilities.js
  14. +194 −0 vortexfilaments.js
  15. +129 −0 vortexspheres.css
  16. +778 −0 vortexspheres.js
  17. +81 −0 webglutilities.js
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 David Li (http://david.li)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
@@ -0,0 +1,7 @@
+#Vortex Spheres
+
+![](http://david.li/images/vortexspheresgithub.png)
+
+[http://david.li/vortexspheres](http://david.li/vortexspheres) ([video](http://www.youtube.com/watch?v=CmnbvHwd3jc))
+
+Vortex filament based simulation + spherical ambient occlusion volume based rendering.
@@ -0,0 +1,41 @@
+'use strict'
+
+var Buttons = function (elements, changeCallback) {
+ var activeElement = elements[0];
+
+ var color;
+
+ this.setColor = function (newColor) {
+ color = newColor;
+ refresh();
+ };
+
+ var refresh = function () {
+ for (var i = 0; i < elements.length; ++i) {
+ if (elements[i] === activeElement) {
+ elements[i].style.color = '#111111';
+ } else {
+ elements[i].style.color = '#777777';
+ }
+ }
+ };
+
+ for (var i = 0; i < elements.length; ++i) {
+ (function () { //create closure to store index
+ var index = i;
+ var clickedElement = elements[i];
+ elements[i].addEventListener('click', function () {
+ if (activeElement !== clickedElement) {
+ activeElement = clickedElement;
+
+ changeCallback(index);
+
+ refresh();
+ }
+
+ });
+ }());
+ }
+
+ refresh();
+};
@@ -0,0 +1,142 @@
+'use strict'
+
+var Camera = function (element, distance, orbitPoint) {
+ var azimuth = 0.0,
+ elevation = 0;
+
+ var lastMouseX = 0,
+ lastMouseY = 0;
+
+ var mouseDown = false;
+
+ var viewMatrix = new Float32Array(16);
+
+ this.getViewMatrix = function () {
+ return viewMatrix;
+ };
+
+ var onDown = function (x, y) {
+ mouseDown = true;
+ lastMouseX = x;
+ lastMouseY = y;
+ };
+
+ var onUp = function () {
+ mouseDown = false;
+ };
+
+ this.isMouseDown = function () {
+ return mouseDown;
+ };
+
+ var currentMouseX = 0,
+ currentMouseY = 0;
+
+ var mouseVelocityX = 0,
+ mouseVelocityY = 0;
+
+ var onMove = function (x, y) {
+ currentMouseX = x;
+ currentMouseY = y;
+ };
+
+ var SENSITIVITY = 0.005;
+ var MIN_ELEVATION = 0;
+ var MAX_ELEVATION = Math.PI / 3;
+
+ this.update = function () {
+ if (mouseDown) {
+ var deltaAzimuth = (currentMouseX - lastMouseX) * SENSITIVITY;
+ var deltaElevation = (currentMouseY - lastMouseY) * SENSITIVITY;
+
+ azimuth += deltaAzimuth;
+ elevation += deltaElevation;
+
+ if (elevation > MAX_ELEVATION) elevation = MAX_ELEVATION;
+ if (elevation < MIN_ELEVATION) elevation = MIN_ELEVATION;
+
+ recomputeViewMatrix();
+
+ mouseVelocityX = (currentMouseX - lastMouseX);
+ mouseVelocityY = (currentMouseY - lastMouseY);
+
+ lastMouseX = currentMouseX;
+ lastMouseY = currentMouseY;
+
+ element.style.cursor = '-webkit-grabbing';
+ element.style.cursor = '-moz-grabbing';
+ element.style.cursor = 'grabbing';
+ } else {
+ var deltaAzimuth = mouseVelocityX * SENSITIVITY;
+ var deltaElevation = mouseVelocityY * SENSITIVITY;
+
+ mouseVelocityX *= 0.8;
+ mouseVelocityY *= 0.8;
+
+ azimuth += deltaAzimuth;
+ elevation += deltaElevation;
+
+ if (elevation > MAX_ELEVATION) elevation = MAX_ELEVATION;
+ if (elevation < MIN_ELEVATION) elevation = MIN_ELEVATION;
+
+ recomputeViewMatrix();
+
+
+ element.style.cursor = '-webkit-grab';
+ element.style.cursor = '-moz-grab';
+ element.style.cursor = 'grab';
+ }
+
+ distance += (targetDistance - distance) * 0.2;
+ };
+
+ element.addEventListener('mousedown', function (event) {
+ event.preventDefault();
+ onDown(getMousePosition(event, element).x, getMousePosition(event, element).y);
+ });
+
+ document.addEventListener('mouseup', function (event) {
+ event.preventDefault();
+ onUp();
+ });
+
+ document.addEventListener('mousemove', function (event) {
+ event.preventDefault();
+ onMove(getMousePosition(event, element).x, getMousePosition(event, element).y);
+ });
+
+
+ var recomputeViewMatrix = function () {
+ var xRotationMatrix = new Float32Array(16),
+ yRotationMatrix = new Float32Array(16),
+ distanceTranslationMatrix = makeIdentityMatrix(new Float32Array(16)),
+ orbitTranslationMatrix = makeIdentityMatrix(new Float32Array(16));
+
+ makeIdentityMatrix(viewMatrix);
+
+ makeXRotationMatrix(xRotationMatrix, elevation);
+ makeYRotationMatrix(yRotationMatrix, azimuth);
+ distanceTranslationMatrix[14] = -distance;
+ orbitTranslationMatrix[12] = -orbitPoint[0];
+ orbitTranslationMatrix[13] = -orbitPoint[1];
+ orbitTranslationMatrix[14] = -orbitPoint[2];
+
+ premultiplyMatrix(viewMatrix, viewMatrix, orbitTranslationMatrix);
+ premultiplyMatrix(viewMatrix, viewMatrix, yRotationMatrix);
+ premultiplyMatrix(viewMatrix, viewMatrix, xRotationMatrix);
+ premultiplyMatrix(viewMatrix, viewMatrix, distanceTranslationMatrix);
+ };
+
+ recomputeViewMatrix();
+
+ var MIN_DISTANCE = 0.5;
+ var MAX_DISTANCE = 1.0;
+ var targetDistance = distance;
+
+ element.addEventListener('wheel', function (event) {
+ var scrollDelta = event.deltaY;
+ targetDistance += ((scrollDelta > 0) ? 1 : -1) * 0.03;
+ if (targetDistance < MIN_DISTANCE) targetDistance = MIN_DISTANCE;
+ if (targetDistance > MAX_DISTANCE) targetDistance = MAX_DISTANCE;
+ })
+};
@@ -0,0 +1,157 @@
+'use strict'
+
+var mod = function (x, n) { //positive modulo
+ var m = x % n;
+ return m < 0 ? m + n : m;
+};
+
+var evaluateCubicSpline = function (outPoint, t, startPoint, endPoint, startTangent, endTangent) {
+ var t2 = t * t;
+ var t3 = t * t * t;
+
+ for (var l = 0; l < 3; ++l) {
+ outPoint[l] = (2 * t3 - 3 * t2 + 1) * startPoint[l] + (t3 - 2 * t2 + t) * startTangent[l] + (-2 * t3 + 3 * t2) * endPoint[l] + (t3 - t2) * endTangent[l];
+ }
+
+ return outPoint;
+};
+
+var evaluateCubicSplineTangent = function (outTangent, t, startPoint, endPoint, startTangent, endTangent) {
+ var t2 = t * t;
+
+ for (var l = 0; l < 3; ++l) {
+ outTangent[l] = (6 * t2 - 6 * t) * startPoint[l] + (3 * t2 - 4 * t + 1) * startTangent[l] + (-6 * t2 + 6 * t) * endPoint[l] + (3 * t2 - 2 * t) * endTangent[l];
+ }
+
+ return outTangent;
+};
+
+var UP_DIRECTION = [0, 1, 0];
+var CIRCLE_SEGMENTS = 5;
+var SPLINE_ALPHA = 0.5; //centripetal catmull rom spline
+var SPLINE_SUBDIVISIONS = 5; //centripetal catmull rom spline subdivisions
+
+var FilamentsGeometry = function (verticesPerFilament) {
+ //cache to avoid garbage collection
+ var temp = [];
+ var surfacePoint = [], surfaceNormal = [];
+ var circleUp = [], circleRight = [];
+ var startTangent = [], endTangent = [];
+ var filamentPoint = [], filamentTangent = [];
+
+
+ this.vertexData = new Float32Array(MAX_FILAMENTS * verticesPerFilament * SPLINE_SUBDIVISIONS * CIRCLE_SEGMENTS * 3);
+ this.normalData = new Float32Array(MAX_FILAMENTS * verticesPerFilament * SPLINE_SUBDIVISIONS * CIRCLE_SEGMENTS * 3);
+
+ var POINTS_PER_FILAMENT = verticesPerFilament * SPLINE_SUBDIVISIONS; //subdivided points along each filament (around which each circle is formed)
+ this.indices = new Uint16Array(MAX_FILAMENTS * POINTS_PER_FILAMENT * CIRCLE_SEGMENTS * 6);
+
+ this.generate = function (filamentSystem) {
+ var filaments = filamentSystem.filaments;
+ var filamentIndexData = this.indexData = [];
+
+ var baseIndex = 0;
+
+ var vertexIndex = 0;
+ var normalIndex = 0;
+ var indicesIndex = 0;
+
+ for (var i = 0; i < MAX_FILAMENTS; ++i) { //for each filament
+ var filamentVertices = filaments[i].vertices;
+
+ var subdividedFilamentPoints = [];
+ var subdividedFilamentTangents = [];
+
+ var filamentTubeRadius = FILAMENT_GEOMETRY_RADIUS_SCALE * filaments[i].smoothingRadius;
+ if (i === 0) { //shrink out the next filament to be destroyed
+ filamentTubeRadius *= smoothstep(1.0, 0.5, filamentSystem.getEmissionT());
+ }
+
+ var indicesInBatch = 0;
+
+ for (var j = 0; j < verticesPerFilament; ++j) { //for each filament segment going from point j to point j + 1
+ var point0 = filamentVertices[mod(j - 1, verticesPerFilament)],
+ point1 = filamentVertices[mod(j + 0, verticesPerFilament)],
+ point2 = filamentVertices[mod(j + 1, verticesPerFilament)],
+ point3 = filamentVertices[mod(j + 2, verticesPerFilament)];
+
+ var t0 = 0,
+ t1 = t0 + Math.pow(distanceBetweenVectors(point0, point1), SPLINE_ALPHA),
+ t2 = t1 + Math.pow(distanceBetweenVectors(point1, point2), SPLINE_ALPHA),
+ t3 = t2 + Math.pow(distanceBetweenVectors(point2, point3), SPLINE_ALPHA);
+
+ var startPoint = point1;
+ var endPoint = point2;
+
+ for (var k = 0; k < 3; ++k) {
+ startTangent[k] = (point1[k] - point0[k]) / (t1 - t0) - (point2[k] - point0[k]) / (t2 - t0) + (point2[k] - point1[k]) / (t2 - t1);
+ endTangent[k] = (point2[k] - point1[k]) / (t2 - t1) - (point3[k] - point1[k]) / (t3 - t1) + (point3[k] - point2[k]) / (t3 - t2);
+
+ startTangent[k] *= (t2 - t1);
+ endTangent[k] *= (t2 - t1);
+ }
+
+ for (var k = 0; k < SPLINE_SUBDIVISIONS; ++k) {
+ evaluateCubicSpline(filamentPoint, k / SPLINE_SUBDIVISIONS, startPoint, endPoint, startTangent, endTangent);
+ evaluateCubicSplineTangent(filamentTangent, k / (SPLINE_SUBDIVISIONS - 1), startPoint, endPoint, startTangent, endTangent);
+
+ if (filamentPoint[1] < filamentTubeRadius) {
+ filamentPoint[1] = filamentTubeRadius;
+ }
+
+ normalizeVector(circleUp, crossVectors(temp, filamentTangent, UP_DIRECTION));
+ normalizeVector(circleRight, crossVectors(circleRight, circleUp, filamentTangent));
+
+ for (var l = 0; l < CIRCLE_SEGMENTS; ++l) {
+ var angle = (l / CIRCLE_SEGMENTS) * Math.PI * 2.0;
+
+ var circleX = Math.cos(angle) * filamentTubeRadius;
+ var circleY = Math.sin(angle) * filamentTubeRadius;
+
+ var normal = [];
+ for (var m = 0; m < 3; ++m) {
+ normal[m] = circleX * circleRight[m] + circleY * circleUp[m];
+ }
+
+ addVectors(surfacePoint, filamentPoint, normal);
+ multiplyVectorByScalar(surfaceNormal, normal, 1.0 / filamentTubeRadius); //normalize
+
+ this.vertexData[vertexIndex] = surfacePoint[0];
+ this.vertexData[vertexIndex + 1] = surfacePoint[1];
+ this.vertexData[vertexIndex + 2] = surfacePoint[2];
+ vertexIndex += 3;
+
+ this.normalData[normalIndex] = surfaceNormal[0];
+ this.normalData[normalIndex + 1] = surfaceNormal[1];
+ this.normalData[normalIndex + 2] = surfaceNormal[2];
+ normalIndex += 3;
+
+ indicesInBatch++;
+ }
+ }
+ }
+
+ for (var filamentX = 0; filamentX < POINTS_PER_FILAMENT; ++filamentX) {
+ for (var filamentY = 0; filamentY < CIRCLE_SEGMENTS; ++filamentY) {
+
+ var bottomLeftIndex = baseIndex + filamentX * CIRCLE_SEGMENTS + filamentY;
+ var topLeftIndex = baseIndex + filamentX * CIRCLE_SEGMENTS + (filamentY + 1) % CIRCLE_SEGMENTS;
+ var bottomRightIndex = baseIndex + ((filamentX + 1) % POINTS_PER_FILAMENT) * CIRCLE_SEGMENTS + filamentY;
+ var topRightIndex = baseIndex + ((filamentX + 1) % POINTS_PER_FILAMENT) * CIRCLE_SEGMENTS + (filamentY + 1) % CIRCLE_SEGMENTS;
+
+ this.indices[indicesIndex + 0] = bottomLeftIndex;
+ this.indices[indicesIndex + 1] = bottomRightIndex;
+ this.indices[indicesIndex + 2] = topRightIndex;
+
+ this.indices[indicesIndex + 3] = topRightIndex;
+ this.indices[indicesIndex + 4] = topLeftIndex;
+ this.indices[indicesIndex + 5] = bottomLeftIndex;
+
+ indicesIndex += 6;
+ }
+ }
+
+ baseIndex += indicesInBatch;
+ }
+ };
+};
Oops, something went wrong.

0 comments on commit b2483ce

Please sign in to comment.