mirror of
https://github.com/open-goal/jak-project
synced 2026-07-09 23:01:56 -04:00
Sprite Distort Instancing (#1630)
* Implement instanced renderer for sprite distort * Clean up + formatting
This commit is contained in:
@@ -88,6 +88,7 @@ ShaderLibrary::ShaderLibrary() {
|
||||
at(ShaderId::COLLISION) = {"collision"};
|
||||
at(ShaderId::MERC2) = {"merc2"};
|
||||
at(ShaderId::SPRITE_DISTORT) = {"sprite_distort"};
|
||||
at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced"};
|
||||
|
||||
for (auto& shader : m_shaders) {
|
||||
ASSERT_MSG(shader.okay(), "Shader compiled");
|
||||
|
||||
@@ -43,6 +43,7 @@ enum class ShaderId {
|
||||
COLLISION = 18,
|
||||
MERC2 = 19,
|
||||
SPRITE_DISTORT = 20,
|
||||
SPRITE_DISTORT_INSTANCED = 21,
|
||||
MAX_SHADERS
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,18 @@ constexpr int SPRITE_RENDERER_MAX_DISTORT_SPRITES =
|
||||
|
||||
Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
: BucketRenderer(name, my_id), m_direct(name, my_id, 1024) {
|
||||
opengl_setup();
|
||||
}
|
||||
|
||||
void Sprite3::opengl_setup() {
|
||||
// Set up OpenGL for 'normal' sprites
|
||||
opengl_setup_normal();
|
||||
|
||||
// Set up OpenGL for distort sprites
|
||||
opengl_setup_distort();
|
||||
}
|
||||
|
||||
void Sprite3::opengl_setup_normal() {
|
||||
glGenBuffers(1, &m_ogl.vertex_buffer);
|
||||
glGenVertexArrays(1, &m_ogl.vao);
|
||||
glBindVertexArray(m_ogl.vao);
|
||||
@@ -127,8 +139,11 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
m_default_mode.set_ab(true);
|
||||
|
||||
m_current_mode = m_default_mode;
|
||||
}
|
||||
|
||||
// distort
|
||||
void Sprite3::opengl_setup_distort() {
|
||||
// Create framebuffer to snapshot current render to a texture that can be bound for the distort
|
||||
// shader This will represent tex0 from the original GS data
|
||||
glGenFramebuffers(1, &m_distort_ogl.fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_distort_ogl.fbo);
|
||||
|
||||
@@ -140,6 +155,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
// Texture clamping here matches the GS init data for distort
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
@@ -151,6 +167,8 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Non-instancing
|
||||
// ----------------------
|
||||
glGenBuffers(1, &m_distort_ogl.vertex_buffer);
|
||||
glGenVertexArrays(1, &m_distort_ogl.vao);
|
||||
glBindVertexArray(m_distort_ogl.vao);
|
||||
@@ -165,7 +183,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
glVertexAttribPointer(0, // location 0 in the shader
|
||||
3, // 3 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_TRUE, // normalized, ignored,
|
||||
GL_FALSE, // don't normalize, ignored
|
||||
sizeof(SpriteDistortVertex), //
|
||||
(void*)offsetof(SpriteDistortVertex, xyz) // offset in array
|
||||
);
|
||||
@@ -173,7 +191,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
glVertexAttribPointer(1, // location 1 in the shader
|
||||
2, // 2 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_TRUE, // normalized, ignored,
|
||||
GL_FALSE, // don't normalize, ignored
|
||||
sizeof(SpriteDistortVertex), //
|
||||
(void*)offsetof(SpriteDistortVertex, st) // offset in array
|
||||
);
|
||||
@@ -193,6 +211,81 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id)
|
||||
m_sprite_distorter_vertices.resize(distort_vert_buffer_len);
|
||||
m_sprite_distorter_indices.resize(distort_idx_buffer_len);
|
||||
m_sprite_distorter_frame_data.resize(SPRITE_RENDERER_MAX_DISTORT_SPRITES);
|
||||
|
||||
// Instancing
|
||||
// ----------------------
|
||||
glGenVertexArrays(1, &m_distort_instanced_ogl.vao);
|
||||
glBindVertexArray(m_distort_instanced_ogl.vao);
|
||||
|
||||
int distort_max_sprite_slices = 0;
|
||||
for (int i = 3; i < 12; i++) {
|
||||
// For each 'resolution', there can be that many slices
|
||||
distort_max_sprite_slices += i;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &m_distort_instanced_ogl.vertex_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer);
|
||||
|
||||
int distort_instanced_vert_buffer_len = distort_max_sprite_slices * 5; // 5 vertices per slice
|
||||
glBufferData(GL_ARRAY_BUFFER, distort_instanced_vert_buffer_len * sizeof(SpriteDistortVertex),
|
||||
nullptr, GL_STREAM_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, // location 0 in the shader
|
||||
3, // 3 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // don't normalize, ignored
|
||||
sizeof(SpriteDistortVertex), //
|
||||
(void*)offsetof(SpriteDistortVertex, xyz) // offset in array
|
||||
);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, // location 1 in the shader
|
||||
2, // 2 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // don't normalize, ignored
|
||||
sizeof(SpriteDistortVertex), //
|
||||
(void*)offsetof(SpriteDistortVertex, st) // offset in array
|
||||
);
|
||||
|
||||
glGenBuffers(1, &m_distort_instanced_ogl.instance_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer);
|
||||
|
||||
int distort_instance_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES;
|
||||
glBufferData(GL_ARRAY_BUFFER, distort_instance_buffer_len * sizeof(SpriteDistortInstanceData),
|
||||
nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, // location 2 in the shader
|
||||
4, // 4 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized, ignored,
|
||||
sizeof(SpriteDistortInstanceData), //
|
||||
(void*)offsetof(SpriteDistortInstanceData, x_y_z_s) // offset in array
|
||||
);
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, // location 3 in the shader
|
||||
4, // 4 floats per vert
|
||||
GL_FLOAT, // floats
|
||||
GL_FALSE, // normalized, ignored,
|
||||
sizeof(SpriteDistortInstanceData), //
|
||||
(void*)offsetof(SpriteDistortInstanceData, sx_sy_sz_t) // offset in array
|
||||
);
|
||||
|
||||
glVertexAttribDivisor(2, 1);
|
||||
glVertexAttribDivisor(3, 1);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
m_sprite_distorter_vertices_instanced.resize(distort_instanced_vert_buffer_len);
|
||||
|
||||
for (int i = 3; i < 12; i++) {
|
||||
auto vec = std::vector<SpriteDistortInstanceData>();
|
||||
vec.resize(distort_instance_buffer_len);
|
||||
|
||||
m_sprite_distorter_instances_by_res[i] = vec;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -224,13 +317,21 @@ void Sprite3::render_distorter(DmaFollower& dma,
|
||||
// Setup vertex data
|
||||
{
|
||||
auto prof_node = prof.make_scoped_child("setup");
|
||||
distort_setup(prof_node);
|
||||
if (m_enable_distort_instancing) {
|
||||
distort_setup_instanced(render_state, prof_node);
|
||||
} else {
|
||||
distort_setup(prof_node);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
{
|
||||
auto prof_node = prof.make_scoped_child("drawing");
|
||||
distort_draw(render_state, prof_node);
|
||||
if (m_enable_distort_instancing) {
|
||||
distort_draw_instanced(render_state, prof_node);
|
||||
} else {
|
||||
distort_draw(render_state, prof_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +459,7 @@ void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) {
|
||||
math::Vector2f vf03 = frame_data.st;
|
||||
math::Vector3f vf14 = frame_data.xyz;
|
||||
|
||||
// Each slice shares a center vertex, we can use this fact and cut out duplicate vertexes
|
||||
// Each slice shares a center vertex, we can use this fact and cut out duplicate vertices
|
||||
u32 center_vert_idx = m_sprite_distorter_vertices.size();
|
||||
m_sprite_distorter_vertices.push_back({vf14, vf03});
|
||||
|
||||
@@ -407,59 +508,93 @@ void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets up OpenGL data for rendering distort sprites using instanced rendering.
|
||||
*
|
||||
* A mesh is built once for each possible sprite resolution and is only re-built
|
||||
* when the dimensions of the window are changed. These meshes are built just like
|
||||
* the triangle strips in the VU program, but with the sprite-specific data removed.
|
||||
*
|
||||
* Required sprite-specific frame data is kept as is and is grouped by resolution.
|
||||
*/
|
||||
void Sprite3::distort_setup_instanced(SharedRenderState* render_state,
|
||||
ScopedProfilerNode& /*prof*/) {
|
||||
if (m_distort_instanced_ogl.last_window_width != render_state->window_width_px ||
|
||||
m_distort_instanced_ogl.last_window_height != render_state->window_height_px) {
|
||||
m_distort_instanced_ogl.last_window_width = render_state->window_width_px;
|
||||
m_distort_instanced_ogl.last_window_height = render_state->window_height_px;
|
||||
|
||||
// Window dimensions changed, which means the aspect ratio may have changed, which means we have
|
||||
// a new sine table
|
||||
m_sprite_distorter_vertices_instanced.clear();
|
||||
|
||||
// Build a mesh for every possible distort sprite resolution
|
||||
auto vf03 = math::Vector2f(0, 0);
|
||||
auto vf14 = math::Vector3f(0, 0, 0);
|
||||
|
||||
for (int res = 3; res < 12; res++) {
|
||||
int entry_index = m_sprite_distorter_sine_tables.ientry[res - 3].x() - 352;
|
||||
|
||||
for (int i = 0; i < res; i++) {
|
||||
math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz();
|
||||
math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy();
|
||||
math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz();
|
||||
math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy();
|
||||
|
||||
// Normally, there would be a bunch of transformations here against the sprite data.
|
||||
// Instead, we'll let the shader do it and just store the sine table specific parts here.
|
||||
|
||||
m_sprite_distorter_vertices_instanced.push_back({vf06, vf07});
|
||||
m_sprite_distorter_vertices_instanced.push_back({vf08, vf09});
|
||||
m_sprite_distorter_vertices_instanced.push_back({vf06, vf07});
|
||||
m_sprite_distorter_vertices_instanced.push_back({vf08, vf09});
|
||||
m_sprite_distorter_vertices_instanced.push_back({vf14, vf03});
|
||||
}
|
||||
}
|
||||
|
||||
m_distort_instanced_ogl.vertex_data_changed = true;
|
||||
}
|
||||
|
||||
// Set up instance data for each sprite
|
||||
m_distort_stats.total_tris = 0;
|
||||
|
||||
for (auto& [res, vec] : m_sprite_distorter_instances_by_res) {
|
||||
vec.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_distort_stats.total_sprites; i++) {
|
||||
SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(i);
|
||||
|
||||
// Shader just needs the position, tex coords, and scale
|
||||
auto x_y_z_s = math::Vector4f(frame_data.xyz.x(), frame_data.xyz.y(), frame_data.xyz.z(),
|
||||
frame_data.st.x());
|
||||
auto sx_sy_sz_t = math::Vector4f(frame_data.rgba.x(), frame_data.rgba.y(), frame_data.rgba.z(),
|
||||
frame_data.st.y());
|
||||
|
||||
int res = frame_data.flag;
|
||||
|
||||
m_sprite_distorter_instances_by_res[res].push_back({x_y_z_s, sx_sy_sz_t});
|
||||
|
||||
m_distort_stats.total_tris += res * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Draws each distort sprite.
|
||||
*/
|
||||
void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
||||
// First, make sure the distort framebuffer is the correct size
|
||||
if (m_distort_ogl.fbo_width != render_state->window_width_px ||
|
||||
m_distort_ogl.fbo_height != render_state->window_height_px) {
|
||||
m_distort_ogl.fbo_width = render_state->window_width_px;
|
||||
m_distort_ogl.fbo_height = render_state->window_height_px;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
distort_setup_framebuffer_dims(render_state);
|
||||
|
||||
if (m_distort_stats.total_tris == 0) {
|
||||
// No distort sprites to draw, we can end early
|
||||
return;
|
||||
}
|
||||
|
||||
// The distort effect needs to read the current framebuffer, so copy what's been rendered so far
|
||||
// to a texture that we can then pass to the shader
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo);
|
||||
// Do common distort drawing logic
|
||||
distort_draw_common(render_state, prof);
|
||||
|
||||
glBlitFramebuffer(render_state->window_offset_x_px, // srcX0
|
||||
render_state->window_offset_y_px, // srcY0
|
||||
render_state->window_width_px + render_state->window_offset_x_px, // srcX1
|
||||
render_state->window_height_px + render_state->window_offset_y_px, // srcY1
|
||||
0, // dstX0
|
||||
0, // dstY0
|
||||
m_distort_ogl.fbo_width, // dstX1
|
||||
m_distort_ogl.fbo_height, // dstY1
|
||||
GL_COLOR_BUFFER_BIT, // mask
|
||||
GL_NEAREST // filter
|
||||
);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Set up OpenGL state
|
||||
m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf
|
||||
glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0
|
||||
m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1
|
||||
update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1
|
||||
// note: clamp and miptbp are skipped since that is set up ahead of time with the distort
|
||||
// framebuffer texture
|
||||
|
||||
setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false);
|
||||
|
||||
// Setup shader
|
||||
// Set up shader
|
||||
auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT];
|
||||
shader->activate();
|
||||
|
||||
@@ -498,6 +633,119 @@ void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode&
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Draws each distort sprite using instanced rendering.
|
||||
*/
|
||||
void Sprite3::distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
||||
// First, make sure the distort framebuffer is the correct size
|
||||
distort_setup_framebuffer_dims(render_state);
|
||||
|
||||
if (m_distort_stats.total_tris == 0) {
|
||||
// No distort sprites to draw, we can end early
|
||||
return;
|
||||
}
|
||||
|
||||
// Do common distort drawing logic
|
||||
distort_draw_common(render_state, prof);
|
||||
|
||||
// Set up shader
|
||||
auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT_INSTANCED];
|
||||
shader->activate();
|
||||
|
||||
Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f,
|
||||
m_sprite_distorter_sine_tables.color.y() / 255.0f,
|
||||
m_sprite_distorter_sine_tables.color.z() / 255.0f,
|
||||
m_sprite_distorter_sine_tables.color.w() / 255.0f);
|
||||
glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data());
|
||||
|
||||
// Bind vertex array
|
||||
glBindVertexArray(m_distort_instanced_ogl.vao);
|
||||
|
||||
// Upload vertex data (if it changed)
|
||||
if (m_distort_instanced_ogl.vertex_data_changed) {
|
||||
m_distort_instanced_ogl.vertex_data_changed = false;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
m_sprite_distorter_vertices_instanced.size() * sizeof(SpriteDistortVertex),
|
||||
m_sprite_distorter_vertices_instanced.data(), GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
// Draw each resolution group
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer);
|
||||
prof.add_tri(m_distort_stats.total_tris);
|
||||
|
||||
int vert_offset = 0;
|
||||
for (int res = 3; res < 12; res++) {
|
||||
auto& instances = m_sprite_distorter_instances_by_res[res];
|
||||
int num_verts = res * 5;
|
||||
|
||||
if (instances.size() > 0) {
|
||||
// Upload instance data
|
||||
glBufferData(GL_ARRAY_BUFFER, instances.size() * sizeof(SpriteDistortInstanceData),
|
||||
instances.data(), GL_DYNAMIC_DRAW);
|
||||
|
||||
// Draw
|
||||
prof.add_draw_call();
|
||||
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, vert_offset, num_verts, instances.size());
|
||||
}
|
||||
|
||||
vert_offset += num_verts;
|
||||
}
|
||||
|
||||
// Done
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) {
|
||||
// The distort effect needs to read the current framebuffer, so copy what's been rendered so far
|
||||
// to a texture that we can then pass to the shader
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo);
|
||||
|
||||
glBlitFramebuffer(render_state->window_offset_x_px, // srcX0
|
||||
render_state->window_offset_y_px, // srcY0
|
||||
render_state->window_width_px + render_state->window_offset_x_px, // srcX1
|
||||
render_state->window_height_px + render_state->window_offset_y_px, // srcY1
|
||||
0, // dstX0
|
||||
0, // dstY0
|
||||
m_distort_ogl.fbo_width, // dstX1
|
||||
m_distort_ogl.fbo_height, // dstY1
|
||||
GL_COLOR_BUFFER_BIT, // mask
|
||||
GL_NEAREST // filter
|
||||
);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
// Set up OpenGL state
|
||||
m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf
|
||||
glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0
|
||||
m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1
|
||||
update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1
|
||||
// note: clamp and miptbp are skipped since that is set up ahead of time with the distort
|
||||
// framebuffer texture
|
||||
|
||||
setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false);
|
||||
}
|
||||
|
||||
void Sprite3::distort_setup_framebuffer_dims(SharedRenderState* render_state) {
|
||||
// Distort framebuffer must be the same dimensions as the default window framebuffer
|
||||
if (m_distort_ogl.fbo_width != render_state->window_width_px ||
|
||||
m_distort_ogl.fbo_height != render_state->window_height_px) {
|
||||
m_distort_ogl.fbo_width = render_state->window_width_px;
|
||||
m_distort_ogl.fbo_height = render_state->window_height_px;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle DMA data that does the per-frame setup.
|
||||
* This should get the dma chain immediately after the call to sprite-draw-distorters.
|
||||
@@ -770,6 +1018,7 @@ void Sprite3::draw_debug_window() {
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("3d", &m_3d_enable);
|
||||
ImGui::Checkbox("Distort", &m_distort_enable);
|
||||
ImGui::Checkbox("Distort instancing", &m_enable_distort_instancing);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -19,12 +19,20 @@ class Sprite3 : public BucketRenderer {
|
||||
static constexpr int SPRITES_PER_CHUNK = 48;
|
||||
|
||||
private:
|
||||
void opengl_setup();
|
||||
void opengl_setup_normal();
|
||||
void opengl_setup_distort();
|
||||
|
||||
void render_distorter(DmaFollower& dma,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
void distort_dma(DmaFollower& dma, ScopedProfilerNode& prof);
|
||||
void distort_setup(ScopedProfilerNode& prof);
|
||||
void distort_setup_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
void distort_setup_framebuffer_dims(SharedRenderState* render_state);
|
||||
void handle_sprite_frame_setup(DmaFollower& dma);
|
||||
void render_3d(DmaFollower& dma);
|
||||
void render_2d_group0(DmaFollower& dma,
|
||||
@@ -90,6 +98,11 @@ class Sprite3 : public BucketRenderer {
|
||||
math::Vector2f st;
|
||||
};
|
||||
|
||||
struct SpriteDistortInstanceData {
|
||||
math::Vector4f x_y_z_s; // position, S-texture coord
|
||||
math::Vector4f sx_sy_sz_t; // scale, T-texture coord
|
||||
};
|
||||
|
||||
struct {
|
||||
GLuint vao;
|
||||
GLuint vertex_buffer;
|
||||
@@ -100,6 +113,15 @@ class Sprite3 : public BucketRenderer {
|
||||
int fbo_height = 480;
|
||||
} m_distort_ogl;
|
||||
|
||||
struct {
|
||||
GLuint vao;
|
||||
GLuint vertex_buffer; // contains vertex data for each possible sprite resolution (3-11)
|
||||
GLuint instance_buffer; // contains all instance specific data for each sprite per frame
|
||||
int last_window_width = -1;
|
||||
int last_window_height = -1;
|
||||
bool vertex_data_changed = false;
|
||||
} m_distort_instanced_ogl;
|
||||
|
||||
struct {
|
||||
int total_sprites;
|
||||
int total_tris;
|
||||
@@ -110,6 +132,8 @@ class Sprite3 : public BucketRenderer {
|
||||
SpriteDistorterSetup m_sprite_distorter_setup; // direct data
|
||||
SpriteDistorterSineTables m_sprite_distorter_sine_tables;
|
||||
std::vector<SpriteDistortFrameData> m_sprite_distorter_frame_data;
|
||||
std::vector<SpriteDistortVertex> m_sprite_distorter_vertices_instanced;
|
||||
std::map<int, std::vector<SpriteDistortInstanceData>> m_sprite_distorter_instances_by_res;
|
||||
|
||||
u8 m_sprite_direct_setup[3 * 16];
|
||||
SpriteFrameData m_frame_data; // qwa: 980
|
||||
@@ -127,6 +151,7 @@ class Sprite3 : public BucketRenderer {
|
||||
int count_2d_grp1 = 0;
|
||||
} m_debug_stats;
|
||||
|
||||
bool m_enable_distort_instancing = true;
|
||||
bool m_enable_culling = true;
|
||||
|
||||
bool m_2d_enable = true;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#version 430 core
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
uniform sampler2D framebuffer_tex;
|
||||
|
||||
in flat vec4 fragment_color;
|
||||
in vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
vec4 color = fragment_color;
|
||||
|
||||
// correct color
|
||||
color *= 2;
|
||||
|
||||
// correct texture coordinates
|
||||
vec2 texture_coords = vec2(tex_coord.x, (1.0f - tex_coord.y) - (1 - (448.0/512.0)) / 2);
|
||||
|
||||
// sample framebuffer texture
|
||||
out_color = color * texture(framebuffer_tex, texture_coords);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#version 430 core
|
||||
|
||||
layout (location = 0) in vec3 xyz; // position from sine table
|
||||
layout (location = 1) in vec2 st; // tex coord from sine table
|
||||
layout (location = 2) in vec4 instance_xyz_s; // sprite position + texture S-coord
|
||||
layout (location = 3) in vec4 instance_scale_t; // sprite scale + texture T-coord
|
||||
|
||||
uniform vec4 u_color;
|
||||
|
||||
out flat vec4 fragment_color;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
// Pass on fragment color
|
||||
fragment_color = u_color;
|
||||
|
||||
// Adjust position and texture coordinates the same way as in the VU program
|
||||
//
|
||||
// The VU program operated on each "slice" of the sprite separately, which in
|
||||
// this case is every 5 vertices. To know the exact transformation to apply
|
||||
// for each vertex, we need to know which one it is. The order is always 2
|
||||
// vertices scaled by sizeX, 2 scaled by sizeY (sizeZ for tex coords), and
|
||||
// finally the center vertex which isn't modified.
|
||||
float slice_vert_id = mod(gl_VertexID, 5);
|
||||
|
||||
vec2 texture_coord = vec2(instance_xyz_s.w, instance_scale_t.w);
|
||||
if (slice_vert_id < 2) {
|
||||
texture_coord += st * instance_scale_t.x;
|
||||
} else if (slice_vert_id < 4) {
|
||||
texture_coord += st * instance_scale_t.z;
|
||||
}
|
||||
|
||||
tex_coord = texture_coord;
|
||||
|
||||
vec3 position = instance_xyz_s.xyz;
|
||||
if (slice_vert_id < 2) {
|
||||
position += xyz * instance_scale_t.x;
|
||||
} else if (slice_vert_id < 4) {
|
||||
position += xyz * instance_scale_t.y;
|
||||
}
|
||||
|
||||
vec4 transformed = vec4(position, 1.0);
|
||||
|
||||
// correct xy offset
|
||||
transformed.xy -= (2048.);
|
||||
// correct z scale
|
||||
transformed.z /= (8388608);
|
||||
transformed.z -= 1;
|
||||
// correct xy scale
|
||||
transformed.x /= (256);
|
||||
transformed.y /= -(128);
|
||||
|
||||
gl_Position = transformed;
|
||||
}
|
||||
Reference in New Issue
Block a user