diff --git a/main.lua b/main.lua index df1ee37..f90d519 100644 --- a/main.lua +++ b/main.lua @@ -1,110 +1,133 @@ +local zprite = require("zprite") + local tex = love.graphics.newImage("atlas.png") tex:setFilter("nearest", "nearest") -local function water_color(layer) - local value = layer / 24 - return value / 4, value / 2, value -end - -local function sand_color(layer) - return layer / 32 + 0.2, layer / 32 + 0.15, 0.4 -end - -local function dirt_color(layer) - if layer < 32 then - return layer / 40 / 2.5, layer / 80 / 2.5, 0.1 - else - return 0.05, (layer - 32) / 36 + 0.3, 0.05 - end -end - -local function mountain_color(layer) - if layer < 58 then - local value = layer / 64 / 2 - 0.1 - return value, value, value - else - local value = (layer - 58) / 20 + 0.7 - return value, value, value - end -end - -local function pick_colorfn(maxlayer) - if maxlayer < 16 then - return water_color - elseif maxlayer < 22 then - return sand_color - elseif maxlayer < 46 then - return dirt_color - else - return mountain_color - end -end - -local function ease(n) - return 5 ^ (3.1 * (n ^ 0.3) - 3) + 1 - 1.1 ^ n -end - -local WATER = love.graphics.newQuad(1, 1, 8, 8, tex) -local SAND = love.graphics.newQuad(11, 1, 8, 8, tex) -local DIRT = love.graphics.newQuad(21, 1, 8, 8, tex) -local GRASS = love.graphics.newQuad(31, 1, 8, 8, tex) -local STONE = love.graphics.newQuad(1, 11, 8, 8, tex) -local SNOW = love.graphics.newQuad(11, 11, 8, 8, tex) - -local function dirt_sprite(layer) - if layer < 32 then - return DIRT - end - return GRASS -end - -local function mountain_sprite(layer) - if layer < 58 then - return STONE - end - return SNOW -end - -local function pick_sprite(maxlayer) - if maxlayer < 16 then - return WATER - end - if maxlayer < 22 then - return SAND - end - if maxlayer < 46 then - return dirt_sprite - end - return mountain_sprite -end - -local z = require("zprite").zchunk.new(tex, 128, 64) - -local function update_map() - z:clear() - for i = -160, 160 do - for j = -160, 160 do - local noise_value = love.math.simplexNoise(i * 0.008, j * 0.008) - local layers = ease(noise_value) * 64 - z:put(i * 8, j * 8, pick_sprite(layers), math.floor(layers) + 1, pick_colorfn(layers)) - coroutine.yield() - end - end -end -local co = coroutine.create(update_map) - local x = 0 local y = 0 local angle = 0 local scale = 1 +local dist = 4 -function love.update(dt) - for _ = 1, 64 do - if coroutine.status(co) == "suspended" then - coroutine.resume(co) - end +local atlas = { + water = love.graphics.newQuad(1, 1, 8, 8, tex), + sand = love.graphics.newQuad(11, 1, 8, 8, tex), + dirt = love.graphics.newQuad(21, 1, 8, 8, tex), + grass = love.graphics.newQuad(31, 1, 8, 8, tex), + stone = love.graphics.newQuad(1, 11, 8, 8, tex), + snow = love.graphics.newQuad(11, 11, 8, 8, tex), +} + +local colors = {} +function colors.water(layer) + return 0.1, layer / 16, layer / 8 +end +function colors.sand(layer) + return layer / 16 + 0.2, layer / 16 + 0.15, 0.4 +end +function colors.dirt(layer) + if layer < 20 then + return layer / 40, layer / 80, 0.1 + else + return 0.1, layer / 32 + 0.3, 0.1 + end +end +function colors.stone(layer) + local value = layer / 48 - 0.2 + return value / 1.1, value, value * 1.1 +end +function colors.snow(layer) + local value = layer / 48 + 0.2 + return value / 1.1, value, value * 1.1 +end + +local z = zprite.zchunk.new(tex, 16 * 8, 32, dist * 2 + 1) + +local function layered_noise(x, y, layers, persistence) + local freq = 1 + local ampl = 1 + local maxval = 0 + local val = 0 + for i = 1, layers do + val = val + love.math.perlinNoise(x * freq, y * freq) * ampl + + maxval = maxval + ampl + + ampl = ampl * persistence + freq = freq * 2 end + return val / maxval +end + +local function heightmap(x, y) + return layered_noise(x * 0.003, y * 0.003, 1, 0.1) +end + +local function dirtgrass_sprite(layer) + if layer < 20 then + return atlas.dirt + end + return atlas.grass +end + +local function pick_from_atlas(height) + if height < 0.2 then + return atlas.water + end + if height < 0.3 then + return atlas.sand + end + if height < 0.7 then + return dirtgrass_sprite + end + if height < 0.9 then + return atlas.stone + end + return atlas.snow +end + +local function pick_color(height) + if height < 0.25 then + return colors.water + end + if height < 0.3 then + return colors.sand + end + if height < 0.7 then + return colors.dirt + end + if height < 0.9 then + return colors.stone + end + return colors.snow +end + +local function generate(x, y) + local height = heightmap(x, y) + z:put(x, y, pick_from_atlas(height), math.ceil(height * 32), pick_color(height)) +end + +local generated = {} +local function generate_chunk(cx, cy) + local idx = z:_chunk_index(cx * z._chunk_size, cy * z._chunk_size) + if generated[idx] then + return + end + generated[idx] = { cx, cy } + z:clear(cx * z._chunk_size, cy * z._chunk_size) + + for i = 1, z._chunk_size, 8 do + for j = 1, z._chunk_size, 8 do + local x = cx * z._chunk_size + i - 1 + local y = cy * z._chunk_size + j - 1 + + generate(x, y) + end + end +end + +function love.update(dt) if love.keyboard.isDown("d") then local dx = 256 * math.cos(-angle) local dy = 256 * math.sin(-angle) @@ -136,17 +159,31 @@ function love.update(dt) angle = angle - dt end if love.keyboard.isDown("f") then - scale = scale - dt * 5 + scale = scale * (1 - dt) if scale < 0.2 then scale = 0.2 end end if love.keyboard.isDown("r") then - scale = scale + dt * 5 + scale = scale * (1 + dt) if scale > 5 then scale = 5 end end + + local center_cx = -math.floor(x / z._chunk_size) + local center_cy = -math.floor(y / z._chunk_size) + + for key, gen in pairs(generated) do + if math.abs(gen[1] - center_cx) > dist or math.abs(gen[2] - center_cy) > dist then + generated[key] = nil + end + end + for cx = center_cx - dist, center_cx + dist do + for cy = center_cy - dist, center_cy + dist do + generate_chunk(cx, cy) + end + end end local t = love.math.newTransform() @@ -155,10 +192,11 @@ function love.draw() for _, chunk in pairs(z._chunks) do chunk.z._height_scale = 12 * scale end + t:reset() t:translate(love.graphics.getWidth() / 2, love.graphics.getHeight() / 2) t:scale(scale, scale) t:rotate(angle) - t:translate(x, y) + t:translate(x - z._chunk_size, y - z._chunk_size / 2) z:draw(t) end diff --git a/zprite b/zprite index ccfa075..071b00d 160000 --- a/zprite +++ b/zprite @@ -1 +1 @@ -Subproject commit ccfa075d244c87171799941125decd143c3fb37a +Subproject commit 071b00d922ba6dcf76b5ecae0fdd5c01579482bf