mirror of
https://github.com/open-goal/jak-project
synced 2026-05-29 08:43:08 -04:00
wip
This commit is contained in:
@@ -587,6 +587,28 @@ void MercModelGroup::serialize(Serializer& ser) {
|
||||
ser.from_pod_vector(&vertices);
|
||||
}
|
||||
|
||||
void ShadowModel::serialize(Serializer& ser) {
|
||||
ser.from_str(&name);
|
||||
ser.from_ptr(&max_bones);
|
||||
ser.from_ptr(&single_tris);
|
||||
ser.from_ptr(&double_tris);
|
||||
ser.from_ptr(&single_edges);
|
||||
ser.from_ptr(&double_edges);
|
||||
}
|
||||
|
||||
void ShadowModelGroup::serialize(Serializer& ser) {
|
||||
ser.from_pod_vector(&vertices);
|
||||
ser.from_pod_vector(&indices);
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(models.size());
|
||||
} else {
|
||||
models.resize(ser.load<size_t>());
|
||||
}
|
||||
for (auto& model : models) {
|
||||
model.serialize(ser);
|
||||
}
|
||||
}
|
||||
|
||||
void Level::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&version);
|
||||
if (ser.is_loading() && version != TFRAG3_VERSION) {
|
||||
@@ -647,9 +669,9 @@ void Level::serialize(Serializer& ser) {
|
||||
}
|
||||
|
||||
hfrag.serialize(ser);
|
||||
|
||||
collision.serialize(ser);
|
||||
merc_data.serialize(ser);
|
||||
shadow_data.serialize(ser);
|
||||
|
||||
ser.from_ptr(&version2);
|
||||
if (ser.is_loading() && version2 != TFRAG3_VERSION) {
|
||||
@@ -770,6 +792,11 @@ void Hfragment::memory_usage(tfrag3::MemoryUsageTracker* tracker) const {
|
||||
tracker->add(MemoryUsageCategory::HFRAG_CORNERS, corners.size() * sizeof(HfragmentCorner));
|
||||
}
|
||||
|
||||
void ShadowModelGroup::memory_usage(MemoryUsageTracker* tracker) const {
|
||||
tracker->add(SHADOW_VERTS, vertices.size() * sizeof(ShadowVertex));
|
||||
tracker->add(SHADOW_INDEX, indices.size() * sizeof(u32));
|
||||
}
|
||||
|
||||
void Level::memory_usage(MemoryUsageTracker* tracker) const {
|
||||
for (const auto& texture : textures) {
|
||||
texture.memory_usage(tracker);
|
||||
@@ -793,6 +820,7 @@ void Level::memory_usage(MemoryUsageTracker* tracker) const {
|
||||
hfrag.memory_usage(tracker);
|
||||
collision.memory_usage(tracker);
|
||||
merc_data.memory_usage(tracker);
|
||||
shadow_data.memory_usage(tracker);
|
||||
}
|
||||
|
||||
void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) {
|
||||
@@ -837,8 +865,9 @@ void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) {
|
||||
{"hfrag-verts", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_VERTS]},
|
||||
{"hfrag-index", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_INDEX]},
|
||||
{"hfrag-time-of-day", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_TIME_OF_DAY]},
|
||||
{"hfrag-corners", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_CORNERS]}
|
||||
|
||||
{"hfrag-corners", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_CORNERS]},
|
||||
{"shadow-vert", mem_use.data[SHADOW_VERTS]},
|
||||
{"shadow-ind", mem_use.data[SHADOW_INDEX]},
|
||||
};
|
||||
for (auto& known : known_categories) {
|
||||
total_accounted += known.second;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace tfrag3 {
|
||||
// - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage
|
||||
// - if adding a new category to the memory usage, update extract_level to print it.
|
||||
|
||||
constexpr int TFRAG3_VERSION = 43;
|
||||
constexpr int TFRAG3_VERSION = 44;
|
||||
|
||||
enum MemoryUsageCategory {
|
||||
TEXTURE,
|
||||
@@ -66,6 +66,9 @@ enum MemoryUsageCategory {
|
||||
HFRAG_TIME_OF_DAY,
|
||||
HFRAG_CORNERS,
|
||||
|
||||
SHADOW_VERTS,
|
||||
SHADOW_INDEX,
|
||||
|
||||
COLLISION,
|
||||
|
||||
NUM_CATEGORIES
|
||||
@@ -614,7 +617,33 @@ struct MercModelGroup {
|
||||
void memory_usage(MemoryUsageTracker* tracker) const;
|
||||
};
|
||||
|
||||
//
|
||||
struct ShadowVertex {
|
||||
float pos[3];
|
||||
float weight;
|
||||
u8 mats[2];
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
struct ShadowModel {
|
||||
std::string name;
|
||||
u32 max_bones;
|
||||
|
||||
struct Run {
|
||||
u32 first_index;
|
||||
u32 count;
|
||||
};
|
||||
Run single_tris, double_tris, single_edges, double_edges;
|
||||
|
||||
void serialize(Serializer& ser);
|
||||
};
|
||||
|
||||
struct ShadowModelGroup {
|
||||
std::vector<ShadowVertex> vertices;
|
||||
std::vector<u32> indices;
|
||||
std::vector<ShadowModel> models;
|
||||
void serialize(Serializer& ser);
|
||||
void memory_usage(MemoryUsageTracker* tracker) const;
|
||||
};
|
||||
|
||||
constexpr int TFRAG_GEOS = 3;
|
||||
constexpr int TIE_GEOS = 4;
|
||||
@@ -630,6 +659,7 @@ struct Level {
|
||||
Hfragment hfrag;
|
||||
CollisionMesh collision;
|
||||
MercModelGroup merc_data;
|
||||
ShadowModelGroup shadow_data;
|
||||
u16 version2 = TFRAG3_VERSION;
|
||||
void serialize(Serializer& ser);
|
||||
void memory_usage(MemoryUsageTracker* tracker) const;
|
||||
|
||||
@@ -62,6 +62,7 @@ add_library(
|
||||
level_extractor/extract_joint_group.cpp
|
||||
level_extractor/extract_level.cpp
|
||||
level_extractor/extract_merc.cpp
|
||||
level_extractor/extract_shadow.cpp
|
||||
level_extractor/extract_tfrag.cpp
|
||||
level_extractor/extract_tie.cpp
|
||||
level_extractor/extract_shrub.cpp
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
#include "extract_shadow.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/SimpleThreadGroup.h"
|
||||
@@ -129,6 +131,7 @@ void extract_art_groups_from_level(const ObjectFileDB& db,
|
||||
extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version(),
|
||||
swapped_info);
|
||||
extract_joint_group(ag_file, db.dts, db.version(), art_group_data);
|
||||
extract_shadow(ag_file, db.dts, level_data, false, db.version());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
#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
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
||||
void extract_shadow(const ObjectFileData& ag_data,
|
||||
const DecompilerTypeSystem& dts,
|
||||
tfrag3::Level& out,
|
||||
bool dump_level,
|
||||
GameVersion version);
|
||||
} // namespace decompiler
|
||||
@@ -51,7 +51,8 @@ void read_plain_data_field(const TypedRef& object,
|
||||
}
|
||||
}
|
||||
|
||||
void memcpy_from_plain_data(u8* dest, const Ref& source, int size_bytes) {
|
||||
void memcpy_from_plain_data(void* _dest, const Ref& source, int size_bytes) {
|
||||
u8* dest = (u8*)_dest;
|
||||
const auto& words = source.data->words_by_seg.at(source.seg);
|
||||
for (int byte = 0; byte < size_bytes; byte++) {
|
||||
int byte_in_words = byte + source.byte_offset;
|
||||
|
||||
@@ -45,7 +45,7 @@ T read_plain_data_field(const TypedRef& object,
|
||||
return result;
|
||||
}
|
||||
|
||||
void memcpy_from_plain_data(u8* dest, const Ref& source, int size_bytes);
|
||||
void memcpy_from_plain_data(void* dest, const Ref& source, int size_bytes);
|
||||
std::vector<u8> bytes_from_plain_data(const Ref& source, int size_bytes);
|
||||
|
||||
decompiler::LinkedWord::Kind get_word_kind_for_field(const TypedRef& object,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,6 +55,7 @@ set(RUNTIME_SOURCE
|
||||
graphics/opengl_renderer/foreground/Merc2.cpp
|
||||
graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp
|
||||
graphics/opengl_renderer/foreground/Shadow2.cpp
|
||||
graphics/opengl_renderer/foreground/Shadow3.cpp
|
||||
graphics/opengl_renderer/loader/Loader.cpp
|
||||
graphics/opengl_renderer/loader/LoaderStages.cpp
|
||||
graphics/opengl_renderer/ocean/CommonOceanRenderer.cpp
|
||||
|
||||
@@ -55,6 +55,8 @@ struct SharedRenderState {
|
||||
|
||||
// including transformation, rotation, perspective
|
||||
math::Vector4f camera_matrix[4];
|
||||
math::Vector4f camera_rot[4];
|
||||
math::Vector4f perspective[4];
|
||||
math::Vector4f camera_hvdf_off;
|
||||
math::Vector4f camera_fog;
|
||||
math::Vector4f camera_pos;
|
||||
|
||||
@@ -112,6 +112,7 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
|
||||
}
|
||||
|
||||
m_merc2 = std::make_shared<Merc2>(m_render_state.shaders, anim_slot_array());
|
||||
m_shadow3 = std::make_shared<Shadow3>(m_render_state.shaders);
|
||||
m_generic2 = std::make_shared<Generic2>(m_render_state.shaders);
|
||||
|
||||
// initialize all renderers
|
||||
@@ -757,8 +758,9 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
||||
|
||||
init_bucket_renderer<Generic2BucketRenderer>("common-alpha-generic", BucketCategory::GENERIC,
|
||||
BucketId::GENERIC_ALPHA, m_generic2,
|
||||
Generic2::Mode::NORMAL); // 46
|
||||
init_bucket_renderer<ShadowRenderer>("shadow", BucketCategory::OTHER, BucketId::SHADOW); // 47
|
||||
Generic2::Mode::NORMAL); // 46
|
||||
init_bucket_renderer<ShadowRenderer>("shadow", BucketCategory::OTHER, BucketId::SHADOW,
|
||||
m_shadow3); // 47
|
||||
|
||||
//-----------------------
|
||||
// LEVEL 0 pris texture
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "game/graphics/opengl_renderer/TextureAnimator.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Merc2.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Shadow3.h"
|
||||
#include "game/graphics/opengl_renderer/opengl_utils.h"
|
||||
#include "game/tools/filter_menu/filter_menu.h"
|
||||
#include "game/tools/subtitle_editor/subtitle_editor.h"
|
||||
@@ -112,6 +113,7 @@ class OpenGLRenderer {
|
||||
|
||||
std::shared_ptr<Merc2> m_merc2;
|
||||
std::shared_ptr<Generic2> m_generic2;
|
||||
std::shared_ptr<Shadow3> m_shadow3;
|
||||
std::shared_ptr<TextureAnimator> m_texture_animator;
|
||||
std::vector<std::unique_ptr<BucketRenderer>> m_bucket_renderers;
|
||||
std::vector<BucketCategory> m_bucket_categories;
|
||||
|
||||
@@ -132,6 +132,7 @@ ShaderLibrary::ShaderLibrary(GameVersion version) {
|
||||
at(ShaderId::HFRAG_MONTAGE) = {"hfrag_montage", version};
|
||||
at(ShaderId::PLAIN_TEXTURE) = {"plain_texture", version};
|
||||
at(ShaderId::TIE_WIND) = {"tie_wind", version};
|
||||
at(ShaderId::SHADOW3) = {"shadow3", version};
|
||||
|
||||
for (auto& shader : m_shaders) {
|
||||
ASSERT_MSG(shader.okay(), "error compiling shader");
|
||||
|
||||
@@ -65,6 +65,7 @@ enum class ShaderId {
|
||||
HFRAG_MONTAGE = 38,
|
||||
PLAIN_TEXTURE = 39,
|
||||
TIE_WIND = 40,
|
||||
SHADOW3 = 41,
|
||||
MAX_SHADERS
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
ShadowRenderer::ShadowRenderer(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
|
||||
ShadowRenderer::ShadowRenderer(const std::string& name, int my_id, std::shared_ptr<Shadow3> shadow3)
|
||||
: BucketRenderer(name, my_id), m_shadow3(shadow3) {
|
||||
// create OpenGL objects
|
||||
glGenBuffers(1, &m_ogl.vertex_buffer);
|
||||
|
||||
@@ -35,9 +36,13 @@ ShadowRenderer::ShadowRenderer(const std::string& name, int my_id) : BucketRende
|
||||
}
|
||||
|
||||
void ShadowRenderer::draw_debug_window() {
|
||||
ImGui::Checkbox("Volume", &m_debug_draw_volume);
|
||||
ImGui::Text("Vert: %d, Front: %d, Back: %d\n", m_next_vertex, m_next_front_index,
|
||||
m_next_back_index);
|
||||
if (m_using_shadow3) {
|
||||
m_shadow3->draw_debug_window();
|
||||
} else {
|
||||
ImGui::Checkbox("Volume", &m_debug_draw_volume);
|
||||
ImGui::Text("Vert: %d, Front: %d, Back: %d\n", m_next_vertex, m_next_front_index,
|
||||
m_next_back_index);
|
||||
}
|
||||
}
|
||||
|
||||
ShadowRenderer::~ShadowRenderer() {
|
||||
@@ -201,6 +206,12 @@ void ShadowRenderer::render(DmaFollower& dma,
|
||||
return;
|
||||
}
|
||||
|
||||
m_using_shadow3 = dma.current_tag_vifcode0().kind != VifCode::Kind::STCYCL;
|
||||
if (m_using_shadow3) {
|
||||
m_shadow3->render_jak1(dma, render_state, prof);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// constants
|
||||
auto constants = dma.read_and_advance();
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
|
||||
#include "game/common/vu.h"
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Shadow3.h"
|
||||
|
||||
/*!
|
||||
* Jak 1 shadow renderer. This uses mips2c'd VU1 code and isn't very efficient.
|
||||
* If it detects PC shadow enabled, it will instead render with Shadow3.
|
||||
*/
|
||||
class ShadowRenderer : public BucketRenderer {
|
||||
public:
|
||||
ShadowRenderer(const std::string& name, int my_id);
|
||||
ShadowRenderer(const std::string& name, int my_id, std::shared_ptr<Shadow3> shadow3);
|
||||
~ShadowRenderer();
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
|
||||
void draw_debug_window() override;
|
||||
@@ -127,4 +132,6 @@ class ShadowRenderer : public BucketRenderer {
|
||||
} m_ogl;
|
||||
|
||||
bool m_debug_draw_volume = false;
|
||||
std::shared_ptr<Shadow3> m_shadow3;
|
||||
bool m_using_shadow3 = false;
|
||||
};
|
||||
|
||||
@@ -829,6 +829,8 @@ void update_render_state_from_pc_settings(SharedRenderState* state, const TfragP
|
||||
for (int i = 0; i < 4; i++) {
|
||||
state->camera_planes[i] = data.camera.planes[i];
|
||||
state->camera_matrix[i] = data.camera.camera[i];
|
||||
state->camera_rot[i] = data.camera.rot[i];
|
||||
state->perspective[i] = data.camera.perspective[i];
|
||||
}
|
||||
state->camera_pos = data.camera.trans;
|
||||
state->camera_hvdf_off = data.camera.hvdf_off;
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
#include "Shadow3.h"
|
||||
|
||||
#include "game/runtime.h"
|
||||
|
||||
Shadow3::Shadow3(ShaderLibrary& shaders) {
|
||||
glGenVertexArrays(1, &m_opengl.vao);
|
||||
glBindVertexArray(m_opengl.vao);
|
||||
|
||||
glGenBuffers(1, &m_opengl.bones_buffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, m_opengl.bones_buffer);
|
||||
|
||||
GLint val;
|
||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &val);
|
||||
if (val <= 16) {
|
||||
m_opengl.buffer_alignment = 1;
|
||||
} else {
|
||||
m_opengl.buffer_alignment = val / 16;
|
||||
if (m_opengl.buffer_alignment * 16 != (u32)val) {
|
||||
ASSERT_MSG(false,
|
||||
fmt::format("opengl uniform buffer alignment is {}, which is strange\n", val));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto& shader = shaders.at(ShaderId::SHADOW3);
|
||||
shader.activate();
|
||||
auto id = shader.id();
|
||||
m_uniforms.camera_rot = glGetUniformLocation(id, "camera_rot");
|
||||
m_uniforms.fog_constants = glGetUniformLocation(id, "fog_constants");
|
||||
m_uniforms.hvdf_offset = glGetUniformLocation(id, "hvdf_offset");
|
||||
m_uniforms.perspective_matrix = glGetUniformLocation(id, "perspective_matrix");
|
||||
m_uniforms.debug_color = glGetUniformLocation(id, "debug_color");
|
||||
m_uniforms.origin = glGetUniformLocation(id, "origin");
|
||||
m_uniforms.top_plane = glGetUniformLocation(id, "top_plane");
|
||||
m_uniforms.bottom_plane = glGetUniformLocation(id, "bottom_plane");
|
||||
m_uniforms.bottom_cap = glGetUniformLocation(id, "bottom_cap");
|
||||
}
|
||||
|
||||
std::vector<u8> temp(MAX_SHADER_BONE_VECTORS * sizeof(math::Vector4f));
|
||||
glBufferData(GL_UNIFORM_BUFFER, MAX_SHADER_BONE_VECTORS * sizeof(math::Vector4f), temp.data(),
|
||||
GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
Shadow3::~Shadow3() {
|
||||
glDeleteBuffers(1, &m_opengl.bones_buffer);
|
||||
glDeleteVertexArrays(1, &m_opengl.vao);
|
||||
}
|
||||
|
||||
void Shadow3::setup_for_level(SharedRenderState* render_state, const LevelData* level_data) {
|
||||
glBindVertexArray(m_opengl.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, level_data->shadow_vertices);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, level_data->shadow_indices);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
|
||||
glVertexAttribPointer(0, // location 0 in the shader
|
||||
3, // 3 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(tfrag3::ShadowVertex), // stride
|
||||
(void*)offsetof(tfrag3::ShadowVertex, pos) // offset (0)
|
||||
);
|
||||
|
||||
glVertexAttribPointer(1, // location 1 in the
|
||||
1, // 3 values per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized
|
||||
sizeof(tfrag3::ShadowVertex), // stride
|
||||
(void*)offsetof(tfrag3::ShadowVertex, weight) // offset (0)
|
||||
);
|
||||
|
||||
glVertexAttribIPointer(2, // location 2 in the
|
||||
2, //
|
||||
GL_UNSIGNED_BYTE, // u8's
|
||||
sizeof(tfrag3::ShadowVertex), //
|
||||
(void*)offsetof(tfrag3::ShadowVertex, mats[0]) // offset in array
|
||||
);
|
||||
|
||||
glVertexAttribIPointer(3, // location 2 in the
|
||||
1, //
|
||||
GL_UNSIGNED_BYTE, // u8's
|
||||
sizeof(tfrag3::ShadowVertex), //
|
||||
(void*)offsetof(tfrag3::ShadowVertex, flags) // offset in array
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void set_uniform(GLuint uniform, const math::Vector3f& val) {
|
||||
glUniform3f(uniform, val.x(), val.y(), val.z());
|
||||
}
|
||||
void set_uniform(GLuint uniform, const math::Vector4f& val) {
|
||||
glUniform4f(uniform, val.x(), val.y(), val.z(), val.w());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Shadow3::draw_model(SharedRenderState* render_state,
|
||||
ShadowRequest* request,
|
||||
ScopedProfilerNode& prof) {
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_opengl.bones_buffer,
|
||||
sizeof(math::Vector4f) * request->bone_idx, 128 * 16 * 4);
|
||||
const auto* geo = request->model.model;
|
||||
|
||||
set_uniform(m_uniforms.origin, request->origin);
|
||||
set_uniform(m_uniforms.top_plane, request->top_plane);
|
||||
set_uniform(m_uniforms.bottom_plane, request->bottom_plane);
|
||||
|
||||
// enable stencil!
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilMask(0xFF);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
// glDepthMask(GL_FALSE); // no depth writes.
|
||||
|
||||
auto do_draw = [&](const tfrag3::ShadowModel::Run& run, const math::Vector3f& color) {
|
||||
set_uniform(m_uniforms.debug_color, color);
|
||||
glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT,
|
||||
(void*)(sizeof(u32) * run.first_index));
|
||||
glDisable(GL_BLEND);
|
||||
set_uniform(m_uniforms.debug_color,color);
|
||||
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
// glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT,
|
||||
// (void*)(sizeof(u32) * run.first_index));
|
||||
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
};
|
||||
|
||||
auto do_all_draws = [&]( const math::Vector3f& color) {
|
||||
glUniform1i(m_uniforms.bottom_cap, 0);
|
||||
do_draw(geo->single_tris, color);
|
||||
// do_draw(geo->double_tris, math::Vector3f(0.8, 0.5, 0.5));
|
||||
do_draw(geo->single_edges, color);
|
||||
// do_draw(geo->double_edges, math::Vector3f(0.5, 0.8, 0.5));
|
||||
// glUniform1i(m_uniforms.bottom_cap, 1);
|
||||
// do_draw(geo->single_tris, math::Vector3f(0.5, 0.5, 0.8));
|
||||
// do_draw(geo->double_tris, math::Vector3f(0.5, 0.5, 0.8));
|
||||
};
|
||||
|
||||
// using glCullFace(GL_FRONT) seems to give us back faces.
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment on depth pass.
|
||||
do_all_draws({0.1f, 0.1f, 0.8f});
|
||||
glCullFace(GL_FRONT);
|
||||
glStencilFunc(GL_ALWAYS, 0, 0);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); // decrement on depth pass.
|
||||
do_all_draws({0.8f, 0.1f, 0.1f});
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
void Shadow3::finish(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
||||
|
||||
}
|
||||
|
||||
void Shadow3::flush_requests(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
||||
if (m_next_request == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_did_first_time_setup) {
|
||||
first_time_setup(render_state);
|
||||
m_did_first_time_setup = true;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, m_opengl.bones_buffer);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, m_next_free_bone_vector * sizeof(math::Vector4f),
|
||||
m_shader_bone_vector_buffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
for (auto& c : m_level_chains) {
|
||||
if (!c.head)
|
||||
continue;
|
||||
setup_for_level(render_state, c.level);
|
||||
ShadowRequest* iter = c.head;
|
||||
while (iter) {
|
||||
draw_model(render_state, iter, prof);
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& c : m_level_chains) {
|
||||
c.level = nullptr;
|
||||
c.head = nullptr;
|
||||
}
|
||||
m_next_request = 0;
|
||||
m_next_free_bone_vector = 0;
|
||||
}
|
||||
|
||||
void Shadow3::first_time_setup(SharedRenderState* render_state) {
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
render_state->shaders[ShaderId::SHADOW3].activate();
|
||||
glUniformMatrix4fv(m_uniforms.camera_rot, 1, GL_FALSE, &render_state->camera_rot[0].x());
|
||||
glUniformMatrix4fv(m_uniforms.perspective_matrix, 1, GL_FALSE, &render_state->perspective[0].x());
|
||||
set_uniform(m_uniforms.fog_constants, render_state->camera_fog);
|
||||
set_uniform(m_uniforms.hvdf_offset, render_state->camera_hvdf_off);
|
||||
}
|
||||
|
||||
void Shadow3::draw_debug_window() {}
|
||||
|
||||
void Shadow3::render_jak1(DmaFollower& dma,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
printf("Jak1 shadow render\n");
|
||||
m_did_first_time_setup = false;
|
||||
|
||||
while (dma.current_tag_offset() != render_state->next_bucket) {
|
||||
auto dmatag = dma.current_tag();
|
||||
auto data = dma.read_and_advance();
|
||||
int run_idx = 0;
|
||||
if (data.vifcode0().kind == VifCode::Kind::PC_PORT) {
|
||||
printf(" Run %d start\n", run_idx);
|
||||
u32 next = data.data_offset;
|
||||
while (next) {
|
||||
Jak1ShadowRequest game_request;
|
||||
memcpy(&game_request, g_ee_main_mem + next, sizeof(Jak1ShadowRequest));
|
||||
next = game_request.next;
|
||||
|
||||
char name[128];
|
||||
strncpy(name, (const char*)(g_ee_main_mem) + 4 + game_request.geo_name, 128);
|
||||
name[127] = 0;
|
||||
printf(" draw %s\n", name);
|
||||
|
||||
auto model = render_state->loader->get_shadow_model(name);
|
||||
if (!model) {
|
||||
printf(" SKIP: no model data\n");
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* (shdf00) ;; unused
|
||||
(disable-fade)
|
||||
(shdf02) ;; only set, never used.
|
||||
(shdf03)
|
||||
(shdf04) ;; unused
|
||||
(disable-draw)
|
||||
*/
|
||||
constexpr u32 kCullWhenUnderPlane = 1;
|
||||
constexpr u32 kDisableFade = 2;
|
||||
constexpr u32 kAbsolutePlanes = 4;
|
||||
constexpr u32 kFlag3 = 8;
|
||||
constexpr u32 kFlag4 = 16;
|
||||
constexpr u32 kDisableDraw = 32;
|
||||
|
||||
if (game_request.settings.flags & kDisableDraw) {
|
||||
printf(" SKIP: disable flag set\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (game_request.num_joints * 4 + m_next_free_bone_vector >= MAX_SHADER_BONE_VECTORS) {
|
||||
flush_requests(render_state, prof);
|
||||
}
|
||||
|
||||
if (m_next_request == m_requests.size()) {
|
||||
flush_requests(render_state, prof);
|
||||
}
|
||||
|
||||
LevelChain* chain = nullptr;
|
||||
for (auto& c : m_level_chains) {
|
||||
if (c.level == model->level || !c.level) {
|
||||
chain = &c;
|
||||
chain->level = model->level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!chain) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// grab the next request and link it to the chain for the level.
|
||||
auto& request = m_requests[m_next_request++];
|
||||
request.next = chain->head;
|
||||
chain->head = &request;
|
||||
|
||||
request.model = *model;
|
||||
// the origin of "light" for the shadow is found by starting at the "center" point
|
||||
// (somewhere in the model) and following the shadow direction backward.
|
||||
request.origin = game_request.settings.center +
|
||||
game_request.settings.shadow_dir * game_request.settings.dist_to_locus;
|
||||
|
||||
// copy bones to buffer
|
||||
constexpr int in_stride = 8 * 4 * sizeof(float);
|
||||
constexpr int out_stride = 4 * 4 * sizeof(float);
|
||||
constexpr int in_offset = 3 * in_stride;
|
||||
request.bone_idx = m_next_free_bone_vector;
|
||||
for (int i = 0; i < game_request.num_joints; i++) {
|
||||
memcpy(&m_shader_bone_vector_buffer[m_next_free_bone_vector].x(),
|
||||
g_ee_main_mem + game_request.mtx + in_offset + i * in_stride, out_stride);
|
||||
m_next_free_bone_vector += 4;
|
||||
}
|
||||
|
||||
// the clipping planes for the shadow
|
||||
request.top_plane = game_request.settings.top_plane;
|
||||
request.bottom_plane = game_request.settings.bot_plane;
|
||||
if (!(kAbsolutePlanes & game_request.settings.flags)) {
|
||||
printf("relative plane mode, base is %f, move by %f\n",
|
||||
-request.bottom_plane.w() / 4096.0, game_request.settings.center.y() / 4096.0);
|
||||
// in relative planes mode, the height of the plane is adjusted to be relative to the
|
||||
// height of the center, so the planes move and down with the model
|
||||
request.top_plane.w() -= game_request.settings.center.y();
|
||||
request.bottom_plane.w() -= game_request.settings.center.y();
|
||||
}
|
||||
|
||||
// skip drawing if the camera is below the lower clipping plane
|
||||
if (kCullWhenUnderPlane & game_request.settings.flags) {
|
||||
if (render_state->camera_pos.xyz().dot(request.bottom_plane.xyz()) +
|
||||
request.bottom_plane.w() <
|
||||
0) {
|
||||
printf(" SKIP: camera below lower clipping plane.\n");
|
||||
m_next_request--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// detect if the origin is below the clipping plane and if so, move it up.
|
||||
const float dot = request.bottom_plane.xyz().dot(request.origin);
|
||||
if (dot + request.bottom_plane.w() > 0) {
|
||||
printf(" the origin is below the clipping plane, moving it up.\n");
|
||||
printf(" center was %s\n", game_request.settings.center.to_string_aligned().c_str());
|
||||
printf(" dir was %s\n", game_request.settings.shadow_dir.to_string_aligned().c_str());
|
||||
printf(" locus %f\n", game_request.settings.dist_to_locus);
|
||||
printf(" bottom plane was %s\n",
|
||||
game_request.settings.bot_plane.to_string_aligned().c_str());
|
||||
printf(" adjusted bottom plane was %s\n",
|
||||
request.bottom_plane.to_string_aligned().c_str());
|
||||
printf(" abs flag %d\n", game_request.settings.flags & kAbsolutePlanes);
|
||||
|
||||
request.bottom_plane.w() = -dot;
|
||||
}
|
||||
|
||||
const auto& cam_rot = render_state->camera_rot;
|
||||
const auto& cam_pos = render_state->camera_pos;
|
||||
|
||||
request.light_dir = game_request.settings.shadow_dir;
|
||||
|
||||
// transform to camera frame
|
||||
auto rotate = [&](const math::Vector3f& in) {
|
||||
return (cam_rot[0] * in[0] + cam_rot[1] * in[1] + cam_rot[2] * in[2]).xyz();
|
||||
};
|
||||
|
||||
auto transform = [&](const math::Vector3f& in) {
|
||||
return (cam_rot[0] * in[0] + cam_rot[1] * in[1] + cam_rot[2] * in[2] + cam_rot[3]).xyz();
|
||||
};
|
||||
|
||||
auto rotate_plane = [&](const math::Vector4f& in) {
|
||||
auto xyz = rotate(in.xyz());
|
||||
return math::Vector4f(xyz.x(), xyz.y(), xyz.z(), in.w() - xyz.dot(cam_rot[3].xyz()));
|
||||
};
|
||||
|
||||
printf("plane offset before: %f\n",
|
||||
game_request.settings.center.dot(request.bottom_plane.xyz()) +
|
||||
request.bottom_plane.w());
|
||||
|
||||
request.light_dir = rotate(request.light_dir);
|
||||
request.top_plane = rotate_plane(request.top_plane);
|
||||
request.bottom_plane = rotate_plane(request.bottom_plane);
|
||||
request.origin = transform(request.origin);
|
||||
|
||||
printf("plane offset after: %f\n",
|
||||
transform(game_request.settings.center).dot(request.bottom_plane.xyz()) +
|
||||
request.bottom_plane.w());
|
||||
printf("rot3: %s\n", cam_rot[3].to_string_aligned().c_str());
|
||||
printf(" 2: %s\n", cam_pos.to_string_aligned().c_str());
|
||||
|
||||
// printf(" origin: %s\n", (request.origin / 4096.f).to_string_aligned().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flush_requests(render_state, prof);
|
||||
finish(render_state, prof);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
|
||||
struct Jak1ShadowSettings {
|
||||
math::Vector<float, 3> center;
|
||||
u32 flags;
|
||||
math::Vector<float, 3> shadow_dir;
|
||||
float dist_to_locus;
|
||||
math::Vector4f bot_plane;
|
||||
math::Vector4f top_plane;
|
||||
float fade_dist;
|
||||
float fade_start;
|
||||
s32 dummy2;
|
||||
s32 dummy3;
|
||||
};
|
||||
static_assert(sizeof(Jak1ShadowSettings) == 5 * 16);
|
||||
|
||||
struct Jak1ShadowRequest {
|
||||
u8 dma[16];
|
||||
Jak1ShadowSettings settings;
|
||||
u32 geo_name;
|
||||
u32 mtx;
|
||||
u32 num_joints;
|
||||
u32 next;
|
||||
};
|
||||
|
||||
class Shadow3 {
|
||||
public:
|
||||
Shadow3(ShaderLibrary& shaders);
|
||||
~Shadow3();
|
||||
void render_jak1(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void draw_debug_window();
|
||||
|
||||
private:
|
||||
struct ShadowRequest {
|
||||
ShadowRef model;
|
||||
math::Vector<float, 3> origin;
|
||||
math::Vector4f top_plane, bottom_plane;
|
||||
math::Vector3f light_dir;
|
||||
ShadowRequest* next = nullptr;
|
||||
u32 bone_idx = 0;
|
||||
};
|
||||
|
||||
struct LevelChain {
|
||||
const LevelData* level = nullptr;
|
||||
ShadowRequest* head = nullptr;
|
||||
};
|
||||
void flush_requests(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void first_time_setup(SharedRenderState* render_state);
|
||||
void setup_for_level(SharedRenderState* render_state, const LevelData* level_data);
|
||||
void draw_model(SharedRenderState* render_state,
|
||||
ShadowRequest* request,
|
||||
ScopedProfilerNode& prof);
|
||||
void finish(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
std::array<ShadowRequest, 128> m_requests;
|
||||
std::array<LevelChain, 16> m_level_chains;
|
||||
int m_next_request = 0;
|
||||
|
||||
static constexpr int MAX_SHADER_BONE_VECTORS = 1024 * 16; // ??
|
||||
math::Vector4f m_shader_bone_vector_buffer[MAX_SHADER_BONE_VECTORS];
|
||||
u32 m_next_free_bone_vector = 0;
|
||||
|
||||
struct {
|
||||
GLuint vao = -1;
|
||||
GLuint bones_buffer = -1;
|
||||
int buffer_alignment = 0;
|
||||
} m_opengl;
|
||||
|
||||
struct {
|
||||
GLuint hvdf_offset = 0;
|
||||
GLuint fog_constants = 0;
|
||||
GLuint perspective_matrix = 0;
|
||||
GLuint camera_rot = 0;
|
||||
GLuint debug_color = 0;
|
||||
GLuint bottom_plane = 0;
|
||||
GLuint top_plane = 0;
|
||||
GLuint origin = 0;
|
||||
GLuint bottom_cap = 0;
|
||||
} m_uniforms;
|
||||
bool m_did_first_time_setup = false;
|
||||
};
|
||||
@@ -152,6 +152,7 @@ void Loader::draw_debug_window() {
|
||||
lev.second->frames_since_last_used);
|
||||
ImGui::Text(" %d textures", (int)lev.second->textures.size());
|
||||
ImGui::Text(" %d merc", (int)lev.second->merc_model_lookup.size());
|
||||
ImGui::Text(" %d shadow", (int)lev.second->shadow_model_lookup.size());
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
@@ -267,14 +268,20 @@ const tfrag3::Level& Loader::load_common(TexturePool& tex_pool, const std::strin
|
||||
|
||||
Timer tim;
|
||||
MercLoaderStage mls;
|
||||
ShadowLoaderStage sls;
|
||||
LoaderInput input;
|
||||
input.tex_pool = &tex_pool;
|
||||
input.mercs = &m_all_merc_models;
|
||||
input.shadows = &m_all_shadow_models;
|
||||
input.lev_data = &m_common_level;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
done = mls.run(tim, input);
|
||||
}
|
||||
done = false;
|
||||
while (!done) {
|
||||
done = sls.run(tim, input);
|
||||
}
|
||||
return *m_common_level.level;
|
||||
}
|
||||
|
||||
@@ -416,6 +423,7 @@ void Loader::update(TexturePool& texture_pool) {
|
||||
LoaderInput loader_input;
|
||||
loader_input.lev_data = lev.get();
|
||||
loader_input.mercs = &m_all_merc_models;
|
||||
loader_input.shadows = &m_all_shadow_models;
|
||||
loader_input.tex_pool = &texture_pool;
|
||||
|
||||
for (auto& stage : m_loader_stages) {
|
||||
@@ -496,6 +504,8 @@ void Loader::update(TexturePool& texture_pool) {
|
||||
m_garbage_buffers.push_back(lev->collide_vertices);
|
||||
m_garbage_buffers.push_back(lev->merc_vertices);
|
||||
m_garbage_buffers.push_back(lev->merc_indices);
|
||||
m_garbage_buffers.push_back(lev->shadow_indices);
|
||||
m_garbage_buffers.push_back(lev->shadow_vertices);
|
||||
|
||||
for (auto& model : lev->level->merc_data.models) {
|
||||
auto& mercs = m_all_merc_models.at(model.name);
|
||||
@@ -505,6 +515,14 @@ void Loader::update(TexturePool& texture_pool) {
|
||||
mercs.erase(it);
|
||||
}
|
||||
|
||||
for (auto& model : lev->level->shadow_data.models) {
|
||||
auto& shadows = m_all_shadow_models.at(model.name);
|
||||
ShadowRef ref{&model, lev->load_id};
|
||||
auto it = std::find(shadows.begin(), shadows.end(), ref);
|
||||
ASSERT_MSG(it != shadows.end(), fmt::format("missing shadow: {}\n", model.name));
|
||||
shadows.erase(it);
|
||||
}
|
||||
|
||||
m_loaded_tfrag3_levels.erase(*to_unload);
|
||||
}
|
||||
}
|
||||
@@ -544,3 +562,14 @@ std::optional<MercRef> Loader::get_merc_model(const char* model_name) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ShadowRef> Loader::get_shadow_model(const char* model_name) {
|
||||
// don't think we need to lock here...
|
||||
const auto& it = m_all_shadow_models.find(model_name);
|
||||
if (it != m_all_shadow_models.end() && !it->second.empty()) {
|
||||
// it->second.front().parent_level->frames_since_last_used = 0;
|
||||
return it->second.front();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class Loader {
|
||||
void update_blocking(TexturePool& tex_pool);
|
||||
const LevelData* get_tfrag3_level(const std::string& level_name);
|
||||
std::optional<MercRef> get_merc_model(const char* model_name);
|
||||
std::optional<ShadowRef> get_shadow_model(const char* model_name);
|
||||
const tfrag3::Level& load_common(TexturePool& tex_pool, const std::string& name);
|
||||
void set_want_levels(const std::vector<std::string>& levels);
|
||||
void set_active_levels(const std::vector<std::string>& levels);
|
||||
@@ -52,6 +53,7 @@ class Loader {
|
||||
std::unordered_map<std::string, std::unique_ptr<LevelData>> m_loaded_tfrag3_levels;
|
||||
|
||||
std::unordered_map<std::string, std::vector<MercRef>> m_all_merc_models;
|
||||
std::unordered_map<std::string, std::vector<ShadowRef>> m_all_shadow_models;
|
||||
|
||||
std::vector<std::string> m_desired_levels;
|
||||
std::vector<std::string> m_active_levels;
|
||||
|
||||
@@ -689,6 +689,68 @@ bool MercLoaderStage::run(Timer& /*timer*/, LoaderInput& data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ShadowLoaderStage::ShadowLoaderStage() : LoaderStage("shadow") {}
|
||||
void ShadowLoaderStage::reset() {
|
||||
m_done = false;
|
||||
m_opengl = false;
|
||||
m_vtx_uploaded = false;
|
||||
m_idx = 0;
|
||||
}
|
||||
|
||||
bool ShadowLoaderStage::run(Timer& /*timer*/, LoaderInput& data) {
|
||||
if (m_done) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_opengl) {
|
||||
glGenBuffers(1, &data.lev_data->shadow_indices);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.lev_data->shadow_indices);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
data.lev_data->level->shadow_data.indices.size() * sizeof(u32), nullptr,
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, &data.lev_data->shadow_vertices);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
data.lev_data->level->shadow_data.vertices.size() * sizeof(tfrag3::ShadowVertex),
|
||||
nullptr, GL_STATIC_DRAW);
|
||||
m_opengl = true;
|
||||
}
|
||||
|
||||
if (!m_vtx_uploaded) {
|
||||
u32 start = m_idx;
|
||||
m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.indices.size());
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_indices);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, start * sizeof(u32), (m_idx - start) * sizeof(u32),
|
||||
data.lev_data->level->shadow_data.indices.data() + start);
|
||||
if (m_idx != data.lev_data->level->shadow_data.indices.size()) {
|
||||
return false;
|
||||
} else {
|
||||
m_idx = 0;
|
||||
m_vtx_uploaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
u32 start = m_idx;
|
||||
m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.vertices.size());
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, start * sizeof(tfrag3::ShadowVertex),
|
||||
(m_idx - start) * sizeof(tfrag3::ShadowVertex),
|
||||
data.lev_data->level->shadow_data.vertices.data() + start);
|
||||
|
||||
if (m_idx != data.lev_data->level->shadow_data.vertices.size()) {
|
||||
return false;
|
||||
} else {
|
||||
m_done = true;
|
||||
for (auto& model : data.lev_data->level->shadow_data.models) {
|
||||
data.lev_data->shadow_model_lookup[model.name] = &model;
|
||||
(*data.shadows)[model.name].push_back({&model, data.lev_data->load_id, data.lev_data});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<LoaderStage>> make_loader_stages() {
|
||||
std::vector<std::unique_ptr<LoaderStage>> ret;
|
||||
ret.push_back(std::make_unique<TieLoadStage>());
|
||||
@@ -697,6 +759,7 @@ std::vector<std::unique_ptr<LoaderStage>> make_loader_stages() {
|
||||
ret.push_back(std::make_unique<ShrubLoadStage>());
|
||||
ret.push_back(std::make_unique<CollideLoaderStage>());
|
||||
ret.push_back(std::make_unique<MercLoaderStage>());
|
||||
ret.push_back(std::make_unique<ShadowLoaderStage>());
|
||||
ret.push_back(std::make_unique<HfragLoaderStage>());
|
||||
ret.push_back(std::make_unique<StallLoaderStage>());
|
||||
return ret;
|
||||
|
||||
@@ -16,4 +16,17 @@ class MercLoaderStage : public LoaderStage {
|
||||
bool m_opengl = false;
|
||||
bool m_vtx_uploaded = false;
|
||||
u32 m_idx = 0;
|
||||
};
|
||||
|
||||
class ShadowLoaderStage : public LoaderStage {
|
||||
public:
|
||||
ShadowLoaderStage();
|
||||
bool run(Timer& timer, LoaderInput& data) override;
|
||||
void reset() override;
|
||||
|
||||
private:
|
||||
bool m_done = false;
|
||||
bool m_opengl = false;
|
||||
bool m_vtx_uploaded = false;
|
||||
u32 m_idx = 0;
|
||||
};
|
||||
@@ -28,6 +28,10 @@ struct LevelData {
|
||||
GLuint merc_indices;
|
||||
std::unordered_map<std::string, const tfrag3::MercModel*> merc_model_lookup;
|
||||
|
||||
GLuint shadow_vertices;
|
||||
GLuint shadow_indices;
|
||||
std::unordered_map<std::string, const tfrag3::ShadowModel*> shadow_model_lookup;
|
||||
|
||||
GLuint hfrag_vertices;
|
||||
GLuint hfrag_indices;
|
||||
|
||||
@@ -43,10 +47,20 @@ struct MercRef {
|
||||
}
|
||||
};
|
||||
|
||||
struct ShadowRef {
|
||||
const tfrag3::ShadowModel* model = nullptr;
|
||||
u64 load_id = 0;
|
||||
const LevelData* level = nullptr;
|
||||
bool operator==(const ShadowRef& other) const {
|
||||
return model == other.model && load_id == other.load_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct LoaderInput {
|
||||
LevelData* lev_data;
|
||||
TexturePool* tex_pool;
|
||||
std::unordered_map<std::string, std::vector<MercRef>>* mercs;
|
||||
std::unordered_map<std::string, std::vector<ShadowRef>>* shadows;
|
||||
};
|
||||
|
||||
class LoaderStage {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#version 410 core
|
||||
|
||||
out vec4 color;
|
||||
in vec4 vtx_color;
|
||||
|
||||
void main() {
|
||||
color = vtx_color;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#version 410 core
|
||||
|
||||
// merc vertex definition
|
||||
layout (location = 0) in vec3 position_in;
|
||||
layout (location = 1) in float weight_in;
|
||||
layout (location = 2) in uvec2 mats;
|
||||
layout (location = 3) in uint flags;
|
||||
|
||||
// camera control
|
||||
uniform vec4 hvdf_offset;
|
||||
uniform vec4 fog_constants;
|
||||
uniform mat4 perspective_matrix;
|
||||
uniform mat4 camera_rot;
|
||||
uniform vec3 debug_color;
|
||||
uniform vec4 bottom_plane;
|
||||
uniform vec4 top_plane;
|
||||
uniform vec3 origin;
|
||||
uniform bool bottom_cap;
|
||||
|
||||
// output
|
||||
out vec4 vtx_color;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
struct MercMatrixData {
|
||||
mat4 X;
|
||||
};
|
||||
|
||||
layout (std140) uniform ub_bones {
|
||||
MercMatrixData bones[128];
|
||||
};
|
||||
|
||||
/*
|
||||
- 0 `sub.xyzw vf19, vf01, vf03` : `vf19 = center - vert`
|
||||
- 1 `mul.xyzw vf11, vf03, vf02` : `vf11 = dot(vert, plane)`
|
||||
- 2 `mul.xyz vf15, vf19, vf02` : `vf15 = dot3(center - vert, plane)`
|
||||
- 3 `move.xyzw vf07, vf03` : `vf07 = vert`
|
||||
- 4 `addy.x vf11, vf11, vf11` : `vf11.x += vf11.y`
|
||||
- 5 `addy.x vf15, vf15, vf15` : `vf15.x += vf15.y`
|
||||
- 6 `addz.x vf11, vf11, vf11` : `vf11.x += vf11.z`
|
||||
- 7 `addz.x vf15, vf15, vf15` : `vf15.x += vf15.z`
|
||||
- 8 `addw.x vf11, vf11, vf11` : `vf11.x += vf11.w`
|
||||
- 9 `div Q, vf11.x, vf15.x` : `Q = dot(vert, plane) / dot3(center - vert, plane)`
|
||||
- 10 `mul.xyzw vf19, vf19, Q` :
|
||||
- 11 `sub.xyzw vf07, vf07, vf19`:
|
||||
*/
|
||||
|
||||
vec4 dual(vec4 p, vec4 plane) {
|
||||
vec4 offset = vec4(origin, 1) - p;
|
||||
return p - offset * dot(p, plane) / dot(offset.xyz, plane.xyz);
|
||||
}
|
||||
|
||||
vec4 scissor(vec4 p, vec4 plane) {
|
||||
float plane_offset = dot(p, plane);
|
||||
if (plane_offset > 0) {
|
||||
vec4 offset = vec4(origin, 1) - p;
|
||||
return p - offset * plane_offset / dot(offset.xyz, plane.xyz);
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 p = vec4(position_in, 1);
|
||||
|
||||
vec4 vtx_pos = -bones[mats[0] + offset].X * p * weight_in;
|
||||
|
||||
if (weight_in > 1) {
|
||||
vtx_pos += -bones[mats[1] + offset].X * p * (1.f - weight_in);
|
||||
}
|
||||
|
||||
|
||||
if (bottom_cap) {
|
||||
vtx_pos = dual(vtx_pos, bottom_plane);
|
||||
} else {
|
||||
if ((flags & uint(1)) != 0) {
|
||||
vtx_pos = dual(vtx_pos, bottom_plane);
|
||||
} else {
|
||||
vtx_pos = scissor(vtx_pos, top_plane);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vec4 transformed = perspective_matrix * vtx_pos;
|
||||
|
||||
float Q = fog_constants.x / transformed[3];
|
||||
|
||||
transformed.xyz *= Q;
|
||||
transformed.xyz += hvdf_offset.xyz;
|
||||
transformed.xy -= (2048.);
|
||||
transformed.z /= (8388608);
|
||||
transformed.z -= 1;
|
||||
transformed.x /= (256);
|
||||
transformed.y /= -(128);
|
||||
transformed.xyz *= transformed.w;
|
||||
transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE;
|
||||
gl_Position = transformed;
|
||||
|
||||
|
||||
vtx_color = vec4(debug_color, 1.0);
|
||||
}
|
||||
@@ -718,7 +718,10 @@
|
||||
(&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint s4-1)))
|
||||
(set! (-> a0-26 data 86 total) (-> a0-26 data 86 used)))))))
|
||||
(when #t
|
||||
(let ((v1-41 *shadow-queue*)) (+! (-> v1-41 cur-run) 1)))
|
||||
(let ((v1-41 *shadow-queue*)) (+! (-> v1-41 cur-run) 1))
|
||||
;; og:preserve-this
|
||||
(+! (-> *pc-shadow-queue* cur-run) 1)
|
||||
)
|
||||
0
|
||||
(none))
|
||||
|
||||
@@ -856,7 +859,10 @@
|
||||
(with-profiler "merc"
|
||||
(set! (-> *merc-global-array* count) (the-as uint 0))
|
||||
(set! *merc-globals* (the-as merc-globals (-> *merc-global-array* globals)))
|
||||
(set! (-> *shadow-queue* cur-run) (the-as uint 0)))
|
||||
(set! (-> *shadow-queue* cur-run) (the-as uint 0))
|
||||
;; og:preserve-this
|
||||
(set! (-> *pc-shadow-queue* cur-run) 0)
|
||||
)
|
||||
;; draw the background!
|
||||
(with-profiler "background"
|
||||
(init-background)
|
||||
|
||||
@@ -27,6 +27,27 @@
|
||||
|
||||
(defglobalconstant BACKWARD_COMPAT_MERC_CLIP #f)
|
||||
|
||||
;; when set, render some environment mapped stuff with jak 2's emerc.
|
||||
;; this is much faster, and does significantly speed up the game thread on finalboss.
|
||||
(define *emerc-hack* #t)
|
||||
|
||||
;; when set, use merc for blerc instead of generic.
|
||||
(define *blerc-hack* #t)
|
||||
|
||||
;; when true, uses the PC float blerc implementation.
|
||||
(define *use-fp-blerc* #t)
|
||||
|
||||
(define *texscroll-force-generic* #f)
|
||||
|
||||
(define *ripple-force-generic* #f)
|
||||
|
||||
;; when set, use the rewritten PC shadow render (faster)
|
||||
(define *use-pc-shadow* #f)
|
||||
|
||||
;; use rewritten bones math (GOAL asm instead of mips2c)
|
||||
(define *use-new-bones* #t)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
;; calc list
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
@@ -326,6 +347,10 @@
|
||||
(let ((v1-13 (-> gp-1 run (-> gp-1 cur-run))))
|
||||
(set! (-> v1-13 first) (the-as dma-packet 0))
|
||||
(set! (-> v1-13 next) (the-as (pointer dma-packet) 0))))
|
||||
;; og:preserve-this
|
||||
(set! (-> *pc-shadow-queue* run (-> *pc-shadow-queue* cur-run) first) (the pc-shadow-request 0))
|
||||
(set! (-> *pc-shadow-queue* run (-> *pc-shadow-queue* cur-run) next) (the (pointer pc-shadow-request) 0))
|
||||
|
||||
0
|
||||
(none))
|
||||
|
||||
@@ -464,7 +489,6 @@
|
||||
(.svf (&-> out n-mtx quad 2) nmat2))))
|
||||
(none))
|
||||
|
||||
(define *use-new-bones* #t)
|
||||
|
||||
(defun bones-mtx-calc-execute ()
|
||||
"Do all pending bone calculations"
|
||||
@@ -660,7 +684,83 @@
|
||||
(new 'static 'plane :y 1.0 :w 4096.0)
|
||||
:fade-dist 409600.0))
|
||||
|
||||
;; og:preserve-this
|
||||
(defun pc-draw-bones-shadow ((dc draw-control) (mtx pointer) (dma-ptr pointer))
|
||||
"Add shadows for this draw-control to the *pc-shadow-queue* to be drawn in pc-shadow-execute-all.
|
||||
This places a pc-shadow-request in the DMA buffer and adds it to the linked list of requests
|
||||
for the currently selected run in *pc-shadow-queue*"
|
||||
(let* ((pse (the pc-shadow-request dma-ptr))
|
||||
(sgeo (-> dc shadow))
|
||||
(settings (if (-> dc shadow-ctrl) (-> dc shadow-ctrl settings) *default-shadow-settings*))
|
||||
(flags (-> settings flags))
|
||||
)
|
||||
|
||||
;; if fade is enabled, and we're all the way faded out, disable draw
|
||||
(when (not (logtest? flags (shadow-flags disable-fade)))
|
||||
(let ((dist (-> (scratchpad-object terrain-context) work foreground bone-mem work distance w)))
|
||||
(if (< (-> settings fade-dist) dist)
|
||||
(logior! flags (shadow-flags disable-draw))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; if disabled, early return
|
||||
(if (logtest? flags (shadow-flags disable-draw))
|
||||
(return dma-ptr)
|
||||
)
|
||||
|
||||
|
||||
;; settings
|
||||
(mem-copy! (the pointer (-> pse settings)) (the pointer settings) (size-of shadow-settings))
|
||||
|
||||
;; update the "center" position.
|
||||
(let ((center-pos (-> dc skeleton bones (-> dc shadow-joint-index) position)))
|
||||
(set! (-> pse settings center x) (-> center-pos x))
|
||||
(set! (-> pse settings center y) (-> center-pos y))
|
||||
(set! (-> pse settings center z) (-> center-pos z))
|
||||
)
|
||||
|
||||
;; set the other properties
|
||||
(set! (-> pse geo-name) (-> sgeo name))
|
||||
(set! (-> pse mtx) mtx)
|
||||
(set! (-> pse num-joints) (-> sgeo header num-joints))
|
||||
|
||||
;; set up linked list.
|
||||
(let* ((run (-> *pc-shadow-queue* run (-> *pc-shadow-queue* cur-run)))
|
||||
(next (-> run next))
|
||||
)
|
||||
|
||||
;; if we're the first in the list, store in the run
|
||||
(if (zero? (-> run first)) (set! (-> run first) pse))
|
||||
|
||||
;; patch next pointer of previous
|
||||
(if (nonzero? next) (set! (-> next 0) pse))
|
||||
|
||||
;; remember where to patch for the next one
|
||||
(set! (-> run next) (&-> pse next))
|
||||
|
||||
;; clear our next pointer in case we're last
|
||||
(set! (-> pse next) (the pc-shadow-request 0))
|
||||
|
||||
;; set up next tag at the start, to skip over this data.
|
||||
;; this is a bit of a hack, this function gets called when building merc chains,
|
||||
;; and inserts a bit of shadow dma that will later be referenced by the shadow bucket.
|
||||
;; but the original game did the same thing!
|
||||
(&+! dma-ptr (size-of pc-shadow-request))
|
||||
(set! (-> pse dma-next dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (the-as int dma-ptr)))
|
||||
(set! (-> pse dma-next vif0) (new 'static 'vif-tag))
|
||||
(set! (-> pse dma-next vif1) (new 'static 'vif-tag))
|
||||
)
|
||||
|
||||
dma-ptr
|
||||
)
|
||||
)
|
||||
|
||||
(defun draw-bones-shadow ((arg0 draw-control) (arg1 pointer) (arg2 pointer))
|
||||
;; og:preserve-this
|
||||
(when *use-pc-shadow*
|
||||
(return (pc-draw-bones-shadow arg0 arg1 arg2))
|
||||
)
|
||||
;; (local-vars (ra-0 int))
|
||||
;; the dma packet we'll use for shadow in the end.
|
||||
(let* ((v1-0 (the-as dma-packet (&+ arg2 0)))
|
||||
@@ -1025,19 +1125,7 @@
|
||||
(set! dma-buf (the pointer (&+ packet 16))))
|
||||
dma-buf))
|
||||
|
||||
;; when set, render some environment mapped stuff with jak 2's emerc.
|
||||
;; this is much faster, and does significantly speed up the game thread on finalboss.
|
||||
(define *emerc-hack* #t)
|
||||
|
||||
;; when set, use merc for blerc instead of generic.
|
||||
(define *blerc-hack* #t)
|
||||
|
||||
;; when true, uses the PC float blerc implementation.
|
||||
(define *use-fp-blerc* #t)
|
||||
|
||||
(define *texscroll-force-generic* #f)
|
||||
|
||||
(define *ripple-force-generic* #f)
|
||||
|
||||
(defun draw-bones ((arg0 draw-control) (dma-buf dma-buffer) (arg2 float))
|
||||
"Main draw function for all bone-related renderers. Will set up merc, generic and shadow.
|
||||
|
||||
@@ -103,6 +103,35 @@
|
||||
|
||||
(define *shadow-queue* (new 'global 'shadow-queue))
|
||||
|
||||
;; og:preserve-this
|
||||
;; new shadow queue for drawing PC shadows.
|
||||
(declare-type pc-shadow-request structure)
|
||||
(deftype pc-shadow-request (structure)
|
||||
(
|
||||
(dma-next dma-packet :inline)
|
||||
(settings shadow-settings :inline)
|
||||
(geo-name string) ;; name to send to PC renderer
|
||||
(mtx pointer) ;; pointer to DMA memory that will contain bones
|
||||
(num-joints uint32) ;; number of joints needed for shadow
|
||||
(next pc-shadow-request)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(deftype pc-shadow-run (structure)
|
||||
((first pc-shadow-request)
|
||||
(next (pointer pc-shadow-request)))
|
||||
)
|
||||
|
||||
(deftype pc-shadow-queue (structure)
|
||||
((cur-run uint32)
|
||||
(run pc-shadow-run 16 :inline)
|
||||
)
|
||||
)
|
||||
|
||||
(define *pc-shadow-queue* (new 'global 'pc-shadow-queue))
|
||||
|
||||
|
||||
(deftype shadow-vertex (structure)
|
||||
((x float)
|
||||
(y float)
|
||||
|
||||
@@ -390,7 +390,38 @@
|
||||
0
|
||||
(none))
|
||||
|
||||
;; og:preserve-this
|
||||
(defun pc-shadow-execute-all ()
|
||||
"Send PC shadow queue to the PC shadow renderer."
|
||||
|
||||
;; bail if disabled
|
||||
(if (not (logtest? *vu1-enable-user* (vu1-renderer-mask shadow)))
|
||||
(return #f)
|
||||
)
|
||||
|
||||
;; TODO: plus1 here?
|
||||
(dotimes (run-idx (-> *pc-shadow-queue* cur-run))
|
||||
(when (nonzero? (-> *pc-shadow-queue* run run-idx first))
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> (current-frame) global-buf)) (bucket-id shadow))
|
||||
(dma-buffer-add-ref-vif2
|
||||
dma-buf
|
||||
6
|
||||
(-> *pc-shadow-queue* run run-idx first)
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port))
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(none)
|
||||
)
|
||||
|
||||
(defun shadow-execute-all ((arg0 dma-buffer) (arg1 shadow-queue))
|
||||
;; og:preserve-this
|
||||
(when *use-pc-shadow*
|
||||
(pc-shadow-execute-all)
|
||||
(return #f)
|
||||
)
|
||||
(if *debug-segment*
|
||||
(add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
|
||||
'draw
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"regex":
|
||||
["(with-gensyms|defenum|countdown|while|defglobalconstant|desfun|defsmacro|catch|defvar|defclass|defconstant|defcustom|defparameter|defconst|define-condition|define-modify-macro|",
|
||||
"defsetf|defun|defgeneric|define-setf-method|define-self-expander|defmacro|defsubst|deftype|defmethod|",
|
||||
"defpackage|defstruct|dolist|dotimes|lambda|let|let\\*|prog1|prog2|unless|when)$"]
|
||||
"defpackage|defstruct|dolist|dotimes|lambda|let|let\\*|prog1|prog2|unless|when|with-dma-buffer-add-bucket)$"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user