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.
*/