Skip to content

Commit

Permalink
[graphics] tfrag3 renderer (#978)
Browse files Browse the repository at this point in the history
* begin work

* work

* working objs

* exporting

* it works

* before some time of day fixes

* add time of day interp and also fix zbuffer

* some small blending fixes

* improve randomess

* clean up extraction and missing blend mode

* culling, time of day, more level fixes

* more cleanup

* cleanup memory usage

* windows fix
  • Loading branch information
water111 authored Dec 4, 2021
1 parent 0d7b7da commit 0832029
Show file tree
Hide file tree
Showing 92 changed files with 5,533 additions and 666 deletions.
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

0 comments on commit 0832029

Please sign in to comment.