Add Mesh & Face classes for #59

Together, these classes provide a way to represent a mesh of faces
generated from the Minetest world. This way, the generation of a mesh
can be abstracted away from any potential output file writers, thereby
allowing for multiple different output file formats to be supported.
This commit is contained in:
Starbeamrainbowlabs 2021-06-26 22:03:55 +01:00
parent dbdf509d79
commit f32d8588e0
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
5 changed files with 120 additions and 0 deletions

View file

@ -9,6 +9,7 @@ worldeditadditions = {}
worldeditadditions.modpath = minetest.get_modpath("worldeditadditions")
dofile(worldeditadditions.modpath.."/utils/vector.lua")
dofile(worldeditadditions.modpath.."/utils/vector3.lua")
dofile(worldeditadditions.modpath.."/utils/mesh.lua")
dofile(worldeditadditions.modpath.."/utils/strings/init.lua")
dofile(worldeditadditions.modpath.."/utils/format/init.lua")

View file

@ -0,0 +1,99 @@
local wea = worldeditadditions
-- ███████ █████ ██████ ███████
-- ██ ██ ██ ██ ██
-- █████ ███████ ██ █████
-- ██ ██ ██ ██ ██
-- ██ ██ ██ ██████ ███████
--- A single face of a Mesh.
local Face = {}
Face.__index = Face
--- Creates a new face from a list of vertices.
-- The list of vertices should be anti-clockwise.
-- @param vertices Vector3[] A list of Vector3 vertices that define the face.
function Face.new(vertices)
local result = { vertices = vertices }
setmetatable(result, Face)
return result
end
--- Determines whether this face is equal to another face or not.
-- @param a Face The first face to compare.
-- @param b Face The second face to compare.
-- @returns bool Whether the 2 faces are equal or not.
function Face.equal(a, b)
if #a.vertices ~= #b.vertices then return false end
for i,vertex in ipairs(a) do
if vertex ~= b.vertices[i] then return false end
end
return true
end
function Face.__eq(a, b) return Face.equal(a, b) end
-- ███ ███ ███████ ███████ ██ ██
-- ████ ████ ██ ██ ██ ██
-- ██ ████ ██ █████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ███████ ██ ██
--- A mesh of faces.
local Mesh = {}
Mesh.__index = Mesh
--- Creates a new empty mesh object container.
-- @returns Mesh
function Mesh.new()
local result = { faces = {} }
setmetatable(result, Mesh)
end
--- Adds a face to this mesh.
-- @param self Mesh The mesh instance to operate on.
-- @param face Face The face to add.
-- @returns void
function Mesh.add_face(self, face)
table.insert(self.faces, face)
end
--- Deduplicate the list of faces in this Mesh.
-- Removes all faces that are exactly equal to one another. This reduces the
-- filesize.
-- @returns number The number of faces removed.
function Mesh.dedupe(self)
-- Find the faces to remove
local toremove = {}
for i,face_check in ipairs(self.faces) do
for j,face_next in ipairs(self.faces) do
if i ~= j -- If we're not comparing a face to itself...
and face_check == face_next -- ....and the 2 faces are equal....
and not wea.table_contains(toremove, j) then -- ...and we haven't already marked it for removal...
-- Mark it for removal
table.insert(toremove, j)
end
end
end
-- Sort the list of indexes marked for removal from largest to smallest
-- This way, removing smaller items doesn't alter the index of larger ones
table.sort(toremove, function(a, b) return a > b end)
-- Remove the faces marked for removal
for i, remove_index in ipairs(toremove) do
table.remove(self.faces, remove_index)
end
return #toremove
end
if worldeditadditions then
worldeditadditions.Face = Face
worldeditadditions.Mesh = Mesh
else
return {
Face = Face,
Mesh = Mesh
}
end

View file

@ -20,3 +20,4 @@ dofile(worldeditadditions.modpath.."/utils/tables/table_map.lua")
dofile(worldeditadditions.modpath.."/utils/tables/table_tostring.lua")
dofile(worldeditadditions.modpath.."/utils/tables/table_unique.lua")
dofile(worldeditadditions.modpath.."/utils/tables/table_unpack.lua")
dofile(worldeditadditions.modpath.."/utils/tables/table_contains.lua")

View file

@ -0,0 +1,18 @@
--- Looks to see whether a given table contains a given value.
-- @param tbl table The table to look in.
-- @param target any The target to look for.
-- @returns bool Whether the table contains the given target or not.
local function table_contains(tbl, target)
for key, value in ipairs(tbl) do
if value == target then return true end
end
return false
end
if worldeditadditions then
worldeditadditions.table_contains = table_contains
else
return table_contains
end

View file

@ -1,3 +1,4 @@
--- A 3-dimensional vector.
local Vector3 = {}
Vector3.__index = Vector3