Index: binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js @@ -0,0 +1,71 @@ +/** + * @param {Boolean} type - ELEVATION_MODIFY or ELEVATION_SET. + * @param {Number} scale - Global scale of dune size. > 1 bigger, < 1 smaller. + * @param {Number} scaleVertical - Vertical scale of dune size. > 1 taller, < 1 smaller. + * @param {Number} baseHeight - Dunes base altitude if type == ELEVATION_SET. + */ +class DunePainter { + constructor(type = ELEVATION_MODIFY, scale = 1, scaleVertical = 1, baseHeight = 0) { + this.type = type; + this.scale = scale; + this.scaleVertical = scaleVertical; + this.baseHeight = baseHeight; + } + + /** + * Dune profile shape primitive. + * @param {Number} x - [0,1] value. + * @param {Number} xCrest - (0,1) position of the dune crest relative to the dune width. + * @param {Boolean} dune - True is dune, false is dome. + * @returns {Number} [0,1] - Height of the dune at point x + */ + duneProfile(x, xCrest = 0.75, dune = true) { + return x < xCrest ? (1 - Math.cos(Math.PI * x / xCrest)) * 0.5 : + dune ? (1 - Math.cos(Math.PI * (x - 1) / (xCrest - 1))) * 0.5 : + 1 - Math.cos(Math.PI * (x - 1) / (xCrest - 1) * 0.5); + } + + paint(area) { + + // Dunes width. + let width = 20.0 * this.scale; + let mod = (val, period) => val % period < 0 ? period + val % period : val % period; + + for (let p of area.getPoints()) { + + let x = p.x; + let y = p.y; + + // Main dunes creation height. + let height1; + { + let xProfile = x + + this.scale * 20 * valueNoise2d(x / (3 * width), y / (3 * width)) + + this.scale * 5 * valueNoise2d(x / width, y / width); + xProfile = mod(xProfile / width, 1); + let duneLongintudinalScale = valueNoise2d(x / (3 * width), y / (3 * width)) * 0.5 + 0.5; + height1 = this.duneProfile(xProfile, 0.70, false) * duneLongintudinalScale; + } + + // Secondary dunes creation height. + let height2; + { + // Rotate slightly + let rx = Math.cos(3.14 / 20); + let ry = Math.sin(3.14 / 20); + [x, y] = [x * rx - y * ry, x * ry + y * rx]; + + let xProfile = x + + this.scale * 20 * valueNoise2d(x / (3 * width), y / (3 * width)) + + this.scale * 5 * valueNoise2d(x / width, y / width); + xProfile = mod(xProfile / width, 1); + height2 = this.duneProfile(xProfile, 0.60, false); + } + + let height = (height1 * 0.8 + height2 * 0.2) * 12 * this.scale * this.scaleVertical; + // Add or set height. + let base = this.type == ELEVATION_MODIFY ? g_Map.getHeight(p) : baseHeight; + g_Map.setHeight(p, base + height); + } + } +}