Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jak2] Toxic Slime texture animations #2922

Merged
merged 1 commit into from Aug 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/texture/texture_slots.cpp
Expand Up @@ -38,6 +38,8 @@ std::vector<std::string> jak2_slots = {
"fort-roboscreen-dest",
"squid-env-rim-dest",
"krew-holo-dest",
"cas-toxic-slime-dest",
"cas-toxic-slime-scroll-dest",
};

}
Expand Down
6 changes: 5 additions & 1 deletion decompiler/config/jak2/ntsc_v1/inputs.jsonc
Expand Up @@ -563,7 +563,11 @@

// krew
"krew-holo-dest",
"metkor-phong-env"
"metkor-phong-env",

// castle slime
"cas-toxic-slime-scroll-dest",
"cas-toxic-slime-dest"

// "kor-eyeeffect-formorph",
// "kor-eyeeffect-formorph-start",
Expand Down
251 changes: 216 additions & 35 deletions game/graphics/opengl_renderer/TextureAnimator.cpp
Expand Up @@ -357,7 +357,16 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* co
m_psm32_to_psm8_32_32(32, 32, 16, 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) {
m_sky_final_texture(kFinalSkyTextureSize, kFinalSkyTextureSize, GL_UNSIGNED_INT_8_8_8_8_REV),
m_slime_blend_texture(kFinalSlimeTextureSize,
kFinalSlimeTextureSize,
GL_UNSIGNED_INT_8_8_8_8_REV),
m_slime_final_texture(kFinalSlimeTextureSize,
kFinalSlimeTextureSize,
GL_UNSIGNED_INT_8_8_8_8_REV),
m_slime_final_scroll_texture(kFinalSlimeTextureSize,
kFinalSlimeTextureSize,
GL_UNSIGNED_INT_8_8_8_8_REV) {
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vertex_buffer);
glBindVertexArray(m_vao);
Expand Down Expand Up @@ -393,6 +402,7 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* co
m_uniforms.alpha_multiply = glGetUniformLocation(shader.id(), "alpha_multiply");
m_uniforms.minimum = glGetUniformLocation(shader.id(), "minimum");
m_uniforms.maximum = glGetUniformLocation(shader.id(), "maximum");
m_uniforms.slime_scroll = glGetUniformLocation(shader.id(), "slime_scroll");

// create a single "dummy texture" with all 0 data.
// this is faster and easier than switching shaders to one without texturing, and is used
Expand Down Expand Up @@ -481,9 +491,25 @@ int TextureAnimator::create_fixed_anim_array(const std::vector<FixedAnimDef>& de
return ret;
}

void imgui_show_tex(GLuint tex) {
glBindTexture(GL_TEXTURE_2D, tex);
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)tex, ImVec2(w, h));
}

void TextureAnimator::draw_debug_window() {
ImGui::Checkbox("fast-scrambler", &m_debug.use_fast_scrambler);

ImGui::Text("Slime:");
ImGui::Text("dests %d %d", m_debug_slime_input.dest, m_debug_slime_input.scroll_dest);
for (int i = 0; i < 9; i++) {
ImGui::Text("Time[%d]: %f", i, m_debug_slime_input.times[i]);
}
imgui_show_tex(m_slime_final_texture.texture());
imgui_show_tex(m_slime_final_scroll_texture.texture());

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);
Expand All @@ -492,25 +518,13 @@ void TextureAnimator::draw_debug_window() {
}
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));
imgui_show_tex(m_sky_blend_texture.texture());
imgui_show_tex(m_sky_final_texture.texture());

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]);
glBindTexture(GL_TEXTURE_2D, m_private_output_slots[i]);
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_private_output_slots[i], ImVec2(w, h));
imgui_show_tex(m_private_output_slots[i]);
ImGui::Checkbox(fmt::format("mark {}", i).c_str(), &m_output_debug_flags.at(i).b);
}
glBindTexture(GL_TEXTURE_2D, 0);
Expand Down Expand Up @@ -597,6 +611,7 @@ enum PcTextureAnimCodes {
SHIELD = 39,
KREW_HOLO = 40,
CLOUDS_AND_FOG = 41,
SLIME = 42,
};

// metadata for an upload from GOAL memory
Expand Down Expand Up @@ -756,6 +771,10 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
auto p = scoped_prof("clouds-and-fog");
handle_clouds_and_fog(tf, texture_pool);
} break;
case SLIME: {
auto p = scoped_prof("slime");
handle_slime(tf, texture_pool);
} break;
default:
fmt::print("bad imm: {}\n", vif0.immediate);
ASSERT_NOT_REACHED();
Expand Down Expand Up @@ -971,7 +990,7 @@ void TextureAnimator::handle_clouds_and_fog(const DmaTransfer& tf, TexturePool*

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);
ASSERT((int)texture_pool->lookup(input.cloud_dest).value() == tex);
} else {
TextureInput in;
in.gpu_texture = tex;
Expand All @@ -984,6 +1003,51 @@ void TextureAnimator::handle_clouds_and_fog(const DmaTransfer& tf, TexturePool*
}
}

void TextureAnimator::handle_slime(const DmaTransfer& tf, TexturePool* texture_pool) {
ASSERT(tf.size_bytes >= sizeof(SlimeInput));
SlimeInput input;
memcpy(&input, tf.data, sizeof(SlimeInput));

run_slime(input);

{
auto no_scroll_tex = m_slime_final_texture.texture();
if (m_slime_pool_gpu_tex) {
texture_pool->move_existing_to_vram(m_slime_pool_gpu_tex, input.dest);
ASSERT(texture_pool->lookup(input.dest).value() == no_scroll_tex);
} else {
TextureInput in;
in.gpu_texture = no_scroll_tex;
in.w = kFinalSkyTextureSize;
in.h = kFinalSkyTextureSize;
in.debug_page_name = "PC-ANIM";
in.debug_name = "slime";
in.id = get_id_for_tbp(texture_pool, input.dest, 778);
m_slime_pool_gpu_tex = texture_pool->give_texture_and_load_to_vram(in, input.dest);
}
m_private_output_slots.at(m_slime_output_slot) = no_scroll_tex;
}

{
auto tex = m_slime_final_scroll_texture.texture();
if (m_slime_scroll_pool_gpu_tex) {
texture_pool->move_existing_to_vram(m_slime_scroll_pool_gpu_tex, input.scroll_dest);
ASSERT(texture_pool->lookup(input.scroll_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 = "slime-scroll";
in.id = get_id_for_tbp(texture_pool, input.dest, 779);
m_slime_scroll_pool_gpu_tex =
texture_pool->give_texture_and_load_to_vram(in, input.scroll_dest);
}
m_private_output_slots.at(m_slime_scroll_output_slot) = tex;
}
}

void TextureAnimator::clear_stale_textures(u64 frame_idx) {
for (auto& group : m_clut_blender_groups) {
if (frame_idx > group.last_updated_frame) {
Expand Down Expand Up @@ -2485,23 +2549,45 @@ void TextureAnimator::setup_sky() {
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);
{
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);
}
}

{
m_slime_output_slot = output_slot_by_idx(GameVersion::Jak2, "cas-toxic-slime-dest");
m_slime_scroll_output_slot =
output_slot_by_idx(GameVersion::Jak2, "cas-toxic-slime-scroll-dest");
const float max_times[4] = {600.f, 300.f, 150.f, 75.f};
const float scales[4] = {0.55, 0.6, 0.3, 0.1f};
for (int i = 0, dim = kFinalSlimeTextureSize >> (kNumSlimeNoiseLayers - 1);
i < kNumSlimeNoiseLayers; i++, dim *= 2) {
auto& tex = m_slime_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);
}
}
}

Expand Down Expand Up @@ -2591,6 +2677,101 @@ GLint TextureAnimator::run_clouds(const SkyInput& input) {
glUniform1f(m_uniforms.maximum, input.cloud_max);
glDisable(GL_BLEND);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

glBindTexture(GL_TEXTURE_2D, m_sky_final_texture.texture());
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
return m_sky_final_texture.texture();
}

void TextureAnimator::run_slime(const SlimeInput& input) {
m_debug_slime_input = input;

int times_idx = 0;
times_idx++;
{
FramebufferTexturePairContext ctxt(m_slime_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 < kNumSlimeNoiseLayers; noise_layer_idx++) {
const float new_time = input.times[times_idx];
auto& ntp = m_slime_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_slime_final_texture);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(m_uniforms.enable_tex, 3);
glUniform1f(m_uniforms.slime_scroll, 0);
glBindTexture(GL_TEXTURE_2D, m_slime_blend_texture.texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glDisable(GL_BLEND);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glBindTexture(GL_TEXTURE_2D, m_slime_final_texture.texture());
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);

{
FramebufferTexturePairContext ctxt(m_slime_final_scroll_texture);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(m_uniforms.enable_tex, 3);
float scroll = input.times[8] / 1200.f;
glUniform1f(m_uniforms.slime_scroll, scroll);
glBindTexture(GL_TEXTURE_2D, m_slime_blend_texture.texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glDisable(GL_BLEND);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glBindTexture(GL_TEXTURE_2D, m_slime_final_scroll_texture.texture());
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}