aboutsummaryrefslogtreecommitdiff
path: root/client
Commit message (Collapse)AuthorAge
* Distribute shadow map update over multiple frames to reduce stutter (#11422)x20482021-07-25
| | | | | | | | | | Reduces stutter and freezes when playing. * Maintains double SM and SM Color textures * Light frustum update triggers incremental generation of shadow map into secondary 'future' textures. * Every incremental update renders a portion of the shadow draw list (split equally). * After defined number of frames (currently, 4), 'future' and 'current' textures are swapped, and DirectionalLight 'commits' the new frustum to use when rendering shadows on screen. Co-authored-by: sfan5 <sfan5@live.de>
* Add smooth light-shadow transition at noon (#11430)x20482021-07-25
| | | | Node faces with normals pointing East/West (+X/-X) will transition between light and shadow at noon. This code makes the transition smooth.
* Improve shadow rendering with non-default camera FOV (#11385)x20482021-07-11
| | | | | | | | | | | * Adjust minimum filter radius for perspective * Expand shadow frustum when camera FOV changes, reuse FOV distance adjustment from numeric.cpp * Read shadow_soft_radius setting as float * Use adaptive filter radius to accomodate for PSM distortion * Adjust filter radius for texture resolution
* Shadow mapping render pass (#11244)Liso2021-06-06
| | | Co-authored-by: x2048 <codeforsmile@gmail.com>
* Fix swapped vertex colors on GLES2sfan52021-05-11
|
* Use vec4 for varTexCoord in interlaced shader (#11004)Muhammad Rifqi Priyo Susanto2021-03-01
| | | Somewhen in the past, inTexCoord0 was a vec2. Now, it is a vec4.
* Fix GLES shader support after #9247 (#10727)Vitaliy2020-12-22
|
* Cleanup shader generation code (#10663)Vitaliy2020-12-19
| | | Shader generation is a mess. This commit cleans some parts up, including dropping remains of HLSL support which was never actually implemented.
* Fix MSAA stripes (#9247)HybridDog2020-12-04
| | | | | This only works when shaders are enabled. The centroid varying avoids that the textures (which repeat themselves out of bounds) are sampled out of bounds in MSAA. If MSAA (called FSAA in minetest) is disabled, the centroid keyword does nothing.
* Sky: support GLES2numzero2020-11-26
| | | | IrrLicht built-in shader is broken, have to write my own
* Shaders for Android (GLES 2) (#10506)Vitaliy2020-10-25
| | | | | Shader support for OpenGL ES 2 devices (Android) Co-authored-by: sfan5 <sfan5@live.de>
* Remove all bump mapping and parallax occlusion related code.Lars2020-10-17
|
* Remove "generate normal maps" feature (#10313)hecks2020-09-14
| | | | | Erase all traces of normal "generation" from fragment shaders Remove the "feature" from the engine and default config Remove any leftover documentation of it
* shaders: Fix transparency on GC7000L (#10036)mntmn2020-08-25
| | | Workaround for the missing GL_ALPHA_TEST implementation in Mesa (etnaviv driver).
* Make shading of CAOs optional (#10033)Danila Shutov2020-06-16
|
* Fix broken coloring of wielditems (#9969)Danila Shutov2020-06-09
| | | Fixes a regression that appeared in 5.3.0-dev.
* Reuse object_shader for "wielditem" and "item" entity drawtypes (#9537)Danila Shutov2020-04-19
|
* Shaders: Complete 478e753. OpenGL 4.3 compatiblitySmallJoker2020-04-18
|
* Add tone mapping for entities (#9521)Danila Shutov2020-04-06
| | | fixes #9301
* Transform texture UVs with provided tex. matrix (#9515)Danila Shutov2020-03-16
| | | fixes #9481
* Basic model shading (#9374)Danila Shutov2020-02-16
|
* Shaders: Fix OpenGL < 4.3 compatibilitySmallJoker2020-02-16
|
* Waves generated with Perlin-type noise #8994Lars Hofhansl2019-11-19
|
* Simple shader fixes. (#8991)lhofhansl2019-09-26
| | | | | 1. Pass current camera offset to shader, so shader have access to the global coordinates 2. Pass animation timer to fragment shader. C++ code is already there, just wasn't declared in the shader 3. Delay animation timer wrap-around (from 100s to about 16 minutes)
* Require 'waving = 3' in a nodedef to apply the liquid waving shader (#8418)Paramat2019-03-27
| | | | | | | | Makes the liquid waving shader per-nodedef like waving leaves/plants, instead of being applied to all liquids. Like the waving leaves/plants shaders, the liquid waving shader can also be applied to meshes and nodeboxes. Derived from a PR by t0ny2.
* Shaders: Fix comment line (#7668)xzcx2018-08-30
| | | Fixed comment as finalColorBlend() does not exist in the code base.
* Rewrite rendering engine (#6253)Vitaliy2017-10-31
| | | | | | | | | | | | * Clean draw_*() arguments * Split rendering core * Add anaglyph 3D * Interlaced 3D * Drop obsolete methods
* Shaders: Remove unused water surface shaderparamat2017-05-08
| | | | | | | | | | Also remove hardcoded MTGame node. The 'water surface shader' was duplicated shader code in preparation for intended new water surface shaders. For development purposes the MTGame node 'default:water_source' had it's top tile assigned to 'water surface shader'. Due to shader duplication this commit does not cause any change to shader behaviour.
* Fix fog weirdness (#5146)numberZero2017-01-31
|
* Add hardware node coloring. Includes:Dániel Juhász2017-01-23
| | | | | | - Increase ContentFeatures serialization version - Color property and palettes for nodes - paramtype2 = "color", "colored facedir" or "colored wallmounted"
* Shaders: Remove unnecessary 'if' statementsLars Hofhansl2016-12-24
| | | | | | Pull if GENERATE_NORMALMAPS == 1 into the template to avoid evaluating it for each fragment. Remove if (fogDistance != 0.0).
* Fog: Make fraction of visible distance at which fog starts configurableLars Hofhansl2016-12-07
| | | | | | Optimise the fetching of global settings 'camera_smoothing', 'cinematic' and 'cinematic_camera_smoothing'. Cache 'cam_smoothing'.
* Fix unexplained shader issue (glsl compiler bug??) (#4757)Rogier-52016-11-17
|
* Remove unused shader matrices. (#4723)lhofhansl2016-11-04
|
* Shaders: Remove special handling for liquids. (#4670)lhofhansl2016-10-26
|
* Shaders: Apply tone mapping before fog calculation.Lars Hofhansl2016-10-25
|
* Shaders: Harmonize Irrlicht and shader fog calculationsLars Hofhansl2016-10-24
|
* Use range-based fog instead of z-plane based.Lars Hofhansl2016-10-13
|
* Nodes shader: Decrease amplitude of waving leaves and plantsparamat2016-03-30
| | | | | | Fix initialisation of variable 'disp' Fix a few minor code style issues Add independent X motion combining 2 prime frequencies
* Replace CRLF with LF in shader filesest312016-03-25
|
* Shaders: fix fog not affecting opaque liquidsRealBadAngel2016-02-23
|
* Filmic HDR tone mappingRealBadAngel2016-02-09
|
* Cleanup selection mesh code, add shaders for halo and selection boxesRealBadAngel2016-02-08
|
* Speed up and make more accurate relief mappingRealBadAngel2015-12-10
| | | | using linear + binary search.
* Shaders: use triple-frequency waving for leaves and plantsparamat2015-09-07
|
* Remove use of engine sent texture tiling flags - theyre no longer neededRealBadAngel2015-08-20
|
* Add wielded (and CAOs) shaderRealBadAngel2015-07-21
|
* Fix relief mapping issuesRealBadAngel2015-07-16
|
* Shaders fixes and cleanup relief mapping code.RealBadAngel2015-07-02
|
* Bugfix: variable type mismatchRealBadAngel2015-06-28
|
lc">// re-carving the solid overtop placed for blocking sunlight noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); } CavesNoiseIntersection::~CavesNoiseIntersection() { delete noise_cave1; delete noise_cave2; } void CavesNoiseIntersection::generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap) { assert(vm); assert(biomemap); noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); v3s16 em = vm->m_area.getExtent(); u32 index2d = 0; for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { bool column_is_open = false; // Is column open to overground bool is_under_river = false; // Is column under river water bool is_tunnel = false; // Is tunnel or tunnel floor u32 vi = vm->m_area.index(x, nmax.Y, z); u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + (x - nmin.X); // Biome of column Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]); // Don't excavate the overgenerated stone at nmax.Y + 1, // this creates a 'roof' over the tunnel, preventing light in // tunnels at mapchunk borders when generating mapchunks upwards. // This 'roof' is removed when the mapchunk above is generated. for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, index3d -= m_ystride, vm->m_area.add_y(em, vi, -1)) { content_t c = vm->m_data[vi].getContent(); if (c == CONTENT_AIR || c == biome->c_water_top || c == biome->c_water) { column_is_open = true; continue; } else if (c == biome->c_river_water) { column_is_open = true; is_under_river = true; continue; } // Ground float d1 = contour(noise_cave1->result[index3d]); float d2 = contour(noise_cave2->result[index3d]); if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) { // In tunnel and ground content, excavate vm->m_data[vi] = MapNode(CONTENT_AIR); is_tunnel = true; } else { // Not in tunnel or not ground content if (is_tunnel && column_is_open && (c == biome->c_filler || c == biome->c_stone)) { // Tunnel entrance floor if (is_under_river) vm->m_data[vi] = MapNode(biome->c_riverbed); else vm->m_data[vi] = MapNode(biome->c_top); } column_is_open = false; is_tunnel = false; } } } } //// //// CavesRandomWalk //// CavesRandomWalk::CavesRandomWalk( INodeDefManager *ndef, GenerateNotifier *gennotify, s32 seed, int water_level, content_t water_source, content_t lava_source) { assert(ndef); this->ndef = ndef; this->gennotify = gennotify; this->seed = seed; this->water_level = water_level; this->np_caveliquids = &nparams_caveliquids; this->lava_depth = DEFAULT_LAVA_DEPTH; c_water_source = water_source; if (c_water_source == CONTENT_IGNORE) c_water_source = ndef->getId("mapgen_water_source"); if (c_water_source == CONTENT_IGNORE) c_water_source = CONTENT_AIR; c_lava_source = lava_source; if (c_lava_source == CONTENT_IGNORE) c_lava_source = ndef->getId("mapgen_lava_source"); if (c_lava_source == CONTENT_IGNORE) c_lava_source = CONTENT_AIR; } void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap) { assert(vm); assert(ps); this->vm = vm; this->ps = ps; this->node_min = nmin; this->node_max = nmax; this->heightmap = heightmap; this->large_cave = is_large_cave; this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness int dswitchint = ps->range(1, 14); flooded = ps->range(1, 2) == 2; if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); min_tunnel_diameter = 5; max_tunnel_diameter = ps->range(7, ps->range(8, 24)); } else { part_max_length_rs = ps->range(2, 9); tunnel_routepoints = ps->range(10, ps->range(15, 30)); min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); } large_cave_is_flat = (ps->range(0, 1) == 0); main_direction = v3f(0, 0, 0); // Allowed route area size in nodes ar = node_max - node_min + v3s16(1, 1, 1); // Area starting point in nodes of = node_min; // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 insure = 10; s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); if (large_cave) { s16 minpos = 0; if (node_min.Y < water_level && node_max.Y > water_level) { minpos = water_level - max_tunnel_diameter / 3 - of.Y; route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; } route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position orp.Z = (float)(ps->next() % ar.Z) + 0.5f; orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; gennotify->addEvent(notifytype, abs_pos); } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); } } void CavesRandomWalk::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; main_direction *= (float)ps->range(0, 10) / 10; } // Randomize size s16 min_d = min_tunnel_diameter; s16 max_d = max_tunnel_diameter; rs = ps->range(min_d, max_d); s16 rs_part_max_length_rs = rs * part_max_length_rs; v3s16 maxlen; if (large_cave) { maxlen = v3s16( rs_part_max_length_rs, rs_part_max_length_rs / 2, rs_part_max_length_rs ); } else { maxlen = v3s16( rs_part_max_length_rs, ps->range(1, rs_part_max_length_rs), rs_part_max_length_rs ); } v3f vec; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } else { vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2; vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } // Do not make caves that are above ground. // It is only necessary to check the startpoint and endpoint. v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; if (isPosAboveSurface(p1) || isPosAboveSurface(p2)) return; vec += main_direction; v3f rp = orp + vec; if (rp.X < 0) rp.X = 0; else if (rp.X >= ar.X) rp.X = ar.X - 1; if (rp.Y < route_y_min) rp.Y = route_y_min; else if (rp.Y >= route_y_max) rp.Y = route_y_max - 1; if (rp.Z < 0) rp.Z = 0; else if (rp.Z >= ar.Z) rp.Z = ar.Z - 1; vec = rp - orp; float veclen = vec.getLength(); if (veclen < 0.05f) veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps->range(1, 2) == 1); // Carve routes for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz); orp = rp; } void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); MapNode lavanode(c_lava_source); v3s16 startp(orp.X, orp.Y, orp.Z); startp += of; float nval = NoisePerlin3D(np_caveliquids, startp.X, startp.Y, startp.Z, seed); MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ? lavanode : waternode; v3f fp = orp + vec * f; fp.X += 0.1f * ps->range(-10, 10); fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); d1 += ps->range(-1, 1); } bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2; for (s16 z0 = d0; z0 <= d1; z0++) { s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { s16 maxabsxz = MYMAX(abs(x0), abs(z0)); s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); for (s16 y0 = -si2; y0 <= si2; y0++) { // Make better floors in small caves if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7) continue; if (large_cave_is_flat) { // Make large caves not so tall if (rs > 7 && abs(y0) >= rs / 3) continue; } v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; if (vm->m_area.contains(p) == false) continue; u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (!ndef->get(c).is_ground_content) continue; if (large_cave) { int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; if (flooded && full_ymin < water_level && full_ymax > water_level) vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; else if (flooded && full_ymax < water_level) vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode; else vm->m_data[i] = airnode; } else { if (c == CONTENT_IGNORE) continue; vm->m_data[i] = airnode; vm->m_flags[i] |= VMANIP_FLAG_CAVE; } } } } } inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p) { if (heightmap != NULL && p.Z >= node_min.Z && p.Z <= node_max.Z && p.X >= node_min.X && p.X <= node_max.X) { u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); if (heightmap[index] < p.Y) return true; } else if (p.Y > water_level) { return true; } return false; } //// //// CavesV6 //// CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify, int water_level, content_t water_source, content_t lava_source) { assert(ndef); this->ndef = ndef; this->gennotify = gennotify; this->water_level = water_level; c_water_source = water_source; if (c_water_source == CONTENT_IGNORE) c_water_source = ndef->getId("mapgen_water_source"); if (c_water_source == CONTENT_IGNORE) c_water_source = CONTENT_AIR; c_lava_source = lava_source; if (c_lava_source == CONTENT_IGNORE) c_lava_source = ndef->getId("mapgen_lava_source"); if (c_lava_source == CONTENT_IGNORE) c_lava_source = CONTENT_AIR; } void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave, int max_stone_height, s16 *heightmap) { assert(vm); assert(ps); assert(ps2); this->vm = vm; this->ps = ps; this->ps2 = ps2; this->node_min = nmin; this->node_max = nmax; this->heightmap = heightmap; this->large_cave = is_large_cave; this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); int dswitchint = ps->range(1, 14); if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); min_tunnel_diameter = 5; max_tunnel_diameter = ps->range(7, ps->range(8, 24)); } else { part_max_length_rs = ps->range(2, 9); tunnel_routepoints = ps->range(10, ps->range(15, 30)); } large_cave_is_flat = (ps->range(0, 1) == 0); main_direction = v3f(0, 0, 0); // Allowed route area size in nodes ar = node_max - node_min + v3s16(1, 1, 1); // Area starting point in nodes of = node_min; // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 max_spread_amount = MAP_BLOCKSIZE; const s16 insure = 10; s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); if (large_cave) { s16 minpos = 0; if (node_min.Y < water_level && node_max.Y > water_level) { minpos = water_level - max_tunnel_diameter / 3 - of.Y; route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; } route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position orp.Z = (float)(ps->next() % ar.Z) + 0.5f; orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify != NULL) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; gennotify->addEvent(notifytype, abs_pos); } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event if (gennotify != NULL) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); } } void CavesV6::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; main_direction *= (float)ps->range(0, 10) / 10; } // Randomize size s16 min_d = min_tunnel_diameter; s16 max_d = max_tunnel_diameter; rs = ps->range(min_d, max_d); s16 rs_part_max_length_rs = rs * part_max_length_rs; v3s16 maxlen; if (large_cave) { maxlen = v3s16( rs_part_max_length_rs, rs_part_max_length_rs / 2, rs_part_max_length_rs ); } else { maxlen = v3s16( rs_part_max_length_rs, ps->range(1, rs_part_max_length_rs), rs_part_max_length_rs ); } v3f vec; vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2; vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; } // Do not make caves that are entirely above ground, to fix shadow bugs // caused by overgenerated large caves. // It is only necessary to check the startpoint and endpoint. v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; // If startpoint and endpoint are above ground, disable placement of nodes // in carveRoute while still running all PseudoRandom calls to ensure caves // are consistent with existing worlds. bool tunnel_above_ground = p1.Y > getSurfaceFromHeightmap(p1) && p2.Y > getSurfaceFromHeightmap(p2); vec += main_direction; v3f rp = orp + vec; if (rp.X < 0) rp.X = 0; else if (rp.X >= ar.X) rp.X = ar.X - 1; if (rp.Y < route_y_min) rp.Y = route_y_min; else if (rp.Y >= route_y_max) rp.Y = route_y_max - 1; if (rp.Z < 0) rp.Z = 0; else if (rp.Z >= ar.Z) rp.Z = ar.Z - 1; vec = rp - orp; float veclen = vec.getLength(); // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE if (veclen < 0.05f) veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps2->range(1, 2) == 1); // Carve routes for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz, tunnel_above_ground); orp = rp; } void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); MapNode lavanode(c_lava_source); v3s16 startp(orp.X, orp.Y, orp.Z); startp += of; v3f fp = orp + vec * f; fp.X += 0.1f * ps->range(-10, 10); fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); d1 += ps->range(-1, 1); } for (s16 z0 = d0; z0 <= d1; z0++) { s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { if (tunnel_above_ground) continue; s16 maxabsxz = MYMAX(abs(x0), abs(z0)); s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); for (s16 y0 = -si2; y0 <= si2; y0++) { if (large_cave_is_flat) { // Make large caves not so tall if (rs > 7 && abs(y0) >= rs / 3) continue; } v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; if (vm->m_area.contains(p) == false) continue; u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (!ndef->get(c).is_ground_content) continue; if (large_cave) { int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; if (full_ymin < water_level && full_ymax > water_level) { vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; } else if (full_ymax < water_level) { vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode; } else { vm->m_data[i] = airnode; } } else { if (c == CONTENT_IGNORE || c == CONTENT_AIR) continue; vm->m_data[i] = airnode; vm->m_flags[i] |= VMANIP_FLAG_CAVE; } } } } } inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p) { if (heightmap != NULL && p.Z >= node_min.Z && p.Z <= node_max.Z && p.X >= node_min.X && p.X <= node_max.X) { u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); return heightmap[index]; } else { return water_level; } }