From f32d8588e07b2e605a4fddd558d5ee0293854227 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Sat, 26 Jun 2021 22:03:55 +0100 Subject: [PATCH] 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. --- worldeditadditions/init.lua | 1 + worldeditadditions/utils/mesh.lua | 99 +++++++++++++++++++ worldeditadditions/utils/tables/init.lua | 1 + .../utils/tables/table_contains.lua | 18 ++++ worldeditadditions/utils/vector3.lua | 1 + 5 files changed, 120 insertions(+) create mode 100644 worldeditadditions/utils/mesh.lua create mode 100644 worldeditadditions/utils/tables/table_contains.lua diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 85df12c..4a1e1cb 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -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") diff --git a/worldeditadditions/utils/mesh.lua b/worldeditadditions/utils/mesh.lua new file mode 100644 index 0000000..e496f8f --- /dev/null +++ b/worldeditadditions/utils/mesh.lua @@ -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 diff --git a/worldeditadditions/utils/tables/init.lua b/worldeditadditions/utils/tables/init.lua index 2536672..2ccf93b 100644 --- a/worldeditadditions/utils/tables/init.lua +++ b/worldeditadditions/utils/tables/init.lua @@ -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") diff --git a/worldeditadditions/utils/tables/table_contains.lua b/worldeditadditions/utils/tables/table_contains.lua new file mode 100644 index 0000000..c65fdfe --- /dev/null +++ b/worldeditadditions/utils/tables/table_contains.lua @@ -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 diff --git a/worldeditadditions/utils/vector3.lua b/worldeditadditions/utils/vector3.lua index cfa2440..095c3d8 100644 --- a/worldeditadditions/utils/vector3.lua +++ b/worldeditadditions/utils/vector3.lua @@ -1,3 +1,4 @@ +--- A 3-dimensional vector. local Vector3 = {} Vector3.__index = Vector3