1
0
Fork 0
mirror of https://github.com/Reuh/wirefame.git synced 2026-01-18 02:23:04 +00:00
wirefame/wirefame/model.lua
2021-02-18 17:22:21 +01:00

375 lines
9.2 KiB
Lua

local wirefame = require((...):match("^(.*)%.wirefame%.model$"))
local m4, v3, bb3, bs3 = wirefame.m4, wirefame.v3, wirefame.bb3, wirefame.bs3
local unpack = table.unpack or unpack
local lg = love.graphics
-- Model methods.
local model_mt = {
--- Called when the model is added to a scene.
onAddToScene = function(self, scene)
self.scene = scene
end,
--- Called when the model is removed from a scene.
onRemoveFromScene = function(self, scene)
self.scene = nil
end,
--- Create a new group containing this entity
group = function(self)
return wirefame.group{self}
end,
-- Add tags on this model only.
tag = function(self, ...)
for _, t in ipairs({...}) do
self.tags[t] = true
end
return self
end,
-- Check tags on this model only.
is = function(self, ...)
local r = true
for _, t in ipairs({...}) do
r = r and self.tags[t]
end
return r
end,
-- List tags on this model only.
listTags = function(self)
local r = {}
for t, _ in pairs(self.tags) do
table.insert(r, t)
end
return r
end,
--- Get a new group of all models recursively contained in this object with these tags.
get = function(self, ...)
if self:is(...) then
return self
else
return wirefame.group{}
end
end,
--- Apply a transformation matrix to the model vertices coordinates.
-- If no i is given, will transform the first matrix.
transform = function(self, i, tr)
if not tr then
i, tr = 1, i
elseif type(i) == "string" then
i = self.transformStack.names[i]
end
self.transformStack[i]:transform(tr)
self.transformStack.changed = true
return self
end,
--- Retrieve the ith transformation matrix.
-- If no i is given, will retrieve the first matrix.
getTransform = function(self, i)
if not i then
i = 1
elseif type(i) == "string" then
i = self.transformStack.names[i]
end
return self.transformStack[i]
end,
--- Set the ith transformation matrix.
-- If no i is given, will set the first matrix.
setTransform = function(self, i, tr)
if not tr then
i, tr = 1, i
elseif type(i) == "string" then
i = self.transformStack.names[i]
end
self.transformStack[i] = tr
self.transformStack.changed = true
return self
end,
--- Reset the ith transformation matrix.
-- If no i is given, will reset the first matrix.
resetTransform = function(self, i)
if not i then
i = 1
end
self.transformStack[i] = m4.identity()
self.transformStack.changed = true
return self
end,
--- Set the whole transformation stack.
setTransformStack = function(self, stack)
self.transformStack = { names = self.transformStack.names, [0] = self.transformStack[0] }
for i, tr in ipairs(stack) do
self.transformStack[i] = tr
end
self.transformStack.n = #self.transformStack
self.transformStack.changed = true
return self
end,
--- Retrieve the whole transformation stack.
getTransformStack = function(self)
return self.transformStack
end,
--- Set the size of the transformation stack, initiatializing new transformations matrices if needed.
setTransformStackSize = function(self, n)
self.transformStack.n = n
for i=1, self.transformStack.n, 1 do
if self.transformStack[i] == nil then
self.transformStack[i] = m4.identity()
end
end
self.transformStack.changed = true
return self
end,
--- Assign to each transform in the transformation stack a name and resize the transformation stack to match.
setTransformNames = function(self, list)
self:setTransformStackSize(#list)
local names = {}
for i, name in ipairs(list) do
names[name] = i
end
self.transformStack.names = names
return self
end,
--- Retrieve the transformation names.
getTransformNames = function(self, list)
local names = {}
for name, i in pairs(self.transformStack.names) do
names[i] = name
end
return names
end,
--- Assign a name to the transform i.
setTransformName = function(self, i, name)
self.transformStack.names[name] = i
return self
end,
--- Calculate the final transformation matrix if needed, and send it to eventual children (using :sendFinalTransform).
-- Returns the transform.
updateFinalTransform = function(self)
if self.transformStack.changed then
self.finalTransform = m4.identity()
for i=1, self.transformStack.n, 1 do
self.finalTransform:transform(self.transformStack[i])
end
self.finalTransform:transform(self.transformStack[0])
self.transformStack.changed = false
self:sendFinalTransform(self.finalTransform)
end
return self.finalTransform
end,
--- Send the final transformation matrix to the eventual children objects.
sendFinalTransform = function(self, tr)
if self.object.setTransform then
self.object:setTransform(tr)
end
end,
--- Common tranforms
translate = function(self, i, v)
if v then
return self:transform(i, m4.translate(v))
else
return self:transform(m4.translate(i))
end
end,
scale = function(self, i, v)
if v then
return self:transform(i, m4.scale(v))
else
return self:transform(m4.scale(i))
end
end,
rotate = function(self, i, a, v)
if v then
return self:transform(i, m4.rotate(a, v))
else
return self:transform(m4.rotate(i, a))
end
end,
shear = function(self, i, vxy, vyz)
if vyz then
return self:transform(i, m4.shear(vxy, vyz))
else
return self:transform(m4.shear(i, vxy))
end
end,
--- Returns the minimum bounding box.
boundingBox = function(self)
if self.object.boundingBox then
return self.object:boundingBox(self:updateFinalTransform())
else
return bb3(v3(0,0,0), v3(0,0,0))
end
end,
--- Returns the minimum bounding sphere.
boundingSphere = function(self)
if self.object.boundingSphere then
return self.object:boundingSphere(self:updateFinalTransform())
else
return bs3(v3(0,0,0), 0)
end
end,
--- Sets the color used to draw the model.
setColor = function(self, r, g, b, a)
self.color[1], self.color[2], self.color[3], self.color[4] = r, g, b, a or 1
return self
end,
getColor = function(self)
return self.color[1], self.color[2], self.color[3], self.color[4]
end,
--- Initialize all relevant uniforms in the shader (called after shader rebuild)
initShader = function(self, shader)
end,
-- Draw the model. Shader may be either render or pick.
draw = function(self, shader, transp)
if self.object.draw then
-- Transparency sort
if transp then
if self.color[4] >= 1 then return end
else
if self.color[4] < 1 then return end
end
-- Transformation matrix (will automatically update object that need to know the matrix)
shader:send("model_transform", self:updateFinalTransform())
-- Draw
lg.setColor(self.color)
self.object:draw(shader)
end
end,
__tostring = function(self)
local mt = getmetatable(self)
setmetatable(self, nil)
local str = "model: "..(tostring(self):gsub("^table: ", ""))
setmetatable(self, mt)
return str
end
}
model_mt.__index = model_mt
--- Loads an .obj/.iqe file, or import a LÖVE drawable, or import a drawing function, or create a empty model.
function wirefame.model(object)
local model = {
-- Size
n = 1,
true,
-- Tags
tags = {},
-- Model transformation matrices. Matrix 0 is the transform inherited from parent chain.
transformStack = { n = 1, changed = true, names = {}, [0] = m4.identity(), m4.identity() },
finalTransform = nil,
-- Model color
color = { wirefame.defaultColor[1], wirefame.defaultColor[2], wirefame.defaultColor[3], wirefame.defaultColor[4] },
-- Model object
-- object:draw(shader)
-- object:setTransform(tr)
-- object:boundingBox(tr)
-- object:boundingSphere(tr)
object = nil,
-- Hierarchy
parent = nil,
scene = nil
}
-- Empty model
if object == nil then
model.object = {}
-- Load a model file
elseif type(object) == "string" then
local ext = object:match("%.(.-)$") or ""
if wirefame.loader[ext] then
model.object = wirefame.loader[ext](object)
else
error(("unknown model type %s"):format(ext))
end
-- Function
elseif type(object) == "function" then
model.object = { draw = object }
-- Convertible to LÖVE meshes.
elseif type(object) == "userdata" then
if object:type() == "Image" or object:type() == "Canvas" then
local t = lg.newMesh({
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4 },
{ "VertexNormal", "float", 3 }
},
{
{
0, 0, 0,
0, 1,
1, 1, 1, 1,
0, 0, 1
},
{
object:getWidth(), 0, 0,
1, 1,
1, 1, 1, 1,
0, 0, 1
},
{
object:getWidth(), object:getHeight(), 0,
1, 0,
1, 1, 1, 1,
0, 0, 1
},
{
0, object:getHeight(), 0,
0, 0,
1, 1, 1, 1,
0, 0, 1
}
}, "fan")
t:setVertexMap(1, 2, 3, 4)
t:setTexture(object)
model.object = {
draw = function()
lg.draw(t)
end,
boundingBox = function(self, tr)
return bb3.fromMesh(t, tr)
end,
boundingSphere = function(self, tr)
return bs3.fromMesh(t, tr)
end
}
elseif object:type() == "Mesh" then
model.object = {
draw = function()
lg.draw(object)
end,
boundingBox = function(self, tr)
return bb3.fromMesh(object, tr)
end,
boundingSphere = function(self, tr)
return bs3.fromMesh(t, tr)
end
}
else
error("unknown userdata")
end
else
model.object = object
end
-- Create & return object
return setmetatable(model, model_mt)
end
return model_mt