mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 21:27:52 -04:00
e56b2e8d56
* tfrag3 data for merc2 * dma hooks for merc2 * start designing merc2 opengl, seems like the simple approach will be the best here * before bone packing experiment * fix up bones.gc * use uniform buffer * speedup, fix faces and eyes * final fixes
222 lines
7.5 KiB
C++
222 lines
7.5 KiB
C++
#include "TFragment.h"
|
|
|
|
#include "third-party/imgui/imgui.h"
|
|
#include "game/graphics/opengl_renderer/dma_helpers.h"
|
|
|
|
namespace {
|
|
bool looks_like_tfragment_dma(const DmaFollower& follow) {
|
|
return follow.current_tag_vifcode0().kind == VifCode::Kind::STCYCL;
|
|
}
|
|
|
|
bool looks_like_tfrag_init(const DmaFollower& follow) {
|
|
return follow.current_tag_vifcode0().kind == VifCode::Kind::NOP &&
|
|
follow.current_tag_vifcode1().kind == VifCode::Kind::DIRECT &&
|
|
follow.current_tag_vifcode1().immediate == 2;
|
|
}
|
|
} // namespace
|
|
|
|
TFragment::TFragment(const std::string& name,
|
|
BucketId my_id,
|
|
const std::vector<tfrag3::TFragmentTreeKind>& trees,
|
|
bool child_mode,
|
|
int level_id)
|
|
: BucketRenderer(name, my_id),
|
|
m_child_mode(child_mode),
|
|
m_tree_kinds(trees),
|
|
m_level_id(level_id) {
|
|
for (auto& buf : m_buffered_data) {
|
|
for (auto& x : buf.pad) {
|
|
x = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TFragment::render(DmaFollower& dma,
|
|
SharedRenderState* render_state,
|
|
ScopedProfilerNode& prof) {
|
|
if (!m_enabled) {
|
|
while (dma.current_tag_offset() != render_state->next_bucket) {
|
|
dma.read_and_advance();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// First thing should be a NEXT with two nops.
|
|
// unless we are a child, in which case our parent took this already.
|
|
if (!m_child_mode) {
|
|
auto data0 = dma.read_and_advance();
|
|
ASSERT(data0.vif1() == 0);
|
|
ASSERT(data0.vif0() == 0);
|
|
ASSERT(data0.size_bytes == 0);
|
|
}
|
|
|
|
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
|
|
// renderer didn't run, let's just get out of here.
|
|
for (int i = 0; i < 4; i++) {
|
|
dma.read_and_advance();
|
|
}
|
|
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
|
|
return;
|
|
}
|
|
|
|
if (m_my_id == BucketId::TFRAG_LEVEL0) {
|
|
DmaTransfer transfers[2];
|
|
|
|
transfers[0] = dma.read_and_advance();
|
|
auto next0 = dma.read_and_advance();
|
|
ASSERT(next0.size_bytes == 0);
|
|
transfers[1] = dma.read_and_advance();
|
|
auto next1 = dma.read_and_advance();
|
|
ASSERT(next1.size_bytes == 0);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (transfers[i].size_bytes == 128 * 16) {
|
|
if (render_state->use_occlusion_culling) {
|
|
render_state->occlusion_vis[i].valid = true;
|
|
memcpy(render_state->occlusion_vis[i].data, transfers[i].data, 128 * 16);
|
|
}
|
|
} else {
|
|
ASSERT(transfers[i].size_bytes == 16);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
|
|
// renderer didn't run, let's just get out of here.
|
|
for (int i = 0; i < 4; i++) {
|
|
dma.read_and_advance();
|
|
}
|
|
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
|
|
return;
|
|
}
|
|
|
|
std::string level_name;
|
|
while (looks_like_tfrag_init(dma)) {
|
|
handle_initialization(dma);
|
|
if (level_name.empty()) {
|
|
level_name = m_pc_port_data.level_name;
|
|
} else if (level_name != m_pc_port_data.level_name) {
|
|
ASSERT(false);
|
|
}
|
|
|
|
while (looks_like_tfragment_dma(dma)) {
|
|
dma.read_and_advance();
|
|
}
|
|
}
|
|
|
|
while (dma.current_tag_offset() != render_state->next_bucket) {
|
|
dma.read_and_advance();
|
|
}
|
|
|
|
ASSERT(!level_name.empty());
|
|
{
|
|
m_tfrag3.setup_for_level(m_tree_kinds, level_name, render_state);
|
|
TfragRenderSettings settings;
|
|
settings.hvdf_offset = m_tfrag_data.hvdf_offset;
|
|
settings.fog = m_tfrag_data.fog;
|
|
memcpy(settings.math_camera.data(), &m_buffered_data[0].pad[TFragDataMem::TFragMatrix0 * 16],
|
|
64);
|
|
settings.tree_idx = 0;
|
|
if (render_state->occlusion_vis[m_level_id].valid) {
|
|
settings.occlusion_culling = render_state->occlusion_vis[m_level_id].data;
|
|
}
|
|
|
|
update_render_state_from_pc_settings(render_state, m_pc_port_data);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
settings.planes[i] = m_pc_port_data.planes[i];
|
|
}
|
|
|
|
if (m_override_time_of_day) {
|
|
for (int i = 0; i < 8; i++) {
|
|
settings.time_of_day_weights[i] = m_time_of_days[i];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < 8; i++) {
|
|
settings.time_of_day_weights[i] =
|
|
2 * (0xff & m_pc_port_data.itimes[i / 2].data()[2 * (i % 2)]) / 127.f;
|
|
}
|
|
}
|
|
|
|
auto t3prof = prof.make_scoped_child("t3");
|
|
m_tfrag3.render_matching_trees(m_tfrag3.lod(), m_tree_kinds, settings, render_state, t3prof);
|
|
}
|
|
|
|
while (dma.current_tag_offset() != render_state->next_bucket) {
|
|
auto tag = dma.current_tag().print();
|
|
dma.read_and_advance();
|
|
}
|
|
}
|
|
|
|
void TFragment::draw_debug_window() {
|
|
ImGui::Checkbox("Manual Time of Day", &m_override_time_of_day);
|
|
if (m_override_time_of_day) {
|
|
for (int i = 0; i < 8; i++) {
|
|
ImGui::SliderFloat(fmt::format("{}", i).c_str(), m_time_of_days + i, 0.f, 1.f);
|
|
}
|
|
}
|
|
|
|
m_tfrag3.draw_debug_window();
|
|
}
|
|
|
|
void TFragment::handle_initialization(DmaFollower& dma) {
|
|
// Set up test (different between different renderers)
|
|
auto setup_test = dma.read_and_advance();
|
|
ASSERT(setup_test.vif0() == 0);
|
|
ASSERT(setup_test.vifcode1().kind == VifCode::Kind::DIRECT);
|
|
ASSERT(setup_test.vifcode1().immediate == 2);
|
|
ASSERT(setup_test.size_bytes == 32);
|
|
memcpy(m_test_setup, setup_test.data, 32);
|
|
|
|
// matrix 0
|
|
auto mat0_upload = dma.read_and_advance();
|
|
unpack_to_stcycl(&m_buffered_data[0].pad[TFragDataMem::TFragMatrix0 * 16], mat0_upload,
|
|
VifCode::Kind::UNPACK_V4_32, 4, 4, 64, TFragDataMem::TFragMatrix0, false, false);
|
|
|
|
// matrix 1
|
|
auto mat1_upload = dma.read_and_advance();
|
|
unpack_to_stcycl(&m_buffered_data[1].pad[TFragDataMem::TFragMatrix0 * 16], mat1_upload,
|
|
VifCode::Kind::UNPACK_V4_32, 4, 4, 64, TFragDataMem::TFragMatrix1, false, false);
|
|
|
|
// data
|
|
auto data_upload = dma.read_and_advance();
|
|
unpack_to_stcycl(&m_tfrag_data, data_upload, VifCode::Kind::UNPACK_V4_32, 4, 4, sizeof(TFragData),
|
|
TFragDataMem::TFragFrameData, false, false);
|
|
|
|
// call the setup program
|
|
auto mscal_setup = dma.read_and_advance();
|
|
verify_mscal(mscal_setup, TFragProgMem::TFragSetup);
|
|
|
|
auto pc_port_data = dma.read_and_advance();
|
|
ASSERT(pc_port_data.size_bytes == sizeof(TfragPcPortData));
|
|
memcpy(&m_pc_port_data, pc_port_data.data, sizeof(TfragPcPortData));
|
|
m_pc_port_data.level_name[11] = '\0';
|
|
|
|
// setup double buffering.
|
|
auto db_setup = dma.read_and_advance();
|
|
ASSERT(db_setup.size_bytes == 0);
|
|
ASSERT(db_setup.vifcode0().kind == VifCode::Kind::BASE &&
|
|
db_setup.vifcode0().immediate == Buffer0_Start);
|
|
ASSERT(db_setup.vifcode1().kind == VifCode::Kind::OFFSET &&
|
|
db_setup.vifcode1().immediate == (Buffer1_Start - Buffer0_Start));
|
|
}
|
|
|
|
std::string TFragData::print() const {
|
|
std::string result;
|
|
result += fmt::format("fog: {}\n", fog.to_string_aligned());
|
|
result += fmt::format("val: {}\n", val.to_string_aligned());
|
|
result += fmt::format("str-gif: {}\n", str_gif.print());
|
|
result += fmt::format("fan-gif: {}\n", fan_gif.print());
|
|
result += fmt::format("ad-gif: {}\n", ad_gif.print());
|
|
result += fmt::format("hvdf_offset: {}\n", hvdf_offset.to_string_aligned());
|
|
result += fmt::format("hmge_scale: {}\n", hmge_scale.to_string_aligned());
|
|
result += fmt::format("invh_scale: {}\n", invh_scale.to_string_aligned());
|
|
result += fmt::format("ambient: {}\n", ambient.to_string_aligned());
|
|
result += fmt::format("guard: {}\n", guard.to_string_aligned());
|
|
result += fmt::format("k0s[0]: {}\n", k0s[0].to_string_aligned());
|
|
result += fmt::format("k0s[1]: {}\n", k0s[1].to_string_aligned());
|
|
result += fmt::format("k1s[0]: {}\n", k1s[0].to_string_aligned());
|
|
result += fmt::format("k1s[1]: {}\n", k1s[1].to_string_aligned());
|
|
return result;
|
|
}
|