]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapgen/mapgen_valleys.cpp
Revert "Make Lint Happy"
[dragonfireclient.git] / src / mapgen / mapgen_valleys.cpp
1 /*
2 Minetest
3 Copyright (C) 2016-2019 Duane Robertson <duane@duanerobertson.com>
4 Copyright (C) 2016-2019 paramat
5
6 Based on Valleys Mapgen by Gael de Sailly
7 (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
8 and mapgen_v7, mapgen_flat by kwolekr and paramat.
9
10 Licensing changed by permission of Gael de Sailly.
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published by
14 the Free Software Foundation; either version 2.1 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 GNU Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public License along
23 with this program; if not, write to the Free Software Foundation, Inc.,
24 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
27
28 #include "mapgen.h"
29 #include "voxel.h"
30 #include "noise.h"
31 #include "mapblock.h"
32 #include "mapnode.h"
33 #include "map.h"
34 #include "nodedef.h"
35 #include "voxelalgorithms.h"
36 //#include "profiler.h" // For TimeTaker
37 #include "settings.h" // For g_settings
38 #include "emerge.h"
39 #include "dungeongen.h"
40 #include "mg_biome.h"
41 #include "mg_ore.h"
42 #include "mg_decoration.h"
43 #include "mapgen_valleys.h"
44 #include "cavegen.h"
45 #include <cmath>
46
47
48 FlagDesc flagdesc_mapgen_valleys[] = {
49         {"altitude_chill",   MGVALLEYS_ALT_CHILL},
50         {"humid_rivers",     MGVALLEYS_HUMID_RIVERS},
51         {"vary_river_depth", MGVALLEYS_VARY_RIVER_DEPTH},
52         {"altitude_dry",     MGVALLEYS_ALT_DRY},
53         {NULL,               0}
54 };
55
56
57 MapgenValleys::MapgenValleys(MapgenValleysParams *params, EmergeParams *emerge)
58         : MapgenBasic(MAPGEN_VALLEYS, params, emerge)
59 {
60         // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal
61         m_bgen = (BiomeGenOriginal *)biomegen;
62
63         spflags            = params->spflags;
64         altitude_chill     = params->altitude_chill;
65         river_depth_bed    = params->river_depth + 1.0f;
66         river_size_factor  = params->river_size / 100.0f;
67
68         cave_width         = params->cave_width;
69         large_cave_depth   = params->large_cave_depth;
70         small_cave_num_min = params->small_cave_num_min;
71         small_cave_num_max = params->small_cave_num_max;
72         large_cave_num_min = params->large_cave_num_min;
73         large_cave_num_max = params->large_cave_num_max;
74         large_cave_flooded = params->large_cave_flooded;
75         cavern_limit       = params->cavern_limit;
76         cavern_taper       = params->cavern_taper;
77         cavern_threshold   = params->cavern_threshold;
78         dungeon_ymin       = params->dungeon_ymin;
79         dungeon_ymax       = params->dungeon_ymax;
80
81         //// 2D Terrain noise
82         noise_filler_depth       = new Noise(&params->np_filler_depth,       seed, csize.X, csize.Z);
83         noise_inter_valley_slope = new Noise(&params->np_inter_valley_slope, seed, csize.X, csize.Z);
84         noise_rivers             = new Noise(&params->np_rivers,             seed, csize.X, csize.Z);
85         noise_terrain_height     = new Noise(&params->np_terrain_height,     seed, csize.X, csize.Z);
86         noise_valley_depth       = new Noise(&params->np_valley_depth,       seed, csize.X, csize.Z);
87         noise_valley_profile     = new Noise(&params->np_valley_profile,     seed, csize.X, csize.Z);
88
89         //// 3D Terrain noise
90         // 1-up 1-down overgeneration
91         noise_inter_valley_fill = new Noise(&params->np_inter_valley_fill,
92                 seed, csize.X, csize.Y + 2, csize.Z);
93         // 1-down overgeneraion
94         MapgenBasic::np_cave1    = params->np_cave1;
95         MapgenBasic::np_cave2    = params->np_cave2;
96         MapgenBasic::np_cavern   = params->np_cavern;
97         MapgenBasic::np_dungeons = params->np_dungeons;
98 }
99
100
101 MapgenValleys::~MapgenValleys()
102 {
103         delete noise_filler_depth;
104         delete noise_inter_valley_fill;
105         delete noise_inter_valley_slope;
106         delete noise_rivers;
107         delete noise_terrain_height;
108         delete noise_valley_depth;
109         delete noise_valley_profile;
110 }
111
112
113 MapgenValleysParams::MapgenValleysParams():
114         np_filler_depth       (0.0,   1.2,  v3f(256,  256,  256),  1605,  3, 0.5,  2.0),
115         np_inter_valley_fill  (0.0,   1.0,  v3f(256,  512,  256),  1993,  6, 0.8,  2.0),
116         np_inter_valley_slope (0.5,   0.5,  v3f(128,  128,  128),  746,   1, 1.0,  2.0),
117         np_rivers             (0.0,   1.0,  v3f(256,  256,  256),  -6050, 5, 0.6,  2.0),
118         np_terrain_height     (-10.0, 50.0, v3f(1024, 1024, 1024), 5202,  6, 0.4,  2.0),
119         np_valley_depth       (5.0,   4.0,  v3f(512,  512,  512),  -1914, 1, 1.0,  2.0),
120         np_valley_profile     (0.6,   0.50, v3f(512,  512,  512),  777,   1, 1.0,  2.0),
121         np_cave1              (0.0,   12.0, v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
122         np_cave2              (0.0,   12.0, v3f(67,   67,   67),   10325, 3, 0.5,  2.0),
123         np_cavern             (0.0,   1.0,  v3f(768,  256,  768),  59033, 6, 0.63, 2.0),
124         np_dungeons           (0.9,   0.5,  v3f(500,  500,  500),  0,     2, 0.8,  2.0)
125 {
126 }
127
128
129 void MapgenValleysParams::readParams(const Settings *settings)
130 {
131         settings->getFlagStrNoEx("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
132         settings->getU16NoEx("mgvalleys_altitude_chill",       altitude_chill);
133         settings->getS16NoEx("mgvalleys_large_cave_depth",     large_cave_depth);
134         settings->getU16NoEx("mgvalleys_small_cave_num_min",   small_cave_num_min);
135         settings->getU16NoEx("mgvalleys_small_cave_num_max",   small_cave_num_max);
136         settings->getU16NoEx("mgvalleys_large_cave_num_min",   large_cave_num_min);
137         settings->getU16NoEx("mgvalleys_large_cave_num_max",   large_cave_num_max);
138         settings->getFloatNoEx("mgvalleys_large_cave_flooded", large_cave_flooded);
139         settings->getU16NoEx("mgvalleys_river_depth",          river_depth);
140         settings->getU16NoEx("mgvalleys_river_size",           river_size);
141         settings->getFloatNoEx("mgvalleys_cave_width",         cave_width);
142         settings->getS16NoEx("mgvalleys_cavern_limit",         cavern_limit);
143         settings->getS16NoEx("mgvalleys_cavern_taper",         cavern_taper);
144         settings->getFloatNoEx("mgvalleys_cavern_threshold",   cavern_threshold);
145         settings->getS16NoEx("mgvalleys_dungeon_ymin",         dungeon_ymin);
146         settings->getS16NoEx("mgvalleys_dungeon_ymax",         dungeon_ymax);
147
148         settings->getNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
149         settings->getNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
150         settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
151         settings->getNoiseParams("mgvalleys_np_rivers",             np_rivers);
152         settings->getNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
153         settings->getNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
154         settings->getNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
155
156         settings->getNoiseParams("mgvalleys_np_cave1",              np_cave1);
157         settings->getNoiseParams("mgvalleys_np_cave2",              np_cave2);
158         settings->getNoiseParams("mgvalleys_np_cavern",             np_cavern);
159         settings->getNoiseParams("mgvalleys_np_dungeons",           np_dungeons);
160 }
161
162
163 void MapgenValleysParams::writeParams(Settings *settings) const
164 {
165         settings->setFlagStr("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
166         settings->setU16("mgvalleys_altitude_chill",       altitude_chill);
167         settings->setS16("mgvalleys_large_cave_depth",     large_cave_depth);
168         settings->setU16("mgvalleys_small_cave_num_min",   small_cave_num_min);
169         settings->setU16("mgvalleys_small_cave_num_max",   small_cave_num_max);
170         settings->setU16("mgvalleys_large_cave_num_min",   large_cave_num_min);
171         settings->setU16("mgvalleys_large_cave_num_max",   large_cave_num_max);
172         settings->setFloat("mgvalleys_large_cave_flooded", large_cave_flooded);
173         settings->setU16("mgvalleys_river_depth",          river_depth);
174         settings->setU16("mgvalleys_river_size",           river_size);
175         settings->setFloat("mgvalleys_cave_width",         cave_width);
176         settings->setS16("mgvalleys_cavern_limit",         cavern_limit);
177         settings->setS16("mgvalleys_cavern_taper",         cavern_taper);
178         settings->setFloat("mgvalleys_cavern_threshold",   cavern_threshold);
179         settings->setS16("mgvalleys_dungeon_ymin",         dungeon_ymin);
180         settings->setS16("mgvalleys_dungeon_ymax",         dungeon_ymax);
181
182         settings->setNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
183         settings->setNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
184         settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
185         settings->setNoiseParams("mgvalleys_np_rivers",             np_rivers);
186         settings->setNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
187         settings->setNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
188         settings->setNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
189
190         settings->setNoiseParams("mgvalleys_np_cave1",              np_cave1);
191         settings->setNoiseParams("mgvalleys_np_cave2",              np_cave2);
192         settings->setNoiseParams("mgvalleys_np_cavern",             np_cavern);
193         settings->setNoiseParams("mgvalleys_np_dungeons",           np_dungeons);
194 }
195
196
197 void MapgenValleysParams::setDefaultSettings(Settings *settings)
198 {
199         settings->setDefault("mgvalleys_spflags", flagdesc_mapgen_valleys,
200                 MGVALLEYS_ALT_CHILL | MGVALLEYS_HUMID_RIVERS |
201                 MGVALLEYS_VARY_RIVER_DEPTH | MGVALLEYS_ALT_DRY);
202 }
203
204
205 /////////////////////////////////////////////////////////////////
206
207
208 void MapgenValleys::makeChunk(BlockMakeData *data)
209 {
210         // Pre-conditions
211         assert(data->vmanip);
212         assert(data->nodedef);
213         assert(data->blockpos_requested.X >= data->blockpos_min.X &&
214                 data->blockpos_requested.Y >= data->blockpos_min.Y &&
215                 data->blockpos_requested.Z >= data->blockpos_min.Z);
216         assert(data->blockpos_requested.X <= data->blockpos_max.X &&
217                 data->blockpos_requested.Y <= data->blockpos_max.Y &&
218                 data->blockpos_requested.Z <= data->blockpos_max.Z);
219
220         //TimeTaker t("makeChunk");
221
222         this->generating = true;
223         this->vm = data->vmanip;
224         this->ndef = data->nodedef;
225
226         v3s16 blockpos_min = data->blockpos_min;
227         v3s16 blockpos_max = data->blockpos_max;
228         node_min = blockpos_min * MAP_BLOCKSIZE;
229         node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
230         full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
231         full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
232
233         blockseed = getBlockSeed2(full_node_min, seed);
234
235         // Generate biome noises. Note this must be executed strictly before
236         // generateTerrain, because generateTerrain depends on intermediate
237         // biome-related noises.
238         m_bgen->calcBiomeNoise(node_min);
239
240         // Generate terrain
241         s16 stone_surface_max_y = generateTerrain();
242
243         // Create heightmap
244         updateHeightmap(node_min, node_max);
245
246         // Place biome-specific nodes and build biomemap
247         if (flags & MG_BIOMES) {
248                 generateBiomes();
249         }
250
251         // Generate tunnels, caverns and large randomwalk caves
252         if (flags & MG_CAVES) {
253                 // Generate tunnels first as caverns confuse them
254                 generateCavesNoiseIntersection(stone_surface_max_y);
255
256                 // Generate caverns
257                 bool near_cavern = generateCavernsNoise(stone_surface_max_y);
258
259                 // Generate large randomwalk caves
260                 if (near_cavern)
261                         // Disable large randomwalk caves in this mapchunk by setting
262                         // 'large cave depth' to world base. Avoids excessive liquid in
263                         // large caverns and floating blobs of overgenerated liquid.
264                         generateCavesRandomWalk(stone_surface_max_y,
265                                 -MAX_MAP_GENERATION_LIMIT);
266                 else
267                         generateCavesRandomWalk(stone_surface_max_y, large_cave_depth);
268         }
269
270         // Generate the registered ores
271         m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
272
273         // Dungeon creation
274         if (flags & MG_DUNGEONS)
275                 generateDungeons(stone_surface_max_y);
276
277         // Generate the registered decorations
278         if (flags & MG_DECORATIONS)
279                 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
280
281         // Sprinkle some dust on top after everything else was generated
282         if (flags & MG_BIOMES)
283                 dustTopNodes();
284
285         updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
286
287         if (flags & MG_LIGHT)
288                 calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
289                         full_node_min, full_node_max);
290
291         this->generating = false;
292
293         //printf("makeChunk: %lums\n", t.stop());
294 }
295
296
297 int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
298 {
299         // Check if in a river channel
300         float n_rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
301         if (std::fabs(n_rivers) <= river_size_factor)
302                 // Unsuitable spawn point
303                 return MAX_MAP_GENERATION_LIMIT;
304
305         float n_slope          = NoisePerlin2D(&noise_inter_valley_slope->np, p.X, p.Y, seed);
306         float n_terrain_height = NoisePerlin2D(&noise_terrain_height->np, p.X, p.Y, seed);
307         float n_valley         = NoisePerlin2D(&noise_valley_depth->np, p.X, p.Y, seed);
308         float n_valley_profile = NoisePerlin2D(&noise_valley_profile->np, p.X, p.Y, seed);
309
310         float valley_d = n_valley * n_valley;
311         float base = n_terrain_height + valley_d;
312         float river = std::fabs(n_rivers) - river_size_factor;
313         float tv = std::fmax(river / n_valley_profile, 0.0f);
314         float valley_h = valley_d * (1.0f - std::exp(-tv * tv));
315         float surface_y = base + valley_h;
316         float slope = n_slope * valley_h;
317         float river_y = base - 1.0f;
318
319         // Raising the maximum spawn level above 'water_level + 16' is necessary for custom
320         // parameters that set average terrain level much higher than water_level.
321         s16 max_spawn_y = std::fmax(
322                 noise_terrain_height->np.offset +
323                 noise_valley_depth->np.offset * noise_valley_depth->np.offset,
324                 water_level + 16);
325
326         // Starting spawn search at max_spawn_y + 128 ensures 128 nodes of open
327         // space above spawn position. Avoids spawning in possibly sealed voids.
328         for (s16 y = max_spawn_y + 128; y >= water_level; y--) {
329                 float n_fill = NoisePerlin3D(&noise_inter_valley_fill->np, p.X, y, p.Y, seed);
330                 float surface_delta = (float)y - surface_y;
331                 float density = slope * n_fill - surface_delta;
332
333                 if (density > 0.0f) {  // If solid
334                         // Sometimes surface level is below river water level in places that are not
335                         // river channels.
336                         if (y < water_level || y > max_spawn_y || y < (s16)river_y)
337                                 // Unsuitable spawn point
338                                 return MAX_MAP_GENERATION_LIMIT;
339
340                         // y + 2 because y is surface and due to biome 'dust' nodes.
341                         return y + 2;
342                 }
343         }
344         // Unsuitable spawn position, no ground found
345         return MAX_MAP_GENERATION_LIMIT;
346 }
347
348
349 int MapgenValleys::generateTerrain()
350 {
351         MapNode n_air(CONTENT_AIR);
352         MapNode n_river_water(c_river_water_source);
353         MapNode n_stone(c_stone);
354         MapNode n_water(c_water_source);
355
356         noise_inter_valley_slope->perlinMap2D(node_min.X, node_min.Z);
357         noise_rivers->perlinMap2D(node_min.X, node_min.Z);
358         noise_terrain_height->perlinMap2D(node_min.X, node_min.Z);
359         noise_valley_depth->perlinMap2D(node_min.X, node_min.Z);
360         noise_valley_profile->perlinMap2D(node_min.X, node_min.Z);
361
362         noise_inter_valley_fill->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
363
364         const v3s16 &em = vm->m_area.getExtent();
365         s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
366         u32 index_2d = 0;
367
368         for (s16 z = node_min.Z; z <= node_max.Z; z++)
369         for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
370                 float n_slope          = noise_inter_valley_slope->result[index_2d];
371                 float n_rivers         = noise_rivers->result[index_2d];
372                 float n_terrain_height = noise_terrain_height->result[index_2d];
373                 float n_valley         = noise_valley_depth->result[index_2d];
374                 float n_valley_profile = noise_valley_profile->result[index_2d];
375
376                 float valley_d = n_valley * n_valley;
377                 // 'base' represents the level of the river banks
378                 float base = n_terrain_height + valley_d;
379                 // 'river' represents the distance from the river edge
380                 float river = std::fabs(n_rivers) - river_size_factor;
381                 // Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
382                 // 'valley_h' represents the height of the terrain, from the rivers.
383                 float tv = std::fmax(river / n_valley_profile, 0.0f);
384                 float valley_h = valley_d * (1.0f - std::exp(-tv * tv));
385                 // Approximate height of the terrain
386                 float surface_y = base + valley_h;
387                 float slope = n_slope * valley_h;
388                 // River water surface is 1 node below river banks
389                 float river_y = base - 1.0f;
390
391                 // Rivers are placed where 'river' is negative
392                 if (river < 0.0f) {
393                         // Use the function -sqrt(1-x^2) which models a circle
394                         float tr = river / river_size_factor + 1.0f;
395                         float depth = (river_depth_bed *
396                                 std::sqrt(std::fmax(0.0f, 1.0f - tr * tr)));
397                         // There is no logical equivalent to this using rangelim
398                         surface_y = std::fmin(
399                                 std::fmax(base - depth, (float)(water_level - 3)),
400                                 surface_y);
401                         slope = 0.0f;
402                 }
403
404                 // Optionally vary river depth according to heat and humidity
405                 if (spflags & MGVALLEYS_VARY_RIVER_DEPTH) {
406                         float t_heat = m_bgen->heatmap[index_2d];
407                         float heat = (spflags & MGVALLEYS_ALT_CHILL) ?
408                                 // Match heat value calculated below in
409                                 // 'Optionally decrease heat with altitude'.
410                                 // In rivers, 'ground height ignoring riverbeds' is 'base'.
411                                 // As this only affects river water we can assume y > water_level.
412                                 t_heat + 5.0f - (base - water_level) * 20.0f / altitude_chill :
413                                 t_heat;
414                         float delta = m_bgen->humidmap[index_2d] - 50.0f;
415                         if (delta < 0.0f) {
416                                 float t_evap = (heat - 32.0f) / 300.0f;
417                                 river_y += delta * std::fmax(t_evap, 0.08f);
418                         }
419                 }
420
421                 // Highest solid node in column
422                 s16 column_max_y = surface_y;
423                 u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
424                 u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
425
426                 for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
427                         if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
428                                 float n_fill = noise_inter_valley_fill->result[index_3d];
429                                 float surface_delta = (float)y - surface_y;
430                                 // Density = density noise + density gradient
431                                 float density = slope * n_fill - surface_delta;
432
433                                 if (density > 0.0f) {
434                                         vm->m_data[index_data] = n_stone; // Stone
435                                         if (y > surface_max_y)
436                                                 surface_max_y = y;
437                                         if (y > column_max_y)
438                                                 column_max_y = y;
439                                 } else if (y <= water_level) {
440                                         vm->m_data[index_data] = n_water; // Water
441                                 } else if (y <= (s16)river_y) {
442                                         vm->m_data[index_data] = n_river_water; // River water
443                                 } else {
444                                         vm->m_data[index_data] = n_air; // Air
445                                 }
446                         }
447
448                         VoxelArea::add_y(em, index_data, 1);
449                         index_3d += ystride;
450                 }
451
452                 // Optionally increase humidity around rivers
453                 if (spflags & MGVALLEYS_HUMID_RIVERS) {
454                         // Compensate to avoid increasing average humidity
455                         m_bgen->humidmap[index_2d] *= 0.8f;
456                         // Ground height ignoring riverbeds
457                         float t_alt = std::fmax(base, (float)column_max_y);
458                         float water_depth = (t_alt - base) / 4.0f;
459                         m_bgen->humidmap[index_2d] *=
460                                 1.0f + std::pow(0.5f, std::fmax(water_depth, 1.0f));
461                 }
462
463                 // Optionally decrease humidity with altitude
464                 if (spflags & MGVALLEYS_ALT_DRY) {
465                         // Ground height ignoring riverbeds
466                         float t_alt = std::fmax(base, (float)column_max_y);
467                         // Only decrease above water_level
468                         if (t_alt > water_level)
469                                 m_bgen->humidmap[index_2d] -=
470                                         (t_alt - water_level) * 10.0f / altitude_chill;
471                 }
472
473                 // Optionally decrease heat with altitude
474                 if (spflags & MGVALLEYS_ALT_CHILL) {
475                         // Compensate to avoid reducing the average heat
476                         m_bgen->heatmap[index_2d] += 5.0f;
477                         // Ground height ignoring riverbeds
478                         float t_alt = std::fmax(base, (float)column_max_y);
479                         // Only decrease above water_level
480                         if (t_alt > water_level)
481                                 m_bgen->heatmap[index_2d] -=
482                                         (t_alt - water_level) * 20.0f / altitude_chill;
483                 }
484         }
485
486         return surface_max_y;
487 }