From c8db6bcaee36fb553d4104b65f10c8dd7bb83d69 Mon Sep 17 00:00:00 2001 From: water Date: Mon, 24 Jan 2022 21:19:49 -0500 Subject: [PATCH] add code to BspHeader to get GOAL types for shrubs --- decompiler/level_extractor/BspHeader.cpp | 376 ++++++++++++- decompiler/level_extractor/BspHeader.h | 532 +++++++++++++------ decompiler/level_extractor/extract_shrub.cpp | 10 +- decompiler/level_extractor/extract_shrub.h | 145 +---- 4 files changed, 761 insertions(+), 302 deletions(-) diff --git a/decompiler/level_extractor/BspHeader.cpp b/decompiler/level_extractor/BspHeader.cpp index 56704afde0..f77906f87a 100644 --- a/decompiler/level_extractor/BspHeader.cpp +++ b/decompiler/level_extractor/BspHeader.cpp @@ -135,6 +135,10 @@ std::unique_ptr make_draw_node_child(TypedRef ref, auto result = std::make_unique(); result->read_from_file(ref, dts, stats); return result; + } else if (ref.type->get_name() == "instance-shrubbery") { + auto result = std::make_unique(); + result->read_from_file(ref, dts, stats); + return result; } else { throw Error("Unknown child of draw node: {}\n", ref.type->get_name()); } @@ -149,6 +153,8 @@ int get_child_stride(const std::string& type) { return 64; } else if (type == "drawable-actor") { return 32; + } else if (type == "instance-shrubbery") { + return 80; } else { throw Error("unknown child for stride: {}", type); } @@ -752,6 +758,13 @@ std::unique_ptr make_drawable_inline_array( result->read_from_file(ref, dts, stats); return result; } + + if (ref.type->get_name() == "drawable-inline-array-instance-shrub") { + auto result = std::make_unique(); + result->read_from_file(ref, dts, stats); + return result; + } + auto result = std::make_unique(); result->read_from_file(ref, dts, stats); return result; @@ -872,7 +885,6 @@ void PrototypeBucketTie::read_from_file(TypedRef ref, assert(flags == 0 || flags == 2); in_level = read_plain_data_field(ref, "in-level", dts); utextures = read_plain_data_field(ref, "utextures", dts); - // todo drawables dists.read_from_file(get_field_ref(ref, "dists", dts)); rdists.read_from_file(get_field_ref(ref, "rdists", dts)); stiffness = read_plain_data_field(ref, "stiffness", dts); @@ -1106,6 +1118,362 @@ std::string DrawableTreeInstanceTie::my_type() const { return "drawable-tree-instance-tie"; } +////////////////////////// +// shrub +////////////////////////// + +namespace shrub_types { + +void DrawableTreeInstanceShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + // the usual drawable stuff + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + // unfortunately, shrub uses the arrays thing differently. + // there's just one top level array, and the nodes are a bit scattered in memory below that. + // it doesn't have the 8 child rule + auto data_ref = get_field_ref(ref, "data", dts); + if ((data_ref.byte_offset % 4) != 0) { + throw Error("misaligned data array"); + } + for (int idx = 0; idx < length; idx++) { + Ref array_slot_ref = data_ref; + array_slot_ref.byte_offset += idx * 4; + + Ref object_ref = deref_label(array_slot_ref); + object_ref.byte_offset -= 4; + + arrays.push_back(make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } + // confirm that we have the weird shrub pattern and only found one array. + assert(length == 1); + + // now, let's try to discover the remaining arrays (instances). + // basically we just look after the top level array in memory. + // once we find something else (the time of day palette) we know we're at the end. + // the game finds these by traversing the tree, but this is a little easier, and gets us + // the familiar arrays that we used in tie/tfrag. + + Ref object_ref = deref_label(data_ref); + object_ref.byte_offset -= 4; + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + + bool done = false; + object_ref.byte_offset += 16; + while (!done) { + auto& word = object_ref.data->words_by_seg.at(object_ref.seg).at(object_ref.byte_offset / 4); + if (word.kind() == decompiler::LinkedWord::TYPE_PTR) { + if (word.symbol_name() == "drawable-inline-array-node") { + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } else if (word.symbol_name() == "drawable-inline-array-instance-shrub") { + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } else if (word.symbol_name() == "time-of-day-palette") { + done = true; + } else { + assert(word.symbol_name() == "draw-node" || word.symbol_name() == "instance-shrubbery"); + } + } + object_ref.byte_offset += 16; + } + + // this "info" thing holds all the prototypes + auto pt = deref_label(get_field_ref(ref, "info", dts)); + pt.byte_offset -= 4; + info.read_from_file(typed_ref_from_basic(pt, dts), dts, stats); + + // time of day palette. we'll want these colors in the FR3 file. + auto palette = deref_label(get_field_ref(ref, "colors-added", dts)); + time_of_day.width = deref_u32(palette, 0); + assert(time_of_day.width == 8); + time_of_day.height = deref_u32(palette, 1); + time_of_day.pad = deref_u32(palette, 2); + assert(time_of_day.pad == 0); + for (int i = 0; i < int(8 * time_of_day.height); i++) { + time_of_day.colors.push_back(deref_u32(palette, 3 + i)); + } +} + +std::string DrawableTreeInstanceShrub::my_type() const { + return "drawable-tree-instance-shrub"; +} + +std::string DrawableTreeInstanceShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + if (!settings.expand_shrub) { + return ""; + } + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + if (settings.expand_shrub) { + for (size_t i = 0; i < discovered_arrays.size(); i++) { + result += fmt::format("{}arrays [{}] ({}):\n", is, i, discovered_arrays[i]->my_type()); + result += discovered_arrays[i]->print(settings, next_indent); + } + + result += fmt::format("{}prototypes:\n", is); + result += info.print(settings, next_indent); + } + + return result; +} + +void InstanceShrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* /*stats*/) { + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + bucket_index = read_plain_data_field(ref, "bucket-index", dts); + id = read_plain_data_field(ref, "id", dts); + origin.read_from_file(get_field_ref(ref, "origin", dts)); + wind_index = read_plain_data_field(ref, "wind-index", dts); + color_indices = read_plain_data_field(ref, "color-indices", dts); + flat_normal.read_from_file(get_field_ref(ref, "flat-normal", dts)); +} + +std::string InstanceShrubbery::print(const level_tools::PrintSettings& /*settings*/, + int indent) const { + std::string is(indent, ' '); + std::string result; + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + result += fmt::format("{}bucket-index: {}\n", is, bucket_index); + result += fmt::format("{}flat-normal: {}", is, flat_normal.print_meters()); + result += fmt::format("{}color-indices: {}\n", is, color_indices); + result += fmt::format("{}wind-index: {}\n", is, wind_index); + return result; +} + +void DrawableInlineArrayInstanceShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) { + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < length; i++) { + Ref obj_ref = data_ref; + obj_ref.byte_offset += 80 * i; // todo not a constant here + auto type = get_type_of_basic(obj_ref); + if (type != "instance-shrubbery") { + throw Error("bad draw node type: {}", type); + } + instances.emplace_back(); + instances.back().read_from_file(typed_ref_from_basic(obj_ref, dts), dts, stats); + } +} + +std::string DrawableInlineArrayInstanceShrub::print(const PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + if (settings.expand_shrub) { + for (size_t i = 0; i < instances.size(); i++) { + result += fmt::format("{}draw-nodes [{}] ({}):\n", is, i, instances[i].my_type()); + result += instances[i].print(settings, next_indent); + } + } + + return result; +} + +void PrototypeArrayShrubInfo::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + prototype_inline_array_shrub.read_from_file( + get_and_check_ref_to_basic(ref, "prototype-inline-array-shrub", + "prototype-inline-array-shrub", dts), + dts, stats); + wind_vectors = deref_label(get_field_ref(ref, "wind-vectors", dts)); +} + +std::string PrototypeArrayShrubInfo::print(const level_tools::PrintSettings& settings, + int indent) const { + return prototype_inline_array_shrub.print(settings, indent); +} + +void PrototypeInlineArrayShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + length = read_plain_data_field(ref, "length", dts); + auto data_ref = get_field_ref(ref, "data", dts); + + for (int i = 0; i < length; i++) { + Ref thing = data_ref; + // note: unlike tie, these are stored in an inline array. + thing.byte_offset += 112 * i; // todo - not a constant here + auto type = get_type_of_basic(thing); + if (type != "prototype-bucket-shrub") { + throw Error("bad type in PrototypeInlineArrayShrub data: {}\n", type); + } + data.emplace_back(); + data.back().read_from_file(typed_ref_from_basic(thing, dts), dts, stats); + } +} + +std::string PrototypeInlineArrayShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}length: {}\n", is, length); + + for (u32 i = 0; i < data.size(); i++) { + result += fmt::format("{}data [{}]:\n", is, i); + result += data[i].print(settings, next_indent); + } + return result; +} + +void PrototypeBucketShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + name = read_string_field(ref, "name", dts, true); + flags = read_plain_data_field(ref, "flags", dts); + if (flags) { + // lid in misty has flag 2, not sure what it means yet. + fmt::print("proto: {} flags: {}\n", name, flags); + } + assert(flags == 0 || flags == 2); + in_level = read_plain_data_field(ref, "in-level", dts); + utextures = read_plain_data_field(ref, "utextures", dts); + dists.read_from_file(get_field_ref(ref, "dists", dts)); + rdists.read_from_file(get_field_ref(ref, "rdists", dts)); + stiffness = read_plain_data_field(ref, "stiffness", dts); + // 64 to 112 should be zeros + for (int i = 0; i < 12; i++) { + assert(deref_u32(ref.ref, 16 + i) == 0); + } + + auto geom_start = get_field_ref(ref, "geometry", dts); + + // first geometry is generic and we should always have it. + auto generic_geom = deref_label(geom_start); + generic_geom.byte_offset -= 4; + if (get_type_of_basic(generic_geom) != "prototype-generic-shrub") { + throw Error("bad generic shrub type: {}", get_type_of_basic(generic_geom)); + } + geom_start.byte_offset += 4; + + // second is same data, but in prototype-shrubbery form (for normal shrub renderer) + auto normal_geom = deref_label(geom_start); + normal_geom.byte_offset -= 4; + if (get_type_of_basic(normal_geom) != "prototype-shrubbery") { + throw Error("bad normal shrub type: {}", get_type_of_basic(normal_geom)); + } + shrubbery_geom.read_from_file(typed_ref_from_basic(normal_geom, dts), dts, stats); + geom_start.byte_offset += 4; + + // todo transparent version + // todo billboard version. +} + +std::string PrototypeBucketShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + result += fmt::format("{}name: {}\n", is, name); + result += fmt::format("{}flags: {}\n", is, flags); + + result += fmt::format("{}normal-geometry [1]:\n", is); + result += shrubbery_geom.print(settings, indent + 4); + + return result; +} + +void PrototypeShrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < length; i++) { + Ref obj_ref = data_ref; + obj_ref.byte_offset += 32 * i; // todo not a constant here + auto type = get_type_of_basic(obj_ref); + if (type != "shrubbery") { + throw Error("bad draw node type: {}", type); + } + shrubs.emplace_back(); + shrubs.back().read_from_file(typed_ref_from_basic(obj_ref, dts), dts, stats); + } +} + +std::string PrototypeShrubbery::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + for (size_t i = 0; i < shrubs.size(); i++) { + result += fmt::format("{}draw-nodes [{}] ({}):\n", is, i, shrubs[i].my_type()); + result += shrubs[i].print(settings, next_indent); + } + + return result; +} + +void copy_dma_to_vector(std::vector* out, Ref data_start, int qwc) { + out->resize(qwc * 16); + for (int i = 0; i < qwc * 4; i++) { + u32 val = deref_u32(data_start, i); + memcpy(out->data() + i, &val, 4); + } +} + +void Shrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + // read the easy ones. + obj_qwc = read_plain_data_field(ref, "obj-qwc", dts); + vtx_qwc = read_plain_data_field(ref, "vtx-qwc", dts); + col_qwc = read_plain_data_field(ref, "col-qwc", dts); + stq_qwc = read_plain_data_field(ref, "stq-qwc", dts); + + auto header_data = deref_label(get_field_ref(ref, "header", dts)); + // guess that the header is 24 * 4 = 96 bytes here. + // not sure what it's used for yet. + header.resize(24); + for (int i = 0; i < 24; i++) { + u32 val = deref_u32(header_data, i); + memcpy(header.data() + i, &val, 4); + } + + copy_dma_to_vector(&obj, deref_label(get_field_ref(ref, "obj", dts)), obj_qwc); + copy_dma_to_vector(&vtx, deref_label(get_field_ref(ref, "vtx", dts)), vtx_qwc); + copy_dma_to_vector(&col, deref_label(get_field_ref(ref, "col", dts)), col_qwc); + copy_dma_to_vector(&stq, deref_label(get_field_ref(ref, "stq", dts)), stq_qwc); + copy_dma_to_vector(&textures, deref_label(get_field_ref(ref, "textures", dts)), header[0] * 10); +} + +std::string Shrubbery::print(const level_tools::PrintSettings& settings, int indent) const { + std::string is(indent, ' '); + + return fmt::format("{} qwcs: {} {} {} {}, tex: {}\n", is, obj_qwc, vtx_qwc, col_qwc, stq_qwc, + header[0] * 2); +} + +} // namespace shrub_types + std::unique_ptr make_drawable_tree(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats) { @@ -1150,6 +1518,12 @@ std::unique_ptr make_drawable_tree(TypedRef ref, tree->read_from_file(ref, dts, stats); return tree; } + + if (ref.type->get_name() == "drawable-tree-instance-shrub") { + auto tree = std::make_unique(); + tree->read_from_file(ref, dts, stats); + return tree; + } auto tree = std::make_unique(); tree->read_from_file(ref, dts, stats); return tree; diff --git a/decompiler/level_extractor/BspHeader.h b/decompiler/level_extractor/BspHeader.h index 6ee2de7e7b..3fadab61bf 100644 --- a/decompiler/level_extractor/BspHeader.h +++ b/decompiler/level_extractor/BspHeader.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "decompiler/util/goal_data_reader.h" @@ -13,45 +14,16 @@ class DecompilerTypeSystem; } // namespace decompiler namespace level_tools { - -struct Vector { - float data[4]; - - void read_from_file(Ref ref); - - std::string print(int indent = 0) const; - std::string print_meters(int indent = 0) const; -}; - -struct Matrix4h { - u16 data[16]; - void read_from_file(Ref ref); -}; - -struct FileInfo { - std::string file_type; - std::string file_name; - u32 major_version; - u32 minor_version; - - std::string maya_file_name; - std::string tool_debug; - std::string mdb_file_name; - - void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts); - - std::string print(int indent = 0) const; -}; - struct PrintSettings { - bool print_tfrag = true; - bool expand_draw_node = true; - bool expand_drawable_tree_tfrag = true; - bool expand_drawable_tree_trans_tfrag = true; - bool expand_drawable_tree_tie_proto = true; + bool print_tfrag = false; + bool expand_draw_node = false; + bool expand_drawable_tree_tfrag = false; + bool expand_drawable_tree_trans_tfrag = false; + bool expand_drawable_tree_tie_proto = false; bool expand_drawable_tree_tie_proto_data = false; bool expand_drawable_tree_instance_tie = false; bool expand_drawable_tree_actor = false; + bool expand_shrub = true; }; struct DrawStats { @@ -66,6 +38,45 @@ struct DrawStats { std::string print() const; }; +///////////////////// +// Common Types +///////////////////// + +// a normal vector of 4 floats. +struct Vector { + float data[4]; + + void read_from_file(Ref ref); + + std::string print(int indent = 0) const; + std::string print_meters(int indent = 0) const; +}; + +// a matrix with 16-bit integers. +// typically requires some unpacking step to get meaningful values. +struct Matrix4h { + u16 data[16]; + void read_from_file(Ref ref); +}; + +// A time-of-day color palette. +// this is just the raw data, doesn't have any unpacking/meaning. +struct TimeOfDayPalette { + u32 width; + u32 height; + u32 pad; + std::vector colors; +}; + +///////////////////// +// Drawable BVH +///////////////////// + +// these types are all generic container types/base classes. +// note that the unpacker just greedily chases things and doesn't deduplicate, so comparing +// pointers for equality won't tell you if two things are the same game object or not. + +// the base class for everything in the BVH tree system. struct Drawable { virtual void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -75,8 +86,9 @@ struct Drawable { virtual ~Drawable() = default; }; -struct DrawableInlineArray : public Drawable {}; - +// a node in the BVH tree. It's just used for organizing things within a bsphere. +// these nodes are always in inline arrays and have between 0 and 8 children. +// the leaves of these nodes are some other type of drawable - the thing you actually want to draw. struct DrawNode : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -92,6 +104,53 @@ struct DrawNode : public Drawable { float distance = 0; }; +// an inline array of drawable. There are more specific types for the actual arrays. +struct DrawableInlineArray : public Drawable {}; + +// an inline array of draw nodes. All draw nodes at a level are stored in a drawable inline array. +struct DrawableInlineArrayNode : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector draw_nodes; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// the generic base class for a "tree". A tree is a BVH with between 1 and 8 DrawNode roots. +// there's typically a tree per renderer. +struct DrawableTree : public Drawable {}; + +// how we represent drawable trees that we don't support yet - this isn't a real type in the game. +struct DrawableTreeUnknown : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + std::string type_name; +}; + +struct DrawableInlineArrayUnknown : public DrawableInlineArray { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + std::string type_name; +}; + +///////////////////// +// Actors +///////////////////// + +// there's a tree for actors - but we don't do anything with it yet. + struct EntityActor {}; struct DrawableActor : public Drawable { @@ -107,6 +166,25 @@ struct DrawableActor : public Drawable { std::string my_type() const override { return "drawable-actor"; } }; +struct DrawableTreeActor : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; + s16 length; + // todo time of day stuff + Vector bsphere; + + std::vector> arrays; +}; + +///////////////////// +// TFRAG +///////////////////// + struct TFragmentDebugData { u16 num_tris[4]; u16 num_dverts[4]; @@ -116,6 +194,7 @@ struct TFragmentDebugData { void read_from_file(Ref ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats); }; +// the "fragment" is just a collection of data that fits into the VU memory. struct TFragment : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -150,6 +229,67 @@ struct TFragment : public Drawable { // generic // 60 - 64 }; +// array of tfragments. This is used as part of the BVH tree +struct DrawableInlineArrayTFrag : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector tfragments; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// a top-level tfragment tree. +// it has a BVH tree of tfragments as well as a time of day palette. +struct DrawableTreeTfrag : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; + s16 length; + TimeOfDayPalette time_of_day; + Vector bsphere; + + std::vector> arrays; +}; + +// various specializations of tfragment. + +struct DrawableTreeTransTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-trans-tfrag"; } +}; + +struct DrawableTreeLowresTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-lowres-tfrag"; } +}; + +struct DrawableTreeDirtTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-dirt-tfrag"; } +}; + +struct DrawableTreeIceTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-ice-tfrag"; } +}; + +struct DrawableInlineArrayTransTFrag : public DrawableInlineArrayTFrag { + std::string my_type() const override { return "drawable-inline-array-trans-tfrag"; } +}; + +///////////////////// +// TIE +///////////////////// + +// a tie-fragment is a chunk of geometry that can be sent to the VU. +// it's similar to a tfragment, but a bit simpler. +// each prototype is made up of fragments. struct TieFragment : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -173,6 +313,9 @@ struct TieFragment : public Drawable { // todo, lots more }; +// represents an instance of a prototype. +// each instance has its own color data, but the rest of the geometry/texture is shared between +// all instances of the same prototype. struct InstanceTie : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -181,9 +324,9 @@ struct InstanceTie : public Drawable { std::string my_type() const override { return "instance-tie"; } // (bucket-index uint16 :offset 6) - u16 bucket_index; + u16 bucket_index; // which prototype s16 id; - Vector bsphere; + Vector bsphere; // where we are located Matrix4h origin; u16 flags; u16 wind_index; @@ -193,101 +336,7 @@ struct InstanceTie : public Drawable { // todo, lots more }; -struct DrawableInlineArrayNode : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector draw_nodes; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayTFrag : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector tfragments; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayInstanceTie : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector instances; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayTransTFrag : public DrawableInlineArrayTFrag { - std::string my_type() const override { return "drawable-inline-array-trans-tfrag"; } -}; - -struct DrawableInlineArrayUnknown : public DrawableInlineArray { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - std::string type_name; -}; - -struct DrawableTree : public Drawable {}; - -struct TimeOfDayPalette { - u32 width; - u32 height; - u32 pad; - std::vector colors; -}; - -struct DrawableTreeTfrag : public DrawableTree { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - - s16 id; - s16 length; - // todo time of day stuff - TimeOfDayPalette time_of_day; - Vector bsphere; - - std::vector> arrays; -}; - -struct DrawableTreeActor : public DrawableTree { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - - s16 id; - s16 length; - // todo time of day stuff - Vector bsphere; - - std::vector> arrays; -}; - +// a prototype is basically just a collection of fragments struct PrototypeTie : public DrawableInlineArray { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -301,6 +350,9 @@ struct PrototypeTie : public DrawableInlineArray { std::vector tie_fragments; }; +// a prototype bucket is a collection of 4 different prototypes (called geometries), one for each +// level of detail. All geometries share the same time of day palette. +// the bucket also refers to the fact that it collect instances during actual rendering. struct PrototypeBucketTie { std::string name; // 4 - 8 u32 flags; // 8 - 12 @@ -337,6 +389,8 @@ struct PrototypeBucketTie { std::string print(const PrintSettings& settings, int indent) const; }; +// an array of all the prototypes. The prototypes aren't stored in a BVH (it wouldn't make sense - +// they can be located all over the place)- there is just a plain array. struct PrototypeArrayTie { u32 length; u32 allocated_length; @@ -347,6 +401,8 @@ struct PrototypeArrayTie { std::string print(const PrintSettings& settings, int indent) const; }; +// the reason for this type is somewhat unknown, but it just contains the prototype array and +// wind vectors. The wind vector data is all 0's and is updated as the game runs. struct ProxyPrototypeArrayTie { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats); std::string print(const PrintSettings& settings, int indent) const; @@ -355,6 +411,22 @@ struct ProxyPrototypeArrayTie { Ref wind_vectors; }; +// array of instances. These are part of the BVH +struct DrawableInlineArrayInstanceTie : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector instances; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// TIE tree struct DrawableTreeInstanceTie : public DrawableTree { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -370,31 +442,168 @@ struct DrawableTreeInstanceTie : public DrawableTree { std::vector> arrays; }; -struct DrawableTreeTransTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-trans-tfrag"; } -}; +///////////////////////////////// +// SHRUB +///////////////////////////////// -struct DrawableTreeLowresTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-lowres-tfrag"; } -}; +namespace shrub_types { -struct DrawableTreeDirtTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-dirt-tfrag"; } -}; - -struct DrawableTreeIceTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-ice-tfrag"; } -}; - -struct DrawableTreeUnknown : public DrawableTree { +struct Shrubbery : public level_tools::Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - std::string type_name; + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "shrubbery"; } + + // 4 - textures + std::vector textures; + + std::vector header; + u8 obj_qwc; // 12 + u8 vtx_qwc; // 13 + u8 col_qwc; // 14 + u8 stq_qwc; // 15 + + std::vector obj; // 16 + std::vector vtx; // 20 + std::vector col; // 24 + std::vector stq; // 28 }; +struct PrototypeShrubbery : public level_tools::DrawableInlineArray { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "prototype-shrubbery"; } + s16 id; // 0 + s16 length; // 4 + + level_tools::Vector bsphere; // 16 + std::vector shrubs; // 32 +}; + +struct Billboard {}; + +struct PrototypeBucketShrub { + std::string name; // 4 + u32 flags; // 8 + u16 in_level; // 12 + u16 utextures; // 14 + + // PrototypeShrubbery geometry[4]; // 16 + // skip generic, we think it's the same as shrubbery geom. + PrototypeShrubbery shrubbery_geom; // 1 + // todo transparent geom + // todo billboard geom + + level_tools::Vector dists; // 32 + // - near-plane + // - near-stiff + // - mid-plane + // - far-plane + level_tools::Vector rdists; // 48 + // - rlength-near + // - rlength-stiff + // - rlength-mid + // - stiffness + + float stiffness; // - 60 + + // the next/last/count/mod stuff is all 0's. + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; +}; + +struct PrototypeInlineArrayShrub { + s16 id; // 4 + s16 length; // 6 + level_tools::Vector bsphere; // 16 + std::vector data; // 32 + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; +}; + +struct PrototypeArrayShrubInfo { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; + + PrototypeInlineArrayShrub prototype_inline_array_shrub; + Ref wind_vectors; +}; + +struct InstanceShrubbery : public level_tools::Drawable { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "instance-shrubbery"; } + + s16 id; // 4 + u16 bucket_index; // 6 + level_tools::Vector bsphere; // 16 + level_tools::Matrix4h origin; // 32 + u16 wind_index; // 62 + + // --- instance-shrubbery --- + u32 color_indices; // 8 - unlike tie, I think this is just an integer offset + level_tools::Vector flat_normal; // 64 (w is flat_hwidth) +}; + +struct DrawableInlineArrayInstanceShrub : public level_tools::DrawableInlineArray { + s16 id; + s16 length; + level_tools::Vector bsphere; + + std::vector instances; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "drawable-inline-array-instance-shrub"; } +}; + +struct DrawableTreeInstanceShrub : public level_tools::DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; // 0 + s16 length; // 4 + PrototypeArrayShrubInfo info; // 8 + TimeOfDayPalette time_of_day; + level_tools::Vector bsphere; // 16 + + std::vector> arrays; + + // annoyingly, the shrub tree only has the top level in the array.... + // so we can't use the above "arrays" like we did in tfrag/tie. + // luckily, it seems to be possible to figure out the location of the remaining arrays. + // so we add this field to hold the arrays we found. + // note that the tree format is not quite the same as tfrag/tie - the 8 child max is no longer + // true, and the arrays are not truly by depth (the leaves aren't all the same depth) + // this means there may be multiple arrays of instances, and we'll need to check all of them. + std::vector> discovered_arrays; +}; + +} // namespace shrub_types + +//////////////////////////////// +// Main Level Type (bsp-header) +//////////////////////////////// + +// all the different trees are stored in a drawable-tree-array. struct DrawableTreeArray { s16 id; s16 length; @@ -406,11 +615,28 @@ struct DrawableTreeArray { std::vector> trees; }; +// levels may remap textures if they provide one that should be shared struct TextureRemap { u32 original_texid; u32 new_texid; }; +// The "file info" +struct FileInfo { + std::string file_type; + std::string file_name; + u32 major_version; + u32 minor_version; + + std::string maya_file_name; + std::string tool_debug; + std::string mdb_file_name; + + void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts); + + std::string print(int indent = 0) const; +}; + struct BspHeader { // (info file-info :offset 4) FileInfo file_info; diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp index 138aeb52ed..62e1551910 100644 --- a/decompiler/level_extractor/extract_shrub.cpp +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -7,6 +7,7 @@ #include "common/util/FileUtil.h" namespace decompiler { +using namespace level_tools; /// /// Get the index of the first draw node in an array. Works for node or tfrag. @@ -34,8 +35,8 @@ u16 get_first_idx_shrub(const level_tools::DrawableInlineArray* array) { /// /// bool verify_node_indices_from_array_shrub(const level_tools::DrawableInlineArray* array, - u16 start, - u16* end) { + u16 start, + u16* end) { auto as_shrub_instances = dynamic_cast(array); auto as_nodes = dynamic_cast(array); @@ -447,8 +448,9 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, } } - 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); + auto info = + collect_instance_info(as_instance_array, &tree->info.prototype_inline_array_shrub.data); + update_proto_info(&info, map, tex_db, tree->info.prototype_inline_array_shrub.data); // AHHHH - likely stuck diff --git a/decompiler/level_extractor/extract_shrub.h b/decompiler/level_extractor/extract_shrub.h index 9ee4e345f4..3c0a547b69 100644 --- a/decompiler/level_extractor/extract_shrub.h +++ b/decompiler/level_extractor/extract_shrub.h @@ -7,150 +7,7 @@ namespace decompiler { -namespace shrub_types { -struct Shrubbery : public level_tools::Drawable { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats) override; - std::string print(const level_tools::PrintSettings& settings, int indent) const override; - std::string my_type() const override { return "shrubbery"; } - - // 4 - textures - u16 gif_count; - std::vector gif_data; - - u32 header_unknown; // 8 - u8 obj_qwc; // 12 - u8 vtx_qwc; // 13 - u8 col_qwc; // 14 - u8 stq_qwc; // 15 - - u32 obj; // 16 - u32 vtx; // 20 - u32 col; // 24 - u32 steq; // 28 -}; - -struct PrototypeShrubbery : public level_tools::DrawableInlineArray { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats) override; - std::string print(const level_tools::PrintSettings& settings, int indent) const override; - std::string my_type() const override; - s16 id; // 0 - s16 length; // 4 - - level_tools::Vector bsphere; // 16 - std::vector shrubs; // 32 -}; - -struct PrototypeBucketShrub { - std::string name; // 4 - u32 flags; // 8 - u16 in_level; // 12 - u16 utextures; // 14 - - PrototypeShrubbery geometry[4]; // 16 - - level_tools::Vector dists; // 32 - // - near-plane - // - near-stiff - // - mid-plane - // - far-plane - level_tools::Vector rdists; // 48 - // - rlength-near - // - rlength-stiff - // - rlength-mid - // - stiffness - - float stiffness; // - 60 - - u32 next[4]; // 64 - u16 count[4]; // 80 | NOTE - overlayed in places as a 128bit value - - u16 mod_count[4]; // 88 - Ref last[4]; // 96 | NOTE - overlayed to clear with a single uint128 - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats); - std::string print(const level_tools::PrintSettings& settings, int indent) const; -}; - -struct PrototypeArrayShrub { - s16 id; // 4 - s16 length; // 6 - level_tools::Vector bsphere; // 16 - std::vector data; // 32 - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats); - std::string print(const level_tools::PrintSettings& settings, int indent) const; -}; - -struct PrototypeArrayShrubInfo { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats); - std::string print(const level_tools::PrintSettings& settings, int indent) const; - - PrototypeArrayShrub prototype_array_shrub; - // todo wind vectors. -}; - -struct InstanceShrubbery : public level_tools::Drawable { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats) override; - std::string print(const level_tools::PrintSettings& settings, int indent) const override; - std::string my_type() const override { return "instance-shrubbery"; } - - s16 id; // 4 - u16 bucket_index; // 6 - level_tools::Vector bsphere; // 16 - level_tools::Matrix4h origin; // 32 - u16 flags; // ?? - u16 wind_index; // 62 - - // --- instance-shrubbery --- - 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 -}; - -struct DrawableInlineArrayInstanceShrub : public level_tools::DrawableInlineArray { - s16 id; - s16 length; - level_tools::Vector bsphere; - - std::vector instances; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats) override; - std::string print(const level_tools::PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableTreeInstanceShrub : public level_tools::DrawableTree { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - level_tools::DrawStats* stats) override; - std::string print(const level_tools::PrintSettings& settings, int indent) const override; - std::string my_type() const override; - - s16 id; // 0 - s16 length; // 4 - PrototypeArrayShrubInfo info; // 8 - u32 colors_added; // 12 - level_tools::Vector bsphere; // 16 - - std::vector> arrays; -}; - -} // namespace shrub_types /// /// Extract shrubs from the level @@ -162,7 +19,7 @@ struct DrawableTreeInstanceShrub : public level_tools::DrawableTree { /// /// /// -void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, +void extract_shrub(const level_tools::shrub_types::DrawableTreeInstanceShrub* tree, const std::string& debug_name, const std::vector& map, const TextureDB& tex_db,