From a01182315bd4d3d0c7459beee2cc4213d7de7c85 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 2 Dec 2023 12:16:14 -0500 Subject: [PATCH] [jak2] Support per-proto shrub visibility (#3228) Fixes the issue reported in https://github.com/open-goal/jak-project/issues/3168, https://github.com/open-goal/jak-project/issues/3189, https://github.com/open-goal/jak-project/issues/3166, https://github.com/open-goal/jak-project/issues/3152, https://github.com/open-goal/jak-project/issues/3224 where a small part of the pipes in the `strip-grenade` mission appear, but shouldn't. --- common/custom_data/TFrag3Data.cpp | 4 ++ common/custom_data/Tfrag3Data.h | 8 ++- common/formatter/rules/rule_config.h | 4 +- decompiler/level_extractor/extract_shrub.cpp | 16 ++++- .../opengl_renderer/background/Shrub.cpp | 60 +++++++++++++++++++ .../opengl_renderer/background/Shrub.h | 4 ++ goal_src/jak2/engine/gfx/shrub/shrubbery.gc | 53 ++++++++++++++++ 7 files changed, 144 insertions(+), 5 deletions(-) diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index d0cd2d443..5b66d841f 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -42,6 +42,7 @@ void ShrubDraw::serialize(Serializer& ser) { ser.from_ptr(&num_triangles); ser.from_ptr(&first_index_index); ser.from_ptr(&num_indices); + ser.from_ptr(&proto_idx); } void InstancedStripDraw::serialize(Serializer& ser) { @@ -416,6 +417,9 @@ void ShrubTree::serialize(Serializer& ser) { for (auto& draw : static_draws) { draw.serialize(ser); } + + ser.from_ptr(&has_per_proto_visibility_toggle); + ser.from_string_vector(&proto_names); } void BVH::serialize(Serializer& ser) { diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index ec09c9cdd..a0007b6c9 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -18,7 +18,7 @@ namespace tfrag3 { // - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage // - if adding a new category to the memory usage, update extract_level to print it. -constexpr int TFRAG3_VERSION = 38; +constexpr int TFRAG3_VERSION = 39; enum MemoryUsageCategory { TEXTURE, @@ -219,6 +219,8 @@ struct ShrubDraw { // for debug counting. u32 num_triangles = 0; + + u16 proto_idx = 0; void serialize(Serializer& ser); }; @@ -430,6 +432,10 @@ struct ShrubTree { std::vector vertices; // mesh vertices } unpacked; + // jak 2 and later can toggle on and off visibility per proto by name + bool has_per_proto_visibility_toggle = false; + std::vector proto_names; + void serialize(Serializer& ser); void memory_usage(MemoryUsageTracker* tracker) const; void unpack(); diff --git a/common/formatter/rules/rule_config.h b/common/formatter/rules/rule_config.h index 170a2f498..8548a6143 100644 --- a/common/formatter/rules/rule_config.h +++ b/common/formatter/rules/rule_config.h @@ -21,8 +21,8 @@ struct FormFormattingConfig { bool combine_first_two_lines = false; // NOTE - basically hang, but will probably stick around after hang is gone, may be // redundant (inline_until_index!) - std::function(const std::vector& curr_lines)> inline_until_index = - [](std::vector curr_lines) { return std::nullopt; }; + std::function(const std::vector& /*curr_lines*/)> + inline_until_index = [](std::vector /*curr_lines*/) { return std::nullopt; }; bool has_constant_pairs = false; bool prevent_inlining = false; // TODO - duplicate of below std::function should_prevent_inlining = diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp index eb86c3371..ae1ba210e 100644 --- a/decompiler/level_extractor/extract_shrub.cpp +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -424,7 +424,8 @@ void make_draws(tfrag3::Level& lev, std::vector> indices_regrouped_by_draw; std::unordered_map> static_draws_by_tex; size_t global_vert_counter = 0; - for (auto& proto : protos) { + for (u32 proto_idx = 0; proto_idx < protos.size(); proto_idx++) { + auto& proto = protos[proto_idx]; // packed_vert_indices[frag][draw] = {start, end} std::vector>> packed_vert_indices; @@ -509,7 +510,9 @@ void make_draws(tfrag3::Level& lev, std::vector* verts_to_add_to = nullptr; if (existing_draws_in_tex != static_draws_by_tex.end()) { for (auto idx : existing_draws_in_tex->second) { - if (tree_out.static_draws.at(idx).mode == mode) { + auto& candidate_draw_out = tree_out.static_draws.at(idx); + if (candidate_draw_out.mode == mode && (!tree_out.has_per_proto_visibility_toggle || + candidate_draw_out.proto_idx == proto_idx)) { draw_to_add_to = &tree_out.static_draws[idx]; verts_to_add_to = &indices_regrouped_by_draw[idx]; } @@ -523,6 +526,9 @@ void make_draws(tfrag3::Level& lev, draw_to_add_to = &tree_out.static_draws.back(); draw_to_add_to->mode = mode; draw_to_add_to->tree_tex_id = idx_in_lev_data; + if (tree_out.has_per_proto_visibility_toggle) { + draw_to_add_to->proto_idx = proto_idx; + } verts_to_add_to = &indices_regrouped_by_draw.emplace_back(); } @@ -573,10 +579,16 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, bool dump_level, GameVersion version) { auto& tree_out = out.shrub_trees.emplace_back(); + + if (version > GameVersion::Jak1) { + tree_out.has_per_proto_visibility_toggle = true; + } + 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, version)); + tree_out.proto_names.push_back(proto.name); } for (auto& arr : tree->discovered_arrays) { diff --git a/game/graphics/opengl_renderer/background/Shrub.cpp b/game/graphics/opengl_renderer/background/Shrub.cpp index 66dba11a4..623d5bb4f 100644 --- a/game/graphics/opengl_renderer/background/Shrub.cpp +++ b/game/graphics/opengl_renderer/background/Shrub.cpp @@ -45,6 +45,13 @@ void Shrub::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProf memcpy(&m_pc_port_data, pc_port_data.data, sizeof(TfragPcPortData)); m_pc_port_data.level_name[11] = '\0'; + if (render_state->version == GameVersion::Jak2) { + // jak 2 proto visibility + auto proto_mask_data = dma.read_and_advance(); + m_proto_vis_data = proto_mask_data.data; + m_proto_vis_data_size = proto_mask_data.size_bytes; + } + while (dma.current_tag_offset() != render_state->next_bucket) { dma.read_and_advance(); } @@ -91,6 +98,13 @@ void Shrub::update_load(const LevelData* loader_data) { m_trees[l_tree].vertex_buffer = loader_data->shrub_vertex_data[l_tree]; m_trees[l_tree].vert_count = verts; m_trees[l_tree].draws = &tree.static_draws; + m_trees[l_tree].proto_vis_mask.clear(); + m_trees[l_tree].proto_vis_mask.resize(tree.proto_names.size(), true); + m_trees[l_tree].proto_name_to_idx.clear(); + size_t i = 0; + for (auto& name : tree.proto_names) { + m_trees[l_tree].proto_name_to_idx[name].push_back(i++); + } m_trees[l_tree].colors = &tree.time_of_day_colors; m_trees[l_tree].index_data = tree.indices.data(); m_trees[l_tree].tod_cache = swizzle_time_of_day(tree.time_of_day_colors); @@ -216,6 +230,45 @@ void Shrub::render_all_trees(const TfragRenderSettings& settings, } } +namespace { +void update_vis_mask(std::vector& vis_mask, + const u8* data, + u32 data_size, + const std::unordered_map>& name_to_idx) { + char name_buffer[256]; // ?? + + for (u32 i = 0; i < vis_mask.size(); i++) { + vis_mask[i] = true; + } + + const u8* end = data + data_size; + while (true) { + int name_idx = 0; + while (*data) { + name_buffer[name_idx++] = *data; + data++; + } + if (name_idx) { + ASSERT(name_idx < 254); + name_buffer[name_idx] = '\0'; + const auto& it = name_to_idx.find(name_buffer); + if (it != name_to_idx.end()) { + for (auto x : name_to_idx.at(name_buffer)) { + vis_mask[x] = 0; + } + } + } + + while (*data == 0) { + if (data >= end) { + return; + } + data++; + } + } +} +} // namespace + void Shrub::render_tree(int idx, const TfragRenderSettings& settings, SharedRenderState* render_state, @@ -259,6 +312,10 @@ void Shrub::render_tree(int idx, glActiveTexture(GL_TEXTURE0); glEnable(GL_PRIMITIVE_RESTART); glPrimitiveRestartIndex(UINT32_MAX); + if (m_proto_vis_data) { + update_vis_mask(tree.proto_vis_mask, m_proto_vis_data, m_proto_vis_data_size, + tree.proto_name_to_idx); + } tree.perf.tod_time.add(setup_timer.getSeconds()); int last_texture = -1; @@ -282,6 +339,9 @@ void Shrub::render_tree(int idx, for (size_t draw_idx = 0; draw_idx < tree.draws->size(); draw_idx++) { const auto& draw = tree.draws->operator[](draw_idx); + if (!tree.proto_vis_mask.at(draw.proto_idx)) { + continue; + } const auto& multidraw_indices = m_cache.multidraw_offset_per_stripdraw[draw_idx]; const auto& singledraw_indices = m_cache.draw_idx_temp[draw_idx]; diff --git a/game/graphics/opengl_renderer/background/Shrub.h b/game/graphics/opengl_renderer/background/Shrub.h index 4593cc0ec..37f10194b 100644 --- a/game/graphics/opengl_renderer/background/Shrub.h +++ b/game/graphics/opengl_renderer/background/Shrub.h @@ -42,6 +42,8 @@ class Shrub : public BucketRenderer { const std::vector* colors = nullptr; const u32* index_data = nullptr; SwizzledTimeOfDay tod_cache; + std::vector proto_vis_mask; + std::unordered_map> proto_name_to_idx; struct { u32 draws = 0; @@ -78,4 +80,6 @@ class Shrub : public BucketRenderer { std::vector multidraw_index_offset_buffer; } m_cache; TfragPcPortData m_pc_port_data; + const u8* m_proto_vis_data = nullptr; + int m_proto_vis_data_size = 0; }; diff --git a/goal_src/jak2/engine/gfx/shrub/shrubbery.gc b/goal_src/jak2/engine/gfx/shrub/shrubbery.gc index 349f5f0d4..81b4691f8 100644 --- a/goal_src/jak2/engine/gfx/shrub/shrubbery.gc +++ b/goal_src/jak2/engine/gfx/shrub/shrubbery.gc @@ -10,6 +10,58 @@ ;; DECOMP BEGINS +(defun pc-add-shrub-vis-mask ((dma-buf dma-buffer) (lev level)) + "Add data so Proto.cpp can disable drawing for disabled protos." + (let ((packet (the-as dma-packet (-> dma-buf base)))) + (set! (-> packet vif0) (new 'static 'vif-tag)) + (set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port))) + (set! (-> dma-buf base) (the pointer (&+ packet 16))) + + (let ((lev-trees (-> lev bsp drawable-trees)) + (data-ptr (the (pointer uint8) (-> dma-buf base))) + ) + (dotimes (tree-idx (-> lev-trees length)) + (let ((tree (-> lev-trees data tree-idx))) + (when (= (-> tree type) drawable-tree-instance-shrub) + (let* ((shrub-tree (the drawable-tree-instance-shrub tree)) + (protos (-> shrub-tree info prototype-inline-array-shrub)) + ) + (dotimes (i (-> protos length)) + (let ((proto (-> protos data i))) + (when (logtest? (-> proto flags) (prototype-flags visible)) + ;; invisible! + ;; (format 0 "invis: ~A~%" (-> proto name)) + (let ((src (-> proto name data))) + (while (nonzero? (-> src)) + (set! (-> data-ptr) (-> src)) + (&+! src 1) + (&+! data-ptr 1) + ) + (set! (-> data-ptr) 0) + (&+! data-ptr 1) + ) + ) + ) + ) + ) + ) + ) + ) + ;; align + (while (nonzero? (logand data-ptr #xf)) + (set! (-> data-ptr) 0) + (&+! data-ptr 1) + ) + (set! (-> packet dma) (new 'static 'dma-tag + :id (dma-tag-id cnt) + :qwc (/ (&- data-ptr (-> dma-buf base)) 16)) + ) + (set! (-> dma-buf base) data-ptr) + #f + ) + ) + ) + (defmethod login ((this billboard)) "Set up the billboard adgif shader" (adgif-shader-login (-> this flat)) @@ -481,6 +533,7 @@ (let* ((shrub-dma-buff (-> *display* frames (-> *display* on-screen) global-buf)) (dma-start (-> shrub-dma-buff base))) (add-pc-tfrag3-data shrub-dma-buff (-> *level* draw-level *draw-index*)) + (pc-add-shrub-vis-mask shrub-dma-buff (-> *level* draw-level *draw-index*)) (let ((a3-22 (-> shrub-dma-buff base))) (let ((v1-57 (the-as object (-> shrub-dma-buff base)))) (set! (-> (the-as dma-packet v1-57) dma) (new 'static 'dma-tag :id (dma-tag-id next)))