diff --git a/collada_scene.lua b/collada_scene.lua index 7a53ce3..3ff46e6 100644 --- a/collada_scene.lua +++ b/collada_scene.lua @@ -21,7 +21,7 @@ local pixel_ssao = love.filesystem.newFileData("pixel_ssao.glsl") local shader_ssao = love.graphics.newShader(pixel_ssao, vertex_screen) local pixel_clear = love.filesystem.newFileData("pixel_clear.glsl") -local shader_clear = love.graphics.newShader(pixel_clear, vertex_screen) +shader_clear = love.graphics.newShader(pixel_clear, vertex_screen) local pixel_blur = love.filesystem.newFileData("pixel_blur.glsl") local shader_blur = love.graphics.newShader(pixel_blur, vertex_screen) diff --git a/main.lua b/main.lua index ceedf3e..a062df9 100644 --- a/main.lua +++ b/main.lua @@ -1,6 +1,3 @@ -local rotation -local texture - local ffi = require 'ffi' local _math = require '_math' local mat4 = _math.mat4 @@ -21,6 +18,10 @@ local terminus_font local update_global_parameters = require "update_global_parameters" +local minecraft = require "minecraft" + +local joysticks + local scenes = { sci_fi_ship = { descriptor = scene_sci_fi_ship.descriptor, @@ -89,17 +90,23 @@ function init() load_screen_shader() terminus_font = font.load_font(font.fonts.ter_10x18) + + ---------------------------------------------------------------------- + -- minecraft + ---------------------------------------------------------------------- + + minecraft.init() + + -- + joysticks = love.joystick.getJoysticks() end -local rotation = 0.0 local t = 0.0 local update = function(dt) collada_scene_animate.update(t, node_state) t = t + 0.016 * 0.1 - - rotation = rotation + 0.01 end local draw = function() @@ -138,6 +145,14 @@ local keypressed = function(key, scancode, isrepeat) update_global_parameters.update_current_ix(function(v) return v - 1 end) elseif key == "down" then update_global_parameters.update_current_ix(function(v) return v + 1 end) + -- elseif key == "j" then + -- minecraft.viewpos.x = minecraft.viewpos.x - 5 + -- elseif key == "l" then + -- minecraft.viewpos.x = minecraft.viewpos.x + 5 + -- elseif key == "i" then + -- minecraft.viewpos.z = minecraft.viewpos.z - 5 + -- elseif key == "k" then + -- minecraft.viewpos.z = minecraft.viewpos.z + 5 else print(key) end @@ -165,8 +180,24 @@ function love.run() local dt = love.timer.step() update(dt) - draw() + for _, joystick in ipairs(joysticks) do + local lx = joystick:getGamepadAxis("leftx") + local ly = joystick:getGamepadAxis("lefty") + local ry = joystick:getGamepadAxis("righty") + minecraft.viewpos.x = minecraft.viewpos.x + 2.5 * lx + minecraft.viewpos.y = minecraft.viewpos.y + -2.5 * ry + minecraft.viewpos.z = minecraft.viewpos.z + -2.5 * ly + end + + --draw() + local width, height = love.graphics.getDimensions() + local aspect_ratio = width / height + local perspective_projection = mat4.perspective_fov_rh(scalar.convert_to_radians(45 * 0.75), + aspect_ratio, + 1, + 0.1) + minecraft.draw(perspective_projection) love.graphics.present() love.timer.sleep(0.001) end diff --git a/minecraft.lua b/minecraft.lua new file mode 100644 index 0000000..5592360 --- /dev/null +++ b/minecraft.lua @@ -0,0 +1,106 @@ +local ffi = require "ffi" +local _math = require '_math' +local mat4 = _math.mat4 +local vec3 = _math.vec3 +local vec4 = _math.vec4 + +local cube_indices = { + 0, 1, 2, + 3, 4, 5, + 6, 7, 8, + 9, 10, 11, + 12, 13, 14, + 15, 16, 17, + 0, 18, 1, + 3, 19, 4, + 6, 20, 7, + 9, 21, 10, + 12, 22, 13, + 15, 23, 16 +} +local cube_indices_count = #cube_indices +local cube_index_buffer +local blocks +local shader_minecraft +local region_buffers = {} + +local load_blocks_buffer = function(name) + local data = love.filesystem.newFileData(name) + local format = { + { format = "uint8vec4" }, + } + local buffer = love.graphics.newBuffer(format, data, { shaderstorage = true, usage = "static" }) + return buffer +end + +local load_region_buffer = function(path) + local data = love.filesystem.newFileData(path) + local format = { + { format = "uint8vec4" }, + { format = "uint32" }, + } + local buffer = love.graphics.newBuffer(format, data, { shaderstorage = true, usage = "static" }) + return buffer +end + +local load_cube_index_buffer = function() + local data = love.data.newByteData(cube_indices_count * 4) + local ptr = ffi.cast("uint32_t*", data:getFFIPointer()) + for i, index in ipairs(cube_indices) do + ptr[i - 1] = index + end + local buffer = love.graphics.newBuffer("uint32", data, { index = true, usage = "static" }) + return buffer +end + +local load_minecraft_shader = function() + local pixel_data = love.filesystem.newFileData("pixel_minecraft.glsl") + local vertex_data = love.filesystem.newFileData("vertex_minecraft.glsl") + local shader = love.graphics.newShader(pixel_data, vertex_data) + return shader +end + +local init = function() + table.insert(region_buffers, load_region_buffer("minecraft/region.0.0.data")) + table.insert(region_buffers, load_region_buffer("minecraft/region.-1.0.data")) + table.insert(region_buffers, load_region_buffer("minecraft/region.0.-1.data")) + table.insert(region_buffers, load_region_buffer("minecraft/region.-1.-1.data")) + cube_index_buffer = load_cube_index_buffer() + shader_minecraft = load_minecraft_shader() +end + +local viewpos = { + x = -4 * 31, + y = 0, + z = -4 * 31, +} + +local draw = function(projection) + local view = mat4.look_at_rh(vec3(-50 + viewpos.x, -50 + viewpos.z, 100 + 50 + viewpos.y), + vec3(50 + viewpos.x, 50 + viewpos.z, 50 + viewpos.y), + vec3(0, 0, 1)) + + love.graphics.setCanvas() + + love.graphics.setDepthMode("always", true) + love.graphics.setShader(shader_clear) + love.graphics.drawFromShader(screen_index_buffer, 3 * 2, 1, 1) + + love.graphics.setDepthMode("greater", true) + love.graphics.setShader(shader_minecraft) + + local view_projection = view * projection + + for _, region_buffer in ipairs(region_buffers) do + local instance_count = math.floor(region_buffer:getSize() / (4 * 2)) + shader_minecraft:send("transform", "column", view_projection:data()) + shader_minecraft:send("BlocksLayout", region_buffer) + love.graphics.drawFromShader(cube_index_buffer, cube_indices_count, instance_count, 1) + end +end + +return { + viewpos = viewpos, + init = init, + draw = draw, +} diff --git a/minecraft/region.-1.-1.data b/minecraft/region.-1.-1.data new file mode 100644 index 0000000..1193b0b Binary files /dev/null and b/minecraft/region.-1.-1.data differ diff --git a/minecraft/region.-1.0.data b/minecraft/region.-1.0.data new file mode 100644 index 0000000..41736dd Binary files /dev/null and b/minecraft/region.-1.0.data differ diff --git a/minecraft/region.0.-1.data b/minecraft/region.0.-1.data new file mode 100644 index 0000000..f538327 Binary files /dev/null and b/minecraft/region.0.-1.data differ diff --git a/minecraft/region.0.0.data b/minecraft/region.0.0.data new file mode 100644 index 0000000..08a1947 Binary files /dev/null and b/minecraft/region.0.0.data differ diff --git a/pixel_minecraft.glsl b/pixel_minecraft.glsl new file mode 100644 index 0000000..6b7d8fa --- /dev/null +++ b/pixel_minecraft.glsl @@ -0,0 +1,22 @@ +#pragma language glsl4 + +in vec3 PixelNormal; +in vec3 PixelColor; +in float PixelBlock; + +out vec4 out_color; + +void pixelmain() +{ + if (PixelBlock == 0) { + discard; + return; + } + + vec3 light_direction = normalize(vec3(-1, -0.5, 0.5)); + float diffuse_intensity = max(dot(PixelNormal, light_direction), 0.0); + + vec3 color = PixelColor * diffuse_intensity; + + out_color = vec4(color, 1.0); +} diff --git a/reasons_for_raw_opengl.txt b/reasons_for_raw_opengl.txt new file mode 100644 index 0000000..a951a38 --- /dev/null +++ b/reasons_for_raw_opengl.txt @@ -0,0 +1,19 @@ +Reason #1: Can't bind vertex attributes per draw call +---------------------------------------------------------------------- + +While this can be mostly worked around with shader storage buffers and +the Love2D "drawFromShader" API, the format of shader storage buffers +must be defined using GLSL types. + +GLSL has no sized integer or sized float type, which makes it +impossible to use, for example, vectors of uint8. uint8 is useful for +representing things like Minecraft Block IDs. + +A workaround for the lack of sized scalars in shader storage buffers +is to use bit shifts on GLSL's uint(32) type, but this is fragile as +it makes the shader dependent on host endianness. + +However, this is extremely awkward if a vector of signed 8-bit +integers is desired (as in signed Minecraft chunk x/z coordinates). A +workaround for this is to perform sign extension manually via GLSL +arithmetic. diff --git a/vertex_minecraft.glsl b/vertex_minecraft.glsl new file mode 100644 index 0000000..6e4a39e --- /dev/null +++ b/vertex_minecraft.glsl @@ -0,0 +1,81 @@ +#pragma language glsl4 + +struct vertex_t { + vec3 position; + vec3 normal; +}; + +vertex_t vertices[] = vertex_t[](vertex_t(vec3(-1.0, 1.0, -1.0), vec3(0.0, 1.0, 0.0)), + vertex_t(vec3(1.0, 1.0, 1.0), vec3(0.0, 1.0, 0.0)), + vertex_t(vec3(1.0, 1.0, -1.0), vec3(0.0, 1.0, 0.0)), + vertex_t(vec3(1.0, 1.0, 1.0), vec3(0.0, 0.0, 1.0)), + vertex_t(vec3(-1.0, -1.0, 1.0), vec3(0.0, 0.0, 1.0)), + vertex_t(vec3(1.0, -1.0, 1.0), vec3(0.0, 0.0, 1.0)), + vertex_t(vec3(-1.0, 1.0, 1.0), vec3(-1.0, 0.0, 0.0)), + vertex_t(vec3(-1.0, -1.0, -1.0), vec3(-1.0, 0.0, 0.0)), + vertex_t(vec3(-1.0, -1.0, 1.0), vec3(-1.0, 0.0, 0.0)), + vertex_t(vec3(1.0, -1.0, -1.0), vec3(0.0, -1.0, 0.0)), + vertex_t(vec3(-1.0, -1.0, 1.0), vec3(0.0, -1.0, 0.0)), + vertex_t(vec3(-1.0, -1.0, -1.0), vec3(0.0, -1.0, 0.0)), + vertex_t(vec3(1.0, 1.0, -1.0), vec3(1.0, 0.0, 0.0)), + vertex_t(vec3(1.0, -1.0, 1.0), vec3(1.0, 0.0, 0.0)), + vertex_t(vec3(1.0, -1.0, -1.0), vec3(1.0, 0.0, 0.0)), + vertex_t(vec3(-1.0, 1.0, -1.0), vec3(0.0, 0.0, -1.0)), + vertex_t(vec3(1.0, -1.0, -1.0), vec3(0.0, 0.0, -1.0)), + vertex_t(vec3(-1.0, -1.0, -1.0), vec3(0.0, 0.0, -1.0)), + vertex_t(vec3(-1.0, 1.0, 1.0), vec3(0.0, 1.0, 0.0)), + vertex_t(vec3(-1.0, 1.0, 1.0), vec3(0.0, 0.0, 1.0)), + vertex_t(vec3(-1.0, 1.0, -1.0), vec3(-1.0, 0.0, 0.0)), + vertex_t(vec3(1.0, -1.0, 1.0), vec3(0.0, -1.0, 0.0)), + vertex_t(vec3(1.0, 1.0, 1.0), vec3(1.0, 0.0, 0.0)), + vertex_t(vec3(1.0, 1.0, -1.0), vec3(0.0, 0.0, -1.0))); + +uniform mat4 transform; + +out vec3 PixelNormal; +out vec3 PixelColor; +out float PixelBlock; + +struct block_t { + uint x_z_blockid; + uint block_index; +}; + +layout (std430) readonly buffer BlocksLayout +{ + block_t Blocks[]; +}; + +vec3 palette(float t) { + vec3 a = vec3(0.5, 0.5, 0.5); + vec3 b = vec3(0.5, 0.5, 0.5); + vec3 c = vec3(1.0, 1.0, 1.0); + vec3 d = vec3(0.263, 0.416, 0.557); + + return a + b * cos(6.28318 * (c * t + d)); +} + +int sign_extend(uint x) +{ + return int(((x + 0x80) & 0xff) - 0x80); +} + +void vertexmain() +{ + vertex_t vertex = vertices[gl_VertexID]; + + block_t block = Blocks[int(gl_InstanceID)]; + float x = float(block.block_index / (128 * 16)); + float y = float(block.block_index % 128); + float z = float(int(block.block_index / 128) % 16); + + float chunk_x = float(sign_extend((block.x_z_blockid >> 0) & 0xff)); + float chunk_z = float(sign_extend((block.x_z_blockid >> 8) & 0xff)); + int block_id = int((block.x_z_blockid >> 16) & 0xff); + + PixelNormal = vertex.normal; + PixelColor = palette(float(block_id) * 0.01); + PixelBlock = block_id; + vec3 position = ((vertex.position * 0.4) + vec3(x, z, y) + vec3(chunk_x * 16.0, chunk_z * 16.0, 0)); + love_Position = transform * vec4(position, 1.0); +}