diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index 50c67bcd27..324a94c686 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -59,6 +59,7 @@ add_library( level_extractor/extract_tfrag.cpp level_extractor/extract_tie.cpp level_extractor/BspHeader.cpp + level_extractor/extract_shrub.cpp ObjectFile/LinkedObjectFile.cpp ObjectFile/LinkedObjectFileCreation.cpp @@ -76,7 +77,7 @@ add_library( VuDisasm/VuDisassembler.cpp VuDisasm/VuInstruction.cpp - config.cpp "level_extractor/extract_shrub.cpp" "level_extractor/extract_shrub.h" "level_extractor/extract_common.h") + config.cpp) if (UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") @@ -91,7 +92,7 @@ target_link_libraries(decomp ) add_executable(decompiler - main.cpp "level_extractor/extract_shrub.cpp" "level_extractor/extract_shrub.h" "level_extractor/extract_common.h") + main.cpp) target_link_libraries(decompiler decomp diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 38817a43f1..b7b9cd2fdf 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -9502,9 +9502,14 @@ ) (deftype prototype-bucket-shrub (prototype-bucket) - ((mod-count uint16 4 :offset-assert 88) + (;; Geometry Array + ;; - prototype-generic-shrub / shrub-near (drawn with generic) + ;; - prototype-shrubbery + ;; - prototype-trans-shrubbery + ;; - billboard + (mod-count uint16 4 :offset-assert 88) (last dma-packet 4 :offset-assert 96) - (count-clear uint128 :offset 80) + (count-clear-qword uint128 :offset 80) (last-clear uint128 :offset 96) ) :method-count-assert 9 @@ -12525,6 +12530,7 @@ ;; - Types +;; Single rectangle -- only one texture (aka adgif-shader) (deftype billboard (drawable) ((flat adgif-shader :inline :offset-assert 32) ) @@ -12535,17 +12541,18 @@ (deftype shrub-view-data (structure) ((data shrub-view-data 3 :offset-assert 0) - (texture-giftag qword :inline :offset 0) + (texture-giftag gs-gif-tag :inline :offset 0) (consts vector :inline :offset 16) (fog-clamp vector :inline :offset 32) - (tex-start-ptr int32 :offset 16) - (gifbufsum float :offset 16) - (mtx-buf-ptr int32 :offset 20) - (exp23 float :offset 20) - (fog-0 float :offset 24) - (fog-1 float :offset 28) - (fog-min float :offset 32) - (fog-max float :offset 36) + (tex-start-ptr int32 :score 999 :offset 16) + (gifbufsum float :score 999 :offset 16) + ;; suspected to be part of hacks to get around VU's lack of integer operations + (mtx-buf-ptr int32 :score 999 :offset 20) + (exp23 float :score 999 :offset 20) + (fog-0 float :score 999 :offset 24) + (fog-1 float :score 999 :offset 28) + (fog-min float :score 999 :offset 32) + (fog-max float :score 999 :offset 36) ) :method-count-assert 9 :size-assert #x30 @@ -12554,11 +12561,19 @@ (deftype shrubbery (drawable) ((textures (inline-array adgif-shader) :offset 4) + ;; header breakdown: + ;; [0] - number of textures / 2 + ;; [1] - number of vertices + ;; [2] - number of triangle strips + ;; [3] - ?? + ;; + ;; Number of Triangles in the Shrub = header[2] - 2 * header[1] (header qword :offset 8) (obj-qwc uint8 :offset 12) (vtx-qwc uint8 :offset 13) (col-qwc uint8 :offset 14) (stq-qwc uint8 :offset 15) + ;; start of static dma-chain (obj uint32 :score 999 :offset 16) (vtx uint32 :score 999 :offset 20) (col uint32 :score 999 :offset 24) @@ -12570,9 +12585,9 @@ ) (deftype instance-shrubbery (instance) - ((flat-normal vector :inline :offset-assert 64) + ((color-indices uint32 :offset 8) + (flat-normal vector :inline :offset-assert 64) (flat-hwidth float :offset 76) - (color uint32 :offset 8) ) :method-count-assert 18 :size-assert #x50 @@ -12600,6 +12615,8 @@ (deftype generic-shrub-fragment (drawable) ((textures (inline-array adgif-shader) :score 999 :offset 4) (vtx-cnt uint32 :score 999 :offset 8) + ;; the total size of the textures array + ;; - each texture is 5 qwords (cnt-qwc uint8 :score 999 :offset 12) (vtx-qwc uint8 :score 999 :offset 13) (col-qwc uint8 :score 999 :offset 14) @@ -16741,11 +16758,11 @@ (define-extern upload-generic-shrub (function dma-buffer generic-shrub-fragment int int dma-buffer)) (define-extern shrub-num-tris (function shrubbery uint)) (define-extern shrub-init-frame (function dma-buffer gs-test none)) -(define-extern shrub-upload-model (function shrubbery shrub-view-data int symbol)) ;; third arg is `start-bank` from shrub-work +(define-extern shrub-upload-model (function shrubbery dma-buffer int symbol)) ;; third arg is `start-bank` from shrub-work (define-extern shrub-do-init-frame (function dma-buffer symbol)) (define-extern shrub-upload-view-data (function dma-buffer symbol)) (define-extern shrub-init-view-data (function shrub-view-data symbol)) -(define-extern mem-usage-shrub-walk (function draw-node drawable-group memory-usage-block int draw-node)) +(define-extern mem-usage-shrub-walk (function draw-node int memory-usage-block int draw-node)) (define-extern shrub-make-perspective-matrix (function matrix matrix)) (define-extern shrub-time (function int int int int int int)) ;; unused (define-extern draw-inline-array-instance-shrub (function dma-buffer drawable int (inline-array prototype-bucket-shrub) none)) diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index 434b852f78..cfd85de6c3 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -7460,11 +7460,23 @@ "shrub-do-init-frame": [ [[10, 21], "a0", "dma-packet"], - [[24, 29], "a0", "dma-packet"] + [[24, 29], "a0", "dma-packet"], + [33, "v1", "(pointer vif-tag)"], + [[35, 41], "v1", "(pointer uint32)"], + [42, "v1", "(pointer vif-tag)"], + [[43, 51], "v1", "(pointer uint32)"], + [52, "v1", "(pointer vif-tag)"], + [54, "v1", "(pointer uint32)"] ], "shrub-upload-view-data": [ - [[3, 16], "a0", "shrub-view-data"] + [[3, 16], "a0", "dma-packet"] + ], + + "shrub-upload-model": [ + [[17, 26], "a3", "dma-packet"], + [[33, 41], "a0", "dma-packet"], + [[47, 55], "a0", "dma-packet"] ], "placeholder-do-not-add-below": [] diff --git a/decompiler/level_extractor/extract_common.h b/decompiler/level_extractor/extract_common.h deleted file mode 100644 index 3c8932185c..0000000000 --- a/decompiler/level_extractor/extract_common.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "decompiler/level_extractor/BspHeader.h" -#include "common/math/Vector.h" -#include "common/custom_data/Tfrag3Data.h" -#include "decompiler/data/TextureDB.h" - -namespace decompiler { - -/// -/// Get the index of the first draw node in an array. Works for node or tfrag. -/// -/// -/// -u16 get_first_idx(const level_tools::DrawableInlineArray* array) { - auto as_tie_instances = dynamic_cast(array); - auto as_nodes = dynamic_cast(array); - if (as_tie_instances) { - return as_tie_instances->instances.at(0).id; - } else if (as_nodes) { - return as_nodes->draw_nodes.at(0).id; - } else { - assert(false); - } -} - -/// -/// Verify node indices follow the patterns we expect. Takes start as the expected first, writes the -/// end. -/// -/// -/// -/// -/// -bool verify_node_indices_from_array(const level_tools::DrawableInlineArray* array, - u16 start, - u16* end) { - auto as_tie_instances = dynamic_cast(array); - auto as_nodes = dynamic_cast(array); - - if (as_tie_instances) { - for (auto& elt : as_tie_instances->instances) { - if (elt.id != start) { - fmt::print("bad inst: exp {} got {}\n", start, elt.id); - return false; - } - start++; - } - *end = start; - return true; - } else if (as_nodes) { - for (auto& elt : as_nodes->draw_nodes) { - if (elt.id != start) { - fmt::print("bad node: exp {} got {}\n", start, elt.id); - return false; - } - start++; - } - *end = start; - return true; - } else { - fmt::print("bad node array type: {}\n", array->my_type()); - return false; - } -} -} // namespace decompiler diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp index e434869b1a..138aeb52ed 100644 --- a/decompiler/level_extractor/extract_shrub.cpp +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -1,6 +1,5 @@ #include -#include "extract_common.h" #include "extract_shrub.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" @@ -9,18 +8,77 @@ namespace decompiler { +/// +/// Get the index of the first draw node in an array. Works for node or tfrag. +/// +/// +/// +u16 get_first_idx_shrub(const level_tools::DrawableInlineArray* array) { + auto as_tie_instances = dynamic_cast(array); + auto as_nodes = dynamic_cast(array); + if (as_tie_instances) { + return as_tie_instances->instances.at(0).id; + } else if (as_nodes) { + return as_nodes->draw_nodes.at(0).id; + } else { + assert(false); + } +} + +/// +/// Verify node indices follow the patterns we expect. Takes start as the expected first, writes the +/// end. +/// +/// +/// +/// +/// +bool verify_node_indices_from_array_shrub(const level_tools::DrawableInlineArray* array, + u16 start, + u16* end) { + auto as_shrub_instances = + dynamic_cast(array); + auto as_nodes = dynamic_cast(array); + + if (as_shrub_instances) { + for (auto& elt : as_shrub_instances->instances) { + if (elt.id != start) { + fmt::print("bad inst: exp {} got {}\n", start, elt.id); + return false; + } + start++; + } + *end = start; + return true; + } else if (as_nodes) { + for (auto& elt : as_nodes->draw_nodes) { + if (elt.id != start) { + fmt::print("bad node: exp {} got {}\n", start, elt.id); + return false; + } + start++; + } + *end = start; + return true; + } else { + fmt::print("bad node array type: {}\n", array->my_type()); + return false; + } +} + /// /// Verify all node indices in a tree. /// /// /// If valid or not bool verify_node_indices(const shrub_types::DrawableTreeInstanceShrub* tree) { - u16 start = get_first_idx(tree->arrays.at(0).get()); + u16 start = get_first_idx_shrub(tree->arrays.at(0).get()); for (auto& array : tree->arrays) { - if (!verify_node_indices_from_array(array.get(), start, &start)) { + if (!verify_node_indices_from_array_shrub(array.get(), start, &start)) { return false; } - start = (start + 31) & ~(31); // TODO - is this wrong for shrub (just copy pasted from tie for now) + start = + (start + 31) & ~(31); // TODO - is this wrong for shrub (just copy pasted from tie for now) } return true; } @@ -34,41 +92,191 @@ bool verify_node_indices(const shrub_types::DrawableTreeInstanceShrub* tree) { void extract_vis_data(const shrub_types::DrawableTreeInstanceShrub* tree, u16 first_child, tfrag3::ShrubTree& out) { - fmt::print("shrub::extract_vis_data not yet implemeneted!"); + out.bvh.first_leaf_node = first_child; + out.bvh.last_leaf_node = first_child; + + if (tree->arrays.size() == 0) { + // shouldn't hit this? + } else if (tree->arrays.size() == 1) { + auto array = + dynamic_cast(tree->arrays.at(0).get()); + assert(array); + out.bvh.first_root = array->instances.at(0).id; + out.bvh.num_roots = array->instances.size(); + out.bvh.only_children = true; + } else { + auto array = + dynamic_cast(tree->arrays.at(0).get()); + assert(array); + out.bvh.first_root = array->draw_nodes.at(0).id; + out.bvh.num_roots = array->draw_nodes.size(); + out.bvh.only_children = false; + } + + out.bvh.vis_nodes.resize(first_child - out.bvh.first_root); + + // may run 0 times, if there are only children. + for (int i = 0; i < ((int)tree->arrays.size()) - 1; i++) { + bool expecting_leaves = i == ((int)tree->arrays.size()) - 2; + + auto array = + dynamic_cast(tree->arrays.at(i).get()); + assert(array); + u16 idx = first_child; + for (auto& elt : array->draw_nodes) { + auto& vis = out.bvh.vis_nodes.at(elt.id - out.bvh.first_root); + assert(vis.num_kids == 0xff); + for (int j = 0; j < 4; j++) { + vis.bsphere[j] = elt.bsphere.data[j]; + } + vis.num_kids = elt.child_count; + vis.flags = elt.flags; + assert(vis.flags == expecting_leaves ? 0 : 1); + assert(vis.num_kids > 0); + assert(vis.num_kids <= 8); + assert(elt.children.size() == vis.num_kids); + if (expecting_leaves) { + for (int leaf = 0; leaf < (int)vis.num_kids; leaf++) { + auto l = dynamic_cast(elt.children.at(leaf).get()); + assert(l); + + assert(idx == l->id); + + assert(l->id >= out.bvh.first_leaf_node); + if (leaf == 0) { + vis.child_id = l->id; + } + out.bvh.last_leaf_node = std::max((u16)l->id, out.bvh.last_leaf_node); + idx++; + } + + } else { + u16 arr_idx = 0; + for (int child = 0; child < (int)vis.num_kids; child++) { + auto l = dynamic_cast(elt.children.at(child).get()); + assert(l); + if (child == 0) { + arr_idx = l->id; + } else { + assert(arr_idx < l->id); + arr_idx = l->id; + } + if (child == 0) { + vis.child_id = l->id; + } + + assert(l->id < out.bvh.first_leaf_node); + } + } + } + } } -std::vector collect_shrub_info( +struct ShrubInstanceFragInfo { + // the color index table uploaded to VU. + // this contains indices into the shared palette. + std::vector color_indices; + + u16 color_index_offset_in_big_palette = -1; + + math::Vector lq_colors_ui(u32 qw) const { + // note: this includes the unpack + assert(qw >= 204); + qw -= 204; + qw *= 4; + assert(qw + 4 <= color_indices.size()); + math::Vector result; + for (int i = 0; i < 4; i++) { + result[i] = color_indices.at(qw + i); + } + return result; + } +}; + +struct ShrubInstanceInfo { + // The index of the prototype (the geometry) that is used by this instance + // note: we're going to trust that this lines up with bucket. + // if this assumption is wrong, we'll be drawing with the wrong model and it will be super + // obvious. + u16 prototype_idx = 0; + + // our bsphere's index in the BVH tree + u16 vis_id = 0; + + // not totally sure if we'll use this (currently unused in tfrag, but probably worth if we + // actually cull using the tree) + math::Vector4f bsphere; + + std::array mat; + + u16 wind_index = 0; + float unknown_wind_related_value = 0.f; // w of the first mat vec. + + std::vector frags; // per-instance per-fragment info +}; + +std::array extract_shrub_matrix(const u16* data) { + std::array result; + for (int i = 0; i < 4; i++) { + s32 x = data[12 + i]; + x <<= 16; + x >>= 10; + result[3][i] = x; + } + + for (int vec = 0; vec < 3; vec++) { + for (int i = 0; i < 4; i++) { + s32 x = data[vec * 4 + i]; + x <<= 16; + x >>= 16; + result[vec][i] = (float)x / 4096.f; + } + } + + return result; +} + +struct ShrubProtoInfo { + std::string name; + std::vector instances; + bool uses_generic = false; + float stiffness = 0; + u32 generic_flag; + std::vector time_of_day_colors; +}; + +constexpr int GEOM_IDX = 1; // todo 0 or 1?? + +std::vector collect_instance_info( const shrub_types::DrawableInlineArrayInstanceShrub* instances, const std::vector* protos) { - std::vector result; + std::vector result; for (auto& instance : instances->instances) { - // todo left off - shrub_types::Shrub info; + ShrubInstanceInfo info; info.prototype_idx = instance.bucket_index; info.vis_id = instance.id; for (int i = 0; i < 4; i++) { info.bsphere[i] = instance.bsphere.data[i]; } - info.mat = extract_tie_matrix(instance.origin.data); + info.mat = extract_shrub_matrix(instance.origin.data); info.mat[3][0] += info.bsphere[0]; info.mat[3][1] += info.bsphere[1]; info.mat[3][2] += info.bsphere[2]; info.wind_index = instance.wind_index; // there's a value stashed here that we can get rid of // it is related to wind. + // TODO - is this the same for shrubs? info.unknown_wind_related_value = info.mat[0][3]; info.mat[0][3] = 0.f; - // each fragment has its own color data (3 dmatags) - - // the number of colors (qwc) is stored in the prototype, in the color-index-qwc array of bytes. - // at an offset of index-start[geom] + frag_idx. - - // the actual data is located at the instance's color-indices + (proto.base-qw[geom] * 16) - - // and this is only the indices.... there's yet another lookup on the VU - auto& proto = protos->at(info.prototype_idx); + // TODO - from a type-level, the `instance-shrubbery` type also seems to store colors in the + // same way + // + // However, there is no `base_qw` field, `prototype-bucket-shrub` is MUCH simpler + // + // For shrub, perhaps this is stored in the shrub's header? which is a `shrubbery`? + /*auto& proto = protos->at(info.prototype_idx); u32 offset_bytes = proto.base_qw[GEOM_IDX] * 16; for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) { TieInstanceFragInfo frag_info; @@ -84,7 +292,7 @@ std::vector collect_shrub_info( info.frags.push_back(std::move(frag_info)); assert(info.frags.back().color_indices.size() > 0); offset_bytes += num_color_qwc * 16; - } + }*/ if (result.size() <= info.prototype_idx) { result.resize(info.prototype_idx + 1); @@ -95,6 +303,109 @@ std::vector collect_shrub_info( return result; } +void update_proto_info(std::vector* out, + const std::vector& map, + const TextureDB& tdb, + const std::vector& protos) { + out->resize(std::max(out->size(), protos.size())); + for (size_t i = 0; i < protos.size(); i++) { + const auto& proto = protos[i]; + auto& info = out->at(i); + assert(proto.flags == 0 || proto.flags == 2); + info.uses_generic = (proto.flags == 2); + info.name = proto.name; + info.stiffness = proto.stiffness; + info.generic_flag = proto.flags & 2; + + // TODO - shrubbery is much simpler to this, it doens't have fragments (i think) + + // for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) { + // TieFrag frag_info; + // for (int tex_idx = 0; + // tex_idx < proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).tex_count / 5; tex_idx++) + // { + // AdgifInfo adgif; + // auto& gif_data = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].gif_data; + // u8 ra_tex0 = gif_data.at(16 * (tex_idx * 5 + 0) + 8); + // u64 ra_tex0_val; + // memcpy(&ra_tex0_val, &gif_data.at(16 * (tex_idx * 5 + 0)), 8); + // assert(ra_tex0 == (u8)GsRegisterAddress::TEX0_1); + // assert(ra_tex0_val == 0 || ra_tex0_val == 0x800000000); // note: decal + // frag_info.has_magic_tex0_bit = ra_tex0_val == 0x800000000; + // memcpy(&adgif.first_w, &gif_data.at(16 * (tex_idx * 5 + 0) + 12), 4); + + // u8 ra_tex1 = gif_data.at(16 * (tex_idx * 5 + 1) + 8); + // u64 ra_tex1_val; + // memcpy(&ra_tex1_val, &gif_data.at(16 * (tex_idx * 5 + 1)), 8); + // assert(ra_tex1 == (u8)GsRegisterAddress::TEX1_1); + // assert(ra_tex1_val == 0x120); // some flag + // u32 original_tex; + // memcpy(&original_tex, &gif_data.at(16 * (tex_idx * 5 + 1) + 8), 4); + // u32 new_tex = remap_texture(original_tex, map); + // if (original_tex != new_tex) { + // fmt::print("map from 0x{:x} to 0x{:x}\n", original_tex, new_tex); + // } + // u32 tpage = new_tex >> 20; + // u32 tidx = (new_tex >> 8) & 0b1111'1111'1111; + // u32 tex_combo = (((u32)tpage) << 16) | tidx; + // auto tex = tdb.textures.find(tex_combo); + // assert(tex != tdb.textures.end()); + // adgif.combo_tex = tex_combo; + // memcpy(&adgif.second_w, &gif_data.at(16 * (tex_idx * 5 + 1) + 12), 4); + + // if (ra_tex0_val == 0x800000000) { + // fmt::print("texture {} in {} has weird tex setting\n", tex->second.name, proto.name); + // } + + // u8 ra_mip = gif_data.at(16 * (tex_idx * 5 + 2) + 8); + // assert(ra_mip == (u8)GsRegisterAddress::MIPTBP1_1); + // memcpy(&adgif.third_w, &gif_data.at(16 * (tex_idx * 5 + 2) + 12), 4); + + // // who cares about the value + + // u8 ra_clamp = gif_data.at(16 * (tex_idx * 5 + 3) + 8); + // assert(ra_clamp == (u8)GsRegisterAddress::CLAMP_1); + // u64 clamp; + // memcpy(&clamp, &gif_data.at(16 * (tex_idx * 5 + 3)), 8); + // adgif.clamp_val = clamp; + + // u8 ra_alpha = gif_data.at(16 * (tex_idx * 5 + 4) + 8); + // assert(ra_alpha == (u8)GsRegisterAddress::ALPHA_1); + // u64 alpha; + // memcpy(&alpha, &gif_data.at(16 * (tex_idx * 5 + 4)), 8); + // adgif.alpha_val = alpha; + // frag_info.adgifs.push_back(adgif); + // } + // frag_info.expected_dverts = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].num_dverts; + // int tex_qwc = proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).tex_count; + // int other_qwc = proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).gif_count; + // frag_info.other_gif_data.resize(16 * other_qwc); + // memcpy(frag_info.other_gif_data.data(), + // proto.geometry[GEOM_IDX].tie_fragments[frag_idx].gif_data.data() + (16 * tex_qwc), + // 16 * other_qwc); + + // const auto& pr = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].point_ref; + // int in_qw = pr.size() / 16; + // int out_qw = in_qw * 2; + // frag_info.points_data.resize(out_qw * 16); + // { + // const s16* in_ptr = (const s16*)pr.data(); + // s32* out_ptr = (s32*)frag_info.points_data.data(); + // for (int ii = 0; ii < out_qw * 4; ii++) { + // out_ptr[ii] = in_ptr[ii]; + // } + // } + + // // just for debug + // for (int g = 0; g < 4; g++) { + // frag_info.point_sizes.push_back(proto.geometry[g].tie_fragments[frag_idx].point_ref.size()); + // } + + // info.frags.push_back(std::move(frag_info)); + //} + } +} + void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, const std::string& debug_name, const std::vector& map, @@ -107,8 +418,8 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, // sanity check the vis tree (not a perfect check, but this is used in game and should be right) assert(tree->length == (int)tree->arrays.size()); assert(tree->length > 0); - auto last_array = tree->arrays.at(6).get(); // shrub is the second last tree in `background-work` - auto as_instance_array = dynamic_cast(last_array); + auto last_array = tree->arrays.back().get(); + auto as_instance_array = dynamic_cast(last_array); assert(as_instance_array); assert(as_instance_array->length == (int)as_instance_array->instances.size()); assert(as_instance_array->length > 0); @@ -136,45 +447,45 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, } } - auto info = collect_instance_info(as_instance_array, &tree->prototypes.prototype_array_tie.data); - update_proto_info(&info, tex_map, tex_db, tree->prototypes.prototype_array_tie.data); + auto info = collect_instance_info(as_instance_array, &tree->info.prototype_array_shrub.data); + update_proto_info(&info, map, tex_db, tree->info.prototype_array_shrub.data); // AHHHH - likely stuck // debug_print_info(info); - emulate_tie_prototype_program(info); - emulate_tie_instance_program(info); - emulate_kicks(info); + // emulate_tie_prototype_program(info); + // emulate_tie_instance_program(info); + // emulate_kicks(info); - if (dump_level) { - auto dir = file_util::get_file_path({fmt::format("debug_out/tie-{}/", debug_name)}); - file_util::create_dir_if_needed(dir); - for (auto& proto : info) { - auto data = debug_dump_proto_to_obj(proto); - file_util::write_text_file(fmt::format("{}/{}.obj", dir, proto.name), data); - // file_util::create_dir_if_needed() - } + // if (dump_level) { + // auto dir = file_util::get_file_path({fmt::format("debug_out/tie-{}/", debug_name)}); + // file_util::create_dir_if_needed(dir); + // for (auto& proto : info) { + // auto data = debug_dump_proto_to_obj(proto); + // file_util::write_text_file(fmt::format("{}/{}.obj", dir, proto.name), data); + // // file_util::create_dir_if_needed() + // } - auto full = dump_full_to_obj(info); - file_util::write_text_file(fmt::format("{}/ALL.obj", dir), full); - } + // auto full = dump_full_to_obj(info); + // file_util::write_text_file(fmt::format("{}/ALL.obj", dir), full); + //} - auto full_palette = make_big_palette(info); - add_vertices_and_static_draw(this_tree, out, tex_db, info); + // auto full_palette = make_big_palette(info); + // add_vertices_and_static_draw(this_tree, out, tex_db, info); - for (auto& draw : this_tree.static_draws) { - for (auto& str : draw.vis_groups) { - auto it = instance_parents.find(str.vis_idx); - if (it == instance_parents.end()) { - str.vis_idx = UINT32_MAX; - } else { - str.vis_idx = it->second; - } - } - } + // for (auto& draw : this_tree.static_draws) { + // for (auto& str : draw.vis_groups) { + // auto it = instance_parents.find(str.vis_idx); + // if (it == instance_parents.end()) { + // str.vis_idx = UINT32_MAX; + // } else { + // str.vis_idx = it->second; + // } + // } + //} - this_tree.colors = full_palette.colors; - fmt::print("TIE tree has {} draws\n", this_tree.static_draws.size()); - out.tie_trees.push_back(std::move(this_tree)); + // this_tree.colors = full_palette.colors; + // fmt::print("TIE tree has {} draws\n", this_tree.static_draws.size()); + // out.tie_trees.push_back(std::move(this_tree)); } } // namespace decompiler diff --git a/decompiler/level_extractor/extract_shrub.h b/decompiler/level_extractor/extract_shrub.h index d812677857..9ee4e345f4 100644 --- a/decompiler/level_extractor/extract_shrub.h +++ b/decompiler/level_extractor/extract_shrub.h @@ -64,6 +64,8 @@ struct PrototypeBucketShrub { // - rlength-mid // - stiffness + float stiffness; // - 60 + u32 next[4]; // 64 u16 count[4]; // 80 | NOTE - overlayed in places as a 128bit value @@ -113,7 +115,7 @@ struct InstanceShrubbery : public level_tools::Drawable { u16 wind_index; // 62 // --- instance-shrubbery --- - u32 color; // 8 + Ref color_indices; // 8 - can't read this in the first pass because we don't know how long. level_tools::Vector flat_normal; // 64 level_tools::Vector flat_hwidth; // 76 }; diff --git a/decompiler/level_extractor/extract_tie.cpp b/decompiler/level_extractor/extract_tie.cpp index 547c3a75b4..6bfb1cc24f 100644 --- a/decompiler/level_extractor/extract_tie.cpp +++ b/decompiler/level_extractor/extract_tie.cpp @@ -1,6 +1,5 @@ #include -#include "extract_common.h" #include "extract_tie.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" @@ -9,6 +8,64 @@ namespace decompiler { + +/// +/// Get the index of the first draw node in an array. Works for node or tfrag. +/// +/// +/// +u16 get_first_idx(const level_tools::DrawableInlineArray* array) { + auto as_tie_instances = dynamic_cast(array); + auto as_nodes = dynamic_cast(array); + if (as_tie_instances) { + return as_tie_instances->instances.at(0).id; + } else if (as_nodes) { + return as_nodes->draw_nodes.at(0).id; + } else { + assert(false); + } +} + +/// +/// Verify node indices follow the patterns we expect. Takes start as the expected first, writes the +/// end. +/// +/// +/// +/// +/// +bool verify_node_indices_from_array(const level_tools::DrawableInlineArray* array, + u16 start, + u16* end) { + auto as_tie_instances = dynamic_cast(array); + auto as_nodes = dynamic_cast(array); + + if (as_tie_instances) { + for (auto& elt : as_tie_instances->instances) { + if (elt.id != start) { + fmt::print("bad inst: exp {} got {}\n", start, elt.id); + return false; + } + start++; + } + *end = start; + return true; + } else if (as_nodes) { + for (auto& elt : as_nodes->draw_nodes) { + if (elt.id != start) { + fmt::print("bad node: exp {} got {}\n", start, elt.id); + return false; + } + start++; + } + *end = start; + return true; + } else { + fmt::print("bad node array type: {}\n", array->my_type()); + return false; + } +} + /*! * Verify all node indices in a tree. */