Refactor engine for cave wall categorization
							parent
							
								
									817889ef46
								
							
						
					
					
						commit
						a500388803
					
				
							
								
								
									
										6
									
								
								API.md
								
								
								
								
							
							
						
						
									
										6
									
								
								API.md
								
								
								
								
							|  | @ -8,6 +8,12 @@ The API is written in a way to be very similar to the Minetest API. | ||||||
| Underground caves have varying shapes, and the variable | Underground caves have varying shapes, and the variable | ||||||
| `noordstar.registered_shapes` contains all those definitions. | `noordstar.registered_shapes` contains all those definitions. | ||||||
| 
 | 
 | ||||||
|  | For shapes, the following functions are available: | ||||||
|  | 
 | ||||||
|  | - `noordstar_caves.register_shape(shape def)` Define a new cave shape | ||||||
|  | - `noordstar_caves.unregister_shape(name)` Remove a defined cave shape | ||||||
|  | - `noordstar_caves.clear_registered_shapes()` Remove all known cave shapes | ||||||
|  | 
 | ||||||
| The shapes are defined as follows: | The shapes are defined as follows: | ||||||
| 
 | 
 | ||||||
| ```lua | ```lua | ||||||
|  |  | ||||||
							
								
								
									
										267
									
								
								lua/engine.lua
								
								
								
								
							
							
						
						
									
										267
									
								
								lua/engine.lua
								
								
								
								
							|  | @ -1,4 +1,7 @@ | ||||||
| 
 | 
 | ||||||
|  | -- Constants and magic numbers | ||||||
|  | local mapgen_buffer = 16 | ||||||
|  | 
 | ||||||
| -- Convert 3d relative coordinates to an index on a flat array | -- Convert 3d relative coordinates to an index on a flat array | ||||||
| local function from_3d_to_flat(dx, dy, dz, nx, ny) | local function from_3d_to_flat(dx, dy, dz, nx, ny) | ||||||
|     return (nx * ny * dz) + (nx * dy) + dx + 1 |     return (nx * ny * dz) + (nx * dy) + dx + 1 | ||||||
|  | @ -31,6 +34,108 @@ local function iter_3d_area(minp, maxp, callback) | ||||||
|     end |     end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | -- Helper function to convert a set of coordinates to a readable string | ||||||
|  | local function pos_to_str(pos) | ||||||
|  |     return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. " )" | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local Flat3dArray = {} | ||||||
|  | Flat3dArray.__index = Flat3dArray | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:new(minp, maxp, arr) | ||||||
|  |     local instance = {} | ||||||
|  |     setmetatable(instance, Flat3dArray) | ||||||
|  | 
 | ||||||
|  |     local nx = maxp.x - minp.x + 1 | ||||||
|  |     local ny = maxp.y - minp.y + 1 | ||||||
|  |     local nz = maxp.z - minp.z + 1 | ||||||
|  | 
 | ||||||
|  |     if #arr ~= nx * ny * nz then | ||||||
|  |         error( | ||||||
|  |             "Input array doesn't match dimension lengths: " .. nx .. " x " .. | ||||||
|  |             ny .. " x " .. nz .. " = " .. (nx*ny*nz) .. ", but found " .. #arr | ||||||
|  |         ) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     instance.nx = nx | ||||||
|  |     instance.ny = ny | ||||||
|  |     instance.nz = nz | ||||||
|  |     instance.minp = minp | ||||||
|  |     instance.maxp = maxp | ||||||
|  |     instance.arr = arr | ||||||
|  | 
 | ||||||
|  |     return instance | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:from_func(minp, maxp, callback) | ||||||
|  |     local arr = {} | ||||||
|  | 
 | ||||||
|  |     iter_3d_area(minp, maxp, function (i, pos) | ||||||
|  |         arr[i] = callback(i, pos) | ||||||
|  |     end) | ||||||
|  | 
 | ||||||
|  |     return self:new(minp, maxp, arr) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:get_index(i) | ||||||
|  |     local out = self.arr[i] | ||||||
|  |     if out == nil then | ||||||
|  |         error( | ||||||
|  |             "Index " .. i .. " not found in array of length " .. #self.arr | ||||||
|  |         ) | ||||||
|  |     end | ||||||
|  |     return out | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:get_pos(pos) | ||||||
|  |     self:validate_pos(pos) | ||||||
|  | 
 | ||||||
|  |     local dx = pos.x - self.minp.x | ||||||
|  |     local dy = pos.y - self.minp.y | ||||||
|  |     local dz = pos.z - self.minp.z | ||||||
|  | 
 | ||||||
|  |     return self:get_index(from_3d_to_flat(dx, dy, dz, self.nx, self.ny)) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local function is_valid_pos(pos, minp, maxp) | ||||||
|  |     if pos.x < minp.x then | ||||||
|  |         return false | ||||||
|  |     elseif pos.x > maxp.x then | ||||||
|  |         return false | ||||||
|  |     elseif pos.y < minp.y then | ||||||
|  |         return false | ||||||
|  |     elseif pos.y > maxp.y then | ||||||
|  |         return false | ||||||
|  |     elseif pos.z < minp.z then | ||||||
|  |         return false | ||||||
|  |     elseif pos.z > maxp.z then | ||||||
|  |         return false | ||||||
|  |     else | ||||||
|  |         return true | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:valid_pos(pos) | ||||||
|  |     return is_valid_pos(pos, self.minp, self.maxp) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Flat3dArray:validate_pos(pos) | ||||||
|  |     if not self:valid_pos(pos) then | ||||||
|  |         error( | ||||||
|  |             table.concat( | ||||||
|  |                 { "Position " | ||||||
|  |                 , pos_to_str(pos) | ||||||
|  |                 , " out of bounds from minp = " | ||||||
|  |                 , pos_to_str(self.minp) | ||||||
|  |                 , ", maxp = " | ||||||
|  |                 , pos_to_str(self.maxp) | ||||||
|  |                 }, | ||||||
|  |                 "" | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     end | ||||||
|  | end | ||||||
|  | 
 | ||||||
| -- Get an enhanced function from the def function that warns us when the | -- Get an enhanced function from the def function that warns us when the | ||||||
| -- function does not behave properly | -- function does not behave properly | ||||||
| local function enhanced_func(def) | local function enhanced_func(def) | ||||||
|  | @ -83,7 +188,7 @@ local function get_flat_from_shape_def(def, minp, maxp) | ||||||
|         noise_flat_map[i] = f(pos, noise_flat_map[i]) |         noise_flat_map[i] = f(pos, noise_flat_map[i]) | ||||||
|     end) |     end) | ||||||
| 
 | 
 | ||||||
|     return noise_flat_map |     return Flat3dArray:new(minp, maxp, noise_flat_map) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function get_flat_from_noise_params(minp, maxp, noise_params) | local function get_flat_from_noise_params(minp, maxp, noise_params) | ||||||
|  | @ -101,7 +206,7 @@ local function get_flat_from_noise_params(minp, maxp, noise_params) | ||||||
|         p:get_3d_map_flat(minp, buffer) |         p:get_3d_map_flat(minp, buffer) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     return buffer |     return Flat3dArray:new(minp, maxp, buffer) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| -- Based on the number of cave shapes, calculate how quickly connectivity is | -- Based on the number of cave shapes, calculate how quickly connectivity is | ||||||
|  | @ -172,18 +277,16 @@ local function get_threshold_flat(minp, maxp) | ||||||
|         } |         } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     local thresholds = {} |     -- Create the flat array | ||||||
| 
 |     return Flat3dArray:from_func(minp, maxp, function(i, pos) | ||||||
|     -- Fill the table |  | ||||||
|     iter_3d_area(minp, maxp, function(i, pos) |  | ||||||
|         local total = 0 |         local total = 0 | ||||||
|         local count = 0 |         local count = 0 | ||||||
| 
 | 
 | ||||||
|         local x = connectivity[i] |         local x = connectivity:get_pos(pos) | ||||||
|         local y = verticality[i] |         local y = verticality:get_pos(pos) | ||||||
| 
 | 
 | ||||||
|         for _, n in pairs(noise) do |         for _, n in pairs(noise) do | ||||||
|             local v = n.noise[i] |             local v = n.noise:get_pos(pos) | ||||||
| 
 | 
 | ||||||
|             local dx = math.abs(x - n.def.connectivity_point) |             local dx = math.abs(x - n.def.connectivity_point) | ||||||
|             local dy = math.abs(y - n.def.verticality_point) |             local dy = math.abs(y - n.def.verticality_point) | ||||||
|  | @ -197,13 +300,11 @@ local function get_threshold_flat(minp, maxp) | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         if count <= 0 then |         if count <= 0 then | ||||||
|             thresholds[i] = -1000 |             return -1000 | ||||||
|         else |         else | ||||||
|             thresholds[i] = total / count |             return total / count | ||||||
|         end |         end | ||||||
|     end) |     end) | ||||||
| 
 |  | ||||||
|     return thresholds |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -212,7 +313,7 @@ local old_overworld_min = mcl_vars.mg_overworld_min | ||||||
| 
 | 
 | ||||||
| -- If another mod doesn't override the maximum world depth, we will assume that | -- If another mod doesn't override the maximum world depth, we will assume that | ||||||
| -- the world depth is the following value. | -- the world depth is the following value. | ||||||
| local world_depth = -800 | local world_depth = -60 | ||||||
| 
 | 
 | ||||||
| -- Otherwise, this variable can be changed using the following function, | -- Otherwise, this variable can be changed using the following function, | ||||||
| -- which will also update all the other necessary variables | -- which will also update all the other necessary variables | ||||||
|  | @ -246,48 +347,126 @@ local function cave_vastness(pos) | ||||||
|     end |     end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local tpd_yet = false | -- Get a flat array of nodes that are either in caves or not in caves | ||||||
|  | local function get_flat_cave_bools(minp, maxp) | ||||||
|  |     local thresholds = get_threshold_flat(minp, maxp) | ||||||
|  | 
 | ||||||
|  |     return Flat3dArray:from_func(minp, maxp, function(i, pos) | ||||||
|  |         return thresholds:get_pos(pos) >= 1 - cave_vastness(pos) | ||||||
|  |     end) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | local node_type = | ||||||
|  |     { unknown = 1  -- Edge of a chunk | ||||||
|  |     , floor = 2    -- Floor of a cave | ||||||
|  |     , wall = 3     -- Side wall of a cave | ||||||
|  |     , roof = 4     -- Roof of a cave | ||||||
|  |     , content = 5  -- Air in the cave not adjacent to a wall, floor or roof | ||||||
|  |     , stone = 6    -- Underground node not adjacent to a cave | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | local function get_flat_cave_node_types(minp, maxp) | ||||||
|  |     local bools = get_flat_cave_bools(minp, maxp) | ||||||
|  | 
 | ||||||
|  |     return Flat3dArray:from_func(minp, maxp, function (i, pos) | ||||||
|  |         if not bools:get_pos(pos) then | ||||||
|  |             return node_type.stone | ||||||
|  |         -- Floor takes precedence | ||||||
|  |         elseif pos.y == minp.y then -- Could be floor | ||||||
|  |             return node_type.unknown | ||||||
|  |         elseif not bools:get_pos({ x = pos.x, y = pos.y - 1, z = pos.z }) then | ||||||
|  |             return node_type.floor | ||||||
|  |         -- Then roof takes precedence | ||||||
|  |         elseif pos.y == maxp.y then -- Could be roof | ||||||
|  |             return node_type.unknown | ||||||
|  |         elseif not bools:get_pos({ x = pos.x, y = pos.y + 1, z = pos.z }) then | ||||||
|  |             return node_type.roof | ||||||
|  |         else | ||||||
|  |             -- Check for walls | ||||||
|  |             local left  = { x = pos.x - 1, y = pos.y, z = pos.z } | ||||||
|  |             local right = { x = pos.x + 1, y = pos.y, z = pos.z } | ||||||
|  |             local front = { x = pos.x, y = pos.y, z = pos.z - 1 } | ||||||
|  |             local back  = { x = pos.x, y = pos.y, z = pos.z + 1 } | ||||||
|  | 
 | ||||||
|  |             -- Check if the value is near the edge | ||||||
|  |             local on_edge = false | ||||||
|  | 
 | ||||||
|  |             if left.x < minp.x then | ||||||
|  |                 on_edge = true | ||||||
|  |             elseif not bools:get_pos(left) then | ||||||
|  |                 return node_type.wall | ||||||
|  |             end | ||||||
|  |             if right.x > maxp.x then | ||||||
|  |                 on_edge = true | ||||||
|  |             elseif not bools:get_pos(right) then | ||||||
|  |                 return node_type.wall | ||||||
|  |             end | ||||||
|  |             if front.z < minp.z then | ||||||
|  |                 on_edge = true | ||||||
|  |             elseif not bools:get_pos(front) then | ||||||
|  |                 return node_type.wall | ||||||
|  |             end | ||||||
|  |             if back.z > maxp.z then | ||||||
|  |                 on_edge = true | ||||||
|  |             elseif not bools:get_pos(back) then | ||||||
|  |                 return node_type.wall | ||||||
|  |             end | ||||||
|  | 
 | ||||||
|  |             if on_edge then | ||||||
|  |                 return node_type.unknown | ||||||
|  |             else | ||||||
|  |                 return node_type.content | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     end) | ||||||
|  | end | ||||||
| 
 | 
 | ||||||
| minetest.register_on_generated(function(minp, maxp, blockseed) | minetest.register_on_generated(function(minp, maxp, blockseed) | ||||||
|     if maxp.y < world_depth then |     local vminp = | ||||||
|         return |         { x = minp.x - mapgen_buffer | ||||||
|     end |         , y = minp.y - mapgen_buffer | ||||||
|  |         , z = minp.z - mapgen_buffer | ||||||
|  |         } | ||||||
|  |     local vmaxp = | ||||||
|  |         { x = maxp.x + mapgen_buffer | ||||||
|  |         , y = maxp.y + mapgen_buffer | ||||||
|  |         , z = maxp.z + mapgen_buffer | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|     -- Get voxelmanip |     -- Get voxelmanip | ||||||
|     local vm = minetest.get_mapgen_object("voxelmanip") |     local vm = minetest.get_mapgen_object("voxelmanip") | ||||||
|     local data = vm:get_data() |     local flat_data = Flat3dArray:new(vminp, vmaxp, vm:get_data()) | ||||||
|      |      | ||||||
|     -- Get threshold values |     -- Get threshold values | ||||||
|     local thresholds = get_threshold_flat(minp, maxp) |     local node_types = get_flat_cave_node_types(vminp, vmaxp) | ||||||
|     local air = minetest.get_content_id("mcl_core:glass") |      | ||||||
|  |     local node_air   = minetest.get_content_id("air") | ||||||
|  |     local node_floor = minetest.get_content_id("mcl_core:glass_green") | ||||||
|  |     local node_wall  = minetest.get_content_id("mcl_core:glass_purple") | ||||||
|  |     local node_roof  = minetest.get_content_id("mcl_core:glass_red") | ||||||
|  |     local node_other = minetest.get_content_id("mcl_core:glass") | ||||||
| 
 | 
 | ||||||
|     local count = 0 |     local nids = Flat3dArray:from_func(vminp, vmaxp, function(i, pos) | ||||||
|  |         local nt = node_types:get_pos(pos) | ||||||
| 
 | 
 | ||||||
|     iter_3d_area(minp, maxp, function(i, pos) |         if not is_valid_pos(pos, minp, maxp) then | ||||||
|         local nx = maxp.x - minp.x + 1 + 32 |             return flat_data:get_pos(pos) | ||||||
|         local ny = maxp.y - minp.y + 1 + 32 |         elseif nt == node_type.unknown then | ||||||
| 
 |             return node_other | ||||||
|         local dx = pos.x - minp.x |         elseif nt == node_type.floor then | ||||||
|         local dy = pos.y - minp.y |             return node_floor | ||||||
|         local dz = pos.z - minp.z |         elseif nt == node_type.wall then | ||||||
| 
 |             return node_wall | ||||||
| 
 |         elseif nt == node_type.roof then | ||||||
|         local vi = from_3d_to_flat(dx + 16, dy + 16, dz + 16, nx, ny) |             return node_roof | ||||||
| 
 |         elseif nt == node_type.content then | ||||||
|         if not data[vi] then |             return node_air | ||||||
|             error("Vi is not in data (len " .. #data .. "): " .. vi .. " for (dx, dy, dz) = (" .. dx .. ", " .. dy .. ", " .. dz .. ") with minp = (" .. minp.x .. ", " .. minp.y .. ", " .. minp.z .. ") and maxp = (" .. maxp.x .. ", " .. maxp.y .. ", " .. maxp.z .. ") and pos = (" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. ")") |         elseif nt == node_type.stone then | ||||||
|         end |             return flat_data:get_pos(pos) | ||||||
|          |  | ||||||
|         if thresholds[i] >= (1 - cave_vastness(pos)) then |  | ||||||
|             data[vi] = air |  | ||||||
| 
 |  | ||||||
|             count = count + 1 |  | ||||||
|         end |         end | ||||||
|     end) |     end) | ||||||
| 
 | 
 | ||||||
|     -- Write all changes to the Minetest world |     -- Write all changes to the Minetest world | ||||||
|     vm:set_data(data) |     vm:set_data(nids.arr) | ||||||
|     vm:write_to_map() |     vm:write_to_map() | ||||||
| 
 |  | ||||||
|     minetest.chat_send_all("Updated " .. count .. " squares") |  | ||||||
| end) | end) | ||||||
|  |  | ||||||
|  | @ -46,3 +46,7 @@ function noordstar_caves.register_shape(def) | ||||||
|         noordstar_caves.registered_shapes[d.name] = d |         noordstar_caves.registered_shapes[d.name] = d | ||||||
|     end |     end | ||||||
| end | end | ||||||
|  | 
 | ||||||
|  | function noordstar_caves.clear_registered_shapes() | ||||||
|  |     noordstar_caves.registered_shapes = {} | ||||||
|  | end | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue