diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..032b1f8 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,28 @@ +module.exports = { + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + }, + env: { + browser: true, + es6: true, + node: true + }, + globals: { + Atomics: "readonly", + SharedArrayBuffer: "readonly" + }, + rules: { + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-object-literal-type-assertion': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; \ No newline at end of file diff --git a/.gitignore b/.gitignore index d5f19d8..c8d92d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules package-lock.json +dist/ diff --git a/.jscs.json b/.jscs.json deleted file mode 100644 index 8978b38..0000000 --- a/.jscs.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "disallowEmptyBlocks": { - "allExcept": ["comments"] - }, - "disallowImplicitTypeConversion": [], - "disallowKeywords": ["with"], - "disallowKeywordsOnNewLine": [], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineBreaks": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowOperatorBeforeLineBreak": ["."], - "disallowSpaceAfterObjectKeys": true, - "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "~", "!"], - "disallowSpaceBeforeBinaryOperators": [","], - "disallowSpaceBeforeComma": true, - "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], - "disallowSpaceBeforeSemicolon": true, - "disallowSpacesInCallExpression": true, - "disallowSpacesInFunctionDeclaration": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInNamedFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInsideBrackets": { - "allExcept": ["[", "]", "{", "}"] - }, - "disallowTrailingWhitespace": true, - "disallowYodaConditions": true, - "requireBlocksOnNewline": 1, - "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", - "requireCapitalizedConstructors": true, - "requireCommaBeforeLineBreak": true, - "requireCurlyBraces": ["for", "while", "do", "try", "catch"], - "requireKeywordsOnNewLine": ["else"], - "requireLineBreakAfterVariableAssignment": true, - "requireLineFeedAtFileEnd": true, - "requireParenthesesAroundIIFE": true, - "requireSemicolons": true, - "requireSpaceAfterBinaryOperators": ["=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], - "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "case", "return", "try", "void", "with", "typeof"], - "requireSpaceAfterLineComment": { - "allExcept": ["#", "="] - }, - "requireSpaceBeforeBinaryOperators": ["=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], - "requireSpaceBeforeBlockStatements": true, - "requireSpaceBeforeObjectValues": true, - "requireSpaceBetweenArguments": true, - "requireSpacesInConditionalExpression": { - "afterTest": true, - "beforeConsequent": true, - "afterConsequent": true, - "beforeAlternate": true - }, - "requireSpacesInForStatement": true, - "requireSpacesInFunction": { - "beforeOpeningCurlyBrace": true - }, - "requireSpacesInFunctionExpression": { - "beforeOpeningCurlyBrace": true - }, - "validateQuoteMarks": { - "escape": true, - "mark": "'" - } -} diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 5815510..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,121 +0,0 @@ -module.exports = function(grunt) { - var banner = '/**\n' + - ' * THREE.Terrain.js <%= pkg.version %>-<%= grunt.template.today("yyyymmdd") %>\n' + - ' *\n' + - ' * @author <%= pkg.author %>\n' + - ' * @license <%= pkg.license %>\n' + - ' */\n'; - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - concat: { - options: { - banner: banner + '\n', - separator: grunt.util.linefeed, - }, - target: { - src: [ - 'src/noise.js', - 'src/core.js', - 'src/images.js', - 'src/filters.js', - 'src/generators.js', - 'src/materials.js', - 'src/scatter.js', - 'src/influences.js', - ], - dest: 'build/THREE.Terrain.js', - nonull: true, - }, - }, - uglify: { - options: { - banner: banner, - compress: { - dead_code: false, - side_effects: false, - unused: false, - }, - mangle: true, - preserveComments: function(node, comment) { - return (/^!/).test(comment.value); - }, - report: 'min', - sourceMap: true, - }, - target: { - files: { - 'build/THREE.Terrain.min.js': ['build/THREE.Terrain.js'], - }, - }, - }, - jshint: { - options: { - trailing: true, - }, - target: { - src: [ - 'demo/index.js', - 'src/noise.js', - 'src/core.js', - 'src/images.js', - 'src/filters.js', - 'src/gaussian.js', - 'src/weightedBoxBlurGaussian.js', - 'src/generators.js', - 'src/materials.js', - 'src/scatter.js', - 'src/influences.js', - 'src/worley.js', - 'src/brownian.js', - 'src/analysis.js', - 'Gruntfile.js', - ], - }, - }, - jscs: { - options: { - config: '.jscs.json', - }, - main: [ - 'demo/index.js', - 'src/noise.js', - 'src/core.js', - 'src/images.js', - 'src/filters.js', - 'src/gaussian.js', - 'src/weightedBoxBlurGaussian.js', - 'src/generators.js', - 'src/materials.js', - 'src/scatter.js', - 'src/influences.js', - 'src/worley.js', - 'src/brownian.js', - 'src/analysis.js', - 'Gruntfile.js', - ], - }, - watch: { - files: [ - 'src/noise.js', - 'src/core.js', - 'src/images.js', - 'src/filters.js', - 'src/gaussian.js', - 'src/weightedBoxBlurGaussian.js', - 'src/generators.js', - 'src/materials.js', - 'src/scatter.js', - 'src/influences.js', - ], - tasks: ['concat', 'uglify'], - }, - }); - - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-jscs'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.registerTask('default', ['concat', 'uglify', 'jshint', 'jscs']); - grunt.registerTask('lint', ['jshint', 'jscs']); -}; diff --git a/README.md b/README.md index 08e86df..0f2d02b 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,16 @@ ## Usage -You can download the script normally, install it with Bower (`bower install -THREE.Terrain`), or install it with npm (`npm install three.terrain.js`). To -include it on a page client-side without a module loader: +You can download the script normally, or install it with npm or yarn +(`npm install three.terrain.js`). To include it on a page client-side +without a module loader: ```html - + - - - - - + + ``` You then have access to the `THREE.Terrain` object. (Make sure the `three.js` @@ -65,7 +62,7 @@ terrainScene.add(decoScene); ``` All parameters are optional and thoroughly documented in the -[source code](https://github.com/IceCreamYou/THREE.Terrain/blob/gh-pages/build/THREE.Terrain.js). +[source code](https://github.com/IceCreamYou/THREE.Terrain/blob/gh-pages/src/). You can play around with some of the parameters and see what happens in the [demo](https://icecreamyou.github.io/THREE.Terrain/). @@ -146,7 +143,7 @@ Many other utilities are provided, for example for compositing different terrain generation methods; creating islands, cliffs, canyons, and plateaus; manually influencing the terrain's shape at different locations; different kinds of smoothing; and more. These features are all fully documented in the -[source code](https://github.com/IceCreamYou/THREE.Terrain/blob/gh-pages/build/THREE.Terrain.js). +[source code](https://github.com/IceCreamYou/THREE.Terrain/blob/gh-pages/src/). Additionally, you can create custom methods for generating terrain or affecting other processes. diff --git a/bower.json b/bower.json deleted file mode 100644 index c7bba7b..0000000 --- a/bower.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "THREE.Terrain", - "version": "1.6.0", - "main": "build/THREE.Terrain.min.js", - "ignore": [ - "node_modules" - ] -} diff --git a/build/THREE.Terrain.js b/build/THREE.Terrain.js deleted file mode 100644 index 71c46e1..0000000 --- a/build/THREE.Terrain.js +++ /dev/null @@ -1,2204 +0,0 @@ -/** - * THREE.Terrain.js 1.6.0-20180415 - * - * @author Isaac Sukin (http://www.isaacsukin.com/) - * @license MIT - */ - -/** -* Simplex and Perlin noise. -* -* Copied with small edits from https://github.com/josephg/noisejs which is -* public domain. Originally by Stefan Gustavson (stegu@itn.liu.se) with -* optimizations by Peter Eastman (peastman@drizzle.stanford.edu) and converted -* to JavaScript by Joseph Gentle. -*/ - -(function(global) { - var module = global.noise = {}; - - function Grad(x, y, z) { - this.x = x; - this.y = y; - this.z = z; - } - - Grad.prototype.dot2 = function(x, y) { - return this.x*x + this.y*y; - }; - - Grad.prototype.dot3 = function(x, y, z) { - return this.x*x + this.y*y + this.z*z; - }; - - var grad3 = [ - new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), - new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), - new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1), - ]; - - var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103, - 30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94, - 252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171, - 168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122, - 60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161, - 1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159, - 86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147, - 118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183, - 170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129, - 22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228, - 251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239, - 107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4, - 150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215, - 61,156,180]; - // To avoid the need for index wrapping, double the permutation table length - var perm = new Array(512), - gradP = new Array(512); - - // This isn't a very good seeding function, but it works okay. It supports - // 2^16 different seed values. Write your own if you need more seeds. - module.seed = function(seed) { - if (seed > 0 && seed < 1) { - // Scale the seed out - seed *= 65536; - } - - seed = Math.floor(seed); - if (seed < 256) { - seed |= seed << 8; - } - - for (var i = 0; i < 256; i++) { - var v; - if (i & 1) { - v = p[i] ^ (seed & 255); - } - else { - v = p[i] ^ ((seed>>8) & 255); - } - - perm[i] = perm[i + 256] = v; - gradP[i] = gradP[i + 256] = grad3[v % 12]; - } - }; - - module.seed(Math.random()); - - // Skewing and unskewing factors for 2 and 3 dimensions - var F2 = 0.5*(Math.sqrt(3)-1), - G2 = (3-Math.sqrt(3))/6, - F3 = 1/3, - G3 = 1/6; - - // 2D simplex noise - module.simplex = function(xin, yin) { - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var s = (xin+yin)*F2; // Hairy factor for 2D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var t = (i+j)*G2; - var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed - var y0 = yin-j+t; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if (x0 > y0) { // Lower triangle, XY order: (0,0)->(1,0)->(1,1) - i1 = 1; j1 = 0; - } - else { // Upper triangle, YX order: (0,0)->(0,1)->(1,1) - i1 = 0; j1 = 1; - } - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1 + 2 * G2; - // Work out the hashed gradient indices of the three simplex corners - i &= 255; - j &= 255; - var gi0 = gradP[i+perm[j]]; - var gi1 = gradP[i+i1+perm[j+j1]]; - var gi2 = gradP[i+1+perm[j+1]]; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0*x0-y0*y0; - if (t0 < 0) { - n0 = 0; - } - else { - t0 *= t0; - n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient - } - var t1 = 0.5 - x1*x1-y1*y1; - if (t1 < 0) { - n1 = 0; - } - else { - t1 *= t1; - n1 = t1 * t1 * gi1.dot2(x1, y1); - } - var t2 = 0.5 - x2*x2-y2*y2; - if (t2 < 0) { - n2 = 0; - } - else { - t2 *= t2; - n2 = t2 * t2 * gi2.dot2(x2, y2); - } - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70 * (n0 + n1 + n2); - }; - - // ##### Perlin noise stuff - - function fade(t) { - return t*t*t*(t*(t*6-15)+10); - } - - function lerp(a, b, t) { - return (1-t)*a + t*b; - } - - // 2D Perlin Noise - module.perlin = function(x, y) { - // Find unit grid cell containing point - var X = Math.floor(x), - Y = Math.floor(y); - // Get relative xy coordinates of point within that cell - x = x - X; - y = y - Y; - // Wrap the integer cells at 255 (smaller integer period can be introduced here) - X = X & 255; - Y = Y & 255; - - // Calculate noise contributions from each of the four corners - var n00 = gradP[X+perm[Y]].dot2(x, y); - var n01 = gradP[X+perm[Y+1]].dot2(x, y-1); - var n10 = gradP[X+1+perm[Y]].dot2(x-1, y); - var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1); - - // Compute the fade curve value for x - var u = fade(x); - - // Interpolate the four results - return lerp( - lerp(n00, n10, u), - lerp(n01, n11, u), - fade(y) - ); - }; -})(this); - -/** - * A terrain object for use with the Three.js library. - * - * Usage: `var terrainScene = THREE.Terrain();` - * - * @param {Object} [options] - * An optional map of settings that control how the terrain is constructed - * and displayed. Options include: - * - * - `after`: A function to run after other transformations on the terrain - * produce the highest-detail heightmap, but before optimizations and - * visual properties are applied. Takes two parameters, which are the same - * as those for {@link THREE.Terrain.DiamondSquare}: an array of - * `THREE.Vector3` objects representing the vertices of the terrain, and a - * map of options with the same available properties as the `options` - * parameter for the `THREE.Terrain` function. - * - `easing`: A function that affects the distribution of slopes by - * interpolating the height of each vertex along a curve. Valid values - * include `THREE.Terrain.Linear` (the default), `THREE.Terrain.EaseIn`, - * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, - * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float - * between 0 and 1 and returns a float between 0 and 1. - * - `frequency`: For terrain generation methods that support it (Perlin, - * Simplex, and Worley) the octave of randomness. This basically controls - * how big features of the terrain will be (higher frequencies result in - * smaller features). Often running multiple generation functions with - * different frequencies and heights results in nice detail, as - * the PerlinLayers and SimplexLayers methods demonstrate. (The counterpart - * to frequency, amplitude, is represented by the difference between the - * `maxHeight` and `minHeight` parameters.) Defaults to 2.5. - * - `heightmap`: Either a canvas or pre-loaded image (from the same domain - * as the webpage or served with a CORS-friendly header) representing - * terrain height data (lighter pixels are higher); or a function used to - * generate random height data for the terrain. Valid random functions are - * specified in `generators.js` (or custom functions with the same - * signature). Ideally heightmap images have the same number of pixels as - * the terrain has vertices, as determined by the `xSegments` and - * `ySegments` options, but this is not required. If the heightmap is a - * different size, vertex height values will be interpolated.) Defaults to - * `THREE.Terrain.DiamondSquare`. - * - `material`: a THREE.Material instance used to display the terrain. - * Defaults to `new THREE.MeshBasicMaterial({color: 0xee6633})`. - * - `maxHeight`: the highest point, in Three.js units, that a peak should - * reach. Defaults to 100. Setting to `undefined`, `null`, or `Infinity` - * removes the cap, but this is generally not recommended because many - * generators and filters require a vertical range. Instead, consider - * setting the `stretch` option to `false`. - * - `minHeight`: the lowest point, in Three.js units, that a valley should - * reach. Defaults to -100. Setting to `undefined`, `null`, or `-Infinity` - * removes the cap, but this is generally not recommended because many - * generators and filters require a vertical range. Instead, consider - * setting the `stretch` option to `false`. - * - `steps`: If this is a number above 1, the terrain will be paritioned - * into that many flat "steps," resulting in a blocky appearance. Defaults - * to 1. - * - `stretch`: Determines whether to stretch the heightmap across the - * maximum and minimum height range if the height range produced by the - * `heightmap` property is smaller. Defaults to true. - * - `turbulent`: Whether to perform a turbulence transformation. Defaults to - * false. - * - `useBufferGeometry`: a Boolean indicating whether to use - * THREE.BufferGeometry instead of THREE.Geometry for the Terrain plane. - * Defaults to `false`. - * - `xSegments`: The number of segments (rows) to divide the terrain plane - * into. (This basically determines how detailed the terrain is.) Defaults - * to 63. - * - `xSize`: The width of the terrain in Three.js units. Defaults to 1024. - * Rendering might be slightly faster if this is a multiple of - * `options.xSegments + 1`. - * - `ySegments`: The number of segments (columns) to divide the terrain - * plane into. (This basically determines how detailed the terrain is.) - * Defaults to 63. - * - `ySize`: The length of the terrain in Three.js units. Defaults to 1024. - * Rendering might be slightly faster if this is a multiple of - * `options.ySegments + 1`. - */ -THREE.Terrain = function(options) { - var defaultOptions = { - after: null, - easing: THREE.Terrain.Linear, - heightmap: THREE.Terrain.DiamondSquare, - material: null, - maxHeight: 100, - minHeight: -100, - optimization: THREE.Terrain.NONE, - frequency: 2.5, - steps: 1, - stretch: true, - turbulent: false, - useBufferGeometry: false, - xSegments: 63, - xSize: 1024, - ySegments: 63, - ySize: 1024, - _mesh: null, // internal only - }; - options = options || {}; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - options[opt] = typeof options[opt] === 'undefined' ? defaultOptions[opt] : options[opt]; - } - } - options.material = options.material || new THREE.MeshBasicMaterial({ color: 0xee6633 }); - - // Encapsulating the terrain in a parent object allows us the flexibility - // to more easily have multiple meshes for optimization purposes. - var scene = new THREE.Object3D(); - // Planes are initialized on the XY plane, so rotate the plane to make it lie flat. - scene.rotation.x = -0.5 * Math.PI; - - // Create the terrain mesh. - // To save memory, it is possible to re-use a pre-existing mesh. - var mesh = options._mesh; - if (mesh && mesh.geometry.type === 'PlaneGeometry' && - mesh.geometry.parameters.widthSegments === options.xSegments && - mesh.geometry.parameters.heightSegments === options.ySegments) { - mesh.material = options.material; - mesh.scale.x = options.xSize / mesh.geometry.parameters.width; - mesh.scale.y = options.ySize / mesh.geometry.parameters.height; - for (var i = 0, l = mesh.geometry.vertices.length; i < l; i++) { - mesh.geometry.vertices[i].z = 0; - } - } - else { - mesh = new THREE.Mesh( - new THREE.PlaneGeometry(options.xSize, options.ySize, options.xSegments, options.ySegments), - options.material - ); - } - delete options._mesh; // Remove the reference for GC - - // Assign elevation data to the terrain plane from a heightmap or function. - if (options.heightmap instanceof HTMLCanvasElement || options.heightmap instanceof Image) { - THREE.Terrain.fromHeightmap(mesh.geometry.vertices, options); - } - else if (typeof options.heightmap === 'function') { - options.heightmap(mesh.geometry.vertices, options); - } - else { - console.warn('An invalid value was passed for `options.heightmap`: ' + options.heightmap); - } - THREE.Terrain.Normalize(mesh, options); - - if (options.useBufferGeometry) { - mesh.geometry = (new THREE.BufferGeometry()).fromGeometry(mesh.geometry); - } - - // lod.addLevel(mesh, options.unit * 10 * Math.pow(2, lodLevel)); - - scene.add(mesh); - return scene; -}; - -/** - * Normalize the terrain after applying a heightmap or filter. - * - * This applies turbulence, steps, and height clamping; calls the `after` - * callback; updates normals and the bounding sphere; and marks vertices as - * dirty. - * - * @param {THREE.Mesh} mesh - * The terrain mesh. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid options are the same as for {@link THREE.Terrain}(). - */ -THREE.Terrain.Normalize = function(mesh, options) { - var v = mesh.geometry.vertices; - if (options.turbulent) { - THREE.Terrain.Turbulence(v, options); - } - if (options.steps > 1) { - THREE.Terrain.Step(v, options.steps); - THREE.Terrain.Smooth(v, options); - } - // Keep the terrain within the allotted height range if necessary, and do easing. - THREE.Terrain.Clamp(v, options); - // Call the "after" callback - if (typeof options.after === 'function') { - options.after(v, options); - } - // Mark the geometry as having changed and needing updates. - mesh.geometry.verticesNeedUpdate = true; - mesh.geometry.normalsNeedUpdate = true; - mesh.geometry.computeBoundingSphere(); - mesh.geometry.computeFaceNormals(); - mesh.geometry.computeVertexNormals(); -}; - -/** - * Optimization types. - * - * Note that none of these are implemented right now. They should be done as - * shaders so that they execute on the GPU, and the resulting scene would need - * to be updated every frame to adjust to the camera's position. - * - * Further reading: - * - http://vterrain.org/LOD/Papers/ - * - http://vterrain.org/LOD/Implementations/ - * - * GEOMIPMAP: The terrain plane should be split into sections, each with their - * own LODs, for screen-space occlusion and detail reduction. Intermediate - * vertices on higher-detail neighboring sections should be interpolated - * between neighbor edge vertices in order to match with the edge of the - * lower-detail section. The number of sections should be around sqrt(segments) - * along each axis. It's unclear how to make materials stretch across segments. - * Possible example (I haven't looked too much into it) at - * https://github.com/felixpalmer/lod-terrain/tree/master/js/shaders - * - * GEOCLIPMAP: The terrain should be composed of multiple donut-shaped sections - * at decreasing resolution as the radius gets bigger. When the player moves, - * the sections should morph so that the detail "follows" the player around. - * There is an implementation of geoclipmapping at - * https://github.com/CodeArtemis/TriggerRally/blob/unified/server/public/scripts/client/terrain.coffee - * and a tutorial on morph targets at - * http://nikdudnik.com/making-3d-gfx-for-the-cinema-on-low-budget-and-three-js/ - * - * POLYGONREDUCTION: Combine areas that are relatively coplanar into larger - * polygons as described at http://www.shamusyoung.com/twentysidedtale/?p=142. - * This method can be combined with the others if done very carefully, or it - * can be adjusted to be more aggressive at greater distance from the camera - * (similar to combining with geomipmapping). - * - * If these do get implemented, here is the option description to add to the - * `THREE.Terrain` docblock: - * - * - `optimization`: the type of optimization to apply to the terrain. If - * an optimization is applied, the number of segments along each axis that - * the terrain should be divided into at the most detailed level should - * equal (n * 2^(LODs-1))^2 - 1, for arbitrary n, where LODs is the number - * of levels of detail desired. Valid values include: - * - * - `THREE.Terrain.NONE`: Don't apply any optimizations. This is the - * default. - * - `THREE.Terrain.GEOMIPMAP`: Divide the terrain into evenly-sized - * sections with multiple levels of detail. For each section, - * display a level of detail dependent on how close the camera is. - * - `THREE.Terrain.GEOCLIPMAP`: Divide the terrain into donut-shaped - * sections, where detail decreases as the radius increases. The - * rings then morph to "follow" the camera around so that the camera - * is always at the center, surrounded by the most detail. - */ -THREE.Terrain.NONE = 0; -THREE.Terrain.GEOMIPMAP = 1; -THREE.Terrain.GEOCLIPMAP = 2; -THREE.Terrain.POLYGONREDUCTION = 3; - -/** - * Get a 2D array of heightmap values from a 1D array of plane vertices. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Object} options - * A map of settings defining properties of the terrain. The only properties - * that matter here are `xSegments` and `ySegments`, which represent how many - * vertices wide and deep the terrain plane is, respectively (and therefore - * also the dimensions of the returned array). - * - * @return {Number[][]} - * A 2D array representing the terrain's heightmap. - */ -THREE.Terrain.toArray2D = function(vertices, options) { - var tgt = new Array(options.xSegments), - xl = options.xSegments + 1, - yl = options.ySegments + 1, - i, j; - for (i = 0; i < xl; i++) { - tgt[i] = new Float64Array(options.ySegments); - for (j = 0; j < yl; j++) { - tgt[i][j] = vertices[j * xl + i].z; - } - } - return tgt; -}; - -/** - * Set the height of plane vertices from a 2D array of heightmap values. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Number[][]} src - * A 2D array representing a heightmap to apply to the terrain. - */ -THREE.Terrain.fromArray2D = function(vertices, src) { - for (var i = 0, xl = src.length; i < xl; i++) { - for (var j = 0, yl = src[i].length; j < yl; j++) { - vertices[j * xl + i].z = src[i][j]; - } - } -}; - -/** - * Get a 1D array of heightmap values from a 1D array of plane vertices. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Object} options - * A map of settings defining properties of the terrain. The only properties - * that matter here are `xSegments` and `ySegments`, which represent how many - * vertices wide and deep the terrain plane is, respectively (and therefore - * also the dimensions of the returned array). - * - * @return {Number[]} - * A 1D array representing the terrain's heightmap. - */ -THREE.Terrain.toArray1D = function(vertices) { - var tgt = new Float64Array(vertices.length); - for (var i = 0, l = tgt.length; i < l; i++) { - tgt[i] = vertices[i].z; - } - return tgt; -}; - -/** - * Set the height of plane vertices from a 1D array of heightmap values. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Number[]} src - * A 1D array representing a heightmap to apply to the terrain. - */ -THREE.Terrain.fromArray1D = function(vertices, src) { - for (var i = 0, l = Math.min(vertices.length, src.length); i < l; i++) { - vertices[i].z = src[i]; - } -}; - -/** - * Generate a 1D array containing random heightmap data. - * - * This is like {@link THREE.Terrain.toHeightmap} except that instead of - * generating the Three.js mesh and material information you can just get the - * height data. - * - * @param {Function} method - * The method to use to generate the heightmap data. Works with function that - * would be an acceptable value for the `heightmap` option for the - * {@link THREE.Terrain} function. - * @param {Number} options - * The same as the options parameter for the {@link THREE.Terrain} function. - */ -THREE.Terrain.heightmapArray = function(method, options) { - var arr = new Array((options.xSegments+1) * (options.ySegments+1)), - l = arr.length, - i; - // The heightmap functions provided by this script operate on THREE.Vector3 - // objects by changing the z field, so we need to make that available. - // Unfortunately that means creating a bunch of objects we're just going to - // throw away, but a conscious decision was made here to optimize for the - // vector case. - for (i = 0; i < l; i++) { - arr[i] = {z: 0}; - } - options.minHeight = options.minHeight || 0; - options.maxHeight = typeof options.maxHeight === 'undefined' ? 1 : options.maxHeight; - options.stretch = options.stretch || false; - method(arr, options); - THREE.Terrain.Clamp(arr, options); - for (i = 0; i < l; i++) { - arr[i] = arr[i].z; - } - return arr; -}; - -/** - * Randomness interpolation functions. - */ -THREE.Terrain.Linear = function(x) { - return x; -}; - -// x = [0, 1], x^2 -THREE.Terrain.EaseIn = function(x) { - return x*x; -}; - -// x = [0, 1], -x(x-2) -THREE.Terrain.EaseOut = function(x) { - return -x * (x - 2); -}; - -// x = [0, 1], x^2(3-2x) -// Nearly identical alternatives: 0.5+0.5*cos(x*pi-pi), x^a/(x^a+(1-x)^a) (where a=1.6 seems nice) -// For comparison: http://www.wolframalpha.com/input/?i=x^1.6%2F%28x^1.6%2B%281-x%29^1.6%29%2C+x^2%283-2x%29%2C+0.5%2B0.5*cos%28x*pi-pi%29+from+0+to+1 -THREE.Terrain.EaseInOut = function(x) { - return x*x*(3-2*x); -}; - -// x = [0, 1], 0.5*(2x-1)^3+0.5 -THREE.Terrain.InEaseOut = function(x) { - var y = 2*x-1; - return 0.5 * y*y*y + 0.5; -}; - -// x = [0, 1], x^1.55 -THREE.Terrain.EaseInWeak = function(x) { - return Math.pow(x, 1.55); -}; - -// x = [0, 1], x^7 -THREE.Terrain.EaseInStrong = function(x) { - return x*x*x*x*x*x*x; -}; - -/** - * Convert an image-based heightmap into vertex-based height data. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - */ -THREE.Terrain.fromHeightmap = function(g, options) { - var canvas = document.createElement('canvas'), - context = canvas.getContext('2d'), - rows = options.ySegments + 1, - cols = options.xSegments + 1, - spread = options.maxHeight - options.minHeight; - canvas.width = cols; - canvas.height = rows; - context.drawImage(options.heightmap, 0, 0, canvas.width, canvas.height); - var data = context.getImageData(0, 0, canvas.width, canvas.height).data; - for (var row = 0; row < rows; row++) { - for (var col = 0; col < cols; col++) { - var i = row * cols + col, - idx = i * 4; - g[i].z = (data[idx] + data[idx+1] + data[idx+2]) / 765 * spread + options.minHeight; - } - } -}; - -/** - * Convert a terrain plane into an image-based heightmap. - * - * Parameters are the same as for {@link THREE.Terrain.fromHeightmap} except - * that if `options.heightmap` is a canvas element then the image will be - * painted onto that canvas; otherwise a new canvas will be created. - * - * NOTE: this method performs an operation on an array of vertices, which - * aren't available when using `BufferGeometry`. So, if you want to use this - * method, make sure to set the `useBufferGeometry` option to `false` when - * generating your terrain. - * - * @return {HTMLCanvasElement} - * A canvas with the relevant heightmap painted on it. - */ -THREE.Terrain.toHeightmap = function(g, options) { - var hasMax = typeof options.maxHeight === 'undefined', - hasMin = typeof options.minHeight === 'undefined', - max = hasMax ? options.maxHeight : -Infinity, - min = hasMin ? options.minHeight : Infinity; - if (!hasMax || !hasMin) { - var max2 = max, - min2 = min; - for (var k = 0, l = g.length; k < l; k++) { - if (g[k].z > max2) max2 = g[k].z; - if (g[k].z < min2) min2 = g[k].z; - } - if (!hasMax) max = max2; - if (!hasMin) min = min2; - } - var canvas = options.heightmap instanceof HTMLCanvasElement ? options.heightmap : document.createElement('canvas'), - context = canvas.getContext('2d'), - rows = options.ySegments + 1, - cols = options.xSegments + 1, - spread = options.maxHeight - options.minHeight; - canvas.width = cols; - canvas.height = rows; - var d = context.createImageData(canvas.width, canvas.height), - data = d.data; - for (var row = 0; row < rows; row++) { - for (var col = 0; col < cols; col++) { - var i = row * cols + col, - idx = i * 4; - data[idx] = data[idx+1] = data[idx+2] = Math.round(((g[i].z - options.minHeight) / spread) * 255); - data[idx+3] = 255; - } - } - context.putImageData(d, 0, 0); - return canvas; -}; - -/** - * Rescale the heightmap of a terrain to keep it within the maximum range. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}() but only `maxHeight`, `minHeight`, and `easing` - * are used. - */ -THREE.Terrain.Clamp = function(g, options) { - var min = Infinity, - max = -Infinity, - l = g.length, - i; - options.easing = options.easing || THREE.Terrain.Linear; - for (i = 0; i < l; i++) { - if (g[i].z < min) min = g[i].z; - if (g[i].z > max) max = g[i].z; - } - var actualRange = max - min, - optMax = typeof options.maxHeight !== 'number' ? max : options.maxHeight, - optMin = typeof options.minHeight !== 'number' ? min : options.minHeight, - targetMax = options.stretch ? optMax : (max < optMax ? max : optMax), - targetMin = options.stretch ? optMin : (min > optMin ? min : optMin), - range = targetMax - targetMin; - if (targetMax < targetMin) { - targetMax = optMax; - range = targetMax - targetMin; - } - for (i = 0; i < l; i++) { - g[i].z = options.easing((g[i].z - min) / actualRange) * range + optMin; - } -}; - -/** - * Move the edges of the terrain up or down based on distance from the edge. - * - * Useful to make islands or enclosing walls/cliffs. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Boolean} direction - * `true` if the edges should be turned up; `false` if they should be turned - * down. - * @param {Number} distance - * The distance from the edge at which the edges should begin to be affected - * by this operation. - * @param {Number/Function} [e=THREE.Terrain.EaseInOut] - * A function that determines how quickly the terrain will transition between - * its current height and the edge shape as distance to the edge decreases. - * It does this by interpolating the height of each vertex along a curve. - * Valid values include `THREE.Terrain.Linear`, `THREE.Terrain.EaseIn`, - * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, - * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float - * between 0 and 1 and returns a float between 0 and 1. - * @param {Object} [edges={top: true, bottom: true, left: true, right: true}] - * Determines which edges should be affected by this function. Defaults to - * all edges. If passed, should be an object with `top`, `bottom`, `left`, - * and `right` Boolean properties specifying which edges to affect. - */ -THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { - var numXSegments = Math.floor(distance / (options.xSize / options.xSegments)) || 1, - numYSegments = Math.floor(distance / (options.ySize / options.ySegments)) || 1, - peak = direction ? options.maxHeight : options.minHeight, - max = direction ? Math.max : Math.min, - xl = options.xSegments + 1, - yl = options.ySegments + 1, - i, j, multiplier, k1, k2; - easing = easing || THREE.Terrain.EaseInOut; - if (typeof edges !== 'object') { - edges = {top: true, bottom: true, left: true, right: true}; - } - for (i = 0; i < xl; i++) { - for (j = 0; j < numYSegments; j++) { - multiplier = easing(1 - j / numYSegments); - k1 = j*xl + i; - k2 = (options.ySegments-j)*xl + i; - if (edges.top) { - g[k1].z = max(g[k1].z, (peak - g[k1].z) * multiplier + g[k1].z); - } - if (edges.bottom) { - g[k2].z = max(g[k2].z, (peak - g[k2].z) * multiplier + g[k2].z); - } - } - } - for (i = 0; i < yl; i++) { - for (j = 0; j < numXSegments; j++) { - multiplier = easing(1 - j / numXSegments); - k1 = i*xl+j; - k2 = (options.ySegments-i)*xl + (options.xSegments-j); - if (edges.left) { - g[k1].z = max(g[k1].z, (peak - g[k1].z) * multiplier + g[k1].z); - } - if (edges.right) { - g[k2].z = max(g[k2].z, (peak - g[k2].z) * multiplier + g[k2].z); - } - } - } - THREE.Terrain.Clamp(g, { - maxHeight: options.maxHeight, - minHeight: options.minHeight, - stretch: true, - }); -}; - -/** - * Move the edges of the terrain up or down based on distance from the center. - * - * Useful to make islands or enclosing walls/cliffs. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Boolean} direction - * `true` if the edges should be turned up; `false` if they should be turned - * down. - * @param {Number} distance - * The distance from the center at which the edges should begin to be - * affected by this operation. - * @param {Number/Function} [e=THREE.Terrain.EaseInOut] - * A function that determines how quickly the terrain will transition between - * its current height and the edge shape as distance to the edge decreases. - * It does this by interpolating the height of each vertex along a curve. - * Valid values include `THREE.Terrain.Linear`, `THREE.Terrain.EaseIn`, - * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, - * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float - * between 0 and 1 and returns a float between 0 and 1. - */ -THREE.Terrain.RadialEdges = function(g, options, direction, distance, easing) { - var peak = direction ? options.maxHeight : options.minHeight, - max = direction ? Math.max : Math.min, - xl = (options.xSegments + 1), - yl = (options.ySegments + 1), - xl2 = xl * 0.5, - yl2 = yl * 0.5, - xSegmentSize = options.xSize / options.xSegments, - ySegmentSize = options.ySize / options.ySegments, - edgeRadius = Math.min(options.xSize, options.ySize) * 0.5 - distance, - i, j, multiplier, k, vertexDistance; - for (i = 0; i < xl; i++) { - for (j = 0; j < yl2; j++) { - k = j*xl + i; - vertexDistance = Math.min(edgeRadius, Math.sqrt((xl2-i)*xSegmentSize*(xl2-i)*xSegmentSize + (yl2-j)*ySegmentSize*(yl2-j)*ySegmentSize) - distance); - if (vertexDistance < 0) continue; - multiplier = easing(vertexDistance / edgeRadius); - g[k].z = max(g[k].z, (peak - g[k].z) * multiplier + g[k].z); - // Use symmetry to reduce the number of iterations. - k = (options.ySegments-j)*xl + i; - g[k].z = max(g[k].z, (peak - g[k].z) * multiplier + g[k].z); - } - } -}; - -/** - * Smooth the terrain by setting each point to the mean of its neighborhood. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Number} [weight=0] - * How much to weight the original vertex height against the average of its - * neighbors. - */ -THREE.Terrain.Smooth = function(g, options, weight) { - var heightmap = new Float64Array(g.length); - for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { - for (var j = 0; j < yl; j++) { - var sum = 0, - c = 0; - for (var n = -1; n <= 1; n++) { - for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { - sum += g[key].z; - c++; - } - } - } - heightmap[j*xl + i] = sum / c; - } - } - weight = weight || 0; - var w = 1 / (1 + weight); - for (var k = 0, l = g.length; k < l; k++) { - g[k].z = (heightmap[k] + g[k].z * weight) * w; - } -}; - -/** - * Smooth the terrain by setting each point to the median of its neighborhood. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.SmoothMedian = function(g, options) { - var heightmap = new Float64Array(g.length), - neighborValues = [], - neighborKeys = [], - sortByValue = function(a, b) { - return neighborValues[a] - neighborValues[b]; - }; - for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { - for (var j = 0; j < yl; j++) { - neighborValues.length = 0; - neighborKeys.length = 0; - for (var n = -1; n <= 1; n++) { - for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { - neighborValues.push(g[key].z); - neighborKeys.push(key); - } - } - } - neighborKeys.sort(sortByValue); - var halfKey = Math.floor(neighborKeys.length*0.5), - median; - if (neighborKeys.length % 2 === 1) { - median = g[neighborKeys[halfKey]].z; - } - else { - median = (g[neighborKeys[halfKey-1]].z + g[neighborKeys[halfKey]].z) * 0.5; - } - heightmap[j*xl + i] = median; - } - } - for (var k = 0, l = g.length; k < l; k++) { - g[k].z = heightmap[k]; - } -}; - -/** - * Smooth the terrain by clamping each point within its neighbors' extremes. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Number} [multiplier=1] - * By default, this filter clamps each point within the highest and lowest - * value of its neighbors. This parameter is a multiplier for the range - * outside of which the point will be clamped. Higher values mean that the - * point can be farther outside the range of its neighbors. - */ -THREE.Terrain.SmoothConservative = function(g, options, multiplier) { - var heightmap = new Float64Array(g.length); - for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { - for (var j = 0; j < yl; j++) { - var max = -Infinity, - min = Infinity; - for (var n = -1; n <= 1; n++) { - for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && n && m && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { - if (g[key].z < min) min = g[key].z; - if (g[key].z > max) max = g[key].z; - } - } - } - var kk = j*xl + i; - if (typeof multiplier === 'number') { - var halfdiff = (max - min) * 0.5, - middle = min + halfdiff; - max = middle + halfdiff * multiplier; - min = middle - halfdiff * multiplier; - } - heightmap[kk] = g[kk].z > max ? max : (g[kk].z < min ? min : g[kk].z); - } - } - for (var k = 0, l = g.length; k < l; k++) { - g[k].z = heightmap[k]; - } -}; - -/** - * Partition a terrain into flat steps. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Number} [levels] - * The number of steps to divide the terrain into. Defaults to - * (g.length/2)^(1/4). - */ -THREE.Terrain.Step = function(g, levels) { - // Calculate the max, min, and avg values for each bucket - var i = 0, - j = 0, - l = g.length, - inc = Math.floor(l / levels), - heights = new Array(l), - buckets = new Array(levels); - if (typeof levels === 'undefined') { - levels = Math.floor(Math.pow(l*0.5, 0.25)); - } - for (i = 0; i < l; i++) { - heights[i] = g[i].z; - } - heights.sort(function(a, b) { return a - b; }); - for (i = 0; i < levels; i++) { - // Bucket by population (bucket size) not range size - var subset = heights.slice(i*inc, (i+1)*inc), - sum = 0, - bl = subset.length; - for (j = 0; j < bl; j++) { - sum += subset[j]; - } - buckets[i] = { - min: subset[0], - max: subset[subset.length-1], - avg: sum / bl, - }; - } - - // Set the height of each vertex to the average height of its bucket - for (i = 0; i < l; i++) { - var startHeight = g[i].z; - for (j = 0; j < levels; j++) { - if (startHeight >= buckets[j].min && startHeight <= buckets[j].max) { - g[i].z = buckets[j].avg; - break; - } - } - } -}; - -/** - * Transform to turbulent noise. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.Turbulence = function(g, options) { - var range = options.maxHeight - options.minHeight; - for (var i = 0, l = g.length; i < l; i++) { - g[i].z = options.minHeight + Math.abs((g[i].z - options.minHeight) * 2 - range); - } -}; - -/** - * A utility for generating heightmap functions by additive composition. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} [options] - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Object[]} passes - * Determines which heightmap functions to compose to create a new one. - * Consists of an array of objects with the following properties: - * - `method`: Contains something that will be passed around as an - * `options.heightmap` (a heightmap-generating function or a heightmap image) - * - `amplitude`: A multiplier for the heightmap of the pass. Applied before - * the result of the pass is added to the result of previous passes. - * - `frequency`: For terrain generation methods that support it (Perlin, - * Simplex, and Worley) the octave of randomness. This basically controls - * how big features of the terrain will be (higher frequencies result in - * smaller features). Often running multiple generation functions with - * different frequencies and amplitudes results in nice detail. - */ -THREE.Terrain.MultiPass = function(g, options, passes) { - var clonedOptions = {}; - for (var opt in options) { - if (options.hasOwnProperty(opt)) { - clonedOptions[opt] = options[opt]; - } - } - var range = options.maxHeight - options.minHeight; - for (var i = 0, l = passes.length; i < l; i++) { - var amp = typeof passes[i].amplitude === 'undefined' ? 1 : passes[i].amplitude, - move = 0.5 * (range - range * amp); - clonedOptions.maxHeight = options.maxHeight - move; - clonedOptions.minHeight = options.minHeight + move; - clonedOptions.frequency = typeof passes[i].frequency === 'undefined' ? options.frequency : passes[i].frequency; - passes[i].method(g, clonedOptions); - } -}; - -/** - * Generate random terrain using a curve. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Function} curve - * A function that takes an x- and y-coordinate and returns a z-coordinate. - * For example, `function(x, y) { return Math.sin(x*y*Math.PI*100); }` - * generates sine noise, and `function() { return Math.random(); }` sets the - * vertex elevations entirely randomly. The function's parameters (the x- and - * y-coordinates) are given as percentages of a phase (i.e. how far across - * the terrain in the relevant direction they are). - */ -THREE.Terrain.Curve = function(g, options, curve) { - var range = (options.maxHeight - options.minHeight) * 0.5, - scalar = options.frequency / (Math.min(options.xSegments, options.ySegments) + 1); - for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { - for (var j = 0; j < yl; j++) { - g[j * xl + i].z += curve(i * scalar, j * scalar) * range; - } - } -}; - -/** - * Generate random terrain using the Cosine waves. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.Cosine = function(g, options) { - var amplitude = (options.maxHeight - options.minHeight) * 0.5, - frequencyScalar = options.frequency * Math.PI / (Math.min(options.xSegments, options.ySegments) + 1), - phase = Math.random() * Math.PI * 2; - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - g[j * xl + i].z += amplitude * (Math.cos(i * frequencyScalar + phase) + Math.cos(j * frequencyScalar + phase)); - } - } -}; - -/** - * Generate random terrain using layers of Cosine waves. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.CosineLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Cosine, frequency: 2.5 }, - { method: THREE.Terrain.Cosine, amplitude: 0.1, frequency: 12 }, - { method: THREE.Terrain.Cosine, amplitude: 0.05, frequency: 15 }, - { method: THREE.Terrain.Cosine, amplitude: 0.025, frequency: 20 }, - ]); -}; - -/** - * Generate random terrain using the Diamond-Square method. - * - * Based on https://github.com/srchea/Terrain-Generation/blob/master/js/classes/TerrainGeneration.js - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - */ -THREE.Terrain.DiamondSquare = function(g, options) { - // Set the segment length to the smallest power of 2 that is greater than - // the number of vertices in either dimension of the plane - var segments = THREE.Math.nextPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); - - // Initialize heightmap - var size = segments + 1, - heightmap = [], - smoothing = (options.maxHeight - options.minHeight), - i, - j, - xl = options.xSegments + 1, - yl = options.ySegments + 1; - for (i = 0; i <= segments; i++) { - heightmap[i] = new Float64Array(segments+1); - } - - // Generate heightmap - for (var l = segments; l >= 2; l /= 2) { - var half = Math.round(l*0.5), - whole = Math.round(l), - x, - y, - avg, - d, - e; - smoothing /= 2; - // square - for (x = 0; x < segments; x += whole) { - for (y = 0; y < segments; y += whole) { - d = Math.random() * smoothing * 2 - smoothing; - avg = heightmap[x][y] + // top left - heightmap[x+whole][y] + // top right - heightmap[x][y+whole] + // bottom left - heightmap[x+whole][y+whole]; // bottom right - avg *= 0.25; - heightmap[x+half][y+half] = avg + d; - } - } - // diamond - for (x = 0; x < segments; x += half) { - for (y = (x+half) % l; y < segments; y += l) { - d = Math.random() * smoothing * 2 - smoothing; - avg = heightmap[(x-half+size)%size][y] + // middle left - heightmap[(x+half)%size][y] + // middle right - heightmap[x][(y+half)%size] + // middle top - heightmap[x][(y-half+size)%size]; // middle bottom - avg *= 0.25; - avg += d; - heightmap[x][y] = avg; - // top and right edges - if (x === 0) heightmap[segments][y] = avg; - if (y === 0) heightmap[x][segments] = avg; - } - } - } - - // Apply heightmap - for (i = 0; i < xl; i++) { - for (j = 0; j < yl; j++) { - g[j * xl + i].z += heightmap[i][j]; - } - } - - // THREE.Terrain.SmoothConservative(g, options); -}; - -/** - * Generate random terrain using the Fault method. - * - * Based on http://www.lighthouse3d.com/opengl/terrain/index.php3?fault - * Repeatedly draw random lines that cross the terrain. Raise the terrain on - * one side of the line and lower it on the other. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.Fault = function(g, options) { - var d = Math.sqrt(options.xSegments*options.xSegments + options.ySegments*options.ySegments), - iterations = d * options.frequency, - range = (options.maxHeight - options.minHeight) * 0.5, - displacement = range / iterations, - smoothDistance = Math.min(options.xSize / options.xSegments, options.ySize / options.ySegments) * options.frequency; - for (var k = 0; k < iterations; k++) { - var v = Math.random(), - a = Math.sin(v * Math.PI * 2), - b = Math.cos(v * Math.PI * 2), - c = Math.random() * d - d*0.5; - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - var distance = a*i + b*j - c; - if (distance > smoothDistance) { - g[j * xl + i].z += displacement; - } - else if (distance < -smoothDistance) { - g[j * xl + i].z -= displacement; - } - else { - g[j * xl + i].z += Math.cos(distance / smoothDistance * Math.PI * 2) * displacement; - } - } - } - } - // THREE.Terrain.Smooth(g, options); -}; - -/** - * Generate random terrain using the Hill method. - * - * The basic approach is to repeatedly pick random points on or near the - * terrain and raise a small hill around those points. Those small hills - * eventually accumulate into large hills with distinct features. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Function} [feature=THREE.Terrain.Influences.Hill] - * A function describing the feature to raise at the randomly chosen points. - * Typically this is a hill shape so that the accumulated features result in - * something resembling mountains, but it could be any function that accepts - * one parameter representing the distance from the feature's origin - * expressed as a number between -1 and 1 inclusive. Optionally it can accept - * a second and third parameter, which are the x- and y- distances from the - * feature's origin, respectively. It should return a number between -1 and 1 - * representing the height of the feature at the given coordinate. - * `THREE.Terrain.Influences` contains some useful functions for this - * purpose. - * @param {Function} [shape] - * A function that takes an object with `x` and `y` properties consisting of - * uniform random variables from 0 to 1, and returns a number from 0 to 1, - * typically by transforming it over a distribution. The result affects where - * small hills are raised thereby affecting the overall shape of the terrain. - */ -THREE.Terrain.Hill = function(g, options, feature, shape) { - var frequency = options.frequency * 2, - numFeatures = frequency * frequency * 10, - heightRange = options.maxHeight - options.minHeight, - minHeight = heightRange / (frequency * frequency), - maxHeight = heightRange / frequency, - smallerSideLength = Math.min(options.xSize, options.ySize), - minRadius = smallerSideLength / (frequency * frequency), - maxRadius = smallerSideLength / frequency; - feature = feature || THREE.Terrain.Influences.Hill; - - var coords = { x: 0, y: 0 }; - for (var i = 0; i < numFeatures; i++) { - var radius = Math.random() * (maxRadius - minRadius) + minRadius, - height = Math.random() * (maxHeight - minHeight) + minHeight; - var min = 0 - radius, - maxX = options.xSize + radius, - maxY = options.ySize + radius; - coords.x = Math.random(); - coords.y = Math.random(); - if (typeof shape === 'function') shape(coords); - THREE.Terrain.Influence( - g, options, - feature, - coords.x, coords.y, - radius, height, - THREE.AdditiveBlending, - THREE.Terrain.EaseInStrong - ); - } -}; - -/** - * Generate random terrain using the Hill method, centered on the terrain. - * - * The only difference between this and the Hill method is that the locations - * of the points to place small hills are not uniformly randomly distributed - * but instead are more likely to occur close to the center of the terrain. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Function} [feature=THREE.Terrain.Influences.Hill] - * A function describing the feature. The function should accept one - * parameter representing the distance from the feature's origin expressed as - * a number between -1 and 1 inclusive. Optionally it can accept a second and - * third parameter, which are the x- and y- distances from the feature's - * origin, respectively. It should return a number between -1 and 1 - * representing the height of the feature at the given coordinate. - * `THREE.Terrain.Influences` contains some useful functions for this - * purpose. - */ -THREE.Terrain.HillIsland = (function() { - var island = function(coords) { - var theta = Math.random() * Math.PI * 2; - coords.x = 0.5 + Math.cos(theta) * coords.x * 0.4; - coords.y = 0.5 + Math.sin(theta) * coords.y * 0.4; - }; - return function(g, options, feature) { - THREE.Terrain.Hill(g, options, feature, island); - }; -})(); - -(function() { - /** - * Deposit a particle at a vertex. - */ - function deposit(g, i, j, xl, displacement) { - var currentKey = j * xl + i; - // Pick a random neighbor. - for (var k = 0; k < 3; k++) { - var r = Math.floor(Math.random() * 8); - switch (r) { - case 0: i++; break; - case 1: i--; break; - case 2: j++; break; - case 3: j--; break; - case 4: i++; j++; break; - case 5: i++; j--; break; - case 6: i--; j++; break; - case 7: i--; j--; break; - } - var neighborKey = j * xl + i; - // If the neighbor is lower, move the particle to that neighbor and re-evaluate. - if (typeof g[neighborKey] !== 'undefined') { - if (g[neighborKey].z < g[currentKey].z) { - deposit(g, i, j, xl, displacement); - return; - } - } - // Deposit some particles on the edge. - else if (Math.random() < 0.2) { - g[currentKey].z += displacement; - return; - } - } - g[currentKey].z += displacement; - } - - /** - * Generate random terrain using the Particle Deposition method. - * - * Based on http://www.lighthouse3d.com/opengl/terrain/index.php?particle - * Repeatedly deposit particles on terrain vertices. Pick a random neighbor - * of that vertex. If the neighbor is lower, roll the particle to the - * neighbor. When the particle stops, displace the vertex upwards. - * - * The shape of the outcome is highly dependent on options.frequency - * because that affects how many particles will be dropped. Values around - * 0.25 generally result in archipelagos whereas the default of 2.5 - * generally results in one large mountainous island. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ - THREE.Terrain.Particles = function(g, options) { - var iterations = Math.sqrt(options.xSegments*options.xSegments + options.ySegments*options.ySegments) * options.frequency * 300, - xl = options.xSegments + 1, - displacement = (options.maxHeight - options.minHeight) / iterations * 1000, - i = Math.floor(Math.random() * options.xSegments), - j = Math.floor(Math.random() * options.ySegments), - xDeviation = Math.random() * 0.2 - 0.1, - yDeviation = Math.random() * 0.2 - 0.1; - for (var k = 0; k < iterations; k++) { - deposit(g, i, j, xl, displacement); - var d = Math.random() * Math.PI * 2; - if (k % 1000 === 0) { - xDeviation = Math.random() * 0.2 - 0.1; - yDeviation = Math.random() * 0.2 - 0.1; - } - if (k % 100 === 0) { - i = Math.floor(options.xSegments*(0.5+xDeviation) + Math.cos(d) * Math.random() * options.xSegments*(0.5-Math.abs(xDeviation))); - j = Math.floor(options.ySegments*(0.5+yDeviation) + Math.sin(d) * Math.random() * options.ySegments*(0.5-Math.abs(yDeviation))); - } - } - // THREE.Terrain.Smooth(g, options, 3); - }; -})(); - -/** - * Generate random terrain using the Perlin Noise method. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.Perlin = function(g, options) { - noise.seed(Math.random()); - var range = (options.maxHeight - options.minHeight) * 0.5, - divisor = (Math.min(options.xSegments, options.ySegments) + 1) / options.frequency; - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - g[j * xl + i].z += noise.perlin(i / divisor, j / divisor) * range; - } - } -}; - -/** - * Generate random terrain using the Perlin and Diamond-Square methods composed. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.PerlinDiamond = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Perlin }, - { method: THREE.Terrain.DiamondSquare, amplitude: 0.75 }, - { method: function(g, o) { return THREE.Terrain.SmoothMedian(g, o); } }, - ]); -}; - -/** - * Generate random terrain using layers of Perlin noise. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.PerlinLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Perlin, frequency: 1.25 }, - { method: THREE.Terrain.Perlin, amplitude: 0.05, frequency: 2.5 }, - { method: THREE.Terrain.Perlin, amplitude: 0.35, frequency: 5 }, - { method: THREE.Terrain.Perlin, amplitude: 0.15, frequency: 10 }, - ]); -}; - -/** - * Generate random terrain using the Simplex Noise method. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - * - * See https://github.com/mrdoob/three.js/blob/master/examples/webgl_terrain_dynamic.html - * for an interesting comparison where the generation happens in GLSL. - */ -THREE.Terrain.Simplex = function(g, options) { - noise.seed(Math.random()); - var range = (options.maxHeight - options.minHeight) * 0.5, - divisor = (Math.min(options.xSegments, options.ySegments) + 1) * 2 / options.frequency; - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - g[j * xl + i].z += noise.simplex(i / divisor, j / divisor) * range; - } - } -}; - -/** - * Generate random terrain using layers of Simplex noise. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.SimplexLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Simplex, frequency: 1.25 }, - { method: THREE.Terrain.Simplex, amplitude: 0.5, frequency: 2.5 }, - { method: THREE.Terrain.Simplex, amplitude: 0.25, frequency: 5 }, - { method: THREE.Terrain.Simplex, amplitude: 0.125, frequency: 10 }, - { method: THREE.Terrain.Simplex, amplitude: 0.0625, frequency: 20 }, - ]); -}; - -(function() { - /** - * Generate a heightmap using white noise. - * - * @param {THREE.Vector3[]} g The terrain vertices. - * @param {Object} options Settings - * @param {Number} scale The resolution of the resulting heightmap. - * @param {Number} segments The width of the target heightmap. - * @param {Number} range The altitude of the noise. - * @param {Number[]} data The target heightmap. - */ - function WhiteNoise(g, options, scale, segments, range, data) { - if (scale > segments) return; - var i = 0, - j = 0, - xl = segments, - yl = segments, - inc = Math.floor(segments / scale), - lastX = -inc, - lastY = -inc; - // Walk over the target. For a target of size W and a resolution of N, - // set every W/N points (in both directions). - for (i = 0; i <= xl; i += inc) { - for (j = 0; j <= yl; j += inc) { - var k = j * xl + i; - data[k] = Math.random() * range; - if (lastX < 0 && lastY < 0) continue; - // jscs:disable disallowSpacesInsideBrackets - /* c b * - * l t */ - var t = data[k], - l = data[ j * xl + (i-inc)] || t, // left - b = data[(j-inc) * xl + i ] || t, // bottom - c = data[(j-inc) * xl + (i-inc)] || t; // corner - // jscs:enable disallowSpacesInsideBrackets - // Interpolate between adjacent points to set the height of - // higher-resolution target data. - for (var x = lastX; x < i; x++) { - for (var y = lastY; y < j; y++) { - if (x === lastX && y === lastY) continue; - var z = y * xl + x; - if (z < 0) continue; - var px = ((x-lastX) / inc), - py = ((y-lastY) / inc), - r1 = px * b + (1-px) * c, - r2 = px * t + (1-px) * l; - data[z] = py * r2 + (1-py) * r1; - } - } - lastY = j; - } - lastX = i; - lastY = -inc; - } - // Assign the temporary data back to the actual terrain heightmap. - for (i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (j = 0, yl = options.ySegments + 1; j < yl; j++) { - // http://stackoverflow.com/q/23708306/843621 - var kg = j * xl + i, - kd = j * segments + i; - g[kg].z += data[kd]; - } - } - } - - /** - * Generate random terrain using value noise. - * - * The basic approach of value noise is to generate white noise at a - * smaller octave than the target and then interpolate to get a higher- - * resolution result. This is then repeated at different resolutions. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ - THREE.Terrain.Value = function(g, options) { - // Set the segment length to the smallest power of 2 that is greater - // than the number of vertices in either dimension of the plane - var segments = THREE.Math.nextPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); - - // Store the array of white noise outside of the WhiteNoise function to - // avoid allocating a bunch of unnecessary arrays; we can just - // overwrite old data each time WhiteNoise() is called. - var data = new Float64Array((segments+1)*(segments+1)); - - // Layer white noise at different resolutions. - var range = options.maxHeight - options.minHeight; - for (var i = 2; i < 7; i++) { - WhiteNoise(g, options, Math.pow(2, i), segments, range * Math.pow(2, 2.4-i*1.2), data); - } - - // White noise creates some weird artifacts; fix them. - // THREE.Terrain.Smooth(g, options, 1); - THREE.Terrain.Clamp(g, { - maxHeight: options.maxHeight, - minHeight: options.minHeight, - stretch: true, - }); - }; -})(); - -/** - * Generate random terrain using Weierstrass functions. - * - * Weierstrass functions are known for being continuous but not differentiable - * anywhere. This produces some nice shapes that look terrain-like, but can - * look repetitive from above. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ -THREE.Terrain.Weierstrass = function(g, options) { - var range = (options.maxHeight - options.minHeight) * 0.5, - dir1 = Math.random() < 0.5 ? 1 : -1, - dir2 = Math.random() < 0.5 ? 1 : -1, - r11 = 0.5 + Math.random() * 1.0, - r12 = 0.5 + Math.random() * 1.0, - r13 = 0.025 + Math.random() * 0.10, - r14 = -1.0 + Math.random() * 2.0, - r21 = 0.5 + Math.random() * 1.0, - r22 = 0.5 + Math.random() * 1.0, - r23 = 0.025 + Math.random() * 0.10, - r24 = -1.0 + Math.random() * 2.0; - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - var sum = 0; - for (var k = 0; k < 20; k++) { - var x = Math.pow(1+r11, -k) * Math.sin(Math.pow(1+r12, k) * (i + 0.25*Math.cos(j) + r14*j) * r13); - var y = Math.pow(1+r21, -k) * Math.sin(Math.pow(1+r22, k) * (j + 0.25*Math.cos(i) + r24*i) * r23); - sum -= Math.exp(dir1*x*x + dir2*y*y); - } - g[j * xl + i].z += sum * range; - } - } - THREE.Terrain.Clamp(g, options); -}; - -/** - * Generate a material that blends together textures based on vertex height. - * - * Inspired by http://www.chandlerprall.com/2011/06/blending-webgl-textures/ - * - * Usage: - * - * // Assuming the textures are already loaded - * var material = THREE.Terrain.generateBlendedMaterial([ - * {texture: THREE.ImageUtils.loadTexture('img1.jpg')}, - * {texture: THREE.ImageUtils.loadTexture('img2.jpg'), levels: [-80, -35, 20, 50]}, - * {texture: THREE.ImageUtils.loadTexture('img3.jpg'), levels: [20, 50, 60, 85]}, - * {texture: THREE.ImageUtils.loadTexture('img4.jpg'), glsl: '1.0 - smoothstep(65.0 + smoothstep(-256.0, 256.0, vPosition.x) * 10.0, 80.0, vPosition.z)'}, - * ]); - * - * This material tries to behave exactly like a MeshLambertMaterial other than - * the fact that it blends multiple texture maps together, although - * ShaderMaterials are treated slightly differently by Three.js so YMMV. Note - * that this means the texture will appear black unless there are lights - * shining on it. - * - * @param {Object[]} textures - * An array of objects specifying textures to blend together and how to blend - * them. Each object should have a `texture` property containing a - * `THREE.Texture` instance. There must be at least one texture and the first - * texture does not need any other properties because it will serve as the - * base, showing up wherever another texture isn't blended in. Other textures - * must have either a `levels` property containing an array of four numbers - * or a `glsl` property containing a single GLSL expression evaluating to a - * float between 0.0 and 1.0. For the `levels` property, the four numbers - * are, in order: the height at which the texture will start blending in, the - * height at which it will be fully blended in, the height at which it will - * start blending out, and the height at which it will be fully blended out. - * The `vec3 vPosition` variable is available to `glsl` expressions; it - * contains the coordinates in Three-space of the texel currently being - * rendered. - */ -THREE.Terrain.generateBlendedMaterial = function(textures) { - // Convert numbers to strings of floats so GLSL doesn't barf on "1" instead of "1.0" - function glslifyNumber(n) { - return n === (n|0) ? n+'.0' : n+''; - } - - var uniforms = THREE.UniformsUtils.merge([THREE.ShaderLib.lambert.uniforms]), - declare = '', - assign = '', - t0Repeat = textures[0].texture.repeat, - t0Offset = textures[0].texture.offset; - for (var i = 0, l = textures.length; i < l; i++) { - // Uniforms - textures[i].texture.wrapS = textures[i].wrapT = THREE.RepeatWrapping; - textures[i].texture.needsUpdate = true; - uniforms['texture_' + i] = { - type: 't', - value: textures[i].texture, - }; - - // Shader fragments - // Declare each texture, then mix them together. - declare += 'uniform sampler2D texture_' + i + ';\n'; - if (i !== 0) { - var v = textures[i].levels, // Vertex heights at which to blend textures in and out - p = textures[i].glsl, // Or specify a GLSL expression that evaluates to a float between 0.0 and 1.0 indicating how opaque the texture should be at this texel - useLevels = typeof v !== 'undefined', // Use levels if they exist; otherwise, use the GLSL expression - tiRepeat = textures[i].texture.repeat, - tiOffset = textures[i].texture.offset; - if (useLevels) { - // Must fade in; can't start and stop at the same point. - // So, if levels are too close, move one of them slightly. - if (v[1] - v[0] < 1) v[0] -= 1; - if (v[3] - v[2] < 1) v[3] += 1; - for (var j = 0; j < v.length; j++) { - v[j] = glslifyNumber(v[j]); - } - } - // The transparency of the new texture when it is layered on top of the existing color at this texel is - // (how far between the start-blending-in and fully-blended-in levels the current vertex is) + - // (how far between the start-blending-out and fully-blended-out levels the current vertex is) - // So the opacity is 1.0 minus that. - var blendAmount = !useLevels ? p : - '1.0 - smoothstep(' + v[0] + ', ' + v[1] + ', vPosition.z) + smoothstep(' + v[2] + ', ' + v[3] + ', vPosition.z)'; - assign += ' color = mix( ' + - 'texture2D( texture_' + i + ', MyvUv * vec2( ' + glslifyNumber(tiRepeat.x) + ', ' + glslifyNumber(tiRepeat.y) + ' ) + vec2( ' + glslifyNumber(tiOffset.x) + ', ' + glslifyNumber(tiOffset.y) + ' ) ), ' + - 'color, ' + - 'max(min(' + blendAmount + ', 1.0), 0.0)' + - ');\n'; - } - } - - var params = { - // I don't know which of these properties have any effect - fog: true, - lights: true, - // shading: THREE.SmoothShading, - // blending: THREE.NormalBlending, - // depthTest: , - // depthWrite: , - // wireframe: false, - // wireframeLinewidth: 1, - // vertexColors: THREE.NoColors, - // skinning: , - // morphTargets: , - // morphNormals: , - // opacity: 1.0, - // transparent: , - // side: THREE.FrontSide, - - uniforms: uniforms, - vertexShader: THREE.ShaderLib.lambert.vertexShader.replace( - 'void main() {', - 'varying vec2 MyvUv;\nvarying vec3 vPosition;\nvarying vec3 myNormal; void main() {\nMyvUv = uv;\nvPosition = position;\nmyNormal = normal;' - ), - // This is mostly copied from THREE.ShaderLib.lambert.fragmentShader - fragmentShader: [ - 'uniform vec3 diffuse;', - 'uniform vec3 emissive;', - 'uniform float opacity;', - 'varying vec3 vLightFront;', - '#ifdef DOUBLE_SIDED', - ' varying vec3 vLightBack;', - '#endif', - - THREE.ShaderChunk.common, - THREE.ShaderChunk.packing, - THREE.ShaderChunk.dithering_pars_fragment, - THREE.ShaderChunk.color_pars_fragment, - THREE.ShaderChunk.uv_pars_fragment, - THREE.ShaderChunk.uv2_pars_fragment, - THREE.ShaderChunk.map_pars_fragment, - THREE.ShaderChunk.alphamap_pars_fragment, - THREE.ShaderChunk.aomap_pars_fragment, - THREE.ShaderChunk.lightmap_pars_fragment, - THREE.ShaderChunk.emissivemap_pars_fragment, - THREE.ShaderChunk.envmap_pars_fragment, - THREE.ShaderChunk.bsdfs, - THREE.ShaderChunk.lights_pars_begin, - THREE.ShaderChunk.lights_pars_maps, - THREE.ShaderChunk.fog_pars_fragment, - THREE.ShaderChunk.shadowmap_pars_fragment, - THREE.ShaderChunk.shadowmask_pars_fragment, - THREE.ShaderChunk.specularmap_pars_fragment, - THREE.ShaderChunk.logdepthbuf_pars_fragment, - THREE.ShaderChunk.clipping_planes_pars_fragment, - - declare, - 'varying vec2 MyvUv;', - 'varying vec3 vPosition;', - 'varying vec3 myNormal;', - - 'void main() {', - - THREE.ShaderChunk.clipping_planes_fragment, - - 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', - 'vec3 totalEmissiveRadiance = emissive;', - - // TODO: The second vector here is the object's "up" vector. Ideally we'd just pass it in directly. - 'float slope = acos(max(min(dot(myNormal, vec3(0.0, 0.0, 1.0)), 1.0), -1.0));', - - ' vec4 diffuseColor = vec4( diffuse, opacity );', - ' vec4 color = texture2D( texture_0, MyvUv * vec2( ' + glslifyNumber(t0Repeat.x) + ', ' + glslifyNumber(t0Repeat.y) + ' ) + vec2( ' + glslifyNumber(t0Offset.x) + ', ' + glslifyNumber(t0Offset.y) + ' ) ); // base', - assign, - ' diffuseColor = color;', - // ' gl_FragColor = color;', - - THREE.ShaderChunk.logdepthbuf_fragment, - THREE.ShaderChunk.map_fragment, - THREE.ShaderChunk.color_fragment, - THREE.ShaderChunk.alphamap_fragment, - THREE.ShaderChunk.alphatest_fragment, - THREE.ShaderChunk.specularmap_fragment, - THREE.ShaderChunk.emissivemap_fragment, - - // accumulation - ' reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );', - - THREE.ShaderChunk.lightmap_fragment, - - ' reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );', - ' #ifdef DOUBLE_SIDED', - ' reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;', - ' #else', - ' reflectedLight.directDiffuse = vLightFront;', - ' #endif', - ' reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();', - - // modulation - THREE.ShaderChunk.aomap_fragment, - ' vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;', - THREE.ShaderChunk.normal_flip, - THREE.ShaderChunk.envmap_fragment, - ' gl_FragColor = vec4( outgoingLight, diffuseColor.a );', // This will probably change in future three.js releases - THREE.ShaderChunk.tonemapping_fragment, - THREE.ShaderChunk.encodings_fragment, - THREE.ShaderChunk.fog_fragment, - THREE.ShaderChunk.premultiplied_alpha_fragment, - THREE.ShaderChunk.dithering_fragment, - '}' - ].join('\n'), - }; - return new THREE.ShaderMaterial(params); -}; - -/** - * Scatter a mesh across the terrain. - * - * @param {THREE.Geometry} geometry - * The terrain's geometry (or the highest-resolution version of it). - * @param {Object} options - * A map of settings that controls how the meshes are scattered, with the - * following properties: - * - `mesh`: A `THREE.Mesh` instance to scatter across the terrain. - * - `spread`: A number or a function that affects where meshes are placed. - * If it is a number, it represents the percent of faces of the terrain - * onto which a mesh should be placed. If it is a function, it takes a - * vertex from the terrain and the key of a related face and returns a - * boolean indicating whether to place a mesh on that face or not. An - * example could be `function(v, k) { return v.z > 0 && !(k % 4); }`. - * Defaults to 0.025. - * - `smoothSpread`: If the `spread` option is a number, this affects how - * much placement is "eased in." Specifically, if the `randomness` function - * returns a value for a face that is within `smoothSpread` percentiles - * above `spread`, then the probability that a mesh is placed there is - * interpolated between zero and `spread`. This creates a "thinning" effect - * near the edges of clumps, if the randomness function creates clumps. - * - `scene`: A `THREE.Object3D` instance to which the scattered meshes will - * be added. This is expected to be either a return value of a call to - * `THREE.Terrain()` or added to that return value; otherwise the position - * and rotation of the meshes will be wrong. - * - `sizeVariance`: The percent by which instances of the mesh can be scaled - * up or down when placed on the terrain. - * - `randomness`: If `options.spread` is a number, then this property is a - * function that determines where meshes are placed. Specifically, it - * returns an array of numbers, where each number is the probability that - * a mesh is NOT placed on the corresponding face. Valid values include - * `Math.random` and the return value of a call to - * `THREE.Terrain.ScatterHelper`. - * - `maxSlope`: The angle in radians between the normal of a face of the - * terrain and the "up" vector above which no mesh will be placed on the - * related face. Defaults to ~0.63, which is 36 degrees. - * - `maxTilt`: The maximum angle in radians a mesh can be tilted away from - * the "up" vector (towards the normal vector of the face of the terrain). - * Defaults to Infinity (meshes will point towards the normal). - * - `w`: The number of horizontal segments of the terrain. - * - `h`: The number of vertical segments of the terrain. - * - * @return {THREE.Object3D} - * An Object3D containing the scattered meshes. This is the value of the - * `options.scene` parameter if passed. This is expected to be either a - * return value of a call to `THREE.Terrain()` or added to that return value; - * otherwise the position and rotation of the meshes will be wrong. - */ -THREE.Terrain.ScatterMeshes = function(geometry, options) { - if (!options.mesh) { - console.error('options.mesh is required for THREE.Terrain.ScatterMeshes but was not passed'); - return; - } - if (geometry instanceof THREE.BufferGeometry) { - console.warn('The terrain mesh is using BufferGeometry but THREE.Terrain.ScatterMeshes can only work with Geometry.'); - return; - } - if (!options.scene) { - options.scene = new THREE.Object3D(); - } - var defaultOptions = { - spread: 0.025, - smoothSpread: 0, - sizeVariance: 0.1, - randomness: Math.random, - maxSlope: 0.6283185307179586, // 36deg or 36 / 180 * Math.PI, about the angle of repose of earth - maxTilt: Infinity, - w: 0, - h: 0, - }; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - options[opt] = typeof options[opt] === 'undefined' ? defaultOptions[opt] : options[opt]; - } - } - - var spreadIsNumber = typeof options.spread === 'number', - randomHeightmap, - randomness, - spreadRange = 1 / options.smoothSpread, - doubleSizeVariance = options.sizeVariance * 2, - v = geometry.vertices, - meshes = [], - up = options.mesh.up.clone().applyAxisAngle(new THREE.Vector3(1, 0, 0), 0.5*Math.PI); - if (spreadIsNumber) { - randomHeightmap = options.randomness(); - randomness = typeof randomHeightmap === 'number' ? Math.random : function(k) { return randomHeightmap[k]; }; - } - // geometry.computeFaceNormals(); - for (var i = 0, w = options.w*2; i < w; i++) { - for (var j = 0, h = options.h; j < h; j++) { - var key = j*w + i, - f = geometry.faces[key], - place = false; - if (spreadIsNumber) { - var rv = randomness(key); - if (rv < options.spread) { - place = true; - } - else if (rv < options.spread + options.smoothSpread) { - // Interpolate rv between spread and spread + smoothSpread, - // then multiply that "easing" value by the probability - // that a mesh would get placed on a given face. - place = THREE.Terrain.EaseInOut((rv - options.spread) * spreadRange) * options.spread > Math.random(); - } - } - else { - place = options.spread(v[f.a], key, f, i, j); - } - if (place) { - // Don't place a mesh if the angle is too steep. - if (f.normal.angleTo(up) > options.maxSlope) { - continue; - } - var mesh = options.mesh.clone(); - // mesh.geometry.computeBoundingBox(); - mesh.position.copy(v[f.a]).add(v[f.b]).add(v[f.c]).divideScalar(3); - // mesh.translateZ((mesh.geometry.boundingBox.max.z - mesh.geometry.boundingBox.min.z) * 0.5); - if (options.maxTilt > 0) { - var normal = mesh.position.clone().add(f.normal); - mesh.lookAt(normal); - var tiltAngle = f.normal.angleTo(up); - if (tiltAngle > options.maxTilt) { - var ratio = options.maxTilt / tiltAngle; - mesh.rotation.x *= ratio; - mesh.rotation.y *= ratio; - mesh.rotation.z *= ratio; - } - } - mesh.rotation.x += 90 / 180 * Math.PI; - mesh.rotateY(Math.random() * 2 * Math.PI); - if (options.sizeVariance) { - var variance = Math.random() * doubleSizeVariance - options.sizeVariance; - mesh.scale.x = mesh.scale.z = 1 + variance; - mesh.scale.y += variance; - } - meshes.push(mesh); - } - } - } - - // Merge geometries. - var k, l; - if (options.mesh.geometry instanceof THREE.Geometry) { - var g = new THREE.Geometry(); - for (k = 0, l = meshes.length; k < l; k++) { - var m = meshes[k]; - m.updateMatrix(); - g.merge(m.geometry, m.matrix); - } - /* - if (!(options.mesh.material instanceof THREE.MeshFaceMaterial)) { - g = THREE.BufferGeometryUtils.fromGeometry(g); - } - */ - options.scene.add(new THREE.Mesh(g, options.mesh.material)); - } - // There's no BufferGeometry merge method implemented yet. - else { - for (k = 0, l = meshes.length; k < l; k++) { - options.scene.add(meshes[k]); - } - } - - return options.scene; -}; - -/** - * Generate a function that returns a heightmap to pass to ScatterMeshes. - * - * Specifically, this function generates a heightmap and then uses that - * heightmap as a map of probabilities of where meshes will be placed. - * - * @param {Function} method - * A random terrain generation function (i.e. a valid value for the - * `options.heightmap` parameter of the `THREE.Terrain` function). - * @param {Object} options - * A map of settings that control how the resulting noise should be generated - * (with the same parameters as the `options` parameter to the - * `THREE.Terrain` function). `options.minHeight` must equal `0` and - * `options.maxHeight` must equal `1` if they are specified. - * @param {Number} skip - * The number of sequential faces to skip between faces that are candidates - * for placing a mesh. This avoid clumping meshes too closely together. - * Defaults to 1. - * @param {Number} threshold - * The probability that, if a mesh can be placed on a non-skipped face due to - * the shape of the heightmap, a mesh actually will be placed there. Helps - * thin out placement and make it less regular. Defaults to 0.25. - * - * @return {Function} - * Returns a function that can be passed as the value of the - * `options.randomness` parameter to the {@link THREE.Terrain.ScatterMeshes} - * function. - */ -THREE.Terrain.ScatterHelper = function(method, options, skip, threshold) { - skip = skip || 1; - threshold = threshold || 0.25; - options.frequency = options.frequency || 2.5; - - var clonedOptions = {}; - for (var opt in options) { - if (options.hasOwnProperty(opt)) { - clonedOptions[opt] = options[opt]; - } - } - - clonedOptions.xSegments *= 2; - clonedOptions.stretch = true; - clonedOptions.maxHeight = 1; - clonedOptions.minHeight = 0; - var heightmap = THREE.Terrain.heightmapArray(method, clonedOptions); - - for (var i = 0, l = heightmap.length; i < l; i++) { - if (i % skip || Math.random() > threshold) { - heightmap[i] = 1; // 0 = place, 1 = don't place - } - } - return function() { - return heightmap; - }; -}; - -// Allows placing geometrically-described features on a terrain. -// If you want these features to look a little less regular, -// just apply them before a procedural pass. -// If you want more complex influence, you can just composite heightmaps. - -/** - * Equations describing geographic features. - */ -THREE.Terrain.Influences = { - Mesa: function(x) { - return 1.25 * Math.min(0.8, Math.exp(-(x*x))); - }, - Hole: function(x) { - return -THREE.Terrain.Influences.Mesa(x); - }, - Hill: function(x) { - // Same curve as EaseInOut, but mirrored and translated. - return x < 0 ? (x+1)*(x+1)*(3-2*(x+1)) : 1-x*x*(3-2*x); - }, - Valley: function(x) { - return -THREE.Terrain.Influences.Hill(x); - }, - Dome: function(x) { - // Parabola - return -(x+1)*(x-1); - }, - // Not meaningful in Additive or Subtractive mode - Flat: function(x) { - return 0; - }, - Volcano: function(x) { - return 0.94 - 0.32 * (Math.abs(2 * x) + Math.cos(2 * Math.PI * Math.abs(x) + 0.4)); - }, -}; - -/** - * Place a geographic feature on the terrain. - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. This - * method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}(). - * @param {Function} f - * A function describing the feature. The function should accept one - * parameter representing the distance from the feature's origin expressed as - * a number between -1 and 1 inclusive. Optionally it can accept a second and - * third parameter, which are the x- and y- distances from the feature's - * origin, respectively. It should return a number between -1 and 1 - * representing the height of the feature at the given coordinate. - * `THREE.Terrain.Influences` contains some useful functions for this - * purpose. - * @param {Number} [x=0.5] - * How far across the terrain the feature should be placed on the X-axis, in - * PERCENT (as a decimal) of the size of the terrain on that axis. - * @param {Number} [y=0.5] - * How far across the terrain the feature should be placed on the Y-axis, in - * PERCENT (as a decimal) of the size of the terrain on that axis. - * @param {Number} [r=64] - * The radius of the feature. - * @param {Number} [h=64] - * The height of the feature. - * @param {String} [t=THREE.NormalBlending] - * Determines how to layer the feature on top of the existing terrain. Valid - * values include `THREE.AdditiveBlending`, `THREE.SubtractiveBlending`, - * `THREE.MultiplyBlending`, `THREE.NoBlending`, `THREE.NormalBlending`, and - * any function that takes the terrain's current height, the feature's - * displacement at a vertex, and the vertex's distance from the feature - * origin, and returns the new height for that vertex. (If a custom function - * is passed, it can take optional fourth and fifth parameters, which are the - * x- and y-distances from the feature's origin, respectively.) - * @param {Number/Function} [e=THREE.Terrain.EaseIn] - * A function that determines the "falloff" of the feature, i.e. how quickly - * the terrain will get close to its height before the feature was applied as - * the distance increases from the feature's location. It does this by - * interpolating the height of each vertex along a curve. Valid values - * include `THREE.Terrain.Linear`, `THREE.Terrain.EaseIn`, - * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, - * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float - * between 0 and 1 representing the distance to the feature origin and - * returns a float between 0 and 1 with the adjusted distance. (Custom - * functions can also accept optional second and third parameters, which are - * the x- and y-distances to the feature origin, respectively.) - */ -THREE.Terrain.Influence = function(g, options, f, x, y, r, h, t, e) { - f = f || THREE.Terrain.Influences.Hill; // feature shape - x = typeof x === 'undefined' ? 0.5 : x; // x-location % - y = typeof y === 'undefined' ? 0.5 : y; // y-location % - r = typeof r === 'undefined' ? 64 : r; // radius - h = typeof h === 'undefined' ? 64 : h; // height - t = typeof t === 'undefined' ? THREE.NormalBlending : t; // blending - e = e || THREE.Terrain.EaseIn; // falloff - // Find the vertex location of the feature origin - var xl = options.xSegments + 1, // # x-vertices - yl = options.ySegments + 1, // # y-vertices - vx = xl * x, // vertex x-location - vy = yl * y, // vertex y-location - xw = options.xSize / options.xSegments, // width of x-segments - yw = options.ySize / options.ySegments, // width of y-segments - rx = r / xw, // radius of the feature in vertices on the x-axis - ry = r / yw, // radius of the feature in vertices on the y-axis - r1 = 1 / r, // for speed - xs = Math.ceil(vx - rx), // starting x-vertex index - xe = Math.floor(vx + rx), // ending x-vertex index - ys = Math.ceil(vy - ry), // starting y-vertex index - ye = Math.floor(vy + ry); // ending y-vertex index - // Walk over the vertices within radius of origin - for (var i = xs; i < xe; i++) { - for (var j = ys; j < ye; j++) { - var k = j * xl + i, - // distance to the feature origin - fdx = (i - vx) * xw, - fdy = (j - vy) * yw, - fd = Math.sqrt(fdx*fdx + fdy*fdy), - fdr = fd * r1, - fdxr = fdx * r1, - fdyr = fdy * r1, - // Get the displacement according to f, multiply it by h, - // interpolate using e, then blend according to t. - d = f(fdr, fdxr, fdyr) * h * (1 - e(fdr, fdxr, fdyr)); - if (fd > r || typeof g[k] == 'undefined') continue; - if (t === THREE.AdditiveBlending) g[k].z += d; // jscs:ignore requireSpaceAfterKeywords - else if (t === THREE.SubtractiveBlending) g[k].z -= d; - else if (t === THREE.MultiplyBlending) g[k].z *= d; - else if (t === THREE.NoBlending) g[k].z = d; - else if (t === THREE.NormalBlending) g[k].z = e(fdr, fdxr, fdyr) * g[k].z + d; - else if (typeof t === 'function') g[k].z = t(g[k].z, d, fdr, fdxr, fdyr); - } - } -}; diff --git a/build/THREE.Terrain.min.js b/build/THREE.Terrain.min.js deleted file mode 100644 index 1cb233a..0000000 --- a/build/THREE.Terrain.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * THREE.Terrain.js 1.6.0-20180415 - * - * @author Isaac Sukin (http://www.isaacsukin.com/) - * @license MIT - */ - -!function(a){function b(a,b,c){this.x=a,this.y=b,this.z=c}function c(a){return a*a*a*(a*(6*a-15)+10)}function d(a,b,c){return(1-c)*a+c*b}var e=a.noise={};b.prototype.dot2=function(a,b){return this.x*a+this.y*b},b.prototype.dot3=function(a,b,c){return this.x*a+this.y*b+this.z*c};var f=[new b(1,1,0),new b(-1,1,0),new b(1,-1,0),new b(-1,-1,0),new b(1,0,1),new b(-1,0,1),new b(1,0,-1),new b(-1,0,-1),new b(0,1,1),new b(0,-1,1),new b(0,1,-1),new b(0,-1,-1)],g=[151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180],h=new Array(512),i=new Array(512);e.seed=function(a){a>0&&a<1&&(a*=65536),a=Math.floor(a),a<256&&(a|=a<<8);for(var b=0;b<256;b++){var c;c=1&b?g[b]^255&a:g[b]^a>>8&255,h[b]=h[b+256]=c,i[b]=i[b+256]=f[c%12]}},e.seed(Math.random());var j=.5*(Math.sqrt(3)-1),k=(3-Math.sqrt(3))/6,l=1/3,m=1/6;e.simplex=function(a,b){var c,d,e,f=(a+b)*j,g=Math.floor(a+f),l=Math.floor(b+f),m=(g+l)*k,n=a-g+m,o=b-l+m,p,q;n>o?(p=1,q=0):(p=0,q=1);var r=n-p+k,s=o-q+k,t=n-1+2*k,u=o-1+2*k;g&=255,l&=255;var v=i[g+h[l]],w=i[g+p+h[l+q]],x=i[g+1+h[l+1]],y=.5-n*n-o*o;y<0?c=0:(y*=y,c=y*y*v.dot2(n,o));var z=.5-r*r-s*s;z<0?d=0:(z*=z,d=z*z*w.dot2(r,s));var A=.5-t*t-u*u;return A<0?e=0:(A*=A,e=A*A*x.dot2(t,u)),70*(c+d+e)},e.perlin=function(a,b){var e=Math.floor(a),f=Math.floor(b);a-=e,b-=f,e&=255,f&=255;var g=i[e+h[f]].dot2(a,b),j=i[e+h[f+1]].dot2(a,b-1),k=i[e+1+h[f]].dot2(a-1,b),l=i[e+1+h[f+1]].dot2(a-1,b-1),m=c(a);return d(d(g,k,m),d(j,l,m),c(b))}}(this),THREE.Terrain=function(a){var b={after:null,easing:THREE.Terrain.Linear,heightmap:THREE.Terrain.DiamondSquare,material:null,maxHeight:100,minHeight:-100,optimization:THREE.Terrain.NONE,frequency:2.5,steps:1,stretch:!0,turbulent:!1,useBufferGeometry:!1,xSegments:63,xSize:1024,ySegments:63,ySize:1024,_mesh:null};a=a||{};for(var c in b)b.hasOwnProperty(c)&&(a[c]=void 0===a[c]?b[c]:a[c]);a.material=a.material||new THREE.MeshBasicMaterial({color:15623731});var d=new THREE.Object3D;d.rotation.x=-.5*Math.PI;var e=a._mesh;if(e&&"PlaneGeometry"===e.geometry.type&&e.geometry.parameters.widthSegments===a.xSegments&&e.geometry.parameters.heightSegments===a.ySegments){e.material=a.material,e.scale.x=a.xSize/e.geometry.parameters.width,e.scale.y=a.ySize/e.geometry.parameters.height;for(var f=0,g=e.geometry.vertices.length;f1&&(THREE.Terrain.Step(c,b.steps),THREE.Terrain.Smooth(c,b)),THREE.Terrain.Clamp(c,b),"function"==typeof b.after&&b.after(c,b),a.geometry.verticesNeedUpdate=!0,a.geometry.normalsNeedUpdate=!0,a.geometry.computeBoundingSphere(),a.geometry.computeFaceNormals(),a.geometry.computeVertexNormals()},THREE.Terrain.NONE=0,THREE.Terrain.GEOMIPMAP=1,THREE.Terrain.GEOCLIPMAP=2,THREE.Terrain.POLYGONREDUCTION=3,THREE.Terrain.toArray2D=function(a,b){var c=new Array(b.xSegments),d=b.xSegments+1,e=b.ySegments+1,f,g;for(f=0;fg&&(g=a[i].z),a[i].zd&&(d=a[f].z);var g=d-c,h="number"!=typeof b.maxHeight?d:b.maxHeight,i="number"!=typeof b.minHeight?c:b.minHeight,j=b.stretch?h:di?c:i,l=j-k;for(j=0&&h+k>=0&&e+l=0&&j+k>=0&&g+l=0&&h+k>=0&&e+li&&(i=a[m].z))}var n=h*f+e;if("number"==typeof c){var o=.5*(i-j),p=j+o;i=p+o*c,j=p-o*c}d[n]=a[n].z>i?i:a[n].z=h[d].min&&l<=h[d].max){a[c].z=h[d].avg;break}}},THREE.Terrain.Turbulence=function(a,b){for(var c=b.maxHeight-b.minHeight,d=0,e=a.length;d=2;k/=2){var l=Math.round(.5*k),m=Math.round(k),n,o,p,q,r;for(f/=2,n=0;ng?a[o*n+m].z+=f:q<-g?a[o*n+m].z-=f:a[o*n+m].z+=Math.cos(q/g*Math.PI*2)*f}},THREE.Terrain.Hill=function(a,b,c,d){var e=2*b.frequency,f=e*e*10,g=b.maxHeight-b.minHeight,h=g/(e*e),i=g/e,j=Math.min(b.xSize,b.ySize),k=j/(e*e),l=j/e;c=c||THREE.Terrain.Influences.Hill;for(var m={x:0,y:0},n=0;nd)){var g=0,h=0,i=d,j=d,k=Math.floor(d/c),l=-k,m=-k;for(g=0;g<=i;g+=k){for(h=0;h<=j;h+=k){var n=h*i+g;if(f[n]=Math.random()*e,!(l<0&&m<0)){for(var o=f[n],p=f[h*i+(g-k)]||o,q=f[(h-k)*i+g]||o,r=f[(h-k)*i+(g-k)]||o,s=l;sMath.random())}else s=b.spread(j[r.a],q,r,m,o);if(s){if(r.normal.angleTo(l)>b.maxSlope)continue;var u=b.mesh.clone();if(u.position.copy(j[r.a]).add(j[r.b]).add(j[r.c]).divideScalar(3),b.maxTilt>0){var v=u.position.clone().add(r.normal);u.lookAt(v);var w=r.normal.angleTo(l);if(w>b.maxTilt){var x=b.maxTilt/w;u.rotation.x*=x,u.rotation.y*=x,u.rotation.z*=x}}if(u.rotation.x+=.5*Math.PI,u.rotateY(2*Math.random()*Math.PI),b.sizeVariance){var y=Math.random()*i-b.sizeVariance;u.scale.x=u.scale.z=1+y,u.scale.y+=y}k.push(u)}}var z,A;if(b.mesh.geometry instanceof THREE.Geometry){var B=new THREE.Geometry;for(z=0,A=k.length;zd)&&(g[h]=1);return function(){return g}},THREE.Terrain.Influences={Mesa:function(a){return 1.25*Math.min(.8,Math.exp(-a*a))},Hole:function(a){return-THREE.Terrain.Influences.Mesa(a)},Hill:function(a){return a<0?(a+1)*(a+1)*(3-2*(a+1)):1-a*a*(3-2*a)},Valley:function(a){return-THREE.Terrain.Influences.Hill(a)},Dome:function(a){return-(a+1)*(a-1)},Flat:function(a){return 0},Volcano:function(a){return.94-.32*(Math.abs(2*a)+Math.cos(2*Math.PI*Math.abs(a)+.4))}},THREE.Terrain.Influence=function(a,b,c,d,e,f,g,h,i){c=c||THREE.Terrain.Influences.Hill,d=void 0===d?.5:d,e=void 0===e?.5:e,f=void 0===f?64:f,g=void 0===g?64:g,h=void 0===h?THREE.NormalBlending:h,i=i||THREE.Terrain.EaseIn;for(var j=b.xSegments+1,k=b.ySegments+1,l=j*d,m=k*e,n=b.xSize/b.xSegments,o=b.ySize/b.ySegments,p=f/n,q=f/o,r=1/f,s=Math.ceil(l-p),t=Math.floor(l+p),u=Math.ceil(m-q),v=Math.floor(m+q),w=s;wf||void 0===a[y]||(h===THREE.AdditiveBlending?a[y].z+=F:h===THREE.SubtractiveBlending?a[y].z-=F:h===THREE.MultiplyBlending?a[y].z*=F:h===THREE.NoBlending?a[y].z=F:h===THREE.NormalBlending?a[y].z=i(C,D,E)*a[y].z+F:"function"==typeof h&&(a[y].z=h(a[y].z,F,C,D,E)))}}; -//# sourceMappingURL=THREE.Terrain.min.js.map diff --git a/build/THREE.Terrain.min.js.map b/build/THREE.Terrain.min.js.map deleted file mode 100644 index 4dc01ed..0000000 --- a/build/THREE.Terrain.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["THREE.Terrain.js"],"names":["global","Grad","x","y","z","this","fade","t","lerp","a","b","module","noise","prototype","dot2","dot3","grad3","p","perm","Array","gradP","seed","Math","floor","i","v","random","F2","sqrt","G2","F3","G3","simplex","xin","yin","n0","n1","n2","s","j","x0","y0","i1","j1","x1","y1","x2","y2","gi0","gi1","gi2","t0","t1","t2","perlin","X","Y","n00","n01","n10","n11","u","THREE","Terrain","options","defaultOptions","after","easing","Linear","heightmap","DiamondSquare","material","maxHeight","minHeight","optimization","NONE","frequency","steps","stretch","turbulent","useBufferGeometry","xSegments","xSize","ySegments","ySize","_mesh","opt","hasOwnProperty","MeshBasicMaterial","color","scene","Object3D","rotation","PI","mesh","geometry","type","parameters","widthSegments","heightSegments","scale","width","height","l","vertices","length","Mesh","PlaneGeometry","HTMLCanvasElement","Image","fromHeightmap","console","warn","Normalize","BufferGeometry","fromGeometry","add","Turbulence","Step","Smooth","Clamp","verticesNeedUpdate","normalsNeedUpdate","computeBoundingSphere","computeFaceNormals","computeVertexNormals","GEOMIPMAP","GEOCLIPMAP","POLYGONREDUCTION","toArray2D","tgt","xl","yl","Float64Array","fromArray2D","src","toArray1D","fromArray1D","min","heightmapArray","method","arr","EaseIn","EaseOut","EaseInOut","InEaseOut","EaseInWeak","pow","EaseInStrong","g","canvas","document","createElement","context","getContext","rows","cols","spread","drawImage","data","getImageData","row","col","idx","toHeightmap","hasMax","hasMin","max","Infinity","max2","min2","k","d","createImageData","round","putImageData","actualRange","optMax","optMin","targetMax","targetMin","range","Edges","direction","distance","edges","numXSegments","numYSegments","peak","multiplier","k1","k2","top","bottom","left","right","RadialEdges","xl2","yl2","xSegmentSize","ySegmentSize","edgeRadius","vertexDistance","weight","sum","c","n","m","key","w","SmoothMedian","neighborValues","neighborKeys","sortByValue","push","sort","halfKey","median","SmoothConservative","kk","halfdiff","middle","levels","inc","heights","buckets","subset","slice","bl","avg","startHeight","abs","MultiPass","passes","clonedOptions","amp","amplitude","move","Curve","curve","scalar","Cosine","frequencyScalar","phase","cos","CosineLayers","segments","nextPowerOfTwo","size","smoothing","half","whole","e","Fault","iterations","displacement","smoothDistance","sin","Hill","feature","shape","numFeatures","heightRange","smallerSideLength","minRadius","maxRadius","Influences","coords","radius","maxX","maxY","Influence","AdditiveBlending","HillIsland","island","theta","deposit","currentKey","neighborKey","Particles","xDeviation","yDeviation","Perlin","divisor","PerlinDiamond","o","PerlinLayers","Simplex","SimplexLayers","WhiteNoise","lastX","lastY","px","py","r1","r2","kg","kd","Value","Weierstrass","dir1","dir2","r11","r12","r13","r14","r21","r22","r23","r24","exp","generateBlendedMaterial","textures","glslifyNumber","uniforms","UniformsUtils","merge","ShaderLib","lambert","declare","assign","t0Repeat","texture","repeat","t0Offset","offset","wrapS","wrapT","RepeatWrapping","needsUpdate","value","glsl","useLevels","tiRepeat","tiOffset","blendAmount","params","fog","lights","vertexShader","replace","fragmentShader","ShaderChunk","common","packing","dithering_pars_fragment","color_pars_fragment","uv_pars_fragment","uv2_pars_fragment","map_pars_fragment","alphamap_pars_fragment","aomap_pars_fragment","lightmap_pars_fragment","emissivemap_pars_fragment","envmap_pars_fragment","bsdfs","lights_pars_begin","lights_pars_maps","fog_pars_fragment","shadowmap_pars_fragment","shadowmask_pars_fragment","specularmap_pars_fragment","logdepthbuf_pars_fragment","clipping_planes_pars_fragment","clipping_planes_fragment","logdepthbuf_fragment","map_fragment","color_fragment","alphamap_fragment","alphatest_fragment","specularmap_fragment","emissivemap_fragment","lightmap_fragment","aomap_fragment","normal_flip","envmap_fragment","tonemapping_fragment","encodings_fragment","fog_fragment","premultiplied_alpha_fragment","dithering_fragment","join","ShaderMaterial","ScatterMeshes","error","smoothSpread","sizeVariance","randomness","maxSlope","maxTilt","h","spreadIsNumber","randomHeightmap","spreadRange","doubleSizeVariance","meshes","up","clone","applyAxisAngle","Vector3","f","faces","place","rv","normal","angleTo","position","copy","divideScalar","lookAt","tiltAngle","ratio","rotateY","variance","Geometry","updateMatrix","matrix","ScatterHelper","skip","threshold","Mesa","Hole","Valley","Dome","Flat","Volcano","r","NormalBlending","vx","vy","xw","yw","rx","ry","xs","ceil","xe","ys","ye","fdx","fdy","fd","fdr","fdxr","fdyr","SubtractiveBlending","MultiplyBlending","NoBlending"],"mappings":"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","file":"THREE.Terrain.min.js"} \ No newline at end of file diff --git a/build/THREE.Terrain.min.map b/build/THREE.Terrain.min.map deleted file mode 100644 index ef6cac4..0000000 --- a/build/THREE.Terrain.min.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"THREE.Terrain.min.js","sources":["THREE.Terrain.js"],"names":["global","Grad","x","y","z","this","fade","t","lerp","a","b","module","noise","prototype","dot2","dot3","grad3","p","perm","Array","gradP","seed","Math","floor","i","v","random","F2","sqrt","G2","F3","G3","simplex","xin","yin","n0","n1","n2","s","j","x0","y0","i1","j1","x1","y1","x2","y2","gi0","gi1","gi2","t0","t1","t2","perlin","X","Y","n00","n01","n10","n11","u","THREE","Terrain","options","defaultOptions","after","easing","Linear","heightmap","DiamondSquare","material","maxHeight","minHeight","optimization","NONE","frequency","steps","stretch","turbulent","useBufferGeometry","xSegments","xSize","ySegments","ySize","opt","hasOwnProperty","MeshBasicMaterial","color","scene","Object3D","rotation","PI","mesh","Mesh","PlaneGeometry","HTMLCanvasElement","Image","fromHeightmap","geometry","vertices","console","warn","Normalize","BufferGeometry","fromGeometry","add","Turbulence","Step","Smooth","Clamp","verticesNeedUpdate","normalsNeedUpdate","computeBoundingSphere","computeFaceNormals","computeVertexNormals","GEOMIPMAP","GEOCLIPMAP","POLYGONREDUCTION","toArray2D","tgt","xl","yl","Float64Array","fromArray2D","src","length","toArray1D","l","fromArray1D","min","heightmapArray","method","arr","EaseIn","EaseOut","EaseInOut","InEaseOut","EaseInStrong","g","canvas","document","createElement","context","getContext","rows","cols","spread","width","height","drawImage","data","getImageData","row","col","idx","toHeightmap","hasMax","hasMin","max","Infinity","max2","min2","k","d","createImageData","round","putImageData","actualRange","optMax","optMin","targetMax","targetMin","range","Edges","direction","distance","edges","numXSegments","numYSegments","peak","multiplier","k1","k2","top","bottom","left","right","RadialEdges","xl2","yl2","xSegmentSize","ySegmentSize","edgeRadius","vertexDistance","weight","sum","c","n","m","key","w","SmoothMedian","neighborValues","neighborKeys","sortByValue","push","sort","halfKey","median","SmoothConservative","kk","halfdiff","middle","levels","inc","heights","buckets","pow","subset","slice","bl","avg","startHeight","abs","MultiPass","passes","clonedOptions","amp","amplitude","move","Cosine","frequencyScalar","phase","cos","CosineLayers","segments","size","smoothing","half","whole","e","Fault","iterations","displacement","smoothDistance","sin","Hill","feature","shape","numFeatures","heightRange","smallerSideLength","minRadius","maxRadius","Influences","coords","radius","maxX","maxY","Influence","AdditiveBlending","HillIsland","island","theta","deposit","currentKey","r","neighborKey","Particles","xDeviation","yDeviation","Perlin","divisor","PerlinDiamond","o","PerlinLayers","Simplex","SimplexLayers","WhiteNoise","scale","lastX","lastY","px","py","r1","r2","kg","kd","Value","Weierstrass","dir1","dir2","r11","r12","r13","r14","r21","r22","r23","r24","exp","generateBlendedMaterial","textures","uniforms","UniformsUtils","merge","ShaderLib","lambert","declare","assign","wrapS","wrapT","RepeatWrapping","needsUpdate","type","value","texture","glsl","useLevels","blendAmount","params","fog","lights","vertexShader","replace","fragmentShader","ShaderChunk","common","color_pars_fragment","map_pars_fragment","alphamap_pars_fragment","lightmap_pars_fragment","envmap_pars_fragment","fog_pars_fragment","shadowmap_pars_fragment","specularmap_pars_fragment","logdepthbuf_pars_fragment","logdepthbuf_fragment","map_fragment","color_fragment","alphamap_fragment","alphatest_fragment","specularmap_fragment","lightmap_fragment","envmap_fragment","shadowmap_fragment","linear_to_gamma_fragment","fog_fragment","join","ShaderMaterial","ScatterMeshes","error","smoothSpread","sizeVariance","randomness","maxSlope","maxTilt","h","spreadIsNumber","randomHeightmap","spreadRange","doubleSizeVariance","meshes","up","clone","applyAxisAngle","Vector3","f","faces","place","rv","normal","angleTo","position","copy","divideScalar","lookAt","tiltAngle","ratio","rotateY","variance","Geometry","updateMatrix","matrix","ScatterHelper","skip","threshold","Mesa","Hole","Valley","Dome","Flat","Volcano","NormalBlending","vx","vy","xw","yw","rx","ry","xs","ceil","xe","ys","ye","fdx","fdy","fd","fdr","fdxr","fdyr","SubtractiveBlending","MultiplyBlending","NoBlending"],"mappings":"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"} \ No newline at end of file diff --git a/demo/index.js b/demo/index.js index 0ef2d63..d5d7af5 100644 --- a/demo/index.js +++ b/demo/index.js @@ -211,7 +211,7 @@ function setupDatGui() { _mesh: typeof terrainScene === 'undefined' ? null : terrainScene.children[0], // internal only }; scene.remove(terrainScene); - terrainScene = THREE.Terrain(o); + terrainScene = THREE.Terrain.Terrain(o); applySmoothing(that.smoothing, o); scene.add(terrainScene); skyDome.visible = sand.visible = water.visible = that.texture != 'Wireframe'; diff --git a/index.html b/index.html index 72565c1..fc640da 100644 --- a/index.html +++ b/index.html @@ -127,12 +127,7 @@

Fitted Plane

- - - - - - + diff --git a/package.json b/package.json index 6659153..e7de54e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "description": "Extends the Three.js web-based 3D graphics framework to support generating random terrains and rendering terrain from predetermined heightmaps.", "homepage": "https://github.com/IceCreamYou/THREE.Terrain", "bugs": "https://github.com/IceCreamYou/THREE.Terrain/issues", - "main": "build/THREE.Terrain.min.js", + "browser": "dist/THREE.Terrain.umd.js", + "main": "dist/THREE.Terrain.cjs.js", + "module": "dist/THREE.Terrain.esm.js", + "types": "dist/main.d.ts", "directories": { "lib": "build", "example": "demo" @@ -26,11 +29,28 @@ "license": "MIT", "readmeFilename": "README.md", "devDependencies": { - "grunt": "~0.4.5", - "grunt-contrib-concat": "~0.5.1", - "grunt-contrib-jshint": "~0.11.2", - "grunt-contrib-uglify": "~0.9.1", - "grunt-jscs": "~2.1.0", - "grunt-contrib-watch": "^0.6.1" - } + "@typescript-eslint/eslint-plugin": "^1.11.0", + "@typescript-eslint/parser": "^1.11.0", + "eslint": "^5.16.0", + "rimraf": "^3.0.0", + "rollup": "^1.16.6", + "rollup-plugin-sourcemaps": "^0.4.2", + "rollup-plugin-terser": "^5.1.1", + "rollup-plugin-typescript": "^1.0.1", + "serve": "^11.0.2", + "tslib": "^1.10.0", + "typescript": "^3.5.2" + }, + "scripts": { + "ts:defs": "tsc --declaration --outDir ./dist --emitDeclarationOnly", + "build": "rimraf dist && npm run ts:defs && rollup -c", + "lint": "eslint src/*.ts", + "serve": "serve -l 8080" + }, + "dependencies": { + "three": "^0.105.2" + }, + "files": [ + "dist" + ] } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..bff4f6a --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,41 @@ +import { terser } from 'rollup-plugin-terser'; +import typescript from 'rollup-plugin-typescript'; +import sourceMaps from 'rollup-plugin-sourcemaps' +import pkg from './package.json'; + +export default [ + { + input: 'src/main.ts', + external: ['three'], + plugins: [ + typescript(), + terser() + ], + output: [ + { + name: 'THREE.Terrain', + file: pkg.browser, + format: 'umd', + sourcemap: true, + globals: { + three: 'THREE' + }, + } + ] + }, + { + input: 'src/main.ts', + external: ['three'], + watch: { + include: 'src/**', + }, + plugins: [ + typescript(), + sourceMaps() + ], + output: [ + { file: pkg.main, format: 'cjs', sourcemap: true }, + { file: pkg.module, format: 'es', sourcemap: true } + ] + } +]; diff --git a/src/analysis.js b/src/analysis.ts similarity index 81% rename from src/analysis.js rename to src/analysis.ts index cf519af..9506da8 100644 --- a/src/analysis.js +++ b/src/analysis.ts @@ -1,4 +1,6 @@ -(function() { +import { TerrainOptions } from "./basicTypes"; +import { Mesh, PlaneGeometry, Vector3 } from "three"; +import { toArray1D } from "./core"; /** * Analyze a terrain using statistical measures. @@ -13,14 +15,15 @@ * @return {Object} * An object containing statistical information about the terrain. */ -THREE.Terrain.Analyze = function(mesh, options) { - if (mesh.geometry.vertices.length < 3) { +export function Analyze(mesh: Mesh, options: TerrainOptions) { + const geometry: PlaneGeometry = mesh.geometry as PlaneGeometry; + if (geometry.vertices.length < 3) { throw new Error('Not enough vertices to analyze'); } - var sortNumeric = function(a, b) { return a - b; }, + var sortNumeric = function (a: number, b: number) { return a - b; }, elevations = Array.prototype.sort.call( - THREE.Terrain.toArray1D(mesh.geometry.vertices), + toArray1D(geometry.vertices), sortNumeric ), numVertices = elevations.length, @@ -32,9 +35,9 @@ THREE.Terrain.Analyze = function(mesh, options) { pearsonSkewElevation = 0, groeneveldMeedenSkewElevation = 0, kurtosisElevation = 0, - up = mesh.up.clone().applyAxisAngle(new THREE.Vector3(1, 0, 0), 0.5*Math.PI), // correct for mesh rotation - slopes = mesh.geometry.faces - .map(function(v) { return v.normal.angleTo(up) * 180 / Math.PI; }) + up = mesh.up.clone().applyAxisAngle(new Vector3(1, 0, 0), 0.5 * Math.PI), // correct for mesh rotation + slopes = geometry.faces + .map(function (v) { return v.normal.angleTo(up) * 180 / Math.PI; }) .sort(sortNumeric), numFaces = slopes.length, maxSlope = percentile(slopes, 1), @@ -42,7 +45,7 @@ THREE.Terrain.Analyze = function(mesh, options) { medianSlope = percentile(slopes, 0.5), meanSlope = mean(slopes), centroid = mesh.position.clone().setZ(meanElevation), - fittedPlaneNormal = getFittedPlaneNormal(mesh.geometry.vertices, centroid), + fittedPlaneNormal = getFittedPlaneNormal(geometry.vertices, centroid), fittedPlaneSlope = fittedPlaneNormal.angleTo(up) * 180 / Math.PI, stdevSlope = 0, pearsonSkewSlope = 0, @@ -90,13 +93,13 @@ THREE.Terrain.Analyze = function(mesh, options) { for (var j = 0; j < yl; j++) { var neighborhoodMax = -Infinity, neighborhoodMin = Infinity, - v = mesh.geometry.vertices[j*xl + ii].z, + v = geometry.vertices[j * xl + ii].z, sum = 0, c = 0; for (var n = -1; n <= 1; n++) { for (var m = -1; m <= 1; m++) { - if (ii+m >= 0 && j+n >= 0 && ii+m < xl && j+n < yl && !(n === 0 && m === 0)) { - var val = mesh.geometry.vertices[(j+n)*xl + ii + m].z; + if (ii + m >= 0 && j + n >= 0 && ii + m < xl && j + n < yl && !(n === 0 && m === 0)) { + var val = geometry.vertices[(j + n) * xl + ii + m].z; sum += val; c++; if (val > neighborhoodMax) neighborhoodMax = val; @@ -110,7 +113,7 @@ THREE.Terrain.Analyze = function(mesh, options) { } tri = Math.sqrt(tri / numVertices); // ceil(n/2)*ceil(m/2) is the max # of local maxima or minima in an n*m grid - jaggedness /= Math.ceil((options.xSegments+1) * 0.5) * Math.ceil((options.ySegments+1) * 0.5) * 2; + jaggedness /= Math.ceil((options.xSegments + 1) * 0.5) * Math.ceil((options.ySegments + 1) * 0.5) * 2; return { elevation: { @@ -133,9 +136,9 @@ THREE.Terrain.Analyze = function(mesh, options) { options.minHeight, options.maxHeight ), - percentile: function(p) { return percentile(elevations, p); }, - percentRank: function(v) { return percentRank(elevations, v); }, - drawHistogram: function(canvas, bucketCount) { + percentile: function (p: number) { return percentile(elevations, p); }, + percentRank: function (v: number) { return percentRank(elevations, v); }, + drawHistogram: function (canvas: HTMLCanvasElement, bucketCount: number) { drawHistogram( bucketNumbersLinearly( elevations, @@ -164,9 +167,9 @@ THREE.Terrain.Analyze = function(mesh, options) { groeneveldMeedenSkew: groeneveldMeedenSkewSlope, kurtosis: kurtosisSlope, modes: getModes(slopes, 90, 0, 90), - percentile: function(p) { return percentile(slopes, p); }, - percentRank: function(v) { return percentRank(slopes, v); }, - drawHistogram: function(canvas, bucketCount) { + percentile: function (p: number) { return percentile(slopes, p); }, + percentRank: function (v: number) { return percentRank(slopes, v); }, + drawHistogram: function (canvas: HTMLCanvasElement, bucketCount: number) { drawHistogram( bucketNumbersLinearly( slopes, @@ -191,7 +194,7 @@ THREE.Terrain.Analyze = function(mesh, options) { normal: fittedPlaneNormal, slope: fittedPlaneSlope, pctExplained: percentVariationExplainedByFittedPlane( - mesh.geometry.vertices, + geometry.vertices, centroid, fittedPlaneNormal, options.maxHeight - options.minHeight @@ -214,7 +217,7 @@ THREE.Terrain.Analyze = function(mesh, options) { * @return {Number} * The value at the given percentile in the given array. */ -function percentile(arr, p) { +function percentile(arr: Float32Array | number[], p: number) { if (arr.length === 0) return 0; if (typeof p !== 'number') throw new TypeError('p must be a number'); if (p <= 0) return arr[0]; @@ -240,7 +243,7 @@ function percentile(arr, p) { * @return {Number} * The percentile at the given value in the given array. */ -function percentRank(arr, v) { +function percentRank(arr: number[], v: number) { if (typeof v !== 'number') throw new TypeError('v must be a number'); for (var i = 0, l = arr.length; i < l; i++) { if (v <= arr[i]) { @@ -248,8 +251,8 @@ function percentRank(arr, v) { i++; } if (i === 0) return 0; - if (v !== arr[i-1]) { - i += (v - arr[i-1]) / (arr[i] - arr[i-1]); + if (v !== arr[i - 1]) { + i += (v - arr[i - 1]) / (arr[i] - arr[i - 1]); } return i / l; } @@ -268,7 +271,7 @@ function percentRank(arr, v) { * @return {THREE.Vector3} * The normal vector of the fitted plane. */ -function getFittedPlaneNormal(points, centroid) { +function getFittedPlaneNormal(points: Vector3[], centroid: Vector3) { var n = points.length, xx = 0, xy = 0, @@ -278,7 +281,7 @@ function getFittedPlaneNormal(points, centroid) { zz = 0; if (n < 3) throw new Error('At least three points are required to fit a plane'); - var r = new THREE.Vector3(); + var r = new Vector3(); for (var i = 0, l = points.length; i < l; i++) { r.copy(points[i]).sub(centroid); xx += r.x * r.x; @@ -289,30 +292,30 @@ function getFittedPlaneNormal(points, centroid) { zz += r.z * r.z; } - var xDeterminant = yy*zz - yz*yz, - yDeterminant = xx*zz - xz*xz, - zDeterminant = xx*yy - xy*xy, + var xDeterminant = yy * zz - yz * yz, + yDeterminant = xx * zz - xz * xz, + zDeterminant = xx * yy - xy * xy, maxDeterminant = Math.max(xDeterminant, yDeterminant, zDeterminant); if (maxDeterminant <= 0) throw new Error("The points don't span a plane"); if (maxDeterminant === xDeterminant) { r.set( 1, - (xz*yz - xy*zz) / xDeterminant, - (xy*yz - xz*yy) / xDeterminant + (xz * yz - xy * zz) / xDeterminant, + (xy * yz - xz * yy) / xDeterminant ); } else if (maxDeterminant === yDeterminant) { r.set( - (yz*xz - xy*zz) / yDeterminant, + (yz * xz - xy * zz) / yDeterminant, 1, - (xy*xz - yz*xx) / yDeterminant + (xy * xz - yz * xx) / yDeterminant ); } else if (maxDeterminant === zDeterminant) { r.set( - (yz*xy - xz*yy) / zDeterminant, - (xz*xy - yz*xx) / zDeterminant, + (yz * xy - xz * yy) / zDeterminant, + (xz * xy - yz * xx) / zDeterminant, 1 ); } @@ -333,7 +336,7 @@ function getFittedPlaneNormal(points, centroid) { * * @return {Number[][]} An array of buckets of numbers. */ -function bucketNumbersLinearly(data, bucketCount, min, max) { +function bucketNumbersLinearly(data: number[], bucketCount: number, min: number, max: number) { var i = 0, l = data.length; // If min and max aren't given, set them to the highest and lowest data values @@ -355,9 +358,9 @@ function bucketNumbersLinearly(data, bucketCount, min, max) { for (i = 0; i < l; i++) { // Buckets include the lower bound but not the higher bound, except the top bucket try { - if (data[i] === max) buckets[bucketCount-1].push(data[i]); + if (data[i] === max) buckets[bucketCount - 1].push(data[i]); else buckets[((data[i] - min) / inc) | 0].push(data[i]); - } catch(e) { + } catch (e) { console.warn('Numbers in the data are outside of the min and max values used to bucket the data.'); } } @@ -379,10 +382,10 @@ function bucketNumbersLinearly(data, bucketCount, min, max) { * @return {Number[]} * An array containing the bucketed mode(s). */ -function getModes(data, bucketCount, min, max) { +function getModes(data: number[], bucketCount: number, min: number, max: number) { var buckets = bucketNumbersLinearly(data, bucketCount, min, max), maxLen = 0, - modes = []; + modes: number[] = []; for (var i = 0, l = buckets.length; i < l; i++) { if (buckets[i].length > maxLen) { maxLen = buckets[i].length; @@ -409,7 +412,7 @@ function getModes(data, bucketCount, min, max) { * @param {String} [append=''] * A string to append to the bar labels. Defaults to the empty string. */ -function drawHistogram(buckets, canvas, minV, maxV, append) { +function drawHistogram(buckets: number[][], canvas: HTMLCanvasElement, minV: number, maxV: number, append: string = '') { var context = canvas.getContext('2d'), width = 280, height = 180, @@ -419,9 +422,8 @@ function drawHistogram(buckets, canvas, minV, maxV, append) { min = typeof minV === 'undefined' ? Infinity : minV, l = buckets.length, i; - canvas.width = width + border*2; - canvas.height = height + border*2; - if (typeof append === 'undefined') append = ''; + canvas.width = width + border * 2; + canvas.height = height + border * 2; // If max or min is not set, set them to the highest/lowest value. if (max === -Infinity || min === Infinity) { @@ -452,9 +454,9 @@ function drawHistogram(buckets, canvas, minV, maxV, append) { unitSizeX = (width - (buckets.length + 1) * separator) / buckets.length; if (unitSizeX >= 1) unitSizeX = Math.floor(unitSizeX); if (unitSizeY >= 1) unitSizeY = Math.floor(unitSizeY); - context.fillStyle = 'rgba(13, 42, 64, 1)'; + context!.fillStyle = 'rgba(13, 42, 64, 1)'; for (i = 0; i < l; i++) { - context.fillRect( + context!.fillRect( border + separator + i * (unitSizeX + separator), border + height - (separator + buckets[i].length * unitSizeY), unitSizeX, @@ -463,33 +465,33 @@ function drawHistogram(buckets, canvas, minV, maxV, append) { } // Draw the label text on the bar. - context.fillStyle = 'rgba(144, 176, 192, 1)'; - context.font = '12px Arial'; + context!.fillStyle = 'rgba(144, 176, 192, 1)'; + context!.font = '12px Arial'; for (i = 0; i < l; i++) { var text = Math.floor(((i + 0.5) / buckets.length) * (max - min) + min) + '' + append; - context.fillText( + context!.fillText( text, - border + separator + i * (unitSizeX + separator) + Math.floor((unitSizeX - context.measureText(text).width) * 0.5), + border + separator + i * (unitSizeX + separator) + Math.floor((unitSizeX - context!.measureText(text).width) * 0.5), border + height - 8, unitSizeX ); } - context.fillText( + context!.fillText( Math.round(100 * maxBucketSize / n) + '%', border + separator, border + separator + 6 ); // Draw axes. - context.strokeStyle = 'rgba(13, 42, 64, 1)'; - context.lineWidth = 2; - context.beginPath(); - context.moveTo(border, border); - context.lineTo(border, height + border); - context.moveTo(border, height + border); - context.lineTo(width + border, height + border); - context.stroke(); + context!.strokeStyle = 'rgba(13, 42, 64, 1)'; + context!.lineWidth = 2; + context!.beginPath(); + context!.moveTo(border, border); + context!.lineTo(border, height + border); + context!.moveTo(border, height + border); + context!.lineTo(width + border, height + border); + context!.stroke(); } /** @@ -515,20 +517,20 @@ function drawHistogram(buckets, canvas, minV, maxV, append) { * explains the variation in terrain elevation. 1 means entirely explained; 0 * means not explained at all. */ -function percentVariationExplainedByFittedPlane(vertices, centroid, normal, range) { +function percentVariationExplainedByFittedPlane(vertices: Vector3[], centroid: Vector3, normal: Vector3, range: number) { var numVertices = vertices.length, diff = 0; for (var i = 0; i < numVertices; i++) { var fittedZ = Math.sqrt( - (vertices[i].x - centroid.x) * (vertices[i].x - centroid.x) + - (vertices[i].y - centroid.y) * (vertices[i].y - centroid.y) - ) * Math.tan(normal.z * Math.PI) + centroid.z; + (vertices[i].x - centroid.x) * (vertices[i].x - centroid.x) + + (vertices[i].y - centroid.y) * (vertices[i].y - centroid.y) + ) * Math.tan(normal.z * Math.PI) + centroid.z; diff += (vertices[i].z - fittedZ) * (vertices[i].z - fittedZ); } return 1 - Math.sqrt(diff / numVertices) * 2 / range; } -function mean(data) { +function mean(data: number[]) { var sum = 0, l = data.length; for (var i = 0; i < l; i++) { @@ -536,5 +538,3 @@ function mean(data) { } return sum / l; } - -})(); diff --git a/src/basicTypes.ts b/src/basicTypes.ts new file mode 100644 index 0000000..4fc4b8a --- /dev/null +++ b/src/basicTypes.ts @@ -0,0 +1,125 @@ +import { Vector3, Material } from 'three'; + +export type EasingFunction = (x: number) => number; + +export type HeightmapFunction = ((g: Vector3[], options: TerrainOptions) => void); + +export interface TerrainOptions { + after: ((v: Vector3[], options: TerrainOptions) => void) | null; + easing: EasingFunction; + heightmap: HTMLCanvasElement | ImageBitmap | HeightmapFunction; + material: Material | null; + maxHeight: number; + minHeight: number; + optimization: Optimization; + frequency: number; + steps: number; + stretch: boolean; + turbulent: boolean; + useBufferGeometry: boolean; + xSegments: number; + xSize: number; + ySegments: number; + ySize: number; +} + +/** + * Optimization types. + * + * Note that none of these are implemented right now. They should be done as + * shaders so that they execute on the GPU, and the resulting scene would need + * to be updated every frame to adjust to the camera's position. + * + * Further reading: + * - http://vterrain.org/LOD/Papers/ + * - http://vterrain.org/LOD/Implementations/ + * + * GEOMIPMAP: The terrain plane should be split into sections, each with their + * own LODs, for screen-space occlusion and detail reduction. Intermediate + * vertices on higher-detail neighboring sections should be interpolated + * between neighbor edge vertices in order to match with the edge of the + * lower-detail section. The number of sections should be around sqrt(segments) + * along each axis. It's unclear how to make materials stretch across segments. + * Possible example (I haven't looked too much into it) at + * https://github.com/felixpalmer/lod-terrain/tree/master/js/shaders + * + * GEOCLIPMAP: The terrain should be composed of multiple donut-shaped sections + * at decreasing resolution as the radius gets bigger. When the player moves, + * the sections should morph so that the detail "follows" the player around. + * There is an implementation of geoclipmapping at + * https://github.com/CodeArtemis/TriggerRally/blob/unified/server/public/scripts/client/terrain.coffee + * and a tutorial on morph targets at + * http://nikdudnik.com/making-3d-gfx-for-the-cinema-on-low-budget-and-three-js/ + * + * POLYGONREDUCTION: Combine areas that are relatively coplanar into larger + * polygons as described at http://www.shamusyoung.com/twentysidedtale/?p=142. + * This method can be combined with the others if done very carefully, or it + * can be adjusted to be more aggressive at greater distance from the camera + * (similar to combining with geomipmapping). + * + * If these do get implemented, here is the option description to add to the + * `THREE.Terrain` docblock: + * + * - `optimization`: the type of optimization to apply to the terrain. If + * an optimization is applied, the number of segments along each axis that + * the terrain should be divided into at the most detailed level should + * equal (n * 2^(LODs-1))^2 - 1, for arbitrary n, where LODs is the number + * of levels of detail desired. Valid values include: + * + * - `THREE.Terrain.NONE`: Don't apply any optimizations. This is the + * default. + * - `THREE.Terrain.GEOMIPMAP`: Divide the terrain into evenly-sized + * sections with multiple levels of detail. For each section, + * display a level of detail dependent on how close the camera is. + * - `THREE.Terrain.GEOCLIPMAP`: Divide the terrain into donut-shaped + * sections, where detail decreases as the radius increases. The + * rings then morph to "follow" the camera around so that the camera + * is always at the center, surrounded by the most detail. + */ + +export enum Optimization { + NONE = 0, + GEOMIPMAP = 1, + GEOCLIPMAP = 2, + POLYGONREDUCTION = 3, +}; + +/** + * A function describing the feature. The function should accept one + * parameter representing the distance from the feature's origin expressed as + * a number between -1 and 1 inclusive. Optionally it can accept a second and + * third parameter, which are the x- and y- distances from the feature's + * origin, respectively. It should return a number between -1 and 1 + * representing the height of the feature at the given coordinate. + * `THREE.Terrain.Influences` contains some useful functions for this + * purpose. + */ +export type FeatureFunction = (distanceToFeatureOrigin: number, xDistanceToFeatureOrigin?: number, yDistanceToFeatureOrigin?: number) => number; + +/** + * A function that determines the "falloff" of the feature, i.e. how quickly + * the terrain will get close to its height before the feature was applied as + * the distance increases from the feature's location. It does this by + * interpolating the height of each vertex along a curve. Valid values + * include `THREE.Terrain.Linear`, `THREE.Terrain.EaseIn`, + * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, + * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float + * between 0 and 1 representing the distance to the feature origin and + * returns a float between 0 and 1 with the adjusted distance. (Custom + * functions can also accept optional second and third parameters, which are + * the x- and y-distances to the feature origin, respectively.) + */ +export type FalloffFunction = (distanceToFeatureOrigin: number, xDistanceToFeatureOrigin?: number, yDistanceToFeatureOrigin?: number) => number; + +/** + * Any function that takes the terrain's current height, the feature's + * displacement at a vertex, and the vertex's distance from the feature + * origin, and returns the new height for that vertex. (If a custom function + * is passed, it can take optional fourth and fifth parameters, which are the + * x- and y-distances from the feature's origin, respectively.) + */ +export type BlendingFunction = ( + currentTerrainHeight: number, featureVertexDisplacement: number, + vertexDistanceFromFeatureOrigin: number, + xDistanceToFeatureOrigin?: number, yDistanceToFeatureOrigin?: number +) => number; diff --git a/src/brownian.js b/src/brownian.ts similarity index 88% rename from src/brownian.js rename to src/brownian.ts index af0f468..703a305 100644 --- a/src/brownian.js +++ b/src/brownian.ts @@ -1,3 +1,9 @@ +import { Vector3 } from 'three'; + +import { TerrainOptions } from './basicTypes'; +import { EaseInWeak } from './core'; +import { Smooth } from './filters'; + /** * Generate random terrain using Brownian motion. * @@ -5,7 +11,7 @@ * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Brownian = function(g, options) { +export function Brownian(g: Vector3[], options: TerrainOptions) { var untouched = [], touched = [], smallerSideSize = Math.min(options.xSize, options.ySize), @@ -39,8 +45,8 @@ THREE.Terrain.Brownian = function(g, options) { // Mark the untouched neighboring vertices to revisit later. for (n = -1; n <= 1; n++) { for (m = -1; m <= 1; m++) { - key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && touched.indexOf(g[key]) === -1 && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl && n && m) { + key = (j + n) * xl + i + m; + if (typeof g[key] !== 'undefined' && touched.indexOf(g[key]) === -1 && i + m >= 0 && j + n >= 0 && i + m < xl && j + n < yl && n && m) { untouched.push(g[key]); } } @@ -113,8 +119,8 @@ THREE.Terrain.Brownian = function(g, options) { c = 0; for (n = -1; n <= 1; n++) { for (m = -1; m <= 1; m++) { - key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && touched.indexOf(g[key]) !== -1 && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl && n && m) { + key = (j + n) * xl + i + m; + if (typeof g[key] !== 'undefined' && touched.indexOf(g[key]) !== -1 && i + m >= 0 && j + n >= 0 && i + m < xl && j + n < yl && n && m) { sum += g[key].z; c++; } @@ -124,12 +130,12 @@ THREE.Terrain.Brownian = function(g, options) { if (!lastAdjust || Math.random() < changeDirectionProbability) { lastAdjust = Math.random(); } - current.z = sum / c + THREE.Terrain.EaseInWeak(lastAdjust) * maxHeightAdjust * 2 - maxHeightAdjust; + current.z = sum / c + EaseInWeak(lastAdjust) * maxHeightAdjust * 2 - maxHeightAdjust; } touched.push(current); } // Erase artifacts. - THREE.Terrain.Smooth(g, options); - THREE.Terrain.Smooth(g, options); + Smooth(g, options); + Smooth(g, options); }; diff --git a/src/core.js b/src/core.js deleted file mode 100644 index 5fe7456..0000000 --- a/src/core.js +++ /dev/null @@ -1,411 +0,0 @@ -/** - * A terrain object for use with the Three.js library. - * - * Usage: `var terrainScene = THREE.Terrain();` - * - * @param {Object} [options] - * An optional map of settings that control how the terrain is constructed - * and displayed. Options include: - * - * - `after`: A function to run after other transformations on the terrain - * produce the highest-detail heightmap, but before optimizations and - * visual properties are applied. Takes two parameters, which are the same - * as those for {@link THREE.Terrain.DiamondSquare}: an array of - * `THREE.Vector3` objects representing the vertices of the terrain, and a - * map of options with the same available properties as the `options` - * parameter for the `THREE.Terrain` function. - * - `easing`: A function that affects the distribution of slopes by - * interpolating the height of each vertex along a curve. Valid values - * include `THREE.Terrain.Linear` (the default), `THREE.Terrain.EaseIn`, - * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, - * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float - * between 0 and 1 and returns a float between 0 and 1. - * - `frequency`: For terrain generation methods that support it (Perlin, - * Simplex, and Worley) the octave of randomness. This basically controls - * how big features of the terrain will be (higher frequencies result in - * smaller features). Often running multiple generation functions with - * different frequencies and heights results in nice detail, as - * the PerlinLayers and SimplexLayers methods demonstrate. (The counterpart - * to frequency, amplitude, is represented by the difference between the - * `maxHeight` and `minHeight` parameters.) Defaults to 2.5. - * - `heightmap`: Either a canvas or pre-loaded image (from the same domain - * as the webpage or served with a CORS-friendly header) representing - * terrain height data (lighter pixels are higher); or a function used to - * generate random height data for the terrain. Valid random functions are - * specified in `generators.js` (or custom functions with the same - * signature). Ideally heightmap images have the same number of pixels as - * the terrain has vertices, as determined by the `xSegments` and - * `ySegments` options, but this is not required. If the heightmap is a - * different size, vertex height values will be interpolated.) Defaults to - * `THREE.Terrain.DiamondSquare`. - * - `material`: a THREE.Material instance used to display the terrain. - * Defaults to `new THREE.MeshBasicMaterial({color: 0xee6633})`. - * - `maxHeight`: the highest point, in Three.js units, that a peak should - * reach. Defaults to 100. Setting to `undefined`, `null`, or `Infinity` - * removes the cap, but this is generally not recommended because many - * generators and filters require a vertical range. Instead, consider - * setting the `stretch` option to `false`. - * - `minHeight`: the lowest point, in Three.js units, that a valley should - * reach. Defaults to -100. Setting to `undefined`, `null`, or `-Infinity` - * removes the cap, but this is generally not recommended because many - * generators and filters require a vertical range. Instead, consider - * setting the `stretch` option to `false`. - * - `steps`: If this is a number above 1, the terrain will be paritioned - * into that many flat "steps," resulting in a blocky appearance. Defaults - * to 1. - * - `stretch`: Determines whether to stretch the heightmap across the - * maximum and minimum height range if the height range produced by the - * `heightmap` property is smaller. Defaults to true. - * - `turbulent`: Whether to perform a turbulence transformation. Defaults to - * false. - * - `useBufferGeometry`: a Boolean indicating whether to use - * THREE.BufferGeometry instead of THREE.Geometry for the Terrain plane. - * Defaults to `false`. - * - `xSegments`: The number of segments (rows) to divide the terrain plane - * into. (This basically determines how detailed the terrain is.) Defaults - * to 63. - * - `xSize`: The width of the terrain in Three.js units. Defaults to 1024. - * Rendering might be slightly faster if this is a multiple of - * `options.xSegments + 1`. - * - `ySegments`: The number of segments (columns) to divide the terrain - * plane into. (This basically determines how detailed the terrain is.) - * Defaults to 63. - * - `ySize`: The length of the terrain in Three.js units. Defaults to 1024. - * Rendering might be slightly faster if this is a multiple of - * `options.ySegments + 1`. - */ -THREE.Terrain = function(options) { - var defaultOptions = { - after: null, - easing: THREE.Terrain.Linear, - heightmap: THREE.Terrain.DiamondSquare, - material: null, - maxHeight: 100, - minHeight: -100, - optimization: THREE.Terrain.NONE, - frequency: 2.5, - steps: 1, - stretch: true, - turbulent: false, - useBufferGeometry: false, - xSegments: 63, - xSize: 1024, - ySegments: 63, - ySize: 1024, - _mesh: null, // internal only - }; - options = options || {}; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - options[opt] = typeof options[opt] === 'undefined' ? defaultOptions[opt] : options[opt]; - } - } - options.material = options.material || new THREE.MeshBasicMaterial({ color: 0xee6633 }); - - // Encapsulating the terrain in a parent object allows us the flexibility - // to more easily have multiple meshes for optimization purposes. - var scene = new THREE.Object3D(); - // Planes are initialized on the XY plane, so rotate the plane to make it lie flat. - scene.rotation.x = -0.5 * Math.PI; - - // Create the terrain mesh. - // To save memory, it is possible to re-use a pre-existing mesh. - var mesh = options._mesh; - if (mesh && mesh.geometry.type === 'PlaneGeometry' && - mesh.geometry.parameters.widthSegments === options.xSegments && - mesh.geometry.parameters.heightSegments === options.ySegments) { - mesh.material = options.material; - mesh.scale.x = options.xSize / mesh.geometry.parameters.width; - mesh.scale.y = options.ySize / mesh.geometry.parameters.height; - for (var i = 0, l = mesh.geometry.vertices.length; i < l; i++) { - mesh.geometry.vertices[i].z = 0; - } - } - else { - mesh = new THREE.Mesh( - new THREE.PlaneGeometry(options.xSize, options.ySize, options.xSegments, options.ySegments), - options.material - ); - } - delete options._mesh; // Remove the reference for GC - - // Assign elevation data to the terrain plane from a heightmap or function. - if (options.heightmap instanceof HTMLCanvasElement || options.heightmap instanceof Image) { - THREE.Terrain.fromHeightmap(mesh.geometry.vertices, options); - } - else if (typeof options.heightmap === 'function') { - options.heightmap(mesh.geometry.vertices, options); - } - else { - console.warn('An invalid value was passed for `options.heightmap`: ' + options.heightmap); - } - THREE.Terrain.Normalize(mesh, options); - - if (options.useBufferGeometry) { - mesh.geometry = (new THREE.BufferGeometry()).fromGeometry(mesh.geometry); - } - - // lod.addLevel(mesh, options.unit * 10 * Math.pow(2, lodLevel)); - - scene.add(mesh); - return scene; -}; - -/** - * Normalize the terrain after applying a heightmap or filter. - * - * This applies turbulence, steps, and height clamping; calls the `after` - * callback; updates normals and the bounding sphere; and marks vertices as - * dirty. - * - * @param {THREE.Mesh} mesh - * The terrain mesh. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid options are the same as for {@link THREE.Terrain}(). - */ -THREE.Terrain.Normalize = function(mesh, options) { - var v = mesh.geometry.vertices; - if (options.turbulent) { - THREE.Terrain.Turbulence(v, options); - } - if (options.steps > 1) { - THREE.Terrain.Step(v, options.steps); - THREE.Terrain.Smooth(v, options); - } - // Keep the terrain within the allotted height range if necessary, and do easing. - THREE.Terrain.Clamp(v, options); - // Call the "after" callback - if (typeof options.after === 'function') { - options.after(v, options); - } - // Mark the geometry as having changed and needing updates. - mesh.geometry.verticesNeedUpdate = true; - mesh.geometry.normalsNeedUpdate = true; - mesh.geometry.computeBoundingSphere(); - mesh.geometry.computeFaceNormals(); - mesh.geometry.computeVertexNormals(); -}; - -/** - * Optimization types. - * - * Note that none of these are implemented right now. They should be done as - * shaders so that they execute on the GPU, and the resulting scene would need - * to be updated every frame to adjust to the camera's position. - * - * Further reading: - * - http://vterrain.org/LOD/Papers/ - * - http://vterrain.org/LOD/Implementations/ - * - * GEOMIPMAP: The terrain plane should be split into sections, each with their - * own LODs, for screen-space occlusion and detail reduction. Intermediate - * vertices on higher-detail neighboring sections should be interpolated - * between neighbor edge vertices in order to match with the edge of the - * lower-detail section. The number of sections should be around sqrt(segments) - * along each axis. It's unclear how to make materials stretch across segments. - * Possible example (I haven't looked too much into it) at - * https://github.com/felixpalmer/lod-terrain/tree/master/js/shaders - * - * GEOCLIPMAP: The terrain should be composed of multiple donut-shaped sections - * at decreasing resolution as the radius gets bigger. When the player moves, - * the sections should morph so that the detail "follows" the player around. - * There is an implementation of geoclipmapping at - * https://github.com/CodeArtemis/TriggerRally/blob/unified/server/public/scripts/client/terrain.coffee - * and a tutorial on morph targets at - * http://nikdudnik.com/making-3d-gfx-for-the-cinema-on-low-budget-and-three-js/ - * - * POLYGONREDUCTION: Combine areas that are relatively coplanar into larger - * polygons as described at http://www.shamusyoung.com/twentysidedtale/?p=142. - * This method can be combined with the others if done very carefully, or it - * can be adjusted to be more aggressive at greater distance from the camera - * (similar to combining with geomipmapping). - * - * If these do get implemented, here is the option description to add to the - * `THREE.Terrain` docblock: - * - * - `optimization`: the type of optimization to apply to the terrain. If - * an optimization is applied, the number of segments along each axis that - * the terrain should be divided into at the most detailed level should - * equal (n * 2^(LODs-1))^2 - 1, for arbitrary n, where LODs is the number - * of levels of detail desired. Valid values include: - * - * - `THREE.Terrain.NONE`: Don't apply any optimizations. This is the - * default. - * - `THREE.Terrain.GEOMIPMAP`: Divide the terrain into evenly-sized - * sections with multiple levels of detail. For each section, - * display a level of detail dependent on how close the camera is. - * - `THREE.Terrain.GEOCLIPMAP`: Divide the terrain into donut-shaped - * sections, where detail decreases as the radius increases. The - * rings then morph to "follow" the camera around so that the camera - * is always at the center, surrounded by the most detail. - */ -THREE.Terrain.NONE = 0; -THREE.Terrain.GEOMIPMAP = 1; -THREE.Terrain.GEOCLIPMAP = 2; -THREE.Terrain.POLYGONREDUCTION = 3; - -/** - * Get a 2D array of heightmap values from a 1D array of plane vertices. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Object} options - * A map of settings defining properties of the terrain. The only properties - * that matter here are `xSegments` and `ySegments`, which represent how many - * vertices wide and deep the terrain plane is, respectively (and therefore - * also the dimensions of the returned array). - * - * @return {Number[][]} - * A 2D array representing the terrain's heightmap. - */ -THREE.Terrain.toArray2D = function(vertices, options) { - var tgt = new Array(options.xSegments + 1), - xl = options.xSegments + 1, - yl = options.ySegments + 1, - i, j; - for (i = 0; i < xl; i++) { - tgt[i] = new Float64Array(options.ySegments + 1); - for (j = 0; j < yl; j++) { - tgt[i][j] = vertices[j * xl + i].z; - } - } - return tgt; -}; - -/** - * Set the height of plane vertices from a 2D array of heightmap values. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Number[][]} src - * A 2D array representing a heightmap to apply to the terrain. - */ -THREE.Terrain.fromArray2D = function(vertices, src) { - for (var i = 0, xl = src.length; i < xl; i++) { - for (var j = 0, yl = src[i].length; j < yl; j++) { - vertices[j * xl + i].z = src[i][j]; - } - } -}; - -/** - * Get a 1D array of heightmap values from a 1D array of plane vertices. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Object} options - * A map of settings defining properties of the terrain. The only properties - * that matter here are `xSegments` and `ySegments`, which represent how many - * vertices wide and deep the terrain plane is, respectively (and therefore - * also the dimensions of the returned array). - * - * @return {Number[]} - * A 1D array representing the terrain's heightmap. - */ -THREE.Terrain.toArray1D = function(vertices) { - var tgt = new Float64Array(vertices.length); - for (var i = 0, l = tgt.length; i < l; i++) { - tgt[i] = vertices[i].z; - } - return tgt; -}; - -/** - * Set the height of plane vertices from a 1D array of heightmap values. - * - * @param {THREE.Vector3[]} vertices - * A 1D array containing the vertices of the plane geometry representing the - * terrain, where the z-value of the vertices represent the terrain's - * heightmap. - * @param {Number[]} src - * A 1D array representing a heightmap to apply to the terrain. - */ -THREE.Terrain.fromArray1D = function(vertices, src) { - for (var i = 0, l = Math.min(vertices.length, src.length); i < l; i++) { - vertices[i].z = src[i]; - } -}; - -/** - * Generate a 1D array containing random heightmap data. - * - * This is like {@link THREE.Terrain.toHeightmap} except that instead of - * generating the Three.js mesh and material information you can just get the - * height data. - * - * @param {Function} method - * The method to use to generate the heightmap data. Works with function that - * would be an acceptable value for the `heightmap` option for the - * {@link THREE.Terrain} function. - * @param {Number} options - * The same as the options parameter for the {@link THREE.Terrain} function. - */ -THREE.Terrain.heightmapArray = function(method, options) { - var arr = new Array((options.xSegments+1) * (options.ySegments+1)), - l = arr.length, - i; - // The heightmap functions provided by this script operate on THREE.Vector3 - // objects by changing the z field, so we need to make that available. - // Unfortunately that means creating a bunch of objects we're just going to - // throw away, but a conscious decision was made here to optimize for the - // vector case. - for (i = 0; i < l; i++) { - arr[i] = {z: 0}; - } - options.minHeight = options.minHeight || 0; - options.maxHeight = typeof options.maxHeight === 'undefined' ? 1 : options.maxHeight; - options.stretch = options.stretch || false; - method(arr, options); - THREE.Terrain.Clamp(arr, options); - for (i = 0; i < l; i++) { - arr[i] = arr[i].z; - } - return arr; -}; - -/** - * Randomness interpolation functions. - */ -THREE.Terrain.Linear = function(x) { - return x; -}; - -// x = [0, 1], x^2 -THREE.Terrain.EaseIn = function(x) { - return x*x; -}; - -// x = [0, 1], -x(x-2) -THREE.Terrain.EaseOut = function(x) { - return -x * (x - 2); -}; - -// x = [0, 1], x^2(3-2x) -// Nearly identical alternatives: 0.5+0.5*cos(x*pi-pi), x^a/(x^a+(1-x)^a) (where a=1.6 seems nice) -// For comparison: http://www.wolframalpha.com/input/?i=x^1.6%2F%28x^1.6%2B%281-x%29^1.6%29%2C+x^2%283-2x%29%2C+0.5%2B0.5*cos%28x*pi-pi%29+from+0+to+1 -THREE.Terrain.EaseInOut = function(x) { - return x*x*(3-2*x); -}; - -// x = [0, 1], 0.5*(2x-1)^3+0.5 -THREE.Terrain.InEaseOut = function(x) { - var y = 2*x-1; - return 0.5 * y*y*y + 0.5; -}; - -// x = [0, 1], x^1.55 -THREE.Terrain.EaseInWeak = function(x) { - return Math.pow(x, 1.55); -}; - -// x = [0, 1], x^7 -THREE.Terrain.EaseInStrong = function(x) { - return x*x*x*x*x*x*x; -}; diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 0000000..6e8245c --- /dev/null +++ b/src/core.ts @@ -0,0 +1,126 @@ +import { Vector3 } from 'three'; + +import { TerrainOptions } from './basicTypes'; + +/** + * Get a 2D array of heightmap values from a 1D array of plane vertices. + * + * @param {THREE.Vector3[]} vertices + * A 1D array containing the vertices of the plane geometry representing the + * terrain, where the z-value of the vertices represent the terrain's + * heightmap. + * @param {Object} options + * A map of settings defining properties of the terrain. The only properties + * that matter here are `xSegments` and `ySegments`, which represent how many + * vertices wide and deep the terrain plane is, respectively (and therefore + * also the dimensions of the returned array). + * + * @return {Number[][]} + * A 2D array representing the terrain's heightmap. + */ +export function toArray2D(vertices: Vector3[], options: TerrainOptions): Float64Array[] { + var tgt = new Array(options.xSegments + 1), + xl = options.xSegments + 1, + yl = options.ySegments + 1, + i, j; + for (i = 0; i < xl; i++) { + tgt[i] = new Float64Array(options.ySegments + 1); + for (j = 0; j < yl; j++) { + tgt[i][j] = vertices[j * xl + i].z; + } + } + return tgt; +}; + +/** + * Set the height of plane vertices from a 2D array of heightmap values. + * + * @param {THREE.Vector3[]} vertices + * A 1D array containing the vertices of the plane geometry representing the + * terrain, where the z-value of the vertices represent the terrain's + * heightmap. + * @param {Number[][]} src + * A 2D array representing a heightmap to apply to the terrain. + */ +export function fromArray2D(vertices: Vector3[], src: Float64Array[]) { + for (var i = 0, xl = src.length; i < xl; i++) { + for (var j = 0, yl = src[i].length; j < yl; j++) { + vertices[j * xl + i].z = src[i][j]; + } + } +}; + +/** + * Get a 1D array of heightmap values from a 1D array of plane vertices. + * + * @param {THREE.Vector3[]} vertices + * A 1D array containing the vertices of the plane geometry representing the + * terrain, where the z-value of the vertices represent the terrain's + * heightmap. + * + * @return {Number[]} + * A 1D array representing the terrain's heightmap. + */ +export function toArray1D(vertices: Vector3[]): Float64Array { + var tgt = new Float64Array(vertices.length); + for (var i = 0, l = tgt.length; i < l; i++) { + tgt[i] = vertices[i].z; + } + return tgt; +}; + +/** + * Set the height of plane vertices from a 1D array of heightmap values. + * + * @param {THREE.Vector3[]} vertices + * A 1D array containing the vertices of the plane geometry representing the + * terrain, where the z-value of the vertices represent the terrain's + * heightmap. + * @param {Number[]} src + * A 1D array representing a heightmap to apply to the terrain. + */ +export function fromArray1D(vertices: Vector3[], src: Float64Array) { + for (var i = 0, l = Math.min(vertices.length, src.length); i < l; i++) { + vertices[i].z = src[i]; + } +}; + +/** + * Randomness interpolation functions. + */ +export function Linear(x: number) { + return x; +}; + +// x = [0, 1], x^2 +export function EaseIn(x: number) { + return x * x; +}; + +// x = [0, 1], -x(x-2) +export function EaseOut(x: number) { + return -x * (x - 2); +}; + +// x = [0, 1], x^2(3-2x) +// Nearly identical alternatives: 0.5+0.5*cos(x*pi-pi), x^a/(x^a+(1-x)^a) (where a=1.6 seems nice) +// For comparison: http://www.wolframalpha.com/input/?i=x^1.6%2F%28x^1.6%2B%281-x%29^1.6%29%2C+x^2%283-2x%29%2C+0.5%2B0.5*cos%28x*pi-pi%29+from+0+to+1 +export function EaseInOut(x: number) { + return x * x * (3 - 2 * x); +}; + +// x = [0, 1], 0.5*(2x-1)^3+0.5 +export function InEaseOut(x: number) { + var y = 2 * x - 1; + return 0.5 * y * y * y + 0.5; +}; + +// x = [0, 1], x^1.55 +export function EaseInWeak(x: number) { + return Math.pow(x, 1.55); +}; + +// x = [0, 1], x^7 +export function EaseInStrong(x: number) { + return x * x * x * x * x * x * x; +}; diff --git a/src/filters.js b/src/filters.ts similarity index 82% rename from src/filters.js rename to src/filters.ts index 15a51c1..4b028e7 100644 --- a/src/filters.js +++ b/src/filters.ts @@ -1,3 +1,7 @@ +import { TerrainOptions, EasingFunction } from "./basicTypes"; +import { Vector3 } from "three"; +import { Linear, EaseInOut } from "./core"; + /** * Rescale the heightmap of a terrain to keep it within the maximum range. * @@ -7,15 +11,15 @@ * @param {Object} options * A map of settings that control how the terrain is constructed and * displayed. Valid values are the same as those for the `options` parameter - * of {@link THREE.Terrain}() but only `maxHeight`, `minHeight`, and `easing` + * of {@link THREE.Terrain}() but only `maxHeight`, `minHeight`, `easing`, and `stretch` * are used. */ -THREE.Terrain.Clamp = function(g, options) { +export function Clamp(g: Vector3[], options: Pick) { var min = Infinity, max = -Infinity, l = g.length, i; - options.easing = options.easing || THREE.Terrain.Linear; + options.easing = options.easing || Linear; for (i = 0; i < l; i++) { if (g[i].z < min) min = g[i].z; if (g[i].z > max) max = g[i].z; @@ -66,7 +70,7 @@ THREE.Terrain.Clamp = function(g, options) { * all edges. If passed, should be an object with `top`, `bottom`, `left`, * and `right` Boolean properties specifying which edges to affect. */ -THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { +export function Edges(g: Vector3[], options: TerrainOptions, direction: boolean, distance: number, easing: EasingFunction, edges: { top: boolean; bottom: boolean; left: boolean; right: boolean }) { var numXSegments = Math.floor(distance / (options.xSize / options.xSegments)) || 1, numYSegments = Math.floor(distance / (options.ySize / options.ySegments)) || 1, peak = direction ? options.maxHeight : options.minHeight, @@ -74,15 +78,15 @@ THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { xl = options.xSegments + 1, yl = options.ySegments + 1, i, j, multiplier, k1, k2; - easing = easing || THREE.Terrain.EaseInOut; + easing = easing || EaseInOut; if (typeof edges !== 'object') { - edges = {top: true, bottom: true, left: true, right: true}; + edges = { top: true, bottom: true, left: true, right: true }; } for (i = 0; i < xl; i++) { for (j = 0; j < numYSegments; j++) { multiplier = easing(1 - j / numYSegments); - k1 = j*xl + i; - k2 = (options.ySegments-j)*xl + i; + k1 = j * xl + i; + k2 = (options.ySegments - j) * xl + i; if (edges.top) { g[k1].z = max(g[k1].z, (peak - g[k1].z) * multiplier + g[k1].z); } @@ -94,8 +98,8 @@ THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { for (i = 0; i < yl; i++) { for (j = 0; j < numXSegments; j++) { multiplier = easing(1 - j / numXSegments); - k1 = i*xl+j; - k2 = (options.ySegments-i)*xl + (options.xSegments-j); + k1 = i * xl + j; + k2 = (options.ySegments - i) * xl + (options.xSegments - j); if (edges.left) { g[k1].z = max(g[k1].z, (peak - g[k1].z) * multiplier + g[k1].z); } @@ -104,10 +108,11 @@ THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { } } } - THREE.Terrain.Clamp(g, { + Clamp(g, { maxHeight: options.maxHeight, minHeight: options.minHeight, stretch: true, + easing: Linear }); }; @@ -138,7 +143,7 @@ THREE.Terrain.Edges = function(g, options, direction, distance, easing, edges) { * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float * between 0 and 1 and returns a float between 0 and 1. */ -THREE.Terrain.RadialEdges = function(g, options, direction, distance, easing) { +export function RadialEdges(g: Vector3[], options: TerrainOptions, direction: boolean, distance: number, easing: EasingFunction, ) { var peak = direction ? options.maxHeight : options.minHeight, max = direction ? Math.max : Math.min, xl = (options.xSegments + 1), @@ -151,13 +156,13 @@ THREE.Terrain.RadialEdges = function(g, options, direction, distance, easing) { i, j, multiplier, k, vertexDistance; for (i = 0; i < xl; i++) { for (j = 0; j < yl2; j++) { - k = j*xl + i; - vertexDistance = Math.min(edgeRadius, Math.sqrt((xl2-i)*xSegmentSize*(xl2-i)*xSegmentSize + (yl2-j)*ySegmentSize*(yl2-j)*ySegmentSize) - distance); + k = j * xl + i; + vertexDistance = Math.min(edgeRadius, Math.sqrt((xl2 - i) * xSegmentSize * (xl2 - i) * xSegmentSize + (yl2 - j) * ySegmentSize * (yl2 - j) * ySegmentSize) - distance); if (vertexDistance < 0) continue; multiplier = easing(vertexDistance / edgeRadius); g[k].z = max(g[k].z, (peak - g[k].z) * multiplier + g[k].z); // Use symmetry to reduce the number of iterations. - k = (options.ySegments-j)*xl + i; + k = (options.ySegments - j) * xl + i; g[k].z = max(g[k].z, (peak - g[k].z) * multiplier + g[k].z); } } @@ -177,7 +182,7 @@ THREE.Terrain.RadialEdges = function(g, options, direction, distance, easing) { * How much to weight the original vertex height against the average of its * neighbors. */ -THREE.Terrain.Smooth = function(g, options, weight) { +export function Smooth(g: Vector3[], options: TerrainOptions, weight: number = 0) { var heightmap = new Float64Array(g.length); for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { for (var j = 0; j < yl; j++) { @@ -185,17 +190,16 @@ THREE.Terrain.Smooth = function(g, options, weight) { c = 0; for (var n = -1; n <= 1; n++) { for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { + var key = (j + n) * xl + i + m; + if (typeof g[key] !== 'undefined' && i + m >= 0 && j + n >= 0 && i + m < xl && j + n < yl) { sum += g[key].z; c++; } } } - heightmap[j*xl + i] = sum / c; + heightmap[j * xl + i] = sum / c; } } - weight = weight || 0; var w = 1 / (1 + weight); for (var k = 0, l = g.length; k < l; k++) { g[k].z = (heightmap[k] + g[k].z * weight) * w; @@ -207,11 +211,11 @@ THREE.Terrain.Smooth = function(g, options, weight) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.SmoothMedian = function(g, options) { +export function SmoothMedian(g: Vector3[], options: TerrainOptions) { var heightmap = new Float64Array(g.length), - neighborValues = [], + neighborValues: number[] = [], neighborKeys = [], - sortByValue = function(a, b) { + sortByValue = function (a: number, b: number) { return neighborValues[a] - neighborValues[b]; }; for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { @@ -220,23 +224,23 @@ THREE.Terrain.SmoothMedian = function(g, options) { neighborKeys.length = 0; for (var n = -1; n <= 1; n++) { for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { + var key = (j + n) * xl + i + m; + if (typeof g[key] !== 'undefined' && i + m >= 0 && j + n >= 0 && i + m < xl && j + n < yl) { neighborValues.push(g[key].z); neighborKeys.push(key); } } } neighborKeys.sort(sortByValue); - var halfKey = Math.floor(neighborKeys.length*0.5), + var halfKey = Math.floor(neighborKeys.length * 0.5), median; if (neighborKeys.length % 2 === 1) { median = g[neighborKeys[halfKey]].z; } else { - median = (g[neighborKeys[halfKey-1]].z + g[neighborKeys[halfKey]].z) * 0.5; + median = (g[neighborKeys[halfKey - 1]].z + g[neighborKeys[halfKey]].z) * 0.5; } - heightmap[j*xl + i] = median; + heightmap[j * xl + i] = median; } } for (var k = 0, l = g.length; k < l; k++) { @@ -260,7 +264,7 @@ THREE.Terrain.SmoothMedian = function(g, options) { * outside of which the point will be clamped. Higher values mean that the * point can be farther outside the range of its neighbors. */ -THREE.Terrain.SmoothConservative = function(g, options, multiplier) { +export function SmoothConservative(g: Vector3[], options: TerrainOptions, multiplier: number) { var heightmap = new Float64Array(g.length); for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { for (var j = 0; j < yl; j++) { @@ -268,14 +272,14 @@ THREE.Terrain.SmoothConservative = function(g, options, multiplier) { min = Infinity; for (var n = -1; n <= 1; n++) { for (var m = -1; m <= 1; m++) { - var key = (j+n)*xl + i + m; - if (typeof g[key] !== 'undefined' && n && m && i+m >= 0 && j+n >= 0 && i+m < xl && j+n < yl) { + var key = (j + n) * xl + i + m; + if (typeof g[key] !== 'undefined' && n && m && i + m >= 0 && j + n >= 0 && i + m < xl && j + n < yl) { if (g[key].z < min) min = g[key].z; if (g[key].z > max) max = g[key].z; } } } - var kk = j*xl + i; + var kk = j * xl + i; if (typeof multiplier === 'number') { var halfdiff = (max - min) * 0.5, middle = min + halfdiff; @@ -300,24 +304,24 @@ THREE.Terrain.SmoothConservative = function(g, options, multiplier) { * The number of steps to divide the terrain into. Defaults to * (g.length/2)^(1/4). */ -THREE.Terrain.Step = function(g, levels) { +export function Step(g: Vector3[], levels?: number) { // Calculate the max, min, and avg values for each bucket + const l = g.length; + if (typeof levels === 'undefined') { + levels = Math.floor(Math.pow(l * 0.5, 0.25)); + } var i = 0, j = 0, - l = g.length, inc = Math.floor(l / levels), heights = new Array(l), buckets = new Array(levels); - if (typeof levels === 'undefined') { - levels = Math.floor(Math.pow(l*0.5, 0.25)); - } for (i = 0; i < l; i++) { heights[i] = g[i].z; } - heights.sort(function(a, b) { return a - b; }); + heights.sort(function (a, b) { return a - b; }); for (i = 0; i < levels; i++) { // Bucket by population (bucket size) not range size - var subset = heights.slice(i*inc, (i+1)*inc), + var subset = heights.slice(i * inc, (i + 1) * inc), sum = 0, bl = subset.length; for (j = 0; j < bl; j++) { @@ -325,7 +329,7 @@ THREE.Terrain.Step = function(g, levels) { } buckets[i] = { min: subset[0], - max: subset[subset.length-1], + max: subset[subset.length - 1], avg: sum / bl, }; } @@ -347,7 +351,7 @@ THREE.Terrain.Step = function(g, levels) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Turbulence = function(g, options) { +export function Turbulence(g: Vector3[], options: TerrainOptions) { var range = options.maxHeight - options.minHeight; for (var i = 0, l = g.length; i < l; i++) { g[i].z = options.minHeight + Math.abs((g[i].z - options.minHeight) * 2 - range); diff --git a/src/gaussian.js b/src/gaussian.ts similarity index 86% rename from src/gaussian.js rename to src/gaussian.ts index 4b306b5..6333c47 100644 --- a/src/gaussian.js +++ b/src/gaussian.ts @@ -1,4 +1,7 @@ -(function() { +import { Vector3 } from 'three'; + +import { TerrainOptions } from './basicTypes'; +import { fromArray2D, toArray2D } from './core'; /** * Convolve an array with a kernel. @@ -16,7 +19,7 @@ * @return {Number[][]} * An array containing the result of the convolution. */ -function convolve(src, kernel, tgt) { +function convolve(src: Float64Array[], kernel: Float64Array[], tgt?: Float64Array[]) { // src and kernel must be nonzero rectangular number arrays. if (!src.length || !kernel.length) return src; // Initialize tracking variables. @@ -56,9 +59,9 @@ function convolve(src, kernel, tgt) { // with a target smaller than the source. That is // unreasonable for some applications, so we let the caller // make that choice. - if (typeof src[i+a] !== 'undefined' && - typeof src[i+a][j+b] !== 'undefined') { - last = src[i+a][j+b]; + if (typeof src[i + a] !== 'undefined' && + typeof src[i + a][j + b] !== 'undefined') { + last = src[i + a][j + b]; } // Multiply the source and the kernel at this position. // The value at the target position is the sum of these @@ -74,9 +77,9 @@ function convolve(src, kernel, tgt) { /** * Returns the value at X of a Gaussian distribution with standard deviation S. */ -function gauss(x, s) { +function gauss(x: number, s: number) { // 2.5066282746310005 is sqrt(2*pi) - return Math.exp(-0.5 * x*x / (s*s)) / (s * 2.5066282746310005); + return Math.exp(-0.5 * x * x / (s * s)) / (s * 2.5066282746310005); } /** @@ -85,8 +88,7 @@ function gauss(x, s) { * Returns a kernel of size N approximating a 1D Gaussian distribution with * standard deviation S. */ -function gaussianKernel1D(s, n) { - if (typeof n !== 'number') n = 7; +function gaussianKernel1D(s: number, n: number = 7) { var kernel = new Float64Array(n), halfN = Math.floor(n * 0.5), odd = n % 2, @@ -116,9 +118,7 @@ function gaussianKernel1D(s, n) { * @return {Number[][]} * An array containing the result of smoothing the src. */ -function gaussian(src, s, kernelSize) { - if (typeof s === 'undefined') s = 1; - if (typeof kernelSize === 'undefined') kernelSize = 7; +function gaussian(src: Float64Array[], s: number = 1, kernelSize: number = 7) { var kernel = gaussianKernel1D(s, kernelSize), l = kernelSize || kernel.length, kernelH = [kernel], @@ -146,8 +146,6 @@ function gaussian(src, s, kernelSize) { * The size of the Gaussian kernel to use. Larger kernels result in slower * but more accurate smoothing. */ -THREE.Terrain.Gaussian = function(g, options, s, kernelSize) { - THREE.Terrain.fromArray2D(g, gaussian(THREE.Terrain.toArray2D(g, options), s, kernelSize)); +export function Gaussian(g: Vector3[], options: TerrainOptions, s: number = 1, kernelSize: number = 7) { + fromArray2D(g, gaussian(toArray2D(g, options), s, kernelSize)); }; - -})(); diff --git a/src/generators.js b/src/generators.ts similarity index 53% rename from src/generators.js rename to src/generators.ts index 3cb798d..94edabc 100644 --- a/src/generators.js +++ b/src/generators.ts @@ -1,3 +1,17 @@ +import { AdditiveBlending, Math as ThreeMath, Vector3 } from 'three'; + +import { FeatureFunction, HeightmapFunction, TerrainOptions } from './basicTypes'; +import { EaseInStrong, Linear } from './core'; +import { Clamp, SmoothMedian } from './filters'; +import { Influence, Influences } from './influences'; +import { perlin, seed, simplex } from './noise'; + +interface Pass { + method: HeightmapFunction; + amplitude?: number; + frequency?: number; +} + /** * A utility for generating heightmap functions by additive composition. * @@ -21,20 +35,15 @@ * smaller features). Often running multiple generation functions with * different frequencies and amplitudes results in nice detail. */ -THREE.Terrain.MultiPass = function(g, options, passes) { - var clonedOptions = {}; - for (var opt in options) { - if (options.hasOwnProperty(opt)) { - clonedOptions[opt] = options[opt]; - } - } +export function MultiPass(g: Vector3[], options: TerrainOptions, passes: Pass[]) { + var clonedOptions = { ...options }; var range = options.maxHeight - options.minHeight; for (var i = 0, l = passes.length; i < l; i++) { - var amp = typeof passes[i].amplitude === 'undefined' ? 1 : passes[i].amplitude, - move = 0.5 * (range - range * amp); + var amp = typeof passes[i].amplitude === 'undefined' ? 1 : passes[i].amplitude; + var move = 0.5 * (range - range * amp!); clonedOptions.maxHeight = options.maxHeight - move; clonedOptions.minHeight = options.minHeight + move; - clonedOptions.frequency = typeof passes[i].frequency === 'undefined' ? options.frequency : passes[i].frequency; + clonedOptions.frequency = (typeof passes[i].frequency === 'undefined' ? options.frequency : passes[i].frequency)!; passes[i].method(g, clonedOptions); } }; @@ -57,7 +66,7 @@ THREE.Terrain.MultiPass = function(g, options, passes) { * y-coordinates) are given as percentages of a phase (i.e. how far across * the terrain in the relevant direction they are). */ -THREE.Terrain.Curve = function(g, options, curve) { +export function Curve(g: Vector3[], options: TerrainOptions, curve: (x: number, y: number) => number) { var range = (options.maxHeight - options.minHeight) * 0.5, scalar = options.frequency / (Math.min(options.xSegments, options.ySegments) + 1); for (var i = 0, xl = options.xSegments + 1, yl = options.ySegments + 1; i < xl; i++) { @@ -72,7 +81,7 @@ THREE.Terrain.Curve = function(g, options, curve) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Cosine = function(g, options) { +export function Cosine(g: Vector3[], options: TerrainOptions) { var amplitude = (options.maxHeight - options.minHeight) * 0.5, frequencyScalar = options.frequency * Math.PI / (Math.min(options.xSegments, options.ySegments) + 1), phase = Math.random() * Math.PI * 2; @@ -88,12 +97,12 @@ THREE.Terrain.Cosine = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.CosineLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Cosine, frequency: 2.5 }, - { method: THREE.Terrain.Cosine, amplitude: 0.1, frequency: 12 }, - { method: THREE.Terrain.Cosine, amplitude: 0.05, frequency: 15 }, - { method: THREE.Terrain.Cosine, amplitude: 0.025, frequency: 20 }, +export function CosineLayers(g: Vector3[], options: TerrainOptions) { + MultiPass(g, options, [ + { method: Cosine, amplitude: 1, frequency: 2.5 }, + { method: Cosine, amplitude: 0.1, frequency: 12 }, + { method: Cosine, amplitude: 0.05, frequency: 15 }, + { method: Cosine, amplitude: 0.025, frequency: 20 }, ]); }; @@ -110,10 +119,14 @@ THREE.Terrain.CosineLayers = function(g, options) { * displayed. Valid values are the same as those for the `options` parameter * of {@link THREE.Terrain}(). */ -THREE.Terrain.DiamondSquare = function(g, options) { +export function DiamondSquare(g: Vector3[], options: TerrainOptions): void { + function ceilPowerOfTwo(value: number): number { + return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); + } + // Set the segment length to the smallest power of 2 that is greater than // the number of vertices in either dimension of the plane - var segments = THREE.Math.nextPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); + var segments = ceilPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); // Initialize heightmap var size = segments + 1, @@ -124,39 +137,38 @@ THREE.Terrain.DiamondSquare = function(g, options) { xl = options.xSegments + 1, yl = options.ySegments + 1; for (i = 0; i <= segments; i++) { - heightmap[i] = new Float64Array(segments+1); + heightmap[i] = new Float64Array(segments + 1); } // Generate heightmap for (var l = segments; l >= 2; l /= 2) { - var half = Math.round(l*0.5), + var half = Math.round(l * 0.5), whole = Math.round(l), x, y, avg, - d, - e; + d; smoothing /= 2; // square for (x = 0; x < segments; x += whole) { for (y = 0; y < segments; y += whole) { d = Math.random() * smoothing * 2 - smoothing; avg = heightmap[x][y] + // top left - heightmap[x+whole][y] + // top right - heightmap[x][y+whole] + // bottom left - heightmap[x+whole][y+whole]; // bottom right + heightmap[x + whole][y] + // top right + heightmap[x][y + whole] + // bottom left + heightmap[x + whole][y + whole]; // bottom right avg *= 0.25; - heightmap[x+half][y+half] = avg + d; + heightmap[x + half][y + half] = avg + d; } } // diamond for (x = 0; x < segments; x += half) { - for (y = (x+half) % l; y < segments; y += l) { + for (y = (x + half) % l; y < segments; y += l) { d = Math.random() * smoothing * 2 - smoothing; - avg = heightmap[(x-half+size)%size][y] + // middle left - heightmap[(x+half)%size][y] + // middle right - heightmap[x][(y+half)%size] + // middle top - heightmap[x][(y-half+size)%size]; // middle bottom + avg = heightmap[(x - half + size) % size][y] + // middle left + heightmap[(x + half) % size][y] + // middle right + heightmap[x][(y + half) % size] + // middle top + heightmap[x][(y - half + size) % size]; // middle bottom avg *= 0.25; avg += d; heightmap[x][y] = avg; @@ -186,8 +198,8 @@ THREE.Terrain.DiamondSquare = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Fault = function(g, options) { - var d = Math.sqrt(options.xSegments*options.xSegments + options.ySegments*options.ySegments), +export function Fault(g: Vector3[], options: TerrainOptions) { + var d = Math.sqrt(options.xSegments * options.xSegments + options.ySegments * options.ySegments), iterations = d * options.frequency, range = (options.maxHeight - options.minHeight) * 0.5, displacement = range / iterations, @@ -196,10 +208,10 @@ THREE.Terrain.Fault = function(g, options) { var v = Math.random(), a = Math.sin(v * Math.PI * 2), b = Math.cos(v * Math.PI * 2), - c = Math.random() * d - d*0.5; + c = Math.random() * d - d * 0.5; for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - var distance = a*i + b*j - c; + var distance = a * i + b * j - c; if (distance > smoothDistance) { g[j * xl + i].z += displacement; } @@ -212,7 +224,7 @@ THREE.Terrain.Fault = function(g, options) { } } } - // THREE.Terrain.Smooth(g, options); + // Smooth(g, options); }; /** @@ -246,7 +258,9 @@ THREE.Terrain.Fault = function(g, options) { * typically by transforming it over a distribution. The result affects where * small hills are raised thereby affecting the overall shape of the terrain. */ -THREE.Terrain.Hill = function(g, options, feature, shape) { +type ShapeFunction = ({ x, y }: { x: number; y: number }) => void; + +export function Hill(g: Vector3[], options: TerrainOptions, feature: FeatureFunction, shape?: ShapeFunction) { var frequency = options.frequency * 2, numFeatures = frequency * frequency * 10, heightRange = options.maxHeight - options.minHeight, @@ -255,25 +269,22 @@ THREE.Terrain.Hill = function(g, options, feature, shape) { smallerSideLength = Math.min(options.xSize, options.ySize), minRadius = smallerSideLength / (frequency * frequency), maxRadius = smallerSideLength / frequency; - feature = feature || THREE.Terrain.Influences.Hill; + feature = feature || Influences.Hill; var coords = { x: 0, y: 0 }; for (var i = 0; i < numFeatures; i++) { var radius = Math.random() * (maxRadius - minRadius) + minRadius, height = Math.random() * (maxHeight - minHeight) + minHeight; - var min = 0 - radius, - maxX = options.xSize + radius, - maxY = options.ySize + radius; coords.x = Math.random(); coords.y = Math.random(); if (typeof shape === 'function') shape(coords); - THREE.Terrain.Influence( + Influence( g, options, feature, coords.x, coords.y, radius, height, - THREE.AdditiveBlending, - THREE.Terrain.EaseInStrong + AdditiveBlending, + EaseInStrong ); } }; @@ -302,104 +313,102 @@ THREE.Terrain.Hill = function(g, options, feature, shape) { * `THREE.Terrain.Influences` contains some useful functions for this * purpose. */ -THREE.Terrain.HillIsland = (function() { - var island = function(coords) { +export function HillIsland() { + function island(coords: { x: number; y: number }) { var theta = Math.random() * Math.PI * 2; coords.x = 0.5 + Math.cos(theta) * coords.x * 0.4; coords.y = 0.5 + Math.sin(theta) * coords.y * 0.4; }; - return function(g, options, feature) { - THREE.Terrain.Hill(g, options, feature, island); + return function (g: Vector3[], options: TerrainOptions, feature: FeatureFunction) { + Hill(g, options, feature, island); }; -})(); +} -(function() { - /** - * Deposit a particle at a vertex. - */ - function deposit(g, i, j, xl, displacement) { - var currentKey = j * xl + i; - // Pick a random neighbor. - for (var k = 0; k < 3; k++) { - var r = Math.floor(Math.random() * 8); - switch (r) { - case 0: i++; break; - case 1: i--; break; - case 2: j++; break; - case 3: j--; break; - case 4: i++; j++; break; - case 5: i++; j--; break; - case 6: i--; j++; break; - case 7: i--; j--; break; - } - var neighborKey = j * xl + i; - // If the neighbor is lower, move the particle to that neighbor and re-evaluate. - if (typeof g[neighborKey] !== 'undefined') { - if (g[neighborKey].z < g[currentKey].z) { - deposit(g, i, j, xl, displacement); - return; - } - } - // Deposit some particles on the edge. - else if (Math.random() < 0.2) { - g[currentKey].z += displacement; +/** + * Deposit a particle at a vertex. + */ +function deposit(g: Vector3[], i: number, j: number, xl: number, displacement: number) { + var currentKey = j * xl + i; + // Pick a random neighbor. + for (var k = 0; k < 3; k++) { + var r = Math.floor(Math.random() * 8); + switch (r) { + case 0: i++; break; + case 1: i--; break; + case 2: j++; break; + case 3: j--; break; + case 4: i++; j++; break; + case 5: i++; j--; break; + case 6: i--; j++; break; + case 7: i--; j--; break; + } + var neighborKey = j * xl + i; + // If the neighbor is lower, move the particle to that neighbor and re-evaluate. + if (typeof g[neighborKey] !== 'undefined') { + if (g[neighborKey].z < g[currentKey].z) { + deposit(g, i, j, xl, displacement); return; } } - g[currentKey].z += displacement; + // Deposit some particles on the edge. + else if (Math.random() < 0.2) { + g[currentKey].z += displacement; + return; + } } + g[currentKey].z += displacement; +} - /** - * Generate random terrain using the Particle Deposition method. - * - * Based on http://www.lighthouse3d.com/opengl/terrain/index.php?particle - * Repeatedly deposit particles on terrain vertices. Pick a random neighbor - * of that vertex. If the neighbor is lower, roll the particle to the - * neighbor. When the particle stops, displace the vertex upwards. - * - * The shape of the outcome is highly dependent on options.frequency - * because that affects how many particles will be dropped. Values around - * 0.25 generally result in archipelagos whereas the default of 2.5 - * generally results in one large mountainous island. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ - THREE.Terrain.Particles = function(g, options) { - var iterations = Math.sqrt(options.xSegments*options.xSegments + options.ySegments*options.ySegments) * options.frequency * 300, - xl = options.xSegments + 1, - displacement = (options.maxHeight - options.minHeight) / iterations * 1000, - i = Math.floor(Math.random() * options.xSegments), - j = Math.floor(Math.random() * options.ySegments), - xDeviation = Math.random() * 0.2 - 0.1, +/** + * Generate random terrain using the Particle Deposition method. + * + * Based on http://www.lighthouse3d.com/opengl/terrain/index.php?particle + * Repeatedly deposit particles on terrain vertices. Pick a random neighbor + * of that vertex. If the neighbor is lower, roll the particle to the + * neighbor. When the particle stops, displace the vertex upwards. + * + * The shape of the outcome is highly dependent on options.frequency + * because that affects how many particles will be dropped. Values around + * 0.25 generally result in archipelagos whereas the default of 2.5 + * generally results in one large mountainous island. + * + * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. + */ +export function Particles(g: Vector3[], options: TerrainOptions) { + var iterations = Math.sqrt(options.xSegments * options.xSegments + options.ySegments * options.ySegments) * options.frequency * 300, + xl = options.xSegments + 1, + displacement = (options.maxHeight - options.minHeight) / iterations * 1000, + i = Math.floor(Math.random() * options.xSegments), + j = Math.floor(Math.random() * options.ySegments), + xDeviation = Math.random() * 0.2 - 0.1, + yDeviation = Math.random() * 0.2 - 0.1; + for (var k = 0; k < iterations; k++) { + deposit(g, i, j, xl, displacement); + var d = Math.random() * Math.PI * 2; + if (k % 1000 === 0) { + xDeviation = Math.random() * 0.2 - 0.1; yDeviation = Math.random() * 0.2 - 0.1; - for (var k = 0; k < iterations; k++) { - deposit(g, i, j, xl, displacement); - var d = Math.random() * Math.PI * 2; - if (k % 1000 === 0) { - xDeviation = Math.random() * 0.2 - 0.1; - yDeviation = Math.random() * 0.2 - 0.1; - } - if (k % 100 === 0) { - i = Math.floor(options.xSegments*(0.5+xDeviation) + Math.cos(d) * Math.random() * options.xSegments*(0.5-Math.abs(xDeviation))); - j = Math.floor(options.ySegments*(0.5+yDeviation) + Math.sin(d) * Math.random() * options.ySegments*(0.5-Math.abs(yDeviation))); - } } - // THREE.Terrain.Smooth(g, options, 3); - }; -})(); + if (k % 100 === 0) { + i = Math.floor(options.xSegments * (0.5 + xDeviation) + Math.cos(d) * Math.random() * options.xSegments * (0.5 - Math.abs(xDeviation))); + j = Math.floor(options.ySegments * (0.5 + yDeviation) + Math.sin(d) * Math.random() * options.ySegments * (0.5 - Math.abs(yDeviation))); + } + } + // THREE.Terrain.Smooth(g, options, 3); +} /** * Generate random terrain using the Perlin Noise method. * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Perlin = function(g, options) { - noise.seed(Math.random()); +export function Perlin(g: Vector3[], options: TerrainOptions) { + seed(Math.random()); var range = (options.maxHeight - options.minHeight) * 0.5, divisor = (Math.min(options.xSegments, options.ySegments) + 1) / options.frequency; for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - g[j * xl + i].z += noise.perlin(i / divisor, j / divisor) * range; + g[j * xl + i].z += perlin(i / divisor, j / divisor) * range; } } }; @@ -409,11 +418,11 @@ THREE.Terrain.Perlin = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.PerlinDiamond = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Perlin }, - { method: THREE.Terrain.DiamondSquare, amplitude: 0.75 }, - { method: function(g, o) { return THREE.Terrain.SmoothMedian(g, o); } }, +export function PerlinDiamond(g: Vector3[], options: TerrainOptions) { + MultiPass(g, options, [ + { method: Perlin }, + { method: DiamondSquare, amplitude: 0.75 }, + { method: function (g: Vector3[], o: TerrainOptions) { return SmoothMedian(g, o); } }, ]); }; @@ -422,12 +431,12 @@ THREE.Terrain.PerlinDiamond = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.PerlinLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Perlin, frequency: 1.25 }, - { method: THREE.Terrain.Perlin, amplitude: 0.05, frequency: 2.5 }, - { method: THREE.Terrain.Perlin, amplitude: 0.35, frequency: 5 }, - { method: THREE.Terrain.Perlin, amplitude: 0.15, frequency: 10 }, +export function PerlinLayers(g: Vector3[], options: TerrainOptions) { + MultiPass(g, options, [ + { method: Perlin, frequency: 1.25 }, + { method: Perlin, amplitude: 0.05, frequency: 2.5 }, + { method: Perlin, amplitude: 0.35, frequency: 5 }, + { method: Perlin, amplitude: 0.15, frequency: 10 }, ]); }; @@ -439,13 +448,13 @@ THREE.Terrain.PerlinLayers = function(g, options) { * See https://github.com/mrdoob/three.js/blob/master/examples/webgl_terrain_dynamic.html * for an interesting comparison where the generation happens in GLSL. */ -THREE.Terrain.Simplex = function(g, options) { - noise.seed(Math.random()); +export function Simplex(g: Vector3[], options: TerrainOptions) { + seed(Math.random()); var range = (options.maxHeight - options.minHeight) * 0.5, divisor = (Math.min(options.xSegments, options.ySegments) + 1) * 2 / options.frequency; for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { - g[j * xl + i].z += noise.simplex(i / divisor, j / divisor) * range; + g[j * xl + i].z += simplex(i / divisor, j / divisor) * range; } } }; @@ -455,115 +464,112 @@ THREE.Terrain.Simplex = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.SimplexLayers = function(g, options) { - THREE.Terrain.MultiPass(g, options, [ - { method: THREE.Terrain.Simplex, frequency: 1.25 }, - { method: THREE.Terrain.Simplex, amplitude: 0.5, frequency: 2.5 }, - { method: THREE.Terrain.Simplex, amplitude: 0.25, frequency: 5 }, - { method: THREE.Terrain.Simplex, amplitude: 0.125, frequency: 10 }, - { method: THREE.Terrain.Simplex, amplitude: 0.0625, frequency: 20 }, +export function SimplexLayers(g: Vector3[], options: TerrainOptions) { + MultiPass(g, options, [ + { method: Simplex, frequency: 1.25 }, + { method: Simplex, amplitude: 0.5, frequency: 2.5 }, + { method: Simplex, amplitude: 0.25, frequency: 5 }, + { method: Simplex, amplitude: 0.125, frequency: 10 }, + { method: Simplex, amplitude: 0.0625, frequency: 20 }, ]); }; -(function() { - /** - * Generate a heightmap using white noise. - * - * @param {THREE.Vector3[]} g The terrain vertices. - * @param {Object} options Settings - * @param {Number} scale The resolution of the resulting heightmap. - * @param {Number} segments The width of the target heightmap. - * @param {Number} range The altitude of the noise. - * @param {Number[]} data The target heightmap. - */ - function WhiteNoise(g, options, scale, segments, range, data) { - if (scale > segments) return; - var i = 0, - j = 0, - xl = segments, - yl = segments, - inc = Math.floor(segments / scale), - lastX = -inc, - lastY = -inc; - // Walk over the target. For a target of size W and a resolution of N, - // set every W/N points (in both directions). - for (i = 0; i <= xl; i += inc) { - for (j = 0; j <= yl; j += inc) { - var k = j * xl + i; - data[k] = Math.random() * range; - if (lastX < 0 && lastY < 0) continue; - // jscs:disable disallowSpacesInsideBrackets - /* c b * - * l t */ - var t = data[k], - l = data[ j * xl + (i-inc)] || t, // left - b = data[(j-inc) * xl + i ] || t, // bottom - c = data[(j-inc) * xl + (i-inc)] || t; // corner - // jscs:enable disallowSpacesInsideBrackets - // Interpolate between adjacent points to set the height of - // higher-resolution target data. - for (var x = lastX; x < i; x++) { - for (var y = lastY; y < j; y++) { - if (x === lastX && y === lastY) continue; - var z = y * xl + x; - if (z < 0) continue; - var px = ((x-lastX) / inc), - py = ((y-lastY) / inc), - r1 = px * b + (1-px) * c, - r2 = px * t + (1-px) * l; - data[z] = py * r2 + (1-py) * r1; - } +/** + * Generate a heightmap using white noise. + * + * @param {THREE.Vector3[]} g The terrain vertices. + * @param {Object} options Settings + * @param {Number} scale The resolution of the resulting heightmap. + * @param {Number} segments The width of the target heightmap. + * @param {Number} range The altitude of the noise. + * @param {Number[]} data The target heightmap. + */ +function WhiteNoise(g: Vector3[], options: TerrainOptions, scale: number, segments: number, range: number, data: Float64Array) { + if (scale > segments) return; + var i = 0, + j = 0, + xl = segments, + yl = segments, + inc = Math.floor(segments / scale), + lastX = -inc, + lastY = -inc; + // Walk over the target. For a target of size W and a resolution of N, + // set every W/N points (in both directions). + for (i = 0; i <= xl; i += inc) { + for (j = 0; j <= yl; j += inc) { + var k = j * xl + i; + data[k] = Math.random() * range; + if (lastX < 0 && lastY < 0) continue; + /* c b * + * l t */ + var t = data[k], + l = data[j * xl + (i - inc)] || t, // left + b = data[(j - inc) * xl + i] || t, // bottom + c = data[(j - inc) * xl + (i - inc)] || t; // corner + // Interpolate between adjacent points to set the height of + // higher-resolution target data. + for (var x = lastX; x < i; x++) { + for (var y = lastY; y < j; y++) { + if (x === lastX && y === lastY) continue; + var z = y * xl + x; + if (z < 0) continue; + var px = ((x - lastX) / inc), + py = ((y - lastY) / inc), + r1 = px * b + (1 - px) * c, + r2 = px * t + (1 - px) * l; + data[z] = py * r2 + (1 - py) * r1; } - lastY = j; } - lastX = i; - lastY = -inc; + lastY = j; } - // Assign the temporary data back to the actual terrain heightmap. - for (i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (j = 0, yl = options.ySegments + 1; j < yl; j++) { - // http://stackoverflow.com/q/23708306/843621 - var kg = j * xl + i, - kd = j * segments + i; - g[kg].z += data[kd]; - } + lastX = i; + lastY = -inc; + } + // Assign the temporary data back to the actual terrain heightmap. + for (i = 0, xl = options.xSegments + 1; i < xl; i++) { + for (j = 0, yl = options.ySegments + 1; j < yl; j++) { + // http://stackoverflow.com/q/23708306/843621 + var kg = j * xl + i, + kd = j * segments + i; + g[kg].z += data[kd]; } } +} - /** - * Generate random terrain using value noise. - * - * The basic approach of value noise is to generate white noise at a - * smaller octave than the target and then interpolate to get a higher- - * resolution result. This is then repeated at different resolutions. - * - * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. - */ - THREE.Terrain.Value = function(g, options) { - // Set the segment length to the smallest power of 2 that is greater - // than the number of vertices in either dimension of the plane - var segments = THREE.Math.nextPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); +/** + * Generate random terrain using value noise. + * + * The basic approach of value noise is to generate white noise at a + * smaller octave than the target and then interpolate to get a higher- + * resolution result. This is then repeated at different resolutions. + * + * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. + */ +export function Value(g: Vector3[], options: TerrainOptions) { + // Set the segment length to the smallest power of 2 that is greater + // than the number of vertices in either dimension of the plane + var segments = ThreeMath.nextPowerOfTwo(Math.max(options.xSegments, options.ySegments) + 1); - // Store the array of white noise outside of the WhiteNoise function to - // avoid allocating a bunch of unnecessary arrays; we can just - // overwrite old data each time WhiteNoise() is called. - var data = new Float64Array((segments+1)*(segments+1)); + // Store the array of white noise outside of the WhiteNoise function to + // avoid allocating a bunch of unnecessary arrays; we can just + // overwrite old data each time WhiteNoise() is called. + var data = new Float64Array((segments + 1) * (segments + 1)); - // Layer white noise at different resolutions. - var range = options.maxHeight - options.minHeight; - for (var i = 2; i < 7; i++) { - WhiteNoise(g, options, Math.pow(2, i), segments, range * Math.pow(2, 2.4-i*1.2), data); - } + // Layer white noise at different resolutions. + var range = options.maxHeight - options.minHeight; + for (var i = 2; i < 7; i++) { + WhiteNoise(g, options, Math.pow(2, i), segments, range * Math.pow(2, 2.4 - i * 1.2), data); + } - // White noise creates some weird artifacts; fix them. - // THREE.Terrain.Smooth(g, options, 1); - THREE.Terrain.Clamp(g, { - maxHeight: options.maxHeight, - minHeight: options.minHeight, - stretch: true, - }); - }; -})(); + // White noise creates some weird artifacts; fix them. + // THREE.Terrain.Smooth(g, options, 1); + Clamp(g, { + maxHeight: options.maxHeight, + minHeight: options.minHeight, + stretch: true, + easing: Linear + }); +}; /** * Generate random terrain using Weierstrass functions. @@ -574,28 +580,28 @@ THREE.Terrain.SimplexLayers = function(g, options) { * * Parameters are the same as those for {@link THREE.Terrain.DiamondSquare}. */ -THREE.Terrain.Weierstrass = function(g, options) { +export function Weierstrass(g: Vector3[], options: TerrainOptions) { var range = (options.maxHeight - options.minHeight) * 0.5, dir1 = Math.random() < 0.5 ? 1 : -1, dir2 = Math.random() < 0.5 ? 1 : -1, - r11 = 0.5 + Math.random() * 1.0, - r12 = 0.5 + Math.random() * 1.0, - r13 = 0.025 + Math.random() * 0.10, - r14 = -1.0 + Math.random() * 2.0, - r21 = 0.5 + Math.random() * 1.0, - r22 = 0.5 + Math.random() * 1.0, - r23 = 0.025 + Math.random() * 0.10, - r24 = -1.0 + Math.random() * 2.0; + r11 = 0.5 + Math.random() * 1.0, + r12 = 0.5 + Math.random() * 1.0, + r13 = 0.025 + Math.random() * 0.10, + r14 = -1.0 + Math.random() * 2.0, + r21 = 0.5 + Math.random() * 1.0, + r22 = 0.5 + Math.random() * 1.0, + r23 = 0.025 + Math.random() * 0.10, + r24 = -1.0 + Math.random() * 2.0; for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { for (var j = 0, yl = options.ySegments + 1; j < yl; j++) { var sum = 0; for (var k = 0; k < 20; k++) { - var x = Math.pow(1+r11, -k) * Math.sin(Math.pow(1+r12, k) * (i + 0.25*Math.cos(j) + r14*j) * r13); - var y = Math.pow(1+r21, -k) * Math.sin(Math.pow(1+r22, k) * (j + 0.25*Math.cos(i) + r24*i) * r23); - sum -= Math.exp(dir1*x*x + dir2*y*y); + var x = Math.pow(1 + r11, -k) * Math.sin(Math.pow(1 + r12, k) * (i + 0.25 * Math.cos(j) + r14 * j) * r13); + var y = Math.pow(1 + r21, -k) * Math.sin(Math.pow(1 + r22, k) * (j + 0.25 * Math.cos(i) + r24 * i) * r23); + sum -= Math.exp(dir1 * x * x + dir2 * y * y); } g[j * xl + i].z += sum * range; } } - THREE.Terrain.Clamp(g, options); + Clamp(g, options); }; diff --git a/src/images.js b/src/images.ts similarity index 75% rename from src/images.js rename to src/images.ts index 409a387..ced04bf 100644 --- a/src/images.js +++ b/src/images.ts @@ -1,3 +1,6 @@ +import { Vector3 } from "three"; +import { TerrainOptions } from "./basicTypes"; + /** * Convert an image-based heightmap into vertex-based height data. * @@ -9,7 +12,7 @@ * displayed. Valid values are the same as those for the `options` parameter * of {@link THREE.Terrain}(). */ -THREE.Terrain.fromHeightmap = function(g, options) { +export function fromHeightmap(g: Vector3[], options: TerrainOptions) { var canvas = document.createElement('canvas'), context = canvas.getContext('2d'), rows = options.ySegments + 1, @@ -17,13 +20,13 @@ THREE.Terrain.fromHeightmap = function(g, options) { spread = options.maxHeight - options.minHeight; canvas.width = cols; canvas.height = rows; - context.drawImage(options.heightmap, 0, 0, canvas.width, canvas.height); - var data = context.getImageData(0, 0, canvas.width, canvas.height).data; + context!.drawImage(options.heightmap! as HTMLCanvasElement, 0, 0, canvas.width, canvas.height); + var data = context!.getImageData(0, 0, canvas.width, canvas.height).data; for (var row = 0; row < rows; row++) { for (var col = 0; col < cols; col++) { var i = row * cols + col, idx = i * 4; - g[i].z = (data[idx] + data[idx+1] + data[idx+2]) / 765 * spread + options.minHeight; + g[i].z = (data[idx] + data[idx + 1] + data[idx + 2]) / 765 * spread + options.minHeight; } } }; @@ -43,11 +46,11 @@ THREE.Terrain.fromHeightmap = function(g, options) { * @return {HTMLCanvasElement} * A canvas with the relevant heightmap painted on it. */ -THREE.Terrain.toHeightmap = function(g, options) { +export function toHeightmap(g: Vector3[], options: TerrainOptions) { var hasMax = typeof options.maxHeight !== 'undefined', hasMin = typeof options.minHeight !== 'undefined', max = hasMax ? options.maxHeight : -Infinity, - min = hasMin ? options.minHeight : Infinity; + min = hasMin ? options.minHeight : Infinity; if (!hasMax || !hasMin) { var max2 = max, min2 = min; @@ -65,16 +68,16 @@ THREE.Terrain.toHeightmap = function(g, options) { spread = max - min; canvas.width = cols; canvas.height = rows; - var d = context.createImageData(canvas.width, canvas.height), + var d = context!.createImageData(canvas.width, canvas.height), data = d.data; for (var row = 0; row < rows; row++) { for (var col = 0; col < cols; col++) { var i = row * cols + col, - idx = i * 4; - data[idx] = data[idx+1] = data[idx+2] = Math.round(((g[i].z - min) / spread) * 255); - data[idx+3] = 255; + idx = i * 4; + data[idx] = data[idx + 1] = data[idx + 2] = Math.round(((g[i].z - min) / spread) * 255); + data[idx + 3] = 255; } } - context.putImageData(d, 0, 0); + context!.putImageData(d, 0, 0); return canvas; }; diff --git a/src/influences.js b/src/influences.ts similarity index 74% rename from src/influences.js rename to src/influences.ts index 9553a1e..92a4920 100644 --- a/src/influences.js +++ b/src/influences.ts @@ -1,3 +1,16 @@ +import { + AdditiveBlending, + Blending, + MultiplyBlending, + NoBlending, + NormalBlending, + SubtractiveBlending, + Vector3, +} from 'three'; + +import { TerrainOptions, FeatureFunction, BlendingFunction, FalloffFunction } from './basicTypes'; +import { EaseIn } from './core'; + // Allows placing geometrically-described features on a terrain. // If you want these features to look a little less regular, // just apply them before a procedural pass. @@ -6,29 +19,30 @@ /** * Equations describing geographic features. */ -THREE.Terrain.Influences = { - Mesa: function(x) { - return 1.25 * Math.min(0.8, Math.exp(-(x*x))); +export const Influences = { + Mesa: function (x: number) { + return 1.25 * Math.min(0.8, Math.exp(-(x * x))); }, - Hole: function(x) { - return -THREE.Terrain.Influences.Mesa(x); + Hole: function (x: number) { + return -Influences.Mesa(x); }, - Hill: function(x) { + Hill: function (x: number) { // Same curve as EaseInOut, but mirrored and translated. - return x < 0 ? (x+1)*(x+1)*(3-2*(x+1)) : 1-x*x*(3-2*x); + return x < 0 ? (x + 1) * (x + 1) * (3 - 2 * (x + 1)) : 1 - x * x * (3 - 2 * x); }, - Valley: function(x) { - return -THREE.Terrain.Influences.Hill(x); + Valley: function (x: number) { + return -Influences.Hill(x); }, - Dome: function(x) { + Dome: function (x: number) { // Parabola - return -(x+1)*(x-1); + return -(x + 1) * (x - 1); }, // Not meaningful in Additive or Subtractive mode - Flat: function(x) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + Flat: function (x: number) { return 0; }, - Volcano: function(x) { + Volcano: function (x: number) { return 0.94 - 0.32 * (Math.abs(2 * x) + Math.cos(2 * Math.PI * Math.abs(x) + 0.4)); }, }; @@ -71,7 +85,7 @@ THREE.Terrain.Influences = { * origin, and returns the new height for that vertex. (If a custom function * is passed, it can take optional fourth and fifth parameters, which are the * x- and y-distances from the feature's origin, respectively.) - * @param {Number/Function} [e=THREE.Terrain.EaseIn] + * @param {Number | Function} [e=THREE.Terrain.EaseIn] * A function that determines the "falloff" of the feature, i.e. how quickly * the terrain will get close to its height before the feature was applied as * the distance increases from the feature's location. It does this by @@ -84,14 +98,15 @@ THREE.Terrain.Influences = { * functions can also accept optional second and third parameters, which are * the x- and y-distances to the feature origin, respectively.) */ -THREE.Terrain.Influence = function(g, options, f, x, y, r, h, t, e) { - f = f || THREE.Terrain.Influences.Hill; // feature shape - x = typeof x === 'undefined' ? 0.5 : x; // x-location % - y = typeof y === 'undefined' ? 0.5 : y; // y-location % - r = typeof r === 'undefined' ? 64 : r; // radius - h = typeof h === 'undefined' ? 64 : h; // height - t = typeof t === 'undefined' ? THREE.NormalBlending : t; // blending - e = e || THREE.Terrain.EaseIn; // falloff + +export function Influence(g: Vector3[], options: TerrainOptions, + f: FeatureFunction = Influences.Hill, // feature shape + x: number = 0.5, // x-location % + y: number = 0.5, // y-location % + r: number = 64, // radius + h: number = 64, // height + t: Blending | BlendingFunction = NormalBlending, + e: FalloffFunction = EaseIn) { // Find the vertex location of the feature origin var xl = options.xSegments + 1, // # x-vertices yl = options.ySegments + 1, // # y-vertices @@ -113,7 +128,7 @@ THREE.Terrain.Influence = function(g, options, f, x, y, r, h, t, e) { // distance to the feature origin fdx = (i - vx) * xw, fdy = (j - vy) * yw, - fd = Math.sqrt(fdx*fdx + fdy*fdy), + fd = Math.sqrt(fdx * fdx + fdy * fdy), fdr = fd * r1, fdxr = fdx * r1, fdyr = fdy * r1, @@ -121,12 +136,12 @@ THREE.Terrain.Influence = function(g, options, f, x, y, r, h, t, e) { // interpolate using e, then blend according to t. d = f(fdr, fdxr, fdyr) * h * (1 - e(fdr, fdxr, fdyr)); if (fd > r || typeof g[k] == 'undefined') continue; - if (t === THREE.AdditiveBlending) g[k].z += d; // jscs:ignore requireSpaceAfterKeywords - else if (t === THREE.SubtractiveBlending) g[k].z -= d; - else if (t === THREE.MultiplyBlending) g[k].z *= d; - else if (t === THREE.NoBlending) g[k].z = d; - else if (t === THREE.NormalBlending) g[k].z = e(fdr, fdxr, fdyr) * g[k].z + d; - else if (typeof t === 'function') g[k].z = t(g[k].z, d, fdr, fdxr, fdyr); + if (t === AdditiveBlending) g[k].z += d; + else if (t === SubtractiveBlending) g[k].z -= d; + else if (t === MultiplyBlending) g[k].z *= d; + else if (t === NoBlending) g[k].z = d; + else if (t === NormalBlending) g[k].z = e(fdr, fdxr, fdyr) * g[k].z + d; + else if (typeof t === 'function') g[k].z = t(g[k].z, d, fdr, fdxr, fdyr); } } }; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..f970296 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,15 @@ +export * from './terrain'; +export * from './analysis'; +export * from './basicTypes'; +export * from './brownian'; +export * from './core'; +export * from './filters'; +export * from './gaussian'; +export * from './generators'; +export * from './images'; +export * from './influences'; +export * from './materials'; +export * from './noise'; +export * from './scatter'; +export * from './weightedBoxBlurGaussian'; +export * from './worley'; diff --git a/src/materials.js b/src/materials.ts similarity index 64% rename from src/materials.js rename to src/materials.ts index 8dc5ce6..3a46411 100644 --- a/src/materials.js +++ b/src/materials.ts @@ -1,3 +1,5 @@ +import { ShaderLib, UniformsUtils, Texture, RepeatWrapping, ShaderChunk, ShaderMaterial } from 'three'; + /** * Generate a material that blends together textures based on vertex height. * @@ -35,20 +37,34 @@ * contains the coordinates in Three-space of the texel currently being * rendered. */ -THREE.Terrain.generateBlendedMaterial = function(textures) { +interface TextureBasics { + texture: Texture; +} + +interface TextureWithLevels extends TextureBasics { + levels: [number, number, number, number]; +} + +interface TextureWithGLSL extends TextureBasics { + glsl: string; +} + +type TextureParam = TextureBasics | TextureWithLevels | TextureWithGLSL; + +export function generateBlendedMaterial(textures: TextureParam[]) { // Convert numbers to strings of floats so GLSL doesn't barf on "1" instead of "1.0" - function glslifyNumber(n) { - return n === (n|0) ? n+'.0' : n+''; + function glslifyNumber(n: number): string { + return n === (n | 0) ? n + '.0' : n + ''; } - var uniforms = THREE.UniformsUtils.merge([THREE.ShaderLib.lambert.uniforms]), + var uniforms = UniformsUtils.merge([ShaderLib.lambert.uniforms]), declare = '', assign = '', t0Repeat = textures[0].texture.repeat, t0Offset = textures[0].texture.offset; for (var i = 0, l = textures.length; i < l; i++) { // Uniforms - textures[i].texture.wrapS = textures[i].wrapT = THREE.RepeatWrapping; + textures[i].texture.wrapS = textures[i].texture.wrapT = RepeatWrapping; textures[i].texture.needsUpdate = true; uniforms['texture_' + i] = { type: 't', @@ -59,26 +75,31 @@ THREE.Terrain.generateBlendedMaterial = function(textures) { // Declare each texture, then mix them together. declare += 'uniform sampler2D texture_' + i + ';\n'; if (i !== 0) { - var v = textures[i].levels, // Vertex heights at which to blend textures in and out - p = textures[i].glsl, // Or specify a GLSL expression that evaluates to a float between 0.0 and 1.0 indicating how opaque the texture should be at this texel - useLevels = typeof v !== 'undefined', // Use levels if they exist; otherwise, use the GLSL expression - tiRepeat = textures[i].texture.repeat, - tiOffset = textures[i].texture.offset; - if (useLevels) { + const tiRepeat = textures[i].texture.repeat; + const tiOffset = textures[i].texture.offset; + + let blendAmount = ''; + + // Use levels if they exist; otherwise, use the GLSL expression + if (typeof (textures[i] as TextureWithLevels).levels !== 'undefined') { + const v = (textures[i] as TextureWithLevels).levels; + // Vertex heights at which to blend textures in and out // Must fade in; can't start and stop at the same point. // So, if levels are too close, move one of them slightly. if (v[1] - v[0] < 1) v[0] -= 1; if (v[3] - v[2] < 1) v[3] += 1; - for (var j = 0; j < v.length; j++) { - v[j] = glslifyNumber(v[j]); - } + blendAmount = '1.0 - smoothstep(' + glslifyNumber(v[0]) + ', ' + glslifyNumber(v[1]) + ', vPosition.z) + smoothstep(' + glslifyNumber(v[2]) + ', ' + glslifyNumber(v[3]) + ', vPosition.z)'; + } + else { + // GLSL expression that evaluates to a float between 0.0 and 1.0 indicating how opaque the texture should be at this texel + blendAmount = (textures[i] as TextureWithGLSL).glsl; } + // The transparency of the new texture when it is layered on top of the existing color at this texel is // (how far between the start-blending-in and fully-blended-in levels the current vertex is) + // (how far between the start-blending-out and fully-blended-out levels the current vertex is) // So the opacity is 1.0 minus that. - var blendAmount = !useLevels ? p : - '1.0 - smoothstep(' + v[0] + ', ' + v[1] + ', vPosition.z) + smoothstep(' + v[2] + ', ' + v[3] + ', vPosition.z)'; + assign += ' color = mix( ' + 'texture2D( texture_' + i + ', MyvUv * vec2( ' + glslifyNumber(tiRepeat.x) + ', ' + glslifyNumber(tiRepeat.y) + ' ) + vec2( ' + glslifyNumber(tiOffset.x) + ', ' + glslifyNumber(tiOffset.y) + ' ) ), ' + 'color, ' + @@ -106,7 +127,7 @@ THREE.Terrain.generateBlendedMaterial = function(textures) { // side: THREE.FrontSide, uniforms: uniforms, - vertexShader: THREE.ShaderLib.lambert.vertexShader.replace( + vertexShader: ShaderLib.lambert.vertexShader.replace( 'void main() {', 'varying vec2 MyvUv;\nvarying vec3 vPosition;\nvarying vec3 myNormal; void main() {\nMyvUv = uv;\nvPosition = position;\nmyNormal = normal;' ), @@ -120,27 +141,27 @@ THREE.Terrain.generateBlendedMaterial = function(textures) { ' varying vec3 vLightBack;', '#endif', - THREE.ShaderChunk.common, - THREE.ShaderChunk.packing, - THREE.ShaderChunk.dithering_pars_fragment, - THREE.ShaderChunk.color_pars_fragment, - THREE.ShaderChunk.uv_pars_fragment, - THREE.ShaderChunk.uv2_pars_fragment, - THREE.ShaderChunk.map_pars_fragment, - THREE.ShaderChunk.alphamap_pars_fragment, - THREE.ShaderChunk.aomap_pars_fragment, - THREE.ShaderChunk.lightmap_pars_fragment, - THREE.ShaderChunk.emissivemap_pars_fragment, - THREE.ShaderChunk.envmap_pars_fragment, - THREE.ShaderChunk.bsdfs, - THREE.ShaderChunk.lights_pars_begin, - THREE.ShaderChunk.lights_pars_maps, - THREE.ShaderChunk.fog_pars_fragment, - THREE.ShaderChunk.shadowmap_pars_fragment, - THREE.ShaderChunk.shadowmask_pars_fragment, - THREE.ShaderChunk.specularmap_pars_fragment, - THREE.ShaderChunk.logdepthbuf_pars_fragment, - THREE.ShaderChunk.clipping_planes_pars_fragment, + ShaderChunk.common, + ShaderChunk.packing, + ShaderChunk.dithering_pars_fragment, + ShaderChunk.color_pars_fragment, + ShaderChunk.uv_pars_fragment, + ShaderChunk.uv2_pars_fragment, + ShaderChunk.map_pars_fragment, + ShaderChunk.alphamap_pars_fragment, + ShaderChunk.aomap_pars_fragment, + ShaderChunk.lightmap_pars_fragment, + ShaderChunk.emissivemap_pars_fragment, + ShaderChunk.envmap_pars_fragment, + ShaderChunk.bsdfs, + ShaderChunk.lights_pars_begin, + ShaderChunk.lights_pars_maps, + ShaderChunk.fog_pars_fragment, + ShaderChunk.shadowmap_pars_fragment, + ShaderChunk.shadowmask_pars_fragment, + ShaderChunk.specularmap_pars_fragment, + ShaderChunk.logdepthbuf_pars_fragment, + ShaderChunk.clipping_planes_pars_fragment, declare, 'varying vec2 MyvUv;', @@ -149,32 +170,32 @@ THREE.Terrain.generateBlendedMaterial = function(textures) { 'void main() {', - THREE.ShaderChunk.clipping_planes_fragment, + ShaderChunk.clipping_planes_fragment, - 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', - 'vec3 totalEmissiveRadiance = emissive;', + 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', + 'vec3 totalEmissiveRadiance = emissive;', // TODO: The second vector here is the object's "up" vector. Ideally we'd just pass it in directly. 'float slope = acos(max(min(dot(myNormal, vec3(0.0, 0.0, 1.0)), 1.0), -1.0));', ' vec4 diffuseColor = vec4( diffuse, opacity );', ' vec4 color = texture2D( texture_0, MyvUv * vec2( ' + glslifyNumber(t0Repeat.x) + ', ' + glslifyNumber(t0Repeat.y) + ' ) + vec2( ' + glslifyNumber(t0Offset.x) + ', ' + glslifyNumber(t0Offset.y) + ' ) ); // base', - assign, + assign, ' diffuseColor = color;', // ' gl_FragColor = color;', - THREE.ShaderChunk.logdepthbuf_fragment, - THREE.ShaderChunk.map_fragment, - THREE.ShaderChunk.color_fragment, - THREE.ShaderChunk.alphamap_fragment, - THREE.ShaderChunk.alphatest_fragment, - THREE.ShaderChunk.specularmap_fragment, - THREE.ShaderChunk.emissivemap_fragment, + ShaderChunk.logdepthbuf_fragment, + ShaderChunk.map_fragment, + ShaderChunk.color_fragment, + ShaderChunk.alphamap_fragment, + ShaderChunk.alphatest_fragment, + ShaderChunk.specularmap_fragment, + ShaderChunk.emissivemap_fragment, // accumulation ' reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );', - THREE.ShaderChunk.lightmap_fragment, + ShaderChunk.lightmap_fragment, ' reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );', ' #ifdef DOUBLE_SIDED', @@ -184,19 +205,19 @@ THREE.Terrain.generateBlendedMaterial = function(textures) { ' #endif', ' reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();', - // modulation - THREE.ShaderChunk.aomap_fragment, + // modulation + ShaderChunk.aomap_fragment, ' vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;', - THREE.ShaderChunk.normal_flip, - THREE.ShaderChunk.envmap_fragment, + ShaderChunk.normal_flip, + ShaderChunk.envmap_fragment, ' gl_FragColor = vec4( outgoingLight, diffuseColor.a );', // This will probably change in future three.js releases - THREE.ShaderChunk.tonemapping_fragment, - THREE.ShaderChunk.encodings_fragment, - THREE.ShaderChunk.fog_fragment, - THREE.ShaderChunk.premultiplied_alpha_fragment, - THREE.ShaderChunk.dithering_fragment, + ShaderChunk.tonemapping_fragment, + ShaderChunk.encodings_fragment, + ShaderChunk.fog_fragment, + ShaderChunk.premultiplied_alpha_fragment, + ShaderChunk.dithering_fragment, '}' ].join('\n'), }; - return new THREE.ShaderMaterial(params); + return new ShaderMaterial(params); }; diff --git a/src/noise.js b/src/noise.js deleted file mode 100644 index a20e3d0..0000000 --- a/src/noise.js +++ /dev/null @@ -1,186 +0,0 @@ -/** -* Simplex and Perlin noise. -* -* Copied with small edits from https://github.com/josephg/noisejs which is -* public domain. Originally by Stefan Gustavson (stegu@itn.liu.se) with -* optimizations by Peter Eastman (peastman@drizzle.stanford.edu) and converted -* to JavaScript by Joseph Gentle. -*/ - -(function(global) { - var module = global.noise = {}; - - function Grad(x, y, z) { - this.x = x; - this.y = y; - this.z = z; - } - - Grad.prototype.dot2 = function(x, y) { - return this.x*x + this.y*y; - }; - - Grad.prototype.dot3 = function(x, y, z) { - return this.x*x + this.y*y + this.z*z; - }; - - var grad3 = [ - new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), - new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), - new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1), - ]; - - var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103, - 30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94, - 252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171, - 168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122, - 60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161, - 1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159, - 86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147, - 118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183, - 170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129, - 22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228, - 251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239, - 107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4, - 150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215, - 61,156,180]; - // To avoid the need for index wrapping, double the permutation table length - var perm = new Array(512), - gradP = new Array(512); - - // This isn't a very good seeding function, but it works okay. It supports - // 2^16 different seed values. Write your own if you need more seeds. - module.seed = function(seed) { - if (seed > 0 && seed < 1) { - // Scale the seed out - seed *= 65536; - } - - seed = Math.floor(seed); - if (seed < 256) { - seed |= seed << 8; - } - - for (var i = 0; i < 256; i++) { - var v; - if (i & 1) { - v = p[i] ^ (seed & 255); - } - else { - v = p[i] ^ ((seed>>8) & 255); - } - - perm[i] = perm[i + 256] = v; - gradP[i] = gradP[i + 256] = grad3[v % 12]; - } - }; - - module.seed(Math.random()); - - // Skewing and unskewing factors for 2 and 3 dimensions - var F2 = 0.5*(Math.sqrt(3)-1), - G2 = (3-Math.sqrt(3))/6, - F3 = 1/3, - G3 = 1/6; - - // 2D simplex noise - module.simplex = function(xin, yin) { - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var s = (xin+yin)*F2; // Hairy factor for 2D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var t = (i+j)*G2; - var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed - var y0 = yin-j+t; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if (x0 > y0) { // Lower triangle, XY order: (0,0)->(1,0)->(1,1) - i1 = 1; j1 = 0; - } - else { // Upper triangle, YX order: (0,0)->(0,1)->(1,1) - i1 = 0; j1 = 1; - } - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1 + 2 * G2; - // Work out the hashed gradient indices of the three simplex corners - i &= 255; - j &= 255; - var gi0 = gradP[i+perm[j]]; - var gi1 = gradP[i+i1+perm[j+j1]]; - var gi2 = gradP[i+1+perm[j+1]]; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0*x0-y0*y0; - if (t0 < 0) { - n0 = 0; - } - else { - t0 *= t0; - n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient - } - var t1 = 0.5 - x1*x1-y1*y1; - if (t1 < 0) { - n1 = 0; - } - else { - t1 *= t1; - n1 = t1 * t1 * gi1.dot2(x1, y1); - } - var t2 = 0.5 - x2*x2-y2*y2; - if (t2 < 0) { - n2 = 0; - } - else { - t2 *= t2; - n2 = t2 * t2 * gi2.dot2(x2, y2); - } - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70 * (n0 + n1 + n2); - }; - - // ##### Perlin noise stuff - - function fade(t) { - return t*t*t*(t*(t*6-15)+10); - } - - function lerp(a, b, t) { - return (1-t)*a + t*b; - } - - // 2D Perlin Noise - module.perlin = function(x, y) { - // Find unit grid cell containing point - var X = Math.floor(x), - Y = Math.floor(y); - // Get relative xy coordinates of point within that cell - x = x - X; - y = y - Y; - // Wrap the integer cells at 255 (smaller integer period can be introduced here) - X = X & 255; - Y = Y & 255; - - // Calculate noise contributions from each of the four corners - var n00 = gradP[X+perm[Y]].dot2(x, y); - var n01 = gradP[X+perm[Y+1]].dot2(x, y-1); - var n10 = gradP[X+1+perm[Y]].dot2(x-1, y); - var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1); - - // Compute the fade curve value for x - var u = fade(x); - - // Interpolate the four results - return lerp( - lerp(n00, n10, u), - lerp(n01, n11, u), - fade(y) - ); - }; -})(this); diff --git a/src/noise.ts b/src/noise.ts new file mode 100644 index 0000000..0132beb --- /dev/null +++ b/src/noise.ts @@ -0,0 +1,188 @@ +/** +* Simplex and Perlin noise. +* +* Copied with small edits from https://github.com/josephg/noisejs which is +* public domain. Originally by Stefan Gustavson (stegu@itn.liu.se) with +* optimizations by Peter Eastman (peastman@drizzle.stanford.edu) and converted +* to JavaScript by Joseph Gentle. +*/ + +class Grad { + private x: number; + private y: number; + private z: number; + + public constructor(x: number, y: number, z: number) { + this.x = x; + this.y = y; + this.z = z; + } + + public dot2(x: number, y: number): number { + return this.x * x + this.y * y; + } + + public dot3(x: number, y: number, z: number): number { + return this.x * x + this.y * y + this.z * z; + } +} + +var grad3 = [ + new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0), + new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1), + new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1), +]; + +var p = [151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, + 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, + 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, + 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, + 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, + 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, + 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, + 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, + 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, + 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, + 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, + 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, + 61, 156, 180]; +// To avoid the need for index wrapping, double the permutation table length +var perm = new Array(512), + gradP = new Array(512); + +// This isn't a very good seeding function, but it works okay. It supports +// 2^16 different seed values. Write your own if you need more seeds. +function fSeed(seed: number) { + if (seed > 0 && seed < 1) { + // Scale the seed out + seed *= 65536; + } + + seed = Math.floor(seed); + if (seed < 256) { + seed |= seed << 8; + } + + for (var i = 0; i < 256; i++) { + var v; + if (i & 1) { + v = p[i] ^ (seed & 255); + } + else { + v = p[i] ^ ((seed >> 8) & 255); + } + + perm[i] = perm[i + 256] = v; + gradP[i] = gradP[i + 256] = grad3[v % 12]; + } +}; + +export { fSeed as seed }; + +fSeed(Math.random()); + +// Skewing and unskewing factors for 2 dimensions +var F2 = 0.5 * (Math.sqrt(3) - 1), + G2 = (3 - Math.sqrt(3)) / 6; + +// 2D simplex noise +export function simplex(xin: number, yin: number): number { + var n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + var s = (xin + yin) * F2; // Hairy factor for 2D + var i = Math.floor(xin + s); + var j = Math.floor(yin + s); + var t = (i + j) * G2; + var x0 = xin - i + t; // The x,y distances from the cell origin, unskewed + var y0 = yin - j + t; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { // Lower triangle, XY order: (0,0)->(1,0)->(1,1) + i1 = 1; j1 = 0; + } + else { // Upper triangle, YX order: (0,0)->(0,1)->(1,1) + i1 = 0; j1 = 1; + } + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1 + 2 * G2; + // Work out the hashed gradient indices of the three simplex corners + i &= 255; + j &= 255; + var gi0 = gradP[i + perm[j]]; + var gi1 = gradP[i + i1 + perm[j + j1]]; + var gi2 = gradP[i + 1 + perm[j + 1]]; + // Calculate the contribution from the three corners + var t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) { + n0 = 0; + } + else { + t0 *= t0; + n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) { + n1 = 0; + } + else { + t1 *= t1; + n1 = t1 * t1 * gi1.dot2(x1, y1); + } + var t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) { + n2 = 0; + } + else { + t2 *= t2; + n2 = t2 * t2 * gi2.dot2(x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70 * (n0 + n1 + n2); +}; + +// ##### Perlin noise stuff + +function fade(t: number): number { + return t * t * t * (t * (t * 6 - 15) + 10); +} + +function lerp(a: number, b: number, t: number): number { + return (1 - t) * a + t * b; +} + +// 2D Perlin Noise +export function perlin(x: number, y: number) { + // Find unit grid cell containing point + var X = Math.floor(x), + Y = Math.floor(y); + // Get relative xy coordinates of point within that cell + x = x - X; + y = y - Y; + // Wrap the integer cells at 255 (smaller integer period can be introduced here) + X = X & 255; + Y = Y & 255; + + // Calculate noise contributions from each of the four corners + var n00 = gradP[X + perm[Y]].dot2(x, y); + var n01 = gradP[X + perm[Y + 1]].dot2(x, y - 1); + var n10 = gradP[X + 1 + perm[Y]].dot2(x - 1, y); + var n11 = gradP[X + 1 + perm[Y + 1]].dot2(x - 1, y - 1); + + // Compute the fade curve value for x + var u = fade(x); + + // Interpolate the four results + return lerp( + lerp(n00, n10, u), + lerp(n01, n11, u), + fade(y) + ); +}; diff --git a/src/scatter.js b/src/scatter.ts similarity index 70% rename from src/scatter.js rename to src/scatter.ts index d58299b..bf9b5f4 100644 --- a/src/scatter.js +++ b/src/scatter.ts @@ -1,3 +1,23 @@ +import { BufferGeometry, Geometry, Mesh, Object3D, Vector3, Face3 } from 'three'; + +import { HeightmapFunction, TerrainOptions } from './basicTypes'; +import { EaseInOut } from './core'; +import { Clamp } from './filters'; + +type SpreadFunction = (v: Vector3, k: number, f: Face3, faceX: number, faceY: number) => boolean; +interface ScatterOptions { + mesh: Mesh; + spread: number | SpreadFunction; + smoothSpread: number; + scene: Object3D; + sizeVariance: number; + randomness: () => (number | number[]); + maxSlope: number; + maxTilt: number; + w: number; + h: number; +} + /** * Scatter a mesh across the terrain. * @@ -47,19 +67,19 @@ * return value of a call to `THREE.Terrain()` or added to that return value; * otherwise the position and rotation of the meshes will be wrong. */ -THREE.Terrain.ScatterMeshes = function(geometry, options) { - if (!options.mesh) { +export function ScatterMeshes(geometry: Geometry, inputOptions: Partial) { + if (!inputOptions.mesh) { console.error('options.mesh is required for THREE.Terrain.ScatterMeshes but was not passed'); return; } - if (geometry instanceof THREE.BufferGeometry) { - console.warn('The terrain mesh is using BufferGeometry but THREE.Terrain.ScatterMeshes can only work with Geometry.'); + if (geometry instanceof BufferGeometry) { + console.warn('The terrain mesh is using BufferGeometry but ScatterMeshes can only work with Geometry.'); return; } - if (!options.scene) { - options.scene = new THREE.Object3D(); + if (!inputOptions.scene) { + inputOptions.scene = new Object3D(); } - var defaultOptions = { + let options = { spread: 0.025, smoothSpread: 0, sizeVariance: 0.1, @@ -68,32 +88,29 @@ THREE.Terrain.ScatterMeshes = function(geometry, options) { maxTilt: Infinity, w: 0, h: 0, - }; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - options[opt] = typeof options[opt] === 'undefined' ? defaultOptions[opt] : options[opt]; - } - } + ...inputOptions + } as ScatterOptions; - var spreadIsNumber = typeof options.spread === 'number', - randomHeightmap, - randomness, + let randomHeightmap: number | number[], + randomness: (k: number) => number = () => 0.0, spreadRange = 1 / options.smoothSpread, doubleSizeVariance = options.sizeVariance * 2, v = geometry.vertices, meshes = [], - up = options.mesh.up.clone().applyAxisAngle(new THREE.Vector3(1, 0, 0), 0.5*Math.PI); - if (spreadIsNumber) { + up = options.mesh.up.clone().applyAxisAngle(new Vector3(1, 0, 0), 0.5 * Math.PI); + + if (typeof options.spread === 'number') { randomHeightmap = options.randomness(); - randomness = typeof randomHeightmap === 'number' ? Math.random : function(k) { return randomHeightmap[k]; }; + randomness = typeof randomHeightmap === 'number' ? Math.random : function (k: number) { return (randomHeightmap as number[])[k]; }; } + // geometry.computeFaceNormals(); - for (var i = 0, w = options.w*2; i < w; i++) { + for (var i = 0, w = options.w * 2; i < w; i++) { for (var j = 0, h = options.h; j < h; j++) { - var key = j*w + i, + var key = j * w + i, f = geometry.faces[key], place = false; - if (spreadIsNumber) { + if (typeof options.spread === 'number') { var rv = randomness(key); if (rv < options.spread) { place = true; @@ -102,11 +119,11 @@ THREE.Terrain.ScatterMeshes = function(geometry, options) { // Interpolate rv between spread and spread + smoothSpread, // then multiply that "easing" value by the probability // that a mesh would get placed on a given face. - place = THREE.Terrain.EaseInOut((rv - options.spread) * spreadRange) * options.spread > Math.random(); + place = EaseInOut((rv - options.spread) * spreadRange) * options.spread > Math.random(); } } else { - place = options.spread(v[f.a], key, f, i, j); + place = (options.spread as SpreadFunction)(v[f.a], key, f, i, j); } if (place) { // Don't place a mesh if the angle is too steep. @@ -142,19 +159,19 @@ THREE.Terrain.ScatterMeshes = function(geometry, options) { // Merge geometries. var k, l; - if (options.mesh.geometry instanceof THREE.Geometry) { - var g = new THREE.Geometry(); + if (options.mesh.geometry instanceof Geometry) { + var g = new Geometry(); for (k = 0, l = meshes.length; k < l; k++) { var m = meshes[k]; m.updateMatrix(); - g.merge(m.geometry, m.matrix); + g.merge(m.geometry as Geometry, m.matrix); } /* if (!(options.mesh.material instanceof THREE.MeshFaceMaterial)) { g = THREE.BufferGeometryUtils.fromGeometry(g); } */ - options.scene.add(new THREE.Mesh(g, options.mesh.material)); + options.scene.add(new Mesh(g, options.mesh.material)); } // There's no BufferGeometry merge method implemented yet. else { @@ -194,30 +211,61 @@ THREE.Terrain.ScatterMeshes = function(geometry, options) { * `options.randomness` parameter to the {@link THREE.Terrain.ScatterMeshes} * function. */ -THREE.Terrain.ScatterHelper = function(method, options, skip, threshold) { - skip = skip || 1; - threshold = threshold || 0.25; +export function ScatterHelper(method: HeightmapFunction, options: TerrainOptions, skip: number = 1, threshold: number = 0.25) { options.frequency = options.frequency || 2.5; - var clonedOptions = {}; - for (var opt in options) { - if (options.hasOwnProperty(opt)) { - clonedOptions[opt] = options[opt]; - } - } + let clonedOptions = { ...options }; clonedOptions.xSegments *= 2; clonedOptions.stretch = true; clonedOptions.maxHeight = 1; clonedOptions.minHeight = 0; - var heightmap = THREE.Terrain.heightmapArray(method, clonedOptions); + var heightmap = heightmapArray(method, clonedOptions); for (var i = 0, l = heightmap.length; i < l; i++) { if (i % skip || Math.random() > threshold) { heightmap[i] = 1; // 0 = place, 1 = don't place } } - return function() { + return function () { return heightmap; }; }; + +/** + * Generate a 1D array containing random heightmap data. + * + * This is like {@link THREE.Terrain.toHeightmap} except that instead of + * generating the Three.js mesh and material information you can just get the + * height data. + * + * @param {Function} method + * The method to use to generate the heightmap data. Works with function that + * would be an acceptable value for the `heightmap` option for the + * {@link THREE.Terrain} function. + * @param {Number} options + * The same as the options parameter for the {@link THREE.Terrain} function. + */ +export function heightmapArray(method: Function, options: TerrainOptions) { + var arr = new Array((options.xSegments + 1) * (options.ySegments + 1)), + l = arr.length, + i; + // The heightmap functions provided by this script operate on THREE.Vector3 + // objects by changing the z field, so we need to make that available. + // Unfortunately that means creating a bunch of objects we're just going to + // throw away, but a conscious decision was made here to optimize for the + // vector case. + for (i = 0; i < l; i++) { + arr[i] = { z: 0 }; + } + options.minHeight = options.minHeight || 0; + options.maxHeight = typeof options.maxHeight === 'undefined' ? 1 : options.maxHeight; + options.stretch = options.stretch || false; + method(arr, options); + Clamp(arr, options); + for (i = 0; i < l; i++) { + arr[i] = arr[i].z; + } + return arr; +}; + diff --git a/src/terrain.ts b/src/terrain.ts new file mode 100644 index 0000000..2c4735d --- /dev/null +++ b/src/terrain.ts @@ -0,0 +1,199 @@ +import { BufferGeometry, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry } from 'three'; + +import { Optimization, TerrainOptions } from './basicTypes'; +import { Linear } from './core'; +import { Clamp, Smooth, Step, Turbulence } from './filters'; +import { DiamondSquare } from './generators'; +import { fromHeightmap } from './images'; + +/** + * A terrain object for use with the Three.js library. + * + * Usage: `var terrainScene = THREE.Terrain();` + * + * @param {Object} [options] + * An optional map of settings that control how the terrain is constructed + * and displayed. Options include: + * + * - `after`: A function to run after other transformations on the terrain + * produce the highest-detail heightmap, but before optimizations and + * visual properties are applied. Takes two parameters, which are the same + * as those for {@link THREE.Terrain.DiamondSquare}: an array of + * `THREE.Vector3` objects representing the vertices of the terrain, and a + * map of options with the same available properties as the `options` + * parameter for the `THREE.Terrain` function. + * - `easing`: A function that affects the distribution of slopes by + * interpolating the height of each vertex along a curve. Valid values + * include `THREE.Terrain.Linear` (the default), `THREE.Terrain.EaseIn`, + * `THREE.Terrain.EaseOut`, `THREE.Terrain.EaseInOut`, + * `THREE.Terrain.InEaseOut`, and any custom function that accepts a float + * between 0 and 1 and returns a float between 0 and 1. + * - `frequency`: For terrain generation methods that support it (Perlin, + * Simplex, and Worley) the octave of randomness. This basically controls + * how big features of the terrain will be (higher frequencies result in + * smaller features). Often running multiple generation functions with + * different frequencies and heights results in nice detail, as + * the PerlinLayers and SimplexLayers methods demonstrate. (The counterpart + * to frequency, amplitude, is represented by the difference between the + * `maxHeight` and `minHeight` parameters.) Defaults to 2.5. + * - `heightmap`: Either a canvas or pre-loaded image (from the same domain + * as the webpage or served with a CORS-friendly header) representing + * terrain height data (lighter pixels are higher); or a function used to + * generate random height data for the terrain. Valid random functions are + * specified in `generators.js` (or custom functions with the same + * signature). Ideally heightmap images have the same number of pixels as + * the terrain has vertices, as determined by the `xSegments` and + * `ySegments` options, but this is not required. If the heightmap is a + * different size, vertex height values will be interpolated.) Defaults to + * `THREE.Terrain.DiamondSquare`. + * - `material`: a THREE.Material instance used to display the terrain. + * Defaults to `new THREE.MeshBasicMaterial({color: 0xee6633})`. + * - `maxHeight`: the highest point, in Three.js units, that a peak should + * reach. Defaults to 100. Setting to `undefined`, `null`, or `Infinity` + * removes the cap, but this is generally not recommended because many + * generators and filters require a vertical range. Instead, consider + * setting the `stretch` option to `false`. + * - `minHeight`: the lowest point, in Three.js units, that a valley should + * reach. Defaults to -100. Setting to `undefined`, `null`, or `-Infinity` + * removes the cap, but this is generally not recommended because many + * generators and filters require a vertical range. Instead, consider + * setting the `stretch` option to `false`. + * - `steps`: If this is a number above 1, the terrain will be paritioned + * into that many flat "steps," resulting in a blocky appearance. Defaults + * to 1. + * - `stretch`: Determines whether to stretch the heightmap across the + * maximum and minimum height range if the height range produced by the + * `heightmap` property is smaller. Defaults to true. + * - `turbulent`: Whether to perform a turbulence transformation. Defaults to + * false. + * - `useBufferGeometry`: a Boolean indicating whether to use + * THREE.BufferGeometry instead of THREE.Geometry for the Terrain plane. + * Defaults to `false`. + * - `xSegments`: The number of segments (rows) to divide the terrain plane + * into. (This basically determines how detailed the terrain is.) Defaults + * to 63. + * - `xSize`: The width of the terrain in Three.js units. Defaults to 1024. + * Rendering might be slightly faster if this is a multiple of + * `options.xSegments + 1`. + * - `ySegments`: The number of segments (columns) to divide the terrain + * plane into. (This basically determines how detailed the terrain is.) + * Defaults to 63. + * - `ySize`: The length of the terrain in Three.js units. Defaults to 1024. + * Rendering might be slightly faster if this is a multiple of + * `options.ySegments + 1`. + */ + +export function Terrain(givenOptions: Partial) { + var defaultOptions: TerrainOptions = { + after: null, + easing: Linear, + heightmap: DiamondSquare, + material: null, + maxHeight: 100, + minHeight: -100, + optimization: Optimization.NONE, + frequency: 2.5, + steps: 1, + stretch: true, + turbulent: false, + useBufferGeometry: false, + xSegments: 63, + xSize: 1024, + ySegments: 63, + ySize: 1024, + }; + + let options: TerrainOptions & { _mesh: Mesh | null } + = { ...defaultOptions, ...(givenOptions || {}), _mesh: null /* internal, only */ }; + + options.material = options.material || new MeshBasicMaterial({ color: 0xee6633 }); + + // Encapsulating the terrain in a parent object allows us the flexibility + // to more easily have multiple meshes for optimization purposes. + var scene = new Object3D(); + // Planes are initialized on the XY plane, so rotate the plane to make it lie flat. + scene.rotation.x = -0.5 * Math.PI; + + // Create the terrain mesh. + // To save memory, it is possible to re-use a pre-existing mesh. + const { _mesh } = options; + let mesh: Mesh; + let geometry: PlaneGeometry; + if (_mesh && _mesh.geometry.type === 'PlaneGeometry') { + mesh = _mesh; + geometry = _mesh.geometry as PlaneGeometry; + const { parameters, vertices } = geometry; + if (parameters.widthSegments === options.xSegments && + (mesh.geometry as any).parameters.heightSegments === options.ySegments) { + mesh.material = options.material; + mesh.scale.x = options.xSize / parameters.width; + mesh.scale.y = options.ySize / parameters.height; + for (var i = 0, l = vertices.length; i < l; i++) { + vertices[i].z = 0; + } + } + } + else { + geometry = new PlaneGeometry(options.xSize, options.ySize, options.xSegments, options.ySegments); + mesh = new Mesh(geometry, options.material); + } + delete options._mesh; // Remove the reference for GC + + // Assign elevation data to the terrain plane from a heightmap or function. + if (options.heightmap instanceof HTMLCanvasElement || options.heightmap instanceof Image) { + fromHeightmap(geometry.vertices, options); + } + else if (typeof options.heightmap === 'function') { + options.heightmap(geometry.vertices, options); + } + else { + console.warn('An invalid value was passed for `options.heightmap`: ' + options.heightmap); + } + Normalize(mesh, options); + + if (options.useBufferGeometry) { + mesh.geometry = (new BufferGeometry()).fromGeometry(geometry); + } + + // lod.addLevel(mesh, options.unit * 10 * Math.pow(2, lodLevel)); + + scene.add(mesh); + return scene; +}; + +/** + * Normalize the terrain after applying a heightmap or filter. + * + * This applies turbulence, steps, and height clamping; calls the `after` + * callback; updates normals and the bounding sphere; and marks vertices as + * dirty. + * + * @param {THREE.Mesh} mesh + * The terrain mesh. + * @param {Object} options + * A map of settings that control how the terrain is constructed and + * displayed. Valid options are the same as for {@link THREE.Terrain}(). + */ +export function Normalize(mesh: Mesh, options: TerrainOptions) { + const geometry = mesh.geometry as PlaneGeometry; + var v = geometry.vertices; + if (options.turbulent) { + Turbulence(v, options); + } + if (options.steps && options.steps > 1) { + Step(v, options.steps); + Smooth(v, options); + } + // Keep the terrain within the allotted height range if necessary, and do easing. + Clamp(v, options); + // Call the "after" callback + if (typeof options.after === 'function') { + options.after(v, options); + } + // Mark the geometry as having changed and needing updates. + geometry.verticesNeedUpdate = true; + geometry.normalsNeedUpdate = true; + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); +}; diff --git a/src/weightedBoxBlurGaussian.js b/src/weightedBoxBlurGaussian.ts similarity index 68% rename from src/weightedBoxBlurGaussian.js rename to src/weightedBoxBlurGaussian.ts index 1bb6bde..dfe5a09 100644 --- a/src/weightedBoxBlurGaussian.js +++ b/src/weightedBoxBlurGaussian.ts @@ -1,5 +1,6 @@ -// jscs:disable disallowSpaceBeforeSemicolon, requireBlocksOnNewline -(function() { +import { TerrainOptions } from "./basicTypes"; +import { Vector3 } from "three"; +import { fromArray1D, toArray1D } from "./core"; /** * Perform Gaussian smoothing on terrain vertices. @@ -18,11 +19,11 @@ * The number of box blurs to use in the approximation. Larger values result * in slower but more accurate smoothing. */ -THREE.Terrain.GaussianBoxBlur = function(g, options, s, n) { - THREE.Terrain.fromArray1D(g, gaussianBoxBlur( - THREE.Terrain.toArray1D(g, options), - options.xSegments+1, - options.ySegments+1, +export function GaussianBoxBlur(g: Vector3[], options: TerrainOptions, s: number = 1, n: number = 3) { + fromArray1D(g, gaussianBoxBlur( + toArray1D(g), + options.xSegments + 1, + options.ySegments + 1, s, n )); @@ -59,13 +60,11 @@ THREE.Terrain.GaussianBoxBlur = function(g, options, s, n) { * @return Number[] * An array representing the blurred channel. */ -function gaussianBoxBlur(scl, w, h, r, n, tcl) { - if (typeof r === 'undefined') r = 1; - if (typeof n === 'undefined') n = 3; +function gaussianBoxBlur(scl: Float64Array, w: number, h: number, r: number = 1, n: number = 3, tcl?: Float64Array) { if (typeof tcl === 'undefined') tcl = new Float64Array(scl.length); var boxes = boxesForGauss(r, n); for (var i = 0; i < n; i++) { - boxBlur(scl, tcl, w, h, (boxes[i]-1)/2); + boxBlur(scl, tcl, w, h, (boxes[i] - 1) / 2); } return tcl; } @@ -81,14 +80,14 @@ function gaussianBoxBlur(scl, w, h, r, n, tcl) { * The number of boxes (also the number of box blurs you want to perform * using those boxes). */ -function boxesForGauss(sigma, n) { +function boxesForGauss(sigma: number, n: number) { // Calculate how far out we need to go to capture the bulk of the distribution. - var wIdeal = Math.sqrt(12*sigma*sigma/n + 1); // Ideal averaging filter width + var wIdeal = Math.sqrt(12 * sigma * sigma / n + 1); // Ideal averaging filter width var wl = Math.floor(wIdeal); // Lower odd integer bound on the width if (wl % 2 === 0) wl--; - var wu = wl+2; // Upper odd integer bound on the width + var wu = wl + 2; // Upper odd integer bound on the width - var mIdeal = (12*sigma*sigma - n*wl*wl - 4*n*wl - 3*n)/(-4*wl - 4); + var mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4); var m = Math.round(mIdeal); // var sigmaActual = Math.sqrt( (m*wl*wl + (n-m)*wu*wu - n)/12 ); @@ -102,7 +101,7 @@ function boxesForGauss(sigma, n) { * * Uses the same parameters as gaussblur(). */ -function boxBlur(scl, tcl, w, h, r) { +function boxBlur(scl: Float64Array, tcl: Float64Array, w: number, h: number, r: number) { for (var i = 0, l = scl.length; i < l; i++) { tcl[i] = scl[i]; } boxBlurH(tcl, scl, w, h, r); boxBlurV(scl, tcl, w, h, r); @@ -113,24 +112,24 @@ function boxBlur(scl, tcl, w, h, r) { * * Uses the same parameters as gaussblur(). */ -function boxBlurH(scl, tcl, w, h, r) { - var iarr = 1 / (r+r+1); // averaging adjustment parameter +function boxBlurH(scl: Float64Array, tcl: Float64Array, w: number, h: number, r: number) { + var iarr = 1 / (r + r + 1); // averaging adjustment parameter for (var i = 0; i < h; i++) { var ti = i * w, // current target index li = ti, // current left side of the examined range ri = ti + r, // current right side of the examined range fv = scl[ti], // first value in the row lv = scl[ti + w - 1], // last value in the row - val = (r+1) * fv, // target value, accumulated over examined points + val = (r + 1) * fv, // target value, accumulated over examined points j; // Sum the source values in the box for (j = 0; j < r; j++) { val += scl[ti + j]; } // Compute the target value by taking the average of the surrounding // values. This is done by adding the deviations so far and adjusting, // accounting for the edges by extending the first and last values. - for (j = 0 ; j <= r ; j++) { val += scl[ri++] - fv ; tcl[ti++] = val*iarr; } - for (j = r+1; j < w-r; j++) { val += scl[ri++] - scl[li++]; tcl[ti++] = val*iarr; } - for (j = w-r; j < w ; j++) { val += lv - scl[li++]; tcl[ti++] = val*iarr; } + for (j = 0; j <= r; j++) { val += scl[ri++] - fv; tcl[ti++] = val * iarr; } + for (j = r + 1; j < w - r; j++) { val += scl[ri++] - scl[li++]; tcl[ti++] = val * iarr; } + for (j = w - r; j < w; j++) { val += lv - scl[li++]; tcl[ti++] = val * iarr; } } } @@ -139,25 +138,23 @@ function boxBlurH(scl, tcl, w, h, r) { * * Uses the same parameters as gaussblur(). */ -function boxBlurV(scl, tcl, w, h, r) { - var iarr = 1 / (r+r+1); // averaging adjustment parameter +function boxBlurV(scl: Float64Array, tcl: Float64Array, w: number, h: number, r: number) { + var iarr = 1 / (r + r + 1); // averaging adjustment parameter for (var i = 0; i < w; i++) { var ti = i, // current target index li = ti, // current top of the examined range - ri = ti+r*w, // current bottom of the examined range + ri = ti + r * w, // current bottom of the examined range fv = scl[ti], // first value in the column - lv = scl[ti + w*(h-1)], // last value in the column - val = (r+1) * fv, // target value, accumulated over examined points + lv = scl[ti + w * (h - 1)], // last value in the column + val = (r + 1) * fv, // target value, accumulated over examined points j; // Sum the source values in the box for (j = 0; j < r; j++) { val += scl[ti + j * w]; } // Compute the target value by taking the average of the surrounding // values. This is done by adding the deviations so far and adjusting, // accounting for the edges by extending the first and last values. - for (j = 0 ; j <= r ; j++) { val += scl[ri] - fv ; tcl[ti] = val*iarr; ri+=w; ti+=w; } - for (j = r+1; j < h-r; j++) { val += scl[ri] - scl[li]; tcl[ti] = val*iarr; li+=w; ri+=w; ti+=w; } - for (j = h-r; j < h ; j++) { val += lv - scl[li]; tcl[ti] = val*iarr; li+=w; ti+=w; } + for (j = 0; j <= r; j++) { val += scl[ri] - fv; tcl[ti] = val * iarr; ri += w; ti += w; } + for (j = r + 1; j < h - r; j++) { val += scl[ri] - scl[li]; tcl[ti] = val * iarr; li += w; ri += w; ti += w; } + for (j = h - r; j < h; j++) { val += lv - scl[li]; tcl[ti] = val * iarr; li += w; ti += w; } } } - -})(); diff --git a/src/worley.js b/src/worley.js deleted file mode 100644 index e580f64..0000000 --- a/src/worley.js +++ /dev/null @@ -1,220 +0,0 @@ -(function() { - /* - * A set of functions to calculate the 2D distance between two vectors. - * - * The other alternatives are distanceTo (Euclidean) and distanceToSquared - * (Euclidean squared). - */ - THREE.Vector2.prototype.distanceToManhattan = function(b) { - return Math.abs(this.x - b.x) + Math.abs(this.y - b.y); - }; - THREE.Vector2.prototype.distanceToChebyshev = function(b) { - var c = Math.abs(this.x - b.x), - d = Math.abs(this.y - b.y); - return c <= d ? d : c; - }; - THREE.Vector2.prototype.distanceToQuadratic = function(b) { - var c = Math.abs(this.x - b.x), - d = Math.abs(this.y - b.y); - return c*c + c*d + d*d; - }; - - /** - * Find the Voronoi centroid closest to the current terrain vertex. - * - * This approach is naive, but since the number of cells isn't typically - * very big, it's plenty fast enough. - * - * Alternatives approaches include using Fortune's algorithm or tracking - * cells based on a grid. - */ - function distanceToNearest(coords, points, distanceType) { - var color = Infinity, - distanceFunc = 'distanceTo' + distanceType; - for (var k = 0; k < points.length; k++) { - var d = points[k][distanceFunc](coords); - if (d < color) { - color = d; - } - } - return color; - } - - /** - * Generate random terrain using Worley noise. - * - * Worley noise is also known as Cell or Voronoi noise. It is generated by - * scattering a bunch of points in heightmap-space, then setting the height - * of every point in the heightmap based on how close it is to the closest - * scattered point (or the nth-closest point, but this results in - * heightmaps that don't look much like terrain). - * - * @param {THREE.Vector3[]} g - * The vertex array for plane geometry to modify with heightmap data. - * This method sets the `z` property of each vertex. - * @param {Object} options - * A map of settings that control how the terrain is constructed and - * displayed. Valid values are the same as those for the `options` - * parameter of {@link THREE.Terrain}(), plus three additional available - * properties: - * - `distanceType`: The name of a method to use to calculate the - * distance between a point in the heightmap and a Voronoi centroid in - * order to determine the height of that point. Available methods - * include 'Manhattan', 'Chebyshev', 'Quadratic', 'Squared' (squared - * Euclidean), and '' (the empty string, meaning Euclidean, the - * default). - * - `worleyDistanceTransformation`: A function that takes the distance - * from a heightmap vertex to a Voronoi centroid and returns a relative - * height for that vertex. Defaults to function(d) { return -d; }. - * Interesting choices of algorithm include - * `0.5 + 1.0 * Math.cos((0.5*d-1) * Math.PI) - d`, which produces - * interesting stepped cones, and `-Math.sqrt(d)`, which produces sharp - * peaks resembling stalagmites. - * - `worleyDistribution`: A function to use to distribute Voronoi - * centroids. Available methods include - * `THREE.Terrain.Worley.randomPoints` (the default), - * `THREE.Terrain.Worley.PoissonDisks`, and any function that returns - * an array of `THREE.Vector2` instances. You can wrap the PoissonDisks - * function to use custom parameters. - * - `worleyPoints`: The number of Voronoi cells to use (must be at least - * one). Calculated by default based on the size of the terrain. - */ - THREE.Terrain.Worley = function(g, options) { - var points = (options.worleyDistribution || THREE.Terrain.Worley.randomPoints)(options.xSegments, options.ySegments, options.worleyPoints), - transform = options.worleyDistanceTransformation || function(d) { return -d; }, - currentCoords = new THREE.Vector2(0, 0); - // The height of each heightmap vertex is the distance to the closest Voronoi centroid - for (var i = 0, xl = options.xSegments + 1; i < xl; i++) { - for (var j = 0; j < options.ySegments + 1; j++) { - currentCoords.x = i; - currentCoords.y = j; - g[j*xl+i].z = transform(distanceToNearest(currentCoords, points, options.distanceType || '')); - } - } - // We set the heights to distances so now we need to normalize - THREE.Terrain.Clamp(g, { - maxHeight: options.maxHeight, - minHeight: options.minHeight, - stretch: true, - }); - }; - - /** - * Randomly distribute points in space. - */ - THREE.Terrain.Worley.randomPoints = function(width, height, numPoints) { - numPoints = numPoints || Math.floor(Math.sqrt(width * height * 0.025)) || 1; - var points = new Array(numPoints); - for (var i = 0; i < numPoints; i++) { - points[i] = new THREE.Vector2( - Math.random() * width, - Math.random() * height - ); - } - return points; - }; - - /* Utility functions for Poisson Disks. */ - - function removeAndReturnRandomElement(arr) { - return arr.splice(Math.floor(Math.random() * arr.length), 1)[0]; - } - - function putInGrid(grid, point, cellSize) { - var gx = Math.floor(point.x / cellSize), - gy = Math.floor(point.y / cellSize); - if (!grid[gx]) grid[gx] = []; - grid[gx][gy] = point; - } - - function inRectangle(point, width, height) { - return point.x >= 0 && // jscs:ignore requireSpaceAfterKeywords - point.y >= 0 && - point.x <= width+1 && - point.y <= height+1; - } - - function inNeighborhood(grid, point, minDist, cellSize) { - var gx = Math.floor(point.x / cellSize), - gy = Math.floor(point.y / cellSize); - for (var x = gx - 1; x <= gx + 1; x++) { - for (var y = gy - 1; y <= gy + 1; y++) { - if (x !== gx && y !== gy && - typeof grid[x] !== 'undefined' && typeof grid[x][y] !== 'undefined') { - var cx = x * cellSize, - cy = y * cellSize; - if (Math.sqrt((point.x - cx) * (point.x - cx) + (point.y - cy) * (point.y - cy)) < minDist) { - return true; - } - } - } - } - return false; - } - - function generateRandomPointAround(point, minDist) { - var radius = minDist * (Math.random() + 1), - angle = 2 * Math.PI * Math.random(); - return new THREE.Vector2( - point.x + radius * Math.cos(angle), - point.y + radius * Math.sin(angle) - ); - } - - /** - * Generate a set of points using Poisson disk sampling. - * - * Useful for clustering scattered meshes and Voronoi cells for Worley noise. - * - * Ported from pseudocode at http://devmag.org.za/2009/05/03/poisson-disk-sampling/ - * - * @param {Object} options - * A map of settings that control how the resulting noise should be generated - * (with the same parameters as the `options` parameter to the - * `THREE.Terrain` function). - * - * @return {THREE.Vector2[]} - * An array of points. - */ - THREE.Terrain.Worley.PoissonDisks = function(width, height, numPoints, minDist) { - numPoints = numPoints || Math.floor(Math.sqrt(width * height * 0.2)) || 1; - minDist = Math.sqrt((width + height) * 2.5); - if (minDist > numPoints * 0.67) minDist = numPoints * 0.67; - var cellSize = minDist / Math.sqrt(2); - if (cellSize < 2) cellSize = 2; - - var grid = []; - - var processList = [], - samplePoints = []; - - var firstPoint = new THREE.Vector2( - Math.random() * width, - Math.random() * height - ); - processList.push(firstPoint); - samplePoints.push(firstPoint); - putInGrid(grid, firstPoint, cellSize); - - var count = 0; - while (processList.length) { - var point = removeAndReturnRandomElement(processList); - for (var i = 0; i < numPoints; i++) { - // optionally, minDist = perlin(point.x / width, point.y / height) - var newPoint = generateRandomPointAround(point, minDist); - if (inRectangle(newPoint, width, height) && !inNeighborhood(grid, newPoint, minDist, cellSize)) { - processList.push(newPoint); - samplePoints.push(newPoint); - putInGrid(grid, newPoint, cellSize); - if (samplePoints.length >= numPoints) break; - } - } - if (samplePoints.length >= numPoints) break; - // Sanity check - if (++count > numPoints*numPoints) { - break; - } - } - return samplePoints; - }; -})(); diff --git a/src/worley.ts b/src/worley.ts new file mode 100644 index 0000000..5000f05 --- /dev/null +++ b/src/worley.ts @@ -0,0 +1,256 @@ +import { Vector2, Vector3 } from "three"; +import { TerrainOptions } from "./basicTypes"; +import { Clamp } from "./filters"; +import { Linear } from "./core"; + +interface DistanceMetrics { + [index: string]: (a: Vector2, b: Vector2) => number; +} + +/** + * A set of functions to calculate the 2D distance between two vectors. + */ +const distanceMetrics: DistanceMetrics = { + distanceToManhattan: function (a: Vector2, b: Vector2): number { + return Math.abs(a.x - b.x) + Math.abs(a.y - b.y); + }, + + distanceToChebyshev: function (a: Vector2, b: Vector2): number { + var c = Math.abs(a.x - b.x), + d = Math.abs(a.y - b.y); + return c <= d ? d : c; + }, + + distanceToQuadratic: function (a: Vector2, b: Vector2): number { + var c = Math.abs(a.x - b.x), + d = Math.abs(a.y - b.y); + return c * c + c * d + d * d; + }, + + distanceTo: function (a: Vector2, b: Vector2): number { + return a.distanceTo(b); + }, + + distanceToSquared: function (a: Vector2, b: Vector2): number { + return a.distanceToSquared(b); + } +} + + +/** + * Find the Voronoi centroid closest to the current terrain vertex. + * + * This approach is naive, but since the number of cells isn't typically + * very big, it's plenty fast enough. + * + * Alternatives approaches include using Fortune's algorithm or tracking + * cells based on a grid. + */ +function distanceToNearest(coords: Vector2, points: Vector2[], distanceType: string) { + var color = Infinity, + distanceFunc = distanceMetrics['distanceTo' + distanceType]; + for (var k = 0; k < points.length; k++) { + var d = distanceFunc(points[k], coords); + if (d < color) { + color = d; + } + } + return color; +} + +interface WorleyOptions extends Pick { + /** + * The name of a method to use to calculate the + * distance between a point in the heightmap and a Voronoi centroid in + * order to determine the height of that point. Available methods + * include 'Manhattan', 'Chebyshev', 'Quadratic', 'Squared' (squared + * Euclidean), and '' (the empty string, meaning Euclidean, the + * default). + **/ + distanceType?: string; + + /** + * A function that takes the distance + * from a heightmap vertex to a Voronoi centroid and returns a relative + * height for that vertex.Defaults to function(d) { return -d; }. + * Interesting choices of algorithm include + * `0.5 + 1.0 * Math.cos((0.5*d-1) * Math.PI) - d`, which produces + * interesting stepped cones, and`-Math.sqrt(d)`, which produces sharp + * peaks resembling stalagmites. + */ + worleyDistanceTransformation?: (d: number) => number; + + /** + * A function to use to distribute Voronoi + * centroids.Available methods include + * `THREE.Terrain.Worley.randomPoints`(the default ), + * `THREE.Terrain.Worley.PoissonDisks`, and any function that returns + * an array of`THREE.Vector2` instances.You can wrap the PoissonDisks + * function to use custom parameters. + */ + worleyDistribution?: (width: number, height: number, numPoints: number) => Vector2[]; + + /** + * The number of Voronoi cells to use(must be at least + * one).Calculated by default based on the size of the terrain. + */ + worleyPoints?: number; +} + +/** + * Generate random terrain using Worley noise. + * + * Worley noise is also known as Cell or Voronoi noise. It is generated by + * scattering a bunch of points in heightmap-space, then setting the height + * of every point in the heightmap based on how close it is to the closest + * scattered point (or the nth-closest point, but this results in + * heightmaps that don't look much like terrain). + * + * @param {THREE.Vector3[]} g + * The vertex array for plane geometry to modify with heightmap data. + * This method sets the `z` property of each vertex. + * @param {Object} options + * A map of settings that control how the terrain is constructed and + * displayed. Valid values are the same as those for the `options` + * parameter of {@link THREE.Terrain}(), plus three additional available + * properties (see interface type `WorleyOptions`). + */ +export function Worley(g: Vector3[], options: WorleyOptions) { + var points = (options.worleyDistribution || randomPoints)(options.xSegments, options.ySegments, options.worleyPoints!), + transform = options.worleyDistanceTransformation || function (d) { return -d; }, + currentCoords = new Vector2(0, 0); + // The height of each heightmap vertex is the distance to the closest Voronoi centroid + for (var i = 0, xl = options.xSegments! + 1; i < xl; i++) { + for (var j = 0; j < options.ySegments! + 1; j++) { + currentCoords.x = i; + currentCoords.y = j; + g[j * xl + i].z = transform(distanceToNearest(currentCoords, points, options.distanceType || '')); + } + } + // We set the heights to distances so now we need to normalize + Clamp(g, { + maxHeight: options.maxHeight, + minHeight: options.minHeight, + stretch: true, + easing: Linear + }); +}; + +/** + * Randomly distribute points in space. + */ +export function randomPoints(width: number, height: number, numPoints: number) { + numPoints = numPoints || Math.floor(Math.sqrt(width * height * 0.025)) || 1; + var points = new Array(numPoints); + for (var i = 0; i < numPoints; i++) { + points[i] = new Vector2( + Math.random() * width, + Math.random() * height + ); + } + return points; +}; + +/* Utility functions for Poisson Disks. */ + +function removeAndReturnRandomElement(arr: Vector2[]) { + return arr.splice(Math.floor(Math.random() * arr.length), 1)[0]; +} + +function putInGrid(grid: Vector2[][], point: Vector2, cellSize: number): void { + var gx = Math.floor(point.x / cellSize), + gy = Math.floor(point.y / cellSize); + if (!grid[gx]) grid[gx] = []; + grid[gx][gy] = point; +} + +function inRectangle(point: Vector2, width: number, height: number): boolean { + return point.x >= 0 && + point.y >= 0 && + point.x <= width + 1 && + point.y <= height + 1; +} + +function inNeighborhood(grid: Vector2[][], point: Vector2, minDist: number, cellSize: number): boolean { + var gx = Math.floor(point.x / cellSize), + gy = Math.floor(point.y / cellSize); + for (var x = gx - 1; x <= gx + 1; x++) { + for (var y = gy - 1; y <= gy + 1; y++) { + if (x !== gx && y !== gy && + typeof grid[x] !== 'undefined' && typeof grid[x][y] !== 'undefined') { + var cx = x * cellSize, + cy = y * cellSize; + if (Math.sqrt((point.x - cx) * (point.x - cx) + (point.y - cy) * (point.y - cy)) < minDist) { + return true; + } + } + } + } + return false; +} + +function generateRandomPointAround(point: Vector2, minDist: number): Vector2 { + var radius = minDist * (Math.random() + 1), + angle = 2 * Math.PI * Math.random(); + return new Vector2( + point.x + radius * Math.cos(angle), + point.y + radius * Math.sin(angle) + ); +} + +/** + * Generate a set of points using Poisson disk sampling. + * + * Useful for clustering scattered meshes and Voronoi cells for Worley noise. + * + * Ported from pseudocode at http://devmag.org.za/2009/05/03/poisson-disk-sampling/ + * + * @param {Object} options + * A map of settings that control how the resulting noise should be generated + * (with the same parameters as the `options` parameter to the + * `THREE.Terrain` function). + * + * @return {THREE.Vector2[]} + * An array of points. + */ +export function PoissonDisks(width: number, height: number, numPoints: number) { + numPoints = numPoints || Math.floor(Math.sqrt(width * height * 0.2)) || 1; + let minDist = Math.sqrt((width + height) * 2.5); + if (minDist > numPoints * 0.67) minDist = numPoints * 0.67; + var cellSize = minDist / Math.sqrt(2); + if (cellSize < 2) cellSize = 2; + + var grid: Vector2[][] = []; + + var processList: Vector2[] = [], + samplePoints: Vector2[] = []; + + var firstPoint = new Vector2( + Math.random() * width, + Math.random() * height + ); + processList.push(firstPoint); + samplePoints.push(firstPoint); + putInGrid(grid, firstPoint, cellSize); + + var count = 0; + while (processList.length) { + var point = removeAndReturnRandomElement(processList); + for (var i = 0; i < numPoints; i++) { + // optionally, minDist = perlin(point.x / width, point.y / height) + var newPoint = generateRandomPointAround(point, minDist); + if (inRectangle(newPoint, width, height) && !inNeighborhood(grid, newPoint, minDist, cellSize)) { + processList.push(newPoint); + samplePoints.push(newPoint); + putInGrid(grid, newPoint, cellSize); + if (samplePoints.length >= numPoints) break; + } + } + if (samplePoints.length >= numPoints) break; + // Sanity check + if (++count > numPoints * numPoints) { + break; + } + } + return samplePoints; +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fc21a6e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,60 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +}