123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- Xform = {}
- Xform.__index = Xform
- -- TODO
- -- [x] optional constructor args, usually want to set pos, maybe rot
- -- [x] values should be copied into persistant properties
- -- [ ] ensure that `xform.position.x += 1` triggers an update
- -- [x] allow user data
- -- [x] fns to add and remove children from parent
- -- [ ] prevent DAG cycle in parent/child
- -- [ ] correct mat4 calc (need to do parent.world * child.local??)
- -- [ ] ensure children vector is loop/remove safe
- -- [ ] parenting xform should preserve it's world space in the new
- -- parent local
- -- [ ] stub out all transform fns from Unity
- -- [ ] write test suite?
- local function index_of(list, o)
- for i,v in pairs(list) do
- if v == o then
- return i
- end
- end
- end
- function remove(list, o)
- table.remove(list, index_of(list, o))
- return list
- end
- local function append(list, o)
- list[#list + 1] = o
- end
- local function copy_v3(s, t)
- t.x = s.x
- t.y = s.y
- t.z = s.z
- end
- local function copy_quat(s, t)
- t.x = s.x
- t.y = s.y
- t.z = s.z
- t.w = s.w
- end
- local function is_xform(o)
- return o['__members'] and o.__members.type == "Xform"
- end
- local function update(xform)
- -- compose local mat4
- xform.mat4:set(xform.position, xform.scale, xform.rotation)
- if xform.parent then
- -- parent world * child local
- xform.mat4:mul(xform.parent.mat4)
- end
- for i, child in ipairs(xform.children) do
- update(child)
- end
- end
- local function mount(child, parent)
- assert(parent == nil or is_xform(parent), "parent must be an Xform")
- assert(child ~= parent, "parent can't be self")
- if child['parent'] == parent then return end
- if child['parent'] and child.parent ~= parent then
- remove(child.parent.children, child)
- end
- child.__members.parent = parent
- if parent then
- append(parent.children, child)
- end
- update(child)
- end
- function Xform:new(position, scale, rotation)
- local o = {
- __members = {
- type = "Xform",
- mat4 = lovr.math.newMat4(),
- parent = nil,
- children = {},
- -- TODO these need to be copied over
- position = position or lovr.math.newVec3(),
- scale = scale or lovr.math.newVec3(1),
- rotation = rotation or lovr.math.newQuat()
- }
- }
- setmetatable(o, Xform)
- update(o)
- return o
- end
- function Xform:__newindex(index, value)
- if index == "position" or index == "scale" then
- copy_v3(value, self.__members[index])
- update(self)
- elseif index == "rotation" then
- copy_quat(value, self.__members[index])
- update(self)
- elseif index == "parent" then
- mount(self, value)
- elseif index == "children" or
- index == "mat4" then
- elseif index ~= "__members" then
- rawset(self, index, value)
- end
- end
- function Xform:__index(index)
- return self.__members[index]
- end
- return Xform
|