From 7000c5220d9ce8864e028d3b035d86040e7de7aa Mon Sep 17 00:00:00 2001 From: orwell96 Date: Sun, 18 Oct 2020 15:27:55 +0200 Subject: Initial working state --- gridgen.lua | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 gridgen.lua (limited to 'gridgen.lua') diff --git a/gridgen.lua b/gridgen.lua new file mode 100644 index 0000000..770f1f6 --- /dev/null +++ b/gridgen.lua @@ -0,0 +1,248 @@ +-- gridgen.lua +-- Actual cell generation process. These methods are called by the mapgen callback to allocate enough structure cells. +-- All randomness happens here. + +--[[ +The mapgen callback initiates the gridgen as follows: +1. mapgen builds a list of unallocated cells it requires to generate the current chunk +2. as long as there are some left, pick one cell that has at least one neighbor generated, randomly +The neighbor cell should have an exit towards the picked unallocated cell, only if there are no such cells it may fall back to any cell. +3. instruct gridgen to generate from the cell that is present, into the direction of the missing cell. + +Given a cell and a direction, the gridgen then performs the following heuristic: +1. initialize an area with the unallocated cell that was passed. +2. assuming the passed direction as "forward", expand the area left, right, up and down as long as there's free space or the maximum structure size is hit. +3. expand the area forward as long as there's space or the maximum structure size is hit + -> maybe extend randomly into each of the 5 directions. Could lead to problems otherwise +The area now represents the maximum size of structures that would fit. +4. filter the structures that do not fit into the frame. +5. randomly select one of the remaining structures, taking their chance into account. +6. using the degrees of freedom the structure has inside the frame, choose a random position of the new structure. +if the origin cell had an exit: try to find a position so that the exit matches one of the new structure +]]-- + +local gg_verbose = function() end +--local gg_verbose = cellworld.log + +-- Make gridgen generate at least the area specified by gp_min, gp_max (in grid units) +-- Important: area may span 255x255x255 cells at maximum! +function cellworld.gridgen_generate_cells(gp_min, gp_max) + cellworld.log("Gridgen: generating area", gp_min, gp_max) + + -- let the allocation efficiently check whether one, all or some cells are allocated + local alloc = cellworld.alloc_check_some_cells_allocated(gp_min, gp_max) + if alloc == 2 then + -- nothing to do + cellworld.log("All cells allocated, nothing to do") + return + elseif alloc == 0 then + -- no cells allocated. seed a 1x1 structure somewhere + local posx, posy, posz = cellworld.random(gp_min.x, gp_max.x), cellworld.random(gp_min.y, gp_max.y), cellworld.random(gp_min.z, gp_max.z) + local posv = vector.new(posx, posy, posz) + local ones = vector.new(1,1,1) + local seedst = cellworld.structure_set.seed_structure + assert(seedst, "No seed structure defined") + cellworld.log("No cells allocated, seeding", seedst,"at",posv) + assert(vector.equals(cellworld.structures[seedst].size, ones)) + cellworld.alloc_set_structure(posv, seedst, 0, ones, true) + end + + gg_verbose("generate_cells searching sprout positions") + local count = 0 + while true do + -- find cells as starting points + -- {cell = , dir = } + local cells_with_exit = {} + local cells_without_exit = {} + local ox, oy, oz = gp_min.x, gp_min.y, gp_min.z + for x,y,z,cid in cellworld.alloc_get_area_iterator(gp_min, gp_max) do + local stname, relpos, rotation = cellworld.alloc_resolve_cid(x,y,z,cid) + assert(stname, "alloc_resolve_cid failed to resolve") + assert(rotation==0, "Rotation is not supported yet") + local struct = cellworld.structures[stname] + if struct then + -- check_borders + local borders = cellworld.get_border_sides(relpos, struct.size) + for _,dir in ipairs(borders) do + local off = cellworld.direction_to_vector[dir] + if not cellworld.alloc_get_cid(x+off.x, y+off.y, z+off.z) then + -- possible candidate + local cell = cellworld.get_structure_cell(struct, relpos) + -- has it an exit here? + if cell.exits and cell.exits[dir] then + --gg_verbose(" found with exit",x,y,z,"dir",cellworld.dirrev[dir]) + table.insert(cells_with_exit, {cell=vector.new(x,y,z), dir=dir}) + else + --gg_verbose(" found without exit",x,y,z,"dir",cellworld.dirrev[dir]) + table.insert(cells_without_exit, {cell=vector.new(x,y,z), dir=dir}) + end + end + end + end + end + gg_verbose("generate_cells sprouting, found",#cells_with_exit,"cells with exit,",#cells_without_exit,"without.") + -- select one of the sprouts randomly + local selected_sprout, exit_present + if #cells_with_exit > 0 then + selected_sprout = cellworld.select_random_element(cells_with_exit) + exit_present = true + elseif #cells_without_exit > 0 then + selected_sprout = cellworld.select_random_element(cells_without_exit) + else + -- both lists are empty. We're either done, or there was another problem. + cellworld.log("generate_cells sprouted",count,"times") + return + end + + cellworld.gridgen_fit_structure(selected_sprout.cell, selected_sprout.dir, exit_present) + count = count + 1 + end +end + +local function can_i_has_space(lx, ly, lz, ux, uy, uz) + -- this calls the iterator once to see if it produces a result + local res = cellworld.alloc_get_area_iterator_raw(lx, ly, lz, ux, uy, uz)() ~= nil + gg_verbose("Space check for area",lx,ly,lz,"<->",ux,uy,uz,"resulted in",(res and "BLOCKED" or "FREE")) + return not res +end + +-- Allocate and place a random structure in the space ahead of sprout_pos towards sprout_dir, try to match exits if requested. +function cellworld.gridgen_fit_structure(sprout_pos, sprout_dir, exit_present) + gg_verbose("fit_structure started with sprout",sprout_pos,"direction",cellworld.dirrev[sprout_dir],"exit present",exit_present) + local start_pos = vector.add(sprout_pos, cellworld.direction_to_vector[sprout_dir]) + local stx, sty, stz = cellworld.v_decompose(start_pos) -- Start position + local slx, sly, slz = stx, sty, stz -- lower bound of free space + local sux, suy, suz = stx, sty, stz -- upper bound of free space + + local max_struct_size = cellworld.maximum_structure_size + local msx, msy, msz = cellworld.v_decompose(max_struct_size) -- maximum structure size of all registered structures + local can_into_dir = {0, 1, 2, 3, 4, 5} + table.remove(can_into_dir, cellworld.opposite_directions[sprout_dir]+1)-- cannot into the sprout cell + -- extend the space randomly into directions + while #can_into_dir > 0 do + gg_verbose("Directions left: ",table.concat(can_into_dir,",")) + local diridx = cellworld.random(1, #can_into_dir) + local dir = can_into_dir[diridx] + gg_verbose("area is now",slx,sly,slz,"<>",sux, suy, suz,", extending",cellworld.dirrev[dir],"(index",diridx,")") + if dir == cellworld.NORTH then + if suz-stz < msz and can_i_has_space(slx, sly, suz+1, sux, suy, suz+1) then + suz = suz + 1 + else + gg_verbose("extending NORTH failed, hit limit",suz-stz < msz) + table.remove(can_into_dir, diridx) + end + end + if dir == cellworld.EAST then + if sux-stx < msx and can_i_has_space(sux+1, sly, slz, sux+1, suy, suz) then + sux = sux + 1 + else + gg_verbose("extending EAST failed, hit limit",sux-stx < msx) + table.remove(can_into_dir, diridx) + end + end + if dir == cellworld.SOUTH then + if stz-slz < msz and can_i_has_space(slx, sly, slz-1, sux, suy, slz-1) then + slz = slz - 1 + else + gg_verbose("extending SOUTH failed, hit limit",stz-slz < msz) + table.remove(can_into_dir, diridx) + end + end + if dir == cellworld.WEST then + if stx-slx < msx and can_i_has_space(slx-1, sly, slz, slx-1, suy, suz) then + slx = slx - 1 + else + gg_verbose("extending WEST failed, hit limit",sux-stx < msx) + table.remove(can_into_dir, diridx) + end + end + if dir == cellworld.UP then + if suy-sty < msy and can_i_has_space(slx, suy+1, slz, sux, suy+1, suz) then + suy = suy + 1 + else + gg_verbose("extending UP failed, hit limit",suy-sty < msy) + table.remove(can_into_dir, diridx) + end + end + if dir == cellworld.DOWN then + if sty-sly < msy and can_i_has_space(slx, sly-1, slz, sux, sly-1, suz) then + sly = sly - 1 + else + gg_verbose("extending DOWN failed, hit limit",sty-sly < msy) + table.remove(can_into_dir, diridx) + end + end + end + + -- determine how much space we have produced + local spx, spy, spz = sux-slx+1, suy-sly+1, suz-slz+1 + gg_verbose("extending done, area is now",slx,sly,slz,"<>",sux, suy, suz,", space",spx, spy, spz) + + -- filter available structures by their size and insert according to probability + local selstructs = {} + for stname, struct in pairs(cellworld.structures) do + local extx, exty, extz = cellworld.v_decompose(struct.size) + if extx <= spx and exty <= spy and extz <= spz then + gg_verbose("structure",stname,struct.size,"fits") + -- structure fits. insert chance times + for i=1,struct.chance do + selstructs[#selstructs+1] = stname + end + else + gg_verbose("structure",stname,struct.size,"doesnt fit") + end + end + if #selstructs==0 then + error("Failed to fit a structure into a space of",spx,spy,spz,", make sure that the structure set includes 1x1x1 structures as fallback!") + end + + local stname = cellworld.select_random_element(selstructs) + local struct = cellworld.structures[stname] + gg_verbose("Selected ",stname, struct.size) + + -- determine degrees of freedom + local extx, exty, extz = cellworld.v_decompose(struct.size) + -- we still want the structure to overlap with our sprout cell + local frlx, frly, frlz = math.max(slx, stx-extx+1), math.max(sly, sty-exty+1), math.max(slz, stz-extz+1) + local frux, fruy, fruz = math.min(sux-extx+1, stx), math.min(suy-exty+1, sty), math.min(suz-extz+1, stz) + + gg_verbose("degrees of freedom",frlx, frly, frlz,"<>",frux, fruy, fruz) + + local posx, posy, posz + if exit_present then + local opp_dir = cellworld.opposite_directions[sprout_dir] + -- find alignments with matching exits + for x in iterate_random_order(frlx, frux) do + for y in iterate_random_order(frly, fruy) do + for z in iterate_random_order(frlz, fruz) do + -- which structure cell borders our sprout cell? + local pos_in_struct = vector.new(stx - x, sty - y, stz - z) + local cell = cellworld.get_structure_cell(struct, pos_in_struct) + if cell.exits and cell.exits[opp_dir] then + gg_verbose("Alignment ",x,y,z,", in struct",pos_in_struct,", has matching exit") + posx, posy, posz = x,y,z + break --break out of the inner loop + end + end + if posx then break end --break out of the outer loop if inner loop broken + end + if posx then break end --break out of the outer loop if inner loop broken + end + end + -- if no matching exit found or none present on sprout cell, select one alignment randomly + if not posx then + posx, posy, posz = cellworld.random(frlx, frux), cellworld.random(frly, fruy), cellworld.random(frlz, fruz), + gg_verbose("Alignment ",posx, posy, posz,"selected randomly") + end + + -- place the structure + gg_verbose("-!- placing",stname,"at",posx, posy, posz) + cellworld.alloc_set_structure(vector.new(posx, posy, posz), stname, 0, struct.size, true) +end + + + + + + + -- cgit v1.2.3