├── LICENSE.md ├── README.md ├── img ├── cramped-corners.png ├── discontinuous-edges.png ├── faceted-cube-wireframe.png ├── nearest-neighbour.png ├── normalized-faceted-cube-wireframe.png ├── planet.png ├── sphere-wireframe.png ├── spherical-mapping-butthole-2.png ├── tricosine.png ├── tricubic.png ├── trilinear.png ├── uv-coordinates.png └── webgl-cube.png ├── index.html ├── js ├── main.js ├── material.js ├── planet.js ├── spheremap.js ├── starbox.js └── util.js └── lib └── three.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Holger Ludvigsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | procedural-planet 2 | ================= 3 | 4 | Proceural planet in WebGL and three.js 5 | 6 |  7 | 8 | See the [resulting 3D animation](http://holgerl.github.io/procedural-planet/) 9 | 10 | See the [blog post](https://blogg.bekk.no/procedural-planet-in-webgl-and-three-js-fc77f14f5505) 11 | 12 | See also [part 2](https://github.com/holgerl/procedural-planet-part-2) of the blog post 13 | -------------------------------------------------------------------------------- /img/cramped-corners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/cramped-corners.png -------------------------------------------------------------------------------- /img/discontinuous-edges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/discontinuous-edges.png -------------------------------------------------------------------------------- /img/faceted-cube-wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/faceted-cube-wireframe.png -------------------------------------------------------------------------------- /img/nearest-neighbour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/nearest-neighbour.png -------------------------------------------------------------------------------- /img/normalized-faceted-cube-wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/normalized-faceted-cube-wireframe.png -------------------------------------------------------------------------------- /img/planet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/planet.png -------------------------------------------------------------------------------- /img/sphere-wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/sphere-wireframe.png -------------------------------------------------------------------------------- /img/spherical-mapping-butthole-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/spherical-mapping-butthole-2.png -------------------------------------------------------------------------------- /img/tricosine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/tricosine.png -------------------------------------------------------------------------------- /img/tricubic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/tricubic.png -------------------------------------------------------------------------------- /img/trilinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/trilinear.png -------------------------------------------------------------------------------- /img/uv-coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/uv-coordinates.png -------------------------------------------------------------------------------- /img/webgl-cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holgerl/procedural-planet/b87849eef80f568be532bee1860e8f69fd5b9ec3/img/webgl-cube.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.main = SS.main || {}; 3 | 4 | SS.main.main = function() { 5 | renderer = new THREE.WebGLRenderer({antialias: true}); 6 | renderer.gammaInput = true; 7 | renderer.gammaOutput = true; 8 | renderer.setClearColor(0x000000, 1); 9 | renderer.setSize(window.innerWidth, window.innerHeight); 10 | renderer.domElement.setAttribute('id', 'renderer'); 11 | document.body.appendChild(renderer.domElement); 12 | 13 | scene = new THREE.Scene(); 14 | var ratio = renderer.getContext().drawingBufferWidth / renderer.getContext().drawingBufferHeight; 15 | camera = new THREE.PerspectiveCamera(60, ratio, 0.1, 10000); 16 | editorCamera = new SS.util.EditorCamera(camera, document, 15, new THREE.Vector2(-Math.PI*(2/4),-Math.PI*(1/4))); 17 | 18 | SS.util.addResizeListener(); 19 | SS.main.addSceneContent(scene); 20 | 21 | SS.main.render(); 22 | } 23 | 24 | SS.main.render = function() { 25 | requestAnimationFrame(SS.main.render); 26 | 27 | time = window.time || new Date().getTime(); 28 | var newTime = new Date().getTime(); 29 | var diff = newTime - time; 30 | if (editorCamera.mouseDown == false) { 31 | editorCamera.cameraPos.x += diff/3000; 32 | editorCamera.cameraStartPos = editorCamera.cameraPos; 33 | editorCamera.rotateCamera(); 34 | } 35 | time = newTime; 36 | 37 | renderer.render(scene, camera); 38 | }; 39 | 40 | SS.main.addSceneContent = function(scene) { 41 | sunLight = new THREE.PointLight(new THREE.Color(0xffffff), 1.0); 42 | sunLight.position.set(100, 0, 0); 43 | scene.add(sunLight); 44 | 45 | scene.add(new SS.planet.Planet(5)); 46 | 47 | //scene.add(new SS.starbox.StarBox(4000)); 48 | } -------------------------------------------------------------------------------- /js/material.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.material = SS.material || {}; 3 | 4 | SS.material.shaderMaterial = function(map) { 5 | var vertexShader = "\ 6 | varying vec3 vNormal;\ 7 | varying vec3 cameraVector;\ 8 | varying vec3 vPosition;\ 9 | varying vec2 vUv;\ 10 | \ 11 | void main() {\ 12 | vNormal = normal;\ 13 | vec4 vPosition4 = modelMatrix * vec4(position, 1.0);\ 14 | vPosition = vPosition4.xyz;\ 15 | cameraVector = cameraPosition - vPosition;\ 16 | vUv = uv;\ 17 | \ 18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\ 19 | }\ 20 | "; 21 | 22 | var fragmentShader = "\ 23 | uniform vec3 pointLightPosition;\ 24 | uniform sampler2D map;\ 25 | uniform sampler2D normalMap;\ 26 | \ 27 | varying vec3 vNormal;\ 28 | varying vec3 vPosition;\ 29 | varying vec3 cameraVector;\ 30 | varying vec2 vUv;\ 31 | \ 32 | mat4 rotationMatrix(vec3 axis, float angle) {\ 33 | axis = normalize(axis);\ 34 | float s = sin(angle);\ 35 | float c = cos(angle);\ 36 | float oc = 1.0 - c;\ 37 | \ 38 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,\ 39 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,\ 40 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,\ 41 | 0.0, 0.0, 0.0, 1.0);\ 42 | }\ 43 | \ 44 | vec3 bumpNormal(sampler2D normalMap, vec2 vUv) {\ 45 | vec3 bumpedNormal = normalize(texture2D(normalMap, vUv).xyz * 2.0 - 1.0);\ 46 | \ 47 | vec3 y_axis = vec3(0,1,0);\ 48 | float rot_angle = acos(dot(bumpedNormal,y_axis));\ 49 | vec3 rot_axis = normalize(cross(bumpedNormal,y_axis));\ 50 | return vec3(rotationMatrix(rot_axis, rot_angle) * vec4(vNormal, 1.0));\ 51 | }\ 52 | \ 53 | void main() {\ 54 | float PI = 3.14159265358979323846264;\ 55 | vec3 light = pointLightPosition - vPosition;\ 56 | vec3 cameraDir = normalize(cameraVector);\ 57 | vec3 newNormal = bumpNormal(normalMap, vUv);\ 58 | \ 59 | light = normalize(light);\ 60 | \ 61 | float lightAngle = max(0.0, dot(newNormal, light));\ 62 | float viewAngle = max(0.0, dot(vNormal, cameraDir));\ 63 | float adjustedLightAngle = min(0.6, lightAngle) / 0.6;\ 64 | float adjustedViewAngle = min(0.65, viewAngle) / 0.65;\ 65 | float invertedViewAngle = pow(acos(viewAngle), 3.0) * 0.4;\ 66 | \ 67 | float dProd = 0.0;\ 68 | dProd += 0.5 * lightAngle;\ 69 | dProd += 0.2 * lightAngle * (invertedViewAngle - 0.1);\ 70 | dProd += invertedViewAngle * 0.5 * (max(-0.35, dot(vNormal, light)) + 0.35);\ 71 | dProd *= 0.7 + pow(invertedViewAngle/(PI/2.0), 2.0);\ 72 | \ 73 | dProd *= 0.5;\ 74 | vec4 atmColor = vec4(dProd, dProd, dProd, 1.0);\ 75 | \ 76 | vec4 texelColor = texture2D(map, vUv) * min(asin(lightAngle), 1.0);\ 77 | gl_FragColor = texelColor + min(atmColor, 0.8);\ 78 | }\ 79 | "; 80 | 81 | var uniforms = { 82 | "pointLightPosition": {"type": "v3", "value": sunLight.position}, 83 | "map": {"type": "t", "value": map}, 84 | "normalMap": {"type": "t", "value": SS.util.heightToNormalMap(map)} 85 | }; 86 | 87 | return new THREE.ShaderMaterial({ 88 | uniforms: uniforms, 89 | vertexShader: vertexShader, 90 | fragmentShader: fragmentShader, 91 | transparent: true 92 | }); 93 | } -------------------------------------------------------------------------------- /js/planet.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.planet = SS.planet || {}; 3 | 4 | var maxDetail = SS.lowgraphics ? 16 : 512; //256 = 11 seconds (before), 512 = 5 seconds (now) 5 | 6 | SS.planet.Planet = function(planetRadius) { 7 | THREE.Object3D.call(this); 8 | 9 | var sphere = new SS.spheremap.Sphere( 10 | SS.planet.planetScalarField, 11 | planetRadius, 12 | function(map) { 13 | return SS.material.shaderMaterial(map); 14 | }, 15 | maxDetail, 16 | true 17 | ); 18 | 19 | this.add(sphere); 20 | } 21 | SS.planet.Planet.prototype = Object.create(THREE.Object3D.prototype); 22 | 23 | SS.planet.planetScalarField = function(x, y, z) { 24 | var resolution1 = 4; 25 | var resolution2 = 16; 26 | var resolution3 = 64; 27 | var resolutionMax = maxDetail; 28 | 29 | var coordFloat = new THREE.Vector3(); 30 | 31 | var randomScalarField = function(x, y, z) { 32 | return SS.util.random4(x, y, z); 33 | } 34 | 35 | var helper = function(x, y, z, scalarField, resolution, interpolationMethod) { 36 | // Because the sphere sample function gives normalized coordinates: 37 | x = (x+1)/2*resolution; 38 | y = (y+1)/2*resolution; 39 | z = (z+1)/2*resolution; 40 | 41 | coordFloat.set(x, y, z); 42 | var interpolated = interpolationMethod(coordFloat, scalarField); 43 | return interpolated*2 - 1; // Gives values (-1, 1) 44 | } 45 | 46 | var level1 = helper(x, y, z, randomScalarField, resolution1, SS.util.tricosineInterpolation); 47 | var level2 = helper(x, y, z, randomScalarField, resolution2, SS.util.tricosineInterpolation); 48 | var level3 = helper(x, y, z, randomScalarField, resolution3, SS.util.tricosineInterpolation); 49 | var levelMax = helper(x, y, z, randomScalarField, resolutionMax, SS.util.nearestNeighbour); 50 | 51 | var c = 0.5; 52 | c *= 1 + level1*0.75; 53 | c *= 1 + level2*0.25; 54 | c *= 1 + level3*0.075; 55 | c *= 1 + levelMax*(1/25); 56 | 57 | if (c < 0.5) c *= 0.9; 58 | 59 | c = SS.util.clamp(c, 0, 1); 60 | 61 | return new THREE.Color().setRGB(c, c, c); 62 | } -------------------------------------------------------------------------------- /js/spheremap.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.spheremap = SS.spheremap || {}; 3 | 4 | SS.spheremap.Sphere = function(scalarField, radius, materialCallback, resolution) { 5 | THREE.Object3D.call(this); 6 | 7 | radius = radius || 1; 8 | materialCallback = materialCallback || function() {return new THREE.MeshPhongMaterial()}; 9 | resolution = resolution || 128; 10 | 11 | var geometry = new THREE.BoxGeometry(1, 1, 1, 64, 64, 64); 12 | 13 | for (var i in geometry.vertices) { 14 | var vertex = geometry.vertices[i]; 15 | vertex.normalize().multiplyScalar(radius); 16 | } 17 | 18 | SS.util.computeGeometry(geometry); 19 | 20 | var computeVertexNormals = function(geometry) { 21 | for (var f = 0; f < geometry.faces.length; f++) { 22 | var face = geometry.faces[f]; 23 | face.vertexNormals[0] = geometry.vertices[face.a].clone().normalize(); 24 | face.vertexNormals[1] = geometry.vertices[face.b].clone().normalize(); 25 | face.vertexNormals[2] = geometry.vertices[face.c].clone().normalize(); 26 | } 27 | } 28 | 29 | computeVertexNormals(geometry); // TODO: Why is this neccessary? (Why does geometry.computeVertexNormals not work correctly?) 30 | 31 | var materialArray = []; 32 | for (var i = 0; i < 6; i++) { 33 | var map = SS.spheremap.createMap(i, scalarField, resolution); 34 | var faceMaterial = materialCallback(map); 35 | materialArray.push(faceMaterial); 36 | } 37 | 38 | var sphereMaterial = new THREE.MeshFaceMaterial(materialArray); 39 | var sphere = new THREE.Mesh(geometry, sphereMaterial); 40 | 41 | this.add(sphere); 42 | } 43 | SS.spheremap.Sphere.prototype = Object.create(THREE.Object3D.prototype); 44 | 45 | SS.spheremap.createMap = function(index, scalarField, resolution) { 46 | var map = THREE.ImageUtils.generateDataTexture(resolution, resolution, new THREE.Color(0x000000)); 47 | addScalarField(map, index, scalarField); 48 | map.needsUpdate = true; 49 | return map; 50 | } 51 | 52 | var addScalarField = function(map, index, scalarField) { 53 | var width = map.image.width; 54 | var height = map.image.height; 55 | var nofPixels = width*height; 56 | 57 | for (var i = 0; i < nofPixels; i++) { 58 | var x = i%width; 59 | var y = Math.floor(i/width); 60 | var sphericalCoord = getSphericalCoord(index, x, y, width); 61 | 62 | var color = scalarField(sphericalCoord.x, sphericalCoord.y, sphericalCoord.z); 63 | 64 | map.image.data[i*3] = color.r*255; 65 | map.image.data[i*3+1] = color.g*255; 66 | map.image.data[i*3+2] = color.b*255; 67 | } 68 | } 69 | 70 | var getSphericalCoord = function(index, x, y, width) { 71 | width /= 2; 72 | x -= width; 73 | y -= width; 74 | var coord = new THREE.Vector3(); 75 | 76 | if (index == 0) {coord.x=width; coord.y=-y, coord.z=-x} 77 | else if (index == 1) {coord.x=-width; coord.y=-y, coord.z=x} 78 | else if (index == 2) {coord.x=x; coord.y=width, coord.z=y} 79 | else if (index == 3) {coord.x=x; coord.y=-width, coord.z=-y} 80 | else if (index == 4) {coord.x=x; coord.y=-y, coord.z=width} 81 | else if (index == 5) {coord.x=-x; coord.y=-y, coord.z=-width} 82 | 83 | coord.normalize(); 84 | return coord; 85 | } -------------------------------------------------------------------------------- /js/starbox.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.starbox = SS.starbox || {}; 3 | 4 | var maxDetail = SS.lowgraphics ? 16 : 512; //256 = 11 seconds (before), 512 = 5 seconds (now) 5 | 6 | SS.starbox.StarBox = function(radius) { 7 | THREE.Object3D.call(this); 8 | 9 | var sphere = new SS.spheremap.Sphere( 10 | SS.starbox.starboxcalarField, 11 | radius, 12 | function() {return new THREE.MeshBasicMaterial({side: THREE.BackSide, depthWrite: false});}, 13 | maxDetail, 14 | false 15 | ); 16 | 17 | this.add(sphere); 18 | } 19 | SS.starbox.StarBox.prototype = Object.create(THREE.Object3D.prototype); 20 | 21 | SS.starbox.starboxcalarField = function(x, y, z) { 22 | var starResolution = maxDetail; 23 | var starDensity = 1/1000; 24 | 25 | var coordFloat = new THREE.Vector3(); 26 | 27 | var starScalarField = function(x, y, z) { 28 | var rand = SS.util.random4(Math.abs(x), Math.abs(y), Math.abs(z)); 29 | return rand < starDensity ? rand/starDensity*(1-0.25) : 0; 30 | } 31 | 32 | var helper = function(x, y, z, scalarField, resolution, interpolationMethod) { 33 | // Because the sphere sample function gives normalized coordinates: 34 | x = (x+1)/2*resolution; 35 | y = (y+1)/2*resolution; 36 | z = (z+1)/2*resolution; 37 | 38 | coordFloat.set(x, y, z); 39 | 40 | return interpolationMethod(coordFloat, scalarField); 41 | } 42 | 43 | var c = helper(x, y, z, starScalarField, starResolution, SS.util.nearestNeighbour); 44 | 45 | c = SS.util.clamp(c, 0, 1); 46 | 47 | return new THREE.Color().setRGB(c, c, c); 48 | } -------------------------------------------------------------------------------- /js/util.js: -------------------------------------------------------------------------------- 1 | window.SS = window.SS || {}; 2 | SS.util = SS.util || {}; 3 | 4 | SS.util.clamp = function(number, from, to) { 5 | return Math.max(Math.min(number, to), from); 6 | } 7 | 8 | SS.util.randomInt = function(from, to, seed) { 9 | return Math.floor(SS.util.randomFloat(from, to+1, seed)); 10 | } 11 | 12 | SS.util.randomFloat = function(from, to, seed) { 13 | return SS.util.random(seed)*(to-from)+from; 14 | } 15 | 16 | SS.util.random = function(seed) { 17 | var scope = arguments.callee; 18 | 19 | scope.MAX = scope.MAX || Math.pow(2, 32); 20 | scope.a = 1664525; 21 | scope.c = 1013904223; 22 | 23 | scope.seeds = scope.seeds || {}; 24 | 25 | seed = seed || 0; 26 | var key = seed; 27 | if (typeof seed == "string") { 28 | if (scope.seeds[seed] == undefined) { 29 | var numeric = SS.util.numberFromString(seed); 30 | scope.seeds[seed] = numeric; // Memoization 31 | seed = numeric; 32 | } else { 33 | seed = scope.seeds[seed]; 34 | } 35 | } 36 | scope.series = scope.series || {}; 37 | scope.series[key] = scope.series[key] || seed; 38 | 39 | var lastRandom = scope.series[key]; 40 | var newRandom = (scope.a * lastRandom + scope.c) % scope.MAX; 41 | 42 | scope.series[key] = newRandom; 43 | 44 | return newRandom/scope.MAX; 45 | } 46 | 47 | SS.util.resetRandomSeries = function(prefix) { 48 | var toBeCleared = []; 49 | for (var i in SS.util.random.series) { 50 | if (i.indexOf(prefix) == 0) toBeCleared.push(i); 51 | } 52 | for (var i in toBeCleared) { 53 | delete SS.util.random.series[toBeCleared[i]]; 54 | } 55 | } 56 | 57 | SS.util.makeSpecifiedArray1D = function(size, value, array) { 58 | var valueFloat = value; 59 | for (var x = 0; x < size; x++) { 60 | if (typeof(value) == "function") valueFloat = value(x); 61 | array[x] = valueFloat; 62 | } 63 | return array; 64 | } 65 | 66 | window.N = 256*256; 67 | window.G = SS.util.makeSpecifiedArray1D(N, Math.random, new Float32Array(N)); 68 | window.P = SS.util.makeSpecifiedArray1D(N, function() {return SS.util.randomInt(0, N-1)}, new Uint32Array(N)); 69 | 70 | SS.util.random4 = function(i, j, k) { 71 | return G[(i + P[(j + P[k % N]) % N]) % N]; 72 | } 73 | 74 | SS.util.EditorCamera = function(camera, document, startRadius, cameraStartPos, originObject) { 75 | this.camera = camera; 76 | this.mouseDown = false; 77 | this.startRadius = startRadius || 20; 78 | this.startExp = 6; 79 | this.radius = this.startExp; 80 | this.originObject = originObject || {position: new THREE.Vector3()}; 81 | this.cameraStartPos = cameraStartPos || new THREE.Vector2(Math.PI/8, -Math.PI/4); 82 | this.cameraPos = this.cameraStartPos.clone(); 83 | this.mouseClickPos = new THREE.Vector2(); 84 | 85 | var editorCamera = this; 86 | 87 | var addEventListeners = function() { 88 | document.addEventListener('mousemove', function(event) { 89 | var mousePos = new THREE.Vector2(event.clientX, event.clientY); 90 | if (editorCamera.mouseDown == true) { 91 | var diff = mousePos.clone().sub(editorCamera.mouseClickPos).multiplyScalar(1/250); 92 | editorCamera.cameraPos = editorCamera.cameraStartPos.clone().add(diff); 93 | editorCamera.rotateCamera(); 94 | } 95 | }); 96 | 97 | document.addEventListener('mousewheel', function(event) { 98 | var delta = event.wheelDelta/10000; 99 | if (editorCamera.getScaledRadius(editorCamera.radius - delta) >= 0) { 100 | editorCamera.radius -= delta; 101 | editorCamera.zoomCamera(); 102 | } 103 | }); 104 | 105 | document.addEventListener('mousedown', function(event) { 106 | var mousePos = new THREE.Vector2(event.clientX, event.clientY); 107 | editorCamera.mouseClickPos = mousePos; 108 | editorCamera.mouseDown = true; 109 | }); 110 | 111 | document.addEventListener('mouseup', function(event) { 112 | editorCamera.cameraStartPos = editorCamera.cameraPos; 113 | editorCamera.mouseDown = false; 114 | }); 115 | } 116 | 117 | 118 | this.getScaledRadius = function(radius) { 119 | return Math.exp(radius) - Math.exp(this.startExp) + this.startRadius 120 | } 121 | 122 | this.zoomCamera = function() { 123 | this.camera.position.normalize().multiplyScalar(this.getScaledRadius(this.radius)); 124 | } 125 | 126 | this.rotateCamera = function() { 127 | this.camera.position.y = -this.cameraPos.y; 128 | this.camera.position.x = Math.sin(this.cameraPos.x); 129 | this.camera.position.z = Math.cos(this.cameraPos.x); 130 | 131 | this.zoomCamera(); 132 | 133 | this.camera.lookAt(new THREE.Vector3(0, 0, 0)); 134 | 135 | this.camera.position.add(this.originObject.position); 136 | } 137 | 138 | addEventListeners(); 139 | this.rotateCamera(); 140 | } 141 | 142 | SS.util.addResizeListener = function() { 143 | window.addEventListener('resize', function() { 144 | renderer.setSize(window.innerWidth, window.innerHeight); 145 | camera.aspect = window.innerWidth / window.innerHeight; 146 | camera.updateProjectionMatrix(); 147 | }); 148 | } 149 | 150 | SS.util.computeGeometry = function(geometry) { 151 | geometry.makeGroups(); 152 | geometry.computeVertexNormals() 153 | geometry.computeFaceNormals(); 154 | geometry.computeMorphNormals(); 155 | geometry.computeBoundingSphere(); 156 | geometry.computeBoundingBox(); 157 | geometry.computeLineDistances(); 158 | 159 | geometry.verticesNeedUpdate = true; 160 | geometry.elementsNeedUpdate = true; 161 | geometry.uvsNeedUpdate = true; 162 | geometry.normalsNeedUpdate = true; 163 | geometry.tangentsNeedUpdate = true; 164 | geometry.colorsNeedUpdate = true; 165 | geometry.lineDistancesNeedUpdate = true; 166 | geometry.buffersNeedUpdate = true; 167 | geometry.groupsNeedUpdate = true; 168 | } 169 | 170 | SS.util.trilinearInterpolation = function(coordFloat, scalarField, interpolation) { 171 | interpolation = interpolation || function(a, b, x) { 172 | return a*(1-x) + b*x; 173 | } 174 | 175 | var coord0 = {x: Math.floor(coordFloat.x), y: Math.floor(coordFloat.y), z: Math.floor(coordFloat.z)}; 176 | var coord1 = {x: coord0.x+1, y: coord0.y+1, z: coord0.z+1}; 177 | var xd = (coordFloat.x - coord0.x)/Math.max(1, (coord1.x-coord0.x)); 178 | var yd = (coordFloat.y - coord0.y)/Math.max(1, (coord1.y-coord0.y)); 179 | var zd = (coordFloat.z - coord0.z)/Math.max(1, (coord1.z-coord0.z)); 180 | var c00 = interpolation(scalarField(coord0.x, coord0.y, coord0.z), scalarField(coord1.x, coord0.y, coord0.z), xd); 181 | var c10 = interpolation(scalarField(coord0.x, coord1.y, coord0.z), scalarField(coord1.x, coord1.y, coord0.z), xd); 182 | var c01 = interpolation(scalarField(coord0.x, coord0.y, coord1.z), scalarField(coord1.x, coord0.y, coord1.z), xd); 183 | var c11 = interpolation(scalarField(coord0.x, coord1.y, coord1.z), scalarField(coord1.x, coord1.y, coord1.z), xd); 184 | var c0 = interpolation(c00, c10, yd); 185 | var c1 = interpolation(c01, c11, yd); 186 | var c = interpolation(c0, c1, zd); 187 | 188 | return c; 189 | } 190 | 191 | SS.util.nearestNeighbour = function(coordFloat, scalarField) { 192 | return scalarField(Math.floor(coordFloat.x), Math.floor(coordFloat.y), Math.floor(coordFloat.z)); 193 | } 194 | 195 | SS.util.tricosineInterpolation = function(coordFloat, scalarField) { 196 | var interpolation = function(a, b, x) { 197 | var ft = x * 3.1415927; 198 | var f = (1 - Math.cos(ft)) * 0.5; 199 | return a*(1-f) + b*f 200 | } 201 | 202 | return SS.util.trilinearInterpolation(coordFloat, scalarField, interpolation); 203 | } 204 | 205 | SS.util.heightToNormalMap = function(map, intensity) { 206 | var width = map.image.width; 207 | var height = map.image.height; 208 | var nofPixels = width*height; 209 | 210 | intensity = intensity || 1.0; 211 | 212 | var getHeight = function(x, y) { 213 | x = Math.min(x, width-1); 214 | y = Math.min(y, height-1); 215 | return ( 216 | map.image.data[(y*width+x)*3+0]/255 + 217 | map.image.data[(y*width+x)*3+1]/255 + 218 | map.image.data[(y*width+x)*3+2]/255 219 | )/3*intensity; 220 | } 221 | 222 | var normalMap = THREE.ImageUtils.generateDataTexture(width, height, new THREE.Color(0x000000)); 223 | 224 | for (var i = 0; i < nofPixels; i++) { 225 | var x = i%width; 226 | var y = Math.floor(i/width); 227 | 228 | var pixel00 = new THREE.Vector3(0, 0, getHeight(x, y)); 229 | var pixel01 = new THREE.Vector3(0, 1, getHeight(x, y+1)); 230 | var pixel10 = new THREE.Vector3(1, 0, getHeight(x+1, y)); 231 | var orto = pixel10.clone().sub(pixel00).cross(pixel01.clone().sub(pixel00)).normalize(); 232 | 233 | var color = new THREE.Color(orto.x+0.5, orto.y+0.5, -orto.z); 234 | 235 | normalMap.image.data[i*3+0] = color.r*255; 236 | normalMap.image.data[i*3+1] = color.g*255; 237 | normalMap.image.data[i*3+2] = color.b*255; 238 | } 239 | 240 | return normalMap; 241 | } --------------------------------------------------------------------------------