From 97e04a7612f8155e6005690c08a8220fbfb9e920 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:32:25 -0700 Subject: [PATCH] [jak2] Fix collision renderer extract (#3081) Fixes missing collision geometry reported in https://github.com/open-goal/jak-project/issues/3011 The issue happens when there are 256 polygons. In this case `num-polys` is 0 (it's a u8). There are actual cases where there are 0 polygons, so we have to do a more complicated check to get the real count. I should have done this in the first place, but it seemed to work... --- .../level_extractor/extract_collide_frags.cpp | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/decompiler/level_extractor/extract_collide_frags.cpp b/decompiler/level_extractor/extract_collide_frags.cpp index a195364c702..f86393f9c41 100644 --- a/decompiler/level_extractor/extract_collide_frags.cpp +++ b/decompiler/level_extractor/extract_collide_frags.cpp @@ -296,16 +296,69 @@ void handle_collide_fragment(const TypedRef& collide_fragment, level_tools::Vector bbox_min; bbox_min.read_from_file(get_field_ref(collide_fragment, "bbox", dts)); + + // There's a lot of indirection: + // grid -> buckets -> index -> poly -> vertex. + + // First step: 3D coordinates are converted to a cell in a 3D grid. + // The dimensions of this grid are stored as u8's in the dimension array. + u32 dim_array = deref_u32(get_field_ref(collide_fragment, "dimension-array", dts), 0); + u8 counts[4]; + memcpy(counts, &dim_array, 4); + ASSERT(counts[3] == 0); // unused + + // Each grid cell maps to a bucket (they go in order, no fancy hash function) + u32 num_buckets = read_plain_data_field(collide_fragment, "num-buckets", dts); + // this should match the grid dims + ASSERT(counts[0] * counts[1] * counts[2] == num_buckets); + + // read the buckets + struct BucketEntry { + s16 index, count; + }; + std::vector buckets(num_buckets); + memcpy_from_plain_data((u8*)buckets.data(), + deref_label(get_field_ref(collide_fragment, "bucket-array", dts)), + sizeof(BucketEntry) * num_buckets); + + // Each bucket references a series of entries in the index list. Check all buckets to see the + // length of the index list. + int max_from_buckets = 0; + for (const auto& bucket : buckets) { + int end = bucket.count + bucket.index; + if (end > max_from_buckets) { + max_from_buckets = end; + } + } + // confirm the index list length matches + u32 num_indices = read_plain_data_field(collide_fragment, "num-indices", dts); + ASSERT(max_from_buckets == (int)num_indices); + + // read the index list + std::vector index_list(num_indices); + memcpy_from_plain_data((u8*)index_list.data(), + deref_label(get_field_ref(collide_fragment, "index-array", dts)), + num_indices); + u8 max_in_index_list = 0; + for (auto x : index_list) { + max_in_index_list = std::max(x, max_in_index_list); + } + u8 poly_count = read_plain_data_field(collide_fragment, "poly-count", dts); u8 num_polys = read_plain_data_field(collide_fragment, "num-polys", dts); ASSERT(poly_count == num_polys); + if (poly_count == 0) { + ASSERT(max_in_index_list == 255); + } else { + ASSERT(num_polys == max_in_index_list + 1); + } // this value seems to be bogus // u16 vert_count = read_plain_data_field(collide_fragment, "num-verts", dts); - std::vector polys(poly_count); + std::vector polys(max_in_index_list + 1); memcpy_from_plain_data((u8*)polys.data(), deref_label(get_field_ref(collide_fragment, "poly-array", dts)), - 4 * poly_count); + 4 * (max_in_index_list + 1)); int max_vi = 0; int max_pat = 0; for (const auto& p : polys) { @@ -402,7 +455,7 @@ void extract_collide_frags(const level_tools::CollideHash& chash, data_ref.byte_offset += 4; } - // now, ties + // first, all ties. Store collide-hash-fragments that are associated by ties. for (auto tt : ties) { auto last_array = tt->arrays.back().get(); auto as_instance_array = dynamic_cast(last_array);