mirror of
https://github.com/open-goal/jak-project
synced 2026-07-11 15:28:58 -04:00
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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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?)))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user