From ec23e6c5d932be104bd507b8f36e8f5f3dc63351 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:49:23 -0700 Subject: [PATCH] [glb export] Export bones. (#3087) ![image](https://github.com/open-goal/jak-project/assets/48171810/0f26e77b-af68-4450-882e-762a501bdef4) --- common/math/geometry.h | 74 +++++++ decompiler/CMakeLists.txt | 1 + decompiler/level_extractor/common_formats.h | 18 ++ .../level_extractor/extract_joint_group.cpp | 46 +++++ .../level_extractor/extract_joint_group.h | 14 ++ decompiler/level_extractor/extract_level.cpp | 16 +- decompiler/level_extractor/extract_merc.cpp | 16 +- decompiler/level_extractor/fr3_to_gltf.cpp | 184 +++++++++++++++++- decompiler/level_extractor/fr3_to_gltf.h | 8 +- decompiler/util/goal_data_reader.cpp | 12 ++ decompiler/util/goal_data_reader.h | 2 + 11 files changed, 367 insertions(+), 24 deletions(-) create mode 100644 decompiler/level_extractor/extract_joint_group.cpp create mode 100644 decompiler/level_extractor/extract_joint_group.h diff --git a/common/math/geometry.h b/common/math/geometry.h index a61988f44..0d498e21f 100644 --- a/common/math/geometry.h +++ b/common/math/geometry.h @@ -50,4 +50,78 @@ math::Vector4f bsphere_of_triangle(const Vector3f* vertices); inline bool point_in_bsphere(const Vector4f& sphere, const Vector3f& pt) { return (sphere.xyz() - pt).squared_length() <= (sphere.w() * sphere.w()); } + +template +math::Matrix affine_inverse(const math::Matrix& in) { + math::Matrix result; + + // transpose rotation + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + result(i, j) = in(j, i); + } + } + + result(3, 0) = 0; + result(3, 1) = 0; + result(3, 2) = 0; + result(3, 3) = 1; + result(0, 3) = 0; + result(1, 3) = 0; + result(2, 3) = 0; + for (int rx = 0; rx < 3; rx++) { + for (int cx = 0; cx < 3; cx++) { + result(rx, 3) -= result(rx, cx) * in(cx, 3); + } + } + + return result; +} + +template +math::Matrix inverse(const math::Matrix& m) { + math::Matrix im; + T A2323 = m(2, 2) * m(3, 3) - m(2, 3) * m(3, 2); + T A1323 = m(2, 1) * m(3, 3) - m(2, 3) * m(3, 1); + T A1223 = m(2, 1) * m(3, 2) - m(2, 2) * m(3, 1); + T A0323 = m(2, 0) * m(3, 3) - m(2, 3) * m(3, 0); + T A0223 = m(2, 0) * m(3, 2) - m(2, 2) * m(3, 0); + T A0123 = m(2, 0) * m(3, 1) - m(2, 1) * m(3, 0); + T A2313 = m(1, 2) * m(3, 3) - m(1, 3) * m(3, 2); + T A1313 = m(1, 1) * m(3, 3) - m(1, 3) * m(3, 1); + T A1213 = m(1, 1) * m(3, 2) - m(1, 2) * m(3, 1); + T A2312 = m(1, 2) * m(2, 3) - m(1, 3) * m(2, 2); + T A1312 = m(1, 1) * m(2, 3) - m(1, 3) * m(2, 1); + T A1212 = m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1); + T A0313 = m(1, 0) * m(3, 3) - m(1, 3) * m(3, 0); + T A0213 = m(1, 0) * m(3, 2) - m(1, 2) * m(3, 0); + T A0312 = m(1, 0) * m(2, 3) - m(1, 3) * m(2, 0); + T A0212 = m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0); + T A0113 = m(1, 0) * m(3, 1) - m(1, 1) * m(3, 0); + T A0112 = m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0); + + T det = m(0, 0) * (m(1, 1) * A2323 - m(1, 2) * A1323 + m(1, 3) * A1223) - + m(0, 1) * (m(1, 0) * A2323 - m(1, 2) * A0323 + m(1, 3) * A0223) + + m(0, 2) * (m(1, 0) * A1323 - m(1, 1) * A0323 + m(1, 3) * A0123) - + m(0, 3) * (m(1, 0) * A1223 - m(1, 1) * A0223 + m(1, 2) * A0123); + det = 1 / det; + + im(0, 0) = det * (m(1, 1) * A2323 - m(1, 2) * A1323 + m(1, 3) * A1223); + im(0, 1) = det * -(m(0, 1) * A2323 - m(0, 2) * A1323 + m(0, 3) * A1223); + im(0, 2) = det * (m(0, 1) * A2313 - m(0, 2) * A1313 + m(0, 3) * A1213); + im(0, 3) = det * -(m(0, 1) * A2312 - m(0, 2) * A1312 + m(0, 3) * A1212); + im(1, 0) = det * -(m(1, 0) * A2323 - m(1, 2) * A0323 + m(1, 3) * A0223); + im(1, 1) = det * (m(0, 0) * A2323 - m(0, 2) * A0323 + m(0, 3) * A0223); + im(1, 2) = det * -(m(0, 0) * A2313 - m(0, 2) * A0313 + m(0, 3) * A0213); + im(1, 3) = det * (m(0, 0) * A2312 - m(0, 2) * A0312 + m(0, 3) * A0212); + im(2, 0) = det * (m(1, 0) * A1323 - m(1, 1) * A0323 + m(1, 3) * A0123); + im(2, 1) = det * -(m(0, 0) * A1323 - m(0, 1) * A0323 + m(0, 3) * A0123); + im(2, 2) = det * (m(0, 0) * A1313 - m(0, 1) * A0313 + m(0, 3) * A0113); + im(2, 3) = det * -(m(0, 0) * A1312 - m(0, 1) * A0312 + m(0, 3) * A0112); + im(3, 0) = det * -(m(1, 0) * A1223 - m(1, 1) * A0223 + m(1, 2) * A0123); + im(3, 1) = det * (m(0, 0) * A1223 - m(0, 1) * A0223 + m(0, 2) * A0123); + im(3, 2) = det * -(m(0, 0) * A1213 - m(0, 1) * A0213 + m(0, 2) * A0113); + im(3, 3) = det * (m(0, 0) * A1212 - m(0, 1) * A0212 + m(0, 2) * A0112); + return im; +} } // namespace math \ No newline at end of file diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index accb6d249..a46781bf0 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -58,6 +58,7 @@ add_library( level_extractor/extract_actors.cpp level_extractor/extract_collide_frags.cpp level_extractor/extract_common.cpp + level_extractor/extract_joint_group.cpp level_extractor/extract_level.cpp level_extractor/extract_merc.cpp level_extractor/extract_tfrag.cpp diff --git a/decompiler/level_extractor/common_formats.h b/decompiler/level_extractor/common_formats.h index 597b3a88a..5546c0025 100644 --- a/decompiler/level_extractor/common_formats.h +++ b/decompiler/level_extractor/common_formats.h @@ -1,5 +1,6 @@ #pragma once #include "common/common_types.h" +#include "common/math/Vector.h" namespace level_tools { @@ -8,4 +9,21 @@ struct TextureRemap { u32 original_texid; u32 new_texid; }; + +struct Joint { + std::string name; + int parent_idx = -1; // -1 for magic ROOT joint. + math::Matrix4f bind_pose_T_w; +}; + +/*! + * Data extracted from art groups that is not needed for .FR3, but is potentially needed for other + * stuff (skeleton export). + */ +struct ArtData { + std::string art_group_name; + std::string art_name; + std::vector joint_group; +}; + } // namespace level_tools \ No newline at end of file diff --git a/decompiler/level_extractor/extract_joint_group.cpp b/decompiler/level_extractor/extract_joint_group.cpp new file mode 100644 index 000000000..fb185a9e1 --- /dev/null +++ b/decompiler/level_extractor/extract_joint_group.cpp @@ -0,0 +1,46 @@ +#include "extract_joint_group.h" + +#include "common/math/geometry.h" + +#include "decompiler/util/goal_data_reader.h" + +namespace decompiler { + +void extract_joint_group(const ObjectFileData& ag_data, + const DecompilerTypeSystem& dts, + GameVersion /*version*/, + std::map& out) { + auto locations = find_objects_with_type(ag_data.linked_data, "art-joint-geo"); + for (auto loc : locations) { + TypedRef ref(Ref{&ag_data.linked_data, 0, loc * 4}, dts.ts.lookup_type("art-joint-geo")); + auto name = read_string_field(ref, "name", dts, false); + auto& data = out[name]; + data.art_name = name; + data.art_group_name = ag_data.name_in_dgo; + ASSERT(data.joint_group.empty()); + const int length = read_plain_data_field(ref, "length", dts); + Ref iter = get_field_ref(ref, "data", dts); + std::map offset_to_joint; + for (int i = 0; i < length; i++) { + auto& njoint = data.joint_group.emplace_back(); + Ref joint = deref_label(iter); + joint.byte_offset -= 4; + bool inserted = offset_to_joint.insert({joint.byte_offset, i}).second; + ASSERT(inserted); + TypedRef tjoint = typed_ref_from_basic(joint, dts); + ASSERT(tjoint.type->get_name() == "joint"); + ASSERT(read_plain_data_field(tjoint, "number", dts) == i); + njoint.name = read_string_field(tjoint, "name", dts, true); + memcpy_from_plain_data((u8*)njoint.bind_pose_T_w.data(), + get_field_ref(tjoint, "bind-pose", dts), 4 * 4 * sizeof(float)); + if (get_word_kind_for_field(tjoint, "parent", dts) == LinkedWord::PTR) { + auto pjoint = deref_label(get_field_ref(tjoint, "parent", dts)); + njoint.parent_idx = offset_to_joint.at(pjoint.byte_offset - 4); + } else { + ASSERT(i == 0 || i == 1); + } + iter.byte_offset += 4; + } + } +} +} // namespace decompiler \ No newline at end of file diff --git a/decompiler/level_extractor/extract_joint_group.h b/decompiler/level_extractor/extract_joint_group.h new file mode 100644 index 000000000..da4b9d570 --- /dev/null +++ b/decompiler/level_extractor/extract_joint_group.h @@ -0,0 +1,14 @@ +#pragma once + +#include "common/custom_data/Tfrag3Data.h" + +#include "decompiler/ObjectFile/ObjectFileDB.h" +#include "decompiler/data/TextureDB.h" +#include "decompiler/level_extractor/common_formats.h" + +namespace decompiler { +void extract_joint_group(const ObjectFileData& ag_data, + const DecompilerTypeSystem& dts, + GameVersion version, + std::map& out); +} \ No newline at end of file diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 00521b57d..aa9eda114 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -12,6 +12,7 @@ #include "decompiler/level_extractor/BspHeader.h" #include "decompiler/level_extractor/extract_actors.h" #include "decompiler/level_extractor/extract_collide_frags.h" +#include "decompiler/level_extractor/extract_joint_group.h" #include "decompiler/level_extractor/extract_merc.h" #include "decompiler/level_extractor/extract_shrub.h" #include "decompiler/level_extractor/extract_tfrag.h" @@ -122,12 +123,14 @@ void extract_art_groups_from_level(const ObjectFileDB& db, const TextureDB& tex_db, const std::vector& tex_remap, const std::string& dgo_name, - tfrag3::Level& level_data) { + tfrag3::Level& level_data, + std::map& art_group_data) { const auto& files = db.obj_files_by_dgo.at(dgo_name); for (const auto& file : files) { if (file.name.length() > 3 && !file.name.compare(file.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(file); extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version()); + extract_joint_group(ag_file, db.dts, db.version(), art_group_data); } } } @@ -273,8 +276,9 @@ void extract_common(const ObjectFileDB& db, confirm_textures_identical(tex_db); tfrag3::Level tfrag_level; + std::map art_group_data; add_all_textures_from_level(tfrag_level, dgo_name, tex_db); - extract_art_groups_from_level(db, tex_db, {}, dgo_name, tfrag_level); + extract_art_groups_from_level(db, tex_db, {}, dgo_name, tfrag_level, art_group_data); std::set textures_we_have; @@ -322,7 +326,7 @@ void extract_common(const ObjectFileDB& db, if (dump_levels) { auto file_path = file_util::get_jak_project_dir() / "glb_out" / "common.glb"; file_util::create_dir_if_needed_for_file(file_path); - save_level_foreground_as_gltf(tfrag_level, file_path); + save_level_foreground_as_gltf(tfrag_level, art_group_data, file_path); } } @@ -339,12 +343,14 @@ void extract_from_level(const ObjectFileDB& db, return; } tfrag3::Level level_data; + std::map art_group_data; add_all_textures_from_level(level_data, dgo_name, tex_db); // the bsp header file data auto bsp_header = extract_bsp_from_level(db, tex_db, dgo_name, config.hacks, extract_collision, level_data); - extract_art_groups_from_level(db, tex_db, bsp_header.texture_remap_table, dgo_name, level_data); + extract_art_groups_from_level(db, tex_db, bsp_header.texture_remap_table, dgo_name, level_data, + art_group_data); Serializer ser; level_data.serialize(ser); @@ -366,7 +372,7 @@ void extract_from_level(const ObjectFileDB& db, auto fore_file_path = file_util::get_jak_project_dir() / "glb_out" / fmt::format("{}_foreground.glb", level_data.level_name); file_util::create_dir_if_needed_for_file(fore_file_path); - save_level_foreground_as_gltf(level_data, fore_file_path); + save_level_foreground_as_gltf(level_data, art_group_data, fore_file_path); } file_util::write_text_file(entities_folder / fmt::format("{}_actors.json", level_data.level_name), extract_actors_to_json(bsp_header.actors)); diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index d51dc8071..aa1539728 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -181,20 +181,6 @@ MercCtrl extract_merc_ctrl(const LinkedObjectFile& file, return ctrl; } -/*! - * Find the word indices for the merc ctrls (the type tags) - */ -std::vector find_merc_ctrls(const LinkedObjectFile& file) { - std::vector result; - for (size_t i = 0; i < file.words_by_seg.at(0).size(); i++) { - const auto& word = file.words_by_seg[0][i]; - if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "merc-ctrl") { - result.push_back(i); - } - } - return result; -} - namespace { /*! * Merc models tend to have strange texture ids. I don't really understand why. @@ -1616,7 +1602,7 @@ void extract_merc(const ObjectFileData& ag_data, file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/merc"})); } // find all merc-ctrls in the object file - auto ctrl_locations = find_merc_ctrls(ag_data.linked_data); + auto ctrl_locations = find_objects_with_type(ag_data.linked_data, "merc-ctrl"); // extract them. this does very basic unpacking of data, as done by the VIF/DMA on PS2. std::vector ctrls; diff --git a/decompiler/level_extractor/fr3_to_gltf.cpp b/decompiler/level_extractor/fr3_to_gltf.cpp index 0ed11afaf..826c92cdd 100644 --- a/decompiler/level_extractor/fr3_to_gltf.cpp +++ b/decompiler/level_extractor/fr3_to_gltf.cpp @@ -4,6 +4,7 @@ #include "common/custom_data/Tfrag3Data.h" #include "common/math/Vector.h" +#include "common/math/geometry.h" #include "decompiler/level_extractor/tfrag_tie_fixup.h" @@ -11,6 +12,17 @@ namespace { +/*! + * Remove 4096 meter scaling from a transformation matrix. + */ +math::Matrix4f unscale_translation(const math::Matrix4f& in) { + auto out = in; + for (int i = 0; i < 3; i++) { + out(i, 3) /= 4096.; + } + return out; +} + /*! * Convert fr3 format indices (strip format, with UINT32_MAX as restart) to unstripped tris. * Assumes that this is the tfrag/tie format of stripping. Will flip tris as needed so the faces @@ -557,6 +569,10 @@ int add_material_for_tex(const tfrag3::Level& level, int tex_idx, std::unordered_map& tex_image_map, const DrawMode& draw_mode) { + if (tex_idx < 0) { + // anim textures, just use default material + return 0; + } int mat_idx = (int)model.materials.size(); auto& mat = model.materials.emplace_back(); auto& tex = level.textures.at(tex_idx); @@ -759,7 +775,114 @@ void add_shrub(const tfrag3::Level& level, } } +int make_weights_accessor(const std::vector& vertices, tinygltf::Model& model) { + // first create a buffer: + int buffer_idx = (int)model.buffers.size(); + auto& buffer = model.buffers.emplace_back(); + buffer.data.resize(sizeof(float) * 4 * vertices.size()); + + // and fill it + u8* buffer_ptr = buffer.data.data(); + for (const auto& vtx : vertices) { + float weights[4] = {vtx.weights[0], vtx.weights[1], vtx.weights[2], 0}; + memcpy(buffer_ptr, weights, 4 * sizeof(float)); + buffer_ptr += 4 * sizeof(float); + } + + // create a view of this buffer + int buffer_view_idx = (int)model.bufferViews.size(); + auto& buffer_view = model.bufferViews.emplace_back(); + buffer_view.buffer = buffer_idx; + buffer_view.byteOffset = 0; + buffer_view.byteLength = buffer.data.size(); + buffer_view.byteStride = 0; // tightly packed + buffer_view.target = TINYGLTF_TARGET_ARRAY_BUFFER; + + int accessor_idx = (int)model.accessors.size(); + auto& accessor = model.accessors.emplace_back(); + accessor.bufferView = buffer_view_idx; + accessor.byteOffset = 0; + accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + accessor.count = vertices.size(); + accessor.type = TINYGLTF_TYPE_VEC4; + return accessor_idx; +} + +int make_bones_accessor(const std::vector& vertices, tinygltf::Model& model) { + // first create a buffer: + int buffer_idx = (int)model.buffers.size(); + auto& buffer = model.buffers.emplace_back(); + buffer.data.resize(sizeof(float) * 4 * vertices.size()); + + // and fill it + u8* buffer_ptr = buffer.data.data(); + for (const auto& vtx : vertices) { + s32 indices[4]; + for (int i = 0; i < 3; i++) { + indices[i] = vtx.mats[i] ? vtx.mats[i] - 1 : 0; + } + indices[3] = 0; + memcpy(buffer_ptr, indices, 4 * sizeof(s32)); + buffer_ptr += 4 * sizeof(s32); + } + + // create a view of this buffer + int buffer_view_idx = (int)model.bufferViews.size(); + auto& buffer_view = model.bufferViews.emplace_back(); + buffer_view.buffer = buffer_idx; + buffer_view.byteOffset = 0; + buffer_view.byteLength = buffer.data.size(); + buffer_view.byteStride = 0; // tightly packed + buffer_view.target = TINYGLTF_TARGET_ARRAY_BUFFER; + + int accessor_idx = (int)model.accessors.size(); + auto& accessor = model.accessors.emplace_back(); + accessor.bufferView = buffer_view_idx; + accessor.byteOffset = 0; + accessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; // blender doesn't support INT... + accessor.count = vertices.size(); + accessor.type = TINYGLTF_TYPE_VEC4; + return accessor_idx; +} + +int make_inv_matrix_bind_poses(const std::vector& joints, + tinygltf::Model& model) { + // first create a buffer: + int buffer_idx = (int)model.buffers.size(); + auto& buffer = model.buffers.emplace_back(); + buffer.data.resize(sizeof(float) * 16 * joints.size()); + + // and fill it + for (int m = 0; m < (int)joints.size(); m++) { + auto matrix = unscale_translation(joints[m].bind_pose_T_w); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + memcpy(buffer.data.data() + sizeof(float) * (i * 4 + j + m * 16), &matrix(j, i), 4); + } + } + } + + // create a view of this buffer + int buffer_view_idx = (int)model.bufferViews.size(); + auto& buffer_view = model.bufferViews.emplace_back(); + buffer_view.buffer = buffer_idx; + buffer_view.byteOffset = 0; + buffer_view.byteLength = buffer.data.size(); + buffer_view.byteStride = 0; // tightly packed + buffer_view.target = TINYGLTF_TARGET_ARRAY_BUFFER; + + int accessor_idx = (int)model.accessors.size(); + auto& accessor = model.accessors.emplace_back(); + accessor.bufferView = buffer_view_idx; + accessor.byteOffset = 0; + accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT; + accessor.count = joints.size(); + accessor.type = TINYGLTF_TYPE_MAT4; + return accessor_idx; +} + void add_merc(const tfrag3::Level& level, + const std::map& art_data, tinygltf::Model& model, std::unordered_map& tex_image_map) { const auto& mverts = level.merc_data.vertices; @@ -773,9 +896,12 @@ void add_merc(const tfrag3::Level& level, level.merc_data.indices, level.merc_data.models, model, draw_to_start, draw_to_count); int colors = make_color_buffer_accessor(mverts, model); + auto joints_accessor = make_bones_accessor(mverts, model); + auto weights_accessor = make_weights_accessor(mverts, model); + for (size_t model_idx = 0; model_idx < level.merc_data.models.size(); model_idx++) { const auto& mmodel = level.merc_data.models[model_idx]; - + const auto& art = art_data.find(mmodel.name); int node_idx = (int)model.nodes.size(); auto& node = model.nodes.emplace_back(); model.scenes.at(0).nodes.push_back(node_idx); @@ -785,6 +911,54 @@ void add_merc(const tfrag3::Level& level, mesh.name = node.name; node.mesh = mesh_idx; + if (art != art_data.end() && !art->second.joint_group.empty()) { + node.skin = model.skins.size(); + auto& skin = model.skins.emplace_back(); + const auto& game_bones = art->second.joint_group; + int n_bones = game_bones.size(); + std::vector> children(n_bones); + for (size_t i = 0; i < game_bones.size(); i++) { + if (game_bones[i].parent_idx >= 0) { + children.at(game_bones[i].parent_idx).push_back(i); + } + } + skin.skeleton = model.nodes.size(); + for (int i = 0; i < n_bones; i++) { + const auto& gbone = game_bones[i]; + skin.joints.push_back(skin.skeleton + i); + auto& snode = model.nodes.emplace_back(); + snode.name = gbone.name; + + // bind pose is bind_T_w + // for glb we want bind_parent_T_bind_child + // so bindp_T_w * inverse(bindc_T_w) + math::Matrix4f matrix; + if (gbone.parent_idx >= 0) { + matrix = unscale_translation(game_bones.at(gbone.parent_idx).bind_pose_T_w) * + inverse(unscale_translation(gbone.bind_pose_T_w)); + + } else { + // I think this value is ignored anyway. + for (int r = 0; r < 4; r++) { + for (int c = 0; c < 4; c++) { + matrix(r, c) = (r == c) ? 1 : 0; + } + } + } + + for (int r = 0; r < 4; r++) { + for (int c = 0; c < 4; c++) { + snode.matrix.push_back(matrix(c, r)); + } + } + for (auto child : children.at(i)) { + snode.children.push_back(skin.skeleton + child); + } + } + ASSERT(skin.skeleton + n_bones == (int)model.nodes.size()); + skin.inverseBindMatrices = make_inv_matrix_bind_poses(game_bones, model); + } + for (size_t effect_idx = 0; effect_idx < mmodel.effects.size(); effect_idx++) { const auto& effect = mmodel.effects[effect_idx]; for (size_t draw_idx = 0; draw_idx < effect.all_draws.size(); draw_idx++) { @@ -798,6 +972,8 @@ void add_merc(const tfrag3::Level& level, prim.attributes["POSITION"] = position_buffer_accessor; prim.attributes["TEXCOORD_0"] = texture_buffer_accessor; prim.attributes["COLOR_0"] = colors; + prim.attributes["JOINTS_0"] = joints_accessor; + prim.attributes["WEIGHTS_0"] = weights_accessor; prim.mode = TINYGLTF_MODE_TRIANGLES; } } @@ -847,7 +1023,9 @@ void save_level_background_as_gltf(const tfrag3::Level& level, const fs::path& g true); // write binary } -void save_level_foreground_as_gltf(const tfrag3::Level& level, const fs::path& glb_file) { +void save_level_foreground_as_gltf(const tfrag3::Level& level, + const std::map& art_data, + const fs::path& glb_file) { // the top level container for everything is the model. tinygltf::Model model; @@ -864,7 +1042,7 @@ void save_level_foreground_as_gltf(const tfrag3::Level& level, const fs::path& g std::unordered_map tex_image_map; - add_merc(level, model, tex_image_map); + add_merc(level, art_data, model, tex_image_map); model.asset.generator = "opengoal"; tinygltf::TinyGLTF gltf; diff --git a/decompiler/level_extractor/fr3_to_gltf.h b/decompiler/level_extractor/fr3_to_gltf.h index f2b52989f..88af85b9b 100644 --- a/decompiler/level_extractor/fr3_to_gltf.h +++ b/decompiler/level_extractor/fr3_to_gltf.h @@ -1,10 +1,16 @@ #pragma once +#include + #include "common/custom_data/Tfrag3Data.h" #include "common/util/FileUtil.h" +#include "decompiler/level_extractor/common_formats.h" + /*! * Export the background geometry (tie, tfrag, shrub) to a GLTF binary format (.glb) file. */ void save_level_background_as_gltf(const tfrag3::Level& level, const fs::path& glb_file); -void save_level_foreground_as_gltf(const tfrag3::Level& level, const fs::path& glb_file); \ No newline at end of file +void save_level_foreground_as_gltf(const tfrag3::Level& level, + const std::map& art_data, + const fs::path& glb_file); \ No newline at end of file diff --git a/decompiler/util/goal_data_reader.cpp b/decompiler/util/goal_data_reader.cpp index 639c44248..12777b849 100644 --- a/decompiler/util/goal_data_reader.cpp +++ b/decompiler/util/goal_data_reader.cpp @@ -420,4 +420,16 @@ u8 deref_u8(const Ref& ref, int byte) { u8 vals[4]; memcpy(vals, &word.data, 4); return vals[total_offset & 3]; +} + +std::vector find_objects_with_type(const decompiler::LinkedObjectFile& file, + const std::string& name) { + std::vector result; + for (size_t i = 0; i < file.words_by_seg.at(0).size(); i++) { + const auto& word = file.words_by_seg[0][i]; + if (word.kind() == decompiler::LinkedWord::TYPE_PTR && word.symbol_name() == name) { + result.push_back(i); + } + } + return result; } \ No newline at end of file diff --git a/decompiler/util/goal_data_reader.h b/decompiler/util/goal_data_reader.h index d44e8a0c0..8a3ad5339 100644 --- a/decompiler/util/goal_data_reader.h +++ b/decompiler/util/goal_data_reader.h @@ -91,3 +91,5 @@ u8 deref_u8(const Ref& ref, int byte); float deref_float(const Ref& ref, int array_idx); u64 deref_u64(const Ref& ref, int dw_offset); std::string inspect_ref(const Ref& ref); +std::vector find_objects_with_type(const decompiler::LinkedObjectFile& file, + const std::string& name); \ No newline at end of file