mirror of
https://github.com/open-goal/jak-project
synced 2026-05-29 16:45:10 -04:00
369 lines
14 KiB
C++
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
|