Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[graphics] tfrag3 renderer #978

Merged
merged 16 commits into from
Dec 4, 2021
Merged
2 changes: 2 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_library(common
audio/audio_formats.cpp
cross_os_debug/xdbg.cpp
cross_sockets/xsocket.cpp
custom_data/TFrag3Data.cpp
dma/dma.cpp
dma/dma_copy.cpp
dma/gs.cpp
Expand Down Expand Up @@ -32,6 +33,7 @@ add_library(common
util/FileUtil.cpp
util/json_util.cpp
util/Timer.cpp
util/os.cpp
util/print_float.cpp
util/FontUtils.cpp
util/image_loading.cpp)
Expand Down
81 changes: 81 additions & 0 deletions common/custom_data/TFrag3Data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "Tfrag3Data.h"
#include "common/util/assert.h"

namespace tfrag3 {

void Draw::serialize(Serializer& ser) {
ser.from_ptr(&mode);
ser.from_ptr(&tree_tex_id);
ser.from_pod_vector(&vertex_index_stream);
ser.from_pod_vector(&vis_groups);
ser.from_ptr(&num_triangles);
}

void Tree::serialize(Serializer& ser) {
ser.from_ptr(&kind);

if (ser.is_saving()) {
ser.save<size_t>(draws.size());
} else {
draws.resize(ser.load<size_t>());
}
for (auto& draw : draws) {
draw.serialize(ser);
}

ser.from_pod_vector(&vertices);
ser.from_pod_vector(&color_indices_per_vertex);
ser.from_pod_vector(&vis_nodes);
ser.from_pod_vector(&colors);
ser.from_ptr(&first_leaf_node);
ser.from_ptr(&last_leaf_node);
ser.from_ptr(&first_root);
ser.from_ptr(&num_roots);
ser.from_ptr(&only_children);
}

void Texture::serialize(Serializer& ser) {
ser.from_ptr(&w);
ser.from_ptr(&h);
ser.from_pod_vector(&data);
ser.from_str(&debug_name);
ser.from_str(&debug_tpage_name);
}

void Level::serialize(Serializer& ser) {
ser.from_ptr(&version);
if (ser.is_loading() && version != TFRAG3_VERSION) {
fmt::print("version mismatch when loading tfrag3 data. Got {}, expected {}\n", version,
TFRAG3_VERSION);
assert(false);
}

ser.from_str(&level_name);

if (ser.is_saving()) {
ser.save<size_t>(textures.size());
} else {
textures.resize(ser.load<size_t>());
}
for (auto& tex : textures) {
tex.serialize(ser);
}

if (ser.is_saving()) {
ser.save<size_t>(trees.size());
} else {
trees.resize(ser.load<size_t>());
}
for (auto& tree : trees) {
tree.serialize(ser);
}

ser.from_ptr(&version2);
if (ser.is_loading() && version2 != TFRAG3_VERSION) {
fmt::print("version mismatch when loading tfrag3 data (at end). Got {}, expected {}\n",
version2, TFRAG3_VERSION);
assert(false);
}
}

} // namespace tfrag3
99 changes: 99 additions & 0 deletions common/custom_data/Tfrag3Data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#pragma once

// Data format for the tfrag3 renderer.

#include "common/common_types.h"
#include "common/util/assert.h"
#include "common/dma/gs.h"
#include "common/util/Serializer.h"
#include "common/math/Vector.h"

namespace tfrag3 {

constexpr int TFRAG3_VERSION = 6;

// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
// the vertex position
float x, y, z;
// texture coordinates
float s, t, q;
// currently unused, color table indices.
u16 color_index;
u16 pad[3];
};
static_assert(sizeof(PreloadedVertex) == 32, "PreloadedVertex size");

// Settings for an OpenGL draw
struct Draw {
DrawMode mode; // the OpenGL draw settings.
u32 tree_tex_id = 0; // the texture that should be bound for the draw

// the list of vertices in the draw.
std::vector<u32> vertex_index_stream;

// to do culling, the above vertex stream is grouped.
// by following the visgroups and checking the visibility of the tfrag_idx, you can leave out
// invisible vertices.
struct VisGroup {
u32 num = 0;
u32 tfrag_idx = 0;
};
std::vector<VisGroup> vis_groups;
u32 num_triangles = 0;

void serialize(Serializer& ser);
};

struct VisNode {
math::Vector<float, 4> bsphere; // the bounding sphere, in meters (4096 = 1 game meter). w = rad
u16 child_id = 0xffff; // the ID of our first child.
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a Tfrag.
};

enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, INVALID };

constexpr const char* tfrag_tree_names[] = {"normal", "trans", "dirt", "ice",
"lowres", "lowres-trans", "invalid"};

struct TimeOfDayColor {
math::Vector<u8, 4> rgba[8];
};

struct Tree {
TFragmentTreeKind kind;
std::vector<Draw> draws;
std::vector<u16> color_indices_per_vertex;
std::vector<VisNode> vis_nodes;
std::vector<PreloadedVertex> vertices;
std::vector<TimeOfDayColor> colors;
u16 first_leaf_node = 0;
u16 last_leaf_node = 0;
u16 first_root = 0;
u16 num_roots = 0;
bool only_children = false;

void serialize(Serializer& ser);
};

struct Texture {
u16 w, h;
u32 combo_id = 0;
std::vector<u32> data;
std::string debug_name;
std::string debug_tpage_name;

void serialize(Serializer& ser);
};

struct Level {
u16 version = TFRAG3_VERSION;
std::string level_name;
std::vector<Texture> textures;
std::vector<Tree> trees;
u16 version2 = TFRAG3_VERSION;
void serialize(Serializer& ser);
};

} // namespace tfrag3
75 changes: 75 additions & 0 deletions common/dma/gs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,4 +356,79 @@ std::string GsTex0::print() const {

std::string GsPrim::print() const {
return fmt::format("0x{:x}, kind {}\n", data, kind());
}

std::string DrawMode::to_string() const {
std::string result;
result += fmt::format(" depth-write: {}\n", get_depth_write_enable());
result += fmt::format(" depth-test: ");
switch (get_depth_test()) {
case GsTest::ZTest::NEVER:
result += "never\n";
break;
case GsTest::ZTest::GEQUAL:
result += "gequal\n";
break;
case GsTest::ZTest::ALWAYS:
result += "always\n";
break;
case GsTest::ZTest::GREATER:
result += "greater\n";
break;
default:
assert(false);
}
result += fmt::format(" alpha: ");
switch (get_alpha_blend()) {
case AlphaBlend::SRC_0_SRC_DST:
result += "src, 0, src, dst\n";
break;
case AlphaBlend::SRC_DST_SRC_DST:
result += "src, dst, src, dst\n";
break;
case AlphaBlend::DISABLED:
result += "disabled\n";
break;
default:
assert(false);
}
result += fmt::format(" clamp: s {} t {}\n", get_clamp_s_enable(), get_clamp_t_enable());
result += fmt::format(" filt: {}\n", get_filt_enable());
result += fmt::format(" tcc: {}\n", get_tcc_enable());
result += fmt::format(" aref: {}\n", get_aref());
result += fmt::format(" ate: {}\n", get_at_enable());
result += fmt::format(" atst: ");
switch (get_alpha_test()) {
case AlphaTest::ALWAYS:
result += "always\n";
break;
case AlphaTest::GEQUAL:
result += "gequal\n";
break;
case AlphaTest::NEVER:
result += "never\n";
break;
default:
assert(false);
}
result += fmt::format(" zte: {}\n", get_zt_enable());
result += fmt::format(" abe: {}\n", get_ab_enable());
result += fmt::format(" afail: ");
switch (get_alpha_fail()) {
case GsTest::AlphaFail::KEEP:
result += "keep\n";
break;
case GsTest::AlphaFail::FB_ONLY:
result += "fb-only\n";
break;
case GsTest::AlphaFail::RGB_ONLY:
result += "rgb-only\n";
break;
case GsTest::AlphaFail::ZB_ONLY:
result += "zb-only\n";
break;
default:
assert(false);
}
return result;
}
Loading