quack2/data/scripts/classes/obj.lua
2025-02-21 02:03:50 +03:00

125 lines
2.9 KiB
Lua

---@class Obj
---@field model string
---@field texture string
---@field texture_size integer
---@field position Vector3
---@field triangles table
local Obj = {}
local Vector3 = require "types.vector3"
Obj.__index = Obj
local function string_split(str, delimiter)
local res = {}
local i = 0
local f
local match = '(.-)' .. delimiter .. '()'
if string.find(str, delimiter) == nil then
return {str}
end
for sub, j in string.gmatch(str, match) do
i = i + 1
res[i] = sub
f = j
end
if i ~= 0 then
res[i+1] = string.sub(str, f)
end
return res
end
---parses the obj file and triangulates it for drawing
---@param model string path to obj model
---@param texture {file: string, size: number}
---@param position Vector3?
---@return Obj
function Obj.create(model, texture, position)
local pos
if position ~= nil then
pos = position
else
pos = Vector3()
end
local obj = {
model = model,
texture = texture.file,
texture_size = texture.size,
position = pos:copy(),
triangles = {}
}
obj.position = obj.position:copy()
setmetatable(obj, Obj)
-- adapted from https://github.com/karai17/lua-obj/blob/master/obj_loader.lua
-- Copyright (c) 2014 Landon Manning - LManning17@gmail.com - LandonManning.com
-- MIT
local file = file_read{file = obj.model}
local lines = string_split(file, "\r?\n")
local obj_data = {
v = {},
vt = {},
f = {},
}
for _, line in ipairs(lines) do
local l = string_split(line, "%s+")
if l[1] == "v" then
local v = {
x = tonumber(l[2]),
y = tonumber(l[3]),
z = tonumber(l[4]),
}
table.insert(obj_data.v, v)
elseif l[1] == "vt" then
local vt = {
x = tonumber(l[2]) * obj.texture_size,
y = tonumber(l[3]) * obj.texture_size,
}
table.insert(obj_data.vt, vt)
elseif l[1] == "f" then
local f = {}
for i = 2, #l do
local split = string_split(l[i], "/")
local v = {}
v.v = tonumber(split[1])
if split[2] ~= "" then v.vt = tonumber(split[2]) end
-- v.vn = tonumber(split[3])
table.insert(f, v)
end
table.insert(obj_data.f, f)
end
end
for _, face in ipairs(obj_data.f) do
for i = 2, #face - 1 do
local triangle = {
v0 = obj_data.v[face[1].v],
v1 = obj_data.v[face[i].v],
v2 = obj_data.v[face[i + 1].v],
uv0 = obj_data.vt[face[1].vt],
uv1 = obj_data.vt[face[i].vt],
uv2 = obj_data.vt[face[i + 1].vt],
texture = obj.texture
}
table.insert(obj.triangles, triangle)
end
end
return obj
end
function Obj:draw()
for _, triangle in ipairs(self.triangles) do
local newt = util.shallow_copy(triangle)
newt.v0 = Vector3(triangle.v0) + self.position
newt.v1 = Vector3(triangle.v1) + self.position
newt.v2 = Vector3(triangle.v2) + self.position
draw_triangle(newt)
end
end
return Obj