mirror of
https://github.com/open-goal/jak-project
synced 2026-06-18 23:37:22 -04:00
3d08d079c9
Initial implementation of the `ocean-mid`, `ocean-far` and `ocean-near` renderers for Jak 2. There's still a few things to sort out, mainly: - [x] ~Backwards compatibility with Jak 1. The only thing that currently stands in the way of that is figuring out a clean way to "un-hardcode" the texture base pointer in C++ without creating a completely separate `OceanTexture` class for Jak 2. One thing I thought of would be modifying `BucketRenderer`'s virtual `init_textures` method to also pass the `GameVersion`, but I'm not sure if there's a better way.~ - [x] ~The sudden transition from `ocean-near` to `ocean-mid`. Not sure why it's happening or how to fix it.~ - [x] The ocean has two new methods in Jak 2, `ocean::89` and `ocean::79`, one of which seems to be related to time of day sky colors. ~Even without them implemented, the end result looks quite close, so we may be able to skip them?~ `ocean::89` generates `ocean-mid` envmap textures, so it will likely be required, but will not be handled right now. Reverted the VU prologue removals because it made the tests fail.
540 lines
18 KiB
C++
540 lines
18 KiB
C++
#include "CommonOceanRenderer.h"
|
|
|
|
#include "common/log/log.h"
|
|
|
|
constexpr int OCEAN_TEX_TBP_JAK1 = 8160; // todo
|
|
constexpr int OCEAN_TEX_TBP_JAK2 = 672;
|
|
|
|
CommonOceanRenderer::CommonOceanRenderer() {
|
|
m_vertices.resize(4096 * 10); // todo decrease
|
|
for (auto& buf : m_indices) {
|
|
buf.resize(4096 * 10);
|
|
}
|
|
|
|
// create OpenGL objects
|
|
glGenBuffers(1, &m_ogl.vertex_buffer);
|
|
glGenBuffers(NUM_BUCKETS, m_ogl.index_buffer);
|
|
glGenVertexArrays(1, &m_ogl.vao);
|
|
|
|
// set up the vertex array
|
|
glBindVertexArray(m_ogl.vao);
|
|
for (int i = 0; i < NUM_BUCKETS; i++) {
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[i]);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices[i].size() * sizeof(u32), nullptr,
|
|
GL_STREAM_DRAW);
|
|
}
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
|
|
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex), nullptr, GL_STREAM_DRAW);
|
|
|
|
// xyz
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, // location 0 in the shader
|
|
3, // 3 floats per vert
|
|
GL_FLOAT, // floats
|
|
GL_TRUE, // normalized, ignored,
|
|
sizeof(Vertex), //
|
|
(void*)offsetof(Vertex, xyz) // offset in array
|
|
);
|
|
|
|
// rgba
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, // location 1 in the shader
|
|
4, // 4 color components
|
|
GL_UNSIGNED_BYTE, // u8
|
|
GL_TRUE, // normalized (255 becomes 1)
|
|
sizeof(Vertex), //
|
|
(void*)offsetof(Vertex, rgba) //
|
|
);
|
|
|
|
// stq
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, // location 2 in the shader
|
|
3, // 2 floats per vert
|
|
GL_FLOAT, // floats
|
|
GL_FALSE, // normalized, ignored
|
|
sizeof(Vertex), //
|
|
(void*)offsetof(Vertex, stq) // offset in array
|
|
);
|
|
|
|
// byte data
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribIPointer(3, // location 3 in the shader
|
|
4, //
|
|
GL_UNSIGNED_BYTE, // u8's
|
|
sizeof(Vertex), //
|
|
(void*)offsetof(Vertex, fog) // offset in array
|
|
);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
CommonOceanRenderer::~CommonOceanRenderer() {
|
|
glDeleteBuffers(1, &m_ogl.vertex_buffer);
|
|
glDeleteBuffers(3, m_ogl.index_buffer);
|
|
glDeleteVertexArrays(1, &m_ogl.vao);
|
|
}
|
|
|
|
void CommonOceanRenderer::init_for_near() {
|
|
m_next_free_vertex = 0;
|
|
for (auto& x : m_next_free_index) {
|
|
x = 0;
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::kick_from_near(const u8* data) {
|
|
bool eop = false;
|
|
|
|
u32 offset = 0;
|
|
while (!eop) {
|
|
GifTag tag(data + offset);
|
|
offset += 16;
|
|
|
|
if (tag.nreg() == 3) {
|
|
ASSERT(tag.pre());
|
|
if (GsPrim(tag.prim()).kind() == GsPrim::Kind::TRI_STRIP) {
|
|
handle_near_vertex_gif_data_strip(data, offset, tag.nloop());
|
|
} else {
|
|
handle_near_vertex_gif_data_fan(data, offset, tag.nloop());
|
|
}
|
|
offset += 16 * 3 * tag.nloop();
|
|
} else if (tag.nreg() == 1) {
|
|
handle_near_adgif(data, offset, tag.nloop());
|
|
offset += 16 * 1 * tag.nloop();
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
|
|
eop = tag.eop();
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::handle_near_vertex_gif_data_strip(const u8* data, u32 offset, u32 loop) {
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = UINT32_MAX;
|
|
bool reset_last = false;
|
|
for (u32 i = 0; i < loop; i++) {
|
|
auto& dest_vert = m_vertices[m_next_free_vertex++];
|
|
|
|
// stq
|
|
memcpy(dest_vert.stq.data(), data + offset, 12);
|
|
offset += 16;
|
|
|
|
// rgba
|
|
dest_vert.rgba[0] = data[offset];
|
|
dest_vert.rgba[1] = data[offset + 4];
|
|
dest_vert.rgba[2] = data[offset + 8];
|
|
dest_vert.rgba[3] = data[offset + 12];
|
|
offset += 16;
|
|
|
|
// xyz
|
|
u32 x = 0, y = 0;
|
|
memcpy(&x, data + offset, 4);
|
|
memcpy(&y, data + offset + 4, 4);
|
|
|
|
u64 upper;
|
|
memcpy(&upper, data + offset + 8, 8);
|
|
u32 z = (upper >> 4) & 0xffffff;
|
|
offset += 16;
|
|
|
|
dest_vert.xyz[0] = (float)(x << 16) / (float)UINT32_MAX;
|
|
dest_vert.xyz[1] = (float)(y << 16) / (float)UINT32_MAX;
|
|
dest_vert.xyz[2] = (float)(z << 8) / (float)UINT32_MAX;
|
|
|
|
u8 f = (upper >> 36);
|
|
dest_vert.fog = f;
|
|
|
|
auto vidx = m_next_free_vertex - 1;
|
|
bool adc = upper & (1ull << 47);
|
|
if (!adc) {
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = vidx;
|
|
reset_last = false;
|
|
} else {
|
|
if (reset_last) {
|
|
m_next_free_index[m_current_bucket] -= 3;
|
|
}
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = UINT32_MAX;
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = vidx - 1;
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = vidx;
|
|
reset_last = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::handle_near_vertex_gif_data_fan(const u8* data, u32 offset, u32 loop) {
|
|
u32 ind_of_fan_start = UINT32_MAX;
|
|
bool fan_running = false;
|
|
// :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2 (gif-reg-id xyzf2)
|
|
for (u32 i = 0; i < loop; i++) {
|
|
auto& dest_vert = m_vertices[m_next_free_vertex++];
|
|
|
|
// stq
|
|
memcpy(dest_vert.stq.data(), data + offset, 12);
|
|
offset += 16;
|
|
|
|
// rgba
|
|
dest_vert.rgba[0] = data[offset];
|
|
dest_vert.rgba[1] = data[offset + 4];
|
|
dest_vert.rgba[2] = data[offset + 8];
|
|
dest_vert.rgba[3] = data[offset + 12];
|
|
offset += 16;
|
|
|
|
// xyz
|
|
u32 x = 0, y = 0;
|
|
memcpy(&x, data + offset, 4);
|
|
memcpy(&y, data + offset + 4, 4);
|
|
|
|
u64 upper;
|
|
memcpy(&upper, data + offset + 8, 8);
|
|
u32 z = (upper >> 4) & 0xffffff;
|
|
offset += 16;
|
|
|
|
dest_vert.xyz[0] = (float)(x << 16) / (float)UINT32_MAX;
|
|
dest_vert.xyz[1] = (float)(y << 16) / (float)UINT32_MAX;
|
|
dest_vert.xyz[2] = (float)(z << 8) / (float)UINT32_MAX;
|
|
|
|
u8 f = (upper >> 36);
|
|
dest_vert.fog = f;
|
|
|
|
auto vidx = m_next_free_vertex - 1;
|
|
|
|
if (ind_of_fan_start == UINT32_MAX) {
|
|
ind_of_fan_start = vidx;
|
|
} else {
|
|
if (fan_running) {
|
|
// hack to draw fans with strips. this isn't efficient, but fans happen extremely rarely
|
|
// (you basically have to put the camera intersecting the ocean and looking fwd)
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = UINT32_MAX;
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = vidx;
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = vidx - 1;
|
|
m_indices[m_current_bucket][m_next_free_index[m_current_bucket]++] = ind_of_fan_start;
|
|
} else {
|
|
fan_running = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::handle_near_adgif(const u8* data, u32 offset, u32 count) {
|
|
u32 most_recent_tbp = 0;
|
|
|
|
for (u32 i = 0; i < count; i++) {
|
|
u64 value;
|
|
GsRegisterAddress addr;
|
|
memcpy(&value, data + offset + 16 * i, sizeof(u64));
|
|
memcpy(&addr, data + offset + 16 * i + 8, sizeof(GsRegisterAddress));
|
|
switch (addr) {
|
|
case GsRegisterAddress::MIPTBP1_1:
|
|
// ignore this, it's just mipmapping settings
|
|
break;
|
|
case GsRegisterAddress::TEX1_1: {
|
|
GsTex1 reg(value);
|
|
ASSERT(reg.mmag());
|
|
} break;
|
|
case GsRegisterAddress::CLAMP_1: {
|
|
bool s = value & 0b001;
|
|
bool t = value & 0b100;
|
|
ASSERT(s == t);
|
|
if (s) {
|
|
m_current_bucket = VertexBucket::ENV_MAP;
|
|
}
|
|
} break;
|
|
case GsRegisterAddress::TEX0_1: {
|
|
GsTex0 reg(value);
|
|
ASSERT(reg.tfx() == GsTex0::TextureFunction::MODULATE);
|
|
if (!reg.tcc()) {
|
|
m_current_bucket = VertexBucket::RGB_TEXTURE;
|
|
}
|
|
most_recent_tbp = reg.tbp0();
|
|
} break;
|
|
case GsRegisterAddress::ALPHA_1: {
|
|
GsAlpha reg(value);
|
|
if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
|
|
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
|
|
reg.c_mode() == GsAlpha::BlendMode::DEST && reg.d_mode() == GsAlpha::BlendMode::DEST) {
|
|
m_current_bucket = VertexBucket::ENV_MAP;
|
|
}
|
|
} break;
|
|
|
|
case GsRegisterAddress::FRAME_1: {
|
|
u32 mask = value >> 32;
|
|
if (mask) {
|
|
m_current_bucket = VertexBucket::ALPHA;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
lg::debug("reg: {}", register_address_name(addr));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_current_bucket == VertexBucket::ENV_MAP) {
|
|
m_envmap_tex = most_recent_tbp;
|
|
}
|
|
|
|
if (m_vertices.size() - 128 < m_next_free_vertex) {
|
|
ASSERT(false); // add more vertices.
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::flush_near(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
|
glBindVertexArray(m_ogl.vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
|
|
glEnable(GL_PRIMITIVE_RESTART);
|
|
glPrimitiveRestartIndex(UINT32_MAX);
|
|
glBufferData(GL_ARRAY_BUFFER, m_next_free_vertex * sizeof(Vertex), m_vertices.data(),
|
|
GL_STREAM_DRAW);
|
|
render_state->shaders[ShaderId::OCEAN_COMMON].activate();
|
|
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "fog_color"),
|
|
render_state->fog_color[0] / 255.f, render_state->fog_color[1] / 255.f,
|
|
render_state->fog_color[2] / 255.f, render_state->fog_intensity / 255);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_BLEND);
|
|
glDepthFunc(GL_GEQUAL);
|
|
|
|
for (int bucket = 0; bucket < 3; bucket++) {
|
|
switch (bucket) {
|
|
case 0: {
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
auto tbp =
|
|
render_state->version == GameVersion::Jak1 ? OCEAN_TEX_TBP_JAK1 : OCEAN_TEX_TBP_JAK2;
|
|
auto tex = render_state->texture_pool->lookup(tbp);
|
|
if (!tex) {
|
|
tex = render_state->texture_pool->get_placeholder_texture();
|
|
}
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, *tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "tex_T0"), 0);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "bucket"), 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
break;
|
|
case 1:
|
|
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
|
glUniform1f(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "alpha_mult"),
|
|
1.f);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "bucket"), 1);
|
|
break;
|
|
case 2:
|
|
auto tex = render_state->texture_pool->lookup(m_envmap_tex);
|
|
if (!tex) {
|
|
tex = render_state->texture_pool->get_placeholder_texture();
|
|
}
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, *tex);
|
|
|
|
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ONE, GL_ZERO);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "bucket"), 2);
|
|
break;
|
|
}
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[bucket]);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_next_free_index[bucket] * sizeof(u32),
|
|
m_indices[bucket].data(), GL_STREAM_DRAW);
|
|
glDrawElements(GL_TRIANGLE_STRIP, m_next_free_index[bucket], GL_UNSIGNED_INT, nullptr);
|
|
prof.add_draw_call();
|
|
prof.add_tri(m_next_free_index[bucket]);
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::kick_from_mid(const u8* data) {
|
|
bool eop = false;
|
|
|
|
u32 offset = 0;
|
|
while (!eop) {
|
|
GifTag tag(data + offset);
|
|
offset += 16;
|
|
|
|
// unpack registers.
|
|
// faster to do it once outside of the nloop loop.
|
|
GifTag::RegisterDescriptor reg_desc[16];
|
|
u32 nreg = tag.nreg();
|
|
for (u32 i = 0; i < nreg; i++) {
|
|
reg_desc[i] = tag.reg(i);
|
|
}
|
|
|
|
auto format = tag.flg();
|
|
if (format == GifTag::Format::PACKED) {
|
|
if (tag.nreg() == 1) {
|
|
ASSERT(!tag.pre());
|
|
ASSERT(tag.nloop() == 5);
|
|
handle_mid_adgif(data, offset);
|
|
offset += 5 * 16;
|
|
} else {
|
|
ASSERT(tag.nreg() == 3);
|
|
ASSERT(tag.pre());
|
|
m_current_bucket = GsPrim(tag.prim()).abe() ? 1 : 0;
|
|
|
|
int count = tag.nloop();
|
|
if (GsPrim(tag.prim()).kind() == GsPrim::Kind::TRI_STRIP) {
|
|
handle_near_vertex_gif_data_strip(data, offset, tag.nloop());
|
|
} else {
|
|
handle_near_vertex_gif_data_fan(data, offset, tag.nloop());
|
|
}
|
|
offset += 3 * 16 * count;
|
|
// todo handle.
|
|
}
|
|
} else {
|
|
ASSERT(false); // format not packed or reglist.
|
|
}
|
|
|
|
eop = tag.eop();
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::handle_mid_adgif(const u8* data, u32 offset) {
|
|
u32 most_recent_tbp = 0;
|
|
|
|
for (u32 i = 0; i < 5; i++) {
|
|
u64 value;
|
|
GsRegisterAddress addr;
|
|
memcpy(&value, data + offset + 16 * i, sizeof(u64));
|
|
memcpy(&addr, data + offset + 16 * i + 8, sizeof(GsRegisterAddress));
|
|
switch (addr) {
|
|
case GsRegisterAddress::MIPTBP1_1:
|
|
case GsRegisterAddress::MIPTBP2_1:
|
|
// ignore this, it's just mipmapping settings
|
|
break;
|
|
case GsRegisterAddress::TEX1_1: {
|
|
GsTex1 reg(value);
|
|
ASSERT(reg.mmag());
|
|
} break;
|
|
case GsRegisterAddress::CLAMP_1: {
|
|
bool s = value & 0b001;
|
|
bool t = value & 0b100;
|
|
ASSERT(s == t);
|
|
} break;
|
|
case GsRegisterAddress::TEX0_1: {
|
|
GsTex0 reg(value);
|
|
ASSERT(reg.tfx() == GsTex0::TextureFunction::MODULATE);
|
|
most_recent_tbp = reg.tbp0();
|
|
} break;
|
|
case GsRegisterAddress::ALPHA_1: {
|
|
} break;
|
|
|
|
default:
|
|
lg::debug("reg: {}", register_address_name(addr));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (most_recent_tbp != OCEAN_TEX_TBP_JAK2) {
|
|
m_envmap_tex = most_recent_tbp;
|
|
}
|
|
|
|
if (m_vertices.size() - 128 < m_next_free_vertex) {
|
|
ASSERT(false); // add more vertices.
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::init_for_mid() {
|
|
m_next_free_vertex = 0;
|
|
for (auto& x : m_next_free_index) {
|
|
x = 0;
|
|
}
|
|
}
|
|
|
|
void reverse_indices(u32* indices, u32 count) {
|
|
if (count) {
|
|
for (u32 a = 0, b = count - 1; a < b; a++, b--) {
|
|
std::swap(indices[a], indices[b]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommonOceanRenderer::flush_mid(SharedRenderState* render_state, ScopedProfilerNode& prof) {
|
|
glBindVertexArray(m_ogl.vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer);
|
|
glEnable(GL_PRIMITIVE_RESTART);
|
|
glPrimitiveRestartIndex(UINT32_MAX);
|
|
glBufferData(GL_ARRAY_BUFFER, m_next_free_vertex * sizeof(Vertex), m_vertices.data(),
|
|
GL_STREAM_DRAW);
|
|
render_state->shaders[ShaderId::OCEAN_COMMON].activate();
|
|
glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "fog_color"),
|
|
render_state->fog_color[0] / 255.f, render_state->fog_color[1] / 255.f,
|
|
render_state->fog_color[2] / 255.f, render_state->fog_intensity / 255);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glDisable(GL_BLEND);
|
|
|
|
// note:
|
|
// there are some places where the game draws the same section of ocean twice, in this order:
|
|
// - low poly mesh with ocean texture
|
|
// - low poly mesh with envmap texture
|
|
// - high poly mesh with ocean texture (overwrites previous draw)
|
|
// - high poly mesh with envmap texture (overwrites previous draw)
|
|
|
|
// we draw all ocean textures together and all envmap textures togther. luckily, there's a trick
|
|
// we can use to get the same result.
|
|
// first, we'll draw all ocean textures. The high poly mesh is drawn second, so it wins.
|
|
// then, we'll draw all envmaps, but with two changes:
|
|
// - first, we draw it in reverse, so the high poly versions are drawn first
|
|
// - second, we'll modify the shader to set alpha = 0 of the destination. when the low poly
|
|
// version is drawn on top, it won't draw at all because of the blending mode
|
|
// (s_factor = DST_ALPHA, d_factor = 1)
|
|
|
|
// draw it in reverse
|
|
reverse_indices(m_indices[1].data(), m_next_free_index[1]);
|
|
|
|
for (int bucket = 0; bucket < 2; bucket++) {
|
|
switch (bucket) {
|
|
case 0: {
|
|
auto tbp =
|
|
render_state->version == GameVersion::Jak1 ? OCEAN_TEX_TBP_JAK1 : OCEAN_TEX_TBP_JAK2;
|
|
auto tex = render_state->texture_pool->lookup(tbp);
|
|
if (!tex) {
|
|
tex = render_state->texture_pool->get_placeholder_texture();
|
|
}
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, *tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "tex_T0"), 0);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "bucket"), 3);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
break;
|
|
case 1:
|
|
glEnable(GL_BLEND);
|
|
auto tex = render_state->texture_pool->lookup(m_envmap_tex);
|
|
if (!tex) {
|
|
tex = render_state->texture_pool->get_placeholder_texture();
|
|
}
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, *tex);
|
|
|
|
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ONE, GL_ZERO);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
glUniform1i(
|
|
glGetUniformLocation(render_state->shaders[ShaderId::OCEAN_COMMON].id(), "bucket"), 4);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
}
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer[bucket]);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_next_free_index[bucket] * sizeof(u32),
|
|
m_indices[bucket].data(), GL_STREAM_DRAW);
|
|
glDrawElements(GL_TRIANGLE_STRIP, m_next_free_index[bucket], GL_UNSIGNED_INT, nullptr);
|
|
prof.add_draw_call();
|
|
prof.add_tri(m_next_free_index[bucket]);
|
|
}
|
|
}
|