From 13c9711b883931845a11c591d1880ce00827e1a3 Mon Sep 17 00:00:00 2001 From: ultrakatiz <ultrakatiz@gmail.com> Date: Fri, 20 May 2022 00:11:17 +0200 Subject: [PATCH] shapes can now render with a camera --- all.lua | 1 + camera.lua | 35 +++++++++++++++++++++++++++++++++++ shape.lua | 21 +++++++++++++++------ transform.lua | 24 +++++++++++++++++++++++- 4 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 camera.lua diff --git a/all.lua b/all.lua index 3834ea8..f8b0e4f 100644 --- a/all.lua +++ b/all.lua @@ -3,3 +3,4 @@ require("kara3d.matrix") require("kara3d.quaternion") require("kara3d.transform") require("kara3d.shape") +require("kara3d.camera") diff --git a/camera.lua b/camera.lua new file mode 100644 index 0000000..d5b2108 --- /dev/null +++ b/camera.lua @@ -0,0 +1,35 @@ +require("kara3d.transform") + +-- ********** DEFINITION AND GENERIC FUNCTIONS ********** + +camera = {class = "camera"} +camera.__index = camera + +-- Creates a camera at transform t with aspect ratio a, horizontal fov f, +-- near plane distance nd and far plane distance fd +function camera.new(t, f, nd, fd) + local c = {transform = t, fov = f or 80 / 180 * math.pi, + near = nd or 1, far = fd or 1000000000} + setmetatable(c, camera) + return c +end + +-- Clones this camera +function camera:clone() + return camera.new(self.transform, self.fov, self.near, self.far) +end + +-- ********** INSTANCE FUNCTIONS ********** + +-- Returns this camera's projection * view matrix +function camera:matrix() + local s = 1 / math.tan(self.fov / 2) + local n, f = self.near, self.far + local xres, yres, _, _ = aegisub.video_size() + local m = matrix.new(4, 4, + {s, 0, 0, 0, + 0, s, 0, 0, + 0, 0, (n+f) / (n-f), 2*n*f / (n-f), + 0, 0, 1 / xres, 0}) + return m * self.transform:inv_matrix(true) +end diff --git a/shape.lua b/shape.lua index 803e4f0..22aadec 100644 --- a/shape.lua +++ b/shape.lua @@ -2,6 +2,7 @@ require("kara3d.vector") require("kara3d.matrix") require("kara3d.quaternion") require("kara3d.transform") +require("kara3d.camera") require("utils") -- ********** DEFINITION AND GENERIC FUNCTIONS ********** @@ -36,37 +37,45 @@ end -- Generates a line for each face of this shape, with the indicated tags -- The boolean cull determines if the faces facing away from the screen are -- drawn or not -function shape:draw(subs, line, tags, cull) +function shape:draw(subs, line, tags, cull, cam) local l = table.copy(line) local p = self.transform:position(true) local r = self.transform:rotation(true) - local m = self.transform:matrix(true) local c = vector.cardinal(4, 4) local layer = line.layer + local pv = cam and cam:matrix() or matrix.identity(4, 4) + local cam_inv_rot = cam and cam.transform:rotation(true):inv() or quaternion.identity() + local xres, yres, _, _ = aegisub.video_size() + + local m = pv * self.transform:matrix(true) + for i = 1, #self.faces do local face = self.faces[i] local n = quaternion.mul_vector(r, face.normal) + n = quaternion.mul_vector(cam_inv_rot, n) if not (cull and n:z() > 0) then local vs = face.vertices - local v = matrix.mul_vector(m, vs[#vs] + c) - p + local v = m * (vs[#vs] + c) + v = (1 / v:w()) * v local sum = vector.zero(4) local str = (tags and "{" .. tags .. "}" or "") .. (face.tags and "{" .. face.tags .. "}" or "") - .. "{\\an7\\pos(" .. p:x() .. ", " .. p:y() .. ")\\p1}" + .. "{\\an7\\pos(" .. (xres / 2) .. ", " .. (yres / 2) .. ")\\p1}" .. "m " .. v:x() .. " " .. v:y() .. " " for i = 1, #vs do - v = matrix.mul_vector(m, vs[i] + c) - p + v = m * (vs[i] + c) sum = sum + v + v = (1 / v:w()) * v str = str .. "l " .. v:x() .. " " .. v:y() .. " " end l.text = str -- layers must be between 0 and 9999999999 - l.layer = 500000000 + math.floor(-(p + sum):z() / #vs) + l.layer = 500000000 + math.floor((p + sum):z() / #vs) l.effect = "fx" subs.append(l) end diff --git a/transform.lua b/transform.lua index e755162..b141d8f 100644 --- a/transform.lua +++ b/transform.lua @@ -81,9 +81,31 @@ function transform:matrix(global) 0, 0, 0, 1}) local m = translation * rotation * scale - if (global and self.parent ~= nil) then + if (global and self.parent) then m = self.parent:matrix(true) * m end return m end + +-- Returns the inverse of the transformation matrix of this transform +function transform:inv_matrix(global) + local translation = matrix.new(4, 4, + {1, 0, 0, -self.pos:get(1), + 0, 1, 0, -self.pos:get(2), + 0, 0, 1, -self.pos:get(3), + 0, 0, 0, 1}) + local rotation = self.rot:normalized():inv():to_matrix() + local scale = matrix.new(4, 4, + {1/self.scl:get(1), 0, 0, 0, + 0, 1/self.scl:get(2), 0, 0, + 0, 0, 1/self.scl:get(3), 0, + 0, 0, 0, 1}) + local m = scale * rotation * translation + + if (global and self.parent) then + m = m * self.parent:inv_matrix(true) + end + + return m +end -- GitLab