240 lines
5.4 KiB
Lua
240 lines
5.4 KiB
Lua
local zprite = require("zprite")
|
|
|
|
local tex = love.graphics.newImage("atlas.png")
|
|
tex:setFilter("nearest", "nearest")
|
|
|
|
local gx = 0
|
|
local gy = 0
|
|
local angle = 0
|
|
local scale = 0.35
|
|
local dist = 4
|
|
|
|
local function layered_noise(x, y, layers, persistence)
|
|
local freq = 1
|
|
local ampl = 1
|
|
local maxval = 0
|
|
local val = 0
|
|
for _ = 1, layers do
|
|
val = val + love.math.simplexNoise(x * freq, y * freq) * ampl
|
|
|
|
maxval = maxval + ampl
|
|
|
|
ampl = ampl * persistence
|
|
freq = freq * 2
|
|
end
|
|
|
|
return val / maxval
|
|
end
|
|
|
|
local function ease(n)
|
|
return math.min((1.1 * n ^ 2 + 1 - math.cos(n * math.pi / 2)) / 2, 1)
|
|
end
|
|
|
|
local function heightmap(x, y)
|
|
return ease(layered_noise(x * 0.0005, y * 0.0005, 4, 0.8))
|
|
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 alpha = 0.8
|
|
local shadow_alpha
|
|
local colors = {}
|
|
local time = 0
|
|
|
|
local function calculate_shadow_alpha(x, y)
|
|
local delta = 0.01
|
|
local dhdx = (heightmap(x + delta / 2, y) - heightmap(x - delta / 2, y)) / delta
|
|
local dhdy = (heightmap(x, y + delta / 2) - heightmap(x, y - delta / 2)) / delta
|
|
|
|
local value = math.max(math.abs(dhdx), math.abs(dhdy)) * 100
|
|
|
|
return (1 - 1 / (1 + math.exp(value))) / 8
|
|
end
|
|
|
|
function colors.water(layer)
|
|
if layer % 2 == 0 then
|
|
return 0, 0, 0, shadow_alpha
|
|
end
|
|
if layer < 14 then
|
|
local r, g, b, a = colors.sand(layer)
|
|
return r, g, b, a * 1.2
|
|
end
|
|
layer = layer / 2
|
|
return layer / 32, layer / 16, layer / 8, alpha
|
|
end
|
|
function colors.sand(layer)
|
|
if layer % 2 == 0 then
|
|
return 0, 0, 0, shadow_alpha
|
|
end
|
|
layer = layer / 2
|
|
return layer / 24 + 0.3, layer / 24 + 0.2, layer / 24 + 0.1, alpha
|
|
end
|
|
function colors.dirt(layer)
|
|
if layer % 2 == 0 then
|
|
return 0, 0, 0, shadow_alpha
|
|
end
|
|
layer = layer / 2
|
|
if layer < 24 then
|
|
return layer / 32 + 0.1, layer / 48 + 0.05, layer / 64, alpha
|
|
else
|
|
return layer / 50 - 0.1, layer / 50 + 0.1, 0.1, alpha
|
|
end
|
|
end
|
|
function colors.stone(layer)
|
|
if layer % 2 == 0 then
|
|
return 0, 0, 0, shadow_alpha
|
|
end
|
|
layer = layer / 2
|
|
local value = layer / 48 - 0.2
|
|
return value / 1.1, value, value * 1.1, alpha
|
|
end
|
|
function colors.snow(layer)
|
|
if layer % 2 == 0 then
|
|
return 0, 0, 0, shadow_alpha
|
|
end
|
|
layer = layer / 2
|
|
local value = layer / 48 + 0.25
|
|
return value / 1.1, value, value * 1.1, alpha
|
|
end
|
|
|
|
local function offset_map(layer)
|
|
if layer % 2 == 0 then
|
|
return math.cos(time) / 16, math.sin(time) / 16 + 0.5
|
|
else
|
|
return 0, 0.5
|
|
end
|
|
end
|
|
|
|
local z = zprite.zchunk.new(tex, 256, 80, dist * 2 + 1)
|
|
|
|
local function pick_color(height)
|
|
if height < 0.25 then
|
|
return colors.water
|
|
end
|
|
if height < 0.4 then
|
|
return colors.sand
|
|
end
|
|
if height < 0.67 then
|
|
return colors.dirt
|
|
end
|
|
if height < 0.85 then
|
|
return colors.stone
|
|
end
|
|
return colors.snow
|
|
end
|
|
|
|
local function generate(x, y)
|
|
local height = heightmap(x, y)
|
|
shadow_alpha = calculate_shadow_alpha(x, y)
|
|
z:put(x, y, atlas.stone, math.ceil(height * 64) + 16, pick_color(height), offset_map)
|
|
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.keyreleased(key)
|
|
if key == "space" then
|
|
generated = {}
|
|
end
|
|
end
|
|
|
|
function love.update(dt)
|
|
if love.keyboard.isDown("space") then
|
|
time = math.fmod(time + dt, 2 * math.pi)
|
|
end
|
|
|
|
local speed = 512
|
|
if love.keyboard.isDown("d") then
|
|
local dx = speed * math.cos(-angle)
|
|
local dy = speed * math.sin(-angle)
|
|
gx = gx - dx * dt
|
|
gy = gy - dy * dt
|
|
end
|
|
if love.keyboard.isDown("a") then
|
|
local dx = speed * math.cos(-angle)
|
|
local dy = speed * math.sin(-angle)
|
|
gx = gx + dx * dt
|
|
gy = gy + dy * dt
|
|
end
|
|
if love.keyboard.isDown("s") then
|
|
local dx = speed * math.cos(-angle + math.pi / 2)
|
|
local dy = speed * math.sin(-angle + math.pi / 2)
|
|
gx = gx - dx * dt
|
|
gy = gy - dy * dt
|
|
end
|
|
if love.keyboard.isDown("w") then
|
|
local dx = speed * math.cos(-angle + math.pi / 2)
|
|
local dy = speed * math.sin(-angle + math.pi / 2)
|
|
gx = gx + dx * dt
|
|
gy = gy + dy * dt
|
|
end
|
|
if love.keyboard.isDown("q") then
|
|
angle = angle + dt
|
|
end
|
|
if love.keyboard.isDown("e") then
|
|
angle = angle - dt
|
|
end
|
|
if love.keyboard.isDown("f") then
|
|
scale = scale * (1 - dt)
|
|
if scale < 0.2 then
|
|
scale = 0.2
|
|
end
|
|
end
|
|
if love.keyboard.isDown("r") then
|
|
scale = scale * (1 + dt)
|
|
if scale > 5 then
|
|
scale = 5
|
|
end
|
|
end
|
|
|
|
local center_cx = -math.floor(gx / z._chunk_size)
|
|
local center_cy = -math.floor(gy / 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()
|
|
|
|
function love.draw()
|
|
z._height_scale = scale * 20
|
|
|
|
t:reset()
|
|
t:translate(love.graphics.getWidth() / 2, love.graphics.getHeight() / 2)
|
|
t:scale(scale, scale)
|
|
t:rotate(angle)
|
|
t:translate(gx - z._chunk_size / 2, gy - z._chunk_size / 2)
|
|
z:draw(t)
|
|
end
|