From 45dbe47d17303050cbea7c2c51e838acfe21c2fb Mon Sep 17 00:00:00 2001
From: sanine-a <sanine.not@pm.me>
Date: Tue, 28 Mar 2023 16:35:22 -0500
Subject: add cached mesh loading

---
 honey.bak/ecs-systems.lua |  76 ++++++++++
 honey.bak/ecs.lua         | 234 ++++++++++++++++++++++++++++++
 honey.bak/ecs.test.lua    |  51 +++++++
 honey.bak/glm.lua         | 361 ++++++++++++++++++++++++++++++++++++++++++++++
 honey.bak/init.lua        |  48 ++++++
 honey.bak/mesh.lua        |  95 ++++++++++++
 honey.bak/shader.lua      | 118 +++++++++++++++
 honey.bak/std.lua         |  17 +++
 honey.bak/window.lua      | 160 ++++++++++++++++++++
 honey/ecs.lua             |  93 +++++++++++-
 honey/ecs.test.lua        |  48 ++++++
 honey/mesh.lua            |  17 ++-
 honey/std.lua             |   2 +-
 main.lua                  | 213 +++++----------------------
 14 files changed, 1344 insertions(+), 189 deletions(-)
 create mode 100644 honey.bak/ecs-systems.lua
 create mode 100644 honey.bak/ecs.lua
 create mode 100644 honey.bak/ecs.test.lua
 create mode 100644 honey.bak/glm.lua
 create mode 100644 honey.bak/init.lua
 create mode 100644 honey.bak/mesh.lua
 create mode 100644 honey.bak/shader.lua
 create mode 100644 honey.bak/std.lua
 create mode 100644 honey.bak/window.lua

diff --git a/honey.bak/ecs-systems.lua b/honey.bak/ecs-systems.lua
new file mode 100644
index 0000000..5dec159
--- /dev/null
+++ b/honey.bak/ecs-systems.lua
@@ -0,0 +1,76 @@
+local ecs = require 'honey.ecs'
+
+
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+
+--===== transform cascading =====--
+
+local function recursiveComputeTransform(entity)
+	if entity._transformComputed then 
+		return entity._transform 
+	end
+	if entity.parent == false then
+		entity._transformComputed = true
+		entity._transform = entity.transform
+		return entity.transform
+	end
+
+	entity._transformComputed = true
+	local parentTransform = recursiveComputeTransform(entity.parent)
+	entity._transform = parentTransform * entity.transform
+	return entity._transform
+end
+
+-- update transforms
+transformCascade = {
+	filter=ecs.Filter.AND{"transform", "parent"},
+	prepareEntity=function(self, entity)
+		entity._transform = nil
+		entity._transformComputed = false
+	end,
+	update=function(self, entity, dt)
+		recursiveComputeTransform(entity)
+	end,
+	priority=98,
+}
+
+
+--===== rendering =====--
+
+function renderCam(camera, priority)
+	local priority = priority or 99
+	return {
+		filter=ecs.Filter.AND{"mesh", "shader", "transform"},
+		update=function(self, entity, dt)
+			entity.shader:use()
+			entity.shader:configure{
+				matrix={
+					model=entity._transform,
+					view=camera.view,
+					projection=camera.projection,
+				},
+			}
+			entity.mesh:drawElements()
+		end,
+		nopause=true,
+		priority=priority,
+	}
+end
+
+--===== update functions =====--
+
+update = {
+	filter=ecs.Filter.AND{"update"},
+	update=function(self, entity, dt)
+		entity.update(entity, dt)
+	end,
+	priority=50,
+}
+
+
+
+return module
diff --git a/honey.bak/ecs.lua b/honey.bak/ecs.lua
new file mode 100644
index 0000000..0672efe
--- /dev/null
+++ b/honey.bak/ecs.lua
@@ -0,0 +1,234 @@
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+--===== filters =====--
+
+
+Filter = {}
+
+-- base filter creation
+function Filter.new(_, operation, tbl)
+	local self = {}
+	self.keys = {}
+	self.filters = {}
+	self.op = operation
+
+	for _, v in ipairs(tbl) do
+		if type(v) == "function" then
+			table.insert(self.filters, v)
+		elseif type(v) == "string" then
+			table.insert(self.keys, v)
+		end
+	end
+
+	return function(entity, _self) 
+		local entity = _self or entity -- able to call as . or :
+		return Filter.check(self, entity)
+	end
+end
+setmetatable(Filter, {__call=Filter.new})
+
+
+-- base filter checking
+function Filter.check(self, entity)
+	local funcname = "check" .. self.op
+	return Filter[funcname](self, entity)
+end
+
+
+-- AND filter (all keys and subfilters must match)
+function Filter.AND(tbl)
+	return Filter("AND", tbl)
+end
+function Filter.checkAND(self, entity)
+	for _, subfilter in ipairs(self.filters) do
+		if not subfilter(entity) then return false end
+	end
+	for _, key in ipairs(self.keys) do
+		if entity[key] == nil then return false end
+	end
+	return true
+end
+
+
+-- OR filter (at least one key or subfilter must match)
+function Filter.OR(tbl)
+	return Filter("OR", tbl)
+end
+function Filter.checkOR(self, entity)
+	for _, subfilter in ipairs(self.filters) do
+		if subfilter(entity) then return true end
+	end
+	for _, key in ipairs(self.keys) do
+		if entity[key] ~= nil then return true end
+	end
+	return false
+end
+
+
+-- NAND filter (at least one key or subfilter must NOT match)
+function Filter.NAND(tbl)
+	return Filter("NAND", tbl)
+end
+function Filter.checkNAND(self, entity)
+	for _, subfilter in ipairs(self.filters) do
+		if not subfilter(entity) then return true end
+	end
+	for _, key in ipairs(self.keys) do
+		if entity[key] == nil then return true end
+	end
+	return false
+end
+
+
+-- NOR filter (no keys or subfilters may match)
+function Filter.NOR(tbl)
+	return Filter("NOR", tbl)
+end
+function Filter.checkNOR(self, entity)
+	for _, subfilter in ipairs(self.filters) do
+		if subfilter(entity) then return false end
+	end
+	for _, key in ipairs(self.keys) do
+		if entity[key] ~= nil then return false end
+	end
+	return true
+end
+
+
+--===== levels =====--
+
+Level = {}
+Level.__index = Level
+
+function Level.new(_)
+	local self = {}
+	self.systems = {}
+	self.entities = {}
+	self.nextId = 1
+	setmetatable(self, Level)
+	return self
+end
+setmetatable(Level, {__call=Level.new})
+
+
+function Level.getEntity(self, id)
+	return self.entities[id]
+end
+
+
+local function systemLt(a, b)
+	return (a.priority or 50) < (b.priority or 50)
+end
+function Level.addSystem(self, system)
+	assert(system.update, "systems must have an 'update' key")
+	assert(system.filter, "systems must have a 'filter' key")
+	system.entities = {}
+	system.level = self
+	table.insert(self.systems, system)
+	table.sort(self.systems, systemLt)
+	if system.setup then
+		system.setup(system)
+	end
+end
+
+
+local function addEntityToSystem(system, id, entity)
+	-- check if entity is already present
+	if system.entities[id] then return end
+
+	if system.onAddEntity then
+		system:onAddEntity(id, entity)
+	end
+	system.entities[id] = true
+end
+
+
+local function removeEntityFromSystem(system, id, entity)
+	-- check if entity is already absent
+	if not system.entities[id] then return end
+
+	if system.onRemoveEntity then
+		system:onRemoveEntity(id, entity)
+	end
+	system.entities[id] = nil
+end
+
+
+function Level.addEntity(self, entity)
+	local id = self.nextId
+	self.entities[id] = entity
+	self.nextId = id + 1
+
+	for _, system in ipairs(self.systems) do
+		if system.filter(entity) then
+			addEntityToSystem(system, id, entity)
+		end
+	end
+
+	return id
+end
+
+
+function Level.reconfigureEntity(self, id)
+	local entity = self.entities[id]
+	
+	for _, system in ipairs(self.systems) do
+		if system.filter(entity) then
+			addEntityToSystem(system, id, entity)
+		else
+			removeEntityFromSystem(system, id, entity)
+		end
+	end
+end
+
+
+function Level.removeEntity(self, id)
+	local entity = self.entities[id]
+	if not entity then error("bad id: "..tostring(id)) end
+	for _, system in ipairs(self.systems) do
+		removeEntityFromSystem(system, id, entity)
+	end
+	self.entities[id] = nil
+end
+
+
+function Level.reconfigureAllEntities(self)
+	for id in ipairs(self.entities) do
+		self:reconfigureEntity(id)
+	end
+end
+
+
+function Level.update(self, dt, paused)
+	local paused = paused or false
+	for _, system in ipairs(self.systems) do
+		if (not paused) or (paused and system.nopause) then
+			if system.preUpdate then
+				system:preUpdate()
+			end
+			if system.prepareEntity then
+				for id in pairs(system.entities) do
+					local entity = self.entities[id]
+					if entity then
+						system:prepareEntity(entity)
+					end
+				end
+			end
+			for id in pairs(system.entities) do
+				local entity = self.entities[id]
+				if entity then
+					system:update(entity, dt)
+				end
+			end
+			if system.postUpdate then
+				system:postUpdate()
+			end
+		end
+	end
+end
+
+
+return module
diff --git a/honey.bak/ecs.test.lua b/honey.bak/ecs.test.lua
new file mode 100644
index 0000000..7814097
--- /dev/null
+++ b/honey.bak/ecs.test.lua
@@ -0,0 +1,51 @@
+local function test(msg, f)
+	local success, error = pcall(f)
+	if success then
+		print(msg .. "\t\t[OK]")
+	else
+		print(msg .. "\t\t[FAIL]")
+		print(error)
+	end
+end
+
+
+local ecs = require 'ecs'
+local Filter = ecs.Filter
+
+
+test("Filter.AND correctly matches basic keys", function()
+	local filter = Filter.AND{"hello", "world"}
+
+	assert(filter{hello=true} == false)
+	assert(filter{world=true} == false)
+	assert(filter{hello=true, world=true} == true)
+	assert(filter{asm=true, hello=true, world=true} == true)
+end)
+test("Filter.AND correctly matches subfilters", function()
+	local subfilter = Filter.AND{"hello"}
+	local filter = Filter.AND{subfilter, "world"}
+
+	assert(filter{hello=true} == false)
+	assert(filter{world=true} == false)
+	assert(filter{hello=true, world=true} == true)
+	assert(filter{asm=true, hello=true, world=true} == true)
+end)
+
+
+test("Filter.OR correctly matches basic keys", function()
+	local filter = Filter.OR{"hello", "world"}
+
+	assert(filter{hello=true} == true)
+	assert(filter{world=true} == true)
+	assert(filter{hello=true, world=true} == true)
+	assert(filter{asm=true} == false)
+end)
+test("Filter.OR correctly matches subfilters", function()
+	local subfilter = Filter.OR{"hello"}
+	local filter = Filter.OR{subfilter, "world"}
+
+	assert(filter{hello=true} == true)
+	assert(filter{world=true} == true)
+	assert(filter{hello=true, world=true} == true)
+	assert(filter{asm=true} == false)
+end)
diff --git a/honey.bak/glm.lua b/honey.bak/glm.lua
new file mode 100644
index 0000000..af8dcb3
--- /dev/null
+++ b/honey.bak/glm.lua
@@ -0,0 +1,361 @@
+local glm = honey.glm
+
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+Vec3 = {}
+Mat4 = {}
+Quaternion = {}
+
+
+--===== Vec3 =====--
+
+
+function Vec3.new(_, values)
+	local self = {}
+	self.data = glm.vec3_create()
+	setmetatable(self, Vec3)
+	if values then
+		self[1] = values[1]
+		self[2] = values[2]
+		self[3] = values[3]
+	end
+	return self
+end
+setmetatable(Vec3, {__call=Vec3.new})
+
+
+function Vec3.__index(self, key)
+	if type(key) == 'number' then
+		return glm.vec3_get(self.data, key-1)
+	else
+		return Vec3[key]
+	end
+end
+
+
+function Vec3.__newindex(self, key, value)
+	glm.vec3_set(self.data, key-1, value)
+end
+
+
+function Vec3.__tostring(self)
+	return string.format("Vec3[%.4f, %.4f, %.4f]", self[1], self[2], self[3])
+end
+
+
+--===== arithmetic =====--
+
+local function swapIfNumber(self, other)
+	if type(self) == "number" and type(other) == "table" then
+		return other, self
+	else
+		return self, other
+	end
+end
+
+
+function Vec3.__add(self, other)
+	local self, other = swapIfNumber(self, other)
+
+	local dest = Vec3()
+	if type(other) == "number" then
+		glm.vec3_adds(self.data, other, dest.data)
+	elseif type(other) == "table" then
+		glm.vec3_add(self.data, other.data, dest.data)
+	else
+		error(string.format("cannot add %s to Vec3", type(other)))
+	end
+	return dest
+end
+
+
+function Vec3.__sub(self, other)
+	local dest = Vec3()
+	if type(other) == "number" then
+		glm.vec3_subs(self.data, other, dest.data)
+	elseif type(other) == "table" then
+		glm.vec3_sub(self.data, other.data, dest.data)
+	else
+		error(string.format("cannot subtract %s from Vec3", type(other)))
+	end
+	return dest
+end
+
+
+function Vec3.__mul(self, other)
+	local self, other = swapIfNumber(self, other)
+	local dest = Vec3()
+	if type(other) == "number" then
+		glm.vec3_scale(self.data, other, dest.data)
+	elseif type(other) == "table" then
+		glm.vec3_mul(self.data, other.data, dest.data)
+	else
+		error(string.format("cannot multiply %s and Vec3", type(other)))
+	end
+	return dest
+end
+
+
+function Vec3.__div(self, other)
+	local dest = Vec3()
+	if type(other) == "number" then
+		glm.vec3_divs(self.data, other, dest.data)
+	elseif type(other) == "table" then
+		glm.vec3_div(self.data, other.data, dest.data)
+	else
+		error(string.format("cannot divide Vec3 by %s", type(other)))
+	end
+	return dest
+end
+
+
+
+
+function Vec3.copyTo(self, dest)
+	glm.vec3_copy(self.data, dest.data)
+end
+
+
+function Vec3.zero(self)
+	glm.vec3_zero(self.data)
+end
+
+
+function Vec3.zero(self)
+	glm.vec3_zero(self.data)
+end
+function Vec3.one(self)
+	glm.vec3_one(self.data)
+end
+
+
+function Vec3.dot(self, other)
+	return glm.vec3_dot(self.data, other.data)
+end
+
+
+function Vec3.crossTo(self, other, dest)
+	glm.vec3_cross(self.data, other.data, dest.data)
+end
+function Vec3.cross(self, other)
+	local dest = Vec3()
+	self:crossTo(other, dest)
+	return dest
+end
+
+
+function Vec3.crossnTo(self, other, dest)
+	glm.vec3_crossn(self.data, other.data, dest.data)
+end
+function Vec3.crossn(self, other)
+	local dest = Vec3()
+	self:crossTo(other, dest)
+	return dest
+end
+
+
+function Vec3.norm2(self)
+	return glm.vec3_norm2(self.data)
+end
+function Vec3.norm(self)
+	return glm.vec3_norm(self.data)
+end
+
+
+function Vec3.normalize(self)
+	glm.vec3_normalize(self.data)
+end
+function Vec3.normalizeTo(self, dest)
+	glm.vec3_normalize_to(self.data, dest.data)
+end
+
+
+----------------------------------------
+
+local RowLookup = {}
+function RowLookup.new(_, row, data)
+	local self = {
+		row=row,
+		data=data,
+	}
+	setmetatable(self, RowLookup)
+	return self
+end
+setmetatable(RowLookup, {__call=RowLookup.new})
+function RowLookup.__index(self, col)
+	return glm.mat4_get(self.data, col-1, self.row-1)
+end
+function RowLookup.__newindex(self, col, value)
+	return glm.mat4_set(self.data, col-1, self.row-1, value)
+end
+
+
+--===== Mat4 =====--
+
+
+function Mat4.new(_, self, values)
+	local self = {}
+	self.type = "mat4"
+	self.data = glm.mat4_create()
+	setmetatable(self, Mat4)
+	if values then
+		self[1][1] = values[1]
+		self[1][2] = values[2]
+		self[1][3] = values[3]
+		self[1][4] = values[4]
+
+		self[2][1] = values[5]
+		self[2][2] = values[6]
+		self[2][3] = values[7]
+		self[2][4] = values[8]
+
+		self[3][1] = values[9]
+		self[3][2] = values[10]
+		self[3][3] = values[11]
+		self[3][4] = values[12]
+
+		self[4][1] = values[13]
+		self[4][2] = values[14]
+		self[4][3] = values[15]
+		self[4][4] = values[16]
+	end
+	return self
+end
+setmetatable(Mat4, {__call=Mat4.new})
+
+
+function Mat4.__index(self, key)
+	if type(key) == "number" then
+		return RowLookup(key, self.data)
+	else
+		return Mat4[key]
+	end
+end
+
+
+function Mat4.__tostring(self)
+	return string.format(
+		"/ %0.4f, %0.4f, %0.4f, %0.4f \\\n" ..
+		"| %0.4f, %0.4f, %0.4f, %0.4f |\n" ..
+		"| %0.4f, %0.4f, %0.4f, %0.4f |\n" ..
+		"\\ %0.4f, %0.4f, %0.4f, %0.4f /",
+		self[1][1], self[1][2], self[1][3], self[1][4],
+		self[2][1], self[2][2], self[2][3], self[2][4],
+		self[3][1], self[3][2], self[3][3], self[3][4],
+		self[4][1], self[4][2], self[4][3], self[4][4]
+	)
+end
+
+
+function Mat4.__mul(self, other)
+	if other.type == "mat4" then
+		local dest = Mat4()
+		glm.mat4_mul(self.data, other.data, dest.data)
+		return dest
+	elseif other.type == "vec4" then
+		-- todo
+	elseif other.type == "vec3" then
+		local dest = Vec3()
+		glm.mat4_mulv3(self.data, other.data, 1.0, dest.data)
+		return dest
+	else
+		error(string.format("cannot multiply Mat4 by %s", type(other)))
+	end
+end
+
+
+function Mat4.copy(self, other)
+	glm.mat4_copy(other.data, self.data)
+	return self
+end
+function Mat4.copyTo(self, dest)
+	glm.mat4_copy(self.data, dest.data)
+	return self
+end
+
+
+function Mat4.identity(self)
+	glm.mat4_identity(self.data)
+	return self
+end
+
+
+function Mat4.zero(self)
+	glm.mat4_zero(self.data)
+	return self
+end
+
+
+function Mat4.mul(self, other)
+	glm.mat4_mul(self.data, other.data, self.data)
+	return self
+end
+
+
+function Mat4.translate(self, vec)
+	glm.translate(self.data, vec.data)
+	return self
+end
+
+
+function Mat4.rotateX(self, angle)
+	glm.rotate_x(self.data, angle, self.data)
+	return self
+end
+function Mat4.rotateY(self, angle)
+	glm.rotate_y(self.data, angle, self.data)
+	return self
+end
+function Mat4.rotateZ(self, angle)
+	glm.rotate_z(self.data, angle, self.data)
+	return self
+end
+
+
+function Mat4.scale(self, vec)
+	glm.scale(self.data, vec.data)
+	return self
+end
+
+
+function Mat4.perspective(self, fovy, aspect, near, far)
+	glm.perspective(fovy, aspect, near, far, self.data)
+	return self
+end
+function Mat4.perspectiveResize(self, aspect)
+	glm.perspective_resize(aspect, self.data)
+	return self
+end
+
+
+--===== Quaternion =====--
+
+
+Quaternion.__index = Quaternion
+
+
+function Quaternion.new(_, tbl)
+	local tbl = tbl or { 0, 0, 0, 0 }
+	local self = {}
+	self.data = glm.quat_create()
+	glm.quat_init(self.data, unpack(tbl))
+	setmetatable(self, Quaternion)
+	return self
+end
+setmetatable(Quaternion, {__call=Quaternion.new})
+
+
+function Quaternion.toMat4(self)
+	local m = Mat4()
+	glm.quat_mat4(self.data, m.data)
+	return m
+end
+
+
+---------------------------
+
+
+return module
diff --git a/honey.bak/init.lua b/honey.bak/init.lua
new file mode 100644
index 0000000..6cf8801
--- /dev/null
+++ b/honey.bak/init.lua
@@ -0,0 +1,48 @@
+local glfw = honey.glfw
+local gl = honey.gl
+local window = require 'honey.window'
+
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+function init(width, height, title)
+	local width = width or 640
+	local height = height or 480
+	local title = title or "honey3d"
+
+	glfw.Init()
+	glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
+	glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 4)
+	glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 1)
+	local window = honey.Window(width, height, title)
+	glfw.MakeContextCurrent(window.win)
+	gl.InitGlad()
+	gl.Enable(gl.DEPTH_TEST)
+
+	honey.ode.InitODE()
+
+	return window
+end
+
+
+function loop(window, update)
+	local prevTime = 0
+	while not window:shouldClose() do
+		local time = glfw.GetTime()
+		local dt = time - prevTime
+		prevTime = time
+		update(dt)
+		window:swapBuffers()
+		glfw.PollEvents()
+	end
+end
+
+
+function terminate()
+	glfw.Terminate()
+end
+
+
+return module
diff --git a/honey.bak/mesh.lua b/honey.bak/mesh.lua
new file mode 100644
index 0000000..430d5c3
--- /dev/null
+++ b/honey.bak/mesh.lua
@@ -0,0 +1,95 @@
+local mesh = {}
+local gl = honey.gl
+setmetatable(mesh, {__index=_G})
+setfenv(1, mesh)
+
+
+local function insertVertex(vertices, attrib, vertex)
+	local pos = 3*vertex.v_idx
+	for i=1,3 do
+		table.insert(vertices, attrib.vertices[pos+i])
+	end
+
+	local normal = 3*vertex.vn_idx
+	for i=1,3 do
+		table.insert(vertices, attrib.normals[normal+i])
+	end
+
+	local tex = 3*vertex.vt_idx
+	for i=1,2 do
+		table.insert(vertices, attrib.texcoords[tex+i])
+	end
+end
+
+
+function loadShape(shape, attrib)
+	local vertices = {}
+	local indices = {}
+
+	local start = shape.face_offset
+	local finish = start + shape.length
+	for i=start,finish-1 do
+		assert(attrib.face_num_verts[i+1] == 3, "non-triangular face!")
+		for j=0,2 do
+			local vertex = attrib.faces[(3*i) + j + 1]
+			insertVertex(vertices, attrib, vertex)
+			table.insert(indices, #indices)
+		end
+	end
+
+	return vertices, indices
+end
+
+
+function loadFile(filename)
+	local flags = honey.tinyobj.FLAG_TRIANGULATE
+	local attrib, shapes, materials = honey.tinyobj.parse_obj(filename, flags)
+	
+	local meshes = {}
+	for _, shape in ipairs(shapes) do
+		local vertices, indices = loadShape(shape, attrib)
+		table.insert(meshes, Mesh(vertices, indices))
+	end
+	return meshes
+end
+
+
+Mesh = {}
+Mesh.__index = Mesh
+
+
+function Mesh.new(_, vertices, indices)
+	local self = {}
+	setmetatable(self, Mesh)
+
+	self.vertexArray = gl.GenVertexArrays()
+	self.vertexBuffer = gl.GenBuffers()
+	self.elementBuffer = gl.GenBuffers()
+	self.vertexCount = #indices
+
+	gl.BindVertexArray(self.vertexArray)
+	gl.BindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer)
+	gl.BufferData(gl.ARRAY_BUFFER, gl.FLOAT, vertices, gl.STATIC_DRAW)
+
+	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.elementBuffer)
+	gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_INT, indices, gl.STATIC_DRAW)
+
+	gl.VertexAttribPointer(0, 3, false, 8, 0)
+	gl.EnableVertexAttribArray(0)
+	gl.VertexAttribPointer(1, 3, false, 8, 3)
+	gl.EnableVertexAttribArray(1)
+	gl.VertexAttribPointer(2, 2, false, 8, 6)
+	gl.EnableVertexAttribArray(2)
+
+	return self
+end
+setmetatable(Mesh, {__call=Mesh.new})
+
+
+function Mesh.drawElements(self)
+	gl.BindVertexArray(self.vertexArray)
+	gl.DrawElements(gl.TRIANGLES, self.vertexCount, gl.UNSIGNED_INT, 0)
+end
+
+
+return mesh
diff --git a/honey.bak/shader.lua b/honey.bak/shader.lua
new file mode 100644
index 0000000..083b260
--- /dev/null
+++ b/honey.bak/shader.lua
@@ -0,0 +1,118 @@
+local gl = honey.gl
+
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+Shader = {}
+Shader.__index = Shader
+
+local function compileShader(source, type)
+	local shader = gl.CreateShader(type)
+	gl.ShaderSource(shader, source)
+	gl.CompileShader(shader)
+	return shader
+end
+
+local function readFile(filename)
+	local f, err = io.open(filename)
+	if not f then error(err) end
+	local str = f:read("*a")
+	f:close()
+	return str
+end
+
+function Shader.new(_, sources)
+	local self = {}
+	self.locations = {}
+	self.links = {}
+
+	if sources.vertexFile then
+		sources.vertex = readFile(sources.vertexFile)
+	end
+	if sources.fragmentFile then
+		sources.fragment = readFile(sources.fragmentFile)
+	end
+
+	local shaders = {}
+	if sources.vertex then
+		table.insert(shaders, compileShader(sources.vertex, gl.VERTEX_SHADER))
+	end
+	if sources.fragment then
+		table.insert(shaders, compileShader(sources.fragment, gl.FRAGMENT_SHADER))
+	end
+
+	self.program = gl.CreateProgram()
+	for _, shader in ipairs(shaders) do
+		gl.AttachShader(self.program, shader)
+	end
+	gl.LinkProgram(self.program)
+	for _, shader in ipairs(shaders) do
+		gl.DeleteShader(shader)
+	end
+
+	setmetatable(self, Shader)
+	return self
+end
+setmetatable(Shader, {__call=Shader.new})
+
+
+function Shader.getLocation(self, name)
+	if self.locations[name] then
+		return self.locations[name]
+	end
+
+	local location = gl.GetUniformLocation(self.program, name)
+	self.locations[name] = location
+	return location
+end
+
+
+function Shader.use(self)
+	gl.UseProgram(self.program)
+end
+
+
+function Shader.setInt(self, name, value)
+	local location = self:getLocation(name)
+	gl.Uniform1i(location, value)
+end
+function Shader.setFloat(self, name, value)
+	local location = self:getLocation(name)
+	gl.Uniform1f(location, value)
+end
+
+function Shader.setVec3(self, name, value)
+	local location = self:getLocation(name)
+	gl.Uniform3f(location, value[1], value[2], value[3])
+end
+function Shader.setVec4(self, name, value)
+	local location = self:getLocation(name)
+	gl.Uniform3f(location, value[1], value[2], value[3], value[4])
+end
+
+function Shader.setMatrix(self, name, matrix)
+	local location = self:getLocation(name)
+	gl.UniformMatrix4fv(location, false, matrix.data)
+end
+
+
+function Shader.configure(self, tbl)
+	local processKey = function(key, set)
+		local subtbl = tbl[key]
+		if subtbl then
+			for name, value in pairs(subtbl) do 
+				self[set](self, name, value) 
+			end
+		end
+	end
+
+	processKey("int", "setInt")
+	processKey("float", "setFloat")
+	processKey("vec3", "setVec3")
+	processKey("vec4", "setVec4")
+	processKey("matrix", "setMatrix")
+end
+
+return module.Shader
diff --git a/honey.bak/std.lua b/honey.bak/std.lua
new file mode 100644
index 0000000..a08bf35
--- /dev/null
+++ b/honey.bak/std.lua
@@ -0,0 +1,17 @@
+local init = require 'honey.init'
+
+honey.init = init.init
+honey.loop = init.loop
+honey.terminate = init.terminate
+
+honey.ecs             = require 'honey.ecs'
+honey.standardSystems = require 'honey.ecs-systems'
+honey.mesh            = require 'honey.mesh'
+honey.Shader          = require 'honey.shader'
+honey.Window          = require 'honey.window'
+
+
+local glm = require 'honey.glm'
+honey.Vec3 = glm.Vec3
+honey.Mat4 = glm.Mat4
+honey.Quaternion = glm.Quaternion
diff --git a/honey.bak/window.lua b/honey.bak/window.lua
new file mode 100644
index 0000000..fa777d0
--- /dev/null
+++ b/honey.bak/window.lua
@@ -0,0 +1,160 @@
+local module = {}
+local glfw = honey.glfw
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+Window = {}
+Window.__index = Window
+
+
+function Window.new(_, width, height, title, monitor, share)
+	local monitor = monitor or glfw.monitor_NULL
+	local share = share or glfw.window_NULL
+
+	local self = {}
+	setmetatable(self, Window)
+
+	self.win = glfw.CreateWindow(width, height, title, monitor, share)
+	self.__gc = honey.util.gc_canary(function()
+		glfw.DestroyWindow(self.win)
+	end)
+
+	return self
+end
+setmetatable(Window, {__call=Window.new})
+
+
+function Window.shouldClose(self)
+	return glfw.WindowShouldClose(self.win) == glfw.TRUE
+end
+function Window.setShouldClose(self, state)
+	glfw.SetWindowShouldClose(self.win, state)
+end
+
+
+function Window.setTitle(self, title)
+	glfw.SetWindowTitle(self.win, title)
+end
+
+
+function Window.getPos(self)
+	return glfw.GetWindowPos(self.win)
+end
+function Window.setPos(self, x, y)
+	glfw.SetWindowPos(self.win, x, y)
+end
+
+
+function Window.getSize(self)
+	return glfw.GetWindowSize(self.win)
+end
+function Window.setSizeLimits(self, minwidth, minheight, maxwidth, maxheight)
+	glfw.SetWindowSizeLimits(self.win, minwidth, minheight, maxwidth, maxheight)
+end
+function Window.setAspectRatio(self, numerator, denominator)
+	glfw.SetWindowAspectRatio(self.win, numerator, denominator)
+end
+function Window.setSize(self, width, height)
+	glfw.SetWindowSize(self.win, width, height)
+end
+
+
+function Window.getFramebufferSize(self)
+	return glfw.GetFramebufferSize(self.win)
+end
+function Window.getFrameSize(self)
+	return glfw.GetWindowFrameSize(self.win)
+end
+
+function Window.getContentScale(self)
+	return glfw.GetWindowContentScale(self.win)
+end
+
+
+function Window.getOpacity(self)
+	return glfw.GetWindowOpacity(self.win)
+end
+function Window.setOpacity(self, opacity)
+	return glfw.SetWindowOpacity(self.win, opacity)
+end
+
+
+function Window.iconify(self)
+	return glfw.IconityWindow(self.win)
+end
+function Window.restore(self)
+	return glfw.RestoreWindow(self.win)
+end
+function Window.maximize(self)
+	return glfw.MaximizeWindow(self.win)
+end
+function Window.show(self)
+	return glfw.ShowWindow(self.win)
+end
+function Window.hide(self)
+	return glfw.HideWindow(self.win)
+end
+function Window.focus(self)
+	return glfw.FocusWindow(self.win)
+end
+function Window.requestAttention(self)
+	return glfw.RequestWindowAttention(self.win)
+end
+
+
+function Window.getMonitor(self)
+	return glfw.GetWindowMonitor(self.win)
+end
+function Window.setMonitor(self, monitor, xpos, ypos, width, height, refreshRate)
+	return glfw.SetWindowMonitor(self.win, monitor, xpos, ypos, width, height, refreshRate)
+end
+
+
+function Window.getAttrib(self, attrib)
+	return glfw.GetWindowAttrib(self.win, attrib)
+end
+function Window.setAttrib(self, attrib, value)
+	return glfw.SetWindowAttrib(self.win, attrib, value)
+end
+
+
+function Window.setPositionCallback(self, cb)
+	return glfw.SetWindowPosCallback(self.win, cb)
+end
+function Window.setSizeCallback(self, cb)
+	return glfw.SetWindowSizeCallback(self.win, cb)
+end
+function Window.setCloseCallback(self, cb)
+	return glfw.SetWindowCloseCallback(self.win, cb)
+end
+function Window.setRefreshCallback(self, cb)
+	return glfw.SetWindowRefreshCallback(self.win, cb)
+end
+function Window.setFocusCallback(self, cb)
+	return glfw.SetWindowFocusCallback(self.win, cb)
+end
+function Window.setIconifyCallback(self, cb)
+	return glfw.SetWindowIconifyCallback(self.win, cb)
+end
+function Window.setMaximizeCallback(self, cb)
+	return glfw.SetWindowIconifyCallback(self.win, cb)
+end
+function Window.setFramebufferSizeCallback(self, cb)
+	return glfw.SetFramebufferSizeCallback(self.win, cb)
+end
+function Window.setContentScaleCallback(self, cb)
+	return glfw.SetContentScaleCallback(self.win, cb)
+end
+
+function Window.setKeyCallback(self, cb)
+	return glfw.SetKeyCallback(self.win, cb)
+end
+
+
+function Window.swapBuffers(self)
+	glfw.SwapBuffers(self.win)
+end
+
+
+return module.Window
diff --git a/honey/ecs.lua b/honey/ecs.lua
index 8aa5c5c..72cc41d 100644
--- a/honey/ecs.lua
+++ b/honey/ecs.lua
@@ -84,6 +84,7 @@ end
 setmetatable(EntityDb, {__call=EntityDb.new})
 
 
+-- check if a given entity id is legitimate
 function EntityDb.checkIsValid(self, id)
 	if not self.entities[id] then
 		error(string.format("invalid entity id: %s", tostring(id)))
@@ -91,22 +92,29 @@ function EntityDb.checkIsValid(self, id)
 end
 
 
-local function uid()
-	local template ='xxxx:xxxx:xxxx'
-	return string.gsub(template, 'x', function (c)
-		local v = math.random(0, 0xf)
+local random = math.random
+local function uuid()
+	local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
+	return string.gsub(template, '[xy]', function (c)
+		local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
 		return string.format('%x', v)
 	end)
 end
-function EntityDb.createEntity(self)
-	local id = uid()
+
+
+-- create a new entity
+function EntityDb.createEntity(self, id)
+	local id = id or uuid() -- allow inserting entities at preset ids for loading
 	self.entities[id] = true
 	return id
 end
 
 
+-- add a component to an entity
 function EntityDb.addComponent(self, id, name, value)
 	self:checkIsValid(id)
+	
+	-- create the relevant component table if it doesn't exist
 	if not self.components[name] then
 		self.components[name] = { count=0 }
 	end
@@ -124,6 +132,7 @@ local function shallowCopy(tbl)
 end
 
 
+-- get all entities with a given component
 function EntityDb.queryComponent(self, name)
 	local query = shallowCopy(self.components[name])
 	query.count = nil
@@ -131,6 +140,7 @@ function EntityDb.queryComponent(self, name)
 end
 
 
+-- get all components associated with an entity
 function EntityDb.queryEntity(self, id)
 	self:checkIsValid(id)
 	local query = {}
@@ -141,6 +151,14 @@ function EntityDb.queryEntity(self, id)
 end
 
 
+-- get a specific component from an entity
+function EntityDb.getComponent(self, id, name)
+	self:checkIsValid(id)
+	return self.components[name][id]
+end
+
+
+-- remove a component from an entity
 function EntityDb.removeComponent(self, id, name)
 	self:checkIsValid(id)
 	local component = self.components[name]
@@ -154,6 +172,7 @@ function EntityDb.removeComponent(self, id, name)
 end
 
 
+-- remove an entity from the db
 function EntityDb.deleteEntity(self, id)
 	self:checkIsValid(id)
 	for name in pairs(self.components) do
@@ -162,4 +181,66 @@ function EntityDb.deleteEntity(self, id)
 	self.entities[id] = nil
 end
 
+
+--===== SystemDb =====--
+
+SystemDb = {}
+SystemDb.__index = SystemDb
+
+
+function SystemDb.new(_)
+	local self = {
+		systems = {},
+		sorted = {},
+	}
+	setmetatable(self, SystemDb)
+	return self
+end
+setmetatable(SystemDb, {__call=SystemDb.new})
+
+
+local function systemId()
+	local template = "xx:xx:xx"
+	return string.gsub(template, "x", function(c)
+		return string.format("%x", random(0, 0xf))
+	end)
+end
+
+
+function SystemDb.addSystem(self, systemFunc, params)
+	local system
+	if type(systemFunc) == "table" then
+		system = systemFunc
+	else
+		system = systemFunc(params)
+	end
+
+	local id = systemId()
+	self.systems[id] = system
+	table.insert(self.sorted, id)
+	self:sort()
+	return id
+end
+
+
+function SystemDb.sort(self)
+	table.sort(self.sorted, function(a, b) return (self.systems[a].priority or 100) < (self.systems[b].priority or 100) end)
+end
+
+
+function SystemDb.update(self, dt)
+	for _, system in ipairs(self.sorted) do
+		self.systems[system]:update(dt)
+	end
+end
+
+
+function SystemDb.removeSystem(self, id)
+	self.systems[id] = nil
+	for i=#self.sorted,1,-1 do
+		if self.sorted[i] == id then table.remove(self.sorted, i) end
+	end
+end
+
+
 return module
diff --git a/honey/ecs.test.lua b/honey/ecs.test.lua
index 0577dcd..74e27dc 100644
--- a/honey/ecs.test.lua
+++ b/honey/ecs.test.lua
@@ -229,4 +229,52 @@ test("EntityDb.deleteEntity() correctly removes an entity", function()
 end)
 
 
+--===== SystemDb tests =====--
+
+local SystemDb = ecs.SystemDb
+
+test("addSystem() correctly sorts systems", function()
+	local sdb = SystemDb(nil)
+	local str = ""
+	sdb:addSystem(function () return {
+		update=function(self, dt) str = str .. "c" end,
+		priority = 3,
+	} end)
+	sdb:addSystem{
+		update=function(self, dt) str = "a" end,
+		priority = 1,
+	}
+	sdb:addSystem{
+		update=function(self, dt) str = str .. "b" end,
+		priority = 2,
+	}
+	sdb:update(0)
+	assert(str == "abc")
+end)
+
+
+test("removeSystem() correctly handles things", function()
+	local sdb = SystemDb(nil)
+	local str = ""
+	sdb:addSystem(function () return {
+		update=function(self, dt) str = str .. "c" end,
+		priority = 3,
+	} end)
+	sdb:addSystem{
+		update=function(self, dt) str = "a" end,
+		priority = 1,
+	}
+	local id = sdb:addSystem{
+		update=function(self, dt) str = str .. "b" end,
+		priority = 2,
+	}
+	sdb:update(0)
+	assert(str == "abc")
+
+	sdb:removeSystem(id)
+	sdb:update(1)
+	assert(str == "ac")
+end)
+
+
 print(string.format("ran %d tests, %d failed", testCount, failCount))
diff --git a/honey/mesh.lua b/honey/mesh.lua
index 430d5c3..e46c399 100644
--- a/honey/mesh.lua
+++ b/honey/mesh.lua
@@ -1,7 +1,7 @@
-local mesh = {}
+local module = {}
 local gl = honey.gl
-setmetatable(mesh, {__index=_G})
-setfenv(1, mesh)
+setmetatable(module, {__index=_G})
+setfenv(1, module)
 
 
 local function insertVertex(vertices, attrib, vertex)
@@ -54,6 +54,15 @@ function loadFile(filename)
 end
 
 
+local meshCache = {}
+function loadMesh(filename, index)
+	if not meshCache[filename] then
+		meshCache[filename] = loadFile(filename)
+	end
+	return meshCache[filename][index]
+end
+
+
 Mesh = {}
 Mesh.__index = Mesh
 
@@ -92,4 +101,4 @@ function Mesh.drawElements(self)
 end
 
 
-return mesh
+return module 
diff --git a/honey/std.lua b/honey/std.lua
index a08bf35..2a5d2c2 100644
--- a/honey/std.lua
+++ b/honey/std.lua
@@ -5,7 +5,7 @@ honey.loop = init.loop
 honey.terminate = init.terminate
 
 honey.ecs             = require 'honey.ecs'
-honey.standardSystems = require 'honey.ecs-systems'
+--honey.standardSystems = require 'honey.ecs-systems'
 honey.mesh            = require 'honey.mesh'
 honey.Shader          = require 'honey.shader'
 honey.Window          = require 'honey.window'
diff --git a/main.lua b/main.lua
index 2463bdd..098a65b 100644
--- a/main.lua
+++ b/main.lua
@@ -1,28 +1,4 @@
 require 'honey.std'
-local paused = true
-local balls = {}
-
-local function formatize(f)
-	return function(fmt, ...)
-		f(string.format(fmt, ...))
-	end
-end
-local printf = formatize(print)
-local log = honey.log
-log.fatal = formatize(log.fatal)
-log.error = formatize(log.error)
-log.warn = formatize(log.warn)
-log.info = formatize(log.info)
-log.debug = formatize(log.debug)
-log.trace = formatize(log.trace)
-
-log.info(
-	"honey v%d.%d.%d -- %s",
-	honey.version.major,
-	honey.version.minor,
-	honey.version.patch,
-	honey.version.string
-)
 
 local glfw = honey.glfw
 local gl = honey.gl
@@ -43,80 +19,14 @@ local vg = nvg.CreateContext()
 local november = nvg.CreateFont(vg, "November", "assets/november.regular.ttf")
 local vw, vh = 640, 480
 
+
 -- create camera matrices
 local camera = {
 	view=Mat4():identity():translate(Vec3{0, 0, -30}),
 	projection=Mat4():perspective(math.rad(45), 640/480, 0.1, 100),
 }
 
--- setup ecs
-local level = ecs.Level()
-level:addSystem(systems.transformCascade)
-level:addSystem(systems.renderCam(camera))
-level:addSystem(systems.update)
-
--- physics system
-local world = ode.WorldCreate()
-local space = ode.HashSpaceCreate(ode.Space0)
-level:addSystem{
-	setup=function(self)
-		self.time = 0
-		self.world=world
-		self.space=space
-		ode.WorldSetGravity(self.world, 0, -100, 0)
-		ode.WorldSetCFM(self.world, 1e-5)
-		self.contactgroup = ode.JointGroupCreate(200)
-		self.__gc = honey.util.gc_canary(function()
-			ode.WorldDestroy(self.world)
-			ode.SpaceDestroy(self.space)
-		end)
-	end,
-	filter = ecs.Filter.AND{"transform", "collisionShape", "physicsBody"},
-	preUpdate = function(self)
-		self.dt = nil
-		if self.time > 0.01 then
-			self.time = self.time - 0.01
-			ode.SpaceCollide(self.space, function(o1, o2)
-				local b1 = ode.GeomGetBody(o1)
-				local b2 = ode.GeomGetBody(o2)
-				local contact = ode.CreateContact{
-					surface = {
-						mode = ode.ContactBounce + ode.ContactSoftCFM,
-						mu = ode.Infinity,
-						bounce = 0.90,
-						bounce_vel = 0.1,
-						soft_cfm = 0.001,
-					},
-				}
-				local collisions = ode.Collide(o1, o2, 1)
-				if #collisions > 0 then
-					local e1 = ode.GeomGetData(o1)
-					local e2 = ode.GeomGetData(o2)
-					ode.ContactSetGeom(contact, collisions[1])
-					local joint = ode.JointCreateContact(self.world, self.contactgroup, contact)
-					ode.JointAttach(joint, b1, b2)
-				end
-			end)
-			ode.WorldQuickStep(self.world, 0.01)
-			ode.JointGroupEmpty(self.contactgroup)
-		end
-	end,
-	update = function(self, entity, dt) 
-		if not self.dt then
-			self.dt = dt
-			self.time = self.time + dt
-		end
 
-		local x, y, z = ode.BodyGetPosition(entity.physicsBody)
-		local d, a, b, c = ode.BodyGetQuaternion(entity.physicsBody)
-		entity.transform:identity():translate(Vec3{x, y, z}):mul(Quaternion{a, b, c, d}:toMat4())
-	end,
-	onRemoveEntity = function(self, id, entity)
-		print("remove", id)
-		ode.GeomDestroy(entity.collisionShape)
-		ode.BodyDestroy(entity.physicsBody)
-	end,
-}
 
 -- create shader
 local shader = honey.Shader{
@@ -132,97 +42,12 @@ local octa = honey.mesh.loadFile("assets/octahedron.obj")[1]
 local dodeca = honey.mesh.loadFile("assets/dodecahedron.obj")[1]
 local icosa  = honey.mesh.loadFile("assets/icosahedron.obj")[1]
 
--- update function for each entity
-function updateTransform(self, dt)
-	self.transform:rotateY(0.3 * math.pi * dt)
-	self.transform:rotateX(0.1 * math.pi * dt)
-end
-
--- create entities
-function growLine(prev, depth)
-	if depth == 0 then return prev end
-
-	local entity = {
-		transform=Mat4():identity():translate(Vec3{2, 0, 0}),
-		parent=false,
-		mesh=octa,
-		shader=shader,
-		update=updateTransform,
-	}
-	prev.parent = entity
-	level:addEntity(prev)
-	return growLine(entity, depth-1)
-end
-
-local leaf = {
-	transform=Mat4():identity():translate(Vec3{2, 0, 0}),
-	parent=false,
-	mesh=tetra,
-	shader=shader,
-}
-local root = growLine(leaf, 24)
-root.update = function(self, dt)
-	self.transform:rotateY(0.2 * math.pi * dt)
-end
-level:addEntity(root)
-
-local groundPlane = {
-	transform=Mat4():identity():translate(Vec3{0, -2, 0}):scale(Vec3{10, 10, 10}),
-	parent=false,
-	mesh=plane,
-	shader=shader,
-}
-level:addEntity(groundPlane)
-
-
-local function createNewBall()
-	local ball = {
-		transform=Mat4():identity(),
-		parent=false,
-		mesh=icosa,
-		shader=shader,
-		collisionShape=ode.CreateSphere(space, 1.0),
-		physicsBody=ode.BodyCreate(world),
-	}
-
-	local id = level:addEntity(ball)
-	ball.id = id
-	table.insert(balls, id)
-	if #balls > 200 then
-		level:removeEntity(table.remove(balls, 1))
-	end
-	
-	local mass = ode.MassCreate()
-	ode.MassSetSphere(mass, 1, 0.5)
-	ode.BodySetMass(ball.physicsBody, mass)
-	local function tiny() return (6 * math.random()) - 3 end
-	local x, y, z = tiny(), tiny(), tiny()
-	ode.GeomSetBody(ball.collisionShape, ball.physicsBody)
-	ode.BodySetPosition(ball.physicsBody, x, y, z)
-	ode.BodySetLinearVel(ball.physicsBody, x, y, z)
-	ode.GeomSetData(ball.collisionShape, ball)
-end
-createNewBall()
-
-ode.CreatePlane(space, 0, 1, 0, -2)
 
 -- close window on ESCAPE key
 window:setKeyCallback(function(_, key, scancode, action)
 	if action == glfw.PRESS then
 		if key == glfw.KEY_ESCAPE then
 			window:setShouldClose(true)
-		elseif key == glfw.KEY_SPACE then
-			paused = not paused
-		elseif 
-			key == glfw.KEY_Z or 
-			key == glfw.KEY_X or 
-			key == glfw.KEY_C or 
-			key == glfw.KEY_V or
-			key == glfw.KEY_B or 
-			key == glfw.KEY_N or
-			key == glfw.KEY_M
-			then
-			createNewBall()
 		end
 	end
 end)
@@ -248,15 +73,47 @@ function averager(memory)
 end
 local fpsAverage = averager(200)
 
+
+-- setup ecs
+local edb = ecs.EntityDb()
+local sdb = ecs.SystemDb()
+
+sdb:addSystem{
+	update = function(self, dt)
+		local entities = self.db:queryComponent("renderMesh")
+		for entity, tbl in pairs(entities) do
+			tbl.shader:use()
+			tbl.shader:configure{matrix={
+				view=camera.view,
+				projection=camera.projection,
+				model=Mat4():identity(),
+			}}
+			local mesh = honey.mesh.loadMesh(tbl.mesh.filename, tbl.mesh.index)
+			mesh:drawElements()
+		end
+	end,
+	db = edb,
+	priority = 99,
+}
+
+
+local id = edb:createEntity()
+edb:addComponent(id, "renderMesh", {
+	shader = shader,
+	mesh = { filename="assets/icosahedron.obj", index=1 },
+})
+
+
+
 -- main loop
 honey.loop(window, function(dt)
 	gl.ClearColor(0.2, 0.4, 1.0, 1.0)
 	gl.Clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT + gl.STENCIL_BUFFER_BIT)
 
-	gl.UseProgram(0)
 	gl.Enable(gl.DEPTH_TEST)
+	gl.Disable(gl.CULL_FACE)
 
-	level:update(dt, paused)
+	sdb:update(dt)
 	nvg.BeginFrame(vg, vw, vh, 1.0)
 	nvg.StrokeColor(vg, nvg.RGBf(1, 1, 1))
 	nvg.FontFace(vg, "November")
-- 
cgit v1.2.1