From ee3b19aa4f0ec65c159a31d589bbafd4a644610e Mon Sep 17 00:00:00 2001
From: sanine <sanine.not@pm.me>
Date: Wed, 29 Mar 2023 00:46:16 -0500
Subject: re-implement recursive transform system

---
 main.lua | 150 +++++++++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 113 insertions(+), 37 deletions(-)

diff --git a/main.lua b/main.lua
index f13a1a6..38556ca 100644
--- a/main.lua
+++ b/main.lua
@@ -20,51 +20,74 @@ 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, -3}),
+-- setup ecs
+local edb = ecs.EntityDb()
+local sdb = ecs.SystemDb()
+
+local camera = edb:createEntity()
+edb:addComponent(camera, "camera", {
 	projection=Mat4():perspective(math.rad(45), 640/480, 0.1, 100),
+})
+edb:addComponent(camera, "transform", {
+	matrix=Mat4():identity():translate(Vec3{0, 0, -6}),
+})
+
+
+sdb:addSystem{
+	update = function(self, dt)
+		local transform = edb:getComponent(camera, "transform")
+		transform.matrix:translate(Vec3{0, 0, -0.2*math.sin(math.pi * glfw.GetTime())})
+	end
 }
 
 
+sdb:addSystem{
+	update = function(self, dt)
+		local entities = self.db:queryComponent("transform")
 
--- 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)
+		-- prepare transforms
+		for id, transform in pairs(entities) do
+			transform._visited = false
 		end
-	end
-end)
 
--- resize window correctly
-window:setFramebufferSizeCallback(function(_, width, height)
-	gl.Viewport(0, 0, width, height)
-	camera.projection:perspectiveResize(width/height)
-	vw, vh = width, height
-end)
-
--- averager (for fps)
-function averager(memory)
-	local buf = {}
-	local avg = 0
-	for i=1,memory do table.insert(buf, 0) end
-	return function(value)
-		table.insert(buf, value)
-		local val = table.remove(buf, 1)
-		avg = avg + value - val
-		return avg / memory
-	end
-end
-local fpsAverage = averager(200)
+		-- helper function
+		local function recursiveTransform(transform)
+			if transform._visited then
+				return transform._matrix
+			end
 
+			if not transform.parent then
+				transform._matrix = transform.matrix
+			else
+				local parentTransform = self.db:getComponent(transform.parent, "transform")
+				local parentMatrix = recursiveTransform(parentTransform)
+				transform._matrix = parentMatrix * transform.matrix
+			end
+			transform._visited = true
+			return transform._matrix
+		end
 
--- setup ecs
-local edb = ecs.EntityDb()
-local sdb = ecs.SystemDb()
+		-- compute transforms
+		for id, transform in pairs(entities) do
+			recursiveTransform(transform)
+		end
+	end,
+	db = edb,
+	priority = 0,
+}
 
 sdb:addSystem{
+	camera = camera,
 	update = function(self, dt)
+		local cameraParams = self.db:getComponent(camera, "camera")
+		local cameraTransform = self.db:getComponent(camera, "transform")
+		local view
+		if cameraTransform then
+			view = cameraTransform._matrix
+		else
+			view = Mat4():identity()
+		end
+
 		local entities = self.db:queryComponent("renderMesh")
 		for entity, tbl in pairs(entities) do
 			-- get shader
@@ -73,7 +96,7 @@ sdb:addSystem{
 
 			-- bind textures
 			local texOffset = 0
-			for name, texTbl in pairs(tbl.textures) do
+			for name, texTbl in pairs(tbl.textures or {}) do
 				local texture = honey.image.loadImage(texTbl.filename, texTbl.params)
 				gl.BindTexture(gl.TEXTURE_2D + texOffset, texture.texture)
 				shader:setInt(name, texOffset)
@@ -81,14 +104,16 @@ sdb:addSystem{
 			end
 
 			-- configure default uniforms
+			local query = self.db:getComponent(entity, "transform")
+			local model = (query and query._matrix) or Mat4():identity()
 			shader:configure{
 				float={
 					time=glfw.GetTime(),
 				},
 				matrix={
-					view=camera.view,
-					projection=camera.projection,
-					model=Mat4():identity(),
+					view=view,
+					projection=cameraParams.projection,
+					model=model,
 				},
 			}
 
@@ -115,7 +140,58 @@ edb:addComponent(id, "renderMesh", {
 	shader = { vertex="vertex.glsl", fragment="fragment.glsl" },
 	mesh = { filename="assets/icosahedron.obj", index=1 },
 })
+edb:addComponent(id, "transform", {
+	matrix = Mat4():identity():rotateZ(math.rad(45)),
+})
+sdb:addSystem{
+	update = function(self, dt)
+		local transform = edb:getComponent(id, "transform")
+		transform.matrix:rotateZ(math.pi * dt)
+	end,
+}
 
+local id2 = edb:createEntity()
+edb:addComponent(id2, "renderMesh", {
+	shader = { vertex="vertex.glsl", fragment="fragment.glsl" },
+	mesh = { filename="assets/tetrahedron.obj", index=1 },
+})
+edb:addComponent(id2, "transform", {
+	parent=id,
+	matrix=Mat4():identity():translate(Vec3{0, 2, 0}),
+})
+
+
+
+-- 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)
+		end
+	end
+end)
+
+-- resize window correctly
+window:setFramebufferSizeCallback(function(_, width, height)
+	gl.Viewport(0, 0, width, height)
+	local cameraParams = edb:getComponent(camera, "camera")
+	cameraParams.projection:perspectiveResize(width/height)
+	vw, vh = width, height
+end)
+
+-- averager (for fps)
+function averager(memory)
+	local buf = {}
+	local avg = 0
+	for i=1,memory do table.insert(buf, 0) end
+	return function(value)
+		table.insert(buf, value)
+		local val = table.remove(buf, 1)
+		avg = avg + value - val
+		return avg / memory
+	end
+end
+local fpsAverage = averager(200)
 
 
 -- main loop
-- 
cgit v1.2.1