[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.
This commit is contained in:
water111 2023-12-02 12:16:14 -05:00 committed by GitHub
parent e38fe70ac4
commit a01182315b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 5 deletions

View file

@ -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) {

View file

@ -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<ShrubGpuVertex> 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<std::string> proto_names;
void serialize(Serializer& ser);
void memory_usage(MemoryUsageTracker* tracker) const;
void unpack();

View file

@ -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<std::optional<int>(const std::vector<std::string>& curr_lines)> inline_until_index =
[](std::vector<std::string> curr_lines) { return std::nullopt; };
std::function<std::optional<int>(const std::vector<std::string>& /*curr_lines*/)>
inline_until_index = [](std::vector<std::string> /*curr_lines*/) { return std::nullopt; };
bool has_constant_pairs = false;
bool prevent_inlining = false; // TODO - duplicate of below
std::function<bool(FormFormattingConfig, int num_refs)> should_prevent_inlining =

View file

@ -424,7 +424,8 @@ void make_draws(tfrag3::Level& lev,
std::vector<std::vector<u32>> indices_regrouped_by_draw;
std::unordered_map<u32, std::vector<u32>> 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<std::vector<std::pair<int, int>>> packed_vert_indices;
@ -509,7 +510,9 @@ void make_draws(tfrag3::Level& lev,
std::vector<u32>* 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<ShrubProtoInfo> 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) {

View file

@ -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<bool>& vis_mask,
const u8* data,
u32 data_size,
const std::unordered_map<std::string, std::vector<u32>>& 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];

View file

@ -42,6 +42,8 @@ class Shrub : public BucketRenderer {
const std::vector<tfrag3::TimeOfDayColor>* colors = nullptr;
const u32* index_data = nullptr;
SwizzledTimeOfDay tod_cache;
std::vector<bool> proto_vis_mask;
std::unordered_map<std::string, std::vector<u32>> proto_name_to_idx;
struct {
u32 draws = 0;
@ -78,4 +80,6 @@ class Shrub : public BucketRenderer {
std::vector<void*> 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;
};

View file

@ -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)))