diff --git a/zchunk.lua b/zchunk.lua index a4daec5..5943c55 100644 --- a/zchunk.lua +++ b/zchunk.lua @@ -4,7 +4,7 @@ local zprite = require("zprite.zprite") ---@field private _chunks {integer: {z: zprite, x: integer, y: integer}} ---@field private _chunk_size integer ---@field private _chunk_height integer ----@field private _chunk_wrap integer +---@field private _chunk_wrap integer? ---@field private _texture love.Image ---@field private _height_scale number local zchunk = {} @@ -96,7 +96,7 @@ function zchunk:put(x, y, quad, layer_count, color_map, base_layer) local index = self:_chunk_index(x, y) if not self._chunks[index] then self._chunks[index] = { - z = zprite.new(self._texture, self._chunk_size * self._chunk_size * self._chunk_height), + z = zprite.new(self._texture, self._chunk_size * self._chunk_size), } self._chunks[index].z._height_scale = self._height_scale end @@ -105,34 +105,19 @@ function zchunk:put(x, y, quad, layer_count, color_map, base_layer) self._chunks[index].y = math.floor(y / self._chunk_size) end -local transform = love.math.newTransform() -local function sort_fn(a, b) - local _, ay_rotated = transform:transformPoint(a.x, a.y) - local _, by_rotated = transform:transformPoint(b.x, b.y) - - return ay_rotated < by_rotated -end - --- Draw to the screen / current canvas ---@overload fun(self, x: number, y: number, r: number?, sx: number?, sy: number?, ox: number?, oy: number?, kx: number?, ky: number?) ---@overload fun(self, transform: love.Transform) -function zchunk:draw(x, y, r, ...) - local chunks = {} - for _, chunk in pairs(self._chunks) do - table.insert(chunks, chunk) - end +function zchunk:draw(...) + zprite._set_shader(self._height_scale) - transform:reset() - if type(x) ~= "number" then - transform:apply(x) - else - transform:rotate(r and r or 0) - end - - table.sort(chunks, sort_fn) - - for _, chunk in ipairs(chunks) do - chunk.z:draw(x, y, r, ...) + for i = 1, self._chunk_height do + for _, chunk in pairs(self._chunks) do + chunk.z:_upload() + if chunk.z._instances[i] then + love.graphics.drawInstanced(chunk.z._meshes[i], #chunk.z._instances[i] / 3, ...) + end + end end end diff --git a/zprite.lua b/zprite.lua index 594d3ea..3c0171e 100644 --- a/zprite.lua +++ b/zprite.lua @@ -1,13 +1,12 @@ ---@class zprite ---@field private _texture love.Image ---@field private _instance_vertex_count integer ----@field private _mesh love.Mesh ----@field private _instance_buffer love.Buffer +---@field private _meshes love.Mesh[] +---@field private _instance_buffers love.Buffer[] ---@field private _instances {integer: {}[]} ---@field private _top_layer integer ---@field private _height_scale number ---@field private _changed boolean ----@field private _count integer local zprite = {} zprite.__index = zprite @@ -74,44 +73,26 @@ function zprite.new(texture, max_instances) error("max_instances must be at least 1") end - local tw, th = texture:getPixelDimensions() - local vertices = { - { 0, 0, 0, 0 }, - { 1, 0, 1 / tw, 0 }, - { 0, 1, 0, 1 / th }, - { 1, 1, 1 / tw, 1 / th }, - } - local obj = setmetatable({}, zprite) obj._texture = texture - obj._instance_vertex_count = max_instances or 16384 - obj._mesh = love.graphics.newMesh({ - { name = "VertexPosition", format = "floatvec2", location = 0 }, - { name = "VertexTexCoord", format = "floatvec2", location = 1 }, - }, vertices, "strip", "static") - obj._mesh:setTexture(texture) - obj._instance_buffer = love.graphics.newBuffer({ - { name = "InstanceData", format = "uint32vec3", location = 3 }, - }, obj._instance_vertex_count, { vertex = true }) + obj._instance_vertex_count = max_instances or 1024 + obj._meshes = {} obj._instances = {} + obj._instance_buffers = {} obj._height_scale = 4 obj._changed = false - obj._mesh:attachAttribute(3, obj._instance_buffer, "perinstance") - obj._top_layer = 0 - obj._count = 0 return obj end --- Clear all drawn objects function zprite:clear() - -- no need to set _changed to true, as #self._instances will be 0 + self._changed = true self._instances = {} self._top_layer = 0 - self._count = 0 end --- Add an 'entity' to be rendered @@ -183,8 +164,9 @@ function zprite:put(x, y, quad, layer_count, color_map, base_layer) local uv_scale = bit.bor(uv, scale) - table.insert(self._instances[i], { pos, uv_scale, layer_rgb }) - self._count = self._count + 1 + table.insert(self._instances[i], pos) + table.insert(self._instances[i], uv_scale) + table.insert(self._instances[i], layer_rgb) end end @@ -192,21 +174,49 @@ end ---@overload fun(self, x: number, y: number, r: number?, sx: number?, sy: number?, ox: number?, oy: number?, kx: number?, ky: number?) ---@overload fun(self, transform: love.Transform) function zprite:draw(...) - love.graphics.setShader(ZPRITE_SHADER) - ZPRITE_SHADER:send("HeightScale", self._height_scale) + zprite._set_shader(self._height_scale) + self:_upload() + + for i = 1, self._top_layer do + if self._instances[i] then + love.graphics.drawInstanced(self._meshes[i], #self._instances[i] / 3, ...) + end + end +end + +function zprite._set_shader(height_scale) + love.graphics.setShader(ZPRITE_SHADER) + ZPRITE_SHADER:send("HeightScale", height_scale) +end + +function zprite:_upload() if self._changed then - local current = 1 for i = 1, self._top_layer do if self._instances[i] then - self._instance_buffer:setArrayData(self._instances[i], 1, current, #self._instances[i]) - current = current + #self._instances[i] + if not self._instance_buffers[i] then + local tw, th = self._texture:getPixelDimensions() + local vertices = { + { 0, 0, 0, 0 }, + { 1, 0, 1 / tw, 0 }, + { 0, 1, 0, 1 / th }, + { 1, 1, 1 / tw, 1 / th }, + } + self._meshes[i] = love.graphics.newMesh({ + { name = "VertexPosition", format = "floatvec2", location = 0 }, + { name = "VertexTexCoord", format = "floatvec2", location = 1 }, + }, vertices, "strip", "static") + self._meshes[i]:setTexture(self._texture) + self._instance_buffers[i] = love.graphics.newBuffer({ + { name = "InstanceData", format = "uint32vec3", location = 3 }, + }, self._instance_vertex_count, { vertex = true }) + self._meshes[i]:attachAttribute(3, self._instance_buffers[i], "perinstance") + end + self._instance_buffers[i]:setArrayData(self._instances[i], 1, 1, #self._instances[i] / 3) end end self._changed = false end - - love.graphics.drawInstanced(self._mesh, self._count, ...) end return zprite