Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[glb export] Export bones. #3087

Merged
merged 4 commits into from Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 74 additions & 0 deletions common/math/geometry.h
Expand Up @@ -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 <typename T>
math::Matrix<T, 4, 4> affine_inverse(const math::Matrix<T, 4, 4>& in) {
math::Matrix<T, 4, 4> 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 <typename T>
math::Matrix<T, 4, 4> inverse(const math::Matrix<T, 4, 4>& m) {
math::Matrix<T, 4, 4> 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
1 change: 1 addition & 0 deletions decompiler/CMakeLists.txt
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions 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 {

Expand All @@ -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> joint_group;
};

} // namespace level_tools
46 changes: 46 additions & 0 deletions 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<std::string, level_tools::ArtData>& 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<int32_t>(ref, "length", dts);
Ref iter = get_field_ref(ref, "data", dts);
std::map<int, int> 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<int32_t>(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
14 changes: 14 additions & 0 deletions 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<std::string, level_tools::ArtData>& out);
}
16 changes: 11 additions & 5 deletions decompiler/level_extractor/extract_level.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -122,12 +123,14 @@ void extract_art_groups_from_level(const ObjectFileDB& db,
const TextureDB& tex_db,
const std::vector<level_tools::TextureRemap>& tex_remap,
const std::string& dgo_name,
tfrag3::Level& level_data) {
tfrag3::Level& level_data,
std::map<std::string, level_tools::ArtData>& 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);
}
}
}
Expand Down Expand Up @@ -273,8 +276,9 @@ void extract_common(const ObjectFileDB& db,
confirm_textures_identical(tex_db);

tfrag3::Level tfrag_level;
std::map<std::string, level_tools::ArtData> 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<std::string> textures_we_have;

Expand Down Expand Up @@ -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);
}
}

Expand All @@ -339,12 +343,14 @@ void extract_from_level(const ObjectFileDB& db,
return;
}
tfrag3::Level level_data;
std::map<std::string, level_tools::ArtData> 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);
Expand All @@ -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));
Expand Down
16 changes: 1 addition & 15 deletions decompiler/level_extractor/extract_merc.cpp
Expand Up @@ -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<int> find_merc_ctrls(const LinkedObjectFile& file) {
std::vector<int> 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.
Expand Down Expand Up @@ -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<MercCtrl> ctrls;
Expand Down