add obj loader

This commit is contained in:
Lera Elvoé 2025-02-21 02:03:50 +03:00
parent 82da17287e
commit db4809d385
Signed by: yagich
SSH Key Fingerprint: SHA256:6xjGb6uA7lAVcULa7byPEN//rQ0wPoG+UzYVMfZnbvc
4 changed files with 177 additions and 5 deletions

BIN
data/images/measure002a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

38
data/models/unit_cube.obj Normal file
View File

@ -0,0 +1,38 @@
# Blender 4.0.2
# www.blender.org
o Cube
v -0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 -0.500000 0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 -0.500000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/4/2 4/3/2 8/5/2 7/6/2
f 7/6/3 8/5/3 6/7/3 5/8/3
f 5/8/4 6/7/4 2/9/4 1/10/4
f 3/11/5 7/6/5 5/8/5 1/12/5
f 8/5/6 4/13/6 2/14/6 6/7/6

View File

@ -0,0 +1,125 @@
---@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

View File

@ -3,6 +3,10 @@ local player = require "classes.player"
local Vector3 = require "types.vector3" local Vector3 = require "types.vector3"
local List = require "types.list" local List = require "types.list"
local Obj = require "classes.obj"
local cube = Obj.create("models/unit_cube.obj", {file = "images/measure002a.png", size = 512}, Vector3(0, 1, 0))
local Feed = require "classes.feed" local Feed = require "classes.feed"
---@type List ---@type List
local feed = List() local feed = List()
@ -41,10 +45,6 @@ function game_tick()
-- ctx.initialization_needed is true first frame and every time dynamic reload is performed -- ctx.initialization_needed is true first frame and every time dynamic reload is performed
if ctx.initialization_needed then if ctx.initialization_needed then
audio_play{audio = "music/bg1.xm", loops = true, channel = "music"} audio_play{audio = "music/bg1.xm", loops = true, channel = "music"}
-- ctx.udata persists on reload
ctx.udata = {
capture = false,
}
player.ThrowPressed:connect(create_feed) player.ThrowPressed:connect(create_feed)
-- spawn some ducks -- spawn some ducks
@ -55,7 +55,13 @@ function game_tick()
duck.SeekFeed:connect(duck_seek_feed) duck.SeekFeed:connect(duck_seek_feed)
duck.index = i duck.index = i
end end
end
-- ctx.udata persists on reload
if ctx.udata == nil then
ctx.udata = {
capture = false,
}
end end
ctx.mouse_capture = ctx.udata.capture ctx.mouse_capture = ctx.udata.capture
@ -80,4 +86,7 @@ function game_tick()
texture_region = { x = 0, y = 0, w = 512, h = 512 }, texture_region = { x = 0, y = 0, w = 512, h = 512 },
} }
draw_quad(util.merge(q, params)) draw_quad(util.merge(q, params))
cube.position.x = math.sin(ctx.frame_number * 0.01)
cube.position.z = math.cos(ctx.frame_number * 0.01)
cube:draw()
end end