updated nodes library (unfinished), fixed dependency (mtul-cpml renamed)

This commit is contained in:
FatalErr42O 2023-09-08 20:14:01 -07:00
parent 15fff69e75
commit 220d837842
4 changed files with 98 additions and 65 deletions

View File

@ -21,11 +21,16 @@ dofile(modpath.."/modlib/write_b3d.lua") --this is untested, could be very broke
if mtul.loaded_modules.cpml then
mtul.b3d_nodes = dofile(modpath.."/nodes.lua")
mtul.loaded_modules.b3d_nodes = true
mtul.b3d_nodes.loaded = true
else
mtul.b3d_nodes = {}
setmetatable(mtul.b3d_nodes, {
__index = function()
error("MTUL-CPML not present, b3d_nodes module inaccessible.")
__index = function(_, k)
if k ~= "loaded" then
error("MTUL-CPML not present, b3d_nodes module inaccessible.")
else
return false
end
end
})
end

View File

@ -2,5 +2,5 @@ name = mtul_b3d
title = MTUL b3d standalone
description = a b3d reader for minetest, cloned from modlib. Requires MTUL-core, also requires MTUL-math for bone reading implementations.
depends = mtul_core
optional_depends = mtul_math_cpml
optional_depends = mtul_cpml
author = FatalError42O, Appgurue

View File

@ -17,19 +17,18 @@ local read_int, read_single = mtul.binary.read_int, mtul.binary.read_single
--reads a model directly (based on name). Note that "node_only" abstracts chunks not necessary to finding the position/transform of a bone/node.
function mtul.b3d.read_model(modelname, node_only)
local path = mtul.media_paths[modelname]
assert(modelname, "no modelname provided")
local path = assert(mtul.media_paths[modelname], "no model found by the name "..modelname.."'")
local out
if path then
local ignored
if node_only then
ignored = {"TEXS", "BRUS", "BONE", "MESH"}
end
local stream = io.open(path, "rb")
if not stream then return end --if the file wasn't found we probably shouldnt just assert.
out = mtul.b3d.read_from_stream(stream, ignored)
assert(stream:read(1)==nil, "MTUL B3D: unknown error, EOF not reached")
stream:close()
local ignored
if node_only then
ignored = {"TEXS", "BRUS", "BONE", "MESH"}
end
local stream = io.open(path, "rb")
if not stream then return end --if the file wasn't found we probably shouldnt just assert.
out = mtul.b3d.read_from_stream(stream, ignored)
assert(stream:read(1)==nil, "MTUL B3D: unknown error, EOF not reached")
stream:close()
return out
end
@ -45,6 +44,7 @@ end
--this is ideal if you need to, say, solve for the transform of a node- instead of iterating 100s of times to get every parent node
--it's all provided for you. Note that it's from highest to lowest, where lowest of course is the current node, the last element.
--made originally by appgurueu
function mtul.b3d.read_from_stream(stream, ignore_chunks)
local left = 8
@ -283,30 +283,36 @@ function mtul.b3d.read_from_stream(stream, ignore_chunks)
-- Order is not validated; double occurrences of mutually exclusive node def are
while content() do
local elem, type = chunk()
if not ignored[type] then
if type == "MESH" then
assert(not node_type)
node_type = "mesh"
if type == "MESH" then
assert(not node_type)
node_type = "mesh"
if not ignored[type] then
node.mesh = elem
elseif type == "BONE" then
assert(not node_type)
node_type = "bone"
node.bone = elem
elseif type == "KEYS" then
mtul.tbl.append(node.keys, elem)
elseif type == "NODE" then
elem.parent = node
table.insert(node.children, elem)
elseif type == "ANIM" then
node.animation = elem
else
assert(not node_type)
node_type = "pivot"
end
elseif type == "BONE" then
assert(not node_type)
node_type = "bone"
if not ignored[type] then
node.bone = elem
end
elseif type == "KEYS" then
if not ignored[type] then
mtul.tbl.append(node.keys, elem)
end
elseif type == "NODE" then
elem.parent = node
table.insert(node.children, elem)
elseif type == "ANIM" then
if not ignored[type] then
node.animation = elem
end
else
assert(not node_type, "Appgurueu decided to not put actual messages, so I'm not sure, but your .b3d file is fscked up lol. I dont even think this assert is needed.")
node_type = "pivot"
end
end
--added because ignored nodes may unintentionally obfuscate the type of node- which could be necessary for finding bone "paths"
node.type = node_type
--added because ignored nodes may obfuscate the type of node- which could be necessary for finding bone "paths"
node.type = node_type or "pivot"
-- Ensure frames are sorted ascendingly
table.sort(node.keys, function(a, b)
assert(a.frame ~= b.frame, "duplicate frame")
@ -363,7 +369,7 @@ function mtul.b3d.read_from_stream(stream, ignore_chunks)
--luckily most of the ground work is layed out for us already.
--also, Fatal here: for the sake of my reputation (which is nonexistent), typically I wouldn't nest these functions
--because I am not a physcopath and or a german named Lars, but for the sake of consistency it has to happen. (Not that its *always* a bad idea, but unless you're baking in parameters it's sort of awful)
--because I am not a physcopath and or a german named Lars, but for the sake of consistency it has to happen. (Not that its *always* a bad idea, but unless you're baking in parameters it's sort of useless and potentially wasteful)
local copy_path = mtul.table and mtul.table.shallow_copy or function(tbl)
local new_table = {}
for i, v in pairs(tbl) do
@ -383,6 +389,7 @@ function mtul.b3d.read_from_stream(stream, ignore_chunks)
local self = chunk{BB3D = true}
self.node_paths = {}
self.excluded_chunks = ignore_chunks and table.copy(ignore_chunks) or {}
make_paths(self.node, {}, self.node_paths)
--b3d metatable unimplemented

View File

@ -8,7 +8,7 @@ function b3d_nodes.get_node_by_name(self, node_name, is_bone)
return this_node
end
end
error("MTUL-b3d, b3d_nodes: no node found by the name '"..node_name.."'")
error("MTUL-b3d, b3d_nodes: no node found by the name '"..tostring(node_name).."'")
end
--non-methods:
@ -33,7 +33,6 @@ function b3d_nodes.get_animated_local_transform(node, target_frame)
--need this so we can replace it if before doesnt exist
local frame_before_tbl = frames[key_index_before]
local frame_after_tbl = frames[key_index_before+1] --frame to interpolate will be out immediate neighbor since we know its either the frame or after the frame.
print(target_frame)
--it may still be zero, indicating that the frame before doesnt exist.
if not frame_before_tbl then
frame_before_tbl = node --set it to the node so it pulls from PRS directly as that's it's default state.
@ -56,45 +55,67 @@ function b3d_nodes.get_animated_local_transform(node, target_frame)
end
local mat4 = mtul.math.mat4
local quat = mtul.math.quat
function b3d_nodes.get_node_global_transform(node, frame)
--param 3 (outputs) is either "rotation" or "transform"- determines what's calculated. You can use this if you dont want uncessary calculations. If nil outputs both
function b3d_nodes.get_node_global_transform(node, frame, outputs)
local global_transform
local rotation
for i, current_node in pairs(node.path) do
local pos_vec, rot_vec, scl_vec = b3d_nodes.get_animated_local_transform(current_node, frame)
--rot_vec = {rot_vec[2], rot_vec[3], rot_vec[4], rot_vec[1]}
local local_transform = mat4.identity()
--translate rotation by position
local_transform = local_transform:translate(local_transform, {-pos_vec[1], pos_vec[2], pos_vec[3]})
local_transform = local_transform*(mat4.from_quaternion(quat.new(-rot_vec[1], rot_vec[2], rot_vec[3], rot_vec[4]):normalize()))
--scale the mat4.
--local_transform = local_transform:scale(local_transform, {scl_vec[1], scl_vec[2], scl_vec[3]})
--I dont really know why this works and the above doesn't, but at this point I'm done trying to figure it out...
local identity = mat4.identity()
local_transform = local_transform*identity:scale(identity, {scl_vec[1], scl_vec[2], scl_vec[3]})
--get new global trasnform with the local.
if global_transform then
global_transform=global_transform*local_transform
else
global_transform=local_transform
--find the transform
if not (outputs and outputs ~= "transform") then
--rot_vec = {rot_vec[2], rot_vec[3], rot_vec[4], rot_vec[1]}
local local_transform = mat4.identity()
local_transform = local_transform:translate(local_transform, {-pos_vec[1], pos_vec[2], pos_vec[3]})--not sure why x has to be inverted,
local_transform = local_transform*(mat4.from_quaternion(quat.new(-rot_vec[1], rot_vec[2], rot_vec[3], rot_vec[4]):normalize())) --W has to be inverted
--for some reason the scaling has to be broken up, I can't be bothered to figure out why after the time I've spent trying.
local identity = mat4.identity()
local_transform = local_transform*identity:scale(identity, {scl_vec[1], scl_vec[2], scl_vec[3]})
--get new global trasnform with the local.
if global_transform then
global_transform=global_transform*local_transform
else
global_transform=local_transform
end
end
--find the rotation
if not (outputs and outputs ~= "rotation") then
--find the rotation. Please note that modlib's code (in the b3d reader from mtul-b3d-standalone) converts xyzw to wxyz when reading b3ds
if not rotation then
rotation = quat.new(-rot_vec[1], rot_vec[2], rot_vec[3], rot_vec[4])
else
rotation = rotation*quat.new(-rot_vec[1], rot_vec[2], rot_vec[3], rot_vec[4])
end
end
end
--pos = global_transform:apply({pos[1], pos[2], pos[3], 1})
--print(dump(global_transform))
--return vector.new(pos[1], pos[2], pos[3])
return global_transform
--x needs to be inverted (as mentioned earlier.)
if global_transform then
global_transform[13] = -global_transform[13]
end
return global_transform, rotation
end
--Returns X, Y, Z. is_bone is optional, if "node" is the name of a node (and not the node table), this is used to find it.
function b3d_nodes.get_node_position(self, node, is_bone, frame)
--Returns X, Y, Z. is_bone is optional, if "node" is the name of a node (and not the node table), parameter 1 (self) and parameter 3 (is_bone) is used to find it.
function b3d_nodes.get_node_global_position(self, node, is_bone, frame)
assert(self or not type(node)=="string")
if type(node) == "string" then
node = b3d_nodes.get_node_by_name(self, node)
node = b3d_nodes.get_node_by_name(self, node, is_bone)
end
local transform = b3d_nodes.get_node_global_transform(node, frame)
local transform = b3d_nodes.get_node_global_transform(node, frame, "transform")
return transform[13], transform[14], transform[15]
end
--since it's impossible to determine the difference between rotation
--and non-uniform scaling, we have to use a different method for this.
function b3d_nodes.get_node_rotation()
--exactly like get_node_global_position, but it returns a vec3 quaternion.
function b3d_nodes.get_node_rotation(self, node, is_bone, frame)
assert(self or not type(node)=="string")
if type(node) == "string" then
node = b3d_nodes.get_node_by_name(self, node, is_bone)
end
local _, rotation = b3d_nodes.get_node_global_transform(node, frame, "rotation")
return rotation
end
return b3d_nodes