mirror of
https://github.com/open-goal/jak-project
synced 2026-08-15 12:59:39 -04:00
301 lines
8.7 KiB
C++
301 lines
8.7 KiB
C++
#pragma once
|
|
|
|
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
|
#include "game/graphics/opengl_renderer/DirectRenderer.h"
|
|
#include "common/dma/gs.h"
|
|
#include "common/math/Vector.h"
|
|
|
|
using math::Matrix4f;
|
|
using math::Vector4f;
|
|
|
|
/*!
|
|
* GOAL sprite-frame-data, all the data that's uploaded once per frame for the sprite system.
|
|
*/
|
|
struct SpriteFrameData {
|
|
Vector4f xy_array[8];
|
|
Vector4f st_array[4];
|
|
Vector4f xyz_array[4];
|
|
Vector4f hmge_scale;
|
|
float pfog0;
|
|
float deg_to_rad;
|
|
float min_scale;
|
|
float inv_area;
|
|
GifTag adgif_giftag;
|
|
GifTag sprite_2d_giftag;
|
|
GifTag sprite_2d_giftag2;
|
|
Vector4f sincos[5];
|
|
Vector4f basis_x;
|
|
Vector4f basis_y;
|
|
GifTag sprite_3d_giftag;
|
|
AdGifData screen_shader;
|
|
GifTag clipped_giftag;
|
|
Vector4f inv_hmge_scale;
|
|
Vector4f stq_offset;
|
|
Vector4f stq_scale;
|
|
Vector4f rgba_plain;
|
|
GifTag warp_giftag;
|
|
float fog_min;
|
|
float fog_max;
|
|
float max_scale;
|
|
float bonus;
|
|
};
|
|
|
|
/*!
|
|
* "Matrix Data" for 3D sprites. This is shared for all 3D sprites
|
|
*/
|
|
struct Sprite3DMatrixData {
|
|
Matrix4f camera;
|
|
Vector4f hvdf_offset;
|
|
};
|
|
|
|
/*!
|
|
* "Matrix Data" for 2D screen space sprites. These are shared for all 2D HUD sprites
|
|
*/
|
|
struct SpriteHudMatrixData {
|
|
Matrix4f matrix;
|
|
|
|
// the "matrix" field is an index into these 76 quadwords
|
|
Vector4f hvdf_offset;
|
|
Vector4f user_hvdf[75];
|
|
};
|
|
|
|
/*!
|
|
* The "vector data" (sprite-vec-data-2d). Each sprite has its own vector data.
|
|
*/
|
|
struct SpriteVecData2d {
|
|
Vector4f xyz_sx; // position + x scale
|
|
Vector4f flag_rot_sy; // flags, rotation, and scale y
|
|
Vector4f rgba; // color
|
|
|
|
float sx() const { return xyz_sx.w(); }
|
|
|
|
// for HUD, this is the hvdf offset index
|
|
s32 flag() {
|
|
s32 result;
|
|
memcpy(&result, &flag_rot_sy.x(), sizeof(s32));
|
|
return result;
|
|
}
|
|
|
|
// unused for HUD
|
|
s32 matrix() {
|
|
s32 result;
|
|
memcpy(&result, &flag_rot_sy.y(), sizeof(s32));
|
|
return result;
|
|
}
|
|
|
|
// rotation in degrees
|
|
float rot() const { return flag_rot_sy.z(); }
|
|
|
|
// scale y.
|
|
float sy() const { return flag_rot_sy.w(); }
|
|
};
|
|
static_assert(sizeof(SpriteVecData2d) == 48);
|
|
|
|
/*!
|
|
* The layout of VU1 data memory, in quadword addresses
|
|
* The lower 800 qw's hold two buffers for double buffering drawing/loading.
|
|
*/
|
|
enum SpriteDataMem {
|
|
// these three can have an offset of 0 or 400 depending on which buffer
|
|
Header = 0, // number of sprites (updated per chunk)
|
|
Vector = 1, // vector data (updated per chunk)
|
|
Adgif = 145, // adgifs (updated per chunk)
|
|
|
|
// offset of first buffer
|
|
Buffer0 = 0,
|
|
// offset of second buffer
|
|
Buffer1 = 400,
|
|
|
|
GiftagBuilding = 800, // used to store gs packets for xgkicking
|
|
// matrix data (different depending on group)
|
|
Matrix = 900,
|
|
// frame data (same for the whole frame)
|
|
FrameData = 980
|
|
};
|
|
|
|
/*!
|
|
* The GS packet built by the sprite renderer.
|
|
*/
|
|
struct SpriteHud2DPacket {
|
|
GifTag adgif_giftag; // starts the adgif shader. 0
|
|
AdGifData user_adgif; // the adgif shader 16
|
|
GifTag sprite_giftag; // 96
|
|
math::Vector<s32, 4> color;
|
|
Vector4f st0;
|
|
math::Vector<s32, 4> xy0;
|
|
Vector4f st1;
|
|
math::Vector<s32, 4> xy1;
|
|
Vector4f st2;
|
|
math::Vector<s32, 4> xy2;
|
|
Vector4f st3;
|
|
math::Vector<s32, 4> xy3;
|
|
};
|
|
|
|
/*!
|
|
* The layout of VU1 code memory
|
|
*/
|
|
enum SpriteProgMem {
|
|
Init = 0, // the sprite initialization program. runs once per frame.
|
|
Sprites2dGrp0 = 3, // world space 2d sprites
|
|
Sprites2dHud = 109, // hud sprites
|
|
Sprites3d = 211 // 3d sprites
|
|
};
|
|
|
|
static_assert(offsetof(SpriteFrameData, hmge_scale) == 256);
|
|
static_assert(sizeof(SpriteFrameData) == 0x290, "SpriteFrameData size");
|
|
|
|
class SpriteRenderer : public BucketRenderer {
|
|
public:
|
|
SpriteRenderer(const std::string& name, BucketId my_id);
|
|
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
|
|
void draw_debug_window() override;
|
|
static constexpr int SPRITES_PER_CHUNK = 48;
|
|
|
|
private:
|
|
void render_distorter(DmaFollower& dma,
|
|
SharedRenderState* render_state,
|
|
ScopedProfilerNode& prof);
|
|
void handle_sprite_frame_setup(DmaFollower& dma);
|
|
void render_3d(DmaFollower& dma);
|
|
void render_2d_group0(DmaFollower& dma,
|
|
SharedRenderState* render_state,
|
|
ScopedProfilerNode& prof);
|
|
void render_fake_shadow(DmaFollower& dma);
|
|
void render_2d_group1(DmaFollower& dma,
|
|
SharedRenderState* render_state,
|
|
ScopedProfilerNode& prof);
|
|
void do_2d_group1_block_cpu(u32 count, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
void do_2d_group0_block_cpu(u32 count, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
void do_3d_block_cpu(u32 count, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
|
|
void handle_tex0(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
void handle_tex1(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
// void handle_mip(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
void handle_clamp(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
void handle_alpha(u64 val, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
|
|
void update_gl_prim(SharedRenderState* render_state);
|
|
void update_gl_texture(SharedRenderState* render_state, int unit);
|
|
void render_verts(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
|
|
|
u8 m_sprite_distorter_setup[7 * 16]; // direct data
|
|
u8 m_sprite_direct_setup[3 * 16];
|
|
SpriteFrameData m_frame_data; // qwa: 980
|
|
Sprite3DMatrixData m_3d_matrix_data;
|
|
SpriteHudMatrixData m_hud_matrix_data;
|
|
|
|
SpriteVecData2d m_vec_data_2d[SPRITES_PER_CHUNK];
|
|
AdGifData m_adgif[SPRITES_PER_CHUNK];
|
|
|
|
struct DebugStats {
|
|
int blocks_2d_grp0 = 0;
|
|
int count_2d_grp0 = 0;
|
|
int blocks_2d_grp1 = 0;
|
|
int count_2d_grp1 = 0;
|
|
} m_debug_stats;
|
|
|
|
bool m_extra_debug = false;
|
|
bool m_3d_debug = false;
|
|
|
|
bool m_2d_enable = true;
|
|
bool m_3d_enable = true;
|
|
|
|
struct SpriteVertex3D {
|
|
math::Vector4f xyz_sx; // position + x scale
|
|
math::Vector4f quat_sy; // quaternion + y scale
|
|
math::Vector4f rgba; // color
|
|
math::Vector<u16, 4> flags_matrix; // flags + matrix... split
|
|
u32 vert_id;
|
|
math::Vector<u8, 4> tex_info;
|
|
};
|
|
static_assert(sizeof(SpriteVertex3D) == 64);
|
|
|
|
std::vector<SpriteVertex3D> m_vertices_3d;
|
|
|
|
struct {
|
|
GLuint vertex_buffer;
|
|
GLuint vao;
|
|
u32 vertex_buffer_bytes = 0;
|
|
u32 vertex_buffer_max_verts = 0;
|
|
} m_ogl;
|
|
|
|
int m_sprite_offset = 0;
|
|
|
|
// state set through the prim register that requires changing GL stuff.
|
|
struct PrimGlState {
|
|
void from_register(GsPrim reg) {
|
|
current_register = reg;
|
|
gouraud_enable = reg.gouraud();
|
|
texture_enable = reg.tme();
|
|
fogging_enable = reg.fge();
|
|
aa_enable = reg.aa1();
|
|
use_uv = reg.fst();
|
|
ctxt = reg.ctxt();
|
|
fix = reg.fix();
|
|
}
|
|
|
|
GsPrim current_register;
|
|
bool gouraud_enable = false;
|
|
bool texture_enable = false;
|
|
bool fogging_enable = false;
|
|
|
|
bool aa_enable = false;
|
|
bool use_uv = false; // todo: might not require a gl state change
|
|
bool ctxt = false; // do they ever use ctxt2?
|
|
bool fix = false; // what does this even do?
|
|
} m_prim_gl_state;
|
|
|
|
static constexpr int ADGIF_STATE_COUNT = 1;
|
|
|
|
struct AdGifState {
|
|
GsTex0 reg_tex0;
|
|
u32 texture_base_ptr = 0;
|
|
bool using_mt4hh = false;
|
|
bool tcc = false;
|
|
|
|
bool enable_tex_filt = false;
|
|
|
|
u64 reg_clamp = 0b101;
|
|
bool clamp_s = true;
|
|
bool clamp_t = true;
|
|
|
|
GsAlpha reg_alpha;
|
|
GsAlpha::BlendMode a = GsAlpha::BlendMode::SOURCE;
|
|
GsAlpha::BlendMode b = GsAlpha::BlendMode::DEST;
|
|
GsAlpha::BlendMode c = GsAlpha::BlendMode::SOURCE;
|
|
GsAlpha::BlendMode d = GsAlpha::BlendMode::DEST;
|
|
bool alpha_blend_enable = false;
|
|
u8 fix = 0;
|
|
void from_register(GsAlpha reg) {
|
|
reg_alpha = reg;
|
|
a = reg.a_mode();
|
|
b = reg.b_mode();
|
|
c = reg.c_mode();
|
|
d = reg.d_mode();
|
|
fix = reg.fix();
|
|
|
|
assert(fix == 0);
|
|
}
|
|
|
|
bool used = false;
|
|
|
|
bool nontexture_equal(const AdGifState& other) const {
|
|
return reg_alpha == other.reg_alpha && alpha_blend_enable == other.alpha_blend_enable;
|
|
}
|
|
|
|
bool operator==(const AdGifState& other) const {
|
|
return reg_tex0 == other.reg_tex0 && enable_tex_filt == other.enable_tex_filt &&
|
|
reg_clamp == other.reg_clamp && nontexture_equal(other);
|
|
}
|
|
bool operator!=(const AdGifState& other) const { return !operator==(other); }
|
|
} m_adgif_state_stack[ADGIF_STATE_COUNT];
|
|
|
|
AdGifState m_adgif_state; // temp state
|
|
|
|
AdGifState* current_adgif_state() { return &m_adgif_state_stack[m_adgif_index]; }
|
|
|
|
int m_adgif_index = 0;
|
|
|
|
void update_gl_blend(AdGifState& state);
|
|
};
|