| 1 | /**
|
|---|
| 2 | * Copies the given heightmap to the given area.
|
|---|
| 3 | * Scales the horizontal plane proportionally and applies bicubic interpolation.
|
|---|
| 4 | * The heightrange is either scaled proportionally or mapped to the given heightrange.
|
|---|
| 5 | *
|
|---|
| 6 | * @param {Uint16Array} heightmap - One dimensional array of vertex heights.
|
|---|
| 7 | * @param {number} [normalMinHeight] - The minimum height the elevation grid of 320 tiles would have.
|
|---|
| 8 | * @param {number} [normalMaxHeight] - The maximum height the elevation grid of 320 tiles would have.
|
|---|
| 9 | */
|
|---|
| 10 | function HeightmapPainter(heightmap, normalMinHeight = undefined, normalMaxHeight = undefined)
|
|---|
| 11 | {
|
|---|
| 12 | this.heightmap = heightmap;
|
|---|
| 13 | this.bicubicInterpolation = bicubicInterpolation;
|
|---|
| 14 | this.verticesPerSide = heightmap.length;
|
|---|
| 15 | this.normalMinHeight = normalMinHeight;
|
|---|
| 16 | this.normalMaxHeight = normalMaxHeight;
|
|---|
| 17 | }
|
|---|
| 18 |
|
|---|
| 19 | HeightmapPainter.prototype.getScale = function()
|
|---|
| 20 | {
|
|---|
| 21 | return this.verticesPerSide / (g_Map.getSize() + 1);
|
|---|
| 22 | };
|
|---|
| 23 |
|
|---|
| 24 | HeightmapPainter.prototype.scaleHeight = function(height)
|
|---|
| 25 | {
|
|---|
| 26 | if (this.normalMinHeight === undefined || this.normalMaxHeight === undefined)
|
|---|
| 27 | return height / this.getScale() / HEIGHT_UNITS_PER_METRE;
|
|---|
| 28 |
|
|---|
| 29 | const minHeight = this.normalMinHeight * (g_Map.getSize() + 1) / 321;
|
|---|
| 30 | const maxHeight = this.normalMaxHeight * (g_Map.getSize() + 1) / 321;
|
|---|
| 31 |
|
|---|
| 32 | return minHeight + (maxHeight - minHeight) * height / 0xFFFF;
|
|---|
| 33 | };
|
|---|
| 34 |
|
|---|
| 35 | HeightmapPainter.prototype.paint = function(area)
|
|---|
| 36 | {
|
|---|
| 37 | const scale = this.getScale();
|
|---|
| 38 | const leftBottom = new Vector2D(0, 0);
|
|---|
| 39 | const rightTop = new Vector2D(this.verticesPerSide, this.verticesPerSide);
|
|---|
| 40 | const brushSize = new Vector2D(3, 3);
|
|---|
| 41 | const brushCenter = new Vector2D(1, 1);
|
|---|
| 42 |
|
|---|
| 43 | // Additional complexity to process all 4 vertices of each tile, i.e the last row too
|
|---|
| 44 | const seen = new Array(g_Map.height.length).fill(0).map(zero => new Uint8Array(g_Map.height.length).fill(0));
|
|---|
| 45 |
|
|---|
| 46 | for (const point of area.getPoints())
|
|---|
| 47 | for (const vertex of g_TileVertices)
|
|---|
| 48 | {
|
|---|
| 49 | const vertexPos = Vector2D.add(point, vertex);
|
|---|
| 50 |
|
|---|
| 51 | if (!g_Map.validHeight(vertexPos) || seen[vertexPos.x][vertexPos.y])
|
|---|
| 52 | continue;
|
|---|
| 53 |
|
|---|
| 54 | seen[vertexPos.x][vertexPos.y] = 1;
|
|---|
| 55 |
|
|---|
| 56 | const sourcePos = Vector2D.mult(vertexPos, scale);
|
|---|
| 57 | const sourceTilePos = sourcePos.clone().floor();
|
|---|
| 58 |
|
|---|
| 59 | const brushPosition = Vector2D.max(
|
|---|
| 60 | leftBottom,
|
|---|
| 61 | Vector2D.min(
|
|---|
| 62 | Vector2D.sub(sourceTilePos, brushCenter),
|
|---|
| 63 | Vector2D.sub(rightTop, brushSize).sub(brushCenter)));
|
|---|
| 64 |
|
|---|
| 65 | g_Map.setHeight(vertexPos, bicubicInterpolation(
|
|---|
| 66 | Vector2D.sub(sourcePos, brushPosition).sub(brushCenter),
|
|---|
| 67 | ...getPointsInBoundingBox(getBoundingBox([brushPosition, Vector2D.add(brushPosition, brushSize)])).map(pos =>
|
|---|
| 68 | this.scaleHeight(this.heightmap[pos.x][pos.y]))));
|
|---|
| 69 | }
|
|---|
| 70 | };
|
|---|