[jak 2] Clouds V2, Clean up texture animator (#2921)

Some general improvements for the texture animator:
- Clouds are special cased, saving about 1 ms per frame
- Adjusting the amount of clouds now actually works.
- Fixed an issue with the brightness of clouds, and the way that they
fade out around the edges.
This commit is contained in:
water111 2023-08-19 12:50:59 -04:00 committed by GitHub
parent 66e48195cb
commit 3b29da919b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 554 additions and 354 deletions

View file

@ -19,7 +19,9 @@ class Vector {
}
template <typename... Args>
explicit Vector(Args... args) : m_data{T(args)...} {}
constexpr Vector(Args... args) : m_data{T(args)...} {
static_assert(sizeof...(args) == Size, "Incorrect number of args");
}
T* begin() { return &m_data[0]; }
T* end() { return &m_data[Size]; }

View file

@ -1,5 +1,6 @@
#include "CollideMeshRenderer.h"
#include "game/graphics/gfx.h"
#include "game/graphics/opengl_renderer/background/background_common.h"
float material_colors_jak1[23 * 3] = {

View file

@ -1234,8 +1234,8 @@ void DirectRenderer::handle_xyzf2_common(u32 x,
auto& corner2_stq = m_prim_building.building_stq[1];
// should use most recent vertex z.
math::Vector<u32, 4> corner3_vert{corner1_vert[0], corner2_vert[1], corner2_vert[2]};
math::Vector<u32, 4> corner4_vert{corner2_vert[0], corner1_vert[1], corner2_vert[2]};
math::Vector<u32, 4> corner3_vert{corner1_vert[0], corner2_vert[1], corner2_vert[2], 0};
math::Vector<u32, 4> corner4_vert{corner2_vert[0], corner1_vert[1], corner2_vert[2], 0};
math::Vector<float, 3> corner3_stq{corner1_stq[0], corner2_stq[1], corner2_stq[2]};
math::Vector<float, 3> corner4_stq{corner2_stq[0], corner1_stq[1], corner2_stq[2]};

View file

@ -355,7 +355,9 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* co
m_psm32_to_psm8_8_8(8, 8, 8, 64),
m_psm32_to_psm8_16_16(16, 16, 16, 64),
m_psm32_to_psm8_32_32(32, 32, 16, 64),
m_psm32_to_psm8_64_64(64, 64, 64, 64) {
m_psm32_to_psm8_64_64(64, 64, 64, 64),
m_sky_blend_texture(kFinalSkyTextureSize, kFinalSkyTextureSize, GL_UNSIGNED_INT_8_8_8_8_REV),
m_sky_final_texture(kFinalSkyTextureSize, kFinalSkyTextureSize, GL_UNSIGNED_INT_8_8_8_8_REV) {
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vertex_buffer);
glBindVertexArray(m_vao);
@ -389,6 +391,8 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* co
m_uniforms.channel_scramble = glGetUniformLocation(shader.id(), "channel_scramble");
m_uniforms.tcc = glGetUniformLocation(shader.id(), "tcc");
m_uniforms.alpha_multiply = glGetUniformLocation(shader.id(), "alpha_multiply");
m_uniforms.minimum = glGetUniformLocation(shader.id(), "minimum");
m_uniforms.maximum = glGetUniformLocation(shader.id(), "maximum");
// create a single "dummy texture" with all 0 data.
// this is faster and easier than switching shaders to one without texturing, and is used
@ -433,6 +437,8 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* co
// animation-specific stuff
setup_texture_anims();
setup_sky();
}
/*!
@ -478,6 +484,25 @@ int TextureAnimator::create_fixed_anim_array(const std::vector<FixedAnimDef>& de
void TextureAnimator::draw_debug_window() {
ImGui::Checkbox("fast-scrambler", &m_debug.use_fast_scrambler);
ImGui::Text("Sky:");
ImGui::Text("Fog Height: %f", m_debug_sky_input.fog_height);
ImGui::Text("Cloud minmax: %f %f", m_debug_sky_input.cloud_min, m_debug_sky_input.cloud_max);
for (int i = 0; i < 9; i++) {
ImGui::Text("Time[%d]: %f", i, m_debug_sky_input.times[i]);
}
ImGui::Text("Dest %d", m_debug_sky_input.cloud_dest);
glBindTexture(GL_TEXTURE_2D, m_sky_blend_texture.texture());
int w, h;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
ImGui::Image((void*)(u64)m_sky_blend_texture.texture(), ImVec2(w, h));
glBindTexture(GL_TEXTURE_2D, m_sky_final_texture.texture());
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
ImGui::Image((void*)(u64)m_sky_final_texture.texture(), ImVec2(w, h));
auto& slots = jak2_animated_texture_slots();
for (size_t i = 0; i < slots.size(); i++) {
ImGui::Text("Slot %d %s (%d)", (int)i, slots[i].c_str(), (int)m_private_output_slots[i]);
@ -552,9 +577,6 @@ enum PcTextureAnimCodes {
GENERIC_UPLOAD = 16,
SET_SHADER = 17,
DRAW = 18,
MOVE_RG_TO_BA = 19,
SET_CLUT_ALPHA = 20,
COPY_CLUT_ALPHA = 21,
DARKJAK = 22,
PRISON_JAK = 23,
ORACLE_JAK = 24,
@ -574,6 +596,7 @@ enum PcTextureAnimCodes {
METKOR = 38,
SHIELD = 39,
KREW_HOLO = 40,
CLOUDS_AND_FOG = 41,
};
// metadata for an upload from GOAL memory
@ -616,7 +639,7 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
m_opengl_texture_pool.free(t.tex, t.w, t.h);
}
m_in_use_temp_textures.clear(); // reset temp texture allocator.
m_erased_on_this_frame.clear();
m_force_to_gpu.clear();
m_skip_tbps.clear();
// loop over DMA, and do the appropriate texture operations.
@ -653,18 +676,6 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
case FINISH_ARRAY:
done = true;
break;
case MOVE_RG_TO_BA: {
auto p = scoped_prof("rg-to-ba");
handle_rg_to_ba(tf);
} break;
case SET_CLUT_ALPHA: {
auto p = scoped_prof("set-clut-alpha");
handle_set_clut_alpha(tf);
} break;
case COPY_CLUT_ALPHA: {
auto p = scoped_prof("copy-clut-alpha");
handle_copy_clut_alpha(tf);
} break;
case DARKJAK: {
auto p = scoped_prof("darkjak");
run_clut_blender_group(tf, m_darkjak_clut_blender_idx, frame_idx);
@ -741,6 +752,10 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
auto p = scoped_prof("krew-holo");
run_fixed_animation_array(m_krew_holo_anim_array_idx, tf, texture_pool);
} break;
case CLOUDS_AND_FOG: {
auto p = scoped_prof("clouds-and-fog");
handle_clouds_and_fog(tf, texture_pool);
} break;
default:
fmt::print("bad imm: {}\n", vif0.immediate);
ASSERT_NOT_REACHED();
@ -754,11 +769,10 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
}
// The steps above will populate m_textures with some combination of GPU/CPU textures.
// we need to make sure that all final textures end up on the GPU. For now, we detect this by
// seeing if the "erase" operation ran on an tbp, indicating that it was cleared, which is
// always done to all textures by the GOAL code.
for (auto tbp : m_erased_on_this_frame) {
auto p = scoped_prof("handle-one-erased");
// we need to make sure that all final textures end up on the GPU, if desired. (todo: move this to
// happen somewhere else)?
for (auto tbp : m_force_to_gpu) {
auto p = scoped_prof("force-to-gpu");
force_to_gpu(tbp);
}
@ -937,133 +951,6 @@ void debug_save_opengl_texture(const std::string& out, GLuint texture) {
file_util::write_rgba_png(out, data.data(), w, h);
}
/*!
* Copy rg channels to ba from src to dst.
* The PS2 implementation is confusing, and this is just a guess at how it works.
*/
void TextureAnimator::handle_rg_to_ba(const DmaTransfer& tf) {
dprintf("[tex anim] rg -> ba\n");
ASSERT(tf.size_bytes == sizeof(TextureAnimPcTransform));
auto* data = (const TextureAnimPcTransform*)(tf.data);
dprintf(" src: %d, dest: %d\n", data->src_tbp, data->dst_tbp);
const auto& src = m_textures.find(data->src_tbp);
const auto& dst = m_textures.find(data->dst_tbp);
if (src != m_textures.end() && dst != m_textures.end()) {
ASSERT(src->second.kind == VramEntry::Kind::GPU);
ASSERT(dst->second.kind == VramEntry::Kind::GPU);
ASSERT(src->second.tex.value().texture() != dst->second.tex.value().texture());
FramebufferTexturePairContext ctxt(dst->second.tex.value());
float positions[3 * 4] = {0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0};
float uvs[2 * 4] = {0, 0, 1, 0, 1, 1, 0, 1};
glUniform3fv(m_uniforms.positions, 4, positions);
glUniform2fv(m_uniforms.uvs, 4, uvs);
glUniform1i(m_uniforms.enable_tex, 1);
glUniform4f(m_uniforms.rgba, 256, 256, 256, 128); // TODO - seems wrong.
glUniform4i(m_uniforms.channel_scramble, 0, 1, 0, 1);
// not sure if this is right or not: the entire cloud stuff is kinda broken.
glUniform1f(m_uniforms.alpha_multiply, 1.f);
glBindTexture(GL_TEXTURE_2D, src->second.tex.value().texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColorMask(true, true, true, true);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
} else {
ASSERT_NOT_REACHED();
}
// const auto& vram_src = m_vram_entries.find(data->src_tbp);
// if (vram_src != m_vram_entries.end()) {
// ASSERT(vram_src->second.kind == VramEntry::Kind::GENERIC_PSM32);
// // no idea if this is right, but lets try.
// int w = vram_src->second.width;
// int h = vram_src->second.height;
// u8* tdata = vram_src->second.data_psm32.data();
//
// // file_util::write_rgba_png("./before_transform.png", tdata, w, h);
//
// for (int i = 0; i < w * h; i++) {
// tdata[i * 4 + 2] = tdata[i * 4];
// tdata[i * 4 + 3] = tdata[i * 4 + 1];
// }
//
// // file_util::write_rgba_png("./after_transform.png", tdata, w, h);
//
// } else {
// ASSERT_NOT_REACHED();
// }
}
void TextureAnimator::handle_set_clut_alpha(const DmaTransfer& tf) {
ASSERT_NOT_REACHED(); // TODO: if re-enabling, needs alpha multiplier stuff
glUniform1f(m_uniforms.alpha_multiply, 1.f);
dprintf("[tex anim] set clut alpha\n");
ASSERT(tf.size_bytes == sizeof(TextureAnimPcTransform));
auto* data = (const TextureAnimPcTransform*)(tf.data);
dprintf(" src: %d, dest: %d\n", data->src_tbp, data->dst_tbp);
const auto& tex = m_textures.find(data->dst_tbp);
ASSERT(tex != m_textures.end());
ASSERT(tex->second.kind == VramEntry::Kind::GPU);
FramebufferTexturePairContext ctxt(tex->second.tex.value());
float positions[3 * 4] = {0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0};
float uvs[2 * 4] = {0, 0, 1, 0, 1, 1, 0, 1};
glUniform3fv(m_uniforms.positions, 4, positions);
glUniform2fv(m_uniforms.uvs, 4, uvs);
glUniform1i(m_uniforms.enable_tex, 0); // NO TEXTURE!
glUniform4f(m_uniforms.rgba, 128, 128, 128, 128);
glUniform4i(m_uniforms.channel_scramble, 0, 1, 2, 3);
glBindTexture(GL_TEXTURE_2D, m_dummy_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColorMask(false, false, false, true);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glColorMask(true, true, true, true);
}
void TextureAnimator::handle_copy_clut_alpha(const DmaTransfer& tf) {
ASSERT_NOT_REACHED(); // TODO: if re-enabling, needs alpha multiplier stuff
glUniform1f(m_uniforms.alpha_multiply, 1.f);
dprintf("[tex anim] __copy__ clut alpha\n");
ASSERT(tf.size_bytes == sizeof(TextureAnimPcTransform));
auto* data = (const TextureAnimPcTransform*)(tf.data);
dprintf(" src: %d, dest: %d\n", data->src_tbp, data->dst_tbp);
const auto& dst_tex = m_textures.find(data->dst_tbp);
const auto& src_tex = m_textures.find(data->src_tbp);
ASSERT(dst_tex != m_textures.end());
if (src_tex == m_textures.end()) {
lg::error("Skipping copy clut alpha because source texture at {} wasn't found", data->src_tbp);
return;
}
ASSERT(src_tex != m_textures.end());
ASSERT(dst_tex->second.kind == VramEntry::Kind::GPU);
ASSERT(src_tex->second.kind == VramEntry::Kind::GPU);
FramebufferTexturePairContext ctxt(dst_tex->second.tex.value());
float positions[3 * 4] = {0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0};
float uvs[2 * 4] = {0, 0, 1, 0, 1, 1, 0, 1};
glUniform3fv(m_uniforms.positions, 4, positions);
glUniform2fv(m_uniforms.uvs, 4, uvs);
glUniform1i(m_uniforms.enable_tex, 1);
glUniform4f(m_uniforms.rgba, 128, 128, 128, 128); // TODO - seems wrong.
glUniform4i(m_uniforms.channel_scramble, 0, 1, 2, 3);
glBindTexture(GL_TEXTURE_2D, src_tex->second.tex.value().texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColorMask(false, false, false, true);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glColorMask(true, true, true, true);
}
void TextureAnimator::run_clut_blender_group(DmaTransfer& tf, int idx, u64 frame_idx) {
float f;
ASSERT(tf.size_bytes == 16);
@ -1076,6 +963,27 @@ void TextureAnimator::run_clut_blender_group(DmaTransfer& tf, int idx, u64 frame
}
}
void TextureAnimator::handle_clouds_and_fog(const DmaTransfer& tf, TexturePool* texture_pool) {
ASSERT(tf.size_bytes >= sizeof(SkyInput));
SkyInput input;
memcpy(&input, tf.data, sizeof(SkyInput));
auto tex = run_clouds(input);
if (m_sky_pool_gpu_tex) {
texture_pool->move_existing_to_vram(m_sky_pool_gpu_tex, input.cloud_dest);
ASSERT(texture_pool->lookup(input.cloud_dest).value() == tex);
} else {
TextureInput in;
in.gpu_texture = tex;
in.w = kFinalSkyTextureSize;
in.h = kFinalSkyTextureSize;
in.debug_page_name = "PC-ANIM";
in.debug_name = "clouds";
in.id = get_id_for_tbp(texture_pool, input.cloud_dest, 777);
m_sky_pool_gpu_tex = texture_pool->give_texture_and_load_to_vram(in, input.cloud_dest);
}
}
void TextureAnimator::clear_stale_textures(u64 frame_idx) {
for (auto& group : m_clut_blender_groups) {
if (frame_idx > group.last_updated_frame) {
@ -1138,7 +1046,7 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_
}
m_tex_looking_for_clut = nullptr;
if (upload->force_to_gpu) {
m_erased_on_this_frame.insert(upload->dest);
m_force_to_gpu.insert(upload->dest);
}
break;
case (int)GsTex0::PSM::PSMT8:
@ -1149,7 +1057,7 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_
memcpy(vram.data.data(), ee_mem + upload->data, vram.data.size());
m_tex_looking_for_clut = &vram;
if (upload->force_to_gpu) {
m_erased_on_this_frame.insert(upload->dest);
m_force_to_gpu.insert(upload->dest);
}
break;
case (int)GsTex0::PSM::PSMT4:
@ -1160,7 +1068,7 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_
memcpy(vram.data.data(), ee_mem + upload->data, vram.data.size());
m_tex_looking_for_clut = &vram;
if (upload->force_to_gpu) {
m_erased_on_this_frame.insert(upload->dest);
m_force_to_gpu.insert(upload->dest);
}
break;
default:
@ -1176,7 +1084,6 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_
* These may be modified by animation functions, but most of the time they aren't.
*/
void TextureAnimator::handle_erase_dest(DmaFollower& dma) {
dprintf("[tex anim] erase destination texture\n");
// auto& out = m_new_dest_textures.emplace_back();
VramEntry* entry = nullptr;
@ -1265,67 +1172,7 @@ void TextureAnimator::handle_erase_dest(DmaFollower& dma) {
// set as active
m_current_dest_tbp = entry->dest_texture_address;
m_erased_on_this_frame.insert(entry->dest_texture_address);
}
/*!
* Set up this texture as a GPU texture. This does a few things:
* - sets the Kind to GPU
* - makes sure the texture resource points to a valid OpenGL texture of the right size, without
* triggering the resize/delete sync issue mentioned above.
* - sets flags to indicate if this GPU texture needs to be updated in the pool.
*/
VramEntry* TextureAnimator::setup_vram_entry_for_gpu_texture(int w, int h, int tbp) {
auto pp = scoped_prof("setup-vram-entry");
const auto& existing_dest = m_textures.find(tbp);
// see if we have an existing OpenGL texture at all
bool existing_opengl = existing_dest != m_textures.end() && existing_dest->second.tex.has_value();
// see if we can reuse it (same size)
bool can_reuse = true;
if (existing_opengl) {
if (existing_dest->second.tex->height() != h || existing_dest->second.tex->width() != w) {
dprintf(" can't reuse, size mismatch\n");
can_reuse = false;
}
} else {
dprintf(" can't reuse, first time using this address\n");
can_reuse = false;
}
VramEntry* entry = nullptr;
if (can_reuse) {
// texture is the right size, just use it again.
entry = &existing_dest->second;
} else {
if (existing_opengl) {
// we have a texture, but it's the wrong type. Remember that we need to update the pool
entry = &existing_dest->second;
entry->needs_pool_update = true;
} else {
// create the entry. Also need to update the pool
entry = &m_textures[tbp];
entry->reset();
entry->needs_pool_update = true;
}
// if we already have a texture, try to swap it with an OpenGL texture of the right size.
if (entry->tex.has_value()) {
// gross
m_opengl_texture_pool.free(entry->tex->texture(), entry->tex->width(), entry->tex->height());
entry->tex->update_texture_size(w, h);
entry->tex->update_texture_unsafe(m_opengl_texture_pool.allocate(w, h));
} else {
entry->tex.emplace(w, h, GL_UNSIGNED_INT_8_8_8_8_REV);
}
}
entry->kind = VramEntry::Kind::GPU;
entry->tex_width = w;
entry->tex_height = h;
entry->dest_texture_address = tbp;
return entry;
m_force_to_gpu.insert(entry->dest_texture_address);
}
/*!
@ -1432,24 +1279,6 @@ void TextureAnimator::handle_draw(DmaFollower& dma, TexturePool& texture_pool) {
}
}
/*!
* Get a 16x16 CLUT texture, stored in psm32 (in-memory format, not vram). Fatal if it doesn't
* exist.
*/
const u32* TextureAnimator::get_clut_16_16_psm32(int cbp) {
const auto& clut_lookup = m_textures.find(cbp);
if (clut_lookup == m_textures.end()) {
printf("get_clut_16_16_psm32 referenced an unknown clut texture in %d\n", cbp);
ASSERT_NOT_REACHED();
}
if (clut_lookup->second.kind != VramEntry::Kind::CLUT16_16_IN_PSM32) {
ASSERT_NOT_REACHED();
}
return (const u32*)clut_lookup->second.data.data();
}
/*!
* Using the current shader settings, load the CLUT table to the texture coverter "VRAM".
*/
@ -1506,7 +1335,7 @@ GLuint TextureAnimator::make_or_get_gpu_texture_for_current_shader(TexturePool&
case VramEntry::Kind::GPU:
// already on the GPU, just return it.
return lookup->second.tex->texture();
// data on the CPU, in PSM32
// data on the CPU, in PSM32
case VramEntry::Kind::GENERIC_PSM32:
// see how we're reading it:
switch (m_current_shader.tex0.psm()) {
@ -1597,74 +1426,6 @@ GLuint TextureAnimator::make_or_get_gpu_texture_for_current_shader(TexturePool&
}
}
void TextureAnimator::set_up_opengl_for_fixed(const FixedLayerDef& def,
std::optional<GLint> texture) {
if (texture) {
glBindTexture(GL_TEXTURE_2D, *texture);
glUniform1i(m_uniforms.enable_tex, 1);
} else {
glBindTexture(GL_TEXTURE_2D, m_dummy_texture);
glUniform1i(m_uniforms.enable_tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
// tex0
// assuming default-texture-anim-layer-func, which sets 1.
glUniform1i(m_uniforms.tcc, 1);
// ASSERT(shader.tex0.tfx() == GsTex0::TextureFunction::MODULATE);
// tex1
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glColorMask(def.channel_masks[0], def.channel_masks[1], def.channel_masks[2],
def.channel_masks[3]);
if (def.z_test) {
ASSERT_NOT_REACHED();
} else {
glDisable(GL_DEPTH_TEST);
}
if (def.clamp_u) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
}
if (def.clamp_v) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (def.blend_enable) {
auto blend_a = def.blend_modes[0];
auto blend_b = def.blend_modes[1];
auto blend_c = def.blend_modes[2];
auto blend_d = def.blend_modes[3];
glEnable(GL_BLEND);
// 0 2 0 1
if (blend_a == GsAlpha::BlendMode::SOURCE && blend_b == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_c == GsAlpha::BlendMode::SOURCE && blend_d == GsAlpha::BlendMode::DEST) {
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ZERO);
} else if (blend_a == GsAlpha::BlendMode::SOURCE && blend_b == GsAlpha::BlendMode::DEST &&
blend_c == GsAlpha::BlendMode::SOURCE && blend_d == GsAlpha::BlendMode::DEST) {
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
} else {
fmt::print("unhandled blend: {} {} {} {}\n", (int)blend_a, (int)blend_b, (int)blend_c,
(int)blend_d);
ASSERT_NOT_REACHED();
}
} else {
glDisable(GL_BLEND);
}
glUniform4i(m_uniforms.channel_scramble, 0, 1, 2, 3);
}
bool TextureAnimator::set_up_opengl_for_shader(const ShaderContext& shader,
std::optional<GLuint> texture,
bool prim_abe) {
@ -1795,6 +1556,152 @@ bool TextureAnimator::set_up_opengl_for_shader(const ShaderContext& shader,
return writes_alpha;
}
/*!
* Set up this texture as a GPU texture. This does a few things:
* - sets the Kind to GPU
* - makes sure the texture resource points to a valid OpenGL texture of the right size, without
* triggering the resize/delete sync issue mentioned above.
* - sets flags to indicate if this GPU texture needs to be updated in the pool.
*/
VramEntry* TextureAnimator::setup_vram_entry_for_gpu_texture(int w, int h, int tbp) {
auto pp = scoped_prof("setup-vram-entry");
const auto& existing_dest = m_textures.find(tbp);
// see if we have an existing OpenGL texture at all
bool existing_opengl = existing_dest != m_textures.end() && existing_dest->second.tex.has_value();
// see if we can reuse it (same size)
bool can_reuse = true;
if (existing_opengl) {
if (existing_dest->second.tex->height() != h || existing_dest->second.tex->width() != w) {
dprintf(" can't reuse, size mismatch\n");
can_reuse = false;
}
} else {
dprintf(" can't reuse, first time using this address\n");
can_reuse = false;
}
VramEntry* entry = nullptr;
if (can_reuse) {
// texture is the right size, just use it again.
entry = &existing_dest->second;
} else {
if (existing_opengl) {
// we have a texture, but it's the wrong type. Remember that we need to update the pool
entry = &existing_dest->second;
entry->needs_pool_update = true;
} else {
// create the entry. Also need to update the pool
entry = &m_textures[tbp];
entry->reset();
entry->needs_pool_update = true;
}
// if we already have a texture, try to swap it with an OpenGL texture of the right size.
if (entry->tex.has_value()) {
// gross
m_opengl_texture_pool.free(entry->tex->texture(), entry->tex->width(), entry->tex->height());
entry->tex->update_texture_size(w, h);
entry->tex->update_texture_unsafe(m_opengl_texture_pool.allocate(w, h));
} else {
entry->tex.emplace(w, h, GL_UNSIGNED_INT_8_8_8_8_REV);
}
}
entry->kind = VramEntry::Kind::GPU;
entry->tex_width = w;
entry->tex_height = h;
entry->dest_texture_address = tbp;
return entry;
}
/*!
* Get a 16x16 CLUT texture, stored in psm32 (in-memory format, not vram). Fatal if it doesn't
* exist.
*/
const u32* TextureAnimator::get_clut_16_16_psm32(int cbp) {
const auto& clut_lookup = m_textures.find(cbp);
if (clut_lookup == m_textures.end()) {
printf("get_clut_16_16_psm32 referenced an unknown clut texture in %d\n", cbp);
ASSERT_NOT_REACHED();
}
if (clut_lookup->second.kind != VramEntry::Kind::CLUT16_16_IN_PSM32) {
ASSERT_NOT_REACHED();
}
return (const u32*)clut_lookup->second.data.data();
}
void TextureAnimator::set_up_opengl_for_fixed(const FixedLayerDef& def,
std::optional<GLint> texture) {
if (texture) {
glBindTexture(GL_TEXTURE_2D, *texture);
glUniform1i(m_uniforms.enable_tex, 1);
} else {
glBindTexture(GL_TEXTURE_2D, m_dummy_texture);
glUniform1i(m_uniforms.enable_tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
// tex0
// assuming default-texture-anim-layer-func, which sets 1.
glUniform1i(m_uniforms.tcc, 1);
// ASSERT(shader.tex0.tfx() == GsTex0::TextureFunction::MODULATE);
// tex1
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glColorMask(def.channel_masks[0], def.channel_masks[1], def.channel_masks[2],
def.channel_masks[3]);
if (def.z_test) {
ASSERT_NOT_REACHED();
} else {
glDisable(GL_DEPTH_TEST);
}
if (def.clamp_u) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
}
if (def.clamp_v) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (def.blend_enable) {
auto blend_a = def.blend_modes[0];
auto blend_b = def.blend_modes[1];
auto blend_c = def.blend_modes[2];
auto blend_d = def.blend_modes[3];
glEnable(GL_BLEND);
// 0 2 0 1
if (blend_a == GsAlpha::BlendMode::SOURCE && blend_b == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_c == GsAlpha::BlendMode::SOURCE && blend_d == GsAlpha::BlendMode::DEST) {
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ZERO);
} else if (blend_a == GsAlpha::BlendMode::SOURCE && blend_b == GsAlpha::BlendMode::DEST &&
blend_c == GsAlpha::BlendMode::SOURCE && blend_d == GsAlpha::BlendMode::DEST) {
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
} else {
fmt::print("unhandled blend: {} {} {} {}\n", (int)blend_a, (int)blend_b, (int)blend_c,
(int)blend_d);
ASSERT_NOT_REACHED();
}
} else {
glDisable(GL_BLEND);
}
glUniform4i(m_uniforms.channel_scramble, 0, 1, 2, 3);
}
namespace {
void set_uniform(GLuint uniform, const math::Vector<float, 4>& vf) {
glUniform4f(uniform, vf.x(), vf.y(), vf.z(), vf.w());
@ -2503,3 +2410,187 @@ void TextureAnimator::setup_texture_anims() {
m_krew_holo_anim_array_idx = create_fixed_anim_array({def});
}
}
// initial values of the random table for cloud texture generation.
constexpr Vector16ub kInitialRandomTable[TextureAnimator::kRandomTableSize] = {
{0x20, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x89, 0x67, 0x45, 0x23, 0x1},
{0x37, 0x82, 0x87, 0x23, 0x78, 0x87, 0x4, 0x32, 0x97, 0x91, 0x48, 0x98, 0x30, 0x38, 0x89, 0x87},
{0x62, 0x47, 0x2, 0x62, 0x78, 0x92, 0x28, 0x90, 0x81, 0x47, 0x72, 0x28, 0x83, 0x29, 0x71, 0x68},
{0x28, 0x61, 0x17, 0x62, 0x87, 0x74, 0x38, 0x12, 0x83, 0x9, 0x78, 0x12, 0x76, 0x31, 0x72, 0x80},
{0x39, 0x72, 0x98, 0x34, 0x72, 0x98, 0x69, 0x78, 0x65, 0x71, 0x98, 0x83, 0x97, 0x23, 0x98, 0x1},
{0x97, 0x38, 0x72, 0x98, 0x23, 0x87, 0x23, 0x98, 0x93, 0x72, 0x98, 0x20, 0x81, 0x29, 0x10,
0x62},
{0x28, 0x75, 0x38, 0x82, 0x99, 0x30, 0x72, 0x87, 0x83, 0x9, 0x14, 0x98, 0x10, 0x43, 0x87, 0x29},
{0x87, 0x23, 0x0, 0x87, 0x18, 0x98, 0x12, 0x98, 0x10, 0x98, 0x21, 0x83, 0x90, 0x37, 0x62,
0x71}};
/*!
* Update dest and random_table.
*/
int make_noise_texture(u8* dest, Vector16ub* random_table, int dim, int random_index_in) {
ASSERT(dim % 16 == 0);
const int qw_per_row = dim / 16;
for (int row = 0; row < dim; row++) {
for (int qw_in_row = 0; qw_in_row < qw_per_row; qw_in_row++) {
const int row_start_qwi = row * qw_per_row;
Vector16ub* rand_rows[4] = {
random_table + (random_index_in + 0) % TextureAnimator::kRandomTableSize,
random_table + (random_index_in + 3) % TextureAnimator::kRandomTableSize,
random_table + (random_index_in + 5) % TextureAnimator::kRandomTableSize,
random_table + (random_index_in + 7) % TextureAnimator::kRandomTableSize,
};
const int qwi = row_start_qwi + qw_in_row;
*rand_rows[3] = *rand_rows[0] + *rand_rows[1] + *rand_rows[2];
memcpy(dest + 16 * qwi, rand_rows[3]->data(), 16);
random_index_in = (random_index_in + 1) % TextureAnimator::kRandomTableSize;
}
}
return random_index_in;
}
int update_opengl_noise_texture(GLuint texture,
u8* temp,
Vector16ub* random_table,
int dim,
int random_index_in) {
int ret = make_noise_texture(temp, random_table, dim, random_index_in);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, dim, dim, 0, GL_RED, GL_UNSIGNED_BYTE, temp);
glGenerateMipmap(GL_TEXTURE_2D);
return ret;
}
void debug_save_opengl_u8_texture(const std::string& out, GLuint texture) {
glBindTexture(GL_TEXTURE_2D, texture);
int w, h;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
fmt::print("saving texture with size {} x {}\n", w, h);
std::vector<u8> data_r(w * h);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, data_r.data());
std::vector<u8> data(w * h * 4);
for (int i = 0; i < w * h; i++) {
data[i * 4] = data_r[i];
data[i * 4 + 1] = data_r[i];
data[i * 4 + 2] = data_r[i];
data[i * 4 + 3] = 255;
}
file_util::write_rgba_png(out, data.data(), w, h);
}
void TextureAnimator::setup_sky() {
// sky
// initialize random table with values from the game.
for (int i = 0; i < kRandomTableSize; i++) {
m_random_table[i] = kInitialRandomTable[i];
}
const float max_times[4] = {4800.f, 2400.f, 1200.f, 600.f};
const float scales[4] = {0.5, 0.2, 0.15, 0.0075f};
for (int i = 0, dim = kFinalSkyTextureSize >> (kNumSkyNoiseLayers - 1); i < kNumSkyNoiseLayers;
i++, dim *= 2) {
auto& tex = m_sky_noise_textures[i];
tex.temp_data.resize(dim * dim);
tex.max_time = max_times[i];
tex.scale = scales[i];
tex.dim = dim;
glGenTextures(1, &tex.new_tex);
m_random_index = update_opengl_noise_texture(tex.new_tex, tex.temp_data.data(), m_random_table,
dim, m_random_index);
glGenTextures(1, &tex.old_tex);
m_random_index = update_opengl_noise_texture(tex.old_tex, tex.temp_data.data(), m_random_table,
dim, m_random_index);
// debug_save_opengl_u8_texture(fmt::format("{}_old.png", dim), tex.old_tex);
// debug_save_opengl_u8_texture(fmt::format("{}_new.png", dim), tex.new_tex);
}
}
GLint TextureAnimator::run_clouds(const SkyInput& input) {
m_debug_sky_input = input;
// anim 0 creates a clut with rgba = 128, 128, 128, i, at tbp = (24 * 32)
// (this has alphas from 0 to 256).
// This step is eliminated on OpenGL because we don't need this simple ramp CLUT.
// the next anim uses that clut with noise textures.
// so we expect those textures to have values like (128, 128, 128, x) where 0 <= x <= 255.
// (in OpenGL, we create these with a single-channel texture, with that channel in 0 - 1)
// this repeats for different resolutions (4 times in total)
// Next, these are blended together into a single texture
// The blend mode is 0, 2, 0, 1
// [(CSource - 0) * Asource] >> 7 + CDest
// in the PS2, CSource is 128, so the >> 7 cancels entirely.
int times_idx = 0;
// Anim 0:
// this create a 16x16 CLUT with RGB = 128, 128, 128 and alpha = i
// (texture-anim-alpha-ramp-clut-init)
// it's uploaded 24 * 32 = 768. (texture-anim-alpha-ramp-clut-upload)
times_idx++;
{
FramebufferTexturePairContext ctxt(m_sky_blend_texture);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(m_uniforms.tcc, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glColorMask(true, true, true, true);
glDisable(GL_DEPTH_TEST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ZERO);
glUniform4i(m_uniforms.channel_scramble, 0, 0, 0, 0);
glUniform1f(m_uniforms.alpha_multiply, 1.f);
glUniform1i(m_uniforms.enable_tex, 1);
float positions[3 * 4] = {0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0};
glUniform3fv(m_uniforms.positions, 4, positions);
float uv[2 * 4] = {0, 0, 1, 0, 1, 1, 0, 1};
glUniform2fv(m_uniforms.uvs, 4, uv);
// Anim 1:
// noise (16x16)
// while (noise_layer_idx) {
for (int noise_layer_idx = 0; noise_layer_idx < kNumSkyNoiseLayers; noise_layer_idx++) {
const float new_time = input.times[times_idx];
auto& ntp = m_sky_noise_textures[noise_layer_idx];
if (new_time < ntp.last_time) {
std::swap(ntp.new_tex, ntp.old_tex);
m_random_index = update_opengl_noise_texture(ntp.new_tex, ntp.temp_data.data(),
m_random_table, ntp.dim, m_random_index);
}
ntp.last_time = new_time;
float new_interp = ntp.last_time / ntp.max_time;
glBindTexture(GL_TEXTURE_2D, ntp.new_tex);
float s = new_interp * ntp.scale * 128.f;
set_uniform(m_uniforms.rgba, math::Vector4f(s, s, s, 256));
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindTexture(GL_TEXTURE_2D, ntp.old_tex);
s = (1.f - new_interp) * ntp.scale * 128.f;
set_uniform(m_uniforms.rgba, math::Vector4f(s, s, s, 256));
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
times_idx++;
}
}
FramebufferTexturePairContext ctxt(m_sky_final_texture);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(m_uniforms.enable_tex, 2);
glBindTexture(GL_TEXTURE_2D, m_sky_blend_texture.texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glUniform1f(m_uniforms.minimum, input.cloud_min);
glUniform1f(m_uniforms.maximum, input.cloud_max);
glDisable(GL_BLEND);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
return m_sky_final_texture.texture();
}

View file

@ -35,7 +35,6 @@ struct VramEntry {
int dest_texture_address = 0;
int cbp = 0;
std::optional<FramebufferTexturePair> tex;
// math::Vector<u8, 4> rgba_clear;
bool needs_pool_update = false;
GpuTexture* pool_gpu_tex = nullptr;
@ -46,9 +45,7 @@ struct VramEntry {
tex_height = 0;
tex_width = 0;
cbp = 0;
// tex.reset();
needs_pool_update = false;
// pool_gpu_tex = nullptr;
}
};
@ -205,6 +202,37 @@ struct FixedAnimArray {
std::vector<FixedAnim> anims;
};
/*
(deftype sky-input (structure)
((fog-height float)
(cloud-min float)
(cloud-max float)
(times float 9)
(cloud-dest int32)
)
)
*/
struct SkyInput {
float fog_height;
float cloud_min;
float cloud_max;
float times[9];
int32_t cloud_dest;
};
using Vector16ub = math::Vector<u8, 16>;
struct NoiseTexturePair {
GLuint old_tex = 0;
GLuint new_tex = 0;
std::vector<u8> temp_data;
int dim = 0;
float scale = 0;
float last_time = 0;
float max_time = 0;
};
class TexturePool;
class TextureAnimator {
@ -223,28 +251,24 @@ class TextureAnimator {
private:
void copy_private_to_public();
void setup_texture_anims();
void setup_sky();
void handle_upload_clut_16_16(const DmaTransfer& tf, const u8* ee_mem);
void handle_generic_upload(const DmaTransfer& tf, const u8* ee_mem);
void handle_clouds_and_fog(const DmaTransfer& tf, TexturePool* texture_pool);
void handle_erase_dest(DmaFollower& dma);
void handle_set_shader(DmaFollower& dma);
void handle_draw(DmaFollower& dma, TexturePool& texture_pool);
void handle_rg_to_ba(const DmaTransfer& tf);
void handle_set_clut_alpha(const DmaTransfer& tf);
void handle_copy_clut_alpha(const DmaTransfer& tf);
VramEntry* setup_vram_entry_for_gpu_texture(int w, int h, int tbp);
void set_up_opengl_for_fixed(const FixedLayerDef& def, std::optional<GLint> texture);
bool set_up_opengl_for_shader(const ShaderContext& shader,
std::optional<GLuint> texture,
bool prim_abe);
void set_up_opengl_for_fixed(const FixedLayerDef& def, std::optional<GLint> texture);
void load_clut_to_converter();
const u32* get_clut_16_16_psm32(int cbp);
GLuint make_temp_gpu_texture(const u32* data, u32 width, u32 height);
GLuint make_or_get_gpu_texture_for_current_shader(TexturePool& texture_pool);
const u32* get_clut_16_16_psm32(int cbp);
void load_clut_to_converter();
void force_to_gpu(int tbp);
int create_fixed_anim_array(const std::vector<FixedAnimDef>& defs);
@ -278,18 +302,15 @@ class TextureAnimator {
std::unordered_map<u32, VramEntry> m_textures;
std::unordered_map<u64, PcTextureId> m_ids_by_vram;
std::set<u32> m_erased_on_this_frame;
std::set<u32> m_force_to_gpu; // rename? or rework to not need?
struct TempTexture {
GLuint tex;
u32 w, h;
};
std::vector<TempTexture> m_in_use_temp_textures;
ShaderContext m_current_shader;
TextureConverter m_converter;
int m_current_dest_tbp = -1;
std::vector<TempTexture> m_in_use_temp_textures;
ShaderContext m_current_shader;
GLuint m_vao;
GLuint m_vertex_buffer;
struct Vertex {
@ -307,6 +328,7 @@ class TextureAnimator {
GLuint channel_scramble;
GLuint tcc;
GLuint alpha_multiply;
GLuint minimum, maximum;
} m_uniforms;
struct {
@ -318,6 +340,7 @@ class TextureAnimator {
u8 m_index_to_clut_addr[256];
OpenGLTexturePool m_opengl_texture_pool;
int m_current_dest_tbp = -1;
std::vector<GLuint> m_private_output_slots;
std::vector<GLuint> m_public_output_slots;
@ -351,6 +374,7 @@ class TextureAnimator {
const std::string& suffix1,
const std::optional<std::string>& dgo);
void run_clut_blender_group(DmaTransfer& tf, int idx, u64 frame_idx);
GLint run_clouds(const SkyInput& input);
Psm32ToPsm8Scrambler m_psm32_to_psm8_8_8, m_psm32_to_psm8_16_16, m_psm32_to_psm8_32_32,
m_psm32_to_psm8_64_64;
@ -372,4 +396,23 @@ class TextureAnimator {
int m_krew_holo_anim_array_idx = -1;
std::vector<FixedAnimArray> m_fixed_anim_arrays;
public:
// must be power of 2 - number of 16-byte rows in random table. (original game has 8)
static constexpr int kRandomTableSize = 8;
// must be power of 2 - dimensions of the final clouds textures
static constexpr int kFinalSkyTextureSize = 128;
// number of small sub-textures. Must be less than log2(kFinalTextureSize).
static constexpr int kNumSkyNoiseLayers = 4;
private:
SkyInput m_debug_sky_input;
Vector16ub m_random_table[kRandomTableSize];
int m_random_index = 0;
NoiseTexturePair m_sky_noise_textures[kNumSkyNoiseLayers];
FramebufferTexturePair m_sky_blend_texture;
FramebufferTexturePair m_sky_final_texture;
GpuTexture* m_sky_pool_gpu_tex = nullptr;
};

View file

@ -1,6 +1,7 @@
#include "common/log/log.h"
#include "Generic2.h"
#include "game/graphics/gfx.h"
void Generic2::opengl_setup(ShaderLibrary& shaders) {
// create OpenGL objects

View file

@ -9,7 +9,6 @@
#include "common/util/Timer.h"
#include "game/graphics/opengl_renderer/loader/common.h"
#include "game/graphics/pipelines/opengl.h"
#include "game/graphics/texture/TexturePool.h"
class Loader {

View file

@ -7,13 +7,28 @@ uniform int enable_tex;
uniform int tcc;
uniform ivec4 channel_scramble;
uniform float alpha_multiply;
uniform float minimum;
uniform float maximum;
in vec2 uv;
uniform sampler2D tex_T0;
void main() {
float cloud_lookup(float v, float minimum, float maximum) {
maximum = max(minimum, maximum);
if (v <= minimum) {
return 0;
}
if (v >= maximum) {
return 1;
}
float alpha = (v - minimum) / (maximum - minimum);
float sin_alpha = sin(alpha * 3.1415926 / 2.f);
return sin_alpha * sin_alpha;
}
void main() {
if (enable_tex == 1) {
vec4 tex_color = texture(tex_T0, uv);
vec4 unscambled_tex = vec4(tex_color[channel_scramble[0]],
@ -26,9 +41,15 @@ void main() {
} else {
color.xyz *= unscambled_tex.xyz;
}
} else if (enable_tex == 2) {
// cloud version
vec4 tex_color = texture(tex_T0, uv);
color.x = 0.5;
color.y = 0.5;
color.z = 0.5;
color.a = 0.5 * cloud_lookup(tex_color.r, minimum, maximum);
} else {
color = (rgba / 128.);
}
color.a *= alpha_multiply;
}

View file

@ -12,10 +12,11 @@
#include "common/util/SmallVector.h"
#include "common/versions/versions.h"
#include "game/graphics/pipelines/opengl.h"
#include "game/graphics/texture/TextureConverter.h"
#include "game/graphics/texture/TextureID.h"
#include "third-party/glad/include/glad/glad.h"
// verify all texture lookups.
// will make texture lookups slower and likely caused dropped frames when loading
constexpr bool EXTRA_TEX_DEBUG = false;

View file

@ -230,7 +230,7 @@
(set! (-> upload-record height) (-> s5-1 h))
(set! (-> upload-record dest) (-> s5-1 dest 0))
(set! (-> upload-record format) (-> s5-1 psm))
(set! (-> upload-record force-to-gpu) 0)
(set! (-> upload-record force-to-gpu) 1)
)
(&+! (-> arg0 base) 16)

View file

@ -196,6 +196,7 @@
(metkor 38)
(shield 39)
(krew-holo 40)
(clouds-and-fog 41)
)
(deftype texture-anim-pc-upload (structure)
@ -614,6 +615,22 @@
(define-extern *metkor-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *shield-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *krew-holo-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *toxic-slime-texture-anim-array* (texture-anim-array texture-anim))
(defun pc-update-anim-frame-time ((anim texture-anim))
(when (not (paused?))
(with-pp
(let ((f0-2 (+ (-> anim frame-time) (* (-> anim frame-delta) (-> pp clock seconds-per-frame))))
(f1-2 (-> anim frame-mod))
)
(set! (-> anim frame-time) (- f0-2 (* (the float (the int (/ f0-2 f1-2))) f1-2)))
)
(if (< (-> anim frame-time) 0.0)
(+! (-> anim frame-time) (-> anim frame-mod))
)
)
)
)
(defun pc-update-fixed-anim ((bucket bucket-id) (anim-id texture-anim-pc) (anim-array texture-anim-array))
"Run a 'fixed' texture-anim, which should run entirely in C++."
@ -661,18 +678,7 @@
(when (-> anim func)
((-> anim func) dma-buf anim)
)
(when (not (paused?))
(with-pp
(let ((f0-2 (+ (-> anim frame-time) (* (-> anim frame-delta) (-> pp clock seconds-per-frame))))
(f1-2 (-> anim frame-mod))
)
(set! (-> anim frame-time) (- f0-2 (* (the float (the int (/ f0-2 f1-2))) f1-2)))
)
(if (< (-> anim frame-time) 0.0)
(+! (-> anim frame-time) (-> anim frame-mod))
)
)
)
(pc-update-anim-frame-time anim)
)
)
)
@ -685,21 +691,58 @@
(none)
)
(deftype sky-input (structure)
((fog-height float)
(cloud-min float)
(cloud-max float)
(times float 9)
(cloud-dest int32)
)
)
(defun make-sky-input ((si sky-input))
(set! (-> si fog-height) (-> (the-as (array texture-anim) *sky-texture-anim-array*) 8 extra z))
(set! (-> si cloud-min) (-> *sky-texture-anim-array* array-data 7 extra y))
(set! (-> si cloud-max) (-> *sky-texture-anim-array* array-data 7 extra z))
(set! (-> si cloud-dest) (the int (-> *sky-texture-anim-array* array-data 6 tex dest 0)))
(dotimes (i 9)
(set! (-> si times i)
(-> *sky-texture-anim-array* array-data i frame-time)
)
)
)
(defun update-texture-anim ((bucket bucket-id) (anim-array texture-anim-array))
"Generate all DMA to update all textures in the given list for the given bucket."
(let ((anim-idx 0))
(cond
((or (= anim-array *sky-texture-anim-array*)
)
((= anim-array *toxic-slime-texture-anim-array*)
;; not yet implemented
(return #f)
)
((= anim-array *sky-texture-anim-array*)
(when (= bucket (bucket-id tex-lcom-sky-post))
;; skip. I believe this is only used to generate the envmap texture for the ocean.
;; it generates the exact same thing, so if we want this on PC one day, we can just
;; steal if from the beginning of the frame.
(return #f)
)
;; for sky, we basically emulate the full thing
;; (format *stdcon* "doing sky to bucket ~d~%" bucket)
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
(pc-texture-anim-flag start-anim-array dma-buf)
(pc-texture-anim-flag clouds-and-fog dma-buf :qwc 4)
(make-sky-input (the sky-input (-> dma-buf base)))
(&+! (-> dma-buf base) 64)
(pc-texture-anim-flag finish-anim-array dma-buf)
(dotimes (i 8) ;; intentially skipping fog here!!
(pc-update-anim-frame-time (-> *sky-texture-anim-array* array-data i))
)
)
;; falling through on purpose
(set! anim-idx 8) ;; fog
;(return #f)
)
((= anim-array *skull-gem-texture-anim-array*)
(pc-update-fixed-anim bucket (texture-anim-pc skull-gem) anim-array)
@ -780,7 +823,6 @@
((= anim-array *jakb-prison-texture-anim-array*)
;; prison is simple, and we reimplemented it in C++.
;; so we just have to send the frame-time value.
;; (format *stdcon* "doing prison-jak~%")
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
@ -800,7 +842,6 @@
((= anim-array *darkjak-hires-texture-anim-array*)
;; oracle is simple, and we reimplemented it in C++.
;; so we just have to send the frame-time value.
;; (format *stdcon* "doing oracle jak~%")
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
@ -819,7 +860,6 @@
((= anim-array *darkjak-hires-nest-texture-anim-array*)
;; oracle is simple, and we reimplemented it in C++.
;; so we just have to send the frame-time value.
;; (format *stdcon* "doing nest jak~%")
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
@ -838,7 +878,6 @@
((= anim-array *kor-transform-texture-anim-array*)
;; kor is simple, and we reimplemented it in C++.
;; so we just have to send the frame-time value.
;; (format *stdcon* "doing kor~%")
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
@ -855,10 +894,11 @@
(return #f)
)
(else
(return #f) ;; HACK!!!
(format 0 "Unhandled texture animation!~%")
(break!)
(return #f)
)
)
;;
;; (return #f)
;;
@ -877,11 +917,11 @@
(pc-texture-anim-flag start-anim-array dma-buf)
;; loop over animated textures. Each will produce a single texture.
(dotimes (anim-idx (-> anim-array length))
;(dotimes (anim-idx (-> anim-array length))
(while (< anim-idx (-> anim-array length))
(let* ((anim (-> anim-array array-data anim-idx))
(dest-tex (-> anim tex))
)
; (format 0 "texture anim dest tex: ~A~%" dest-tex)
(when dest-tex
0
(let ((tex-width (-> dest-tex w)))
@ -901,7 +941,6 @@
)
)
(when (and (nonzero? tex-width) (nonzero? tex-height))
; (format 0 " clearing~%")
;; configure for drawing to this texture.
(pc-texture-anim-flag erase-and-init dma-buf)
(dma-buffer-add-gs-set-flusha dma-buf
@ -984,6 +1023,7 @@
)
)
)
(+! anim-idx 1)
)
;; reset GS registers - we messed with frame/scissor.
@ -1001,6 +1041,7 @@
(none)
)
)
)
(defun no-alpha-texture-anim-layer-func ((dma-buf dma-buffer) (fbp-to-draw uint) (width int) (height int) (layer texture-anim-layer) (time float))
"Like others, but the tcc value is 0"

View file

@ -170,7 +170,7 @@ void assign_colors(Node& root, std::vector<Color>& palette_out) {
if (n.rgb_sum_count) {
n.final_idx = idx++;
palette_out.emplace_back(n.r_sum / n.rgb_sum_count, n.g_sum / n.rgb_sum_count,
n.b_sum / n.rgb_sum_count);
n.b_sum / n.rgb_sum_count, 0);
}
});
}