jak-project/game/graphics/opengl_renderer/BucketRenderer.cpp
Hat Kid 3d08d079c9
jak2: ocean renderer (#2142)
Initial implementation of the `ocean-mid`, `ocean-far` and `ocean-near`
renderers for Jak 2.

There's still a few things to sort out, mainly:

- [x] ~Backwards compatibility with Jak 1. The only thing that currently
stands in the way of that is figuring out a clean way to "un-hardcode"
the texture base pointer in C++ without creating a completely separate
`OceanTexture` class for Jak 2. One thing I thought of would be
modifying `BucketRenderer`'s virtual `init_textures` method to also pass
the `GameVersion`, but I'm not sure if there's a better way.~
- [x] ~The sudden transition from `ocean-near` to `ocean-mid`. Not sure
why it's happening or how to fix it.~
- [x] The ocean has two new methods in Jak 2, `ocean::89` and
`ocean::79`, one of which seems to be related to time of day sky colors.
~Even without them implemented, the end result looks quite close, so we
may be able to skip them?~ `ocean::89` generates `ocean-mid` envmap
textures, so it will likely be required, but will not be handled right
now.

Reverted the VU prologue removals because it made the tests fail.
2023-01-22 18:07:46 -05:00

112 lines
3.6 KiB
C++

#include "BucketRenderer.h"
#include "third-party/fmt/core.h"
#include "third-party/imgui/imgui.h"
std::string BucketRenderer::name_and_id() const {
return fmt::format("[{:2d}] {}", (int)m_my_id, m_name);
}
EmptyBucketRenderer::EmptyBucketRenderer(const std::string& name, int my_id)
: BucketRenderer(name, my_id) {}
void EmptyBucketRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& /*prof*/) {
if (render_state->version == GameVersion::Jak1) {
// an empty bucket should have 4 things:
// a NEXT in the bucket buffer
// a CALL that calls the default register buffer chain
// a CNT then RET to get out of the default register buffer chain
// a NEXT to get to the next bucket.
// NEXT
auto first_tag = dma.current_tag();
dma.read_and_advance();
ASSERT(first_tag.kind == DmaTag::Kind::NEXT && first_tag.qwc == 0);
// CALL
auto call_tag = dma.current_tag();
dma.read_and_advance();
ASSERT_MSG(call_tag.kind == DmaTag::Kind::CALL && call_tag.qwc == 0,
fmt::format("Bucket renderer {} ({}) was supposed to be empty, but wasn't\n",
m_my_id, m_name));
// in the default reg buffer:
ASSERT(dma.current_tag_offset() == render_state->default_regs_buffer);
dma.read_and_advance();
ASSERT(dma.current_tag().kind == DmaTag::Kind::RET);
dma.read_and_advance();
// NEXT to next buffer
auto to_next_buffer = dma.current_tag();
ASSERT(to_next_buffer.kind == DmaTag::Kind::NEXT);
ASSERT(to_next_buffer.qwc == 0);
dma.read_and_advance();
// and we should now be in the next bucket!
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
} else {
auto first_tag = dma.current_tag();
dma.read_and_advance();
if (first_tag.kind != DmaTag::Kind::CNT || first_tag.qwc != 0) {
fmt::print("Bucket renderer {} ({}) was supposed to be empty, but wasn't\n", m_my_id, m_name);
ASSERT(false);
}
}
}
SkipRenderer::SkipRenderer(const std::string& name, int my_id) : BucketRenderer(name, my_id) {}
void SkipRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& /*prof*/) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
}
void SharedRenderState::reset() {
has_pc_data = false;
for (auto& x : occlusion_vis) {
x.valid = false;
}
load_status_debug.clear();
}
RenderMux::RenderMux(const std::string& name,
int my_id,
std::vector<std::unique_ptr<BucketRenderer>> renderers)
: BucketRenderer(name, my_id), m_renderers(std::move(renderers)) {
for (auto& r : m_renderers) {
m_name_strs.push_back(r->name_and_id());
}
for (auto& n : m_name_strs) {
m_name_str_ptrs.push_back(n.data());
}
}
void RenderMux::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
m_renderers[m_render_idx]->enabled() = m_enabled;
m_renderers[m_render_idx]->render(dma, render_state, prof);
}
void RenderMux::draw_debug_window() {
ImGui::ListBox("Pick", &m_render_idx, m_name_str_ptrs.data(), m_renderers.size());
ImGui::Separator();
m_renderers[m_render_idx]->draw_debug_window();
}
void RenderMux::init_textures(TexturePool& tp, GameVersion version) {
for (auto& rend : m_renderers) {
rend->init_textures(tp, version);
}
}
void RenderMux::init_shaders(ShaderLibrary& sl) {
for (auto& rend : m_renderers) {
rend->init_shaders(sl);
}
}