mirror of
https://github.com/Reuh/wirefame.git
synced 2026-03-12 23:31:11 +00:00
Rewrite
This commit is contained in:
parent
b4798c8c16
commit
77cfbaad52
24 changed files with 3033 additions and 1545 deletions
170
wirefame/group.lua
Normal file
170
wirefame/group.lua
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
local wirefame = require((...):match("^(.*)%.wirefame%.group$"))
|
||||
local model_mt = require((...):match("^(.*)group$").."model")
|
||||
local m4, v3, bb3, bs3 = wirefame.m4, wirefame.v3, wirefame.bb3, wirefame.bs3
|
||||
local unpack = table.unpack or unpack
|
||||
|
||||
--- Group methods.
|
||||
local group_mt = {
|
||||
--- Add a model to the node.
|
||||
add = function(self, ...)
|
||||
assert(not self.filtered, "can't add to a filtered group")
|
||||
for _, m in ipairs({...}) do
|
||||
self.n = self.n + 1
|
||||
table.insert(self, m)
|
||||
if m.parent == nil then
|
||||
m.parent = self
|
||||
if self.scene then
|
||||
m:onAddToScene(self.scene)
|
||||
end
|
||||
m:setTransform(0, self:updateFinalTransform())
|
||||
end
|
||||
end
|
||||
return self
|
||||
end,
|
||||
|
||||
--- Remove a model from the node.
|
||||
remove = function(self, ...)
|
||||
assert(not self.filtered, "can't remove from a filtered group")
|
||||
for _, m in ipairs({...}) do
|
||||
for i=1, self.n do
|
||||
if self[i] == m then
|
||||
self.n = self.n - 1
|
||||
table.remove(self, i)
|
||||
m.parent = nil
|
||||
if m.scene then
|
||||
m:onRemoveFromScene(m.scene)
|
||||
end
|
||||
return self
|
||||
end
|
||||
end
|
||||
end
|
||||
error("can't find model to remove")
|
||||
end,
|
||||
|
||||
--- All these methods and properties of model are present, but operate on the whole group.
|
||||
|
||||
-- Overwrite some model methods:
|
||||
onAddToScene = function(self, scene)
|
||||
self.scene = scene
|
||||
for i=1, self.n do
|
||||
self[i]:onAddToScene(scene)
|
||||
end
|
||||
end,
|
||||
onRemoveFromScene = function(self, scene)
|
||||
self.scene = nil
|
||||
for i=1, self.n do
|
||||
self[i]:onRemoveFromScene(scene)
|
||||
end
|
||||
end,
|
||||
get = function(self, ...)
|
||||
local l = {}
|
||||
for i=1, self.n do
|
||||
local il = self[i]:get(...)
|
||||
if #il > 0 then
|
||||
table.insert(l, il)
|
||||
end
|
||||
end
|
||||
l.n = #l
|
||||
l.filtered = true
|
||||
return setmetatable(l, {__index = self, __tostring = self.__tostring})
|
||||
end,
|
||||
boundingBox = function(self)
|
||||
if #self > 0 then
|
||||
self:updateFinalTransform()
|
||||
local r = self[1]:boundingBox()
|
||||
for i=2, self.n do
|
||||
r:include(self[i]:boundingBox())
|
||||
end
|
||||
return r
|
||||
else
|
||||
return bb3(v3(0,0,0), v3(0,0,0))
|
||||
end
|
||||
end,
|
||||
boundingSphere = function(self)
|
||||
if #self > 0 then
|
||||
self:updateFinalTransform()
|
||||
local r = self[1]:boundingSphere()
|
||||
for i=2, self.n do
|
||||
r:include(self[i]:boundingSphere())
|
||||
end
|
||||
return r
|
||||
else
|
||||
return bs3(v3(0,0,0), 0)
|
||||
end
|
||||
end,
|
||||
initShader = function(self, shader)
|
||||
for i=1, self.n do
|
||||
self[i]:initShader(shader)
|
||||
end
|
||||
end,
|
||||
sendFinalTransform = function(self, tr)
|
||||
if self.filtered then self = getmetatable(self).__index end
|
||||
for i=1, self.n do
|
||||
self[i]:setTransform(0, tr)
|
||||
end
|
||||
end,
|
||||
setColor = function(self, r, g, b, a) -- apply color to all children
|
||||
self.color[1], self.color[2], self.color[3], self.color[4] = r, g, b, a or 1
|
||||
for i=1, self.n do
|
||||
self[i]:setColor(r, g, b, a)
|
||||
end
|
||||
return self
|
||||
end,
|
||||
draw = function(self, shader, transp)
|
||||
-- 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
|
||||
self:updateFinalTransform()
|
||||
|
||||
-- Draw
|
||||
for i=1, self.n do
|
||||
self[i]:draw(shader, transp)
|
||||
end
|
||||
end,
|
||||
|
||||
__tostring = function(self)
|
||||
local mt = getmetatable(self)
|
||||
setmetatable(self, nil)
|
||||
local str = "group: "..(tostring(self):gsub("^table: ", ""))
|
||||
setmetatable(self, mt)
|
||||
return str
|
||||
end
|
||||
}
|
||||
for k, v in pairs(model_mt) do
|
||||
if group_mt[k] == nil then
|
||||
group_mt[k] = v
|
||||
end
|
||||
end
|
||||
group_mt.__index = group_mt
|
||||
|
||||
--- Create a group of models.
|
||||
function wirefame.group(t)
|
||||
local group = {
|
||||
-- Size
|
||||
n = 0,
|
||||
|
||||
-- Tags
|
||||
tags = {},
|
||||
|
||||
-- Group transformation matrices
|
||||
transformStack = { n = 1, changed = true, names = {}, [0] = m4.identity(), m4.identity() },
|
||||
finalTransform = nil,
|
||||
|
||||
-- Group color
|
||||
color = { wirefame.defaultColor[1], wirefame.defaultColor[2], wirefame.defaultColor[3], wirefame.defaultColor[4] },
|
||||
|
||||
-- Hierarchy
|
||||
parent = nil,
|
||||
scene = nil
|
||||
}
|
||||
|
||||
-- Create & return object
|
||||
return setmetatable(group, group_mt):add(unpack(t))
|
||||
end
|
||||
|
||||
return group_mt
|
||||
117
wirefame/light.lua
Normal file
117
wirefame/light.lua
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
local wirefame = require((...):match("^(.*)%.wirefame%.light$"))
|
||||
local model_mt = require((...):match("^(.*)light$").."model")
|
||||
local m4, v3 = wirefame.m4, wirefame.v3
|
||||
|
||||
--- Light methods.
|
||||
local lightType = {
|
||||
none = 0,
|
||||
ambient = 1,
|
||||
directional = 2,
|
||||
point = 3
|
||||
}
|
||||
local light_mt = {
|
||||
-- Rewrite model methods
|
||||
initShader = function(self, shader)
|
||||
shader:send("lights["..(self.index-1).."].type", self.type)
|
||||
shader:send("lights["..(self.index-1).."].position", self.position)
|
||||
shader:send("lights["..(self.index-1).."].color", self.color)
|
||||
end,
|
||||
onAddToScene = function(self, scene)
|
||||
self.scene = scene
|
||||
-- create array if needed
|
||||
if not scene.shader.define.LIGHT_COUNT then
|
||||
scene.shader.define.LIGHT_COUNT = 0
|
||||
scene.shader.lights = {}
|
||||
end
|
||||
-- find free spot
|
||||
self.index = nil
|
||||
for i=1, scene.shader.define.LIGHT_COUNT, 1 do
|
||||
if not scene.shader.lights[i] then
|
||||
self.index = i
|
||||
self:initShader() -- update already existing array in shader
|
||||
break
|
||||
end
|
||||
end
|
||||
if not self.index then -- create new spot
|
||||
scene.shader.define.LIGHT_COUNT = scene.shader.define.LIGHT_COUNT + 1
|
||||
self.index = scene.shader.define.LIGHT_COUNT
|
||||
scene.shader.changed = true
|
||||
end
|
||||
-- register oneself
|
||||
scene.shader.lights[self.index] = true
|
||||
end,
|
||||
onRemoveFromScene = function(self, scene)
|
||||
self.scene = nil
|
||||
-- unregister oneself
|
||||
scene.shader.lights[self.index] = nil
|
||||
-- recalculate minimal array size
|
||||
for i=scene.shader.define.LIGHT_COUNT, 1, -1 do
|
||||
if scene.shader.lights[i] then
|
||||
break
|
||||
else
|
||||
scene.shader.define.LIGHT_COUNT = scene.shader.define.LIGHT_COUNT - 1 -- will update on next recompile, nothing is urgent
|
||||
end
|
||||
end
|
||||
-- update oneself in shader
|
||||
scene.shader.shader:send("lights["..(self.index-1).."].type", lightType.none)
|
||||
end,
|
||||
sendFinalTransform = function(self, tr)
|
||||
self.position = v3(0,0,0):transform(tr)
|
||||
end,
|
||||
draw = function(self, shader, transp)
|
||||
-- Draw
|
||||
if shader:hasUniform("lights["..(self.index-1).."].position") then
|
||||
self:updateFinalTransform()
|
||||
shader:send("lights["..(self.index-1).."].position", self.position)
|
||||
shader:send("lights["..(self.index-1).."].color", self.color)
|
||||
end
|
||||
end,
|
||||
__tostring = function(self)
|
||||
local mt = getmetatable(self)
|
||||
setmetatable(self, nil)
|
||||
local str = "light: "..(tostring(self):gsub("^table: ", ""))
|
||||
setmetatable(self, mt)
|
||||
return str
|
||||
end
|
||||
}
|
||||
for k, v in pairs(model_mt) do
|
||||
if light_mt[k] == nil then
|
||||
light_mt[k] = v
|
||||
end
|
||||
end
|
||||
light_mt.__index = light_mt
|
||||
|
||||
--- Create a light object
|
||||
function wirefame.light(type)
|
||||
local light = {
|
||||
-- Light
|
||||
type = lightType[type],
|
||||
position = v3(0, 0, 0),
|
||||
|
||||
-- Color
|
||||
color = { wirefame.defaultColor[1], wirefame.defaultColor[2], wirefame.defaultColor[3], wirefame.defaultColor[4] },
|
||||
|
||||
-- Size
|
||||
n = 1,
|
||||
true,
|
||||
|
||||
-- Tags
|
||||
tags = {},
|
||||
|
||||
-- Group transformation matrices
|
||||
transformStack = { n = 1, changed = true, names = {}, [0] = m4.identity(), m4.identity() },
|
||||
finalTransform = nil,
|
||||
|
||||
-- Empty model object
|
||||
object = {},
|
||||
|
||||
-- Hierarchy
|
||||
parent = nil,
|
||||
scene = nil
|
||||
}
|
||||
|
||||
-- Create & return object
|
||||
return setmetatable(light, light_mt)
|
||||
end
|
||||
|
||||
return light_mt
|
||||
375
wirefame/model.lua
Normal file
375
wirefame/model.lua
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
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
|
||||
189
wirefame/scene.lua
Normal file
189
wirefame/scene.lua
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
local wirefame = require((...):match("^(.*)%.wirefame%.scene$"))
|
||||
local group_mt = require((...):match("^(.*)scene$").."group")
|
||||
local m4, v3 = wirefame.m4, wirefame.v3
|
||||
local lg = love.graphics
|
||||
|
||||
-- Global shaders
|
||||
local pickShader = lg.newShader(wirefame.shader.pick)
|
||||
|
||||
--- Scene methods
|
||||
local scene_mt = {
|
||||
--- Set the projection matrix.
|
||||
setPerspective = function(self, fovy, ratio, near, far)
|
||||
return self:setTransform(3, m4.perspective(fovy, ratio, near, far))
|
||||
end,
|
||||
|
||||
--- Sets the view and projection matrix.
|
||||
lookAt = function(self, eye, center, up)
|
||||
self.camera = v3(eye)
|
||||
return self:setTransform(2, m4.lookAt(eye, center, up))
|
||||
end,
|
||||
|
||||
--- Rebuild the shader
|
||||
rebuildShader = function(self)
|
||||
if self.shader.changed then
|
||||
if self.shader.shader then self.shader.shader:release() end
|
||||
local s = ""
|
||||
for var, val in pairs(self.shader.define) do
|
||||
s = s .. ("# define %s %s"):format(var, val) .. "\n"
|
||||
end
|
||||
s = s .. wirefame.shader.render
|
||||
self.shader.shader = lg.newShader(s)
|
||||
self.shader.shader:send("scene_transform", m4.identity())
|
||||
self.shader.shader:send("model_transform", m4.identity())
|
||||
for _, model in ipairs(self) do model:initShader(self.shader.shader) end
|
||||
self.shader.changed = false
|
||||
end
|
||||
return self.shader.shader
|
||||
end,
|
||||
|
||||
--- Draw the scene.
|
||||
draw = function(self)
|
||||
-- Init shader
|
||||
local shader = self:rebuildShader()
|
||||
|
||||
-- Calculate transformation matrix
|
||||
shader:send("scene_transform", self:updateFinalTransform())
|
||||
|
||||
-- Draw models
|
||||
lg.setShader(shader)
|
||||
for _, model in ipairs(self) do
|
||||
model:draw(shader, false) -- draw opaque models
|
||||
end
|
||||
for _, model in ipairs(self) do
|
||||
model:draw(shader, true) -- draw transparent models
|
||||
end
|
||||
lg.setShader()
|
||||
end,
|
||||
|
||||
--- Returns the object visible at x, y on the screen.
|
||||
pick = function(self, x, y)
|
||||
local canvas = lg.newCanvas()
|
||||
|
||||
-- Calculate transformation matrix
|
||||
pickShader:send("scene_transform", m4.scale{1, -1, 1} * self:updateFinalTransform())
|
||||
|
||||
-- Draw models
|
||||
lg.setCanvas{canvas, depth = true}
|
||||
lg.setShader(pickShader)
|
||||
local r, g, b = 0, 0, 0
|
||||
for _, model in ipairs(self) do
|
||||
if r < 255 then
|
||||
r = r+1
|
||||
elseif g < 255 then
|
||||
g = g+1
|
||||
elseif b < 255 then
|
||||
b = b+1
|
||||
else
|
||||
error("too many object to pick from")
|
||||
end
|
||||
pickShader:send("pick_color", {r/255, g/255, b/255})
|
||||
model:draw(pickShader, false)
|
||||
end
|
||||
r, g, b = 0, 0, 0
|
||||
for _, model in ipairs(self) do
|
||||
if r < 255 then
|
||||
r = r+1
|
||||
elseif g < 255 then
|
||||
g = g+1
|
||||
elseif b < 255 then
|
||||
b = b+1
|
||||
end
|
||||
pickShader:send("pick_color", {r/255, g/255, b/255})
|
||||
model:draw(pickShader, true)
|
||||
end
|
||||
lg.setShader()
|
||||
love.graphics.setCanvas()
|
||||
|
||||
-- Pick object
|
||||
r, g, b = canvas:newImageData():getPixel(x, y)
|
||||
local i = math.floor(r*255) + math.floor(g*255)*255 + math.floor(b*255)*255*255
|
||||
return self[i]
|
||||
end,
|
||||
|
||||
-- Redefine some group methods (main change is not passing scene transform to children)
|
||||
add = function(self, ...)
|
||||
for _, m in ipairs({...}) do
|
||||
self.n = self.n + 1
|
||||
table.insert(self, m)
|
||||
if m.parent == nil then
|
||||
m.parent = self
|
||||
if self.scene then
|
||||
m:onAddToScene(self.scene)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end,
|
||||
sendFinalTransform = function(self, tr)
|
||||
-- scene final transform is applied in shader
|
||||
end,
|
||||
__tostring = function(self)
|
||||
local mt = getmetatable(self)
|
||||
setmetatable(self, nil)
|
||||
local str = "scene: "..(tostring(self):gsub("^table: ", ""))
|
||||
setmetatable(self, mt)
|
||||
return str
|
||||
end
|
||||
|
||||
-- And every group method
|
||||
}
|
||||
for k, v in pairs(group_mt) do
|
||||
if scene_mt[k] == nil then
|
||||
scene_mt[k] = v
|
||||
end
|
||||
end
|
||||
scene_mt.__index = scene_mt
|
||||
|
||||
--- Create a scene.
|
||||
wirefame.scene = function()
|
||||
local scene = {
|
||||
-- Other scene variables
|
||||
camera = v3(0, 0, 0), -- camera position
|
||||
shader = {
|
||||
changed = true, -- rebuild shader
|
||||
define = {}, -- map of variables to define in the shader
|
||||
shader = nil, -- the shader
|
||||
lights = {} -- list of used lights slots, see light_mt
|
||||
},
|
||||
|
||||
-- Size
|
||||
n = 0,
|
||||
|
||||
-- Tags
|
||||
tags = {},
|
||||
|
||||
-- Scene transformation matrices
|
||||
transformStack = {
|
||||
n = 4,
|
||||
changed = true,
|
||||
names = {},
|
||||
[0] = m4.identity(),
|
||||
m4.identity(), -- Custom: user-defined transformation to the models world coordinates in the scene (excluding camera)
|
||||
m4.identity(), -- View: world coordinates -> camera coordinates
|
||||
m4.identity(), -- Projection: camera coordinates -> perspective camera coordinates
|
||||
m4.identity(), -- Viewport: perspective camera coordinates -> screen coordinates
|
||||
},
|
||||
finalTransform = nil,
|
||||
|
||||
-- Group color
|
||||
color = { wirefame.defaultColor[1], wirefame.defaultColor[2], wirefame.defaultColor[3], wirefame.defaultColor[4] },
|
||||
|
||||
-- Hierarchy
|
||||
parent = nil,
|
||||
scene = nil
|
||||
}
|
||||
scene.scene = scene
|
||||
|
||||
-- enable depth buffer (lequal instead of less in order to allow regular 2D drawing (constant depth value))
|
||||
lg.setDepthMode("lequal", true)
|
||||
|
||||
-- enable back face culling
|
||||
lg.setFrontFaceWinding("ccw")
|
||||
lg.setMeshCullMode("back")
|
||||
|
||||
-- Create & return object
|
||||
return setmetatable(scene, scene_mt)
|
||||
end
|
||||
|
||||
return scene_mt
|
||||
Loading…
Add table
Add a link
Reference in a new issue