diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 1ea169d758..dca141ad36 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -73,7 +73,7 @@ struct MemoryUsageTracker { void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; } }; -constexpr int TFRAG3_VERSION = 33; +constexpr int TFRAG3_VERSION = 34; // These vertices should be uploaded to the GPU at load time and don't change struct PreloadedVertex { diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 20a8943625..028d1fc3da 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -197,7 +197,7 @@ std::vector extract_bsp_from_level(const ObjectFileDB dynamic_cast(draw_tree.get()); ASSERT(as_shrub_tree); extract_shrub(as_shrub_tree, fmt::format("{}-{}-shrub", dgo_name, i++), - bsp_header.texture_remap_table, tex_db, {}, level_data, false); + bsp_header.texture_remap_table, tex_db, {}, level_data, false, db.version()); } else if (draw_tree->my_type() == "drawable-tree-collide-fragment" && extract_collision) { auto as_collide_frags = dynamic_cast(draw_tree.get()); diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp index 78c50be73c..fdb3f95ec3 100644 --- a/decompiler/level_extractor/extract_shrub.cpp +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -142,7 +142,8 @@ u32 remap_texture(u32 original, const std::vector& ma DrawSettings adgif_to_draw_mode(const AdGifData& ad, const TextureDB& tdb, const std::vector& map, - int count) { + int count, + bool alpha_tpage_flag) { // initialize draw mode DrawMode current_mode; current_mode.set_at(true); @@ -155,13 +156,25 @@ DrawSettings adgif_to_draw_mode(const AdGifData& ad, current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_SRC_SRC_SRC); current_mode.enable_fog(); + if (alpha_tpage_flag) { + current_mode.set_alpha_test(DrawMode::AlphaTest::NEVER); + current_mode.set_aref(0); + current_mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY); + } + // ADGIF 0 bool weird = (u8)ad.tex0_addr != (u32)GsRegisterAddress::TEX0_1; if (weird) { lg::info("---------------- WEIRD: 0x{:x}", ad.tex0_addr); lg::info("i have {} verts", count); } else { - ASSERT(ad.tex0_data == 0 || ad.tex0_data == 0x800000000); // note: decal?? todo + if (ad.tex0_data == 0) { + current_mode.set_decal(false); + } else if (ad.tex0_data == 0x8'0000'0000) { + current_mode.set_decal(true); + } else { + ASSERT(false); + } } // tw/th @@ -241,7 +254,8 @@ DrawSettings adgif_to_draw_mode(const AdGifData& ad, ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto, const TextureDB& tdb, - const std::vector& map) { + const std::vector& map, + GameVersion version) { ShrubProtoInfo result; for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) { auto& frag_out = result.frags.emplace_back(); @@ -287,7 +301,11 @@ ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto, ASSERT(3 * (vert_idx + draw.start_vtx_idx) + 3 <= frag.col.size()); } - draw.settings = adgif_to_draw_mode(ag, tdb, map, count); + bool alpha_tpage_flag = false; + if (version > GameVersion::Jak1) { + alpha_tpage_flag = proto.flags & 0x4; // tpage-alpha + } + draw.settings = adgif_to_draw_mode(ag, tdb, map, count, alpha_tpage_flag); } ASSERT(frag.vtx_cnt * 3 * sizeof(u16) <= frag.vtx.size()); @@ -550,12 +568,13 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, const TextureDB& tex_db, const std::vector>& /*expected_missing_textures*/, tfrag3::Level& out, - bool dump_level) { + bool dump_level, + GameVersion version) { auto& tree_out = out.shrub_trees.emplace_back(); auto& protos = tree->info.prototype_inline_array_shrub; std::vector proto_info; for (auto& proto : protos.data) { - proto_info.push_back(extract_proto(proto, tex_db, map)); + proto_info.push_back(extract_proto(proto, tex_db, map, version)); } for (auto& arr : tree->discovered_arrays) { diff --git a/decompiler/level_extractor/extract_shrub.h b/decompiler/level_extractor/extract_shrub.h index c57fc376bb..b8d596ba8d 100644 --- a/decompiler/level_extractor/extract_shrub.h +++ b/decompiler/level_extractor/extract_shrub.h @@ -24,6 +24,7 @@ void extract_shrub(const level_tools::shrub_types::DrawableTreeInstanceShrub* tr const TextureDB& tex_db, const std::vector>& expected_missing_textures, tfrag3::Level& out, - bool dump_level); + bool dump_level, + GameVersion version); } // namespace decompiler diff --git a/decompiler/level_extractor/extract_tfrag.cpp b/decompiler/level_extractor/extract_tfrag.cpp index 18d3f41c75..83cd9c9ac1 100644 --- a/decompiler/level_extractor/extract_tfrag.cpp +++ b/decompiler/level_extractor/extract_tfrag.cpp @@ -1911,7 +1911,13 @@ void process_draw_mode(std::vector& all_draws, update_mode_from_test1(val, mode); break; case GsRegisterAddress::TEX0_1: - // ASSERT(val == 0); HACK jak 2 sets this. + ASSERT(val == 0 || val == 0x8'0000'0000); + if (val == 0x8'0000'0000) { + mode.set_decal(true); + } else { + mode.set_decal(false); + } + mode.set_tcc(false); break; case GsRegisterAddress::TEX1_1: ASSERT(val == 0x120); // some flag diff --git a/game/graphics/opengl_renderer/SkyRenderer.cpp b/game/graphics/opengl_renderer/SkyRenderer.cpp index 23db4a837f..61918f8637 100644 --- a/game/graphics/opengl_renderer/SkyRenderer.cpp +++ b/game/graphics/opengl_renderer/SkyRenderer.cpp @@ -34,6 +34,10 @@ SkyBlendHandler::SkyBlendHandler(const std::string& name, true, level_id) {} +void SkyBlendHandler::init_shaders(ShaderLibrary& shaders) { + m_tfrag_renderer.init_shaders(shaders); +} + void SkyBlendHandler::handle_sky_copies(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) { diff --git a/game/graphics/opengl_renderer/SkyRenderer.h b/game/graphics/opengl_renderer/SkyRenderer.h index 54da19f20f..7d7e5db0ab 100644 --- a/game/graphics/opengl_renderer/SkyRenderer.h +++ b/game/graphics/opengl_renderer/SkyRenderer.h @@ -19,6 +19,7 @@ class SkyBlendHandler : public BucketRenderer { std::shared_ptr shared_cpu_blender); void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; void draw_debug_window() override; + void init_shaders(ShaderLibrary& shaders) override; private: void handle_sky_copies(DmaFollower& dma, diff --git a/game/graphics/opengl_renderer/background/Shrub.cpp b/game/graphics/opengl_renderer/background/Shrub.cpp index 3e115e8fa8..220b1d4829 100644 --- a/game/graphics/opengl_renderer/background/Shrub.cpp +++ b/game/graphics/opengl_renderer/background/Shrub.cpp @@ -10,6 +10,10 @@ Shrub::~Shrub() { discard_tree_cache(); } +void Shrub::init_shaders(ShaderLibrary& shaders) { + m_uniforms.decal = glGetUniformLocation(shaders[ShaderId::SHRUB].id(), "decal"); +} + void Shrub::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) { if (!m_enabled) { while (dma.current_tag_offset() != render_state->next_bucket) { @@ -298,6 +302,8 @@ void Shrub::render_tree(int idx, last_texture = draw.tree_tex_id; } + glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0); + auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::SHRUB); prof.add_draw_call(); diff --git a/game/graphics/opengl_renderer/background/Shrub.h b/game/graphics/opengl_renderer/background/Shrub.h index 05a0234df2..beced713ed 100644 --- a/game/graphics/opengl_renderer/background/Shrub.h +++ b/game/graphics/opengl_renderer/background/Shrub.h @@ -13,6 +13,8 @@ class Shrub : public BucketRenderer { public: Shrub(const std::string& name, int my_id); ~Shrub(); + void init_shaders(ShaderLibrary& shaders) override; + bool setup_for_level(const std::string& level, SharedRenderState* render_state); void render_all_trees(const TfragRenderSettings& settings, SharedRenderState* render_state, @@ -53,6 +55,10 @@ class Shrub : public BucketRenderer { } perf; }; + struct { + GLuint decal; + } m_uniforms; + std::vector m_trees; std::string m_level_name; const std::vector* m_textures; diff --git a/game/graphics/opengl_renderer/background/TFragment.cpp b/game/graphics/opengl_renderer/background/TFragment.cpp index 60f578706f..4cc4d8de53 100644 --- a/game/graphics/opengl_renderer/background/TFragment.cpp +++ b/game/graphics/opengl_renderer/background/TFragment.cpp @@ -145,6 +145,10 @@ void TFragment::draw_debug_window() { m_tfrag3.draw_debug_window(); } +void TFragment::init_shaders(ShaderLibrary& shaders) { + m_tfrag3.init_shaders(shaders); +} + void TFragment::handle_initialization(DmaFollower& dma) { // Set up test (different between different renderers) auto setup_test = dma.read_and_advance(); diff --git a/game/graphics/opengl_renderer/background/TFragment.h b/game/graphics/opengl_renderer/background/TFragment.h index 17adf3a8b2..cb7d61b38f 100644 --- a/game/graphics/opengl_renderer/background/TFragment.h +++ b/game/graphics/opengl_renderer/background/TFragment.h @@ -43,6 +43,7 @@ class TFragment : public BucketRenderer { int level_id); void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; void draw_debug_window() override; + void init_shaders(ShaderLibrary& shaders) override; private: void handle_initialization(DmaFollower& dma); diff --git a/game/graphics/opengl_renderer/background/Tfrag3.cpp b/game/graphics/opengl_renderer/background/Tfrag3.cpp index e334281a31..dd46cd0594 100644 --- a/game/graphics/opengl_renderer/background/Tfrag3.cpp +++ b/game/graphics/opengl_renderer/background/Tfrag3.cpp @@ -40,6 +40,10 @@ Tfrag3::~Tfrag3() { glDeleteVertexArrays(1, &m_debug_vao); } +void Tfrag3::init_shaders(ShaderLibrary& shaders) { + m_uniforms.decal = glGetUniformLocation(shaders[ShaderId::TFRAG3].id(), "decal"); +} + void Tfrag3::update_load(const std::vector& tree_kinds, const LevelData* loader_data) { const auto* lev_data = loader_data->level.get(); @@ -188,15 +192,25 @@ void Tfrag3::render_tree(int geom, return; } auto& tree = m_cached_trees.at(geom).at(settings.tree_idx); + const auto* itimes = settings.itimes; + + if (tree.freeze_itimes) { + itimes = tree.itimes_debug; + } else { + for (int i = 0; i < 4; i++) { + tree.itimes_debug[i] = settings.itimes[i]; + } + } + ASSERT(tree.kind != tfrag3::TFragmentTreeKind::INVALID); if (m_color_result.size() < tree.colors->size()) { m_color_result.resize(tree.colors->size()); } if (m_use_fast_time_of_day) { - interp_time_of_day_fast(settings.itimes, tree.tod_cache, m_color_result.data()); + interp_time_of_day_fast(itimes, tree.tod_cache, m_color_result.data()); } else { - interp_time_of_day_slow(settings.itimes, *tree.colors, m_color_result.data()); + interp_time_of_day_slow(itimes, *tree.colors, m_color_result.data()); } glActiveTexture(GL_TEXTURE10); glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture); @@ -249,6 +263,7 @@ void Tfrag3::render_tree(int geom, ASSERT(m_textures); glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id)); auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3); + glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0); tree.tris_this_frame += draw.num_triangles; tree.draws_this_frame++; @@ -347,7 +362,12 @@ void Tfrag3::draw_debug_window() { ImGui::Checkbox("cull debug (slow)", &tree.cull_debug); ImGui::PopID(); if (tree.rendered_this_frame) { + ImGui::Checkbox("freeze itimes", &tree.freeze_itimes); ImGui::Text(" tris: %d draws: %d", tree.tris_this_frame, tree.draws_this_frame); + for (int j = 0; j < 4; j++) { + ImGui::Text(" itimes[%d] 0x%x 0x%x 0x%x 0x%x", j, tree.itimes_debug[j][0], + tree.itimes_debug[j][1], tree.itimes_debug[j][2], tree.itimes_debug[j][3]); + } } } } diff --git a/game/graphics/opengl_renderer/background/Tfrag3.h b/game/graphics/opengl_renderer/background/Tfrag3.h index 9e67ba6c58..4ebbdaf355 100644 --- a/game/graphics/opengl_renderer/background/Tfrag3.h +++ b/game/graphics/opengl_renderer/background/Tfrag3.h @@ -13,6 +13,7 @@ class Tfrag3 { Tfrag3(); ~Tfrag3(); + void init_shaders(ShaderLibrary& shaders); void render_all_trees(int geom, const TfragRenderSettings& settings, SharedRenderState* render_state, @@ -78,8 +79,15 @@ class Tfrag3 { bool allowed = true; bool forced = false; bool cull_debug = false; + + bool freeze_itimes = false; + math::Vector itimes_debug[4]; }; + struct { + GLuint decal; + } m_uniforms; + struct Cache { std::vector vis_temp; std::vector> draw_idx_temp; diff --git a/game/graphics/opengl_renderer/background/background_common.cpp b/game/graphics/opengl_renderer/background/background_common.cpp index 93df82cc2e..c32b8d7924 100644 --- a/game/graphics/opengl_renderer/background/background_common.cpp +++ b/game/graphics/opengl_renderer/background/background_common.cpp @@ -218,6 +218,7 @@ void interp_time_of_day_slow(const math::Vector itimes[4], } // result += in[color].rgba[component].cast() * weights[component]; } + result[0] = std::min(result[0], 255.f); result[1] = std::min(result[1], 255.f); result[2] = std::min(result[2], 255.f); @@ -349,15 +350,15 @@ void interp_time_of_day_fast(const math::Vector itimes[4], color7 = _mm_mullo_epi16(color7, weights7); // add. This order minimizes dependencies. - color0 = _mm_add_epi16(color0, color1); - color2 = _mm_add_epi16(color2, color3); - color4 = _mm_add_epi16(color4, color5); - color6 = _mm_add_epi16(color6, color7); + color0 = _mm_adds_epi16(color0, color1); + color2 = _mm_adds_epi16(color2, color3); + color4 = _mm_adds_epi16(color4, color5); + color6 = _mm_adds_epi16(color6, color7); - color0 = _mm_add_epi16(color0, color2); - color4 = _mm_add_epi16(color4, color6); + color0 = _mm_adds_epi16(color0, color2); + color4 = _mm_adds_epi16(color4, color6); - color0 = _mm_add_epi16(color0, color4); + color0 = _mm_adds_epi16(color0, color4); // divide, because we multiplied our weights by 2^7. color0 = _mm_srli_epi16(color0, 6); @@ -404,15 +405,15 @@ void interp_time_of_day_fast(const math::Vector itimes[4], color7 = _mm_mullo_epi16(color7, weights7); // add. This order minimizes dependencies. - color0 = _mm_add_epi16(color0, color1); - color2 = _mm_add_epi16(color2, color3); - color4 = _mm_add_epi16(color4, color5); - color6 = _mm_add_epi16(color6, color7); + color0 = _mm_adds_epi16(color0, color1); + color2 = _mm_adds_epi16(color2, color3); + color4 = _mm_adds_epi16(color4, color5); + color6 = _mm_adds_epi16(color6, color7); - color0 = _mm_add_epi16(color0, color2); - color4 = _mm_add_epi16(color4, color6); + color0 = _mm_adds_epi16(color0, color2); + color4 = _mm_adds_epi16(color4, color6); - color0 = _mm_add_epi16(color0, color4); + color0 = _mm_adds_epi16(color0, color4); // divide, because we multiplied our weights by 2^7. color0 = _mm_srli_epi16(color0, 6); diff --git a/game/graphics/opengl_renderer/shaders/shrub.vert b/game/graphics/opengl_renderer/shaders/shrub.vert index b13ec737a8..f3476c499e 100644 --- a/game/graphics/opengl_renderer/shaders/shrub.vert +++ b/game/graphics/opengl_renderer/shaders/shrub.vert @@ -10,6 +10,7 @@ uniform mat4 camera; uniform float fog_constant; uniform float fog_min; uniform float fog_max; +uniform int decal; layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose. out vec4 fragment_color; @@ -72,6 +73,10 @@ void main() { fragment_color *= tod_color * 4; fragment_color.a *= 2; + if (decal == 1) { + fragment_color.xyz = vec3(1.0, 1.0, 1.0); + } + tex_coord = tex_coord_in; tex_coord.xy /= 4096; } diff --git a/game/graphics/opengl_renderer/shaders/tfrag3.vert b/game/graphics/opengl_renderer/shaders/tfrag3.vert index 91c8bb9f02..7ccf073942 100644 --- a/game/graphics/opengl_renderer/shaders/tfrag3.vert +++ b/game/graphics/opengl_renderer/shaders/tfrag3.vert @@ -64,14 +64,15 @@ void main() { transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE; gl_Position = transformed; + // time of day lookup + fragment_color = texelFetch(tex_T1, time_of_day_index, 0); + // color adjustment + fragment_color *= 2; + fragment_color.a *= 2; + if (decal == 1) { - fragment_color = vec4(1.0, 1.0, 1.0, 1.0); - } else { - // time of day lookup - fragment_color = texelFetch(tex_T1, time_of_day_index, 0); - // color adjustment - fragment_color *= 2; - fragment_color.a *= 2; + // tfrag/tie always use TCC=RGB, so even with decal, alpha comes from fragment. + fragment_color.xyz = vec3(1.0, 1.0, 1.0); } // fog hack