mirror of
https://github.com/open-goal/jak-project
synced 2026-07-08 06:30:44 -04:00
@@ -4,6 +4,7 @@
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
#include "decompiler/level_extractor/extract_tfrag.h"
|
||||
#include "decompiler/level_extractor/extract_tie.h"
|
||||
#include "common/util/compress.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
@@ -107,8 +108,12 @@ void extract_from_level(ObjectFileDB& db,
|
||||
|
||||
Serializer ser;
|
||||
tfrag_level.serialize(ser);
|
||||
auto compressed =
|
||||
compression::compress_zstd(ser.get_save_result().first, ser.get_save_result().second);
|
||||
fmt::print("compressed: {} -> {} ({:.2f}%)\n", ser.get_save_result().second, compressed.size(),
|
||||
100.f * compressed.size() / ser.get_save_result().second);
|
||||
file_util::write_binary_file(file_util::get_file_path({fmt::format(
|
||||
"assets/{}.fr3", dgo_name.substr(0, dgo_name.length() - 4))}),
|
||||
ser.get_save_result().first, ser.get_save_result().second);
|
||||
compressed.data(), compressed.size());
|
||||
}
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -525,11 +525,6 @@ void update_proto_info(std::vector<TieProtoInfo>* out,
|
||||
}
|
||||
}
|
||||
|
||||
// just for debug
|
||||
for (int g = 0; g < 4; g++) {
|
||||
frag_info.point_sizes.push_back(proto.geometry[g].tie_fragments[frag_idx].point_ref.size());
|
||||
}
|
||||
|
||||
info.frags.push_back(std::move(frag_info));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Loader.h"
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/compress.h"
|
||||
|
||||
namespace {
|
||||
std::string uppercase_string(const std::string& s) {
|
||||
@@ -13,23 +14,66 @@ std::string uppercase_string(const std::string& s) {
|
||||
} // namespace
|
||||
|
||||
tfrag3::Level* Loader::get_tfrag3_level(const std::string& level_name) {
|
||||
std::unique_lock<std::mutex> lk(m_loader_mutex);
|
||||
const auto& existing = m_tfrag3_levels.find(level_name);
|
||||
if (existing == m_tfrag3_levels.end()) {
|
||||
fmt::print("Loader needs to load tfrag3 level: {}\n", level_name);
|
||||
if (m_level_to_load.empty()) {
|
||||
fmt::print("[pc loader] starting load for {}\n", level_name);
|
||||
m_level_to_load = level_name;
|
||||
lk.unlock();
|
||||
m_loader_cv.notify_all();
|
||||
return nullptr;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
existing->second.frames_since_last_used = 0;
|
||||
return existing->second.level.get();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::loader_thread() {
|
||||
while (!m_want_shutdown) {
|
||||
std::unique_lock<std::mutex> lk(m_loader_mutex);
|
||||
m_loader_cv.wait(lk, [&] { return !m_level_to_load.empty() || m_want_shutdown; });
|
||||
if (m_want_shutdown) {
|
||||
return;
|
||||
}
|
||||
std::string lev = m_level_to_load;
|
||||
lk.unlock();
|
||||
|
||||
Timer disk_timer;
|
||||
auto data = file_util::read_binary_file(
|
||||
file_util::get_file_path({fmt::format("assets/{}.fr3", uppercase_string(level_name))}));
|
||||
file_util::get_file_path({fmt::format("assets/{}.fr3", uppercase_string(lev))}));
|
||||
double disk_load_time = disk_timer.getSeconds();
|
||||
|
||||
Timer decomp_timer;
|
||||
auto decomp_data = compression::decompress_zstd(data.data(), data.size());
|
||||
double decomp_time = decomp_timer.getSeconds();
|
||||
|
||||
Timer import_timer;
|
||||
auto& result = m_tfrag3_levels[level_name];
|
||||
Serializer ser(data.data(), data.size());
|
||||
result.serialize(ser);
|
||||
auto result = std::make_unique<tfrag3::Level>();
|
||||
Serializer ser(decomp_data.data(), decomp_data.size());
|
||||
result->serialize(ser);
|
||||
double import_time = import_timer.getSeconds();
|
||||
fmt::print("------------> Load from file: {:.3f}s, import {:.3f}s\n", disk_load_time,
|
||||
import_time);
|
||||
return &result;
|
||||
} else {
|
||||
return &existing->second;
|
||||
fmt::print("------------> Load from file: {:.3f}s, import {:.3f}s, decomp {:.3f}s\n",
|
||||
disk_load_time, import_time, decomp_time);
|
||||
|
||||
lk.lock();
|
||||
m_level_to_load = "";
|
||||
m_tfrag3_levels[lev].level = std::move(result);
|
||||
}
|
||||
}
|
||||
|
||||
Loader::Loader() {
|
||||
m_loader_thread = std::thread(&Loader::loader_thread, this);
|
||||
}
|
||||
|
||||
Loader::~Loader() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_loader_mutex);
|
||||
m_want_shutdown = true;
|
||||
m_loader_cv.notify_all();
|
||||
}
|
||||
m_loader_thread.join();
|
||||
}
|
||||
@@ -1,11 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
|
||||
class Loader {
|
||||
public:
|
||||
Loader();
|
||||
~Loader();
|
||||
tfrag3::Level* get_tfrag3_level(const std::string& level_name);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, tfrag3::Level> m_tfrag3_levels;
|
||||
struct Level {
|
||||
std::unique_ptr<tfrag3::Level> level;
|
||||
int frames_since_last_used = 0;
|
||||
};
|
||||
|
||||
void loader_thread();
|
||||
|
||||
std::unordered_map<std::string, Level> m_tfrag3_levels;
|
||||
std::string m_level_to_load;
|
||||
|
||||
std::thread m_loader_thread;
|
||||
std::mutex m_loader_mutex;
|
||||
std::condition_variable m_loader_cv;
|
||||
bool m_want_shutdown = false;
|
||||
};
|
||||
|
||||
@@ -27,6 +27,10 @@ Tfrag3::Tfrag3() {
|
||||
(void*)offsetof(DebugVertex, rgba) // offset (0)
|
||||
);
|
||||
glBindVertexArray(0);
|
||||
// regardless of how many we use some fixed max
|
||||
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
|
||||
// indexing works properly.
|
||||
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
|
||||
}
|
||||
|
||||
Tfrag3::~Tfrag3() {
|
||||
@@ -34,139 +38,203 @@ Tfrag3::~Tfrag3() {
|
||||
glDeleteVertexArrays(1, &m_debug_vao);
|
||||
}
|
||||
|
||||
void Tfrag3::setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
bool Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
const tfrag3::Level* lev_data) {
|
||||
switch (m_load_state.state) {
|
||||
case State::DISCARD_TREE:
|
||||
discard_tree_cache();
|
||||
m_load_state.state = State::FREE_OLD_TREES;
|
||||
break;
|
||||
case State::FREE_OLD_TREES:
|
||||
m_cached_trees.clear();
|
||||
m_load_state.state = State::INIT_NEW_TREES;
|
||||
break;
|
||||
case State::INIT_NEW_TREES:
|
||||
|
||||
{
|
||||
size_t time_of_day_count = 0;
|
||||
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;
|
||||
|
||||
const auto& tree = lev_data->tfrag_trees[tree_idx];
|
||||
m_cached_trees.emplace_back();
|
||||
auto& tree_cache = m_cached_trees.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);
|
||||
}
|
||||
}
|
||||
|
||||
m_cache.vis_temp.resize(vis_temp_len);
|
||||
m_cache.draw_idx_temp.resize(max_draw);
|
||||
|
||||
assert(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
|
||||
m_load_state.state = UPLOAD_VERTS;
|
||||
m_load_state.vert = 0;
|
||||
} break;
|
||||
|
||||
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->tfrag_trees[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_load_state.vert++;
|
||||
if (!remaining) {
|
||||
m_load_state.state = INIT_TEX;
|
||||
m_load_state.tex_id = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::INIT_TEX:
|
||||
for (size_t max_tex =
|
||||
std::min((size_t)m_load_state.tex_id + MAX_TEX_PER_FRAME, lev_data->textures.size());
|
||||
m_load_state.tex_id < max_tex; m_load_state.tex_id++) {
|
||||
auto& tex = lev_data->textures[m_load_state.tex_id];
|
||||
GLuint gl_tex;
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
|
||||
m_textures.push_back(gl_tex);
|
||||
}
|
||||
return m_load_state.tex_id == lev_data->textures.size();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tfrag3::setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
const std::string& level,
|
||||
SharedRenderState* render_state) {
|
||||
// regardless of how many we use some fixed max
|
||||
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
|
||||
// indexing works properly.
|
||||
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
|
||||
// first, get the level in memory
|
||||
Timer tfrag3_setup_timer;
|
||||
|
||||
// make sure we have the level data.
|
||||
auto lev_data = render_state->loader.get_tfrag3_level(level);
|
||||
if (m_level_name != level) {
|
||||
Timer tfrag3_setup_timer;
|
||||
fmt::print("new level for tfrag3: {} -> {}\n", m_level_name, level);
|
||||
fmt::print("discarding old stuff\n");
|
||||
discard_tree_cache();
|
||||
fmt::print("level has {} trees\n", lev_data->tfrag_trees.size());
|
||||
m_cached_trees.clear();
|
||||
|
||||
size_t time_of_day_count = 0;
|
||||
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;
|
||||
|
||||
const auto& tree = lev_data->tfrag_trees[tree_idx];
|
||||
m_cached_trees.emplace_back();
|
||||
auto& tree_cache = m_cached_trees.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();
|
||||
fmt::print(" tree {} has {} verts ({} kB) and {} draws\n", tree_idx, verts,
|
||||
verts * sizeof(tfrag3::PreloadedVertex) / 1024.f, tree.draws.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);
|
||||
|
||||
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, &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);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print("TFRAG temporary vis output size: {}\n", vis_temp_len);
|
||||
m_cache.vis_temp.resize(vis_temp_len);
|
||||
fmt::print("TFRAG max draws/tree: {}\n", max_draw);
|
||||
m_cache.draw_idx_temp.resize(max_draw);
|
||||
|
||||
fmt::print("level has {} textures\n", lev_data->textures.size());
|
||||
for (auto& tex : lev_data->textures) {
|
||||
GLuint gl_tex;
|
||||
// fmt::print(" tex: {} x {} {} {}\n", tex.w, tex.h, tex.debug_name, tex.debug_tpage_name);
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
|
||||
m_textures.push_back(gl_tex);
|
||||
}
|
||||
|
||||
fmt::print("level max time of day: {}\n", time_of_day_count);
|
||||
assert(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
|
||||
|
||||
m_level_name = level;
|
||||
fmt::print("TFRAG3 setup: {:.3f}\n", tfrag3_setup_timer.getSeconds());
|
||||
if (!lev_data) {
|
||||
m_has_level = false;
|
||||
return false;
|
||||
}
|
||||
int init_load_state = m_load_state.state;
|
||||
|
||||
if (m_level_name != level) {
|
||||
m_has_level = false;
|
||||
if (!m_load_state.loading) {
|
||||
m_load_state.loading = true;
|
||||
m_load_state.state = State::FIRST;
|
||||
}
|
||||
if (update_load(tree_kinds, lev_data)) {
|
||||
m_has_level = true;
|
||||
m_level_name = level;
|
||||
m_load_state.loading = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
m_has_level = true;
|
||||
}
|
||||
if (tfrag3_setup_timer.getMs() > 5) {
|
||||
fmt::print("TFRAG slow setup: {:.1f}ms s {}\n", tfrag3_setup_timer.getMs(), init_load_state);
|
||||
}
|
||||
|
||||
return m_has_level;
|
||||
}
|
||||
|
||||
void Tfrag3::render_tree(const TfragRenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof) {
|
||||
if (!m_has_level) {
|
||||
return;
|
||||
}
|
||||
auto& tree = m_cached_trees.at(settings.tree_idx);
|
||||
assert(tree.kind != tfrag3::TFragmentTreeKind::INVALID);
|
||||
|
||||
@@ -338,9 +406,6 @@ void Tfrag3::discard_tree_cache() {
|
||||
glDeleteVertexArrays(1, &tree.vao);
|
||||
}
|
||||
}
|
||||
|
||||
// delete textures and stuff.
|
||||
m_cached_trees.clear();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -29,7 +29,7 @@ class Tfrag3 {
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
|
||||
void setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
bool setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
const std::string& level,
|
||||
SharedRenderState* render_state);
|
||||
void discard_tree_cache();
|
||||
@@ -44,6 +44,9 @@ class Tfrag3 {
|
||||
math::Vector4f rgba;
|
||||
};
|
||||
|
||||
bool update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
|
||||
const tfrag3::Level* lev_data);
|
||||
|
||||
private:
|
||||
struct TreeCache {
|
||||
tfrag3::TFragmentTreeKind kind;
|
||||
@@ -93,5 +96,23 @@ class Tfrag3 {
|
||||
static constexpr int DEBUG_TRI_COUNT = 4096;
|
||||
std::vector<DebugVertex> m_debug_vert_data;
|
||||
|
||||
bool m_has_level = false;
|
||||
bool m_use_fast_time_of_day = true;
|
||||
|
||||
enum State : u32 {
|
||||
FIRST = 0,
|
||||
DISCARD_TREE = 0,
|
||||
FREE_OLD_TREES = 1,
|
||||
INIT_NEW_TREES = 2,
|
||||
UPLOAD_VERTS = 3,
|
||||
INIT_TEX = 4,
|
||||
};
|
||||
|
||||
struct {
|
||||
bool loading = false;
|
||||
State state;
|
||||
u32 tex_id = 0;
|
||||
u32 vert = 0;
|
||||
} m_load_state;
|
||||
static constexpr int MAX_TEX_PER_FRAME = 4;
|
||||
};
|
||||
|
||||
@@ -2,177 +2,236 @@
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
Tie3::Tie3(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id) {}
|
||||
Tie3::Tie3(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id) {
|
||||
// regardless of how many we use some fixed max
|
||||
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
|
||||
// indexing works properly.
|
||||
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
|
||||
}
|
||||
|
||||
Tie3::~Tie3() {
|
||||
discard_tree_cache();
|
||||
}
|
||||
|
||||
bool Tie3::update_load(const tfrag3::Level* lev_data) {
|
||||
switch (m_load_state.state) {
|
||||
case DISCARD_TREE:
|
||||
m_wind_vectors.clear();
|
||||
// We changed level!
|
||||
discard_tree_cache();
|
||||
m_trees.resize(lev_data->tie_trees.size());
|
||||
m_load_state.state = INIT_NEW_TREES;
|
||||
break;
|
||||
case INIT_NEW_TREES: {
|
||||
size_t time_of_day_count = 0;
|
||||
size_t vis_temp_len = 0;
|
||||
size_t max_draw = 0;
|
||||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, wind_idx_buffer_len * sizeof(u32), temp.data(),
|
||||
GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
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);
|
||||
m_cache.vis_temp.resize(vis_temp_len);
|
||||
fmt::print("TIE max draws/tree: {}\n", max_draw);
|
||||
m_cache.draw_idx_temp.resize(max_draw);
|
||||
fmt::print("TIE draw with the most verts: {}\n", max_idx_per_draw);
|
||||
fmt::print("wind: {}\n", max_wind_idx);
|
||||
m_wind_vectors.resize(4 * max_wind_idx + 4); // 4x u32's per wind.
|
||||
fmt::print("level max time of day: {}\n", time_of_day_count);
|
||||
assert(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
|
||||
}
|
||||
m_load_state.state = UPLOAD_VERTS;
|
||||
m_load_state.vert = 0;
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_load_state.vert++;
|
||||
if (!remaining) {
|
||||
m_load_state.state = INIT_TEX;
|
||||
m_load_state.tex = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case State::INIT_TEX:
|
||||
for (size_t max_tex = std::min((size_t)m_load_state.tex + 3, lev_data->textures.size());
|
||||
m_load_state.tex < max_tex; m_load_state.tex++) {
|
||||
auto& tex = lev_data->textures[m_load_state.tex];
|
||||
GLuint gl_tex;
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
|
||||
m_textures.push_back(gl_tex);
|
||||
}
|
||||
return m_load_state.tex == lev_data->textures.size();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/*!
|
||||
* Set up all OpenGL and temporary buffers for a given level name.
|
||||
* The level name should be the 3 character short name.
|
||||
*/
|
||||
void Tie3::setup_for_level(const std::string& level, SharedRenderState* render_state) {
|
||||
bool Tie3::setup_for_level(const std::string& level, SharedRenderState* render_state) {
|
||||
// make sure we have the level data.
|
||||
// TODO: right now this will wait to load from disk and unpack it.
|
||||
Timer tfrag3_setup_timer;
|
||||
auto lev_data = render_state->loader.get_tfrag3_level(level);
|
||||
|
||||
// regardless of how many we use some fixed max
|
||||
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
|
||||
// indexing works properly.
|
||||
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
|
||||
if (!lev_data) {
|
||||
return false;
|
||||
}
|
||||
int init_load_state = m_load_state.state;
|
||||
|
||||
if (m_level_name != level) {
|
||||
Timer tie_setup_timer;
|
||||
m_wind_vectors.clear();
|
||||
// We changed level!
|
||||
fmt::print("TIE3 level change! {} -> {}\n", m_level_name, level);
|
||||
fmt::print(" Removing old level...\n");
|
||||
discard_tree_cache();
|
||||
fmt::print(" New level has {} tie trees\n", lev_data->tie_trees.size());
|
||||
m_trees.resize(lev_data->tie_trees.size());
|
||||
|
||||
size_t time_of_day_count = 0;
|
||||
size_t vis_temp_len = 0;
|
||||
size_t max_draw = 0;
|
||||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, wind_idx_buffer_len * sizeof(u32), temp.data(),
|
||||
GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
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);
|
||||
m_has_level = false;
|
||||
if (!m_load_state.loading) {
|
||||
m_load_state.loading = true;
|
||||
m_load_state.state = State::FIRST;
|
||||
}
|
||||
|
||||
fmt::print("TIE temporary vis output size: {}\n", vis_temp_len);
|
||||
m_cache.vis_temp.resize(vis_temp_len);
|
||||
fmt::print("TIE max draws/tree: {}\n", max_draw);
|
||||
m_cache.draw_idx_temp.resize(max_draw);
|
||||
fmt::print("TIE draw with the most verts: {}\n", max_idx_per_draw);
|
||||
|
||||
// todo share textures
|
||||
fmt::print("level has {} textures\n", lev_data->textures.size());
|
||||
for (auto& tex : lev_data->textures) {
|
||||
GLuint gl_tex;
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, aniso);
|
||||
m_textures.push_back(gl_tex);
|
||||
if (update_load(lev_data)) {
|
||||
m_has_level = true;
|
||||
m_level_name = level;
|
||||
m_load_state.loading = false;
|
||||
}
|
||||
|
||||
fmt::print("level max time of day: {}\n", time_of_day_count);
|
||||
assert(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
|
||||
|
||||
fmt::print("wind: {}\n", max_wind_idx);
|
||||
m_wind_vectors.resize(4 * max_wind_idx + 4); // 4x u32's per wind.
|
||||
|
||||
m_level_name = level;
|
||||
fmt::print("TIE setup: {:.3f}\n", tie_setup_timer.getSeconds());
|
||||
} else {
|
||||
m_has_level = true;
|
||||
}
|
||||
|
||||
if (tfrag3_setup_timer.getMs() > 5) {
|
||||
fmt::print("TIE setup: {:.1f}ms s {}\n", tfrag3_setup_timer.getMs(), init_load_state);
|
||||
}
|
||||
|
||||
return m_has_level;
|
||||
}
|
||||
|
||||
void vector_min_in_place(math::Vector4f& v, float val) {
|
||||
@@ -315,7 +374,7 @@ void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi
|
||||
}
|
||||
|
||||
if (m_override_level && m_pending_user_level) {
|
||||
setup_for_level(*m_pending_user_level, render_state);
|
||||
m_has_level = setup_for_level(*m_pending_user_level, render_state);
|
||||
m_pending_user_level = {};
|
||||
}
|
||||
|
||||
@@ -383,7 +442,7 @@ void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi
|
||||
}
|
||||
}
|
||||
if (!m_override_level) {
|
||||
setup_for_level(m_pc_port_data.level_name, render_state);
|
||||
m_has_level = setup_for_level(m_pc_port_data.level_name, render_state);
|
||||
}
|
||||
render_all_trees(settings, render_state, prof);
|
||||
// todo render all...
|
||||
@@ -394,7 +453,7 @@ void Tie3::render_all_trees(const TfragRenderSettings& settings,
|
||||
ScopedProfilerNode& prof) {
|
||||
Timer all_tree_timer;
|
||||
if (m_override_level && m_pending_user_level) {
|
||||
setup_for_level(*m_pending_user_level, render_state);
|
||||
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++) {
|
||||
@@ -525,6 +584,9 @@ void Tie3::render_tree(int idx,
|
||||
tree.perf.verts = 0;
|
||||
tree.perf.full_draws = 0;
|
||||
tree.perf.wind_draws = 0;
|
||||
if (!m_has_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_color_result.size() < tree.colors->size()) {
|
||||
m_color_result.resize(tree.colors->size());
|
||||
|
||||
@@ -21,7 +21,7 @@ class Tie3 : public BucketRenderer {
|
||||
const TfragRenderSettings& settings,
|
||||
SharedRenderState* render_state,
|
||||
ScopedProfilerNode& prof);
|
||||
void setup_for_level(const std::string& str, SharedRenderState* render_state);
|
||||
bool setup_for_level(const std::string& str, SharedRenderState* render_state);
|
||||
|
||||
struct WindWork {
|
||||
u32 paused;
|
||||
@@ -35,6 +35,7 @@ class Tie3 : public BucketRenderer {
|
||||
} m_wind_data;
|
||||
|
||||
private:
|
||||
bool update_load(const tfrag3::Level* lev_data);
|
||||
void discard_tree_cache();
|
||||
void render_tree_wind(int idx,
|
||||
const TfragRenderSettings& settings,
|
||||
@@ -88,6 +89,7 @@ class Tie3 : public BucketRenderer {
|
||||
|
||||
static constexpr int TIME_OF_DAY_COLOR_COUNT = 8192;
|
||||
|
||||
bool m_has_level = false;
|
||||
char m_user_level[255] = "vi1";
|
||||
std::optional<std::string> m_pending_user_level = std::nullopt;
|
||||
bool m_override_level = false;
|
||||
@@ -104,4 +106,19 @@ class Tie3 : public BucketRenderer {
|
||||
float m_wind_multiplier = 1.f;
|
||||
|
||||
static_assert(sizeof(WindWork) == 84 * 16);
|
||||
|
||||
enum State : u32 {
|
||||
FIRST = 0,
|
||||
DISCARD_TREE = 0,
|
||||
INIT_NEW_TREES = 1,
|
||||
UPLOAD_VERTS = 2,
|
||||
INIT_TEX = 4,
|
||||
};
|
||||
|
||||
struct {
|
||||
bool loading = false;
|
||||
State state;
|
||||
u32 tex = 0;
|
||||
u32 vert = 0;
|
||||
} m_load_state;
|
||||
};
|
||||
|
||||
@@ -159,7 +159,7 @@ std::vector<std::shared_ptr<TextureRecord>> TexturePool::convert_textures(const
|
||||
// the sizes given aren't the actual sizes in memory, so if you just use that, you get the
|
||||
// wrong answer. I solved this in the decompiler by using the size of the actual data, but we
|
||||
// don't really have that here.
|
||||
u32 size = ((sizes[0] + sizes[1] + sizes[2] + 255) / 256) * 256;
|
||||
u32 size = ((sizes[0] + sizes[1] + sizes[2] + 2047) / 256) * 256;
|
||||
|
||||
m_tex_converter.upload(memory_base + texture_page.segment[0].block_data_ptr,
|
||||
texture_page.segment[0].dest, size);
|
||||
|
||||
@@ -158,16 +158,18 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; tfrag
|
||||
|
||||
(when (logtest? *texture-enable-user* 1)
|
||||
(dotimes (gp-1 (-> *level* length))
|
||||
(let ((a1-2 (-> *level* level gp-1)))
|
||||
(if (= (-> a1-2 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-2 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; note: PC port uses preconverted tfrag textures.
|
||||
(#unless PC_PORT
|
||||
(when (logtest? *texture-enable-user* 1)
|
||||
(dotimes (gp-1 (-> *level* length))
|
||||
(let ((a1-2 (-> *level* level gp-1)))
|
||||
(if (= (-> a1-2 status) 'active)
|
||||
(add-tex-to-dma! *texture-pool* a1-2 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; pris
|
||||
|
||||
|
||||
Reference in New Issue
Block a user