add MSAA 4x + lod picking for TIE & tfrag (#1167)

* MSAA

* support different TIE lods

* tfrag lod picking

* always compress screenshots

* mute annoying nvidia opengl message

* use a string here instead

* oops

* more mistakes...
This commit is contained in:
ManDude
2022-02-15 23:42:48 +00:00
committed by GitHub
parent 70fcb05a99
commit b36199f4f8
28 changed files with 578 additions and 482 deletions
+18 -14
View File
@@ -111,22 +111,26 @@ void Level::serialize(Serializer& ser) {
tex.serialize(ser);
}
if (ser.is_saving()) {
ser.save<size_t>(tfrag_trees.size());
} else {
tfrag_trees.resize(ser.load<size_t>());
}
for (auto& tree : tfrag_trees) {
tree.serialize(ser);
for (int geom = 0; geom < 3; ++geom) {
if (ser.is_saving()) {
ser.save<size_t>(tfrag_trees[geom].size());
} else {
tfrag_trees[geom].resize(ser.load<size_t>());
}
for (auto& tree : tfrag_trees[geom]) {
tree.serialize(ser);
}
}
if (ser.is_saving()) {
ser.save<size_t>(tie_trees.size());
} else {
tie_trees.resize(ser.load<size_t>());
}
for (auto& tree : tie_trees) {
tree.serialize(ser);
for (int geom = 0; geom < 4; ++geom) {
if (ser.is_saving()) {
ser.save<size_t>(tie_trees[geom].size());
} else {
tie_trees[geom].resize(ser.load<size_t>());
}
for (auto& tree : tie_trees[geom]) {
tree.serialize(ser);
}
}
ser.from_ptr(&version2);
+3 -3
View File
@@ -11,7 +11,7 @@
namespace tfrag3 {
constexpr int TFRAG3_VERSION = 9;
constexpr int TFRAG3_VERSION = 10;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
@@ -161,8 +161,8 @@ struct Level {
u16 version = TFRAG3_VERSION;
std::string level_name;
std::vector<Texture> textures;
std::vector<TfragTree> tfrag_trees;
std::vector<TieTree> tie_trees;
std::array<std::vector<TfragTree>, 3> tfrag_trees;
std::array<std::vector<TieTree>, 4> tie_trees;
u16 version2 = TFRAG3_VERSION;
void serialize(Serializer& ser);
};
+1 -4
View File
@@ -100,11 +100,8 @@ void write_binary_file(const std::string& name, const void* data, size_t size) {
fclose(fp);
}
void write_rgba_png(const std::string& name, void* data, int w, int h, bool compress) {
void write_rgba_png(const std::string& name, void* data, int w, int h) {
auto flags = 0;
if (!compress) {
flags = fpng::FPNG_FORCE_UNCOMPRESSED;
}
auto ok = fpng::fpng_encode_image_to_file(name.c_str(), data, w, h, 4, flags);
+1 -1
View File
@@ -18,7 +18,7 @@ std::string get_project_path();
std::string get_file_path(const std::vector<std::string>& input);
bool create_dir_if_needed(const std::string& path);
void write_binary_file(const std::string& name, const void* data, size_t size);
void write_rgba_png(const std::string& name, void* data, int w, int h, bool compress);
void write_rgba_png(const std::string& name, void* data, int w, int h);
void write_text_file(const std::string& file_name, const std::string& text);
std::vector<uint8_t> read_binary_file(const std::string& filename);
std::string read_text_file(const std::string& path);
+1 -1
View File
@@ -19918,7 +19918,7 @@
(define-extern clone-anim-once (function handle int symbol string none :behavior process-drawable))
(define-extern convert-to-hud-object (function process-drawable hud none :behavior hud))
(define-extern clone-anim (function handle int symbol string none :behavior process-drawable))
(define-extern merc-eye-anim (function manipy none))
(define-extern merc-eye-anim (function process-drawable none))
(define-extern ja-anim-done? (function process symbol))
(define-extern command-get-camera (function object state state))
(define-extern camera-anim (function symbol basic entity (pointer process) :behavior camera-tracker)) ;; unused
+5 -5
View File
@@ -521,7 +521,7 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
fmt::format(file_util::get_file_path(
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
data.name_in_dgo, tex.name, tex.w, tex.h),
out.data(), tex.w, tex.h, false);
out.data(), tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name);
stats.successful_textures++;
@@ -569,7 +569,7 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
fmt::format(file_util::get_file_path(
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
data.name_in_dgo, tex.name, tex.w, tex.h),
out.data(), tex.w, tex.h, false);
out.data(), tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name);
stats.successful_textures++;
@@ -599,7 +599,7 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
fmt::format(file_util::get_file_path(
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
data.name_in_dgo, tex.name, tex.w, tex.h),
out.data(), tex.w, tex.h, false);
out.data(), tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name);
stats.successful_textures++;
@@ -645,7 +645,7 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
fmt::format(file_util::get_file_path(
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
data.name_in_dgo, tex.name, tex.w, tex.h),
out.data(), tex.w, tex.h, false);
out.data(), tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name);
stats.successful_textures++;
@@ -691,7 +691,7 @@ TPageResultStats process_tpage(ObjectFileData& data, TextureDB& texture_db) {
fmt::format(file_util::get_file_path(
{"assets", "textures", texture_page.name, "{}-{}-{}-{}.png"}),
data.name_in_dgo, tex.name, tex.w, tex.h),
out.data(), tex.w, tex.h, false);
out.data(), tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name);
stats.successful_textures++;
+72 -64
View File
@@ -8,6 +8,9 @@
namespace decompiler {
namespace {
static constexpr int GEOM_MAX = 3;
/*!
* Get the index of the first draw node in an array. Works for node or tfrag.
*/
@@ -536,7 +539,8 @@ struct TFragColorUnpack {
}
};
void emulate_dma_building_for_tfrag(const level_tools::TFragment& frag,
void emulate_dma_building_for_tfrag(int geom,
const level_tools::TFragment& frag,
std::vector<u8>& vu_mem,
TFragColorUnpack& color_indices,
TFragExtractStats* stats) {
@@ -546,13 +550,13 @@ void emulate_dma_building_for_tfrag(const level_tools::TFragment& frag,
state.cl = 4;
// do the "canned" unpacks
if (frag.num_level0_colors == 0) {
if (frag.num_level0_colors == 0 || geom == 2) {
// we're using base
ASSERT(frag.num_level1_colors == 0);
// ASSERT(frag.num_level1_colors == 0);
stats->num_base++;
emulate_chain(state, frag.dma_qwc[1] * 4, (const u32*)frag.dma_base.data(), vu_mem.data());
} else if (frag.num_level1_colors == 0) {
} else if (frag.num_level1_colors == 0 || geom == 1) {
stats->num_l0++;
emulate_chain(state, frag.dma_qwc[3] * 4, (const u32*)frag.dma_common_and_level0.data(),
vu_mem.data());
@@ -2070,7 +2074,8 @@ void make_tfrag3_data(std::map<u32, std::vector<GroupedDraw>>& draws,
}
}
void emulate_tfrags(const std::vector<level_tools::TFragment>& frags,
void emulate_tfrags(int geom,
const std::vector<level_tools::TFragment>& frags,
const std::string& debug_name,
const std::vector<level_tools::TextureRemap>& map,
tfrag3::Level& level_out,
@@ -2087,7 +2092,7 @@ void emulate_tfrags(const std::vector<level_tools::TFragment>& frags,
for (auto& frag : frags) {
TFragColorUnpack color_indices;
emulate_dma_building_for_tfrag(frag, vu_mem, color_indices, &stats);
emulate_dma_building_for_tfrag(geom, frag, vu_mem, color_indices, &stats);
VuMemWrapper mem(vu_mem);
auto draws = emulate_tfrag_execution<false>(frag, mem, color_indices, &stats);
all_draws.insert(all_draws.end(), draws.begin(), draws.end());
@@ -2137,75 +2142,78 @@ void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
const std::vector<std::pair<int, int>>& expected_missing_textures,
tfrag3::Level& out,
bool dump_level) {
tfrag3::TfragTree this_tree;
if (tree->my_type() == "drawable-tree-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::NORMAL;
} else if (tree->my_type() == "drawable-tree-dirt-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::DIRT;
} else if (tree->my_type() == "drawable-tree-ice-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::ICE;
} else if (tree->my_type() == "drawable-tree-lowres-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::LOWRES;
} else if (tree->my_type() == "drawable-tree-trans-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::TRANS;
} else {
fmt::print("unknown tfrag tree kind: {}\n", tree->my_type());
ASSERT(false);
}
// go through 4 lods(?)
for (int geom = 0; geom < GEOM_MAX; ++geom) {
tfrag3::TfragTree this_tree;
if (tree->my_type() == "drawable-tree-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::NORMAL;
} else if (tree->my_type() == "drawable-tree-dirt-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::DIRT;
} else if (tree->my_type() == "drawable-tree-ice-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::ICE;
} else if (tree->my_type() == "drawable-tree-lowres-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::LOWRES;
} else if (tree->my_type() == "drawable-tree-trans-tfrag") {
this_tree.kind = tfrag3::TFragmentTreeKind::TRANS;
} else {
fmt::print("unknown tfrag tree kind: {}\n", tree->my_type());
ASSERT(false);
}
ASSERT(tree->length == (int)tree->arrays.size());
ASSERT(tree->length > 0);
ASSERT(tree->length == (int)tree->arrays.size());
ASSERT(tree->length > 0);
auto last_array = tree->arrays.back().get();
auto last_array = tree->arrays.back().get();
auto as_tfrag_array = dynamic_cast<level_tools::DrawableInlineArrayTFrag*>(last_array);
ASSERT(as_tfrag_array);
ASSERT(as_tfrag_array->length == (int)as_tfrag_array->tfragments.size());
ASSERT(as_tfrag_array->length > 0);
u16 idx = as_tfrag_array->tfragments.front().id;
for (auto& elt : as_tfrag_array->tfragments) {
ASSERT(elt.id == idx);
idx++;
}
bool ok = verify_node_indices(tree);
ASSERT(ok);
fmt::print(" tree has {} arrays and {} tfragments\n", tree->length, as_tfrag_array->length);
auto as_tfrag_array = dynamic_cast<level_tools::DrawableInlineArrayTFrag*>(last_array);
ASSERT(as_tfrag_array);
ASSERT(as_tfrag_array->length == (int)as_tfrag_array->tfragments.size());
ASSERT(as_tfrag_array->length > 0);
u16 idx = as_tfrag_array->tfragments.front().id;
for (auto& elt : as_tfrag_array->tfragments) {
ASSERT(elt.id == idx);
idx++;
}
bool ok = verify_node_indices(tree);
ASSERT(ok);
fmt::print(" tree has {} arrays and {} tfragments\n", tree->length, as_tfrag_array->length);
auto vis_nodes = extract_vis_data(tree, as_tfrag_array->tfragments.front().id);
this_tree.bvh.first_leaf_node = vis_nodes.first_child_node;
this_tree.bvh.last_leaf_node = vis_nodes.last_child_node;
this_tree.bvh.num_roots = vis_nodes.num_roots;
this_tree.bvh.only_children = vis_nodes.only_children;
this_tree.bvh.first_root = vis_nodes.first_root;
this_tree.bvh.vis_nodes = std::move(vis_nodes.vis_nodes);
auto vis_nodes = extract_vis_data(tree, as_tfrag_array->tfragments.front().id);
this_tree.bvh.first_leaf_node = vis_nodes.first_child_node;
this_tree.bvh.last_leaf_node = vis_nodes.last_child_node;
this_tree.bvh.num_roots = vis_nodes.num_roots;
this_tree.bvh.only_children = vis_nodes.only_children;
this_tree.bvh.first_root = vis_nodes.first_root;
this_tree.bvh.vis_nodes = std::move(vis_nodes.vis_nodes);
std::unordered_map<int, int> tfrag_parents;
// for (auto& node : this_tree.vis_nodes) {
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
const auto& node = this_tree.bvh.vis_nodes[node_idx];
if (node.flags == 0) {
for (int i = 0; i < node.num_kids; i++) {
tfrag_parents[node.child_id + i] = node_idx;
std::unordered_map<int, int> tfrag_parents;
// for (auto& node : this_tree.vis_nodes) {
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
const auto& node = this_tree.bvh.vis_nodes[node_idx];
if (node.flags == 0) {
for (int i = 0; i < node.num_kids; i++) {
tfrag_parents[node.child_id + i] = node_idx;
}
}
}
}
// ASSERT(result.vis_nodes.last_child_node + 1 == idx);
// ASSERT(result.vis_nodes.last_child_node + 1 == idx);
emulate_tfrags(as_tfrag_array->tfragments, debug_name, map, out, this_tree, tex_db,
expected_missing_textures, dump_level);
extract_time_of_day(tree, this_tree);
emulate_tfrags(geom, as_tfrag_array->tfragments, debug_name, map, out, this_tree, tex_db,
expected_missing_textures, dump_level);
extract_time_of_day(tree, this_tree);
for (auto& draw : this_tree.draws) {
for (auto& str : draw.vis_groups) {
auto it = tfrag_parents.find(str.vis_idx_in_pc_bvh);
if (it == tfrag_parents.end()) {
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx_in_pc_bvh = it->second;
for (auto& draw : this_tree.draws) {
for (auto& str : draw.vis_groups) {
auto it = tfrag_parents.find(str.vis_idx_in_pc_bvh);
if (it == tfrag_parents.end()) {
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
}
merge_groups(draw.vis_groups);
out.tfrag_trees[geom].push_back(this_tree);
}
out.tfrag_trees.push_back(this_tree);
}
} // namespace decompiler
+109 -100
View File
@@ -161,6 +161,8 @@ void extract_vis_data(const level_tools::DrawableTreeInstanceTie* tree,
}
}
constexpr int GEOM_MAX = 4; // the amount of geoms
// Each TIE prototype is broken up into "fragments". These "fragments" have some maximum size based
// on the VU memory limit, so an instance may have multiple fragments, depending on how many
// vertices are in the model.
@@ -362,7 +364,7 @@ std::array<math::Vector4f, 4> extract_tie_matrix(const u16* data) {
}
// geometry we use (todo, should really look at this)
constexpr int GEOM_IDX = 1; // todo 0 or 1??
constexpr int GEOM_IDX = 0; // todo 0 or 1??
/*!
* Confirm that the initial value of all wind vectors is 0.
@@ -388,7 +390,8 @@ void check_wind_vectors_zero(const std::vector<TieProtoInfo>& protos, Ref wind_r
// get per-instance info from the level data
std::vector<TieProtoInfo> collect_instance_info(
const level_tools::DrawableInlineArrayInstanceTie* instances,
const std::vector<level_tools::PrototypeBucketTie>* protos) {
const std::vector<level_tools::PrototypeBucketTie>* protos,
int geo) {
std::vector<TieProtoInfo> result;
// loop over instances in level
@@ -418,13 +421,13 @@ std::vector<TieProtoInfo> collect_instance_info(
// and this is only the indices.... there's yet another lookup on the VU
auto& proto = protos->at(info.prototype_idx);
u32 offset_bytes = proto.base_qw[GEOM_IDX] * 16;
u32 offset_bytes = proto.base_qw[geo] * 16;
// loop over frags. this is only the per-instance info so only colors indices. We know the
// location/layout of the color data from the EE asm code.
for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) {
for (int frag_idx = 0; frag_idx < proto.frag_count[geo]; frag_idx++) {
TieInstanceFragInfo frag_info;
// read the number of quadwords
u32 num_color_qwc = proto.color_index_qwc.at(proto.index_start[GEOM_IDX] + frag_idx);
u32 num_color_qwc = proto.color_index_qwc.at(proto.index_start[geo] + frag_idx);
// loop over 4-byte words
for (u32 i = 0; i < num_color_qwc * 4; i++) {
// loop over bytes in word
@@ -472,7 +475,8 @@ u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& ma
void update_proto_info(std::vector<TieProtoInfo>* out,
const std::vector<level_tools::TextureRemap>& map,
const TextureDB& tdb,
const std::vector<level_tools::PrototypeBucketTie>& protos) {
const std::vector<level_tools::PrototypeBucketTie>& protos,
int geo) {
out->resize(std::max(out->size(), protos.size()));
for (size_t i = 0; i < protos.size(); i++) {
const auto& proto = protos[i];
@@ -497,12 +501,12 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
}
// loop over fragments in the proto. This is the actual mesh data data and drawing settings
for (int frag_idx = 0; frag_idx < proto.frag_count[GEOM_IDX]; frag_idx++) {
for (int frag_idx = 0; frag_idx < proto.frag_count[geo]; frag_idx++) {
TieFrag frag_info;
// loop over adgif shaders
for (int tex_idx = 0;
tex_idx < proto.geometry[GEOM_IDX].tie_fragments.at(frag_idx).tex_count / 5; tex_idx++) {
for (int tex_idx = 0; tex_idx < proto.geometry[geo].tie_fragments.at(frag_idx).tex_count / 5;
tex_idx++) {
// this adgif shader data is modified in the real game by the login methods.
// all TIE things have pretty normal adgif shaders
@@ -510,7 +514,7 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
AdgifInfo adgif;
// pointer to the level data
auto& gif_data = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].gif_data;
auto& gif_data = proto.geometry[geo].tie_fragments[frag_idx].gif_data;
// address for the first adgif shader qw.
u8 ra_tex0 = gif_data.at(16 * (tex_idx * 5 + 0) + 8);
@@ -582,20 +586,20 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
}
// they store a vertex count. we later use this to sanity check out mesh extraction
frag_info.expected_dverts = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].num_dverts;
frag_info.expected_dverts = proto.geometry[geo].tie_fragments[frag_idx].num_dverts;
// each frag also has "other" data. This is some index data that the VU program uses.
// it comes in gif_data, after tex_qwc (determined from EE program)
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;
int tex_qwc = proto.geometry[geo].tie_fragments.at(frag_idx).tex_count;
int other_qwc = proto.geometry[geo].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),
proto.geometry[geo].tie_fragments[frag_idx].gif_data.data() + (16 * tex_qwc),
16 * other_qwc);
// each frag's "point" data. These are stored as int16's, but get unpacked to 32-bit ints by
// the VIF. (determined from EE program)
const auto& pr = proto.geometry[GEOM_IDX].tie_fragments[frag_idx].point_ref;
const auto& pr = proto.geometry[geo].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);
@@ -2263,98 +2267,103 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
const TextureDB& tex_db,
tfrag3::Level& out,
bool dump_level) {
tfrag3::TieTree this_tree;
for (int geo = 0; geo < GEOM_MAX; ++geo) {
tfrag3::TieTree this_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.back().get();
auto as_instance_array = dynamic_cast<level_tools::DrawableInlineArrayInstanceTie*>(last_array);
ASSERT(as_instance_array);
ASSERT(as_instance_array->length == (int)as_instance_array->instances.size());
ASSERT(as_instance_array->length > 0);
u16 idx = as_instance_array->instances.front().id;
for (auto& elt : as_instance_array->instances) {
ASSERT(elt.id == idx);
idx++;
}
bool ok = verify_node_indices(tree);
ASSERT(ok);
fmt::print(" tree has {} arrays and {} instances\n", tree->length, as_instance_array->length);
// extract the vis tree. Note that this extracts the tree only down to the last draw node, a
// parent of between 1 and 8 instances.
extract_vis_data(tree, as_instance_array->instances.front().id, this_tree);
// we use the index of the instance in the instance list as its index. But this is different
// from its visibility index. This map goes from instance index to the parent node in the vis
// tree. later, we can use this to remap from instance idx to the visiblity node index.
std::unordered_map<int, int> instance_parents;
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
const auto& node = this_tree.bvh.vis_nodes[node_idx];
if (node.flags == 0) {
for (int i = 0; i < node.num_kids; i++) {
instance_parents[node.child_id + i] = node_idx;
}
// 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.back().get();
auto as_instance_array = dynamic_cast<level_tools::DrawableInlineArrayInstanceTie*>(last_array);
ASSERT(as_instance_array);
ASSERT(as_instance_array->length == (int)as_instance_array->instances.size());
ASSERT(as_instance_array->length > 0);
u16 idx = as_instance_array->instances.front().id;
for (auto& elt : as_instance_array->instances) {
ASSERT(elt.id == idx);
idx++;
}
}
bool ok = verify_node_indices(tree);
ASSERT(ok);
fmt::print(" tree has {} arrays and {} instances\n", tree->length,
as_instance_array->length);
// convert level format data to a nicer format
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);
check_wind_vectors_zero(info, tree->prototypes.wind_vectors);
// determine draws from VU program
emulate_tie_prototype_program(info);
emulate_tie_instance_program(info);
emulate_kicks(info);
// extract the vis tree. Note that this extracts the tree only down to the last draw node, a
// parent of between 1 and 8 instances.
extract_vis_data(tree, as_instance_array->instances.front().id, this_tree);
// debug save to .obj
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);
}
auto full = dump_full_to_obj(info);
file_util::write_text_file(fmt::format("{}/ALL.obj", dir), full);
}
// create time of day data.
auto full_palette = make_big_palette(info);
// create draws
add_vertices_and_static_draw(this_tree, out, tex_db, info);
// remap vis indices and merge
for (auto& draw : this_tree.static_draws) {
for (auto& str : draw.vis_groups) {
auto it = instance_parents.find(str.vis_idx_in_pc_bvh);
if (it == instance_parents.end()) {
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
}
for (auto& draw : this_tree.instanced_wind_draws) {
for (auto& str : draw.instance_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;
// we use the index of the instance in the instance list as its index. But this is different
// from its visibility index. This map goes from instance index to the parent node in the vis
// tree. later, we can use this to remap from instance idx to the visiblity node index.
std::unordered_map<int, int> instance_parents;
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
const auto& node = this_tree.bvh.vis_nodes[node_idx];
if (node.flags == 0) {
for (int i = 0; i < node.num_kids; i++) {
instance_parents[node.child_id + i] = node_idx;
}
}
}
merge_groups(draw.instance_groups);
}
// convert level format data to a nicer format
auto info =
collect_instance_info(as_instance_array, &tree->prototypes.prototype_array_tie.data, geo);
update_proto_info(&info, tex_map, tex_db, tree->prototypes.prototype_array_tie.data, geo);
check_wind_vectors_zero(info, tree->prototypes.wind_vectors);
// determine draws from VU program
emulate_tie_prototype_program(info);
emulate_tie_instance_program(info);
emulate_kicks(info);
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));
// debug save to .obj
if (dump_level) {
auto dir =
file_util::get_file_path({fmt::format("debug_out/lod{}-tie-{}/", geo, 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);
}
auto full = dump_full_to_obj(info);
file_util::write_text_file(fmt::format("{}/ALL.obj", dir), full);
}
// create time of day data.
auto full_palette = make_big_palette(info);
// create draws
add_vertices_and_static_draw(this_tree, out, tex_db, info);
// remap vis indices and merge
for (auto& draw : this_tree.static_draws) {
for (auto& str : draw.vis_groups) {
auto it = instance_parents.find(str.vis_idx_in_pc_bvh);
if (it == instance_parents.end()) {
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
}
for (auto& draw : this_tree.instanced_wind_draws) {
for (auto& str : draw.instance_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;
}
}
merge_groups(draw.instance_groups);
}
this_tree.colors = full_palette.colors;
fmt::print("TIE tree {} has {} draws\n", geo, this_tree.static_draws.size());
out.tie_trees[geo].push_back(std::move(this_tree));
}
}
} // namespace decompiler
+14
View File
@@ -256,4 +256,18 @@ int PadAnalogValue(Pad::Analog analog, int port) {
return Pad::AnalogValue(g_settings.pad_mapping_info, analog, port);
}
void SetLod(RendererTreeType tree, int lod) {
switch (tree) {
case RendererTreeType::TFRAG3:
g_global_settings.lod_tfrag = lod;
break;
case RendererTreeType::TIE3:
g_global_settings.lod_tie = lod;
break;
default:
lg::error("Invalid tree {} specified for SetLod ({})", tree, lod);
break;
}
}
} // namespace Gfx
+8
View File
@@ -71,6 +71,10 @@ struct GfxGlobalSettings {
// current renderer
const GfxRendererModule* renderer;
// lod settings, used by bucket renderers
int lod_tfrag = 0;
int lod_tie = 0;
};
namespace Gfx {
@@ -105,4 +109,8 @@ s64 get_mapped_button(s64 pad, s64 button);
int PadIsPressed(Pad::Button button, int port);
int PadAnalogValue(Pad::Analog analog, int port);
// matching enum in kernel-defs.gc !!
enum class RendererTreeType { NONE = 0, TFRAG3 = 1, TIE3 = 2, INVALID };
void SetLod(RendererTreeType tree, int lod);
} // namespace Gfx
@@ -51,9 +51,9 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool)
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(opengl_error_callback, nullptr);
// disable specific errors
// const GLuint gl_error_ignores_api_perf[1] = {};
// glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, 0,
// &gl_error_ignores_api_perf[0], GL_FALSE);
const GLuint gl_error_ignores_api_other[1] = {0x20071};
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, 1,
&gl_error_ignores_api_other[0], GL_FALSE);
lg::debug("OpenGL context information: {}", (const char*)glGetString(GL_VERSION));
@@ -256,8 +256,7 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) {
if (settings.save_screenshot) {
finish_screenshot(settings.screenshot_path, settings.window_width_px, settings.window_height_px,
settings.lbox_width_px, settings.lbox_height_px,
settings.screenshot_should_compress);
settings.lbox_width_px, settings.lbox_height_px);
}
m_render_state.loader.update();
@@ -431,8 +430,7 @@ void OpenGLRenderer::finish_screenshot(const std::string& output_name,
int width,
int height,
int x,
int y,
bool compress) {
int y) {
std::vector<u32> buffer(width * height);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer(GL_BACK);
@@ -448,5 +446,5 @@ void OpenGLRenderer::finish_screenshot(const std::string& output_name,
for (auto& px : buffer) {
px |= 0xff000000;
}
file_util::write_rgba_png(output_name, buffer.data(), width, height, compress);
file_util::write_rgba_png(output_name, buffer.data(), width, height);
}
@@ -18,7 +18,6 @@ struct RenderOptions {
bool playing_from_dump = false;
bool save_screenshot = false;
bool screenshot_should_compress = false;
std::string screenshot_path;
};
@@ -35,12 +34,7 @@ class OpenGLRenderer {
void init_bucket_renderers();
void draw_renderer_selection_window();
void finish_screenshot(const std::string& output_name,
int px,
int py,
int x,
int y,
bool compress);
void finish_screenshot(const std::string& output_name, int px, int py, int x, int y);
template <typename T, class... Args>
void init_bucket_renderer(const std::string& name, BucketId id, Args&&... args) {
+1 -2
View File
@@ -96,9 +96,8 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
}
if (ImGui::BeginMenu("Gfx Dump")) {
ImGui::Checkbox("Compress Screenshot", &m_compress_screenshot);
ImGui::MenuItem("Screenshot Next Frame!", nullptr, &m_want_screenshot);
ImGui::InputText("File", m_screenshot_save_name, 30);
ImGui::InputText("File", m_screenshot_save_name, 50);
ImGui::Separator();
ImGui::MenuItem("Dump Next Frame!", nullptr, &m_want_save);
bool old_replay = m_want_replay;
@@ -61,8 +61,6 @@ class OpenGlDebugGui {
return false;
}
bool screenshot_compress_flag() { return m_compress_screenshot; }
bool get_vsync_flag() { return m_vsync; }
bool framelimiter = false;
@@ -78,7 +76,6 @@ class OpenGlDebugGui {
bool m_want_save = false;
bool m_want_replay = false;
bool m_want_dump_load = false;
bool m_compress_screenshot = false;
bool m_want_screenshot = false;
char m_dump_save_name[256] = "dump.bin";
char m_screenshot_save_name[256] = "screenshot.png";
@@ -145,7 +145,7 @@ void TFragment::render(DmaFollower& dma,
}
auto t3prof = prof.make_scoped_child("t3");
m_tfrag3.render_matching_trees(m_tree_kinds, settings, render_state, t3prof);
m_tfrag3.render_matching_trees(m_tfrag3.lod(), m_tree_kinds, settings, render_state, t3prof);
}
m_debug_string += fmt::format("fail: {}\n", dma.current_tag().print());
@@ -186,8 +186,9 @@ void TFragment::render(DmaFollower& dma,
auto t3prof = prof.make_scoped_child(level_names[i]);
m_many_level_render.tfrag_level_renderers[i]->debug_render_all_trees_nolores(
settings, render_state, t3prof);
m_many_level_render.tie_level_renderers[i]->render_all_trees(settings, render_state,
0, settings, render_state, t3prof);
// always renders max lod
m_many_level_render.tie_level_renderers[i]->render_all_trees(0, settings, render_state,
t3prof);
}
}
+132 -120
View File
@@ -46,7 +46,9 @@ bool Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kind
m_load_state.state = State::FREE_OLD_TREES;
break;
case State::FREE_OLD_TREES:
m_cached_trees.clear();
for (int geom = 0; geom < GEOM_MAX; ++geom) {
m_cached_trees[geom].clear();
}
m_load_state.state = State::INIT_NEW_TREES;
break;
case State::INIT_NEW_TREES:
@@ -56,75 +58,77 @@ bool Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kind
size_t vis_temp_len = 0;
size_t max_draw = 0;
for (size_t tree_idx = 0; tree_idx < lev_data->tfrag_trees.size(); tree_idx++) {
size_t idx_buffer_len = 0;
for (int geom = 0; geom < GEOM_MAX; ++geom) {
for (size_t tree_idx = 0; tree_idx < lev_data->tfrag_trees[geom].size(); tree_idx++) {
size_t idx_buffer_len = 0;
const auto& tree = lev_data->tfrag_trees[tree_idx];
m_cached_trees.emplace_back();
auto& tree_cache = m_cached_trees.back();
const auto& tree = lev_data->tfrag_trees[geom][tree_idx];
tree_cache.kind = tree.kind;
if (std::find(tree_kinds.begin(), tree_kinds.end(), tree.kind) != tree_kinds.end()) {
max_draw = std::max(tree.draws.size(), max_draw);
for (auto& draw : tree.draws) {
idx_buffer_len += draw.vertex_index_stream.size();
auto& tree_cache = m_cached_trees[geom].emplace_back();
tree_cache.kind = tree.kind;
if (std::find(tree_kinds.begin(), tree_kinds.end(), tree.kind) != tree_kinds.end()) {
max_draw = std::max(tree.draws.size(), max_draw);
for (auto& draw : tree.draws) {
idx_buffer_len += draw.vertex_index_stream.size();
}
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
u32 verts = tree.vertices.size();
glGenVertexArrays(1, &tree_cache.vao);
glBindVertexArray(tree_cache.vao);
glGenBuffers(1, &tree_cache.vertex_buffer);
tree_cache.vert_count = verts;
tree_cache.draws = &tree.draws; // todo - should we just copy this?
tree_cache.colors = &tree.colors;
tree_cache.vis = &tree.bvh;
tree_cache.tod_cache = swizzle_time_of_day(tree.colors);
vis_temp_len = std::max(vis_temp_len, tree.bvh.vis_nodes.size());
glBindBuffer(GL_ARRAY_BUFFER, tree_cache.vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex), nullptr,
GL_STREAM_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
);
glVertexAttribIPointer(
2, // location 2 in the shader
1, // 1 values per vert
GL_UNSIGNED_SHORT, // u16
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
);
glGenBuffers(1, &tree_cache.index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tree_cache.index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr,
GL_STREAM_DRAW);
tree_cache.index_list.resize(idx_buffer_len);
glGenTextures(1, &tree_cache.time_of_day_texture);
glBindTexture(GL_TEXTURE_1D, tree_cache.time_of_day_texture);
// just fill with zeros. this lets use use the faster texsubimage later
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8, m_color_result.data());
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindVertexArray(0);
}
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
u32 verts = tree.vertices.size();
glGenVertexArrays(1, &tree_cache.vao);
glBindVertexArray(tree_cache.vao);
glGenBuffers(1, &tree_cache.vertex_buffer);
tree_cache.vert_count = verts;
tree_cache.draws = &tree.draws; // todo - should we just copy this?
tree_cache.colors = &tree.colors;
tree_cache.vis = &tree.bvh;
tree_cache.tod_cache = swizzle_time_of_day(tree.colors);
vis_temp_len = std::max(vis_temp_len, tree.bvh.vis_nodes.size());
glBindBuffer(GL_ARRAY_BUFFER, tree_cache.vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex), nullptr,
GL_STREAM_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
);
glVertexAttribIPointer(
2, // location 2 in the shader
1, // 1 values per vert
GL_UNSIGNED_SHORT, // u16
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
);
glGenBuffers(1, &tree_cache.index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tree_cache.index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr,
GL_STREAM_DRAW);
tree_cache.index_list.resize(idx_buffer_len);
glGenTextures(1, &tree_cache.time_of_day_texture);
glBindTexture(GL_TEXTURE_1D, tree_cache.time_of_day_texture);
// just fill with zeros. this lets use use the faster texsubimage later
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8, m_color_result.data());
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindVertexArray(0);
}
}
@@ -139,21 +143,23 @@ bool Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kind
case State::UPLOAD_VERTS: {
constexpr u32 MAX_VERTS = 40000;
bool remaining = false;
for (size_t tree_idx = 0; tree_idx < lev_data->tfrag_trees.size(); tree_idx++) {
const auto& tree = lev_data->tfrag_trees[tree_idx];
for (int geom = 0; geom < GEOM_MAX; ++geom) {
for (size_t tree_idx = 0; tree_idx < lev_data->tfrag_trees[geom].size(); tree_idx++) {
const auto& tree = lev_data->tfrag_trees[geom][tree_idx];
if (std::find(tree_kinds.begin(), tree_kinds.end(), tree.kind) != tree_kinds.end()) {
u32 verts = tree.vertices.size();
u32 start_vert = (m_load_state.vert) * MAX_VERTS;
u32 end_vert = std::min(verts, (m_load_state.vert + 1) * MAX_VERTS);
if (end_vert > start_vert) {
glBindVertexArray(m_cached_trees[tree_idx].vao);
glBindBuffer(GL_ARRAY_BUFFER, m_cached_trees[tree_idx].vertex_buffer);
glBufferSubData(GL_ARRAY_BUFFER, start_vert * sizeof(tfrag3::PreloadedVertex),
(end_vert - start_vert) * sizeof(tfrag3::PreloadedVertex),
tree.vertices.data() + start_vert);
if (end_vert < verts) {
remaining = true;
if (std::find(tree_kinds.begin(), tree_kinds.end(), tree.kind) != tree_kinds.end()) {
u32 verts = tree.vertices.size();
u32 start_vert = (m_load_state.vert) * MAX_VERTS;
u32 end_vert = std::min(verts, (m_load_state.vert + 1) * MAX_VERTS);
if (end_vert > start_vert) {
glBindVertexArray(m_cached_trees[geom][tree_idx].vao);
glBindBuffer(GL_ARRAY_BUFFER, m_cached_trees[geom][tree_idx].vertex_buffer);
glBufferSubData(GL_ARRAY_BUFFER, start_vert * sizeof(tfrag3::PreloadedVertex),
(end_vert - start_vert) * sizeof(tfrag3::PreloadedVertex),
tree.vertices.data() + start_vert);
if (end_vert < verts) {
remaining = true;
}
}
}
}
@@ -211,13 +217,14 @@ bool Tfrag3::setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_
return m_has_level;
}
void Tfrag3::render_tree(const TfragRenderSettings& settings,
void Tfrag3::render_tree(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
if (!m_has_level) {
return;
}
auto& tree = m_cached_trees.at(settings.tree_idx);
auto& tree = m_cached_trees.at(geom).at(settings.tree_idx);
ASSERT(tree.kind != tfrag3::TFragmentTreeKind::INVALID);
if (m_color_result.size() < tree.colors->size()) {
@@ -296,71 +303,74 @@ void Tfrag3::render_tree(const TfragRenderSettings& settings,
* This is intended to be used only for debugging when we can't easily get commands for all trees
* working.
*/
void Tfrag3::render_all_trees(const TfragRenderSettings& settings,
void Tfrag3::render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
TfragRenderSettings settings_copy = settings;
for (size_t i = 0; i < m_cached_trees.size(); i++) {
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID) {
for (size_t i = 0; i < m_cached_trees[geom].size(); i++) {
if (m_cached_trees[geom][i].kind != tfrag3::TFragmentTreeKind::INVALID) {
settings_copy.tree_idx = i;
render_tree(settings_copy, render_state, prof);
render_tree(geom, settings_copy, render_state, prof);
}
}
}
void Tfrag3::render_matching_trees(const std::vector<tfrag3::TFragmentTreeKind>& trees,
void Tfrag3::render_matching_trees(int geom,
const std::vector<tfrag3::TFragmentTreeKind>& trees,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
TfragRenderSettings settings_copy = settings;
for (size_t i = 0; i < m_cached_trees.size(); i++) {
m_cached_trees[i].reset_stats();
if (!m_cached_trees[i].allowed) {
for (size_t i = 0; i < m_cached_trees[geom].size(); i++) {
auto& tree = m_cached_trees[geom][i];
tree.reset_stats();
if (!tree.allowed) {
continue;
}
if (std::find(trees.begin(), trees.end(), m_cached_trees[i].kind) != trees.end() ||
m_cached_trees[i].forced) {
m_cached_trees[i].rendered_this_frame = true;
if (std::find(trees.begin(), trees.end(), tree.kind) != trees.end() || tree.forced) {
tree.rendered_this_frame = true;
settings_copy.tree_idx = i;
render_tree(settings_copy, render_state, prof);
if (m_cached_trees[i].cull_debug) {
render_tree(geom, settings_copy, render_state, prof);
if (tree.cull_debug) {
render_tree_cull_debug(settings_copy, render_state, prof);
}
}
}
}
void Tfrag3::debug_render_all_trees_nolores(const TfragRenderSettings& settings,
void Tfrag3::debug_render_all_trees_nolores(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
TfragRenderSettings settings_copy = settings;
for (size_t i = 0; i < m_cached_trees.size(); i++) {
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID &&
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
if (m_cached_trees[geom][i].kind != tfrag3::TFragmentTreeKind::INVALID &&
m_cached_trees[geom][i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
m_cached_trees[geom][i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
settings_copy.tree_idx = i;
render_tree(settings_copy, render_state, prof);
render_tree(geom, settings_copy, render_state, prof);
}
}
for (size_t i = 0; i < m_cached_trees.size(); i++) {
if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID &&
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
settings_copy.tree_idx = i;
// render_tree_cull_debug(settings_copy, render_state, prof);
}
}
// for (size_t i = 0; i < m_cached_trees.size(); i++) {
// if (m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::INVALID &&
// m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES_TRANS &&
// m_cached_trees[i].kind != tfrag3::TFragmentTreeKind::LOWRES) {
// settings_copy.tree_idx = i;
// render_tree_cull_debug(settings_copy, render_state, prof);
// }
// }
}
void Tfrag3::draw_debug_window() {
for (int i = 0; i < (int)m_cached_trees.size(); i++) {
auto& tree = m_cached_trees[i];
auto& tree = m_cached_trees[lod()][i];
if (tree.kind == tfrag3::TFragmentTreeKind::INVALID) {
continue;
}
ImGui::PushID(i);
ImGui::Text("[%d] %10s", i, tfrag3::tfrag_tree_names[(int)m_cached_trees[i].kind]);
ImGui::Text("[%d] %10s", i, tfrag3::tfrag_tree_names[(int)m_cached_trees[lod()][i].kind]);
ImGui::SameLine();
ImGui::Checkbox("Allow?", &tree.allowed);
ImGui::SameLine();
@@ -376,16 +386,18 @@ void Tfrag3::draw_debug_window() {
void Tfrag3::discard_tree_cache() {
m_textures = nullptr;
for (auto& tree : m_cached_trees) {
if (tree.kind != tfrag3::TFragmentTreeKind::INVALID) {
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glDeleteTextures(1, &tree.time_of_day_texture);
glDeleteBuffers(1, &tree.vertex_buffer);
glDeleteBuffers(1, &tree.index_buffer);
glDeleteVertexArrays(1, &tree.vao);
for (int geom = 0; geom < GEOM_MAX; ++geom) {
for (auto& tree : m_cached_trees[geom]) {
if (tree.kind != tfrag3::TFragmentTreeKind::INVALID) {
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glDeleteTextures(1, &tree.time_of_day_texture);
glDeleteBuffers(1, &tree.vertex_buffer);
glDeleteBuffers(1, &tree.index_buffer);
glDeleteVertexArrays(1, &tree.vao);
}
}
m_cached_trees[geom].clear();
}
m_cached_trees.clear();
}
namespace {
@@ -472,7 +484,7 @@ void Tfrag3::render_tree_cull_debug(const TfragRenderSettings& settings,
ScopedProfilerNode& prof) {
// generate debug verts:
m_debug_vert_data.clear();
auto& tree = m_cached_trees.at(settings.tree_idx);
auto& tree = m_cached_trees.at(settings.tree_idx).at(lod());
debug_vis_draw(tree.vis->first_root, tree.vis->first_root, tree.vis->num_roots, 1,
tree.vis->vis_nodes, m_debug_vert_data);
+15 -6
View File
@@ -2,6 +2,7 @@
#include "common/custom_data/Tfrag3Data.h"
#include "common/math/Vector.h"
#include "game/graphics/gfx.h"
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/pipelines/opengl.h"
#include "game/graphics/opengl_renderer/tfrag/tfrag_common.h"
@@ -12,20 +13,24 @@ class Tfrag3 {
Tfrag3();
~Tfrag3();
void debug_render_all_trees_nolores(const TfragRenderSettings& settings,
void debug_render_all_trees_nolores(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
void render_all_trees(const TfragRenderSettings& settings,
void render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
void render_matching_trees(const std::vector<tfrag3::TFragmentTreeKind>& trees,
void render_matching_trees(int geom,
const std::vector<tfrag3::TFragmentTreeKind>& trees,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
void render_tree(const TfragRenderSettings& settings,
void render_tree(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
@@ -47,7 +52,11 @@ class Tfrag3 {
bool update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
const tfrag3::Level* lev_data);
int lod() const { return Gfx::g_global_settings.lod_tfrag; }
private:
static constexpr int GEOM_MAX = 3;
struct TreeCache {
tfrag3::TFragmentTreeKind kind;
GLuint vertex_buffer = -1;
@@ -82,7 +91,7 @@ class Tfrag3 {
std::string m_level_name;
const std::vector<GLuint>* m_textures = nullptr;
std::vector<TreeCache> m_cached_trees;
std::array<std::vector<TreeCache>, GEOM_MAX> m_cached_trees;
std::vector<math::Vector<u8, 4>> m_color_result;
@@ -92,7 +101,7 @@ class Tfrag3 {
u64 m_load_id = -1;
// in theory could be up to 4096, I think, but we don't see that many...
// should be easy to increase (will require a shader change too for indexing)
// should be easy to increase
static constexpr int TIME_OF_DAY_COLOR_COUNT = 8192;
static constexpr int DEBUG_TRI_COUNT = 4096;
+143 -130
View File
@@ -20,7 +20,9 @@ bool Tie3::update_load(const tfrag3::Level* lev_data) {
m_wind_vectors.clear();
// We changed level!
discard_tree_cache();
m_trees.resize(lev_data->tie_trees.size());
for (int geo = 0; geo < 4; ++geo) {
m_trees[geo].resize(lev_data->tie_trees[geo].size());
}
m_load_state.state = INIT_NEW_TREES;
break;
case INIT_NEW_TREES: {
@@ -30,106 +32,110 @@ bool Tie3::update_load(const tfrag3::Level* lev_data) {
size_t max_idx_per_draw = 0;
u16 max_wind_idx = 0;
// set up each tree
for (size_t tree_idx = 0; tree_idx < lev_data->tie_trees.size(); tree_idx++) {
size_t idx_buffer_len = 0;
size_t wind_idx_buffer_len = 0;
const auto& tree = lev_data->tie_trees[tree_idx];
max_draw = std::max(tree.static_draws.size(), max_draw);
for (auto& draw : tree.static_draws) {
idx_buffer_len += draw.vertex_index_stream.size();
max_idx_per_draw = std::max(max_idx_per_draw, draw.vertex_index_stream.size());
}
for (auto& draw : tree.instanced_wind_draws) {
wind_idx_buffer_len += draw.vertex_index_stream.size();
max_idx_per_draw = std::max(max_idx_per_draw, draw.vertex_index_stream.size());
}
for (auto& inst : tree.instance_info) {
max_wind_idx = std::max(max_wind_idx, inst.wind_idx);
}
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
u32 verts = tree.vertices.size();
fmt::print(" tree {} has {} verts ({} kB) and {} draws\n", tree_idx, verts,
verts * sizeof(tfrag3::PreloadedVertex) / 1024.f, tree.static_draws.size());
glGenVertexArrays(1, &m_trees[tree_idx].vao);
glBindVertexArray(m_trees[tree_idx].vao);
glGenBuffers(1, &m_trees[tree_idx].vertex_buffer);
m_trees[tree_idx].vert_count = verts;
m_trees[tree_idx].draws = &tree.static_draws; // todo - should we just copy this?
m_trees[tree_idx].colors = &tree.colors;
m_trees[tree_idx].vis = &tree.bvh;
m_trees[tree_idx].instance_info = &tree.instance_info;
m_trees[tree_idx].wind_draws = &tree.instanced_wind_draws;
vis_temp_len = std::max(vis_temp_len, tree.bvh.vis_nodes.size());
m_trees[tree_idx].tod_cache = swizzle_time_of_day(tree.colors);
glBindBuffer(GL_ARRAY_BUFFER, m_trees[tree_idx].vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex), nullptr,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
// glBufferSubData(GL_ARRAY_BUFFER, 0, verts * sizeof(tfrag3::PreloadedVertex),
// tree.vertices.data());
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
);
glVertexAttribIPointer(2, // location 2 in the shader
1, // 1 values per vert
GL_UNSIGNED_SHORT, // u16
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
);
glGenBuffers(1, &m_trees[tree_idx].index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_trees[tree_idx].index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr,
GL_STREAM_DRAW);
m_trees[tree_idx].index_list.resize(idx_buffer_len);
if (wind_idx_buffer_len > 0) {
m_trees[tree_idx].wind_matrix_cache.resize(tree.instance_info.size());
m_trees[tree_idx].has_wind = true;
glGenBuffers(1, &m_trees[tree_idx].wind_vertex_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_trees[tree_idx].wind_vertex_index_buffer);
std::vector<u32> temp;
temp.resize(wind_idx_buffer_len);
u32 off = 0;
// set up each tree for each lod
for (int geo = 0; geo < 4; ++geo) {
for (size_t tree_idx = 0; tree_idx < lev_data->tie_trees[geo].size(); tree_idx++) {
size_t idx_buffer_len = 0;
size_t wind_idx_buffer_len = 0;
const auto& tree = lev_data->tie_trees[geo][tree_idx];
max_draw = std::max(tree.static_draws.size(), max_draw);
for (auto& draw : tree.static_draws) {
idx_buffer_len += draw.vertex_index_stream.size();
max_idx_per_draw = std::max(max_idx_per_draw, draw.vertex_index_stream.size());
}
for (auto& draw : tree.instanced_wind_draws) {
m_trees[tree_idx].wind_vertex_index_offsets.push_back(off);
memcpy(temp.data() + off, draw.vertex_index_stream.data(),
draw.vertex_index_stream.size() * sizeof(u32));
off += draw.vertex_index_stream.size();
wind_idx_buffer_len += draw.vertex_index_stream.size();
max_idx_per_draw = std::max(max_idx_per_draw, draw.vertex_index_stream.size());
}
for (auto& inst : tree.instance_info) {
max_wind_idx = std::max(max_wind_idx, inst.wind_idx);
}
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
u32 verts = tree.vertices.size();
fmt::print(" tree {} has {} verts ({} kB) and {} draws\n", tree_idx, verts,
verts * sizeof(tfrag3::PreloadedVertex) / 1024.f, tree.static_draws.size());
auto& lod_tree = m_trees.at(geo);
glGenVertexArrays(1, &lod_tree[tree_idx].vao);
glBindVertexArray(lod_tree[tree_idx].vao);
glGenBuffers(1, &lod_tree[tree_idx].vertex_buffer);
lod_tree[tree_idx].vert_count = verts;
lod_tree[tree_idx].draws = &tree.static_draws; // todo - should we just copy this?
lod_tree[tree_idx].colors = &tree.colors;
lod_tree[tree_idx].vis = &tree.bvh;
lod_tree[tree_idx].instance_info = &tree.instance_info;
lod_tree[tree_idx].wind_draws = &tree.instanced_wind_draws;
vis_temp_len = std::max(vis_temp_len, tree.bvh.vis_nodes.size());
lod_tree[tree_idx].tod_cache = swizzle_time_of_day(tree.colors);
glBindBuffer(GL_ARRAY_BUFFER, lod_tree[tree_idx].vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex), nullptr,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
// glBufferSubData(GL_ARRAY_BUFFER, 0, verts * sizeof(tfrag3::PreloadedVertex),
// tree.vertices.data());
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
);
glVertexAttribIPointer(
2, // location 2 in the shader
1, // 1 values per vert
GL_UNSIGNED_SHORT, // u16
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
);
glGenBuffers(1, &lod_tree[tree_idx].index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lod_tree[tree_idx].index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr,
GL_STREAM_DRAW);
lod_tree[tree_idx].index_list.resize(idx_buffer_len);
if (wind_idx_buffer_len > 0) {
lod_tree[tree_idx].wind_matrix_cache.resize(tree.instance_info.size());
lod_tree[tree_idx].has_wind = true;
glGenBuffers(1, &lod_tree[tree_idx].wind_vertex_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lod_tree[tree_idx].wind_vertex_index_buffer);
std::vector<u32> temp;
temp.resize(wind_idx_buffer_len);
u32 off = 0;
for (auto& draw : tree.instanced_wind_draws) {
lod_tree[tree_idx].wind_vertex_index_offsets.push_back(off);
memcpy(temp.data() + off, draw.vertex_index_stream.data(),
draw.vertex_index_stream.size() * sizeof(u32));
off += draw.vertex_index_stream.size();
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, wind_idx_buffer_len * sizeof(u32), temp.data(),
GL_STATIC_DRAW);
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, wind_idx_buffer_len * sizeof(u32), temp.data(),
GL_STATIC_DRAW);
glActiveTexture(GL_TEXTURE10);
glGenTextures(1, &lod_tree[tree_idx].time_of_day_texture);
glBindTexture(GL_TEXTURE_1D, lod_tree[tree_idx].time_of_day_texture);
// just fill with zeros. this lets use use the faster texsubimage later
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8, m_color_result.data());
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindVertexArray(0);
}
glActiveTexture(GL_TEXTURE10);
glGenTextures(1, &m_trees[tree_idx].time_of_day_texture);
glBindTexture(GL_TEXTURE_1D, m_trees[tree_idx].time_of_day_texture);
// just fill with zeros. this lets use use the faster texsubimage later
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8, m_color_result.data());
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindVertexArray(0);
}
fmt::print("TIE temporary vis output size: {}\n", vis_temp_len);
@@ -149,19 +155,21 @@ bool Tie3::update_load(const tfrag3::Level* lev_data) {
case State::UPLOAD_VERTS: {
constexpr u32 MAX_VERTS = 40000;
bool remaining = false;
for (size_t tree_idx = 0; tree_idx < lev_data->tie_trees.size(); tree_idx++) {
const auto& tree = lev_data->tie_trees[tree_idx];
u32 verts = tree.vertices.size();
u32 start_vert = (m_load_state.vert) * MAX_VERTS;
u32 end_vert = std::min(verts, (m_load_state.vert + 1) * MAX_VERTS);
if (end_vert > start_vert) {
glBindVertexArray(m_trees[tree_idx].vao);
glBindBuffer(GL_ARRAY_BUFFER, m_trees[tree_idx].vertex_buffer);
glBufferSubData(GL_ARRAY_BUFFER, start_vert * sizeof(tfrag3::PreloadedVertex),
(end_vert - start_vert) * sizeof(tfrag3::PreloadedVertex),
tree.vertices.data() + start_vert);
if (end_vert < verts) {
remaining = true;
for (int geo = 0; geo < 4; ++geo) {
for (size_t tree_idx = 0; tree_idx < lev_data->tie_trees[geo].size(); tree_idx++) {
const auto& tree = lev_data->tie_trees[geo][tree_idx];
u32 verts = tree.vertices.size();
u32 start_vert = (m_load_state.vert) * MAX_VERTS;
u32 end_vert = std::min(verts, (m_load_state.vert + 1) * MAX_VERTS);
if (end_vert > start_vert) {
glBindVertexArray(m_trees[geo][tree_idx].vao);
glBindBuffer(GL_ARRAY_BUFFER, m_trees[geo][tree_idx].vertex_buffer);
glBufferSubData(GL_ARRAY_BUFFER, start_vert * sizeof(tfrag3::PreloadedVertex),
(end_vert - start_vert) * sizeof(tfrag3::PreloadedVertex),
tree.vertices.data() + start_vert);
if (end_vert < verts) {
remaining = true;
}
}
}
}
@@ -330,18 +338,20 @@ void do_wind_math(u16 wind_idx,
}
void Tie3::discard_tree_cache() {
for (auto& tree : m_trees) {
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glDeleteTextures(1, &tree.time_of_day_texture);
glDeleteBuffers(1, &tree.vertex_buffer);
glDeleteBuffers(1, &tree.index_buffer);
glDeleteVertexArrays(1, &tree.vao);
if (tree.has_wind) {
glDeleteBuffers(1, &tree.wind_vertex_index_buffer);
for (int geo = 0; geo < 4; ++geo) {
for (auto& tree : m_trees[geo]) {
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glDeleteTextures(1, &tree.time_of_day_texture);
glDeleteBuffers(1, &tree.vertex_buffer);
glDeleteBuffers(1, &tree.index_buffer);
glDeleteVertexArrays(1, &tree.vao);
if (tree.has_wind) {
glDeleteBuffers(1, &tree.wind_vertex_index_buffer);
}
}
}
m_trees.clear();
m_trees[geo].clear();
}
}
void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
@@ -429,11 +439,12 @@ void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi
if (!m_override_level) {
m_has_level = setup_for_level(m_pc_port_data.level_name, render_state);
}
render_all_trees(settings, render_state, prof);
render_all_trees(lod(), settings, render_state, prof);
// todo render all...
}
void Tie3::render_all_trees(const TfragRenderSettings& settings,
void Tie3::render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
Timer all_tree_timer;
@@ -441,17 +452,18 @@ void Tie3::render_all_trees(const TfragRenderSettings& settings,
m_has_level = setup_for_level(*m_pending_user_level, render_state);
m_pending_user_level = {};
}
for (u32 i = 0; i < m_trees.size(); i++) {
render_tree(i, settings, render_state, prof);
for (u32 i = 0; i < m_trees[geom].size(); i++) {
render_tree(i, geom, settings, render_state, prof);
}
m_all_tree_time.add(all_tree_timer.getSeconds());
}
void Tie3::render_tree_wind(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
auto& tree = m_trees.at(idx);
auto& tree = m_trees.at(geom).at(idx);
if (tree.wind_draws->empty()) {
return;
}
@@ -560,11 +572,12 @@ void Tie3::render_tree_wind(int idx,
}
void Tie3::render_tree(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
Timer tree_timer;
auto& tree = m_trees.at(idx);
auto& tree = m_trees.at(geom).at(idx);
tree.perf.draws = 0;
tree.perf.verts = 0;
tree.perf.full_draws = 0;
@@ -704,7 +717,7 @@ void Tie3::render_tree(int idx,
if (!m_hide_wind) {
auto wind_prof = prof.make_scoped_child("wind");
render_tree_wind(idx, settings, render_state, wind_prof);
render_tree_wind(idx, geom, settings, render_state, wind_prof);
}
glBindVertexArray(0);
@@ -725,11 +738,11 @@ void Tie3::draw_debug_window() {
ImGui::Checkbox("Hide Wind", &m_hide_wind);
ImGui::SliderFloat("Wind Multiplier", &m_wind_multiplier, 0., 40.f);
ImGui::Separator();
for (u32 i = 0; i < m_trees.size(); i++) {
auto& perf = m_trees[i].perf;
for (u32 i = 0; i < m_trees[lod()].size(); i++) {
auto& perf = m_trees[lod()][i].perf;
ImGui::Text("Tree: %d", i);
ImGui::Text("index data bytes: %d", perf.index_upload);
ImGui::Text("time of days: %d", (int)m_trees[i].colors->size());
ImGui::Text("time of days: %d", (int)m_trees[lod()][i].colors->size());
ImGui::Text("draw: %d, full: %d, verts: %d", perf.draws, perf.full_draws, perf.verts);
ImGui::Text("wind draw: %d", perf.wind_draws);
ImGui::Text("total: %.2f", perf.tree_time.get());
+11 -2
View File
@@ -2,6 +2,7 @@
#include <optional>
#include "game/graphics/gfx.h"
#include "game/graphics/opengl_renderer/tfrag/tfrag_common.h"
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/pipelines/opengl.h"
@@ -14,10 +15,12 @@ class Tie3 : public BucketRenderer {
void draw_debug_window() override;
~Tie3();
void render_all_trees(const TfragRenderSettings& settings,
void render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
void render_tree(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
@@ -34,13 +37,19 @@ class Tie3 : public BucketRenderer {
u32 pad2[3];
} m_wind_data;
int lod() const { return Gfx::g_global_settings.lod_tie; }
private:
bool update_load(const tfrag3::Level* lev_data);
void discard_tree_cache();
void render_tree_wind(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
int m_geom = 0;
struct Tree {
GLuint vertex_buffer;
GLuint index_buffer;
@@ -76,7 +85,7 @@ class Tie3 : public BucketRenderer {
} perf;
};
std::vector<Tree> m_trees;
std::array<std::vector<Tree>, 4> m_trees; // includes 4 lods!
std::string m_level_name;
const std::vector<GLuint>* m_textures;
u64 m_load_id = -1;
+2 -2
View File
@@ -126,6 +126,8 @@ static int gl_init(GfxSettings& settings) {
} else {
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_FALSE);
}
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_SAMPLES, 4);
return 0;
}
@@ -141,7 +143,6 @@ static std::shared_ptr<GfxDisplay> gl_make_main_display(int width,
int height,
const char* title,
GfxSettings& settings) {
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
if (!window) {
@@ -263,7 +264,6 @@ void render_game_frame(int width, int height, int lbox_width, int lbox_height) {
options.draw_profiler_window = g_gfx_data->debug_gui.should_draw_profiler();
options.playing_from_dump = false;
options.save_screenshot = g_gfx_data->debug_gui.get_screenshot_flag();
options.screenshot_should_compress = g_gfx_data->debug_gui.screenshot_compress_flag();
if (options.save_screenshot) {
options.screenshot_path = make_output_file_name(g_gfx_data->debug_gui.screenshot_name());
}
+1 -1
View File
@@ -249,7 +249,7 @@ std::vector<std::shared_ptr<TextureRecord>> TexturePool::convert_textures(const
fmt::format(
file_util::get_file_path({"debug_out", "textures", tpage_name, "{}-{}-{}.png"}),
tex_idx, tex_name, mip_idx),
texture_record->data.data(), ww, hh, false);
texture_record->data.data(), ww, hh);
}
result.push_back(std::move(texture_record));
}
+1
View File
@@ -826,6 +826,7 @@ void InitMachine_PCPort() {
make_function_symbol_from_c("pc-set-window-size", (void*)Gfx::set_window_size);
make_function_symbol_from_c("pc-set-letterbox", (void*)Gfx::set_letterbox);
make_function_symbol_from_c("pc-set-fullscreen", (void*)Gfx::set_fullscreen);
make_function_symbol_from_c("pc-renderer-tree-set-lod", (void*)Gfx::SetLod);
// discord rich presence
make_function_symbol_from_c("pc-discord-rpc-set", (void*)set_discord_rpc);
+13 -2
View File
@@ -4316,6 +4316,7 @@
)
(menu "Sizes"
(function "640 x 480" #f (lambda () (set-size! *pc-settings* 640 480)))
(function "640 x 360" #f (lambda () (set-size! *pc-settings* 640 360)))
(function "720 x 540" #f (lambda () (set-size! *pc-settings* 720 540)))
(function "960 x 540" #f (lambda () (set-size! *pc-settings* 960 540)))
(function "800 x 600" #f (lambda () (set-size! *pc-settings* 800 600)))
@@ -4357,10 +4358,20 @@
)
(menu "Level of detail"
(flag "PS2 LOD " #f ,(dm-lambda-boolean-flag (-> *pc-settings* ps2-lod-dist?)))
;(int-var "LOD Tfrag" 0 dm-lod-int 0 1 #t 0 3)
;(int-var "LOD Tie" 1 dm-lod-int 0 1 #t 0 3)
(int-var "LOD Tfrag" 0 dm-lod-int 0 1 #t 0 2)
(int-var "LOD Tie" 1 dm-lod-int 0 1 #t 0 3)
;(int-var "LOD Ocean" 2 dm-lod-int 0 1 #t 0 3)
(int-var "LOD Actor" 3 dm-lod-int 0 1 #t 0 3)
(function "Best quality" #f (lambda () (set! (-> *pc-settings* lod-force-tfrag) 0)
(set! (-> *pc-settings* lod-force-tie) 0)
;(set! (-> *pc-settings* lod-force-ocean) 0)
(set! (-> *pc-settings* lod-force-actor) 0)
))
(function "Worst quality" #f (lambda () (set! (-> *pc-settings* lod-force-tfrag) 2)
(set! (-> *pc-settings* lod-force-tie) 3)
;(set! (-> *pc-settings* lod-force-ocean) 2)
(set! (-> *pc-settings* lod-force-actor) 3)
))
)
;(flag "Alt load boundaries" #f ,(dm-lambda-boolean-flag (-> *pc-settings* new-lb?)))
(flag "All actors" #f ,(dm-lambda-boolean-flag (-> *pc-settings* force-actors?)))
+2 -2
View File
@@ -755,8 +755,8 @@
(dotimes (i LEVEL_COUNT)
(format *stdcon* "level ~D ~12A ~A~%" i (-> *level* level i name) (-> *level* level i display?))
)
(format *stdcon* "music ~A (f: ~D/~D) sound ~A ~A~%"
(-> *setting-control* current music) (-> *setting-control* current sound-flava) (-> *setting-control* default sound-flava)
(format *stdcon* "music ~A (f: ~D/~S) sound ~A ~A~%"
(-> *setting-control* current music) (-> *setting-control* current sound-flava) (enum->string music-flava (-> *setting-control* default sound-flava))
*sound-bank-1* *sound-bank-2*)
(let ((pos (target-pos 0)))
(format *stdcon* "target ~m ~m ~m~%" (-> pos x) (-> pos y) (-> pos z))
+1 -1
View File
@@ -6,7 +6,7 @@
;; dgos: GAME, ENGINE
(define-extern get-eye-block (function int int int))
(define-extern merc-eye-anim (function manipy none))
(define-extern merc-eye-anim (function process-drawable none))
;; DECOMP BEGINS
+1 -1
View File
@@ -1115,7 +1115,7 @@
)
)
(defun merc-eye-anim ((arg0 manipy))
(defun merc-eye-anim ((arg0 process-drawable))
; (let* ((s5-0 (-> arg0 draw mgeo header eye-ctrl))
; (a0-1 (-> arg0 skel root-channel 0))
; (s4-0 (-> a0-1 frame-group))
+5
View File
@@ -135,6 +135,11 @@
)
)
(when #t ;; (not (-> obj ps2-lod-dist?))
(pc-renderer-tree-set-lod (pc-renderer-tree-type tfrag3) (-> obj lod-force-tfrag))
(pc-renderer-tree-set-lod (pc-renderer-tree-type tie3) (-> obj lod-force-tie))
)
(none))
(defmethod update pc-settings ((obj pc-settings))
+7
View File
@@ -306,6 +306,12 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; PC Port functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defenum pc-renderer-tree-type
(none 0)
(tfrag3 1)
(tie3 2)
(invalid))
(define-extern __read-ee-timer (function uint))
(define-extern __mem-move (function pointer pointer uint none))
(define-extern __send-gfx-dma-chain (function object object none))
@@ -325,6 +331,7 @@
(define-extern pc-set-window-size (function int int none))
(define-extern pc-set-letterbox (function int int none))
(define-extern pc-set-fullscreen (function int int none))
(define-extern pc-renderer-tree-set-lod (function pc-renderer-tree-type int none))
(define-extern pc-discord-rpc-update (function discord-info none))
(define-extern pc-discord-rpc-set (function int none))