This commit is contained in:
water111
2025-06-15 18:26:56 -04:00
parent f5453b96e4
commit a06348fa9f
32 changed files with 2381 additions and 30 deletions
+32 -3
View File
@@ -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;
+32 -2
View File
@@ -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;
+1
View File
@@ -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
+2 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+1
View File
@@ -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;
+1
View File
@@ -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");
+1
View File
@@ -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);
}
+8 -2
View File
@@ -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)
+101 -13
View File
@@ -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)$"]
}
}
}