add code to BspHeader to get GOAL types for shrubs

This commit is contained in:
water
2022-01-24 21:19:49 -05:00
parent b48a0c0d0c
commit c8db6bcaee
4 changed files with 761 additions and 302 deletions
+375 -1
View File
@@ -135,6 +135,10 @@ std::unique_ptr<Drawable> make_draw_node_child(TypedRef ref,
auto result = std::make_unique<DrawableActor>();
result->read_from_file(ref, dts, stats);
return result;
} else if (ref.type->get_name() == "instance-shrubbery") {
auto result = std::make_unique<shrub_types::InstanceShrubbery>();
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<DrawableInlineArray> 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<shrub_types::DrawableInlineArrayInstanceShrub>();
result->read_from_file(ref, dts, stats);
return result;
}
auto result = std::make_unique<DrawableInlineArrayUnknown>();
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<u16>(ref, "in-level", dts);
utextures = read_plain_data_field<u16>(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<float>(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<s16>(ref, "id", dts);
length = read_plain_data_field<s16>(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<u16>(ref, "bucket-index", dts);
id = read_plain_data_field<s16>(ref, "id", dts);
origin.read_from_file(get_field_ref(ref, "origin", dts));
wind_index = read_plain_data_field<u16>(ref, "wind-index", dts);
color_indices = read_plain_data_field<u32>(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<s16>(ref, "id", dts);
length = read_plain_data_field<s16>(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<s16>(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<u32>(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<u16>(ref, "in-level", dts);
utextures = read_plain_data_field<u16>(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<float>(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<s16>(ref, "id", dts);
length = read_plain_data_field<s16>(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<u8>* 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<u8>(ref, "obj-qwc", dts);
vtx_qwc = read_plain_data_field<u8>(ref, "vtx-qwc", dts);
col_qwc = read_plain_data_field<u8>(ref, "col-qwc", dts);
stq_qwc = read_plain_data_field<u8>(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<DrawableTree> make_drawable_tree(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats) {
@@ -1150,6 +1518,12 @@ std::unique_ptr<DrawableTree> 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<shrub_types::DrawableTreeInstanceShrub>();
tree->read_from_file(ref, dts, stats);
return tree;
}
auto tree = std::make_unique<DrawableTreeUnknown>();
tree->read_from_file(ref, dts, stats);
return tree;
+379 -153
View File
@@ -4,6 +4,7 @@
#include <string>
#include <memory>
#include <vector>
#include <optional>
#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<u32> 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<DrawNode> 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<std::unique_ptr<DrawableInlineArray>> 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<TFragment> 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<std::unique_ptr<DrawableInlineArray>> 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<DrawNode> 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<TFragment> 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<InstanceTie> 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<u32> 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<std::unique_ptr<DrawableInlineArray>> 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<std::unique_ptr<DrawableInlineArray>> 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<TieFragment> 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<InstanceTie> 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<std::unique_ptr<DrawableInlineArray>> 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<u8> textures;
std::vector<u32> header;
u8 obj_qwc; // 12
u8 vtx_qwc; // 13
u8 col_qwc; // 14
u8 stq_qwc; // 15
std::vector<u8> obj; // 16
std::vector<u8> vtx; // 20
std::vector<u8> col; // 24
std::vector<u8> 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<Shrubbery> 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<PrototypeBucketShrub> 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<InstanceShrubbery> 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<std::unique_ptr<level_tools::DrawableInlineArray>> 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<std::unique_ptr<level_tools::DrawableInlineArray>> 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<std::unique_ptr<DrawableTree>> 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;
+6 -4
View File
@@ -7,6 +7,7 @@
#include "common/util/FileUtil.h"
namespace decompiler {
using namespace level_tools;
/// <summary>
/// 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) {
/// <param name="end"></param>
/// <returns></returns>
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<const shrub_types::DrawableInlineArrayInstanceShrub*>(array);
auto as_nodes = dynamic_cast<const level_tools::DrawableInlineArrayNode*>(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
+1 -144
View File
@@ -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<u8> 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<Shrubbery> 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<PrototypeBucketShrub> 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<InstanceShrubbery> 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<std::unique_ptr<level_tools::DrawableInlineArray>> arrays;
};
} // namespace shrub_types
/// <summary>
/// Extract shrubs from the level
@@ -162,7 +19,7 @@ struct DrawableTreeInstanceShrub : public level_tools::DrawableTree {
/// <param name="expected_missing_textures"></param>
/// <param name="out"></param>
/// <param name="dump_level"></param>
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<level_tools::TextureRemap>& map,
const TextureDB& tex_db,