Files
jak-project/decompiler/level_extractor/extract_shadow.cpp
T
water111 a06348fa9f wip
2025-06-15 18:26:56 -04:00

369 lines
14 KiB
C++

#include "extract_shadow.h"
#include "common/log/log.h"
#include "common/util/BitUtils.h"
#include "decompiler/util/goal_data_reader.h"
namespace decompiler {
/*
*(deftype shadow-header (structure)
((qwc-data uint32 :offset-assert 0)
(num-joints uint32 :offset-assert 4)
(num-verts uint16 :offset-assert 8)
(num-twos uint16 :offset-assert 10)
(num-single-tris uint16 :offset-assert 12)
(num-single-edges uint16 :offset-assert 14)
(num-double-tris uint16 :offset-assert 16)
(num-double-edges uint16 :offset-assert 18)
(ofs-verts uint32 :offset-assert 20)
(ofs-refs uint32 :offset-assert 24)
(ofs-single-tris uint32 :offset-assert 28)
(ofs-single-edges uint32 :offset-assert 32)
(ofs-double-tris uint32 :offset-assert 36)
(ofs-double-edges uint32 :offset-assert 40)
)
:method-count-assert 9
:size-assert #x2c
:flag-assert #x90000002c
)
(deftype shadow-geo (art-element)
((total-size uint32 :offset-assert 32)
(header shadow-header :inline :offset 32)
(rest uint64 :dynamic :offset-assert 80)
)
:method-count-assert 13
:size-assert #x50
:flag-assert #xd00000050
)*/
struct ShadowVertex {
math::Vector3f pos;
float weight;
};
struct ShadowRef {
uint8_t joint_0 = 0;
uint8_t joint_1 = 0;
};
struct ShadowTri {
uint8_t verts[3];
uint8_t faces;
};
struct ShadowEdge {
uint8_t ind[2];
uint8_t tri[2];
};
struct ShadowData {
std::string name;
uint32_t num_joints = 0;
std::vector<ShadowVertex> one_bone_vertices;
std::vector<ShadowVertex> two_bone_vertices;
std::vector<ShadowRef> refs;
std::vector<ShadowTri> single_tris, double_tris;
std::vector<ShadowEdge> single_edges, double_edges;
};
std::string debug_dump_to_ply(const ShadowData& data) {
int num_verts = data.one_bone_vertices.size() + data.two_bone_vertices.size();
std::string result = fmt::format(
"ply\nformat ascii 1.0\nelement vertex {}\nproperty float x\nproperty float y\nproperty "
"float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\nelement face "
"{}\nproperty list uchar int vertex_index\nend_header\n",
2 * num_verts, data.single_tris.size() + data.double_tris.size());
for (auto& vtx : data.one_bone_vertices) {
result += fmt::format("{} {} {} {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f, 128, 128, 128);
}
for (auto& vtx : data.two_bone_vertices) {
result += fmt::format("{} {} {} {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f, 128, 128, 128);
}
for (auto& vtx : data.one_bone_vertices) {
result += fmt::format("{} {} {} {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f, 128, 256, 128);
}
for (auto& vtx : data.two_bone_vertices) {
result += fmt::format("{} {} {} {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f, 128, 256, 128);
}
for (auto& face : data.single_tris) {
result += fmt::format("3 {} {} {}\n", face.verts[0], face.verts[1], face.verts[2]);
}
for (auto& face : data.double_tris) {
result += fmt::format("3 {} {} {}\n", face.verts[0] + num_verts, face.verts[1] + num_verts,
face.verts[2] + num_verts);
}
return result;
}
ShadowData extract_shadow_data(const LinkedObjectFile& file,
const DecompilerTypeSystem& dts,
int word_idx) {
Ref ref;
ref.data = &file;
ref.seg = 0;
ref.byte_offset = word_idx * 4;
auto tr = typed_ref_from_basic(ref, dts);
constexpr int kHeaderSize = 48;
ShadowData shadow_data;
auto header_ref = TypedRef(get_field_ref(tr, "header", dts), dts.ts.lookup_type("shadow-header"));
u32 size_qwc = read_plain_data_field<s32>(header_ref, "qwc-data", dts);
ASSERT(size_qwc < 1024 * 1024); // something reasonable
std::vector<u8> data(size_qwc * 16);
Ref shadow_ref = header_ref.ref;
shadow_ref.byte_offset += kHeaderSize;
memcpy_from_plain_data(data.data(), shadow_ref, size_qwc * 16 - kHeaderSize);
lg::info("name is {}, has {} joints, size {} bytes", read_string_field(tr, "name", dts, false),
read_plain_data_field<s32>(header_ref, "num-joints", dts), data.size());
shadow_data.name = read_string_field(tr, "name", dts, false);
shadow_data.num_joints = read_plain_data_field<s32>(header_ref, "num-joints", dts);
const u32 num_verts = read_plain_data_field<u16>(header_ref, "num-verts", dts);
const u32 num_twos = read_plain_data_field<u16>(header_ref, "num-twos", dts);
ASSERT(num_verts >= num_twos);
const u32 num_ones = num_verts - num_twos;
lg::info(" vert counts {} {}", num_ones, num_twos);
const u32 ofs_verts = read_plain_data_field<u32>(header_ref, "ofs-verts", dts);
const u32 ofs_refs = read_plain_data_field<u32>(header_ref, "ofs-refs", dts);
const u32 ofs_single_tris = read_plain_data_field<u32>(header_ref, "ofs-single-tris", dts);
const u32 ofs_single_edges = read_plain_data_field<u32>(header_ref, "ofs-single-edges", dts);
const u32 ofs_double_tris = read_plain_data_field<u32>(header_ref, "ofs-double-tris", dts);
const u32 ofs_double_edges = read_plain_data_field<u32>(header_ref, "ofs-double-edges", dts);
const u32 num_single_tris = read_plain_data_field<u16>(header_ref, "num-single-tris", dts);
const u32 num_single_edges = read_plain_data_field<u16>(header_ref, "num-single-edges", dts);
const u32 num_double_tris = read_plain_data_field<u16>(header_ref, "num-double-tris", dts);
const u32 num_double_edges = read_plain_data_field<u16>(header_ref, "num-double-edges", dts);
ASSERT(ofs_verts == kHeaderSize); // verts always right after the header
lg::info(" offsets {} {} {} {} {} {}", ofs_verts, ofs_refs, ofs_single_tris, ofs_single_edges,
ofs_double_tris, ofs_double_edges);
// vertices
ASSERT(ofs_refs - ofs_verts == 16 * num_verts);
shadow_data.one_bone_vertices.resize(num_ones);
memcpy_from_plain_data(shadow_data.one_bone_vertices.data(), shadow_ref, num_ones * 16);
shadow_ref.byte_offset += num_ones * 16;
for (const auto& x : shadow_data.one_bone_vertices) {
ASSERT(x.weight == 1);
}
shadow_data.two_bone_vertices.resize(num_twos);
memcpy_from_plain_data(shadow_data.two_bone_vertices.data(), shadow_ref, num_twos * 16);
shadow_ref.byte_offset += num_twos * 16;
for (auto x : shadow_data.two_bone_vertices) {
ASSERT(x.weight > 0 && x.weight < 1);
}
// refs
ASSERT(ofs_single_tris - ofs_refs == align16(num_verts * 2));
shadow_data.refs.resize(num_verts);
memcpy_from_plain_data(shadow_data.refs.data(), shadow_ref, num_verts * 2);
shadow_ref.byte_offset += ofs_single_tris - ofs_refs;
for (size_t i = 0; i < num_verts; i++) {
ASSERT(shadow_data.refs[i].joint_0 < shadow_data.num_joints);
if (i < num_ones) {
ASSERT(shadow_data.refs[i].joint_1 == 255);
} else {
ASSERT(shadow_data.refs[i].joint_1 < shadow_data.num_joints);
ASSERT(shadow_data.refs[i].joint_1 != shadow_data.refs[i].joint_0);
}
}
// single tris
ASSERT(ofs_single_edges - ofs_single_tris == align16(num_single_tris * 4));
shadow_data.single_tris.resize(num_single_tris);
memcpy_from_plain_data(shadow_data.single_tris.data(), shadow_ref, num_single_tris * 4);
shadow_ref.byte_offset += ofs_single_edges - ofs_single_tris;
for (auto& tri : shadow_data.single_tris) {
for (auto v : tri.verts) {
ASSERT(v < num_verts);
}
ASSERT(tri.faces == 0);
}
// single edges
ASSERT(ofs_double_tris - ofs_single_edges == align16(num_single_edges * 4));
shadow_data.single_edges.resize(num_single_edges);
memcpy_from_plain_data(shadow_data.single_edges.data(), shadow_ref, num_single_edges * 4);
shadow_ref.byte_offset += ofs_double_tris - ofs_single_edges;
for (auto& edge : shadow_data.single_edges) {
for (auto x : edge.ind) {
ASSERT(x < num_verts);
}
ASSERT(edge.tri[0] != 255);
for (auto x : edge.tri) {
ASSERT(x == 255 || x < shadow_data.single_tris.size());
}
}
// double tris
ASSERT(ofs_double_edges - ofs_double_tris == align16(num_double_tris * 4));
shadow_data.double_tris.resize(num_double_tris);
memcpy_from_plain_data(shadow_data.double_tris.data(), shadow_ref, num_double_tris * 4);
shadow_ref.byte_offset += ofs_double_edges - ofs_double_tris;
for (auto& tri : shadow_data.double_tris) {
for (auto v : tri.verts) {
ASSERT(v < num_verts);
}
ASSERT(tri.faces == 0);
}
// double edges
ASSERT(size_qwc * 16 - ofs_double_edges == align16(num_double_edges * 4));
shadow_data.double_edges.resize(num_double_edges);
memcpy_from_plain_data(shadow_data.double_edges.data(), shadow_ref, num_double_edges * 4);
for (auto& edge : shadow_data.double_edges) {
for (auto x : edge.ind) {
ASSERT(x < num_verts);
}
ASSERT(edge.tri[0] != 255);
for (auto x : edge.tri) {
ASSERT(x == 255 || x < shadow_data.double_tris.size());
}
}
return shadow_data;
}
std::vector<tfrag3::ShadowVertex> convert_vertices(const ShadowData& data) {
std::vector<tfrag3::ShadowVertex> result;
for (size_t i = 0; i < data.one_bone_vertices.size(); i++) {
const auto& in = data.one_bone_vertices[i];
auto& out = result.emplace_back();
out.pos[0] = in.pos.x();
out.pos[1] = in.pos.y();
out.pos[2] = in.pos.z();
out.weight = 1.f;
out.mats[0] = data.refs.at(i).joint_0;
out.mats[1] = data.refs.at(i).joint_1;
ASSERT(out.mats[1] == 255);
ASSERT(in.weight == 1.f);
out.flags = 0;
}
for (size_t i = 0; i < data.two_bone_vertices.size(); i++) {
const auto& in = data.two_bone_vertices[i];
auto& out = result.emplace_back();
out.pos[0] = in.pos.x();
out.pos[1] = in.pos.y();
out.pos[2] = in.pos.z();
out.weight = in.weight;
ASSERT(out.weight != 1.f && out.weight != 0.f);
out.mats[0] = data.refs.at(data.one_bone_vertices.size() + i).joint_0;
out.mats[1] = data.refs.at(data.one_bone_vertices.size() + i).joint_1;
ASSERT(out.mats[0] != 255);
ASSERT(out.mats[1] != 255);
out.flags = 0;
}
return result;
}
void extract_shadow(const ObjectFileData& ag_data,
const DecompilerTypeSystem& dts,
tfrag3::Level& out,
bool dump_level,
GameVersion version) {
// hack
// dump_level = true;
if (dump_level) {
file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/shadow"}));
}
auto geo_locations = find_objects_with_type(ag_data.linked_data, "shadow-geo");
if (!geo_locations.empty()) {
lg::error("{} has {} shadows", ag_data.name_in_dgo, geo_locations.size());
}
int i = 0;
auto& sd = out.shadow_data;
for (auto loc : geo_locations) {
const ShadowData data = extract_shadow_data(ag_data.linked_data, dts, loc);
const u32 vertex_offset = sd.vertices.size();
const u32 num_vertices = data.one_bone_vertices.size() + data.two_bone_vertices.size();
// insert top vertices
auto vertices = convert_vertices(data);
sd.vertices.insert(sd.vertices.end(), vertices.begin(), vertices.end());
// bottom vertices
for (auto& v : vertices) {
v.flags = 1;
}
sd.vertices.insert(sd.vertices.end(), vertices.begin(), vertices.end());
auto& model = sd.models.emplace_back();
model.name = data.name;
model.max_bones = data.num_joints;
// single triangles
model.single_tris.first_index = sd.indices.size();
for (auto& stri : data.single_tris) {
sd.indices.push_back(static_cast<u32>(stri.verts[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(stri.verts[1]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(stri.verts[2]) + vertex_offset);
}
model.single_tris.count = sd.indices.size() - model.single_tris.first_index;
// double triangles
model.double_tris.first_index = sd.indices.size();
for (auto& dtri : data.double_tris) {
sd.indices.push_back(static_cast<u32>(dtri.verts[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(dtri.verts[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(dtri.verts[2]) + vertex_offset + num_vertices);
}
model.double_tris.count = sd.indices.size() - model.double_tris.first_index;
// single edges
model.single_edges.first_index = sd.indices.size();
for (auto& se : data.single_edges) {
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset);
}
model.single_edges.count = sd.indices.size() - model.single_edges.first_index;
model.double_edges.first_index = sd.indices.size();
for (auto& se : data.double_edges) {
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset);
}
model.double_edges.count = sd.indices.size() - model.double_edges.first_index;
if (dump_level) {
auto file_path = file_util::get_file_path(
{"debug_out/shadow", fmt::format("{}_{}.ply", ag_data.name_in_dgo, i)});
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_text_file(file_path, debug_dump_to_ply(data));
}
i++;
}
}
} // namespace decompiler