mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
71cb1aef6f
This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
460 lines
12 KiB
C++
460 lines
12 KiB
C++
#pragma once
|
|
|
|
// Data format for the tfrag3 renderer.
|
|
#include <array>
|
|
|
|
#include "common/common_types.h"
|
|
#include "common/dma/gs.h"
|
|
#include "common/math/Vector.h"
|
|
#include "common/util/Assert.h"
|
|
#include "common/util/Serializer.h"
|
|
|
|
namespace tfrag3 {
|
|
|
|
// NOTE:
|
|
// when updating any data structures in this file:
|
|
// - change the TFRAG3_VERSION
|
|
// - make sure to update the serialize function
|
|
// - 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.
|
|
|
|
enum MemoryUsageCategory {
|
|
TEXTURE,
|
|
|
|
TIE_DEINST_VIS,
|
|
TIE_DEINST_INDEX,
|
|
TIE_INST_VIS,
|
|
TIE_INST_INDEX,
|
|
TIE_BVH,
|
|
TIE_VERTS,
|
|
TIE_TIME_OF_DAY,
|
|
TIE_WIND_INSTANCE_INFO,
|
|
|
|
TIE_CIDX,
|
|
TIE_MATRICES,
|
|
TIE_GRPS,
|
|
|
|
TFRAG_VIS,
|
|
TFRAG_INDEX,
|
|
TFRAG_VERTS,
|
|
TFRAG_CLUSTER,
|
|
TFRAG_TIME_OF_DAY,
|
|
TFRAG_BVH,
|
|
|
|
SHRUB_TIME_OF_DAY,
|
|
SHRUB_VERT,
|
|
SHRUB_IND,
|
|
SHRUB_DRAW,
|
|
|
|
MERC_VERT,
|
|
MERC_INDEX,
|
|
MERC_DRAW,
|
|
|
|
MERC_MOD_DRAW_1,
|
|
MERC_MOD_DRAW_2,
|
|
MERC_MOD_VERT,
|
|
MERC_MOD_IND,
|
|
MERC_MOD_TABLE,
|
|
|
|
COLLISION,
|
|
|
|
NUM_CATEGORIES
|
|
};
|
|
|
|
struct MemoryUsageTracker {
|
|
u32 data[MemoryUsageCategory::NUM_CATEGORIES];
|
|
|
|
MemoryUsageTracker() {
|
|
for (auto& x : data) {
|
|
x = 0;
|
|
}
|
|
}
|
|
|
|
void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; }
|
|
};
|
|
|
|
constexpr int TFRAG3_VERSION = 24;
|
|
|
|
// 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_unused;
|
|
// color table index
|
|
u16 color_index;
|
|
u16 pad[3];
|
|
|
|
struct hash {
|
|
std::size_t operator()(const PreloadedVertex& x) const;
|
|
};
|
|
|
|
bool operator==(const PreloadedVertex& other) const {
|
|
return x == other.x && y == other.y && z == other.z && s == other.s && t == other.t &&
|
|
color_index == other.color_index;
|
|
}
|
|
};
|
|
static_assert(sizeof(PreloadedVertex) == 32, "PreloadedVertex size");
|
|
|
|
struct PackedTieVertices {
|
|
struct Vertex {
|
|
float x, y, z;
|
|
float s, t;
|
|
};
|
|
|
|
struct MatrixGroup {
|
|
s32 matrix_idx;
|
|
u32 start_vert;
|
|
u32 end_vert;
|
|
};
|
|
|
|
std::vector<u16> color_indices;
|
|
std::vector<std::array<math::Vector4f, 4>> matrices;
|
|
std::vector<MatrixGroup> matrix_groups; // todo pack
|
|
std::vector<Vertex> vertices;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
struct PackedTfragVertices {
|
|
struct Vertex {
|
|
u16 xoff, yoff, zoff;
|
|
u16 cluster_idx;
|
|
s16 s, t;
|
|
u16 color_index;
|
|
};
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
std::vector<Vertex> vertices;
|
|
std::vector<math::Vector<u16, 3>> cluster_origins;
|
|
};
|
|
|
|
struct ShrubGpuVertex {
|
|
float x, y, z;
|
|
float s, t;
|
|
u32 pad0;
|
|
u16 color_index;
|
|
u16 pad1;
|
|
u8 rgba_base[3];
|
|
u8 pad2;
|
|
};
|
|
static_assert(sizeof(ShrubGpuVertex) == 32, "ShrubGpuVertex size");
|
|
|
|
struct PackedShrubVertices {
|
|
struct Vertex {
|
|
float x, y, z;
|
|
float s, t;
|
|
u8 rgba[3];
|
|
};
|
|
|
|
struct InstanceGroup {
|
|
s32 matrix_idx;
|
|
u32 start_vert;
|
|
u32 end_vert;
|
|
u16 color_index;
|
|
};
|
|
std::vector<std::array<math::Vector4f, 4>> matrices;
|
|
std::vector<InstanceGroup> instance_groups; // todo pack
|
|
std::vector<Vertex> vertices;
|
|
u32 total_vertex_count;
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
// Settings for drawing a group of triangle strips.
|
|
// This refers to a group of PreloadedVertices that are already uploaded.
|
|
// All triangles here are drawn in the same "mode" (blending, texture, etc)
|
|
// The vertex index list is chunked by visibility group.
|
|
// You can just memcpy the entire list to draw everything, or iterate through visgroups and
|
|
// check visibility.
|
|
struct StripDraw {
|
|
DrawMode mode; // the OpenGL draw settings.
|
|
u32 tree_tex_id = 0; // the texture that should be bound for the draw
|
|
|
|
struct {
|
|
u32 idx_of_first_idx_in_full_buffer = 0;
|
|
} unpacked;
|
|
|
|
// indices can be specified as lists of runs and plain indices.
|
|
// the runs are still drawn with indexed opengl calls, it just uses less space in the file.
|
|
struct VertexRun {
|
|
u32 vertex0;
|
|
u16 length;
|
|
};
|
|
std::vector<VertexRun> runs;
|
|
std::vector<u32> plain_indices;
|
|
|
|
// to do culling, the above vertex stream is grouped.
|
|
// by following the visgroups and checking the visibility, you can leave out invisible vertices.
|
|
struct VisGroup {
|
|
u32 num_inds = 0; // number of vertex indices in this group
|
|
u32 num_tris = 0; // number of triangles
|
|
u32 vis_idx_in_pc_bvh = 0; // the visibility group they belong to (in BVH)
|
|
};
|
|
std::vector<VisGroup> vis_groups;
|
|
|
|
// for debug counting.
|
|
u32 num_triangles = 0;
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
struct ShrubDraw {
|
|
DrawMode mode; // the OpenGL draw settings.
|
|
u32 tree_tex_id = 0; // the texture that should be bound for the draw
|
|
|
|
u32 first_index_index;
|
|
u32 num_indices;
|
|
|
|
// for debug counting.
|
|
u32 num_triangles = 0;
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
struct InstancedStripDraw {
|
|
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. This includes the restart code of UINT32_MAX that OpenGL
|
|
// will use to start a new strip.
|
|
std::vector<u32> vertex_index_stream;
|
|
|
|
// the vertex stream above is segmented by instance.
|
|
struct InstanceGroup {
|
|
u32 num = 0; // number of vertex indices in this group
|
|
u32 instance_idx = 0; // the instance they belong to
|
|
u32 vis_idx = 0;
|
|
};
|
|
std::vector<InstanceGroup> instance_groups;
|
|
|
|
// for debug counting.
|
|
u32 num_triangles = 0;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
// node in the BVH.
|
|
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.
|
|
u16 my_id = 0xffff;
|
|
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 leaf.
|
|
};
|
|
|
|
// The leaf nodes don't actually exist in the vector of VisNodes, but instead they are ID's used
|
|
// by the actual geometry. Currently we do not include the bspheres of these, but this might be
|
|
// worth it if we have a more performant culling algorithm.
|
|
struct BVH {
|
|
std::vector<VisNode> vis_nodes; // bvh for frustum culling
|
|
// additional information about the BVH
|
|
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);
|
|
};
|
|
|
|
// A time-of-day color. Each stores 8 colors. At a given "time of day", they are interpolated
|
|
// to find a single color which goes into a color palette.
|
|
struct TimeOfDayColor {
|
|
math::Vector<u8, 4> rgba[8];
|
|
|
|
bool operator==(const TimeOfDayColor& other) const {
|
|
for (size_t i = 0; i < 8; i++) {
|
|
if (rgba[i] != other.rgba[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// A single texture. Stored as RGBA8888.
|
|
struct Texture {
|
|
u16 w, h;
|
|
u32 combo_id = 0;
|
|
std::vector<u32> data;
|
|
std::string debug_name;
|
|
std::string debug_tpage_name;
|
|
bool load_to_pool = false;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
// Tfrag trees have several kinds:
|
|
enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, WATER, INVALID };
|
|
|
|
constexpr const char* tfrag_tree_names[] = {"normal", "trans", "dirt", "ice",
|
|
"lowres", "lowres-trans", "water", "invalid"};
|
|
|
|
// A tfrag model
|
|
struct TfragTree {
|
|
TFragmentTreeKind kind; // our tfrag kind
|
|
std::vector<StripDraw> draws; // the actual topology and settings
|
|
PackedTfragVertices packed_vertices;
|
|
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)
|
|
BVH bvh; // the bvh for frustum culling
|
|
bool use_strips = true;
|
|
|
|
struct {
|
|
std::vector<PreloadedVertex> vertices; // mesh vertices
|
|
std::vector<u32> indices;
|
|
} unpacked;
|
|
void unpack();
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
struct TieWindInstance {
|
|
std::array<math::Vector4f, 4> matrix;
|
|
u16 wind_idx;
|
|
float stiffness;
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
// A tie model
|
|
struct TieTree {
|
|
BVH bvh;
|
|
std::vector<StripDraw> static_draws; // the actual topology and settings
|
|
|
|
PackedTieVertices packed_vertices;
|
|
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)
|
|
|
|
std::vector<InstancedStripDraw> instanced_wind_draws;
|
|
std::vector<TieWindInstance> wind_instance_info;
|
|
|
|
struct {
|
|
std::vector<PreloadedVertex> vertices; // mesh vertices
|
|
std::vector<u32> indices;
|
|
} unpacked;
|
|
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
void unpack();
|
|
};
|
|
|
|
struct ShrubTree {
|
|
// todo some visibility structure
|
|
std::vector<TimeOfDayColor> time_of_day_colors; // multiplier colors
|
|
|
|
PackedShrubVertices packed_vertices;
|
|
std::vector<ShrubDraw> static_draws; // the actual topology and settings
|
|
std::vector<u32> indices;
|
|
|
|
struct {
|
|
std::vector<ShrubGpuVertex> vertices; // mesh vertices
|
|
} unpacked;
|
|
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
void unpack();
|
|
};
|
|
|
|
struct CollisionMesh {
|
|
struct Vertex {
|
|
float x, y, z;
|
|
u32 flags;
|
|
s16 nx, ny, nz;
|
|
u16 pad;
|
|
u32 pat;
|
|
u32 pad2;
|
|
};
|
|
static_assert(sizeof(Vertex) == 32);
|
|
std::vector<Vertex> vertices;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
// MERC
|
|
|
|
struct MercVertex {
|
|
float pos[3];
|
|
float pad0;
|
|
|
|
float normal[3];
|
|
float pad1;
|
|
|
|
float weights[3];
|
|
float pad2;
|
|
|
|
float st[2];
|
|
|
|
u8 rgba[4];
|
|
u8 mats[3];
|
|
u8 pad3;
|
|
};
|
|
static_assert(sizeof(MercVertex) == 64);
|
|
|
|
struct MercDraw {
|
|
DrawMode mode;
|
|
u32 tree_tex_id = 0; // the texture that should be bound for the draw
|
|
|
|
u32 first_index;
|
|
u32 index_count;
|
|
u32 num_triangles;
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
struct MercModifiableDrawGroup {
|
|
std::vector<MercVertex> vertices;
|
|
std::vector<u16> vertex_lump4_addr;
|
|
std::vector<MercDraw> fix_draw, mod_draw;
|
|
std::vector<u8> fragment_mask;
|
|
u32 expect_vidx_end = 0;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
struct MercEffect {
|
|
std::vector<MercDraw> all_draws;
|
|
MercModifiableDrawGroup mod;
|
|
DrawMode envmap_mode;
|
|
u32 envmap_texture;
|
|
bool has_envmap = false;
|
|
bool has_mod_draw = false;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
struct MercModel {
|
|
std::string name;
|
|
std::vector<MercEffect> effects;
|
|
u32 max_draws;
|
|
u32 max_bones;
|
|
u32 st_vif_add;
|
|
float xyz_scale;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
struct MercModelGroup {
|
|
std::vector<MercVertex> vertices;
|
|
std::vector<u32> indices;
|
|
std::vector<MercModel> models;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
//
|
|
|
|
constexpr int TFRAG_GEOS = 3;
|
|
constexpr int TIE_GEOS = 4;
|
|
|
|
struct Level {
|
|
u16 version = TFRAG3_VERSION;
|
|
std::string level_name;
|
|
std::vector<Texture> textures;
|
|
std::array<std::vector<TfragTree>, TFRAG_GEOS> tfrag_trees;
|
|
std::array<std::vector<TieTree>, TIE_GEOS> tie_trees;
|
|
std::vector<ShrubTree> shrub_trees;
|
|
CollisionMesh collision;
|
|
MercModelGroup merc_data;
|
|
u16 version2 = TFRAG3_VERSION;
|
|
void serialize(Serializer& ser);
|
|
void memory_usage(MemoryUsageTracker* tracker) const;
|
|
};
|
|
|
|
void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size);
|
|
|
|
} // namespace tfrag3
|